diff --git a/cp/app/Controllers/DomainsController.php b/cp/app/Controllers/DomainsController.php index ad775be..e458403 100644 --- a/cp/app/Controllers/DomainsController.php +++ b/cp/app/Controllers/DomainsController.php @@ -22,12 +22,7 @@ class DomainsController extends Controller $domainName = $data['domain_name'] ?? null; if ($domainName) { - $domainParts = explode('.', $domainName); - - if (count($domainParts) > 2) { - // Remove the leftmost part (subdomain) - array_shift($domainParts); - } + $parts = extractDomainAndTLD($domainName); $domainModel = new Domain($this->container->get('db')); $availability = $domainModel->getDomainByName($domainName); @@ -44,7 +39,7 @@ class DomainsController extends Controller } else { // If the domain is not taken, check if it's reserved if ($availability === '1') { - $domain_already_reserved = $this->container->get('db')->selectRow('SELECT id,type FROM reserved_domain_names WHERE name = ? LIMIT 1',[$domainParts[0]]); + $domain_already_reserved = $this->container->get('db')->selectRow('SELECT id,type FROM reserved_domain_names WHERE name = ? LIMIT 1',[$parts['domain']]); if ($domain_already_reserved) { $isAvailable = 0; @@ -109,7 +104,9 @@ class DomainsController extends Controller $authInfo = $data['authInfo'] ?? null; - list($label, $domain_extension) = explode('.', $domainName, 2); + $parts = extractDomainAndTLD($domainName); + $label = $parts['domain']; + $domain_extension = $parts['tld']; $invalid_domain = validate_label($domainName, $db); if ($invalid_domain) { @@ -157,7 +154,7 @@ class DomainsController extends Controller $domain_already_reserved = $db->selectValue( 'SELECT id FROM reserved_domain_names WHERE name = ? LIMIT 1', - [$domainName] + [$label] ); if ($domain_already_reserved) { @@ -1411,7 +1408,9 @@ class DomainsController extends Controller $domainName = $data['domainName'] ?? null; $renewalYears = $data['renewalYears'] ?? null; - list($label, $domain_extension) = explode('.', $domainName, 2); + $parts = extractDomainAndTLD($domainName); + $label = $parts['domain']; + $domain_extension = $parts['tld']; $result = $db->select('SELECT id, tld FROM domain_tld'); foreach ($result as $row) { @@ -1673,7 +1672,9 @@ class DomainsController extends Controller $renewedDate = $domain['renewedDate']; $transferPeriod = $domain['transferPeriod']; - list($label, $domain_extension) = explode('.', $domainName, 2); + $parts = extractDomainAndTLD($domainName); + $label = $parts['domain']; + $domain_extension = $parts['tld']; $result = $db->select('SELECT id, tld FROM domain_tld'); foreach ($result as $row) { diff --git a/cp/bin/file_cache.php b/cp/bin/file_cache.php new file mode 100644 index 0000000..0a8f2c0 --- /dev/null +++ b/cp/bin/file_cache.php @@ -0,0 +1,44 @@ +getItem($cacheKey); +if (!$cachedFile->isHit()) { + // File is not cached, download it + $httpClient = new Client(); + $response = $httpClient->get($fileUrl); + $fileContent = $response->getBody()->getContents(); + + // Save the file content to cache + $cachedFile->set($fileContent); + $cachedFile->expiresAfter(86400); // Cache for 24 hours, for example + $cache->save($cachedFile); + echo "File downloaded and cached.\n"; +} else { + // Retrieve the file content from the cache + $fileContent = $cachedFile->get(); + echo "File loaded from cache.\n"; +} + +// Use $fileContent as needed +// ... + +// For demonstration: Writing first 200 characters of the content +echo substr($fileContent, 0, 50) . "..."; \ No newline at end of file diff --git a/cp/bootstrap/helper.php b/cp/bootstrap/helper.php index a88e179..a9b4fa7 100644 --- a/cp/bootstrap/helper.php +++ b/cp/bootstrap/helper.php @@ -5,6 +5,12 @@ */ use Pinga\Auth\Auth; +use Pdp\Domain; +use Pdp\TopLevelDomains; +use League\Flysystem\Local\LocalFilesystemAdapter; +use League\Flysystem\Filesystem; +use MatthiasMullie\Scrapbook\Adapters\Flysystem as ScrapbookFlysystem; +use MatthiasMullie\Scrapbook\Psr6\Pool; /** * @return mixed|string|string[] @@ -206,8 +212,8 @@ function validate_label($label, $db) { } // Extract TLD from the domain and prepend a dot - $parts = explode('.', $label); - $tld = "." . end($parts); + $parts = extractDomainAndTLD($label); + $tld = "." . $parts['tld']; // Check if the TLD exists in the domain_tld table $tldExists = $db->select('SELECT COUNT(*) FROM domain_tld WHERE tld = ?', [$tld]); @@ -264,4 +270,41 @@ function normalize_v6_address($v6) { $v6 = preg_replace('/:(:0)+/', ':', $v6); return $v6; +} + +function extractDomainAndTLD($urlString) { + $cachePath = __DIR__ . '/../cache'; // Cache directory + $adapter = new LocalFilesystemAdapter($cachePath, null, LOCK_EX); + $filesystem = new Filesystem($adapter); + $cache = new Pool(new ScrapbookFlysystem($filesystem)); + $cacheKey = 'tlds_alpha_by_domain'; + $cachedFile = $cache->getItem($cacheKey); + $fileContent = $cachedFile->get(); + + // Define a list of fake TLDs used in your QA environment + $fakeTlds = ['test', 'xx']; + + // Parse the URL to get the host + $parts = parse_url($urlString); + $host = $parts['host'] ?? $urlString; + + // Check if the TLD is a known fake TLD + foreach ($fakeTlds as $fakeTld) { + if (str_ends_with($host, ".$fakeTld")) { + // Handle the fake TLD case + $hostParts = explode('.', $host); + $tld = array_pop($hostParts); + $sld = array_pop($hostParts); + return ['domain' => $sld, 'tld' => $tld]; + } + } + + // Use the PHP Domain Parser library for real TLDs + $tlds = TopLevelDomains::fromString($fileContent); + $domain = Domain::fromIDNA2008($host); + $result = $tlds->resolve($domain); + $sld = $result->secondLevelDomain()->toString(); + $tld = $result->suffix()->toString(); + + return ['domain' => $sld, 'tld' => $tld]; } \ No newline at end of file diff --git a/cp/cache/.gitignore b/cp/cache/.gitignore new file mode 100644 index 0000000..5e7d273 --- /dev/null +++ b/cp/cache/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/cp/composer.json b/cp/composer.json index b91604f..ca8a452 100644 --- a/cp/composer.json +++ b/cp/composer.json @@ -38,11 +38,15 @@ "stripe/stripe-php": "^13.3", "robthree/twofactorauth": "^2.1", "lbuchs/webauthn": "^2.1", - "bacon/bacon-qr-code": "^2.0" + "bacon/bacon-qr-code": "^2.0", + "jeremykendall/php-domain-parser": "^6.3", + "matthiasmullie/scrapbook": "^1.5", + "guzzlehttp/guzzle": "^7.8", + "league/flysystem": "^3.23" }, "autoload": { "psr-4": { "App\\": "app/" } } -} +} \ No newline at end of file diff --git a/epp/composer.json b/epp/composer.json index 13d102c..0155b26 100644 --- a/epp/composer.json +++ b/epp/composer.json @@ -1,5 +1,9 @@ { "require": { - "monolog/monolog": "^3.5" + "monolog/monolog": "^3.5", + "jeremykendall/php-domain-parser": "^6.3", + "matthiasmullie/scrapbook": "^1.5", + "guzzlehttp/guzzle": "^7.8", + "league/flysystem": "^3.23" } } diff --git a/epp/src/epp-check.php b/epp/src/epp-check.php index af45841..667a6f4 100644 --- a/epp/src/epp-check.php +++ b/epp/src/epp-check.php @@ -129,9 +129,11 @@ function processDomainCheck($conn, $db, $xml, $trans) { $domainEntry[] = 'In use'; } else { // Check if the domain is reserved - $parts = explode('.', $domainName); + $parts = extractDomainAndTLD($domainName); + $label = $parts['domain']; + $stmt = $db->prepare("SELECT type FROM reserved_domain_names WHERE name = :domainName LIMIT 1"); - $stmt->bindParam(':domainName', $parts[0], PDO::PARAM_STR); + $stmt->bindParam(':domainName', $label, PDO::PARAM_STR); $stmt->execute(); $reserved = $stmt->fetchColumn(); diff --git a/epp/src/epp-create.php b/epp/src/epp-create.php index 6d98d19..abf491c 100644 --- a/epp/src/epp-create.php +++ b/epp/src/epp-create.php @@ -535,7 +535,7 @@ function processHostCreate($conn, $db, $xml, $clid, $database_type, $trans) { $stmt->execute([$hostName, $clid, $clid]); $host_id = $db->lastInsertId(); - + $host_status = 'ok'; $stmt = $db->prepare("INSERT INTO host_status (host_id,status) VALUES(?,?)"); $stmt->execute([$host_id, $host_status]); @@ -567,8 +567,11 @@ function processHostCreate($conn, $db, $xml, $clid, $database_type, $trans) { function processDomainCreate($conn, $db, $xml, $clid, $database_type, $trans) { $domainName = $xml->command->create->children('urn:ietf:params:xml:ns:domain-1.0')->create->name; $clTRID = (string) $xml->command->clTRID; + + $parts = extractDomainAndTLD($domainName); + $label = $parts['domain']; + $domain_extension = $parts['tld']; - list($label, $domain_extension) = explode('.', $domainName, 2); $invalid_domain = validate_label($domainName, $db); if ($invalid_domain) { @@ -602,7 +605,7 @@ function processDomainCreate($conn, $db, $xml, $clid, $database_type, $trans) { } $stmt = $db->prepare("SELECT id FROM reserved_domain_names WHERE name = ? LIMIT 1"); - $stmt->execute([$domainName]); + $stmt->execute([$label]); $domain_already_reserved = $stmt->fetchColumn(); if ($domain_already_reserved) { diff --git a/epp/src/helpers.php b/epp/src/helpers.php index 8335b63..0431d93 100644 --- a/epp/src/helpers.php +++ b/epp/src/helpers.php @@ -6,6 +6,12 @@ use Monolog\Logger; use Monolog\Handler\StreamHandler; use Monolog\Handler\RotatingFileHandler; use Monolog\Formatter\LineFormatter; +use Pdp\Domain; +use Pdp\TopLevelDomains; +use League\Flysystem\Local\LocalFilesystemAdapter; +use League\Flysystem\Filesystem; +use MatthiasMullie\Scrapbook\Adapters\Flysystem as ScrapbookFlysystem; +use MatthiasMullie\Scrapbook\Psr6\Pool; /** * Sets up and returns a Logger instance. @@ -194,8 +200,8 @@ function validate_label($label, $pdo) { } // Extract TLD from the domain and prepend a dot - $parts = explode('.', $label); - $tld = "." . end($parts); + $parts = extractDomainAndTLD($label); + $tld = "." . $parts['tld']; // Check if the TLD exists in the domain_tld table $stmtTLD = $pdo->prepare("SELECT COUNT(*) FROM domain_tld WHERE tld = :tld"); @@ -224,6 +230,43 @@ function validate_label($label, $pdo) { } } +function extractDomainAndTLD($urlString) { + $cachePath = '/var/www/cp/cache'; // Cache directory + $adapter = new LocalFilesystemAdapter($cachePath, null, LOCK_EX); + $filesystem = new Filesystem($adapter); + $cache = new Pool(new ScrapbookFlysystem($filesystem)); + $cacheKey = 'tlds_alpha_by_domain'; + $cachedFile = $cache->getItem($cacheKey); + $fileContent = $cachedFile->get(); + + // Define a list of fake TLDs used in your QA environment + $fakeTlds = ['test', 'xx']; + + // Parse the URL to get the host + $parts = parse_url($urlString); + $host = $parts['host'] ?? $urlString; + + // Check if the TLD is a known fake TLD + foreach ($fakeTlds as $fakeTld) { + if (str_ends_with($host, ".$fakeTld")) { + // Handle the fake TLD case + $hostParts = explode('.', $host); + $tld = array_pop($hostParts); + $sld = array_pop($hostParts); + return ['domain' => $sld, 'tld' => $tld]; + } + } + + // Use the PHP Domain Parser library for real TLDs + $tlds = TopLevelDomains::fromString($fileContent); + $domain = Domain::fromIDNA2008($host); + $result = $tlds->resolve($domain); + $sld = $result->secondLevelDomain()->toString(); + $tld = $result->suffix()->toString(); + + return ['domain' => $sld, 'tld' => $tld]; +} + function normalize_v4_address($v4) { // Remove leading zeros from the first octet $v4 = preg_replace('/^0+(\d)/', '$1', $v4); diff --git a/epp/start_epp.php b/epp/start_epp.php index f53c712..668bf1f 100644 --- a/epp/start_epp.php +++ b/epp/start_epp.php @@ -125,6 +125,10 @@ $server->handle(function (Connection $conn) use ($table, $pool, $c, $log, $permi $pw = (string) $xml->command->login->pw; $clTRID = (string) $xml->command->clTRID; $clid = getClid($pdo, $clID); + if (!$clid) { + sendEppError($conn, $pdo, 2200, 'Authentication error', $clTRID); + break; + } $xmlString = $xml->asXML(); $trans = createTransaction($pdo, $clid, $clTRID, $xmlString);