diff --git a/automation/write-zone.php b/automation/write-zone.php
index 3154160..4c85270 100644
--- a/automation/write-zone.php
+++ b/automation/write-zone.php
@@ -42,19 +42,19 @@ Coroutine::create(function () use ($pool, $log, $c) {
while (list($id, $tld) = $sth->fetch(PDO::FETCH_NUM)) {
$tldRE = preg_quote($tld, '/');
$cleanedTld = ltrim(strtolower($tld), '.');
- $zone = new Zone('.');
+ $zone = new Zone($cleanedTld.'.');
$zone->setDefaultTtl(3600);
$soa = new ResourceRecord;
- $soa->setName($cleanedTld . '.');
+ $soa->setName('@');
$soa->setClass(Classes::INTERNET);
$soa->setRdata(Factory::Soa(
$c['ns']['ns1'] . '.',
$c['dns_soa'] . '.',
$timestamp,
- 3600,
- 14400,
- 604800,
+ 900,
+ 1800,
+ 3600000,
3600
));
$zone->addResourceRecord($soa);
@@ -91,7 +91,7 @@ Coroutine::create(function () use ($pool, $log, $c) {
while (list($did, $dname) = $sthDomains->fetch(PDO::FETCH_NUM)) {
if (isset($statuses[$did])) continue;
- $dname_clean = trim($dname, "$tldRE.");
+ $dname_clean = $dname;
$dname_clean = ($dname_clean == "$tld.") ? '@' : $dname_clean;
// NS records for the domain
@@ -109,7 +109,7 @@ Coroutine::create(function () use ($pool, $log, $c) {
$sthHostRecords = $pdo->prepare("SELECT host.name, host_addr.ip, host_addr.addr FROM host INNER JOIN host_addr ON host.id = host_addr.host_id WHERE host.domain_id = :did ORDER BY host.name");
$sthHostRecords->execute([':did' => $did]);
while (list($hname, $type, $addr) = $sthHostRecords->fetch(PDO::FETCH_NUM)) {
- $hname_clean = trim($hname, "$tldRE.");
+ $hname_clean = $hname;
$hname_clean = ($hname_clean == "$tld.") ? '@' : $hname_clean;
$record = new ResourceRecord;
$record->setName($hname_clean . '.');
diff --git a/cp/app/Controllers/ApplicationsController.php b/cp/app/Controllers/ApplicationsController.php
index e8e11d5..40eb3a0 100644
--- a/cp/app/Controllers/ApplicationsController.php
+++ b/cp/app/Controllers/ApplicationsController.php
@@ -23,7 +23,7 @@ class ApplicationsController extends Controller
return $response->withHeader('Location', '/domains')->withStatus(302);
}
}
-
+
public function createApplication(Request $request, Response $response)
{
if ($request->getMethod() === 'POST') {
@@ -31,6 +31,16 @@ class ApplicationsController extends Controller
$data = $request->getParsedBody();
$db = $this->container->get('db');
$domainName = $data['domainName'] ?? null;
+ // Convert to Punycode if the domain is not in ASCII
+ if (!mb_detect_encoding($domainName, 'ASCII', true)) {
+ $convertedDomain = idn_to_ascii($domainName, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ if ($convertedDomain === false) {
+ $this->container->get('flash')->addMessage('error', 'Application name conversion to Punycode failed');
+ return $response->withHeader('Location', '/application/create')->withStatus(302);
+ } else {
+ $domainName = $convertedDomain;
+ }
+ }
$registrar_id = $data['registrar'] ?? null;
$registrars = $db->select("SELECT id, clid, name FROM registrar");
if ($_SESSION["auth_roles"] != 0) {
diff --git a/cp/app/Controllers/DomainsController.php b/cp/app/Controllers/DomainsController.php
index cb50d87..bbb2cb8 100644
--- a/cp/app/Controllers/DomainsController.php
+++ b/cp/app/Controllers/DomainsController.php
@@ -27,6 +27,17 @@ class DomainsController extends Controller
$claims = $data['claims'] ?? null;
if ($domainName) {
+ // Convert to Punycode if the domain is not in ASCII
+ if (!mb_detect_encoding($domainName, 'ASCII', true)) {
+ $convertedDomain = idn_to_ascii($domainName, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ if ($convertedDomain === false) {
+ $this->container->get('flash')->addMessage('error', 'Domain conversion to Punycode failed');
+ return $response->withHeader('Location', '/domain/check')->withStatus(302);
+ } else {
+ $domainName = $convertedDomain;
+ }
+ }
+
$domainName = preg_replace('/[^\p{L}0-9-.]/u', '', $domainName);
$parts = extractDomainAndTLD($domainName);
@@ -105,6 +116,16 @@ class DomainsController extends Controller
$data = $request->getParsedBody();
$db = $this->container->get('db');
$domainName = $data['domainName'] ?? null;
+ // Convert to Punycode if the domain is not in ASCII
+ if (!mb_detect_encoding($domainName, 'ASCII', true)) {
+ $convertedDomain = idn_to_ascii($domainName, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ if ($convertedDomain === false) {
+ $this->container->get('flash')->addMessage('error', 'Domain conversion to Punycode failed');
+ return $response->withHeader('Location', '/domain/create')->withStatus(302);
+ } else {
+ $domainName = $convertedDomain;
+ }
+ }
$registrar_id = $data['registrar'] ?? null;
$registrars = $db->select("SELECT id, clid, name FROM registrar");
if ($_SESSION["auth_roles"] != 0) {
@@ -2229,6 +2250,16 @@ class DomainsController extends Controller
$data = $request->getParsedBody();
$db = $this->container->get('db');
$domainName = $data['domainName'] ?? null;
+ // Convert to Punycode if the domain is not in ASCII
+ if (!mb_detect_encoding($domainName, 'ASCII', true)) {
+ $convertedDomain = idn_to_ascii($domainName, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ if ($convertedDomain === false) {
+ $this->container->get('flash')->addMessage('error', 'Domain conversion to Punycode failed');
+ return $response->withHeader('Location', '/transfers')->withStatus(302);
+ } else {
+ $domainName = $convertedDomain;
+ }
+ }
$registrar_id = $data['registrar'] ?? null;
$authInfo = $data['authInfo'] ?? null;
$transferYears = $data['transferYears'] ?? null;
diff --git a/cp/app/Controllers/HostsController.php b/cp/app/Controllers/HostsController.php
index 8c54cc7..b13362e 100644
--- a/cp/app/Controllers/HostsController.php
+++ b/cp/app/Controllers/HostsController.php
@@ -34,6 +34,17 @@ class HostsController extends Controller
if ($hostName) {
$hostModel = new Host($this->container->get('db'));
+ // Convert to Punycode if the host is not in ASCII
+ if (!mb_detect_encoding($hostName, 'ASCII', true)) {
+ $convertedDomain = idn_to_ascii($hostName, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ if ($convertedDomain === false) {
+ $this->container->get('flash')->addMessage('error', 'Host conversion to Punycode failed');
+ return $response->withHeader('Location', '/host/create')->withStatus(302);
+ } else {
+ $hostName = $convertedDomain;
+ }
+ }
+
if (preg_match('/^([A-Z0-9]([A-Z0-9-]{0,61}[A-Z0-9]){0,1}\.){1,125}[A-Z0-9]([A-Z0-9-]{0,61}[A-Z0-9])$/i', $hostName) && strlen($hostName) < 254) {
$host_id_already_exist = $hostModel->getHostByNom($hostName);
if ($host_id_already_exist) {
@@ -235,14 +246,14 @@ class HostsController extends Controller
function isValidHostname($hostname) {
$hostname = trim($hostname);
-
+
// Check for IDN and convert to ASCII if necessary
if (mb_detect_encoding($hostname, 'ASCII', true) === false) {
- $hostname = idn_to_ascii($hostname, 0, INTL_IDNA_VARIANT_UTS46);
+ $hostname = idn_to_ascii($hostname, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
}
- // Regular expression for validating a hostname (simplified version)
- $pattern = '/^([a-zA-Z0-9-]{1,63}\.){1,}[a-zA-Z]{2,63}$/';
+ // Regular expression for validating a hostname
+ $pattern = '/^((xn--[a-zA-Z0-9-]{1,63}|[a-zA-Z0-9-]{1,63})\.){2,}(xn--[a-zA-Z0-9-]{2,63}|[a-zA-Z]{2,63})$/';
return preg_match($pattern, $hostname);
}
@@ -306,14 +317,14 @@ class HostsController extends Controller
function isValidHostname($hostname) {
$hostname = trim($hostname);
-
+
// Check for IDN and convert to ASCII if necessary
if (mb_detect_encoding($hostname, 'ASCII', true) === false) {
- $hostname = idn_to_ascii($hostname, 0, INTL_IDNA_VARIANT_UTS46);
+ $hostname = idn_to_ascii($hostname, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
}
- // Regular expression for validating a hostname (simplified version)
- $pattern = '/^([a-zA-Z0-9-]{1,63}\.){1,}[a-zA-Z]{2,63}$/';
+ // Regular expression for validating a hostname
+ $pattern = '/^((xn--[a-zA-Z0-9-]{1,63}|[a-zA-Z0-9-]{1,63})\.){2,}(xn--[a-zA-Z0-9-]{2,63}|[a-zA-Z]{2,63})$/';
return preg_match($pattern, $hostname);
}
diff --git a/cp/app/Controllers/SystemController.php b/cp/app/Controllers/SystemController.php
index 3b4cd4e..ba84e54 100644
--- a/cp/app/Controllers/SystemController.php
+++ b/cp/app/Controllers/SystemController.php
@@ -345,7 +345,7 @@ class SystemController extends Controller
return $response->withHeader('Location', '/registry/tld/create')->withStatus(302);
}
- switch ($data['extension']) {
+ switch ($data['script']) {
case 'ascii':
$idntable = '/^(?!-)(?!.*--)[A-Z0-9-]{1,63}(?beginTransaction();
-
+
$currentDateTime = new \DateTime();
$crdate = $currentDateTime->format('Y-m-d H:i:s.v'); // Current timestamp
+ // Convert to Punycode if the domain is not in ASCII
+ if (!mb_detect_encoding($data['extension'], 'ASCII', true)) {
+ $data['extension'] = str_replace('.', '', $data['extension']);
+ $convertedDomain = idn_to_ascii($data['extension'], IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ if ($convertedDomain === false) {
+ $this->container->get('flash')->addMessage('error', 'TLD conversion to Punycode failed');
+ return $response->withHeader('Location', '/registry/tld/create')->withStatus(302);
+ } else {
+ $data['extension'] = '.' . $convertedDomain;
+ }
+ }
+
$db->insert('domain_tld', [
'tld' => $data['extension'],
'idn_table' => $idntable,
@@ -536,11 +548,11 @@ class SystemController extends Controller
if ($args) {
$args = trim($args);
- if (!preg_match('/^\.[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)?[^\.]$/', $args)) {
+ if (!preg_match('/^\.(xn--[a-zA-Z0-9-]+|[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)?)$/', $args)) {
$this->container->get('flash')->addMessage('error', 'Invalid TLD format');
return $response->withHeader('Location', '/registry/tlds')->withStatus(302);
}
-
+
$validators = [
'extension' => v::stringType()->notEmpty()->length(3, 64),
'createm0' => v::numericVal()->between(0.00, 9999999.99, true),
@@ -827,7 +839,7 @@ class SystemController extends Controller
if ($args) {
$args = trim($args);
- if (!preg_match('/^\.[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)?[^\.]$/', $args)) {
+ if (!preg_match('/^\.(xn--[a-zA-Z0-9-]+|[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)?)$/', $args)) {
$this->container->get('flash')->addMessage('error', 'Invalid TLD format');
return $response->withHeader('Location', '/registry/tlds')->withStatus(302);
}
diff --git a/cp/bootstrap/helper.php b/cp/bootstrap/helper.php
index 6078694..46e53f5 100644
--- a/cp/bootstrap/helper.php
+++ b/cp/bootstrap/helper.php
@@ -232,6 +232,10 @@ function validate_label($label, $db) {
return 'Failed to fetch domain IDN table';
}
+ if (strpos($parts['domain'], 'xn--') === 0) {
+ $label = idn_to_utf8($parts['domain'], IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ }
+
// Check for invalid characters using fetched regex
if (!preg_match($idnRegex['idn_table'], $label)) {
return 'Invalid domain name format, please review registry policy about accepted labels';
diff --git a/cp/resources/views/partials/footer.twig b/cp/resources/views/partials/footer.twig
index 13cd25b..c36737e 100644
--- a/cp/resources/views/partials/footer.twig
+++ b/cp/resources/views/partials/footer.twig
@@ -14,7 +14,7 @@
Namingo
- v1.0.0-beta4
+ v1.0.0-beta5
diff --git a/cp/resources/views/partials/js-applications.twig b/cp/resources/views/partials/js-applications.twig
index a083daa..0bae1e3 100644
--- a/cp/resources/views/partials/js-applications.twig
+++ b/cp/resources/views/partials/js-applications.twig
@@ -21,10 +21,10 @@
var isInvalid = rowData.application_status.some(statusObj => statusObj.status && statusObj.status.includes('invalid'));
if (!isRejected && !isInvalid) {
- actionButtons += ` `;
- actionButtons += ` `;
- actionButtons += ` `;
- actionButtons += ``;
+ actionButtons += ` `;
+ actionButtons += ` `;
+ actionButtons += ` `;
+ actionButtons += ``;
} else {
actionButtons += ` Completed`;
}
diff --git a/cp/resources/views/partials/js-contacts.twig b/cp/resources/views/partials/js-contacts.twig
index 97c6b58..a77fada 100644
--- a/cp/resources/views/partials/js-contacts.twig
+++ b/cp/resources/views/partials/js-contacts.twig
@@ -16,8 +16,8 @@
function actionsFormatter(cell, formatterParams, onRendered) {
return `
-
-
+
+
`;
}
diff --git a/cp/resources/views/partials/js-domains.twig b/cp/resources/views/partials/js-domains.twig
index 7b14c3f..c6653b9 100644
--- a/cp/resources/views/partials/js-domains.twig
+++ b/cp/resources/views/partials/js-domains.twig
@@ -21,15 +21,15 @@
var hasPendingRestore = rowData.rgpstatus ? rowData.rgpstatus.includes('pendingRestore') : false;
// Common action button for all statuses
- actionButtons += ` `;
+ actionButtons += ` `;
if (hasPendingRestore) {
actionButtons += ``;
} else if (hasPendingDelete) {
actionButtons += ``;
} else {
- actionButtons += ` `;
- actionButtons += ``;
+ actionButtons += ` `;
+ actionButtons += ``;
}
return actionButtons;
diff --git a/cp/resources/views/partials/js-hosts.twig b/cp/resources/views/partials/js-hosts.twig
index 6eea534..9f52f1d 100644
--- a/cp/resources/views/partials/js-hosts.twig
+++ b/cp/resources/views/partials/js-hosts.twig
@@ -16,8 +16,8 @@
function actionsFormatter(cell, formatterParams, onRendered) {
return `
-
-
+
+
`;
}
diff --git a/cp/resources/views/partials/js-transfers.twig b/cp/resources/views/partials/js-transfers.twig
index 4f35058..21a1f36 100644
--- a/cp/resources/views/partials/js-transfers.twig
+++ b/cp/resources/views/partials/js-transfers.twig
@@ -21,14 +21,14 @@
var clidValue = document.getElementById('clid').value;
if (hasPendingStatus && clidValue === '0') {
- actionButtons += ` `;
- actionButtons += ` `;
- actionButtons += ``;
+ actionButtons += ` `;
+ actionButtons += ` `;
+ actionButtons += ``;
} else if (clidValue === rowData.reid) {
- actionButtons += ` `;
+ actionButtons += ` `;
} else if (clidValue === rowData.acid) {
- actionButtons += ` `;
- actionButtons += ``;
+ actionButtons += ` `;
+ actionButtons += ``;
} else {
actionButtons += ` {{ __('Completed') }}`;
}
diff --git a/das/start_das.php b/das/start_das.php
index 9a6ded3..3f63527 100644
--- a/das/start_das.php
+++ b/das/start_das.php
@@ -67,11 +67,21 @@ $server->on('receive', function ($server, $fd, $reactorId, $data) use ($c, $pool
$server->send($fd, "domain name is too long");
$server->close($fd);
}
- $domain = strtoupper($domain);
- if (preg_match("/(^-|^\.|-\.|\.-|--|\.\.|-$|\.$)/", $domain)) {
+ // Convert to Punycode if the domain is not in ASCII
+ if (!mb_detect_encoding($domain, 'ASCII', true)) {
+ $convertedDomain = idn_to_ascii($domain, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ if ($convertedDomain === false) {
+ $server->send($fd, "Domain conversion to Punycode failed");
+ $server->close($fd);
+ } else {
+ $domain = $convertedDomain;
+ }
+ }
+ if (!preg_match('/^(?:(xn--[a-zA-Z0-9-]{1,63}|[a-zA-Z0-9-]{1,63})\.){1,3}(xn--[a-zA-Z0-9-]{2,63}|[a-zA-Z]{2,63})$/', $domain)) {
$server->send($fd, "domain name invalid format");
$server->close($fd);
}
+ $domain = strtoupper($domain);
// Extract TLD from the domain and prepend a dot
$parts = explode('.', $domain);
@@ -113,8 +123,13 @@ $server->on('receive', function ($server, $fd, $reactorId, $data) use ($c, $pool
}
// Check for invalid characters using fetched regex
- if (!preg_match($idnRegex, $domain)) {
- $server->send($fd, "Domain name invalid format");
+ if (strpos(strtolower($parts[0]), 'xn--') === 0) {
+ $label = idn_to_utf8(strtolower($parts[0]), IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ } else {
+ $label = strtolower($parts[0]);
+ }
+ if (!preg_match($idnRegex, $label)) {
+ $server->send($fd, "Domain name invalid IDN characters");
$server->close($fd);
return;
}
diff --git a/epp/src/helpers.php b/epp/src/helpers.php
index 562a803..3c4a879 100644
--- a/epp/src/helpers.php
+++ b/epp/src/helpers.php
@@ -195,7 +195,7 @@ function validate_label($label, $pdo) {
if (strlen($label) < 2) {
return 'Total lenght of your domain must be greater then 2 characters';
}
- if (preg_match("/(^-|^\.|-\.|\.-|--|\.\.|-$|\.$)/", $label)) {
+ if (strpos($label, 'xn--') === false && preg_match("/(^-|^\.|-\.|\.-|--|\.\.|-$|\.$)/", $label)) {
return 'Invalid domain name format, cannot begin or end with a hyphen (-)';
}
@@ -223,6 +223,10 @@ function validate_label($label, $pdo) {
return 'Failed to fetch domain IDN table';
}
+ if (strpos($parts['domain'], 'xn--') === 0) {
+ $label = idn_to_utf8($parts['domain'], IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ }
+
// Check for invalid characters using fetched regex
if (!preg_match($idnRegex, $label)) {
$server->send($fd, "Domain name invalid format");
@@ -247,7 +251,7 @@ function extractDomainAndTLD($urlString) {
// Parse the URL to get the host
$parts = parse_url($urlString);
$host = $parts['host'] ?? $urlString;
-
+
// Sort test TLDs by length (longest first) to match the longest possible TLD
usort($testTlds, function ($a, $b) {
return strlen($b) - strlen($a);
diff --git a/rdap/start_rdap.php b/rdap/start_rdap.php
index 119885d..f71fcee 100644
--- a/rdap/start_rdap.php
+++ b/rdap/start_rdap.php
@@ -173,7 +173,7 @@ $http->start();
function handleDomainQuery($request, $response, $pdo, $domainName, $c, $log) {
// Extract and validate the domain name from the request
$domain = trim($domainName);
-
+
// Empty domain check
if (!$domain) {
$response->header('Content-Type', 'application/json');
@@ -189,9 +189,22 @@ function handleDomainQuery($request, $response, $pdo, $domainName, $c, $log) {
$response->end(json_encode(['error' => 'Domain name is too long']));
return;
}
-
+
+ // Convert to Punycode if the domain is not in ASCII
+ if (!mb_detect_encoding($domain, 'ASCII', true)) {
+ $convertedDomain = idn_to_ascii($domain, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ if ($convertedDomain === false) {
+ $response->header('Content-Type', 'application/json');
+ $response->status(400); // Bad Request
+ $response->end(json_encode(['error' => 'Domain conversion to Punycode failed']));
+ return;
+ } else {
+ $domain = $convertedDomain;
+ }
+ }
+
// Check for prohibited patterns in domain names
- if (preg_match("/(^-|^\.|-\.|\.-|--|\.\.|-$|\.$)/", $domain)) {
+ if (!preg_match('/^(?:(xn--[a-zA-Z0-9-]{1,63}|[a-zA-Z0-9-]{1,63})\.){1,3}(xn--[a-zA-Z0-9-]{2,63}|[a-zA-Z]{2,63})$/', $domain)) {
$response->header('Content-Type', 'application/json');
$response->status(400); // Bad Request
$response->end(json_encode(['error' => 'Domain name invalid format']));
@@ -241,10 +254,15 @@ function handleDomainQuery($request, $response, $pdo, $domainName, $c, $log) {
}
// Check for invalid characters using fetched regex
- if (!preg_match($idnRegex, $domain)) {
+ if (strpos($parts[0], 'xn--') === 0) {
+ $label = idn_to_utf8($parts[0], IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ } else {
+ $label = $parts[0];
+ }
+ if (!preg_match($idnRegex, $label)) {
$response->header('Content-Type', 'application/json');
$response->status(400); // Bad Request
- $response->end(json_encode(['error' => 'Domain name invalid format']));
+ $response->end(json_encode(['error' => 'Domain name invalid IDN characters']));
return;
}
@@ -1018,9 +1036,22 @@ function handleNameserverQuery($request, $response, $pdo, $nameserverHandle, $c,
$response->end(json_encode(['error' => 'Nameserver is too long']));
return;
}
+
+ // Convert to Punycode if the host is not in ASCII
+ if (!mb_detect_encoding($ns, 'ASCII', true)) {
+ $convertedDomain = idn_to_ascii($ns, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ if ($convertedDomain === false) {
+ $response->header('Content-Type', 'application/json');
+ $response->status(400); // Bad Request
+ $response->end(json_encode(['error' => 'Host conversion to Punycode failed']));
+ return;
+ } else {
+ $ns = $convertedDomain;
+ }
+ }
// Check for prohibited patterns in nameserver
- if (!preg_match("/^(?!-)[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/", $ns)) {
+ if (!preg_match('/^((xn--[a-zA-Z0-9-]{1,63}|[a-zA-Z0-9-]{1,63})\.){2,}(xn--[a-zA-Z0-9-]{2,63}|[a-zA-Z]{2,63})$/', $ns)) {
$response->header('Content-Type', 'application/json');
$response->status(400); // Bad Request
$response->end(json_encode(['error' => 'Nameserver invalid format']));
@@ -1420,8 +1451,21 @@ function handleDomainSearchQuery($request, $response, $pdo, $searchPattern, $c,
return;
}
+ // Convert to Punycode if the domain is not in ASCII
+ if (!mb_detect_encoding($domain, 'ASCII', true)) {
+ $convertedDomain = idn_to_ascii($domain, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ if ($convertedDomain === false) {
+ $response->header('Content-Type', 'application/json');
+ $response->status(400); // Bad Request
+ $response->end(json_encode(['error' => 'Domain conversion to Punycode failed']));
+ return;
+ } else {
+ $domain = $convertedDomain;
+ }
+ }
+
// Check for prohibited patterns in domain names
- if (preg_match("/(^-|^\.|-\.|\.-|--|\.\.|-$|\.$)/", $domain)) {
+ if (!preg_match('/^(?:(xn--[a-zA-Z0-9-]{1,63}|[a-zA-Z0-9-]{1,63})\.){1,3}(xn--[a-zA-Z0-9-]{2,63}|[a-zA-Z]{2,63})$/', $domain)) {
$response->header('Content-Type', 'application/json');
$response->status(400); // Bad Request
$response->end(json_encode(['error' => 'Domain name invalid format']));
@@ -1471,10 +1515,15 @@ function handleDomainSearchQuery($request, $response, $pdo, $searchPattern, $c,
}
// Check for invalid characters using fetched regex
- if (!preg_match($idnRegex, $domain)) {
+ if (strpos($parts[0], 'xn--') === 0) {
+ $label = idn_to_utf8($parts[0], IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ } else {
+ $label = $parts[0];
+ }
+ if (!preg_match($idnRegex, $label)) {
$response->header('Content-Type', 'application/json');
$response->status(400); // Bad Request
- $response->end(json_encode(['error' => 'Domain name invalid format']));
+ $response->end(json_encode(['error' => 'Domain name invalid IDN characters']));
return;
}
@@ -1928,8 +1977,21 @@ function handleNameserverSearchQuery($request, $response, $pdo, $searchPattern,
return;
}
+ // Convert to Punycode if the host is not in ASCII
+ if (!mb_detect_encoding($ns, 'ASCII', true)) {
+ $convertedDomain = idn_to_ascii($ns, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ if ($convertedDomain === false) {
+ $response->header('Content-Type', 'application/json');
+ $response->status(400); // Bad Request
+ $response->end(json_encode(['error' => 'Host conversion to Punycode failed']));
+ return;
+ } else {
+ $ns = $convertedDomain;
+ }
+ }
+
// Check for prohibited patterns in nameserver
- if (!preg_match("/^(?!-)[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/", $ns)) {
+ if (!preg_match('/^((xn--[a-zA-Z0-9-]{1,63}|[a-zA-Z0-9-]{1,63})\.){2,}(xn--[a-zA-Z0-9-]{2,63}|[a-zA-Z]{2,63})$/', $ns)) {
$response->header('Content-Type', 'application/json');
$response->status(400); // Bad Request
$response->end(json_encode(['error' => 'Nameserver invalid format']));
diff --git a/whois/port43/start_whois.php b/whois/port43/start_whois.php
index adcd8a2..785b176 100644
--- a/whois/port43/start_whois.php
+++ b/whois/port43/start_whois.php
@@ -74,11 +74,21 @@ $server->on('receive', function ($server, $fd, $reactorId, $data) use ($c, $pool
$server->send($fd, "domain name is too long");
$server->close($fd);
}
- $domain = strtoupper($domain);
- if (preg_match("/(^-|^\.|-\.|\.-|--|\.\.|-$|\.$)/", $domain)) {
+ // Convert to Punycode if the domain is not in ASCII
+ if (!mb_detect_encoding($domain, 'ASCII', true)) {
+ $convertedDomain = idn_to_ascii($domain, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ if ($convertedDomain === false) {
+ $server->send($fd, "Domain conversion to Punycode failed");
+ $server->close($fd);
+ } else {
+ $domain = $convertedDomain;
+ }
+ }
+ if (!preg_match('/^(?:(xn--[a-zA-Z0-9-]{1,63}|[a-zA-Z0-9-]{1,63})\.){1,3}(xn--[a-zA-Z0-9-]{2,63}|[a-zA-Z]{2,63})$/', $domain)) {
$server->send($fd, "domain name invalid format");
$server->close($fd);
}
+ $domain = strtoupper($domain);
// Extract TLD from the domain and prepend a dot
$parts = explode('.', $domain);
@@ -120,8 +130,13 @@ $server->on('receive', function ($server, $fd, $reactorId, $data) use ($c, $pool
}
// Check for invalid characters using fetched regex
- if (!preg_match($idnRegex, $domain)) {
- $server->send($fd, "Domain name invalid format");
+ if (strpos(strtolower($parts[0]), 'xn--') === 0) {
+ $label = idn_to_utf8(strtolower($parts[0]), IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ } else {
+ $label = strtolower($parts[0]);
+ }
+ if (!preg_match($idnRegex, $label)) {
+ $server->send($fd, "Domain name invalid IDN characters");
$server->close($fd);
return;
}
@@ -434,8 +449,19 @@ $server->on('receive', function ($server, $fd, $reactorId, $data) use ($c, $pool
$server->send($fd, "nameserver is too long");
$server->close($fd);
}
+
+ // Convert to Punycode if the host is not in ASCII
+ if (!mb_detect_encoding($nameserver, 'ASCII', true)) {
+ $convertedDomain = idn_to_ascii($nameserver, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
+ if ($convertedDomain === false) {
+ $server->send($fd, "Host conversion to Punycode failed.");
+ $server->close($fd);
+ } else {
+ $nameserver = $convertedDomain;
+ }
+ }
- if (!preg_match('/^([a-zA-Z0-9\-]+\.)+[a-zA-Z]{2,}$/', $nameserver)) {
+ if (!preg_match('/^((xn--[a-zA-Z0-9-]{1,63}|[a-zA-Z0-9-]{1,63})\.){2,}(xn--[a-zA-Z0-9-]{2,63}|[a-zA-Z]{2,63})$/', $nameserver)) {
$server->send($fd, "Nameserver contains invalid characters or is not in the correct format.");
$server->close($fd);
}