mirror of
https://github.com/getnamingo/registry.git
synced 2025-05-17 10:06:59 +02:00
EPP domain check logic and speed improvements
Also better domain validation as a whole
This commit is contained in:
parent
614829684e
commit
8428a05bfb
2 changed files with 86 additions and 89 deletions
|
@ -255,62 +255,59 @@ function processDomainCheck($conn, $db, $xml, $trans, $clid) {
|
||||||
$names = [];
|
$names = [];
|
||||||
foreach ($domains as $domain) {
|
foreach ($domains as $domain) {
|
||||||
$domainName = (string) $domain;
|
$domainName = (string) $domain;
|
||||||
|
|
||||||
// Check if the domain is already taken
|
|
||||||
$stmt = $db->prepare("SELECT name FROM domain WHERE name = :domainName");
|
|
||||||
$stmt->bindParam(':domainName', $domainName, PDO::PARAM_STR);
|
|
||||||
$stmt->execute();
|
|
||||||
$taken = $stmt->fetchColumn();
|
|
||||||
$availability = $taken ? '0' : '1';
|
|
||||||
|
|
||||||
// Initialize a new domain entry with the domain name
|
|
||||||
$domainEntry = [$domainName];
|
$domainEntry = [$domainName];
|
||||||
|
|
||||||
if ($availability === '0') {
|
$invalid_label = validate_label($domainName, $db);
|
||||||
// Domain is taken
|
if ($invalid_label) {
|
||||||
$domainEntry[] = 0; // Set status to unavailable
|
$domainEntry[] = 0; // Unavailable
|
||||||
$domainEntry[] = 'In use';
|
$domainEntry[] = ucfirst($invalid_label);
|
||||||
} else {
|
$names[] = $domainEntry;
|
||||||
// Check if the domain is reserved
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$parts = extractDomainAndTLD($domainName);
|
$parts = extractDomainAndTLD($domainName);
|
||||||
$label = $parts['domain'];
|
$label = $parts['domain'];
|
||||||
|
|
||||||
$stmt = $db->prepare("SELECT type FROM reserved_domain_names WHERE name = :domainName LIMIT 1");
|
$stmt = $db->prepare("
|
||||||
$stmt->bindParam(':domainName', $label, PDO::PARAM_STR);
|
SELECT 'taken' AS type FROM domain WHERE name = :domainName
|
||||||
|
UNION ALL
|
||||||
|
SELECT type FROM reserved_domain_names WHERE name = :label
|
||||||
|
LIMIT 1
|
||||||
|
");
|
||||||
|
$stmt->bindParam(':domainName', $domainName, PDO::PARAM_STR);
|
||||||
|
$stmt->bindParam(':label', $label, PDO::PARAM_STR);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
$reserved = $stmt->fetchColumn();
|
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
if ($reserved) {
|
if ($result) {
|
||||||
|
if ($result['type'] === 'taken') {
|
||||||
|
// Domain is registered
|
||||||
|
$domainEntry[] = 0; // Unavailable
|
||||||
|
$domainEntry[] = 'In use';
|
||||||
|
} else {
|
||||||
|
// Domain is reserved
|
||||||
if ($allocation_token !== null) {
|
if ($allocation_token !== null) {
|
||||||
$allocationTokenValue = (string)$allocation_token;
|
$allocationTokenValue = (string)$allocation_token;
|
||||||
|
$stmt = $db->prepare("SELECT token FROM allocation_tokens WHERE domain_name = :label AND token = :token LIMIT 1");
|
||||||
$stmt = $db->prepare("SELECT token FROM allocation_tokens WHERE domain_name = :domainName AND token = :token LIMIT 1");
|
$stmt->bindParam(':label', $label, PDO::PARAM_STR);
|
||||||
$stmt->bindParam(':domainName', $label, PDO::PARAM_STR);
|
|
||||||
$stmt->bindParam(':token', $allocationTokenValue, PDO::PARAM_STR);
|
$stmt->bindParam(':token', $allocationTokenValue, PDO::PARAM_STR);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
$token = $stmt->fetchColumn();
|
$token = $stmt->fetchColumn();
|
||||||
|
|
||||||
if ($token) {
|
if ($token) {
|
||||||
$domainEntry[] = 1;
|
$domainEntry[] = 1; // Available with a valid allocation token
|
||||||
} else {
|
} else {
|
||||||
$domainEntry[] = 0;
|
$domainEntry[] = 0;
|
||||||
$domainEntry[] = 'Allocation Token mismatch';
|
$domainEntry[] = 'Allocation Token mismatch';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$domainEntry[] = 0; // Set status to unavailable
|
$domainEntry[] = 0; // Unavailable
|
||||||
$domainEntry[] = ucfirst($reserved); // Capitalize the first letter
|
$domainEntry[] = ucfirst($result['type']); // Reserved reason
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$invalid_label = validate_label($domainName, $db);
|
// Domain is available
|
||||||
|
$domainEntry[] = 1;
|
||||||
// Check if the domain is Invalid
|
|
||||||
if ($invalid_label) {
|
|
||||||
$domainEntry[] = 0; // Set status to unavailable
|
|
||||||
$domainEntry[] = ucfirst($invalid_label); // Capitalize the first letter
|
|
||||||
} else {
|
|
||||||
$domainEntry[] = 1; // Domain is available
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append this domain entry to names
|
// Append this domain entry to names
|
||||||
|
@ -433,7 +430,7 @@ function processDomainCheck($conn, $db, $xml, $trans, $clid) {
|
||||||
'clTRID' => $clTRID,
|
'clTRID' => $clTRID,
|
||||||
'svTRID' => $svTRID,
|
'svTRID' => $svTRID,
|
||||||
];
|
];
|
||||||
if ($fees) {
|
if (!empty($fees)) {
|
||||||
$response['fees'] = $fees;
|
$response['fees'] = $fees;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -182,67 +182,67 @@ function validate_identifier($identifier) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDomainValid(string $domain): bool {
|
function validate_label($domain, $pdo) {
|
||||||
// Ensure the domain only contains valid characters (letters, numbers, hyphens, and dots)
|
if (!$domain) {
|
||||||
if (!preg_match('/^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/', $domain)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split the domain into its labels (subdomains, SLD, etc.)
|
|
||||||
$labels = explode('.', $domain);
|
|
||||||
foreach ($labels as $label) {
|
|
||||||
if (strlen($label) > 63 || empty($label)) { // Labels cannot be empty or exceed 63 chars
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function validate_label($label, $pdo) {
|
|
||||||
if (!$label) {
|
|
||||||
return 'You must enter a domain name';
|
return 'You must enter a domain name';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure input contains only valid domain characters
|
// Ensure domain has at least one dot (.) separating labels
|
||||||
if (!preg_match('/^([a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}$/', $label)) {
|
if (strpos($domain, '.') === false) {
|
||||||
return 'Invalid domain name format: only letters, numbers, dots, and hyphens allowed';
|
return 'Invalid domain name format: must contain at least one dot (.)';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isDomainValid($label)) {
|
// Split domain into labels (subdomains, SLD, TLD)
|
||||||
return 'Domain label is too long (exceeds 63 characters)';
|
$labels = explode('.', $domain);
|
||||||
|
foreach ($labels as $label) {
|
||||||
|
$len = strlen($label);
|
||||||
|
|
||||||
|
// Labels cannot be empty, shorter than 2, or longer than 63 characters
|
||||||
|
if ($len < 2 || $len > 63) {
|
||||||
|
return 'Each domain label must be between 2 and 63 characters';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure domain starts and ends with an alphanumeric character
|
if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$/', $label)) {
|
||||||
$parts = explode('.', $label);
|
|
||||||
foreach ($parts as $part) {
|
|
||||||
if (!preg_match('/^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$/', $part)) {
|
|
||||||
return 'Each domain label must start and end with an alphanumeric character';
|
return 'Each domain label must start and end with an alphanumeric character';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if it's a Punycode label (IDN)
|
||||||
|
if (strpos($label, 'xn--') === 0) {
|
||||||
|
// Ensure valid Punycode structure
|
||||||
|
if (!preg_match('/^xn--[a-zA-Z0-9-]+$/', $label)) {
|
||||||
|
return 'Invalid Punycode format';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert Punycode to UTF-8
|
||||||
|
$decoded = idn_to_utf8($label, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
|
||||||
|
if ($decoded === false || $decoded === '') {
|
||||||
|
return 'Invalid Punycode conversion';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure decoded label follows normal domain rules
|
||||||
|
if (!preg_match('/^[\p{L}0-9][\p{L}0-9-]*[\p{L}0-9]$/u', $decoded)) {
|
||||||
|
return 'IDN must start and end with a letter or number';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Prevent consecutive or invalid hyphen usage
|
||||||
|
if (preg_match('/--|\.\./', $label)) {
|
||||||
|
return 'Domain labels cannot contain consecutive dashes (--) or dots (..)';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract domain and TLD
|
// Extract domain and TLD
|
||||||
$parts = extractDomainAndTLD($label);
|
$parts = extractDomainAndTLD($domain);
|
||||||
if (!$parts || empty($parts['domain']) || empty($parts['tld'])) {
|
if (!$parts || empty($parts['domain']) || empty($parts['tld'])) {
|
||||||
return 'Invalid domain structure, unable to parse domain name';
|
return 'Invalid domain structure, unable to parse domain name';
|
||||||
}
|
}
|
||||||
|
|
||||||
$tld = "." . $parts['tld'];
|
$tld = "." . $parts['tld'];
|
||||||
|
|
||||||
if (strlen($parts['domain']) > 63) {
|
// Validate domain length
|
||||||
return 'Total length of your domain must be less than 63 characters';
|
$domainLength = strlen($parts['domain']);
|
||||||
}
|
if ($domainLength < 2 || $domainLength > 63) {
|
||||||
|
return 'Domain length must be between 2 and 63 characters';
|
||||||
if (strlen($parts['domain']) < 2) {
|
|
||||||
return 'Total length of your domain must be greater than 2 characters';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strpos($label, '.') === false) {
|
|
||||||
return 'Invalid domain name format, must contain at least one dot (.)';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure no invalid use of hyphens (double dashes, leading/trailing dashes)
|
|
||||||
if (strpos($parts['domain'], 'xn--') === false && preg_match("/(^-|^\.|-\.|\.-|--|\.\.|-$|\.$)/", $parts['domain'])) {
|
|
||||||
return 'Domain name cannot contain consecutive dashes (--) unless it is a punycode domain';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the TLD exists in the domain_tld table
|
// Check if the TLD exists in the domain_tld table
|
||||||
|
@ -255,11 +255,11 @@ function validate_label($label, $pdo) {
|
||||||
return 'Zone is not supported';
|
return 'Zone is not supported';
|
||||||
}
|
}
|
||||||
|
|
||||||
// If domain is an IDN (Punycode), convert it to Unicode
|
// IDN-specific validation (only if the domain contains Punycode)
|
||||||
if (strpos($parts['domain'], 'xn--') === 0) {
|
if (strpos($parts['domain'], 'xn--') === 0) {
|
||||||
$label = idn_to_utf8($parts['domain'], IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
|
$label = idn_to_utf8($parts['domain'], IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
|
||||||
|
|
||||||
// Fetch the IDN regex for the given TLD (only if it's an IDN)
|
// Fetch the IDN regex for the given TLD
|
||||||
$stmtRegex = $pdo->prepare("SELECT idn_table FROM domain_tld WHERE tld = :tld");
|
$stmtRegex = $pdo->prepare("SELECT idn_table FROM domain_tld WHERE tld = :tld");
|
||||||
$stmtRegex->bindParam(':tld', $tld, PDO::PARAM_STR);
|
$stmtRegex->bindParam(':tld', $tld, PDO::PARAM_STR);
|
||||||
$stmtRegex->execute();
|
$stmtRegex->execute();
|
||||||
|
@ -269,8 +269,8 @@ function validate_label($label, $pdo) {
|
||||||
return 'Failed to fetch domain IDN table';
|
return 'Failed to fetch domain IDN table';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for invalid characters using fetched regex
|
// Check against IDN regex
|
||||||
if (!preg_match($idnRegex['idn_table'], $label)) {
|
if (!preg_match($idnRegex, $label)) {
|
||||||
return 'Invalid domain name format, please review registry policy about accepted labels';
|
return 'Invalid domain name format, please review registry policy about accepted labels';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue