Much better IDN support, fixes #73

This commit is contained in:
Pinga 2024-01-23 12:58:01 +02:00
parent 4d57ad71a1
commit 137f8170e2
16 changed files with 235 additions and 60 deletions

View file

@ -42,19 +42,19 @@ Coroutine::create(function () use ($pool, $log, $c) {
while (list($id, $tld) = $sth->fetch(PDO::FETCH_NUM)) { while (list($id, $tld) = $sth->fetch(PDO::FETCH_NUM)) {
$tldRE = preg_quote($tld, '/'); $tldRE = preg_quote($tld, '/');
$cleanedTld = ltrim(strtolower($tld), '.'); $cleanedTld = ltrim(strtolower($tld), '.');
$zone = new Zone('.'); $zone = new Zone($cleanedTld.'.');
$zone->setDefaultTtl(3600); $zone->setDefaultTtl(3600);
$soa = new ResourceRecord; $soa = new ResourceRecord;
$soa->setName($cleanedTld . '.'); $soa->setName('@');
$soa->setClass(Classes::INTERNET); $soa->setClass(Classes::INTERNET);
$soa->setRdata(Factory::Soa( $soa->setRdata(Factory::Soa(
$c['ns']['ns1'] . '.', $c['ns']['ns1'] . '.',
$c['dns_soa'] . '.', $c['dns_soa'] . '.',
$timestamp, $timestamp,
3600, 900,
14400, 1800,
604800, 3600000,
3600 3600
)); ));
$zone->addResourceRecord($soa); $zone->addResourceRecord($soa);
@ -91,7 +91,7 @@ Coroutine::create(function () use ($pool, $log, $c) {
while (list($did, $dname) = $sthDomains->fetch(PDO::FETCH_NUM)) { while (list($did, $dname) = $sthDomains->fetch(PDO::FETCH_NUM)) {
if (isset($statuses[$did])) continue; if (isset($statuses[$did])) continue;
$dname_clean = trim($dname, "$tldRE."); $dname_clean = $dname;
$dname_clean = ($dname_clean == "$tld.") ? '@' : $dname_clean; $dname_clean = ($dname_clean == "$tld.") ? '@' : $dname_clean;
// NS records for the domain // 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 = $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]); $sthHostRecords->execute([':did' => $did]);
while (list($hname, $type, $addr) = $sthHostRecords->fetch(PDO::FETCH_NUM)) { 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; $hname_clean = ($hname_clean == "$tld.") ? '@' : $hname_clean;
$record = new ResourceRecord; $record = new ResourceRecord;
$record->setName($hname_clean . '.'); $record->setName($hname_clean . '.');

View file

@ -31,6 +31,16 @@ class ApplicationsController extends Controller
$data = $request->getParsedBody(); $data = $request->getParsedBody();
$db = $this->container->get('db'); $db = $this->container->get('db');
$domainName = $data['domainName'] ?? null; $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; $registrar_id = $data['registrar'] ?? null;
$registrars = $db->select("SELECT id, clid, name FROM registrar"); $registrars = $db->select("SELECT id, clid, name FROM registrar");
if ($_SESSION["auth_roles"] != 0) { if ($_SESSION["auth_roles"] != 0) {

View file

@ -27,6 +27,17 @@ class DomainsController extends Controller
$claims = $data['claims'] ?? null; $claims = $data['claims'] ?? null;
if ($domainName) { 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); $domainName = preg_replace('/[^\p{L}0-9-.]/u', '', $domainName);
$parts = extractDomainAndTLD($domainName); $parts = extractDomainAndTLD($domainName);
@ -105,6 +116,16 @@ class DomainsController extends Controller
$data = $request->getParsedBody(); $data = $request->getParsedBody();
$db = $this->container->get('db'); $db = $this->container->get('db');
$domainName = $data['domainName'] ?? null; $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; $registrar_id = $data['registrar'] ?? null;
$registrars = $db->select("SELECT id, clid, name FROM registrar"); $registrars = $db->select("SELECT id, clid, name FROM registrar");
if ($_SESSION["auth_roles"] != 0) { if ($_SESSION["auth_roles"] != 0) {
@ -2229,6 +2250,16 @@ class DomainsController extends Controller
$data = $request->getParsedBody(); $data = $request->getParsedBody();
$db = $this->container->get('db'); $db = $this->container->get('db');
$domainName = $data['domainName'] ?? null; $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; $registrar_id = $data['registrar'] ?? null;
$authInfo = $data['authInfo'] ?? null; $authInfo = $data['authInfo'] ?? null;
$transferYears = $data['transferYears'] ?? null; $transferYears = $data['transferYears'] ?? null;

View file

@ -34,6 +34,17 @@ class HostsController extends Controller
if ($hostName) { if ($hostName) {
$hostModel = new Host($this->container->get('db')); $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) { 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); $host_id_already_exist = $hostModel->getHostByNom($hostName);
if ($host_id_already_exist) { if ($host_id_already_exist) {
@ -238,11 +249,11 @@ class HostsController extends Controller
// Check for IDN and convert to ASCII if necessary // Check for IDN and convert to ASCII if necessary
if (mb_detect_encoding($hostname, 'ASCII', true) === false) { 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) // Regular expression for validating a hostname
$pattern = '/^([a-zA-Z0-9-]{1,63}\.){1,}[a-zA-Z]{2,63}$/'; $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); return preg_match($pattern, $hostname);
} }
@ -309,11 +320,11 @@ class HostsController extends Controller
// Check for IDN and convert to ASCII if necessary // Check for IDN and convert to ASCII if necessary
if (mb_detect_encoding($hostname, 'ASCII', true) === false) { 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) // Regular expression for validating a hostname
$pattern = '/^([a-zA-Z0-9-]{1,63}\.){1,}[a-zA-Z]{2,63}$/'; $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); return preg_match($pattern, $hostname);
} }

View file

@ -345,7 +345,7 @@ class SystemController extends Controller
return $response->withHeader('Location', '/registry/tld/create')->withStatus(302); return $response->withHeader('Location', '/registry/tld/create')->withStatus(302);
} }
switch ($data['extension']) { switch ($data['script']) {
case 'ascii': case 'ascii':
$idntable = '/^(?!-)(?!.*--)[A-Z0-9-]{1,63}(?<!-)(.(?!-)(?!.*--)[A-Z0-9-]{1,63}(?<!-))*$/i'; $idntable = '/^(?!-)(?!.*--)[A-Z0-9-]{1,63}(?<!-)(.(?!-)(?!.*--)[A-Z0-9-]{1,63}(?<!-))*$/i';
break; break;
@ -369,6 +369,18 @@ class SystemController extends Controller
$currentDateTime = new \DateTime(); $currentDateTime = new \DateTime();
$crdate = $currentDateTime->format('Y-m-d H:i:s.v'); // Current timestamp $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', [ $db->insert('domain_tld', [
'tld' => $data['extension'], 'tld' => $data['extension'],
'idn_table' => $idntable, 'idn_table' => $idntable,
@ -536,7 +548,7 @@ class SystemController extends Controller
if ($args) { if ($args) {
$args = trim($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'); $this->container->get('flash')->addMessage('error', 'Invalid TLD format');
return $response->withHeader('Location', '/registry/tlds')->withStatus(302); return $response->withHeader('Location', '/registry/tlds')->withStatus(302);
} }
@ -827,7 +839,7 @@ class SystemController extends Controller
if ($args) { if ($args) {
$args = trim($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'); $this->container->get('flash')->addMessage('error', 'Invalid TLD format');
return $response->withHeader('Location', '/registry/tlds')->withStatus(302); return $response->withHeader('Location', '/registry/tlds')->withStatus(302);
} }

View file

@ -232,6 +232,10 @@ function validate_label($label, $db) {
return 'Failed to fetch domain IDN table'; 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 // Check for invalid characters using fetched regex
if (!preg_match($idnRegex['idn_table'], $label)) { if (!preg_match($idnRegex['idn_table'], $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';

View file

@ -14,7 +14,7 @@
<a href="https://namingo.org" target="_blank" class="link-secondary" rel="noopener">Namingo</a> <a href="https://namingo.org" target="_blank" class="link-secondary" rel="noopener">Namingo</a>
</li> </li>
<li class="list-inline-item"> <li class="list-inline-item">
v1.0.0-beta4 v1.0.0-beta5
</li> </li>
</ul> </ul>
</div> </div>

View file

@ -21,10 +21,10 @@
var isInvalid = rowData.application_status.some(statusObj => statusObj.status && statusObj.status.includes('invalid')); var isInvalid = rowData.application_status.some(statusObj => statusObj.status && statusObj.status.includes('invalid'));
if (!isRejected && !isInvalid) { if (!isRejected && !isInvalid) {
actionButtons += `<a class="btn btn-green btn-icon" href="application/approve/${cell.getRow().getData().name}" title="Approve Application"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M5 12l5 5l10 -10" /></svg></a> `; actionButtons += `<a class="btn btn-outline-green btn-icon" href="application/approve/${cell.getRow().getData().name}" title="Approve Application"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M5 12l5 5l10 -10" /></svg></a> `;
actionButtons += `<a class="btn btn-warning btn-icon" href="application/reject/${cell.getRow().getData().name}" title="Reject Application"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M5.7 5.7l12.6 12.6" /></svg></a> `; actionButtons += `<a class="btn btn-outline-warning btn-icon" href="application/reject/${cell.getRow().getData().name}" title="Reject Application"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M5.7 5.7l12.6 12.6" /></svg></a> `;
actionButtons += `<a class="btn btn-info btn-icon" href="application/update/${cell.getRow().getData().name}" title="Update Application"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1"></path><path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z"></path><path d="M16 5l3 3"></path></svg></a> `; actionButtons += `<a class="btn btn-outline-info btn-icon" href="application/update/${cell.getRow().getData().name}" title="Update Application"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1"></path><path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z"></path><path d="M16 5l3 3"></path></svg></a> `;
actionButtons += `<a class="btn btn-danger btn-icon delete-btn" id="delete-btn" href="javascript:void(0);" data-delete-url="application/delete/${cell.getRow().getData().name}" title="Delete Application"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M4 7h16"></path><path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12"></path><path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3"></path><path d="M10 12l4 4m0 -4l-4 4"></path></svg></a>`; actionButtons += `<a class="btn btn-outline-danger btn-icon delete-btn" id="delete-btn" href="javascript:void(0);" data-delete-url="application/delete/${cell.getRow().getData().name}" title="Delete Application"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M4 7h16"></path><path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12"></path><path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3"></path><path d="M10 12l4 4m0 -4l-4 4"></path></svg></a>`;
} else { } else {
actionButtons += `<strong class="text-success"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M9 12l2 2l4 -4" /></svg> Completed</strong>`; actionButtons += `<strong class="text-success"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M9 12l2 2l4 -4" /></svg> Completed</strong>`;
} }

View file

@ -16,8 +16,8 @@
function actionsFormatter(cell, formatterParams, onRendered) { function actionsFormatter(cell, formatterParams, onRendered) {
return ` return `
<a class="btn btn-primary btn-icon update-btn" href="contact/update/${cell.getRow().getData().identifier}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1"></path><path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z"></path><path d="M16 5l3 3"></path></svg></a> <a class="btn btn-outline-primary btn-icon update-btn" href="contact/update/${cell.getRow().getData().identifier}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1"></path><path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z"></path><path d="M16 5l3 3"></path></svg></a>
<a class="btn btn-danger btn-icon delete-btn" id="delete-btn" href="javascript:void(0);" data-delete-url="contact/delete/${cell.getRow().getData().identifier}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M4 7h16"></path><path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12"></path><path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3"></path><path d="M10 12l4 4m0 -4l-4 4"></path></svg></a> <a class="btn btn-outline-danger btn-icon delete-btn" id="delete-btn" href="javascript:void(0);" data-delete-url="contact/delete/${cell.getRow().getData().identifier}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M4 7h16"></path><path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12"></path><path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3"></path><path d="M10 12l4 4m0 -4l-4 4"></path></svg></a>
`; `;
} }

View file

@ -21,15 +21,15 @@
var hasPendingRestore = rowData.rgpstatus ? rowData.rgpstatus.includes('pendingRestore') : false; var hasPendingRestore = rowData.rgpstatus ? rowData.rgpstatus.includes('pendingRestore') : false;
// Common action button for all statuses // Common action button for all statuses
actionButtons += `<a class="btn btn-info btn-icon" href="domain/update/${cell.getRow().getData().name}" title="Update Domain"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1"></path><path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z"></path><path d="M16 5l3 3"></path></svg></a> `; actionButtons += `<a class="btn btn-outline-info btn-icon" href="domain/update/${cell.getRow().getData().name}" title="Update Domain"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1"></path><path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z"></path><path d="M16 5l3 3"></path></svg></a> `;
if (hasPendingRestore) { if (hasPendingRestore) {
actionButtons += `<a class="btn btn-outline-dark btn-icon report-btn" id="report-btn" href="javascript:void(0);" data-report-url="domain/report/${cell.getRow().getData().name}" title="Submit Report"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M3.06 13a9 9 0 1 0 .49 -4.087" /><path d="M3 4.001v5h5" /><path d="M12 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" /></svg></a>`; actionButtons += `<a class="btn btn-outline-dark btn-icon report-btn" id="report-btn" href="javascript:void(0);" data-report-url="domain/report/${cell.getRow().getData().name}" title="Submit Report"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M3.06 13a9 9 0 1 0 .49 -4.087" /><path d="M3 4.001v5h5" /><path d="M12 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0" /></svg></a>`;
} else if (hasPendingDelete) { } else if (hasPendingDelete) {
actionButtons += `<a class="btn btn-outline-warning btn-icon restore-btn" id="restore-btn" href="javascript:void(0);" data-restore-url="domain/restore/${cell.getRow().getData().name}" title="Restore Domain"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 4.55a8 8 0 0 0 -6 14.9m0 -4.45v5h-5" /><path d="M18.37 7.16l0 .01" /><path d="M13 19.94l0 .01" /><path d="M16.84 18.37l0 .01" /><path d="M19.37 15.1l0 .01" /><path d="M19.94 11l0 .01" /></svg></a>`; actionButtons += `<a class="btn btn-outline-warning btn-icon restore-btn" id="restore-btn" href="javascript:void(0);" data-restore-url="domain/restore/${cell.getRow().getData().name}" title="Restore Domain"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 4.55a8 8 0 0 0 -6 14.9m0 -4.45v5h-5" /><path d="M18.37 7.16l0 .01" /><path d="M13 19.94l0 .01" /><path d="M16.84 18.37l0 .01" /><path d="M19.37 15.1l0 .01" /><path d="M19.94 11l0 .01" /></svg></a>`;
} else { } else {
actionButtons += `<a class="btn btn-success btn-icon" href="domain/renew/${cell.getRow().getData().name}" title="Renew Domain"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4"></path><path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4"></path></svg></a> `; actionButtons += `<a class="btn btn-outline-success btn-icon" href="domain/renew/${cell.getRow().getData().name}" title="Renew Domain"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4"></path><path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4"></path></svg></a> `;
actionButtons += `<a class="btn btn-danger btn-icon delete-btn" id="delete-btn" href="javascript:void(0);" data-delete-url="domain/delete/${cell.getRow().getData().name}" title="Delete Domain"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M4 7h16"></path><path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12"></path><path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3"></path><path d="M10 12l4 4m0 -4l-4 4"></path></svg></a>`; actionButtons += `<a class="btn btn-outline-danger btn-icon delete-btn" id="delete-btn" href="javascript:void(0);" data-delete-url="domain/delete/${cell.getRow().getData().name}" title="Delete Domain"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M4 7h16"></path><path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12"></path><path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3"></path><path d="M10 12l4 4m0 -4l-4 4"></path></svg></a>`;
} }
return actionButtons; return actionButtons;

View file

@ -16,8 +16,8 @@
function actionsFormatter(cell, formatterParams, onRendered) { function actionsFormatter(cell, formatterParams, onRendered) {
return ` return `
<a class="btn btn-primary btn-icon update-btn" href="host/update/${cell.getRow().getData().name}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1"></path><path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z"></path><path d="M16 5l3 3"></path></svg></a> <a class="btn btn-outline-primary btn-icon update-btn" href="host/update/${cell.getRow().getData().name}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M7 7h-1a2 2 0 0 0 -2 2v9a2 2 0 0 0 2 2h9a2 2 0 0 0 2 -2v-1"></path><path d="M20.385 6.585a2.1 2.1 0 0 0 -2.97 -2.97l-8.415 8.385v3h3l8.385 -8.415z"></path><path d="M16 5l3 3"></path></svg></a>
<a class="btn btn-danger btn-icon delete-btn" id="delete-btn" href="javascript:void(0);" data-delete-url="host/delete/${cell.getRow().getData().name}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M4 7h16"></path><path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12"></path><path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3"></path><path d="M10 12l4 4m0 -4l-4 4"></path></svg></a> <a class="btn btn-outline-danger btn-icon delete-btn" id="delete-btn" href="javascript:void(0);" data-delete-url="host/delete/${cell.getRow().getData().name}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"></path><path d="M4 7h16"></path><path d="M5 7l1 12a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2l1 -12"></path><path d="M9 7v-3a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v3"></path><path d="M10 12l4 4m0 -4l-4 4"></path></svg></a>
`; `;
} }

View file

@ -21,14 +21,14 @@
var clidValue = document.getElementById('clid').value; var clidValue = document.getElementById('clid').value;
if (hasPendingStatus && clidValue === '0') { if (hasPendingStatus && clidValue === '0') {
actionButtons += `<a class="btn btn-success btn-icon approve-btn" id="approve-btn" href="javascript:void(0);" data-approve-url="transfer/approve/${cell.getRow().getData().name}" title="{{ __('Approve Transfer') }}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 11l3 3l8 -8" /><path d="M20 12v6a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h9" /></svg></a> `; actionButtons += `<a class="btn btn-outline-success btn-icon approve-btn" id="approve-btn" href="javascript:void(0);" data-approve-url="transfer/approve/${cell.getRow().getData().name}" title="{{ __('Approve Transfer') }}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 11l3 3l8 -8" /><path d="M20 12v6a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h9" /></svg></a> `;
actionButtons += `<a class="btn btn-dark btn-icon cancel-btn" id="cancel-btn" href="javascript:void(0);" data-cancel-url="transfer/cancel/${cell.getRow().getData().name}" title="{{ __('Cancel Transfer') }}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M10 10l4 4m0 -4l-4 4" /></svg></a> `; actionButtons += `<a class="btn btn-outline-dark btn-icon cancel-btn" id="cancel-btn" href="javascript:void(0);" data-cancel-url="transfer/cancel/${cell.getRow().getData().name}" title="{{ __('Cancel Transfer') }}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M10 10l4 4m0 -4l-4 4" /></svg></a> `;
actionButtons += `<a class="btn btn-danger btn-icon reject-btn" id="reject-btn" href="javascript:void(0);" data-reject-url="transfer/reject/${cell.getRow().getData().name}" title="{{ __('Reject Transfer') }}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M5.7 5.7l12.6 12.6" /></svg></a>`; actionButtons += `<a class="btn btn-outline-danger btn-icon reject-btn" id="reject-btn" href="javascript:void(0);" data-reject-url="transfer/reject/${cell.getRow().getData().name}" title="{{ __('Reject Transfer') }}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M5.7 5.7l12.6 12.6" /></svg></a>`;
} else if (clidValue === rowData.reid) { } else if (clidValue === rowData.reid) {
actionButtons += `<a class="btn btn-dark btn-icon cancel-btn" id="cancel-btn" href="javascript:void(0);" data-cancel-url="transfer/cancel/${cell.getRow().getData().name}" title="{{ __('Cancel Transfer') }}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M10 10l4 4m0 -4l-4 4" /></svg></a> `; actionButtons += `<a class="btn btn-outline-dark btn-icon cancel-btn" id="cancel-btn" href="javascript:void(0);" data-cancel-url="transfer/cancel/${cell.getRow().getData().name}" title="{{ __('Cancel Transfer') }}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M10 10l4 4m0 -4l-4 4" /></svg></a> `;
} else if (clidValue === rowData.acid) { } else if (clidValue === rowData.acid) {
actionButtons += `<a class="btn btn-success btn-icon approve-btn" id="approve-btn" href="javascript:void(0);" data-approve-url="transfer/approve/${cell.getRow().getData().name}" title="{{ __('Approve Transfer') }}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 11l3 3l8 -8" /><path d="M20 12v6a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h9" /></svg></a> `; actionButtons += `<a class="btn btn-outline-success btn-icon approve-btn" id="approve-btn" href="javascript:void(0);" data-approve-url="transfer/approve/${cell.getRow().getData().name}" title="{{ __('Approve Transfer') }}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M9 11l3 3l8 -8" /><path d="M20 12v6a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2v-12a2 2 0 0 1 2 -2h9" /></svg></a> `;
actionButtons += `<a class="btn btn-danger btn-icon reject-btn" id="reject-btn" href="javascript:void(0);" data-reject-url="transfer/reject/${cell.getRow().getData().name}" title="{{ __('Reject Transfer') }}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M5.7 5.7l12.6 12.6" /></svg></a>`; actionButtons += `<a class="btn btn-outline-danger btn-icon reject-btn" id="reject-btn" href="javascript:void(0);" data-reject-url="transfer/reject/${cell.getRow().getData().name}" title="{{ __('Reject Transfer') }}"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M5.7 5.7l12.6 12.6" /></svg></a>`;
} else { } else {
actionButtons += `<strong class="text-success"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M9 12l2 2l4 -4" /></svg> {{ __('Completed') }}</strong>`; actionButtons += `<strong class="text-success"><svg xmlns="http://www.w3.org/2000/svg" class="icon" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0" /><path d="M9 12l2 2l4 -4" /></svg> {{ __('Completed') }}</strong>`;
} }

View file

@ -67,11 +67,21 @@ $server->on('receive', function ($server, $fd, $reactorId, $data) use ($c, $pool
$server->send($fd, "domain name is too long"); $server->send($fd, "domain name is too long");
$server->close($fd); $server->close($fd);
} }
$domain = strtoupper($domain); // Convert to Punycode if the domain is not in ASCII
if (preg_match("/(^-|^\.|-\.|\.-|--|\.\.|-$|\.$)/", $domain)) { 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->send($fd, "domain name invalid format");
$server->close($fd); $server->close($fd);
} }
$domain = strtoupper($domain);
// Extract TLD from the domain and prepend a dot // Extract TLD from the domain and prepend a dot
$parts = explode('.', $domain); $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 // Check for invalid characters using fetched regex
if (!preg_match($idnRegex, $domain)) { if (strpos(strtolower($parts[0]), 'xn--') === 0) {
$server->send($fd, "Domain name invalid format"); $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); $server->close($fd);
return; return;
} }

View file

@ -195,7 +195,7 @@ function validate_label($label, $pdo) {
if (strlen($label) < 2) { if (strlen($label) < 2) {
return 'Total lenght of your domain must be greater then 2 characters'; 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 (-)'; 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'; 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 // Check for invalid characters using fetched regex
if (!preg_match($idnRegex, $label)) { if (!preg_match($idnRegex, $label)) {
$server->send($fd, "Domain name invalid format"); $server->send($fd, "Domain name invalid format");

View file

@ -190,8 +190,21 @@ function handleDomainQuery($request, $response, $pdo, $domainName, $c, $log) {
return; 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 // 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->header('Content-Type', 'application/json');
$response->status(400); // Bad Request $response->status(400); // Bad Request
$response->end(json_encode(['error' => 'Domain name invalid format'])); $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 // 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->header('Content-Type', 'application/json');
$response->status(400); // Bad Request $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; return;
} }
@ -1019,8 +1037,21 @@ function handleNameserverQuery($request, $response, $pdo, $nameserverHandle, $c,
return; 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 // 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->header('Content-Type', 'application/json');
$response->status(400); // Bad Request $response->status(400); // Bad Request
$response->end(json_encode(['error' => 'Nameserver invalid format'])); $response->end(json_encode(['error' => 'Nameserver invalid format']));
@ -1420,8 +1451,21 @@ function handleDomainSearchQuery($request, $response, $pdo, $searchPattern, $c,
return; 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 // 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->header('Content-Type', 'application/json');
$response->status(400); // Bad Request $response->status(400); // Bad Request
$response->end(json_encode(['error' => 'Domain name invalid format'])); $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 // 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->header('Content-Type', 'application/json');
$response->status(400); // Bad Request $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; return;
} }
@ -1928,8 +1977,21 @@ function handleNameserverSearchQuery($request, $response, $pdo, $searchPattern,
return; 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 // 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->header('Content-Type', 'application/json');
$response->status(400); // Bad Request $response->status(400); // Bad Request
$response->end(json_encode(['error' => 'Nameserver invalid format'])); $response->end(json_encode(['error' => 'Nameserver invalid format']));

View file

@ -74,11 +74,21 @@ $server->on('receive', function ($server, $fd, $reactorId, $data) use ($c, $pool
$server->send($fd, "domain name is too long"); $server->send($fd, "domain name is too long");
$server->close($fd); $server->close($fd);
} }
$domain = strtoupper($domain); // Convert to Punycode if the domain is not in ASCII
if (preg_match("/(^-|^\.|-\.|\.-|--|\.\.|-$|\.$)/", $domain)) { 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->send($fd, "domain name invalid format");
$server->close($fd); $server->close($fd);
} }
$domain = strtoupper($domain);
// Extract TLD from the domain and prepend a dot // Extract TLD from the domain and prepend a dot
$parts = explode('.', $domain); $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 // Check for invalid characters using fetched regex
if (!preg_match($idnRegex, $domain)) { if (strpos(strtolower($parts[0]), 'xn--') === 0) {
$server->send($fd, "Domain name invalid format"); $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); $server->close($fd);
return; return;
} }
@ -435,7 +450,18 @@ $server->on('receive', function ($server, $fd, $reactorId, $data) use ($c, $pool
$server->close($fd); $server->close($fd);
} }
if (!preg_match('/^([a-zA-Z0-9\-]+\.)+[a-zA-Z]{2,}$/', $nameserver)) { // 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('/^((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->send($fd, "Nameserver contains invalid characters or is not in the correct format.");
$server->close($fd); $server->close($fd);
} }