2024-07-13 23:57:07 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Service;
|
|
|
|
|
|
2025-08-26 16:18:29 +02:00
|
|
|
use App\Config\DnsKey\Algorithm;
|
|
|
|
|
use App\Config\DnsKey\DigestType;
|
2024-07-13 23:57:07 +02:00
|
|
|
use App\Config\EventAction;
|
2025-09-10 21:35:43 +02:00
|
|
|
use App\Config\RegistrarStatus;
|
2024-07-24 18:52:19 +02:00
|
|
|
use App\Config\TldType;
|
2025-08-26 16:18:29 +02:00
|
|
|
use App\Entity\DnsKey;
|
2024-07-13 23:57:07 +02:00
|
|
|
use App\Entity\Domain;
|
|
|
|
|
use App\Entity\DomainEntity;
|
|
|
|
|
use App\Entity\DomainEvent;
|
2024-12-12 22:56:35 +01:00
|
|
|
use App\Entity\DomainStatus;
|
2024-07-13 23:57:07 +02:00
|
|
|
use App\Entity\Entity;
|
|
|
|
|
use App\Entity\EntityEvent;
|
2025-09-10 22:17:19 +02:00
|
|
|
use App\Entity\IanaAccreditation;
|
2024-07-13 23:57:07 +02:00
|
|
|
use App\Entity\Nameserver;
|
|
|
|
|
use App\Entity\NameserverEntity;
|
2024-07-18 19:13:06 +02:00
|
|
|
use App\Entity\RdapServer;
|
2024-07-19 18:59:21 +02:00
|
|
|
use App\Entity\Tld;
|
2024-07-13 23:57:07 +02:00
|
|
|
use App\Repository\DomainEntityRepository;
|
|
|
|
|
use App\Repository\DomainEventRepository;
|
|
|
|
|
use App\Repository\DomainRepository;
|
|
|
|
|
use App\Repository\EntityEventRepository;
|
|
|
|
|
use App\Repository\EntityRepository;
|
|
|
|
|
use App\Repository\NameserverEntityRepository;
|
|
|
|
|
use App\Repository\NameserverRepository;
|
2024-07-18 19:13:06 +02:00
|
|
|
use App\Repository\RdapServerRepository;
|
2024-07-19 18:59:21 +02:00
|
|
|
use App\Repository\TldRepository;
|
2024-12-30 17:19:07 +01:00
|
|
|
use Doctrine\DBAL\LockMode;
|
2024-07-13 23:57:07 +02:00
|
|
|
use Doctrine\ORM\EntityManagerInterface;
|
2024-07-19 18:59:21 +02:00
|
|
|
use Doctrine\ORM\Exception\ORMException;
|
2024-08-04 04:39:05 +02:00
|
|
|
use Psr\Log\LoggerInterface;
|
2024-12-08 13:54:51 +01:00
|
|
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
2024-08-07 16:21:41 +02:00
|
|
|
use Symfony\Component\HttpClient\Exception\ClientException;
|
2024-08-06 21:43:37 +02:00
|
|
|
use Symfony\Component\HttpFoundation\Exception\BadRequestException;
|
|
|
|
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
2025-08-26 16:18:29 +02:00
|
|
|
use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
|
2024-08-19 18:06:11 +02:00
|
|
|
use Symfony\Component\Yaml\Yaml;
|
2024-07-19 01:17:33 +02:00
|
|
|
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
|
|
|
|
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
|
2024-07-25 16:19:57 +02:00
|
|
|
use Symfony\Contracts\HttpClient\Exception\HttpExceptionInterface;
|
2024-07-19 01:17:33 +02:00
|
|
|
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
|
|
|
|
|
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
|
|
|
|
|
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
2024-07-13 23:57:07 +02:00
|
|
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
|
|
|
|
|
2024-07-14 21:15:13 +02:00
|
|
|
readonly class RDAPService
|
2024-07-13 23:57:07 +02:00
|
|
|
{
|
2024-12-29 18:06:28 +01:00
|
|
|
/* @see https://www.iana.org/domains/root/db */
|
2024-08-02 23:24:52 +02:00
|
|
|
public const ISO_TLD_EXCEPTION = ['ac', 'eu', 'uk', 'su', 'tp'];
|
|
|
|
|
public const INFRA_TLD = ['arpa'];
|
|
|
|
|
public const SPONSORED_TLD = [
|
2024-07-25 00:58:19 +02:00
|
|
|
'aero',
|
|
|
|
|
'asia',
|
|
|
|
|
'cat',
|
|
|
|
|
'coop',
|
|
|
|
|
'edu',
|
|
|
|
|
'gov',
|
|
|
|
|
'int',
|
|
|
|
|
'jobs',
|
|
|
|
|
'mil',
|
|
|
|
|
'museum',
|
|
|
|
|
'post',
|
|
|
|
|
'tel',
|
|
|
|
|
'travel',
|
|
|
|
|
'xxx',
|
|
|
|
|
];
|
2024-08-02 23:24:52 +02:00
|
|
|
public const TEST_TLD = [
|
2024-07-24 18:52:19 +02:00
|
|
|
'xn--kgbechtv',
|
|
|
|
|
'xn--hgbk6aj7f53bba',
|
|
|
|
|
'xn--0zwm56d',
|
|
|
|
|
'xn--g6w251d',
|
|
|
|
|
'xn--80akhbyknj4f',
|
|
|
|
|
'xn--11b5bs3a9aj6g',
|
|
|
|
|
'xn--jxalpdlp',
|
|
|
|
|
'xn--9t4b11yi5a',
|
|
|
|
|
'xn--deba0ad',
|
|
|
|
|
'xn--zckzah',
|
2024-08-02 23:24:52 +02:00
|
|
|
'xn--hlcj6aya9esc7a',
|
2024-07-24 18:52:19 +02:00
|
|
|
];
|
2024-07-13 23:57:07 +02:00
|
|
|
|
2024-12-27 15:29:16 +01:00
|
|
|
public const ENTITY_HANDLE_BLACKLIST = [
|
|
|
|
|
'REDACTED_FOR_PRIVACY',
|
|
|
|
|
'ANO00-FRNIC',
|
2024-12-27 17:53:24 +01:00
|
|
|
'not applicable',
|
2025-08-11 01:18:43 +02:00
|
|
|
'REDACTED FOR PRIVACY-REGISTRANT',
|
2025-08-11 14:15:43 +02:00
|
|
|
'REDACTED FOR PRIVACY-TECH',
|
2025-08-11 01:18:43 +02:00
|
|
|
'REDACTED FOR PRIVACY',
|
2025-08-11 14:15:43 +02:00
|
|
|
'REDACTED-SIDN',
|
2025-08-11 01:18:43 +02:00
|
|
|
'REGISTRANT',
|
|
|
|
|
'REGISTRAR',
|
2025-08-11 14:15:43 +02:00
|
|
|
'ABUSE-CONTACT',
|
2025-08-11 01:18:43 +02:00
|
|
|
'None',
|
2025-08-11 14:15:43 +02:00
|
|
|
'Private',
|
2024-12-27 17:53:24 +01:00
|
|
|
];
|
|
|
|
|
|
2024-08-02 23:24:52 +02:00
|
|
|
public function __construct(private HttpClientInterface $client,
|
|
|
|
|
private EntityRepository $entityRepository,
|
|
|
|
|
private DomainRepository $domainRepository,
|
|
|
|
|
private DomainEventRepository $domainEventRepository,
|
|
|
|
|
private NameserverRepository $nameserverRepository,
|
|
|
|
|
private NameserverEntityRepository $nameserverEntityRepository,
|
|
|
|
|
private EntityEventRepository $entityEventRepository,
|
|
|
|
|
private DomainEntityRepository $domainEntityRepository,
|
|
|
|
|
private RdapServerRepository $rdapServerRepository,
|
|
|
|
|
private TldRepository $tldRepository,
|
2024-08-04 04:39:05 +02:00
|
|
|
private EntityManagerInterface $em,
|
2024-08-22 18:11:07 +02:00
|
|
|
private LoggerInterface $logger,
|
2024-11-01 00:46:25 +01:00
|
|
|
private StatService $statService,
|
2024-12-08 00:52:03 +01:00
|
|
|
private InfluxdbService $influxService,
|
2024-12-08 13:54:51 +01:00
|
|
|
#[Autowire(param: 'influxdb_enabled')]
|
|
|
|
|
private bool $influxdbEnabled,
|
2024-08-02 23:24:52 +02:00
|
|
|
) {
|
2024-07-13 23:57:07 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-14 11:20:04 +02:00
|
|
|
/**
|
2024-07-25 16:19:57 +02:00
|
|
|
* @throws HttpExceptionInterface
|
|
|
|
|
* @throws TransportExceptionInterface
|
|
|
|
|
* @throws DecodingExceptionInterface
|
2024-07-14 11:20:04 +02:00
|
|
|
*/
|
2024-07-15 00:21:40 +02:00
|
|
|
public function registerDomains(array $domains): void
|
|
|
|
|
{
|
|
|
|
|
foreach ($domains as $fqdn) {
|
2024-07-18 19:13:06 +02:00
|
|
|
$this->registerDomain($fqdn);
|
2024-07-15 00:21:40 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2024-07-25 16:19:57 +02:00
|
|
|
* @throws TransportExceptionInterface
|
2024-08-07 16:21:41 +02:00
|
|
|
* @throws ServerExceptionInterface
|
|
|
|
|
* @throws RedirectionExceptionInterface
|
2024-07-25 16:19:57 +02:00
|
|
|
* @throws DecodingExceptionInterface
|
2024-08-07 16:21:41 +02:00
|
|
|
* @throws ClientExceptionInterface
|
2024-12-07 20:08:29 +01:00
|
|
|
* @throws \Exception
|
2024-07-15 00:21:40 +02:00
|
|
|
*/
|
2024-07-20 09:45:13 +02:00
|
|
|
public function registerDomain(string $fqdn): Domain
|
2024-07-13 23:57:07 +02:00
|
|
|
{
|
2025-05-21 13:14:38 +02:00
|
|
|
$idnDomain = RDAPService::convertToIdn($fqdn);
|
2024-07-19 18:59:21 +02:00
|
|
|
$tld = $this->getTld($idnDomain);
|
2024-07-18 19:13:06 +02:00
|
|
|
|
2024-08-04 04:39:05 +02:00
|
|
|
$this->logger->info('An update request for domain name {idnDomain} is requested.', [
|
|
|
|
|
'idnDomain' => $idnDomain,
|
|
|
|
|
]);
|
|
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
$rdapServer = $this->fetchRdapServer($tld);
|
|
|
|
|
$domain = $this->domainRepository->findOneBy(['ldhName' => $idnDomain]);
|
|
|
|
|
|
|
|
|
|
$rdapData = $this->fetchRdapResponse($rdapServer, $idnDomain, $domain);
|
2024-12-30 17:44:38 +01:00
|
|
|
$this->em->beginTransaction();
|
2024-12-20 22:25:41 +01:00
|
|
|
|
|
|
|
|
if (null === $domain) {
|
|
|
|
|
$domain = $this->initNewDomain($idnDomain, $tld);
|
2024-12-30 17:44:38 +01:00
|
|
|
$this->em->persist($domain);
|
2024-12-20 22:25:41 +01:00
|
|
|
}
|
|
|
|
|
|
2024-12-30 17:40:52 +01:00
|
|
|
$this->em->lock($domain, LockMode::PESSIMISTIC_WRITE);
|
|
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
$this->updateDomainStatus($domain, $rdapData);
|
2024-12-22 23:06:52 +01:00
|
|
|
|
|
|
|
|
if (in_array('free', $domain->getStatus())) {
|
2025-03-01 18:52:19 +01:00
|
|
|
throw new NotFoundHttpException("The domain name $idnDomain is not present in the WHOIS database.");
|
2024-12-22 23:06:52 +01:00
|
|
|
}
|
|
|
|
|
|
2025-08-09 00:01:59 +02:00
|
|
|
$domain
|
|
|
|
|
->setRdapServer($rdapServer)
|
|
|
|
|
->setDelegationSigned(isset($rdapData['secureDNS']['delegationSigned']) && $rdapData['secureDNS']['delegationSigned']);
|
2024-12-30 14:31:31 +01:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
$this->updateDomainHandle($domain, $rdapData);
|
|
|
|
|
|
|
|
|
|
$this->updateDomainEvents($domain, $rdapData);
|
|
|
|
|
$this->updateDomainEntities($domain, $rdapData);
|
|
|
|
|
$this->updateDomainNameservers($domain, $rdapData);
|
2025-08-26 16:18:29 +02:00
|
|
|
$this->updateDomainDsData($domain, $rdapData);
|
2024-12-20 22:25:41 +01:00
|
|
|
|
2025-01-29 20:43:26 +01:00
|
|
|
$domain->setDeleted(false)->updateTimestamps();
|
2024-12-21 21:41:33 +01:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
$this->em->flush();
|
2024-12-30 17:19:07 +01:00
|
|
|
$this->em->commit();
|
2024-12-20 22:25:41 +01:00
|
|
|
|
|
|
|
|
return $domain;
|
|
|
|
|
}
|
|
|
|
|
|
2025-05-19 09:47:21 +02:00
|
|
|
public function getTld(string $domain): Tld
|
2024-12-20 22:25:41 +01:00
|
|
|
{
|
|
|
|
|
if (!str_contains($domain, '.')) {
|
2025-03-04 19:51:46 +01:00
|
|
|
$tldEntity = $this->tldRepository->findOneBy(['tld' => '.']);
|
2025-02-09 01:12:17 +01:00
|
|
|
|
|
|
|
|
if (null == $tldEntity) {
|
2025-03-01 18:52:19 +01:00
|
|
|
throw new NotFoundHttpException("The requested TLD $domain is not yet supported, please try again with another one");
|
2025-02-09 01:12:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $tldEntity;
|
2024-12-20 22:25:41 +01:00
|
|
|
}
|
2025-02-09 01:12:17 +01:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
$lastDotPosition = strrpos($domain, '.');
|
2025-02-09 01:12:17 +01:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
if (false === $lastDotPosition) {
|
|
|
|
|
throw new BadRequestException('Domain must contain at least one dot');
|
|
|
|
|
}
|
2024-12-29 18:06:28 +01:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
$tld = strtolower(idn_to_ascii(substr($domain, $lastDotPosition + 1)));
|
2024-12-29 18:06:28 +01:00
|
|
|
$tldEntity = $this->tldRepository->findOneBy(['tld' => $tld]);
|
2025-02-09 01:12:17 +01:00
|
|
|
|
2024-12-29 18:06:28 +01:00
|
|
|
if (null === $tldEntity) {
|
2025-03-01 18:52:19 +01:00
|
|
|
throw new NotFoundHttpException("The requested TLD $tld is not yet supported, please try again with another one");
|
2024-12-29 18:06:28 +01:00
|
|
|
}
|
2024-12-20 22:25:41 +01:00
|
|
|
|
2024-12-29 18:06:28 +01:00
|
|
|
return $tldEntity;
|
2024-12-20 22:25:41 +01:00
|
|
|
}
|
|
|
|
|
|
2025-05-21 13:14:38 +02:00
|
|
|
public static function convertToIdn(string $fqdn): string
|
2024-12-20 22:25:41 +01:00
|
|
|
{
|
|
|
|
|
return strtolower(idn_to_ascii($fqdn));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function fetchRdapServer(Tld $tld): RdapServer
|
|
|
|
|
{
|
2025-03-01 18:52:19 +01:00
|
|
|
$tldString = $tld->getTld();
|
|
|
|
|
$rdapServer = $this->rdapServerRepository->findOneBy(['tld' => $tldString], ['updatedAt' => 'DESC']);
|
2024-07-18 19:13:06 +02:00
|
|
|
|
2024-08-02 23:24:52 +02:00
|
|
|
if (null === $rdapServer) {
|
2025-03-01 18:52:19 +01:00
|
|
|
throw new NotFoundHttpException("TLD $tldString : Unable to determine which RDAP server to contact");
|
2024-08-02 23:24:52 +02:00
|
|
|
}
|
2024-07-13 23:57:07 +02:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
return $rdapServer;
|
|
|
|
|
}
|
2024-07-25 16:19:57 +02:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
/**
|
|
|
|
|
* @throws TransportExceptionInterface
|
|
|
|
|
* @throws ServerExceptionInterface
|
|
|
|
|
* @throws RedirectionExceptionInterface
|
|
|
|
|
* @throws DecodingExceptionInterface
|
|
|
|
|
* @throws ClientExceptionInterface
|
|
|
|
|
* @throws \Exception
|
|
|
|
|
*/
|
|
|
|
|
private function fetchRdapResponse(RdapServer $rdapServer, string $idnDomain, ?Domain $domain): array
|
|
|
|
|
{
|
2024-08-04 04:39:05 +02:00
|
|
|
$rdapServerUrl = $rdapServer->getUrl();
|
|
|
|
|
$this->logger->notice('An RDAP query to update the domain name {idnDomain} will be made to {server}.', [
|
|
|
|
|
'idnDomain' => $idnDomain,
|
|
|
|
|
'server' => $rdapServerUrl,
|
|
|
|
|
]);
|
|
|
|
|
|
2024-07-14 11:20:04 +02:00
|
|
|
try {
|
2024-08-25 03:31:09 +02:00
|
|
|
$this->statService->incrementStat('stats.rdap_queries.count');
|
2024-08-22 18:11:07 +02:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
$req = $this->client->request('GET', $rdapServerUrl.'domain/'.$idnDomain);
|
2024-08-07 16:29:37 +02:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
return $req->toArray();
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
throw $this->handleRdapException($e, $idnDomain, $domain);
|
2024-12-08 13:54:51 +01:00
|
|
|
} finally {
|
|
|
|
|
if ($this->influxdbEnabled && isset($req)) {
|
2024-12-08 14:19:54 +01:00
|
|
|
$this->influxService->addRdapQueryPoint($rdapServer, $idnDomain, $req->getInfo());
|
2024-12-08 13:54:51 +01:00
|
|
|
}
|
2024-07-14 11:20:04 +02:00
|
|
|
}
|
2024-12-20 22:25:41 +01:00
|
|
|
}
|
2024-07-13 23:57:07 +02:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
/**
|
|
|
|
|
* @throws TransportExceptionInterface
|
|
|
|
|
* @throws \Exception
|
|
|
|
|
*/
|
|
|
|
|
private function handleRdapException(\Exception $e, string $idnDomain, ?Domain $domain): \Exception
|
|
|
|
|
{
|
|
|
|
|
if ($e instanceof ClientException && 404 === $e->getResponse()->getStatusCode()) {
|
|
|
|
|
if (null !== $domain) {
|
|
|
|
|
$this->logger->notice('The domain name {idnDomain} has been deleted from the WHOIS database.', [
|
|
|
|
|
'idnDomain' => $idnDomain,
|
|
|
|
|
]);
|
2024-08-04 04:39:05 +02:00
|
|
|
|
2025-08-26 16:34:37 +02:00
|
|
|
$domain->updateTimestamps();
|
|
|
|
|
|
|
|
|
|
if (!$domain->getDeleted() && $domain->getUpdatedAt() !== $domain->getCreatedAt()) {
|
|
|
|
|
$this->em->persist((new DomainStatus())
|
|
|
|
|
->setDomain($domain)
|
|
|
|
|
->setCreatedAt($domain->getUpdatedAt())
|
|
|
|
|
->setDate($domain->getUpdatedAt())
|
|
|
|
|
->setAddStatus([])
|
|
|
|
|
->setDeleteStatus($domain->getStatus()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$domain->setDeleted(true);
|
2024-12-20 22:25:41 +01:00
|
|
|
$this->em->persist($domain);
|
|
|
|
|
$this->em->flush();
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-01 18:52:19 +01:00
|
|
|
throw new NotFoundHttpException("The domain name $idnDomain is not present in the WHOIS database.");
|
2024-08-02 23:24:52 +02:00
|
|
|
}
|
2024-08-04 04:39:05 +02:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
return $e;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function initNewDomain(string $idnDomain, Tld $tld): Domain
|
|
|
|
|
{
|
|
|
|
|
$domain = new Domain();
|
|
|
|
|
|
|
|
|
|
$this->logger->info('The domain name {idnDomain} was not known to this Domain Watchdog instance.', [
|
|
|
|
|
'idnDomain' => $idnDomain,
|
|
|
|
|
]);
|
2024-07-13 23:57:07 +02:00
|
|
|
|
2024-12-21 21:41:33 +01:00
|
|
|
return $domain->setTld($tld)->setLdhName($idnDomain)->setDeleted(false);
|
2024-12-20 22:25:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function updateDomainStatus(Domain $domain, array $rdapData): void
|
|
|
|
|
{
|
2024-12-30 14:31:31 +01:00
|
|
|
if (isset($rdapData['status'])) {
|
2024-12-20 22:25:41 +01:00
|
|
|
$status = array_unique($rdapData['status']);
|
2024-12-18 19:09:54 +01:00
|
|
|
$addedStatus = array_diff($status, $domain->getStatus());
|
|
|
|
|
$deletedStatus = array_diff($domain->getStatus(), $status);
|
|
|
|
|
$domain->setStatus($status);
|
2024-12-12 22:56:35 +01:00
|
|
|
|
|
|
|
|
if (count($addedStatus) > 0 || count($deletedStatus) > 0) {
|
|
|
|
|
$this->em->persist($domain);
|
|
|
|
|
|
2024-12-22 20:42:22 +01:00
|
|
|
if ($domain->getUpdatedAt() !== $domain->getCreatedAt()) {
|
2024-12-13 16:25:53 +01:00
|
|
|
$this->em->persist((new DomainStatus())
|
|
|
|
|
->setDomain($domain)
|
2024-12-29 17:06:52 +01:00
|
|
|
->setCreatedAt(new \DateTimeImmutable('now'))
|
|
|
|
|
->setDate($domain->getUpdatedAt())
|
2024-12-13 16:25:53 +01:00
|
|
|
->setAddStatus($addedStatus)
|
|
|
|
|
->setDeleteStatus($deletedStatus));
|
|
|
|
|
}
|
2024-12-12 22:56:35 +01:00
|
|
|
}
|
2024-08-04 14:45:27 +02:00
|
|
|
} else {
|
|
|
|
|
$this->logger->warning('The domain name {idnDomain} has no WHOIS status.', [
|
2024-12-20 22:25:41 +01:00
|
|
|
'idnDomain' => $domain->getLdhName(),
|
2024-08-04 14:45:27 +02:00
|
|
|
]);
|
2024-08-02 23:24:52 +02:00
|
|
|
}
|
2024-12-20 22:25:41 +01:00
|
|
|
}
|
2024-08-04 14:45:27 +02:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
private function updateDomainHandle(Domain $domain, array $rdapData): void
|
|
|
|
|
{
|
2024-12-30 14:31:31 +01:00
|
|
|
if (isset($rdapData['handle'])) {
|
2024-12-20 22:25:41 +01:00
|
|
|
$domain->setHandle($rdapData['handle']);
|
2024-08-04 14:45:27 +02:00
|
|
|
} else {
|
|
|
|
|
$this->logger->warning('The domain name {idnDomain} has no handle key.', [
|
2024-12-20 22:25:41 +01:00
|
|
|
'idnDomain' => $domain->getLdhName(),
|
2024-08-04 14:45:27 +02:00
|
|
|
]);
|
2024-08-02 23:24:52 +02:00
|
|
|
}
|
2024-12-20 22:25:41 +01:00
|
|
|
}
|
2024-07-23 01:34:59 +02:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
/**
|
|
|
|
|
* @throws \DateMalformedStringException
|
|
|
|
|
* @throws \Exception
|
|
|
|
|
*/
|
|
|
|
|
private function updateDomainEvents(Domain $domain, array $rdapData): void
|
|
|
|
|
{
|
2024-09-01 21:26:07 +02:00
|
|
|
foreach ($domain->getEvents()->getIterator() as $event) {
|
|
|
|
|
$event->setDeleted(true);
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-30 14:31:31 +01:00
|
|
|
if (isset($rdapData['events']) && is_array($rdapData['events'])) {
|
2024-12-20 22:25:41 +01:00
|
|
|
foreach ($rdapData['events'] as $rdapEvent) {
|
2024-09-22 18:48:32 +02:00
|
|
|
if ($rdapEvent['eventAction'] === EventAction::LastUpdateOfRDAPDatabase->value) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-09-22 19:49:35 +02:00
|
|
|
|
2024-09-22 18:48:32 +02:00
|
|
|
$event = $this->domainEventRepository->findOneBy([
|
|
|
|
|
'action' => $rdapEvent['eventAction'],
|
|
|
|
|
'date' => new \DateTimeImmutable($rdapEvent['eventDate']),
|
|
|
|
|
'domain' => $domain,
|
|
|
|
|
]);
|
2024-09-22 19:49:35 +02:00
|
|
|
|
2024-09-22 18:48:32 +02:00
|
|
|
if (null === $event) {
|
|
|
|
|
$event = new DomainEvent();
|
|
|
|
|
}
|
2024-12-20 22:25:41 +01:00
|
|
|
|
2024-09-22 18:48:32 +02:00
|
|
|
$domain->addEvent($event
|
|
|
|
|
->setAction($rdapEvent['eventAction'])
|
|
|
|
|
->setDate(new \DateTimeImmutable($rdapEvent['eventDate']))
|
2024-12-20 22:25:41 +01:00
|
|
|
->setDeleted(false));
|
2024-11-25 22:16:05 +01:00
|
|
|
|
|
|
|
|
$this->em->persist($domain);
|
2024-08-02 23:24:52 +02:00
|
|
|
}
|
2024-07-13 23:57:07 +02:00
|
|
|
}
|
2024-12-20 22:25:41 +01:00
|
|
|
}
|
2024-07-13 23:57:07 +02:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
/**
|
|
|
|
|
* @throws \DateMalformedStringException
|
2024-12-22 23:48:37 +01:00
|
|
|
* @throws \Exception
|
2024-12-20 22:25:41 +01:00
|
|
|
*/
|
|
|
|
|
private function updateDomainEntities(Domain $domain, array $rdapData): void
|
|
|
|
|
{
|
2024-09-04 18:06:02 +02:00
|
|
|
foreach ($domain->getDomainEntities()->getIterator() as $domainEntity) {
|
|
|
|
|
$domainEntity->setDeleted(true);
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-09 00:01:59 +02:00
|
|
|
if (!isset($rdapData['entities']) || !is_array($rdapData['entities'])) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-12-20 22:02:11 +01:00
|
|
|
|
2025-08-09 00:01:59 +02:00
|
|
|
foreach ($rdapData['entities'] as $rdapEntity) {
|
|
|
|
|
$roles = $this->extractEntityRoles($rdapData['entities'], $rdapEntity);
|
|
|
|
|
$entity = $this->registerEntity($rdapEntity, $roles, $domain->getLdhName(), $domain->getTld());
|
2024-12-20 22:02:11 +01:00
|
|
|
|
2025-08-09 00:01:59 +02:00
|
|
|
$domainEntity = $this->domainEntityRepository->findOneBy([
|
|
|
|
|
'domain' => $domain,
|
|
|
|
|
'entity' => $entity,
|
|
|
|
|
]);
|
2024-07-23 03:13:51 +02:00
|
|
|
|
2025-08-09 00:01:59 +02:00
|
|
|
if (null === $domainEntity) {
|
|
|
|
|
$domainEntity = new DomainEntity();
|
2024-07-13 23:57:07 +02:00
|
|
|
}
|
2025-08-09 00:01:59 +02:00
|
|
|
|
|
|
|
|
$domain->addDomainEntity($domainEntity
|
|
|
|
|
->setDomain($domain)
|
|
|
|
|
->setEntity($entity)
|
|
|
|
|
->setRoles($roles)
|
|
|
|
|
->setDeleted(false));
|
|
|
|
|
|
|
|
|
|
$this->em->persist($domainEntity);
|
|
|
|
|
$this->em->flush();
|
2024-07-23 03:13:51 +02:00
|
|
|
}
|
2024-12-20 22:25:41 +01:00
|
|
|
}
|
2024-07-13 23:57:07 +02:00
|
|
|
|
2024-12-23 15:14:23 +01:00
|
|
|
/**
|
|
|
|
|
* @throws \DateMalformedStringException
|
|
|
|
|
*/
|
2024-12-20 22:25:41 +01:00
|
|
|
private function updateDomainNameservers(Domain $domain, array $rdapData): void
|
|
|
|
|
{
|
|
|
|
|
if (array_key_exists('nameservers', $rdapData) && is_array($rdapData['nameservers'])) {
|
2024-08-17 18:22:24 +02:00
|
|
|
$domain->getNameservers()->clear();
|
2024-12-07 14:16:56 +01:00
|
|
|
$this->em->persist($domain);
|
2024-08-17 18:22:24 +02:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
foreach ($rdapData['nameservers'] as $rdapNameserver) {
|
|
|
|
|
$nameserver = $this->fetchOrCreateNameserver($rdapNameserver, $domain);
|
2025-02-18 01:29:29 +01:00
|
|
|
$this->updateNameserverEntities($nameserver, $rdapNameserver, $domain->getTld());
|
2024-07-23 03:13:51 +02:00
|
|
|
|
2024-11-02 16:25:01 +01:00
|
|
|
if (!$domain->getNameservers()->contains($nameserver)) {
|
|
|
|
|
$domain->addNameserver($nameserver);
|
|
|
|
|
}
|
2024-07-23 03:13:51 +02:00
|
|
|
}
|
2024-08-04 14:45:27 +02:00
|
|
|
} else {
|
|
|
|
|
$this->logger->warning('The domain name {idnDomain} has no nameservers.', [
|
2024-12-20 22:25:41 +01:00
|
|
|
'idnDomain' => $domain->getLdhName(),
|
2024-08-04 14:45:27 +02:00
|
|
|
]);
|
2024-07-13 23:57:07 +02:00
|
|
|
}
|
2024-12-20 22:25:41 +01:00
|
|
|
}
|
2024-07-13 23:57:07 +02:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
private function fetchOrCreateNameserver(array $rdapNameserver, Domain $domain): Nameserver
|
|
|
|
|
{
|
|
|
|
|
$nameserver = $this->nameserverRepository->findOneBy([
|
|
|
|
|
'ldhName' => strtolower($rdapNameserver['ldhName']),
|
|
|
|
|
]);
|
2024-07-13 23:57:07 +02:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
$existingDomainNS = $domain->getNameservers()->findFirst(fn (int $key, Nameserver $ns) => $ns->getLdhName() === $rdapNameserver['ldhName']);
|
|
|
|
|
|
|
|
|
|
if (null !== $existingDomainNS) {
|
|
|
|
|
return $existingDomainNS;
|
|
|
|
|
} elseif (null === $nameserver) {
|
|
|
|
|
$nameserver = new Nameserver();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$nameserver->setLdhName($rdapNameserver['ldhName']);
|
|
|
|
|
|
|
|
|
|
return $nameserver;
|
2024-07-13 23:57:07 +02:00
|
|
|
}
|
|
|
|
|
|
2024-12-22 23:48:37 +01:00
|
|
|
/**
|
|
|
|
|
* @throws \DateMalformedStringException
|
|
|
|
|
*/
|
2025-02-18 01:29:29 +01:00
|
|
|
private function updateNameserverEntities(Nameserver $nameserver, array $rdapNameserver, Tld $tld): void
|
2024-07-13 23:57:07 +02:00
|
|
|
{
|
2024-12-30 14:31:31 +01:00
|
|
|
if (!isset($rdapNameserver['entities']) || !is_array($rdapNameserver['entities'])) {
|
2024-12-20 22:25:41 +01:00
|
|
|
return;
|
2024-12-20 17:43:35 +01:00
|
|
|
}
|
2024-12-20 22:25:41 +01:00
|
|
|
|
|
|
|
|
foreach ($rdapNameserver['entities'] as $rdapEntity) {
|
|
|
|
|
$roles = $this->extractEntityRoles($rdapNameserver['entities'], $rdapEntity);
|
2025-02-18 01:29:29 +01:00
|
|
|
$entity = $this->registerEntity($rdapEntity, $roles, $nameserver->getLdhName(), $tld);
|
2024-12-20 22:25:41 +01:00
|
|
|
|
|
|
|
|
$nameserverEntity = $this->nameserverEntityRepository->findOneBy([
|
|
|
|
|
'nameserver' => $nameserver,
|
|
|
|
|
'entity' => $entity,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
if (null === $nameserverEntity) {
|
|
|
|
|
$nameserverEntity = new NameserverEntity();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$nameserver->addNameserverEntity($nameserverEntity
|
|
|
|
|
->setNameserver($nameserver)
|
|
|
|
|
->setEntity($entity)
|
|
|
|
|
->setStatus(array_unique($rdapNameserver['status']))
|
|
|
|
|
->setRoles($roles));
|
2024-12-29 22:31:40 +01:00
|
|
|
|
|
|
|
|
$this->em->persist($nameserverEntity);
|
|
|
|
|
$this->em->flush();
|
2024-07-13 23:57:07 +02:00
|
|
|
}
|
2024-12-20 22:25:41 +01:00
|
|
|
}
|
2024-07-19 18:59:21 +02:00
|
|
|
|
2024-12-20 22:25:41 +01:00
|
|
|
private function extractEntityRoles(array $entities, array $targetEntity): array
|
|
|
|
|
{
|
|
|
|
|
$roles = array_map(
|
|
|
|
|
fn ($e) => $e['roles'],
|
|
|
|
|
array_filter(
|
|
|
|
|
$entities,
|
2024-12-30 14:31:31 +01:00
|
|
|
fn ($e) => isset($targetEntity['handle']) && isset($e['handle'])
|
2024-12-22 23:48:37 +01:00
|
|
|
? $targetEntity['handle'] === $e['handle']
|
|
|
|
|
: (
|
2024-12-30 14:31:31 +01:00
|
|
|
isset($targetEntity['vcardArray']) && isset($e['vcardArray'])
|
|
|
|
|
? $targetEntity['vcardArray'] === $e['vcardArray']
|
|
|
|
|
: $targetEntity === $e
|
2024-12-22 23:48:37 +01:00
|
|
|
)
|
2024-12-20 22:25:41 +01:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (count($roles) !== count($roles, COUNT_RECURSIVE)) {
|
|
|
|
|
$roles = array_merge(...$roles);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $roles;
|
2024-07-13 23:57:07 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-14 11:20:04 +02:00
|
|
|
/**
|
2024-12-20 22:25:41 +01:00
|
|
|
* @throws \DateMalformedStringException
|
2024-08-02 23:24:52 +02:00
|
|
|
* @throws \Exception
|
2024-07-14 11:20:04 +02:00
|
|
|
*/
|
2025-02-18 01:29:29 +01:00
|
|
|
private function registerEntity(array $rdapEntity, array $roles, string $domain, Tld $tld): Entity
|
2024-07-13 23:57:07 +02:00
|
|
|
{
|
2024-12-27 17:53:24 +01:00
|
|
|
/*
|
|
|
|
|
* If the RDAP server transmits the entity's IANA number, it is used as a priority to identify the entity
|
|
|
|
|
*/
|
2024-12-27 18:21:07 +01:00
|
|
|
$isIANAid = false;
|
2024-12-30 14:31:31 +01:00
|
|
|
if (isset($rdapEntity['publicIds'])) {
|
2024-12-27 17:53:24 +01:00
|
|
|
foreach ($rdapEntity['publicIds'] as $publicId) {
|
2024-12-30 14:31:31 +01:00
|
|
|
if ('IANA Registrar ID' === $publicId['type'] && isset($publicId['identifier']) && '' !== $publicId['identifier']) {
|
2024-12-27 17:53:24 +01:00
|
|
|
$rdapEntity['handle'] = $publicId['identifier'];
|
2024-12-27 18:21:07 +01:00
|
|
|
$isIANAid = true;
|
2024-12-27 17:53:24 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If there is no number to identify the entity, one is generated from the domain name and the roles associated with this entity
|
|
|
|
|
*/
|
2024-12-30 14:31:31 +01:00
|
|
|
if (!isset($rdapEntity['handle']) || '' === $rdapEntity['handle'] || in_array($rdapEntity['handle'], self::ENTITY_HANDLE_BLACKLIST)) {
|
2024-12-29 13:47:19 +01:00
|
|
|
sort($roles);
|
|
|
|
|
$rdapEntity['handle'] = 'DW-FAKEHANDLE-'.$domain.'-'.implode(',', $roles);
|
2024-12-20 19:41:48 +01:00
|
|
|
|
2024-12-20 22:02:11 +01:00
|
|
|
$this->logger->warning('The entity {handle} has no handle key.', [
|
|
|
|
|
'handle' => $rdapEntity['handle'],
|
|
|
|
|
]);
|
2024-12-20 19:41:48 +01:00
|
|
|
}
|
2024-12-20 17:43:35 +01:00
|
|
|
|
2024-07-13 23:57:07 +02:00
|
|
|
$entity = $this->entityRepository->findOneBy([
|
2024-08-02 23:24:52 +02:00
|
|
|
'handle' => $rdapEntity['handle'],
|
2025-09-10 21:35:43 +02:00
|
|
|
'tld' => $tld,
|
2024-07-13 23:57:07 +02:00
|
|
|
]);
|
|
|
|
|
|
2025-09-10 21:39:17 +02:00
|
|
|
if (null === $entity) {
|
2025-09-10 21:35:43 +02:00
|
|
|
$entity = $this->entityRepository->findOneBy([
|
|
|
|
|
'handle' => $rdapEntity['handle'],
|
|
|
|
|
'tld' => null,
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-02 23:24:52 +02:00
|
|
|
if (null === $entity) {
|
2025-09-10 21:35:43 +02:00
|
|
|
$entity = (new Entity())->setTld($tld);
|
2024-08-15 19:54:36 +02:00
|
|
|
|
2024-08-04 14:45:27 +02:00
|
|
|
$this->logger->info('The entity {handle} was not known to this Domain Watchdog instance.', [
|
|
|
|
|
'handle' => $rdapEntity['handle'],
|
|
|
|
|
]);
|
2024-08-02 23:24:52 +02:00
|
|
|
}
|
2024-07-13 23:57:07 +02:00
|
|
|
|
2025-09-10 21:35:43 +02:00
|
|
|
$entity->setHandle($rdapEntity['handle']);
|
2024-07-13 23:57:07 +02:00
|
|
|
|
2025-09-10 22:17:19 +02:00
|
|
|
if (isset($rdapEntity['remarks']) && is_array($rdapEntity['remarks']) && null === $entity->getIanaAccreditation()) {
|
2024-12-28 19:26:25 +01:00
|
|
|
$entity->setRemarks($rdapEntity['remarks']);
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-10 22:17:19 +02:00
|
|
|
if (isset($rdapEntity['vcardArray']) && null === $entity->getIanaAccreditation()) {
|
2024-07-23 03:05:35 +02:00
|
|
|
if (empty($entity->getJCard())) {
|
2025-08-11 00:39:44 +02:00
|
|
|
if (!array_key_exists('elements', $rdapEntity['vcardArray'])) {
|
|
|
|
|
$entity->setJCard($rdapEntity['vcardArray']);
|
|
|
|
|
} else {
|
|
|
|
|
/*
|
|
|
|
|
* UZ registry
|
|
|
|
|
*/
|
|
|
|
|
$entity->setJCard([
|
|
|
|
|
'vcard',
|
|
|
|
|
$rdapEntity['vcardArray']['elements'],
|
|
|
|
|
]);
|
|
|
|
|
}
|
2024-07-23 03:05:35 +02:00
|
|
|
} else {
|
|
|
|
|
$properties = [];
|
2025-08-11 00:39:44 +02:00
|
|
|
if (!array_key_exists('elements', $rdapEntity['vcardArray'])) {
|
|
|
|
|
foreach ($rdapEntity['vcardArray'][1] as $prop) {
|
|
|
|
|
$properties[$prop[0]] = $prop;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/*
|
|
|
|
|
* UZ registry
|
|
|
|
|
*/
|
|
|
|
|
foreach ($rdapEntity['vcardArray']['elements'] as $prop) {
|
|
|
|
|
$properties[$prop[0]] = $prop;
|
|
|
|
|
}
|
2024-07-23 03:05:35 +02:00
|
|
|
}
|
|
|
|
|
foreach ($entity->getJCard()[1] as $prop) {
|
|
|
|
|
$properties[$prop[0]] = $prop;
|
|
|
|
|
}
|
2024-08-02 23:24:52 +02:00
|
|
|
$entity->setJCard(['vcard', array_values($properties)]);
|
2024-07-13 23:57:07 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-10 22:17:19 +02:00
|
|
|
if ($isIANAid || !isset($rdapEntity['events']) || null !== $entity->getIanaAccreditation()) {
|
2024-08-02 23:24:52 +02:00
|
|
|
return $entity;
|
|
|
|
|
}
|
2024-07-13 23:57:07 +02:00
|
|
|
|
2024-09-01 21:26:07 +02:00
|
|
|
/** @var EntityEvent $event */
|
|
|
|
|
foreach ($entity->getEvents()->getIterator() as $event) {
|
|
|
|
|
$event->setDeleted(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->em->persist($entity);
|
|
|
|
|
|
2024-07-13 23:57:07 +02:00
|
|
|
foreach ($rdapEntity['events'] as $rdapEntityEvent) {
|
2024-08-02 23:24:52 +02:00
|
|
|
$eventAction = $rdapEntityEvent['eventAction'];
|
|
|
|
|
if ($eventAction === EventAction::LastChanged->value || $eventAction === EventAction::LastUpdateOfRDAPDatabase->value) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-07-13 23:57:07 +02:00
|
|
|
$event = $this->entityEventRepository->findOneBy([
|
2024-08-02 23:24:52 +02:00
|
|
|
'action' => $rdapEntityEvent['eventAction'],
|
|
|
|
|
'date' => new \DateTimeImmutable($rdapEntityEvent['eventDate']),
|
2024-07-13 23:57:07 +02:00
|
|
|
]);
|
|
|
|
|
|
2024-08-02 23:24:52 +02:00
|
|
|
if (null !== $event) {
|
2024-09-01 21:26:07 +02:00
|
|
|
$event->setDeleted(false);
|
2024-08-02 23:24:52 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
2024-07-13 23:57:07 +02:00
|
|
|
$entity->addEvent(
|
|
|
|
|
(new EntityEvent())
|
|
|
|
|
->setEntity($entity)
|
2024-08-02 23:24:52 +02:00
|
|
|
->setAction($rdapEntityEvent['eventAction'])
|
2024-09-01 21:26:07 +02:00
|
|
|
->setDate(new \DateTimeImmutable($rdapEntityEvent['eventDate']))
|
|
|
|
|
->setDeleted(false));
|
2024-07-13 23:57:07 +02:00
|
|
|
}
|
2024-08-02 23:24:52 +02:00
|
|
|
|
2024-12-20 19:41:48 +01:00
|
|
|
$this->em->persist($entity);
|
2024-12-20 22:02:11 +01:00
|
|
|
$this->em->flush();
|
2024-12-20 19:41:48 +01:00
|
|
|
|
2024-07-13 23:57:07 +02:00
|
|
|
return $entity;
|
|
|
|
|
}
|
2024-07-18 19:13:06 +02:00
|
|
|
|
2025-08-26 16:18:29 +02:00
|
|
|
private function updateDomainDsData(Domain $domain, array $rdapData): void
|
|
|
|
|
{
|
|
|
|
|
$domain->getDnsKey()->clear();
|
|
|
|
|
$this->em->persist($domain);
|
|
|
|
|
$this->em->flush();
|
|
|
|
|
|
|
|
|
|
if (array_key_exists('secureDNS', $rdapData) && array_key_exists('dsData', $rdapData['secureDNS']) && is_array($rdapData['secureDNS']['dsData'])) {
|
|
|
|
|
foreach ($rdapData['secureDNS']['dsData'] as $rdapDsData) {
|
|
|
|
|
$dsData = new DnsKey();
|
|
|
|
|
if (array_key_exists('keyTag', $rdapDsData)) {
|
|
|
|
|
$dsData->setKeyTag(pack('n', $rdapDsData['keyTag']));
|
|
|
|
|
}
|
|
|
|
|
if (array_key_exists('algorithm', $rdapDsData)) {
|
|
|
|
|
$dsData->setAlgorithm(Algorithm::from($rdapDsData['algorithm']));
|
|
|
|
|
}
|
|
|
|
|
if (array_key_exists('digest', $rdapDsData)) {
|
|
|
|
|
$blob = hex2bin($rdapDsData['digest']);
|
|
|
|
|
if (false === $blob) {
|
|
|
|
|
throw new ServiceUnavailableHttpException('DNSSEC digest is not a valid hexadecimal value.');
|
|
|
|
|
}
|
|
|
|
|
$dsData->setDigest($blob);
|
|
|
|
|
}
|
|
|
|
|
if (array_key_exists('digestType', $rdapDsData)) {
|
|
|
|
|
$dsData->setDigestType(DigestType::from($rdapDsData['digestType']));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$domain->addDnsKey($dsData);
|
|
|
|
|
$this->em->persist($dsData);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$this->logger->warning('The domain name {idnDomain} has no DS record.', [
|
|
|
|
|
'idnDomain' => $domain->getLdhName(),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-19 01:17:33 +02:00
|
|
|
/**
|
|
|
|
|
* @throws TransportExceptionInterface
|
|
|
|
|
* @throws ServerExceptionInterface
|
|
|
|
|
* @throws RedirectionExceptionInterface
|
|
|
|
|
* @throws DecodingExceptionInterface
|
|
|
|
|
* @throws ClientExceptionInterface
|
2024-07-19 18:59:21 +02:00
|
|
|
* @throws ORMException
|
2024-07-19 01:17:33 +02:00
|
|
|
*/
|
2024-08-19 18:06:11 +02:00
|
|
|
public function updateRDAPServersFromIANA(): void
|
2024-07-18 19:13:06 +02:00
|
|
|
{
|
2024-08-19 18:06:11 +02:00
|
|
|
$this->logger->info('Start of update the RDAP server list from IANA.');
|
2024-08-04 04:39:05 +02:00
|
|
|
|
2024-07-18 19:13:06 +02:00
|
|
|
$dnsRoot = $this->client->request(
|
|
|
|
|
'GET', 'https://data.iana.org/rdap/dns.json'
|
|
|
|
|
)->toArray();
|
|
|
|
|
|
2024-08-19 18:06:11 +02:00
|
|
|
$this->updateRDAPServers($dnsRoot);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @throws ORMException
|
2024-08-19 18:38:14 +02:00
|
|
|
* @throws \Exception
|
2024-08-19 18:06:11 +02:00
|
|
|
*/
|
|
|
|
|
private function updateRDAPServers(array $dnsRoot): void
|
|
|
|
|
{
|
2024-07-18 19:13:06 +02:00
|
|
|
foreach ($dnsRoot['services'] as $service) {
|
|
|
|
|
foreach ($service[0] as $tld) {
|
2025-09-10 21:35:43 +02:00
|
|
|
if ('.' === $tld && null === $this->tldRepository->findOneBy(['tld' => $tld])) {
|
|
|
|
|
$this->em->persist((new Tld())->setTld('.')->setType(TldType::root));
|
2025-02-09 01:12:17 +01:00
|
|
|
$this->em->flush();
|
2024-08-02 23:24:52 +02:00
|
|
|
}
|
2025-02-09 01:12:17 +01:00
|
|
|
|
2025-09-10 21:35:43 +02:00
|
|
|
$tldEntity = $this->tldRepository->findOneBy(['tld' => $tld]);
|
2025-09-10 21:39:17 +02:00
|
|
|
if (null === $tldEntity) {
|
2025-09-10 21:35:43 +02:00
|
|
|
$tldEntity = (new Tld())->setTld($tld)->setType(TldType::gTLD);
|
|
|
|
|
$this->em->persist($tldEntity);
|
|
|
|
|
}
|
2024-12-20 17:43:35 +01:00
|
|
|
|
2024-07-18 19:13:06 +02:00
|
|
|
foreach ($service[1] as $rdapServerUrl) {
|
2025-09-10 21:35:43 +02:00
|
|
|
$server = $this->rdapServerRepository->findOneBy(['tld' => $tldEntity->getTld(), 'url' => $rdapServerUrl]);
|
2025-02-09 01:12:17 +01:00
|
|
|
|
2024-08-02 23:24:52 +02:00
|
|
|
if (null === $server) {
|
|
|
|
|
$server = new RdapServer();
|
|
|
|
|
}
|
2025-02-09 01:12:17 +01:00
|
|
|
|
2024-08-19 18:38:14 +02:00
|
|
|
$server
|
2025-09-10 21:35:43 +02:00
|
|
|
->setTld($tldEntity)
|
2024-08-19 18:38:14 +02:00
|
|
|
->setUrl($rdapServerUrl)
|
2024-12-30 14:31:31 +01:00
|
|
|
->setUpdatedAt(new \DateTimeImmutable($dnsRoot['publication'] ?? 'now'));
|
2024-07-18 19:13:06 +02:00
|
|
|
|
|
|
|
|
$this->em->persist($server);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$this->em->flush();
|
|
|
|
|
}
|
2024-07-19 18:59:21 +02:00
|
|
|
|
2024-08-19 18:38:14 +02:00
|
|
|
/**
|
|
|
|
|
* @throws ORMException
|
|
|
|
|
*/
|
|
|
|
|
public function updateRDAPServersFromFile(string $fileName): void
|
|
|
|
|
{
|
|
|
|
|
if (!file_exists($fileName)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->logger->info('Start of update the RDAP server list from custom config file.');
|
|
|
|
|
$this->updateRDAPServers(Yaml::parseFile($fileName));
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-19 18:59:21 +02:00
|
|
|
/**
|
|
|
|
|
* @throws TransportExceptionInterface
|
|
|
|
|
* @throws ServerExceptionInterface
|
|
|
|
|
* @throws RedirectionExceptionInterface
|
|
|
|
|
* @throws ClientExceptionInterface
|
|
|
|
|
*/
|
|
|
|
|
public function updateTldListIANA(): void
|
|
|
|
|
{
|
2024-08-04 04:39:05 +02:00
|
|
|
$this->logger->info('Start of retrieval of the list of TLDs according to IANA.');
|
2024-07-19 18:59:21 +02:00
|
|
|
$tldList = array_map(
|
2024-08-02 23:24:52 +02:00
|
|
|
fn ($tld) => strtolower($tld),
|
2024-07-19 18:59:21 +02:00
|
|
|
explode(PHP_EOL,
|
|
|
|
|
$this->client->request(
|
|
|
|
|
'GET', 'https://data.iana.org/TLD/tlds-alpha-by-domain.txt'
|
|
|
|
|
)->getContent()
|
|
|
|
|
));
|
|
|
|
|
array_shift($tldList);
|
|
|
|
|
|
2024-07-24 18:52:19 +02:00
|
|
|
foreach ($tldList as $tld) {
|
2024-08-02 23:24:52 +02:00
|
|
|
if ('' === $tld) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-07-25 17:03:00 +02:00
|
|
|
|
2024-07-24 18:52:19 +02:00
|
|
|
$tldEntity = $this->tldRepository->findOneBy(['tld' => $tld]);
|
2024-07-25 17:03:00 +02:00
|
|
|
|
2024-08-02 23:24:52 +02:00
|
|
|
if (null === $tldEntity) {
|
2024-07-25 17:03:00 +02:00
|
|
|
$tldEntity = new Tld();
|
|
|
|
|
$tldEntity->setTld($tld);
|
2024-08-04 04:39:05 +02:00
|
|
|
|
|
|
|
|
$this->logger->notice('New TLD detected according to IANA ({tld}).', [
|
|
|
|
|
'tld' => $tld,
|
|
|
|
|
]);
|
2024-07-25 17:03:00 +02:00
|
|
|
}
|
2024-07-24 18:52:19 +02:00
|
|
|
|
2024-07-24 22:17:54 +02:00
|
|
|
$type = $this->getTldType($tld);
|
2024-07-25 17:03:00 +02:00
|
|
|
|
2024-08-02 23:24:52 +02:00
|
|
|
if (null !== $type) {
|
2024-07-24 22:17:54 +02:00
|
|
|
$tldEntity->setType($type);
|
2024-08-02 23:24:52 +02:00
|
|
|
} elseif (null === $tldEntity->isContractTerminated()) { // ICANN managed, must be a ccTLD
|
2024-07-24 22:17:54 +02:00
|
|
|
$tldEntity->setType(TldType::ccTLD);
|
|
|
|
|
} else {
|
|
|
|
|
$tldEntity->setType(TldType::gTLD);
|
2024-07-24 21:58:45 +02:00
|
|
|
}
|
2024-07-24 18:52:19 +02:00
|
|
|
|
|
|
|
|
$this->em->persist($tldEntity);
|
2024-07-19 18:59:21 +02:00
|
|
|
}
|
|
|
|
|
$this->em->flush();
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-10 21:35:43 +02:00
|
|
|
/**
|
|
|
|
|
* @throws TransportExceptionInterface
|
|
|
|
|
* @throws ServerExceptionInterface
|
|
|
|
|
* @throws RedirectionExceptionInterface
|
|
|
|
|
* @throws DecodingExceptionInterface
|
|
|
|
|
* @throws ClientExceptionInterface
|
|
|
|
|
* @throws \Exception
|
|
|
|
|
*/
|
|
|
|
|
public function updateRegistrarListIANA(): void
|
|
|
|
|
{
|
|
|
|
|
$this->logger->info('Start of retrieval of the list of Registrar IDs according to IANA.');
|
|
|
|
|
$registrarList = $this->client->request(
|
|
|
|
|
'GET', 'https://www.iana.org/assignments/registrar-ids/registrar-ids.xml'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$data = new \SimpleXMLElement($registrarList->getContent());
|
|
|
|
|
|
2025-09-10 21:39:17 +02:00
|
|
|
foreach ($data->registry->record as $registrar) {
|
2025-09-10 21:35:43 +02:00
|
|
|
$entity = $this->entityRepository->findOneBy(['handle' => $registrar->value, 'tld' => null]);
|
2025-09-10 21:39:17 +02:00
|
|
|
if (null === $entity) {
|
|
|
|
|
$entity = new Entity();
|
|
|
|
|
}
|
2025-09-10 21:35:43 +02:00
|
|
|
$entity
|
2025-09-10 22:17:19 +02:00
|
|
|
->setHandle($registrar->value)
|
|
|
|
|
->setTld(null)
|
|
|
|
|
->setJCard(['vcard', [['version', [], 'text', '4.0'], ['fn', [], 'text', $registrar->name]]])
|
2025-09-10 21:35:43 +02:00
|
|
|
->setRemarks(null);
|
|
|
|
|
|
2025-09-10 22:17:19 +02:00
|
|
|
if (null === $entity->getIanaAccreditation()) {
|
|
|
|
|
$entity->setIanaAccreditation(new IanaAccreditation());
|
|
|
|
|
}
|
|
|
|
|
$entity->getIanaAccreditation()
|
|
|
|
|
->setRegistrarName($registrar->name)
|
|
|
|
|
->setStatus(RegistrarStatus::from($registrar->status))
|
|
|
|
|
->setRdapBaseUrl($registrar->rdapurl->count() ? ($registrar->rdapurl->server) : null)
|
|
|
|
|
->setUpdated(null !== $registrar->attributes()->updated ? new \DateTimeImmutable($registrar->attributes()->updated) : null)
|
|
|
|
|
->setDate(null !== $registrar->attributes()->date ? new \DateTimeImmutable($registrar->attributes()->date) : null);
|
|
|
|
|
|
2025-09-10 21:35:43 +02:00
|
|
|
$this->em->persist($entity);
|
|
|
|
|
}
|
|
|
|
|
$this->em->flush();
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-24 18:52:19 +02:00
|
|
|
private function getTldType(string $tld): ?TldType
|
|
|
|
|
{
|
2025-08-27 14:47:33 +02:00
|
|
|
if (in_array(strtolower($tld), self::ISO_TLD_EXCEPTION)) {
|
2024-08-02 23:24:52 +02:00
|
|
|
return TldType::ccTLD;
|
|
|
|
|
}
|
|
|
|
|
if (in_array(strtolower($tld), self::INFRA_TLD)) {
|
|
|
|
|
return TldType::iTLD;
|
|
|
|
|
}
|
|
|
|
|
if (in_array(strtolower($tld), self::SPONSORED_TLD)) {
|
|
|
|
|
return TldType::sTLD;
|
|
|
|
|
}
|
|
|
|
|
if (in_array(strtolower($tld), self::TEST_TLD)) {
|
|
|
|
|
return TldType::tTLD;
|
|
|
|
|
}
|
2024-07-24 18:52:19 +02:00
|
|
|
|
2024-07-24 21:58:45 +02:00
|
|
|
return null;
|
2024-07-24 18:52:19 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-19 18:59:21 +02:00
|
|
|
/**
|
|
|
|
|
* @throws TransportExceptionInterface
|
|
|
|
|
* @throws ServerExceptionInterface
|
|
|
|
|
* @throws RedirectionExceptionInterface
|
|
|
|
|
* @throws ClientExceptionInterface
|
|
|
|
|
* @throws DecodingExceptionInterface
|
2024-08-02 23:24:52 +02:00
|
|
|
* @throws \Exception
|
2024-07-19 18:59:21 +02:00
|
|
|
*/
|
|
|
|
|
public function updateGTldListICANN(): void
|
|
|
|
|
{
|
2024-08-04 04:39:05 +02:00
|
|
|
$this->logger->info('Start of retrieval of the list of gTLDs according to ICANN.');
|
|
|
|
|
|
2024-07-19 18:59:21 +02:00
|
|
|
$gTldList = $this->client->request(
|
|
|
|
|
'GET', 'https://www.icann.org/resources/registries/gtlds/v2/gtlds.json'
|
|
|
|
|
)->toArray()['gTLDs'];
|
|
|
|
|
|
|
|
|
|
foreach ($gTldList as $gTld) {
|
2024-08-02 23:24:52 +02:00
|
|
|
if ('' === $gTld['gTLD']) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2024-12-20 17:43:35 +01:00
|
|
|
/** @var Tld|null $gtTldEntity */
|
2024-07-25 19:04:59 +02:00
|
|
|
$gtTldEntity = $this->tldRepository->findOneBy(['tld' => $gTld['gTLD']]);
|
2024-07-25 17:03:00 +02:00
|
|
|
|
2024-12-20 17:43:35 +01:00
|
|
|
if (null === $gtTldEntity) {
|
2024-07-25 17:03:00 +02:00
|
|
|
$gtTldEntity = new Tld();
|
2025-09-10 21:35:43 +02:00
|
|
|
$gtTldEntity->setTld($gTld['gTLD'])->setType(TldType::gTLD);
|
2024-08-04 04:39:05 +02:00
|
|
|
$this->logger->notice('New gTLD detected according to ICANN ({tld}).', [
|
|
|
|
|
'tld' => $gTld['gTLD'],
|
|
|
|
|
]);
|
2024-07-25 17:03:00 +02:00
|
|
|
}
|
2024-07-19 18:59:21 +02:00
|
|
|
|
2024-07-24 21:58:45 +02:00
|
|
|
$gtTldEntity
|
|
|
|
|
->setContractTerminated($gTld['contractTerminated'])
|
2024-07-19 18:59:21 +02:00
|
|
|
->setRegistryOperator($gTld['registryOperator'])
|
2025-08-27 14:47:33 +02:00
|
|
|
->setSpecification13($gTld['specification13']);
|
|
|
|
|
// NOTICE: sTLDs are listed in ICANN's gTLD list
|
2024-07-24 18:52:19 +02:00
|
|
|
|
2024-08-02 23:24:52 +02:00
|
|
|
if (null !== $gTld['removalDate']) {
|
|
|
|
|
$gtTldEntity->setRemovalDate(new \DateTimeImmutable($gTld['removalDate']));
|
|
|
|
|
}
|
|
|
|
|
if (null !== $gTld['delegationDate']) {
|
|
|
|
|
$gtTldEntity->setDelegationDate(new \DateTimeImmutable($gTld['delegationDate']));
|
|
|
|
|
}
|
|
|
|
|
if (null !== $gTld['dateOfContractSignature']) {
|
|
|
|
|
$gtTldEntity->setDateOfContractSignature(new \DateTimeImmutable($gTld['dateOfContractSignature']));
|
|
|
|
|
}
|
2024-07-19 18:59:21 +02:00
|
|
|
$this->em->persist($gtTldEntity);
|
|
|
|
|
}
|
2024-07-25 17:03:00 +02:00
|
|
|
|
2024-07-19 18:59:21 +02:00
|
|
|
$this->em->flush();
|
|
|
|
|
}
|
2024-08-02 23:24:52 +02:00
|
|
|
}
|