Big RDAP update

This commit is contained in:
Pinga 2023-08-11 00:58:06 +03:00
parent 8ff392d8a0
commit 8d63b93d1f
2 changed files with 161 additions and 44 deletions

View file

@ -5,6 +5,7 @@ CREATE DATABASE IF NOT EXISTS `registry`;
CREATE TABLE IF NOT EXISTS `registry`.`domain_tld` ( CREATE TABLE IF NOT EXISTS `registry`.`domain_tld` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT, `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`tld` varchar(32) NOT NULL, `tld` varchar(32) NOT NULL,
`idn_table` varchar(255) NOT NULL,
PRIMARY KEY (`id`), PRIMARY KEY (`id`),
UNIQUE KEY `tld` (`tld`) UNIQUE KEY `tld` (`tld`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='domain tld'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='domain tld';
@ -542,11 +543,11 @@ CREATE TABLE `premium_domain_pricing` (
FOREIGN KEY (`tld_id`) REFERENCES `domain_tld`(`id`) FOREIGN KEY (`tld_id`) REFERENCES `domain_tld`(`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Premium Domains'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Premium Domains';
INSERT INTO `registry`.`domain_tld` VALUES('1','.COM.XX'); INSERT INTO `registry`.`domain_tld` VALUES('1','.COM.XX','/^(?!-)(?!.*--)[A-Z0-9-]{1,63}(?<!-)(\.(?!-)(?!.*--)[A-Z0-9-]{1,63}(?<!-))*$/i');
INSERT INTO `registry`.`domain_tld` VALUES('2','.ORG.XX'); INSERT INTO `registry`.`domain_tld` VALUES('2','.ORG.XX','/^(?!-)(?!.*--)[A-Z0-9-]{1,63}(?<!-)(\.(?!-)(?!.*--)[A-Z0-9-]{1,63}(?<!-))*$/i');
INSERT INTO `registry`.`domain_tld` VALUES('3','.INFO.XX'); INSERT INTO `registry`.`domain_tld` VALUES('3','.INFO.XX','/^(?!-)(?!.*--)[A-Z0-9-]{1,63}(?<!-)(\.(?!-)(?!.*--)[A-Z0-9-]{1,63}(?<!-))*$/i');
INSERT INTO `registry`.`domain_tld` VALUES('4','.PRO.XX'); INSERT INTO `registry`.`domain_tld` VALUES('4','.PRO.XX','/^(?!-)(?!.*--)[A-Z0-9-]{1,63}(?<!-)(\.(?!-)(?!.*--)[A-Z0-9-]{1,63}(?<!-))*$/i');
INSERT INTO `registry`.`domain_tld` VALUES('5','.XX'); INSERT INTO `registry`.`domain_tld` VALUES('5','.XX','/^(?!-)(?!.*--)[A-Z0-9-]{1,63}(?<!-)(\.(?!-)(?!.*--)[A-Z0-9-]{1,63}(?<!-))*$/i');
INSERT INTO `registry`.`domain_price` VALUES('1','1','create','0.00','5.00','10.00','15.00','20.00','25.00','30.00','35.00','40.00','45.00','50.00'); INSERT INTO `registry`.`domain_price` VALUES('1','1','create','0.00','5.00','10.00','15.00','20.00','25.00','30.00','35.00','40.00','45.00','50.00');
INSERT INTO `registry`.`domain_price` VALUES('2','1','renew','0.00','5.00','10.00','15.00','20.00','25.00','30.00','35.00','40.00','45.00','50.00'); INSERT INTO `registry`.`domain_price` VALUES('2','1','renew','0.00','5.00','10.00','15.00','20.00','25.00','30.00','35.00','40.00','45.00','50.00');

View file

@ -4,8 +4,35 @@ if (!extension_loaded('swoole')) {
die('Swoole extension must be installed'); die('Swoole extension must be installed');
} }
function mapContactToVCard($contactDetails, $role) {
return [
'objectClassName' => 'entity',
'roles' => [$role],
'vcardArray' => [
"vcard",
[
["version", "4.0"],
["fn", $contactDetails['name']],
["org", $contactDetails['org']],
["adr", [
"", // Post office box
$contactDetails['street1'], // Extended address
$contactDetails['street2'], // Street address
$contactDetails['city'], // Locality
$contactDetails['sp'], // Region
$contactDetails['pc'], // Postal code
$contactDetails['cc'] // Country name
]],
["tel", $contactDetails['voice'], ["type" => "voice"]],
["tel", $contactDetails['fax'], ["type" => "fax"]],
["email", $contactDetails['email']],
]
],
];
}
// Create a Swoole HTTP server // Create a Swoole HTTP server
$http = new Swoole\Http\Server('0.0.0.0', 8080); $http = new Swoole\Http\Server('0.0.0.0', 7500);
// Register a callback to handle incoming requests // Register a callback to handle incoming requests
$http->on('request', function ($request, $response) { $http->on('request', function ($request, $response) {
@ -59,8 +86,69 @@ $http->start();
function handleDomainQuery($request, $response, $pdo, $domainName) { function handleDomainQuery($request, $response, $pdo, $domainName) {
// Extract and validate the domain name from the request // Extract and validate the domain name from the request
$domain = $domainName; $domain = trim($domainName);
// ... Perform validation as in the WHOIS server ...
// Empty domain check
if (!$domain) {
$response->header('Content-Type', 'application/json');
$response->status(400); // Bad Request
$response->end(json_encode(['error' => 'Please enter a domain name']));
return;
}
// Check domain length
if (strlen($domain) > 68) {
$response->header('Content-Type', 'application/json');
$response->status(400); // Bad Request
$response->end(json_encode(['error' => 'Domain name is too long']));
return;
}
// Check for prohibited patterns in domain names
if (preg_match("/(^-|^\.|-\.|\.-|--|\.\.|-$|\.$)/", $domain)) {
$response->header('Content-Type', 'application/json');
$response->status(400); // Bad Request
$response->end(json_encode(['error' => 'Domain name invalid format']));
return;
}
// Extract TLD from the domain
$parts = explode('.', $domain);
$tld = "." . end($parts);
// Check if the TLD exists in the domain_tld table
$stmtTLD = $pdo->prepare("SELECT COUNT(*) FROM domain_tld WHERE tld = :tld");
$stmtTLD->bindParam(':tld', $tld, PDO::PARAM_STR);
$stmtTLD->execute();
$tldExists = $stmtTLD->fetchColumn();
if (!$tldExists) {
$response->header('Content-Type', 'application/json');
$response->status(400); // Bad Request
$response->end(json_encode(['error' => 'Invalid TLD. Please search only allowed TLDs']));
return;
}
// Fetch the IDN regex for the given TLD
$stmtRegex = $pdo->prepare("SELECT idn_table FROM domain_tld WHERE tld = :tld");
$stmtRegex->bindParam(':tld', $tld, PDO::PARAM_STR);
$stmtRegex->execute();
$idnRegex = $stmtRegex->fetchColumn();
if (!$idnRegex) {
$response->header('Content-Type', 'application/json');
$response->status(400); // Bad Request
$response->end(json_encode(['error' => 'Failed to fetch domain IDN table']));
return;
}
// Check for invalid characters using fetched regex
if (!preg_match($idnRegex, $domain)) {
$response->header('Content-Type', 'application/json');
$response->status(400); // Bad Request
$response->end(json_encode(['error' => 'Domain name invalid format']));
return;
}
// Perform the RDAP lookup // Perform the RDAP lookup
try { try {
@ -73,6 +161,7 @@ function handleDomainQuery($request, $response, $pdo, $domainName) {
// Check if the domain exists // Check if the domain exists
if (!$domainDetails) { if (!$domainDetails) {
// Domain not found, respond with a 404 error // Domain not found, respond with a 404 error
$response->header('Content-Type', 'application/json');
$response->status(404); $response->status(404);
$response->end(json_encode([ $response->end(json_encode([
'errorCode' => 404, 'errorCode' => 404,
@ -102,8 +191,35 @@ function handleDomainQuery($request, $response, $pdo, $domainName) {
$stmt4->execute(); $stmt4->execute();
$registrantDetails = $stmt4->fetch(PDO::FETCH_ASSOC); $registrantDetails = $stmt4->fetch(PDO::FETCH_ASSOC);
// Query 5: Get admin and tech contacts (similar to registrant, with different conditions) // Query 5: Get admin, billing and tech contacts
// ... $stmtMap = $pdo->prepare("SELECT contact_id, type FROM domain_contact_map WHERE domain_id = :domain_id");
$stmtMap->bindParam(':domain_id', $domainDetails['id'], PDO::PARAM_INT);
$stmtMap->execute();
$contactMap = $stmtMap->fetchAll(PDO::FETCH_ASSOC);
$adminDetails = [];
$techDetails = [];
$billingDetails = [];
foreach ($contactMap as $map) {
$stmtDetails = $pdo->prepare("SELECT contact.identifier, contact_postalInfo.name, contact_postalInfo.org, contact_postalInfo.street1, contact_postalInfo.street2, contact_postalInfo.street3, contact_postalInfo.city, contact_postalInfo.sp, contact_postalInfo.pc, contact_postalInfo.cc, contact.voice, contact.voice_x, contact.fax, contact.fax_x, contact.email FROM contact, contact_postalInfo WHERE contact.id = :contact_id AND contact_postalInfo.contact_id = contact.id");
$stmtDetails->bindParam(':contact_id', $map['contact_id'], PDO::PARAM_INT);
$stmtDetails->execute();
$contactDetails = $stmtDetails->fetch(PDO::FETCH_ASSOC);
switch ($map['type']) {
case 'admin':
$adminDetails[] = $contactDetails;
break;
case 'tech':
$techDetails[] = $contactDetails;
break;
case 'billing':
$billingDetails[] = $contactDetails;
break;
}
}
// Query 6: Get nameservers // Query 6: Get nameservers
$stmt6 = $pdo->prepare(" $stmt6 = $pdo->prepare("
@ -116,6 +232,23 @@ function handleDomainQuery($request, $response, $pdo, $domainName) {
$stmt6->execute(); $stmt6->execute();
$nameservers = $stmt6->fetchAll(PDO::FETCH_ASSOC); $nameservers = $stmt6->fetchAll(PDO::FETCH_ASSOC);
// Define the basic events
$events = [
['eventAction' => 'registration', 'eventDate' => $domainDetails['crdate']],
['eventAction' => 'expiration', 'eventDate' => $domainDetails['exdate']],
['eventAction' => 'last rdap database update', 'eventDate' => date('Y-m-d\TH:i:s\Z')],
];
// Check if domain last update is set and not empty
if (isset($domainDetails['update']) && !empty($domainDetails['update'])) {
$events[] = ['eventAction' => 'last domain update', 'eventDate' => date('Y-m-d', strtotime($domainDetails['update']))];
}
// Check if domain transfer date is set and not empty
if (isset($domainDetails['trdate']) && !empty($domainDetails['trdate'])) {
$events[] = ['eventAction' => 'domain transfer', 'eventDate' => date('Y-m-d', strtotime($domainDetails['trdate']))];
}
// Construct the RDAP response in JSON format // Construct the RDAP response in JSON format
$rdapResponse = [ $rdapResponse = [
'rdapConformance' => [ 'rdapConformance' => [
@ -124,39 +257,21 @@ function handleDomainQuery($request, $response, $pdo, $domainName) {
'icann_rdap_technical_implementation_guide_0', 'icann_rdap_technical_implementation_guide_0',
], ],
'objectClassName' => 'domain', 'objectClassName' => 'domain',
'entities' => [ 'entities' => array_merge(
[ [
'objectClassName' => 'entity', mapContactToVCard($registrantDetails, 'registrant')
'roles' => ['registrant'],
'vcardArray' => [
"vcard",
[
["version", "4.0"],
["fn", $registrantDetails['name']],
["org", $registrantDetails['org']],
["adr", [
"", // Post office box
$registrantDetails['street1'], // Extended address
$registrantDetails['street2'], // Street address
$registrantDetails['city'], // Locality
$registrantDetails['sp'], // Region
$registrantDetails['pc'], // Postal code
$registrantDetails['cc'] // Country name
]],
["tel", $registrantDetails['voice'], ["type" => "voice"]],
["tel", $registrantDetails['fax'], ["type" => "fax"]],
["email", $registrantDetails['email']],
// ... Additional vCard properties ...
]
],
],
// ... Additional entities for admin, tech ...
],
'events' => [
['eventAction' => 'registration', 'eventDate' => $domainDetails['crdate']],
['eventAction' => 'expiration', 'eventDate' => $domainDetails['exdate']],
// ... Additional events ...
], ],
array_map(function ($contact) {
return mapContactToVCard($contact, 'admin');
}, $adminDetails),
array_map(function ($contact) {
return mapContactToVCard($contact, 'tech');
}, $techDetails),
array_map(function ($contact) {
return mapContactToVCard($contact, 'billing');
}, $billingDetails)
),
'events' => $events,
'handle' => $domainDetails['id'] . '', 'handle' => $domainDetails['id'] . '',
'ldhName' => $domain, 'ldhName' => $domain,
'status' => $statuses, 'status' => $statuses,
@ -175,8 +290,8 @@ function handleDomainQuery($request, $response, $pdo, $domainName) {
'nameservers' => array_map(function ($nameserverDetails) { 'nameservers' => array_map(function ($nameserverDetails) {
return [ return [
'objectClassName' => 'nameserver', 'objectClassName' => 'nameserver',
'handle' => $nameserverDetails['host_id'] . '', // Use the 'host_id' from the query 'handle' => $nameserverDetails['host_id'] . '',
'ldhName' => $nameserverDetails['name'], // Use the 'name' from the query 'ldhName' => $nameserverDetails['name'],
'links' => [ 'links' => [
[ [
'href' => 'http://example.com/rdap/nameserver/' . $nameserverDetails['name'], 'href' => 'http://example.com/rdap/nameserver/' . $nameserverDetails['name'],
@ -193,6 +308,7 @@ function handleDomainQuery($request, $response, $pdo, $domainName) {
$response->header('Content-Type', 'application/json'); $response->header('Content-Type', 'application/json');
$response->end(json_encode($rdapResponse, JSON_UNESCAPED_SLASHES)); $response->end(json_encode($rdapResponse, JSON_UNESCAPED_SLASHES));
} catch (PDOException $e) { } catch (PDOException $e) {
$response->header('Content-Type', 'application/json');
$response->end(json_encode(['error' => 'Error connecting to the RDAP database'])); $response->end(json_encode(['error' => 'Error connecting to the RDAP database']));
return; return;
} }