2024-07-13 23:57:07 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace App\Service;
|
|
|
|
|
|
|
|
|
|
use App\Config\DomainRole;
|
|
|
|
|
use App\Config\EventAction;
|
|
|
|
|
use App\Entity\Domain;
|
|
|
|
|
use App\Entity\DomainEntity;
|
|
|
|
|
use App\Entity\DomainEvent;
|
|
|
|
|
use App\Entity\Entity;
|
|
|
|
|
use App\Entity\EntityEvent;
|
|
|
|
|
use App\Entity\Nameserver;
|
|
|
|
|
use App\Entity\NameserverEntity;
|
|
|
|
|
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;
|
|
|
|
|
use DateTimeImmutable;
|
|
|
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
|
|
|
use Exception;
|
|
|
|
|
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
|
|
|
|
|
use Symfony\Contracts\HttpClient\HttpClientInterface;
|
2024-07-14 11:20:04 +02:00
|
|
|
use Throwable;
|
2024-07-13 23:57:07 +02:00
|
|
|
|
2024-07-14 21:15:13 +02:00
|
|
|
readonly class RDAPService
|
2024-07-13 23:57:07 +02:00
|
|
|
{
|
|
|
|
|
|
2024-07-14 21:15:13 +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 EntityManagerInterface $em,
|
|
|
|
|
private ParameterBagInterface $params
|
2024-07-13 23:57:07 +02:00
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-14 11:20:04 +02:00
|
|
|
/**
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
2024-07-15 00:21:40 +02:00
|
|
|
public function registerDomains(array $domains): void
|
|
|
|
|
{
|
|
|
|
|
$dnsRoot = json_decode(file_get_contents($this->params->get('kernel.project_dir') . '/src/Config/dns.json'))->services;
|
|
|
|
|
foreach ($domains as $fqdn) {
|
|
|
|
|
$this->registerDomain($dnsRoot, $fqdn);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
|
|
|
|
private function registerDomain(array $dnsRoot, string $fqdn): void
|
2024-07-13 23:57:07 +02:00
|
|
|
{
|
2024-07-14 00:05:01 +02:00
|
|
|
$idnDomain = idn_to_ascii($fqdn);
|
2024-07-14 11:20:04 +02:00
|
|
|
try {
|
2024-07-15 00:21:40 +02:00
|
|
|
$rdapServer = $this->getRDAPServer($dnsRoot, RDAPService::getTld($idnDomain));
|
2024-07-14 21:15:13 +02:00
|
|
|
} catch (Exception) {
|
2024-07-14 11:20:04 +02:00
|
|
|
throw new Exception("Unable to determine which RDAP server to contact");
|
|
|
|
|
}
|
2024-07-13 23:57:07 +02:00
|
|
|
|
2024-07-14 11:20:04 +02:00
|
|
|
try {
|
|
|
|
|
$res = $this->client->request(
|
|
|
|
|
'GET', $rdapServer . 'domain/' . $idnDomain
|
|
|
|
|
)->toArray();
|
2024-07-14 21:15:13 +02:00
|
|
|
} catch (Throwable) {
|
2024-07-14 11:20:04 +02:00
|
|
|
throw new Exception("Unable to contact RDAP server");
|
|
|
|
|
}
|
2024-07-13 23:57:07 +02:00
|
|
|
|
|
|
|
|
$domain = $this->domainRepository->findOneBy(["ldhName" => strtolower($res['ldhName'])]);
|
|
|
|
|
if ($domain === null) $domain = new Domain();
|
|
|
|
|
|
|
|
|
|
$domain
|
|
|
|
|
->setLdhName($res['ldhName'])
|
|
|
|
|
->setHandle($res['handle'])
|
2024-07-14 21:15:13 +02:00
|
|
|
->setStatus($res['status']);
|
2024-07-13 23:57:07 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($res['events'] as $rdapEvent) {
|
|
|
|
|
$eventAction = EventAction::from($rdapEvent['eventAction']);
|
|
|
|
|
if ($eventAction === EventAction::LastUpdateOfRDAPDatabase) continue;
|
|
|
|
|
|
|
|
|
|
$event = $this->domainEventRepository->findOneBy([
|
2024-07-14 21:15:13 +02:00
|
|
|
"action" => $eventAction,
|
2024-07-13 23:57:07 +02:00
|
|
|
"date" => new DateTimeImmutable($rdapEvent["eventDate"]),
|
|
|
|
|
"domain" => $domain
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
if ($event === null) $event = new DomainEvent();
|
|
|
|
|
$domain->addEvent($event
|
|
|
|
|
->setAction($eventAction)
|
|
|
|
|
->setDate(new DateTimeImmutable($rdapEvent['eventDate'])));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach ($res['entities'] as $rdapEntity) {
|
|
|
|
|
if (!array_key_exists('handle', $rdapEntity)) continue;
|
2024-07-14 00:06:58 +02:00
|
|
|
$entity = $this->registerEntity($rdapEntity);
|
2024-07-13 23:57:07 +02:00
|
|
|
|
|
|
|
|
$this->em->persist($entity);
|
|
|
|
|
$this->em->flush();
|
|
|
|
|
|
|
|
|
|
$domainEntity = $this->domainEntityRepository->findOneBy([
|
|
|
|
|
"domain" => $domain,
|
|
|
|
|
"entity" => $entity
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
if ($domainEntity === null) $domainEntity = new DomainEntity();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$domain->addDomainEntity($domainEntity
|
|
|
|
|
->setDomain($domain)
|
|
|
|
|
->setEntity($entity)
|
|
|
|
|
->setRoles(array_map(fn($str): DomainRole => DomainRole::from($str), $rdapEntity['roles'])));
|
|
|
|
|
|
|
|
|
|
$this->em->persist($domainEntity);
|
|
|
|
|
$this->em->flush();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($res['nameservers'] as $rdapNameserver) {
|
|
|
|
|
$nameserver = $this->nameserverRepository->findOneBy([
|
|
|
|
|
"ldhName" => strtolower($rdapNameserver['ldhName'])
|
|
|
|
|
]);
|
|
|
|
|
if ($nameserver === null) $nameserver = new Nameserver();
|
|
|
|
|
|
|
|
|
|
$nameserver->setLdhName($rdapNameserver['ldhName']);
|
|
|
|
|
|
|
|
|
|
if (!array_key_exists('entities', $rdapNameserver)) {
|
|
|
|
|
$domain->addNameserver($nameserver);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach ($rdapNameserver['entities'] as $rdapEntity) {
|
|
|
|
|
if (!array_key_exists('handle', $rdapEntity)) continue;
|
2024-07-14 00:06:58 +02:00
|
|
|
$entity = $this->registerEntity($rdapEntity);
|
2024-07-13 23:57:07 +02:00
|
|
|
|
|
|
|
|
$this->em->persist($entity);
|
|
|
|
|
$this->em->flush();
|
|
|
|
|
|
|
|
|
|
$nameserverEntity = $this->nameserverEntityRepository->findOneBy([
|
|
|
|
|
"nameserver" => $nameserver,
|
|
|
|
|
"entity" => $entity
|
|
|
|
|
]);
|
|
|
|
|
if ($nameserverEntity === null) $nameserverEntity = new NameserverEntity();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$nameserver->addNameserverEntity($nameserverEntity
|
|
|
|
|
->setNameserver($nameserver)
|
|
|
|
|
->setEntity($entity)
|
2024-07-14 21:15:13 +02:00
|
|
|
->setStatus($rdapNameserver['status'])
|
2024-07-13 23:57:07 +02:00
|
|
|
->setRoles(array_map(fn($str): DomainRole => DomainRole::from($str), $rdapEntity['roles'])));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$domain->addNameserver($nameserver);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-17 19:29:43 +02:00
|
|
|
$domain->updateTimestamps();
|
2024-07-13 23:57:07 +02:00
|
|
|
$this->em->persist($domain);
|
|
|
|
|
$this->em->flush();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-07-14 11:20:04 +02:00
|
|
|
/**
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
2024-07-15 00:21:40 +02:00
|
|
|
private function getRDAPServer(array $dnsRoot, string $tld)
|
2024-07-13 23:57:07 +02:00
|
|
|
{
|
|
|
|
|
|
|
|
|
|
foreach ($dnsRoot as $dns) {
|
|
|
|
|
if (in_array($tld, $dns[0])) return $dns[1][0];
|
|
|
|
|
}
|
2024-07-14 11:20:04 +02:00
|
|
|
throw new Exception("This TLD ($tld) is not supported");
|
2024-07-13 23:57:07 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-14 21:15:13 +02:00
|
|
|
/**
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
2024-07-13 23:57:07 +02:00
|
|
|
private static function getTld($domain): string
|
|
|
|
|
{
|
|
|
|
|
$lastDotPosition = strrpos($domain, '.');
|
|
|
|
|
if ($lastDotPosition === false) {
|
2024-07-14 11:20:04 +02:00
|
|
|
throw new Exception("Domain must contain at least one dot");
|
2024-07-13 23:57:07 +02:00
|
|
|
}
|
|
|
|
|
return strtolower(substr($domain, $lastDotPosition + 1));
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-14 11:20:04 +02:00
|
|
|
/**
|
|
|
|
|
* @throws Exception
|
|
|
|
|
*/
|
2024-07-14 00:06:58 +02:00
|
|
|
private function registerEntity(array $rdapEntity): Entity
|
2024-07-13 23:57:07 +02:00
|
|
|
{
|
|
|
|
|
$entity = $this->entityRepository->findOneBy([
|
|
|
|
|
"handle" => $rdapEntity['handle']
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
if ($entity === null) $entity = new Entity();
|
|
|
|
|
|
|
|
|
|
$entity->setHandle($rdapEntity['handle']);
|
|
|
|
|
|
|
|
|
|
if (empty($entity->getJCard())) {
|
|
|
|
|
$entity->setJCard($rdapEntity['vcardArray']);
|
|
|
|
|
} else {
|
|
|
|
|
$properties = [];
|
|
|
|
|
foreach ($rdapEntity['vcardArray'][1] as $prop) {
|
|
|
|
|
$properties[$prop[0]] = $prop;
|
|
|
|
|
}
|
|
|
|
|
foreach ($entity->getJCard()[1] as $prop) {
|
|
|
|
|
$properties[$prop[0]] = $prop;
|
|
|
|
|
}
|
|
|
|
|
$entity->setJCard(["vcard", array_values($properties)]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!array_key_exists('events', $rdapEntity)) return $entity;
|
|
|
|
|
|
|
|
|
|
foreach ($rdapEntity['events'] as $rdapEntityEvent) {
|
2024-07-15 11:52:51 +02:00
|
|
|
if ($rdapEntityEvent["eventAction"] === EventAction::LastChanged->value) continue;
|
2024-07-13 23:57:07 +02:00
|
|
|
$event = $this->entityEventRepository->findOneBy([
|
|
|
|
|
"action" => EventAction::from($rdapEntityEvent["eventAction"]),
|
|
|
|
|
"date" => new DateTimeImmutable($rdapEntityEvent["eventDate"])
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
if ($event !== null) continue;
|
|
|
|
|
$entity->addEvent(
|
|
|
|
|
(new EntityEvent())
|
|
|
|
|
->setEntity($entity)
|
|
|
|
|
->setAction(EventAction::from($rdapEntityEvent['eventAction']))
|
|
|
|
|
->setDate(new DateTimeImmutable($rdapEntityEvent['eventDate'])));
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return $entity;
|
|
|
|
|
}
|
|
|
|
|
}
|