mirror of
https://github.com/maelgangloff/domain-watchdog.git
synced 2025-12-17 17:55:42 +00:00
refactor: split RDAPService and OfficialDataService
This commit is contained in:
parent
23383f3368
commit
efa56055d0
@ -3,7 +3,7 @@
|
|||||||
namespace App\MessageHandler;
|
namespace App\MessageHandler;
|
||||||
|
|
||||||
use App\Message\UpdateRdapServers;
|
use App\Message\UpdateRdapServers;
|
||||||
use App\Service\RDAPService;
|
use App\Service\OfficialDataService;
|
||||||
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
||||||
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
||||||
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
||||||
@ -16,7 +16,7 @@ use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
|||||||
final readonly class UpdateRdapServersHandler
|
final readonly class UpdateRdapServersHandler
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private RDAPService $RDAPService,
|
private OfficialDataService $officialDataService,
|
||||||
private ParameterBagInterface $bag,
|
private ParameterBagInterface $bag,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
@ -39,9 +39,9 @@ final readonly class UpdateRdapServersHandler
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->RDAPService->updateTldListIANA();
|
$this->officialDataService->updateTldListIANA();
|
||||||
$this->RDAPService->updateGTldListICANN();
|
$this->officialDataService->updateGTldListICANN();
|
||||||
$this->RDAPService->updateDomainsWhenTldIsDeleted();
|
$this->officialDataService->updateDomainsWhenTldIsDeleted();
|
||||||
} catch (\Throwable $throwable) {
|
} catch (\Throwable $throwable) {
|
||||||
$throws[] = $throwable;
|
$throws[] = $throwable;
|
||||||
}
|
}
|
||||||
@ -51,7 +51,7 @@ final readonly class UpdateRdapServersHandler
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->RDAPService->updateRDAPServersFromIANA();
|
$this->officialDataService->updateRDAPServersFromIANA();
|
||||||
} catch (\Throwable $throwable) {
|
} catch (\Throwable $throwable) {
|
||||||
$throws[] = $throwable;
|
$throws[] = $throwable;
|
||||||
}
|
}
|
||||||
@ -61,13 +61,13 @@ final readonly class UpdateRdapServersHandler
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->RDAPService->updateRDAPServersFromFile($this->bag->get('custom_rdap_servers_file'));
|
$this->officialDataService->updateRDAPServersFromFile($this->bag->get('custom_rdap_servers_file'));
|
||||||
} catch (\Throwable $throwable) {
|
} catch (\Throwable $throwable) {
|
||||||
$throws[] = $throwable;
|
$throws[] = $throwable;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->RDAPService->updateRegistrarListIANA();
|
$this->officialDataService->updateRegistrarListIANA();
|
||||||
} catch (\Throwable $throwable) {
|
} catch (\Throwable $throwable) {
|
||||||
$throws[] = $throwable;
|
$throws[] = $throwable;
|
||||||
}
|
}
|
||||||
|
|||||||
316
src/Service/OfficialDataService.php
Normal file
316
src/Service/OfficialDataService.php
Normal file
@ -0,0 +1,316 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Service;
|
||||||
|
|
||||||
|
use App\Config\RegistrarStatus;
|
||||||
|
use App\Config\TldType;
|
||||||
|
use App\Entity\IcannAccreditation;
|
||||||
|
use App\Entity\RdapServer;
|
||||||
|
use App\Entity\Tld;
|
||||||
|
use App\Repository\DomainRepository;
|
||||||
|
use App\Repository\IcannAccreditationRepository;
|
||||||
|
use App\Repository\RdapServerRepository;
|
||||||
|
use App\Repository\TldRepository;
|
||||||
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use Symfony\Component\Yaml\Yaml;
|
||||||
|
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
|
||||||
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
||||||
|
|
||||||
|
class OfficialDataService
|
||||||
|
{
|
||||||
|
/* @see https://www.iana.org/domains/root/db */
|
||||||
|
private const ISO_TLD_EXCEPTION = ['ac', 'eu', 'uk', 'su', 'tp'];
|
||||||
|
private const INFRA_TLD = ['arpa'];
|
||||||
|
private const SPONSORED_TLD = [
|
||||||
|
'aero',
|
||||||
|
'asia',
|
||||||
|
'cat',
|
||||||
|
'coop',
|
||||||
|
'edu',
|
||||||
|
'gov',
|
||||||
|
'int',
|
||||||
|
'jobs',
|
||||||
|
'mil',
|
||||||
|
'museum',
|
||||||
|
'post',
|
||||||
|
'tel',
|
||||||
|
'travel',
|
||||||
|
'xxx',
|
||||||
|
];
|
||||||
|
private const TEST_TLD = [
|
||||||
|
'xn--kgbechtv',
|
||||||
|
'xn--hgbk6aj7f53bba',
|
||||||
|
'xn--0zwm56d',
|
||||||
|
'xn--g6w251d',
|
||||||
|
'xn--80akhbyknj4f',
|
||||||
|
'xn--11b5bs3a9aj6g',
|
||||||
|
'xn--jxalpdlp',
|
||||||
|
'xn--9t4b11yi5a',
|
||||||
|
'xn--deba0ad',
|
||||||
|
'xn--zckzah',
|
||||||
|
'xn--hlcj6aya9esc7a',
|
||||||
|
];
|
||||||
|
|
||||||
|
private const IANA_REGISTRAR_IDS_URL = 'https://www.iana.org/assignments/registrar-ids/registrar-ids.xml';
|
||||||
|
private const IANA_RDAP_SERVER_LIST_URL = 'https://data.iana.org/rdap/dns.json';
|
||||||
|
private const IANA_TLD_LIST_URL = 'https://data.iana.org/TLD/tlds-alpha-by-domain.txt';
|
||||||
|
private const ICANN_GTLD_LIST_URL = 'https://www.icann.org/resources/registries/gtlds/v2/gtlds.json';
|
||||||
|
|
||||||
|
public const DOMAIN_DOT = '.';
|
||||||
|
|
||||||
|
public function __construct(private HttpClientInterface $client,
|
||||||
|
private readonly DomainRepository $domainRepository,
|
||||||
|
private readonly RdapServerRepository $rdapServerRepository,
|
||||||
|
private readonly TldRepository $tldRepository,
|
||||||
|
private readonly IcannAccreditationRepository $icannAccreditationRepository,
|
||||||
|
private readonly EntityManagerInterface $em,
|
||||||
|
private readonly LoggerInterface $logger,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws TransportExceptionInterface
|
||||||
|
* @throws ServerExceptionInterface
|
||||||
|
* @throws RedirectionExceptionInterface
|
||||||
|
* @throws DecodingExceptionInterface
|
||||||
|
* @throws ClientExceptionInterface
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function updateRDAPServersFromIANA(): void
|
||||||
|
{
|
||||||
|
$this->logger->info('Start of update the RDAP server list from IANA');
|
||||||
|
|
||||||
|
$dnsRoot = $this->client->request(
|
||||||
|
'GET', self::IANA_RDAP_SERVER_LIST_URL
|
||||||
|
)->toArray();
|
||||||
|
|
||||||
|
$this->updateRDAPServers($dnsRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
private function updateRDAPServers(array $dnsRoot): void
|
||||||
|
{
|
||||||
|
foreach ($dnsRoot['services'] as $service) {
|
||||||
|
foreach ($service[0] as $tld) {
|
||||||
|
if (self::DOMAIN_DOT === $tld && null === $this->tldRepository->findOneBy(['tld' => $tld])) {
|
||||||
|
$this->em->persist((new Tld())->setTld(self::DOMAIN_DOT)->setType(TldType::root));
|
||||||
|
$this->em->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
$tldEntity = $this->tldRepository->findOneBy(['tld' => $tld]);
|
||||||
|
if (null === $tldEntity) {
|
||||||
|
$tldEntity = (new Tld())->setTld($tld)->setType(TldType::gTLD);
|
||||||
|
$this->em->persist($tldEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($service[1] as $rdapServerUrl) {
|
||||||
|
$server = $this->rdapServerRepository->findOneBy(['tld' => $tldEntity->getTld(), 'url' => $rdapServerUrl]);
|
||||||
|
|
||||||
|
if (null === $server) {
|
||||||
|
$server = new RdapServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
$server
|
||||||
|
->setTld($tldEntity)
|
||||||
|
->setUrl($rdapServerUrl)
|
||||||
|
->setUpdatedAt(new \DateTimeImmutable($dnsRoot['publication'] ?? 'now'));
|
||||||
|
|
||||||
|
$this->em->persist($server);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->em->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws TransportExceptionInterface
|
||||||
|
* @throws ServerExceptionInterface
|
||||||
|
* @throws RedirectionExceptionInterface
|
||||||
|
* @throws ClientExceptionInterface
|
||||||
|
*/
|
||||||
|
public function updateTldListIANA(): void
|
||||||
|
{
|
||||||
|
$this->logger->info('Start of retrieval of the list of TLDs according to IANA');
|
||||||
|
$tldList = array_map(
|
||||||
|
fn ($tld) => strtolower($tld),
|
||||||
|
explode(PHP_EOL,
|
||||||
|
$this->client->request(
|
||||||
|
'GET', self::IANA_TLD_LIST_URL
|
||||||
|
)->getContent()
|
||||||
|
));
|
||||||
|
array_shift($tldList);
|
||||||
|
|
||||||
|
foreach ($tldList as $tld) {
|
||||||
|
if ('' === $tld) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->tldRepository->createQueryBuilder('t')
|
||||||
|
->update()
|
||||||
|
->set('t.deletedAt', 'COALESCE(t.removalDate, CURRENT_TIMESTAMP())')
|
||||||
|
->where('t.tld != :tld')
|
||||||
|
->setParameter('tld', self::DOMAIN_DOT)
|
||||||
|
->getQuery()->execute();
|
||||||
|
|
||||||
|
$tldEntity = $this->tldRepository->findOneBy(['tld' => $tld]);
|
||||||
|
|
||||||
|
if (null === $tldEntity) {
|
||||||
|
$tldEntity = new Tld();
|
||||||
|
$tldEntity->setTld($tld);
|
||||||
|
|
||||||
|
$this->logger->notice('New TLD detected according to IANA', [
|
||||||
|
'tld' => $tld,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = $this->getTldType($tld);
|
||||||
|
|
||||||
|
if (null !== $type) {
|
||||||
|
$tldEntity->setType($type);
|
||||||
|
} elseif (null === $tldEntity->isContractTerminated()) { // ICANN managed, must be a ccTLD
|
||||||
|
$tldEntity->setType(TldType::ccTLD);
|
||||||
|
} else {
|
||||||
|
$tldEntity->setType(TldType::gTLD);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tldEntity->setDeletedAt(null);
|
||||||
|
$this->em->persist($tldEntity);
|
||||||
|
}
|
||||||
|
$this->em->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws TransportExceptionInterface
|
||||||
|
* @throws ServerExceptionInterface
|
||||||
|
* @throws RedirectionExceptionInterface
|
||||||
|
* @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', self::IANA_REGISTRAR_IDS_URL
|
||||||
|
);
|
||||||
|
|
||||||
|
$data = new \SimpleXMLElement($registrarList->getContent());
|
||||||
|
|
||||||
|
foreach ($data->registry->record as $registrar) {
|
||||||
|
$icannAcreditation = $this->icannAccreditationRepository->findOneBy(['id' => (int) $registrar->value]);
|
||||||
|
if (null === $icannAcreditation) {
|
||||||
|
$icannAcreditation = new IcannAccreditation();
|
||||||
|
}
|
||||||
|
|
||||||
|
$icannAcreditation
|
||||||
|
->setId((int) $registrar->value)
|
||||||
|
->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);
|
||||||
|
|
||||||
|
$this->em->persist($icannAcreditation);
|
||||||
|
}
|
||||||
|
$this->em->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getTldType(string $tld): ?TldType
|
||||||
|
{
|
||||||
|
if (in_array(strtolower($tld), self::ISO_TLD_EXCEPTION)) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws TransportExceptionInterface
|
||||||
|
* @throws ServerExceptionInterface
|
||||||
|
* @throws RedirectionExceptionInterface
|
||||||
|
* @throws DecodingExceptionInterface
|
||||||
|
* @throws ClientExceptionInterface
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function updateGTldListICANN(): void
|
||||||
|
{
|
||||||
|
$this->logger->info('Start of retrieval of the list of gTLDs according to ICANN');
|
||||||
|
|
||||||
|
$gTldList = $this->client->request(
|
||||||
|
'GET', self::ICANN_GTLD_LIST_URL
|
||||||
|
)->toArray()['gTLDs'];
|
||||||
|
|
||||||
|
foreach ($gTldList as $gTld) {
|
||||||
|
if ('' === $gTld['gTLD']) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/** @var Tld|null $gtTldEntity */
|
||||||
|
$gtTldEntity = $this->tldRepository->findOneBy(['tld' => $gTld['gTLD']]);
|
||||||
|
|
||||||
|
if (null === $gtTldEntity) {
|
||||||
|
$gtTldEntity = new Tld();
|
||||||
|
$gtTldEntity->setTld($gTld['gTLD'])->setType(TldType::gTLD);
|
||||||
|
$this->logger->notice('New gTLD detected according to ICANN', [
|
||||||
|
'tld' => $gTld['gTLD'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$gtTldEntity
|
||||||
|
->setContractTerminated($gTld['contractTerminated'])
|
||||||
|
->setRegistryOperator($gTld['registryOperator'])
|
||||||
|
->setSpecification13($gTld['specification13']);
|
||||||
|
// NOTICE: sTLDs are listed in ICANN's gTLD list
|
||||||
|
|
||||||
|
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']));
|
||||||
|
}
|
||||||
|
$this->em->persist($gtTldEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->em->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateDomainsWhenTldIsDeleted(): void
|
||||||
|
{
|
||||||
|
$this->domainRepository->createQueryBuilder('d')
|
||||||
|
->update()
|
||||||
|
->set('d.deleted', ':deleted')
|
||||||
|
->where('d.tld IN (SELECT t FROM '.Tld::class.' t WHERE t.deletedAt IS NOT NULL)')
|
||||||
|
->setParameter('deleted', true)
|
||||||
|
->getQuery()->execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,8 +5,6 @@ namespace App\Service;
|
|||||||
use App\Config\DnsKey\Algorithm;
|
use App\Config\DnsKey\Algorithm;
|
||||||
use App\Config\DnsKey\DigestType;
|
use App\Config\DnsKey\DigestType;
|
||||||
use App\Config\EventAction;
|
use App\Config\EventAction;
|
||||||
use App\Config\RegistrarStatus;
|
|
||||||
use App\Config\TldType;
|
|
||||||
use App\Entity\DnsKey;
|
use App\Entity\DnsKey;
|
||||||
use App\Entity\Domain;
|
use App\Entity\Domain;
|
||||||
use App\Entity\DomainEntity;
|
use App\Entity\DomainEntity;
|
||||||
@ -14,7 +12,6 @@ use App\Entity\DomainEvent;
|
|||||||
use App\Entity\DomainStatus;
|
use App\Entity\DomainStatus;
|
||||||
use App\Entity\Entity;
|
use App\Entity\Entity;
|
||||||
use App\Entity\EntityEvent;
|
use App\Entity\EntityEvent;
|
||||||
use App\Entity\IcannAccreditation;
|
|
||||||
use App\Entity\Nameserver;
|
use App\Entity\Nameserver;
|
||||||
use App\Entity\NameserverEntity;
|
use App\Entity\NameserverEntity;
|
||||||
use App\Entity\RdapServer;
|
use App\Entity\RdapServer;
|
||||||
@ -40,7 +37,6 @@ use Psr\Log\LoggerInterface;
|
|||||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||||
use Symfony\Component\HttpClient\Exception\ClientException;
|
use Symfony\Component\HttpClient\Exception\ClientException;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use Symfony\Component\Yaml\Yaml;
|
|
||||||
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
|
||||||
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
|
use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
|
||||||
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
|
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
|
||||||
@ -51,39 +47,6 @@ use Symfony\Contracts\HttpClient\ResponseInterface;
|
|||||||
|
|
||||||
class RDAPService
|
class RDAPService
|
||||||
{
|
{
|
||||||
/* @see https://www.iana.org/domains/root/db */
|
|
||||||
private const ISO_TLD_EXCEPTION = ['ac', 'eu', 'uk', 'su', 'tp'];
|
|
||||||
private const INFRA_TLD = ['arpa'];
|
|
||||||
private const SPONSORED_TLD = [
|
|
||||||
'aero',
|
|
||||||
'asia',
|
|
||||||
'cat',
|
|
||||||
'coop',
|
|
||||||
'edu',
|
|
||||||
'gov',
|
|
||||||
'int',
|
|
||||||
'jobs',
|
|
||||||
'mil',
|
|
||||||
'museum',
|
|
||||||
'post',
|
|
||||||
'tel',
|
|
||||||
'travel',
|
|
||||||
'xxx',
|
|
||||||
];
|
|
||||||
private const TEST_TLD = [
|
|
||||||
'xn--kgbechtv',
|
|
||||||
'xn--hgbk6aj7f53bba',
|
|
||||||
'xn--0zwm56d',
|
|
||||||
'xn--g6w251d',
|
|
||||||
'xn--80akhbyknj4f',
|
|
||||||
'xn--11b5bs3a9aj6g',
|
|
||||||
'xn--jxalpdlp',
|
|
||||||
'xn--9t4b11yi5a',
|
|
||||||
'xn--deba0ad',
|
|
||||||
'xn--zckzah',
|
|
||||||
'xn--hlcj6aya9esc7a',
|
|
||||||
];
|
|
||||||
|
|
||||||
private const ENTITY_HANDLE_BLACKLIST = [
|
private const ENTITY_HANDLE_BLACKLIST = [
|
||||||
'REDACTED_FOR_PRIVACY',
|
'REDACTED_FOR_PRIVACY',
|
||||||
'ANO00-FRNIC',
|
'ANO00-FRNIC',
|
||||||
@ -99,29 +62,23 @@ class RDAPService
|
|||||||
'Private',
|
'Private',
|
||||||
];
|
];
|
||||||
|
|
||||||
private const DOMAIN_DOT = '.';
|
|
||||||
private const IANA_REGISTRAR_IDS_URL = 'https://www.iana.org/assignments/registrar-ids/registrar-ids.xml';
|
|
||||||
private const IANA_RDAP_SERVER_LIST_URL = 'https://data.iana.org/rdap/dns.json';
|
|
||||||
private const IANA_TLD_LIST_URL = 'https://data.iana.org/TLD/tlds-alpha-by-domain.txt';
|
|
||||||
private const ICANN_GTLD_LIST_URL = 'https://www.icann.org/resources/registries/gtlds/v2/gtlds.json';
|
|
||||||
|
|
||||||
public function __construct(private HttpClientInterface $client,
|
public function __construct(private HttpClientInterface $client,
|
||||||
private EntityRepository $entityRepository,
|
private readonly EntityRepository $entityRepository,
|
||||||
private DomainRepository $domainRepository,
|
private readonly DomainRepository $domainRepository,
|
||||||
private DomainEventRepository $domainEventRepository,
|
private readonly DomainEventRepository $domainEventRepository,
|
||||||
private NameserverRepository $nameserverRepository,
|
private readonly NameserverRepository $nameserverRepository,
|
||||||
private NameserverEntityRepository $nameserverEntityRepository,
|
private readonly NameserverEntityRepository $nameserverEntityRepository,
|
||||||
private EntityEventRepository $entityEventRepository,
|
private readonly EntityEventRepository $entityEventRepository,
|
||||||
private DomainEntityRepository $domainEntityRepository,
|
private readonly DomainEntityRepository $domainEntityRepository,
|
||||||
private RdapServerRepository $rdapServerRepository,
|
private readonly RdapServerRepository $rdapServerRepository,
|
||||||
private TldRepository $tldRepository,
|
private readonly TldRepository $tldRepository,
|
||||||
private IcannAccreditationRepository $icannAccreditationRepository,
|
private readonly IcannAccreditationRepository $icannAccreditationRepository,
|
||||||
private EntityManagerInterface $em,
|
private readonly EntityManagerInterface $em,
|
||||||
private LoggerInterface $logger,
|
private readonly LoggerInterface $logger,
|
||||||
private StatService $statService,
|
private readonly StatService $statService,
|
||||||
private InfluxdbService $influxService,
|
private readonly InfluxdbService $influxService,
|
||||||
#[Autowire(param: 'influxdb_enabled')]
|
#[Autowire(param: 'influxdb_enabled')]
|
||||||
private bool $influxdbEnabled,
|
private readonly bool $influxdbEnabled,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,17 +167,17 @@ class RDAPService
|
|||||||
*/
|
*/
|
||||||
public function getTld(string $domain): Tld
|
public function getTld(string $domain): Tld
|
||||||
{
|
{
|
||||||
if (!str_contains($domain, self::DOMAIN_DOT)) {
|
if (!str_contains($domain, OfficialDataService::DOMAIN_DOT)) {
|
||||||
$tldEntity = $this->tldRepository->findOneBy(['tld' => self::DOMAIN_DOT]);
|
$tldEntity = $this->tldRepository->findOneBy(['tld' => OfficialDataService::DOMAIN_DOT]);
|
||||||
|
|
||||||
if (null == $tldEntity) {
|
if (null == $tldEntity) {
|
||||||
throw TldNotSupportedException::fromTld(self::DOMAIN_DOT);
|
throw TldNotSupportedException::fromTld(OfficialDataService::DOMAIN_DOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $tldEntity;
|
return $tldEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
$lastDotPosition = strrpos($domain, self::DOMAIN_DOT);
|
$lastDotPosition = strrpos($domain, OfficialDataService::DOMAIN_DOT);
|
||||||
|
|
||||||
if (false === $lastDotPosition) {
|
if (false === $lastDotPosition) {
|
||||||
throw MalformedDomainException::fromDomain($domain);
|
throw MalformedDomainException::fromDomain($domain);
|
||||||
@ -280,9 +237,8 @@ class RDAPService
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->statService->incrementStat('stats.rdap_queries.count');
|
|
||||||
|
|
||||||
$req = $this->client->request('GET', $rdapServerUrl.'domain/'.$idnDomain);
|
$req = $this->client->request('GET', $rdapServerUrl.'domain/'.$idnDomain);
|
||||||
|
$this->statService->incrementStat('stats.rdap_queries.count');
|
||||||
|
|
||||||
return $req->toArray();
|
return $req->toArray();
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
@ -759,245 +715,4 @@ class RDAPService
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws TransportExceptionInterface
|
|
||||||
* @throws ServerExceptionInterface
|
|
||||||
* @throws RedirectionExceptionInterface
|
|
||||||
* @throws DecodingExceptionInterface
|
|
||||||
* @throws ClientExceptionInterface
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function updateRDAPServersFromIANA(): void
|
|
||||||
{
|
|
||||||
$this->logger->info('Start of update the RDAP server list from IANA');
|
|
||||||
|
|
||||||
$dnsRoot = $this->client->request(
|
|
||||||
'GET', self::IANA_RDAP_SERVER_LIST_URL
|
|
||||||
)->toArray();
|
|
||||||
|
|
||||||
$this->updateRDAPServers($dnsRoot);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
private function updateRDAPServers(array $dnsRoot): void
|
|
||||||
{
|
|
||||||
foreach ($dnsRoot['services'] as $service) {
|
|
||||||
foreach ($service[0] as $tld) {
|
|
||||||
if (self::DOMAIN_DOT === $tld && null === $this->tldRepository->findOneBy(['tld' => $tld])) {
|
|
||||||
$this->em->persist((new Tld())->setTld(self::DOMAIN_DOT)->setType(TldType::root));
|
|
||||||
$this->em->flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
$tldEntity = $this->tldRepository->findOneBy(['tld' => $tld]);
|
|
||||||
if (null === $tldEntity) {
|
|
||||||
$tldEntity = (new Tld())->setTld($tld)->setType(TldType::gTLD);
|
|
||||||
$this->em->persist($tldEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($service[1] as $rdapServerUrl) {
|
|
||||||
$server = $this->rdapServerRepository->findOneBy(['tld' => $tldEntity->getTld(), 'url' => $rdapServerUrl]);
|
|
||||||
|
|
||||||
if (null === $server) {
|
|
||||||
$server = new RdapServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
$server
|
|
||||||
->setTld($tldEntity)
|
|
||||||
->setUrl($rdapServerUrl)
|
|
||||||
->setUpdatedAt(new \DateTimeImmutable($dnsRoot['publication'] ?? 'now'));
|
|
||||||
|
|
||||||
$this->em->persist($server);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->em->flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws TransportExceptionInterface
|
|
||||||
* @throws ServerExceptionInterface
|
|
||||||
* @throws RedirectionExceptionInterface
|
|
||||||
* @throws ClientExceptionInterface
|
|
||||||
*/
|
|
||||||
public function updateTldListIANA(): void
|
|
||||||
{
|
|
||||||
$this->logger->info('Start of retrieval of the list of TLDs according to IANA');
|
|
||||||
$tldList = array_map(
|
|
||||||
fn ($tld) => strtolower($tld),
|
|
||||||
explode(PHP_EOL,
|
|
||||||
$this->client->request(
|
|
||||||
'GET', self::IANA_TLD_LIST_URL
|
|
||||||
)->getContent()
|
|
||||||
));
|
|
||||||
array_shift($tldList);
|
|
||||||
|
|
||||||
foreach ($tldList as $tld) {
|
|
||||||
if ('' === $tld) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->tldRepository->createQueryBuilder('t')
|
|
||||||
->update()
|
|
||||||
->set('t.deletedAt', 'COALESCE(t.removalDate, CURRENT_TIMESTAMP())')
|
|
||||||
->where('t.tld != :tld')
|
|
||||||
->setParameter('tld', self::DOMAIN_DOT)
|
|
||||||
->getQuery()->execute();
|
|
||||||
|
|
||||||
$tldEntity = $this->tldRepository->findOneBy(['tld' => $tld]);
|
|
||||||
|
|
||||||
if (null === $tldEntity) {
|
|
||||||
$tldEntity = new Tld();
|
|
||||||
$tldEntity->setTld($tld);
|
|
||||||
|
|
||||||
$this->logger->notice('New TLD detected according to IANA', [
|
|
||||||
'tld' => $tld,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$type = $this->getTldType($tld);
|
|
||||||
|
|
||||||
if (null !== $type) {
|
|
||||||
$tldEntity->setType($type);
|
|
||||||
} elseif (null === $tldEntity->isContractTerminated()) { // ICANN managed, must be a ccTLD
|
|
||||||
$tldEntity->setType(TldType::ccTLD);
|
|
||||||
} else {
|
|
||||||
$tldEntity->setType(TldType::gTLD);
|
|
||||||
}
|
|
||||||
|
|
||||||
$tldEntity->setDeletedAt(null);
|
|
||||||
$this->em->persist($tldEntity);
|
|
||||||
}
|
|
||||||
$this->em->flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws TransportExceptionInterface
|
|
||||||
* @throws ServerExceptionInterface
|
|
||||||
* @throws RedirectionExceptionInterface
|
|
||||||
* @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', self::IANA_REGISTRAR_IDS_URL
|
|
||||||
);
|
|
||||||
|
|
||||||
$data = new \SimpleXMLElement($registrarList->getContent());
|
|
||||||
|
|
||||||
foreach ($data->registry->record as $registrar) {
|
|
||||||
$icannAcreditation = $this->icannAccreditationRepository->findOneBy(['id' => (int) $registrar->value]);
|
|
||||||
if (null === $icannAcreditation) {
|
|
||||||
$icannAcreditation = new IcannAccreditation();
|
|
||||||
}
|
|
||||||
|
|
||||||
$icannAcreditation
|
|
||||||
->setId((int) $registrar->value)
|
|
||||||
->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);
|
|
||||||
|
|
||||||
$this->em->persist($icannAcreditation);
|
|
||||||
}
|
|
||||||
$this->em->flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getTldType(string $tld): ?TldType
|
|
||||||
{
|
|
||||||
if (in_array(strtolower($tld), self::ISO_TLD_EXCEPTION)) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws TransportExceptionInterface
|
|
||||||
* @throws ServerExceptionInterface
|
|
||||||
* @throws RedirectionExceptionInterface
|
|
||||||
* @throws DecodingExceptionInterface
|
|
||||||
* @throws ClientExceptionInterface
|
|
||||||
* @throws \Exception
|
|
||||||
*/
|
|
||||||
public function updateGTldListICANN(): void
|
|
||||||
{
|
|
||||||
$this->logger->info('Start of retrieval of the list of gTLDs according to ICANN');
|
|
||||||
|
|
||||||
$gTldList = $this->client->request(
|
|
||||||
'GET', self::ICANN_GTLD_LIST_URL
|
|
||||||
)->toArray()['gTLDs'];
|
|
||||||
|
|
||||||
foreach ($gTldList as $gTld) {
|
|
||||||
if ('' === $gTld['gTLD']) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/** @var Tld|null $gtTldEntity */
|
|
||||||
$gtTldEntity = $this->tldRepository->findOneBy(['tld' => $gTld['gTLD']]);
|
|
||||||
|
|
||||||
if (null === $gtTldEntity) {
|
|
||||||
$gtTldEntity = new Tld();
|
|
||||||
$gtTldEntity->setTld($gTld['gTLD'])->setType(TldType::gTLD);
|
|
||||||
$this->logger->notice('New gTLD detected according to ICANN', [
|
|
||||||
'tld' => $gTld['gTLD'],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$gtTldEntity
|
|
||||||
->setContractTerminated($gTld['contractTerminated'])
|
|
||||||
->setRegistryOperator($gTld['registryOperator'])
|
|
||||||
->setSpecification13($gTld['specification13']);
|
|
||||||
// NOTICE: sTLDs are listed in ICANN's gTLD list
|
|
||||||
|
|
||||||
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']));
|
|
||||||
}
|
|
||||||
$this->em->persist($gtTldEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->em->flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updateDomainsWhenTldIsDeleted(): void
|
|
||||||
{
|
|
||||||
$this->domainRepository->createQueryBuilder('d')
|
|
||||||
->update()
|
|
||||||
->set('d.deleted', ':deleted')
|
|
||||||
->where('d.tld IN (SELECT t FROM '.Tld::class.' t WHERE t.deletedAt IS NOT NULL)')
|
|
||||||
->setParameter('deleted', true)
|
|
||||||
->getQuery()->execute();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user