From 34da89751ddbeab8e3b39f19c1a0e70a9b1ba76c Mon Sep 17 00:00:00 2001 From: Pinga <121483313+getpinga@users.noreply.github.com> Date: Wed, 24 Jan 2024 13:48:52 +0200 Subject: [PATCH] Added scripts for testing the components - DAS server output optimization - Fixed EPP formatting error --- das/start_das.php | 16 +-- epp/start_epp.php | 10 +- tests/das.php | 71 +++++++++++++ tests/epp.php | 253 ++++++++++++++++++++++++++++++++++++++++++++++ tests/rdap.php | 78 ++++++++++++++ tests/whois.php | 211 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 626 insertions(+), 13 deletions(-) create mode 100644 tests/das.php create mode 100644 tests/epp.php create mode 100644 tests/rdap.php create mode 100644 tests/whois.php diff --git a/das/start_das.php b/das/start_das.php index 3f63527..1db8555 100644 --- a/das/start_das.php +++ b/das/start_das.php @@ -60,25 +60,25 @@ $server->on('receive', function ($server, $fd, $reactorId, $data) use ($c, $pool try { // Validate and sanitize the domain name if (!$domain) { - $server->send($fd, "please enter a domain name"); + $server->send($fd, "2"); $server->close($fd); } if (strlen($domain) > 68) { - $server->send($fd, "domain name is too long"); + $server->send($fd, "2"); $server->close($fd); } // 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->send($fd, "2"); $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, "2"); $server->close($fd); } $domain = strtoupper($domain); @@ -94,7 +94,7 @@ $server->on('receive', function ($server, $fd, $reactorId, $data) use ($c, $pool $tldExists = $stmtTLD->fetchColumn(); if (!$tldExists) { - $server->send($fd, "Invalid TLD. Please search only allowed TLDs"); + $server->send($fd, "2"); $server->close($fd); return; } @@ -105,7 +105,7 @@ $server->on('receive', function ($server, $fd, $reactorId, $data) use ($c, $pool $domain_already_reserved = $stmtReserved->fetchColumn(); if ($domain_already_reserved) { - $server->send($fd, "Domain name is reserved or restricted"); + $server->send($fd, "3"); $server->close($fd); return; } @@ -117,7 +117,7 @@ $server->on('receive', function ($server, $fd, $reactorId, $data) use ($c, $pool $idnRegex = $stmtRegex->fetchColumn(); if (!$idnRegex) { - $server->send($fd, "Failed to fetch domain IDN table"); + $server->send($fd, "2"); $server->close($fd); return; } @@ -129,7 +129,7 @@ $server->on('receive', function ($server, $fd, $reactorId, $data) use ($c, $pool $label = strtolower($parts[0]); } if (!preg_match($idnRegex, $label)) { - $server->send($fd, "Domain name invalid IDN characters"); + $server->send($fd, "2"); $server->close($fd); return; } diff --git a/epp/start_epp.php b/epp/start_epp.php index 900fbb5..a79fcf5 100644 --- a/epp/start_epp.php +++ b/epp/start_epp.php @@ -101,6 +101,11 @@ $server->handle(function (Connection $conn) use ($table, $pool, $c, $log, $permi libxml_use_internal_errors(true); $xml = simplexml_load_string($xmlData); + if ($xml === false) { + sendEppError($conn, $pdo, 2001, 'Invalid XML'); + break; + } + $xml->registerXPathNamespace('e', 'urn:ietf:params:xml:ns:epp-1.0'); $xml->registerXPathNamespace('xsi', 'http://www.w3.org/2001/XMLSchema-instance'); $xml->registerXPathNamespace('domain', 'urn:ietf:params:xml:ns:domain-1.0'); @@ -113,11 +118,6 @@ $server->handle(function (Connection $conn) use ($table, $pool, $c, $log, $permi $xml->registerXPathNamespace('mark', 'urn:ietf:params:xml:ns:mark-1.0'); $xml->registerXPathNamespace('allocationToken', 'urn:ietf:params:xml:ns:allocationToken-1.0'); - if ($xml === false) { - sendEppError($conn, $pdo, 2001, 'Invalid XML'); - break; - } - if ($xml->getName() != 'epp') { continue; // Skip this iteration if not an EPP command } diff --git a/tests/das.php b/tests/das.php new file mode 100644 index 0000000..31e3464 --- /dev/null +++ b/tests/das.php @@ -0,0 +1,71 @@ +alert("XSS").test', '2', 'XSS Injection attempt'); +testDomainRequest('`; ls -la`.test', '2', 'Command Injection attempt'); +testDomainRequest('测试.test', '2', 'Unicode characters in domain'); +testDomainRequest(' test .test', '2', 'Domain with leading whitespace'); +testDomainRequest('test .test', '2', 'Domain with trailing whitespace'); \ No newline at end of file diff --git a/tests/epp.php b/tests/epp.php new file mode 100644 index 0000000..05d2922 --- /dev/null +++ b/tests/epp.php @@ -0,0 +1,253 @@ +server = $server; + $this->port = $port; + $this->sslCert = $sslCert; + $this->sslKey = $sslKey; + } + + public function connect() { + $contextOptions = [ + 'ssl' => [ + 'local_cert' => $this->sslCert, + 'local_pk' => $this->sslKey, + 'allow_self_signed' => true, // Set to false in production + 'verify_peer' => false, // Set to true in production + 'verify_peer_name' => false, // Set to true in production + ], + ]; + $context = stream_context_create($contextOptions); + $this->connection = stream_socket_client("ssl://{$this->server}:{$this->port}", $errno, $errstr, 30, STREAM_CLIENT_CONNECT, $context); + + if (!$this->connection) { + throw new Exception("Could not connect to EPP Server: $errstr ($errno)"); + } + + } + + public function generateUniqueClTRID() { + $timeComponent = microtime(true); + $randomComponent = bin2hex(random_bytes(8)); + return "clTRID-{$timeComponent}-{$randomComponent}"; + } + + public function sendRequest($xml) { + $length = strlen($xml) + 4; // 4 bytes for the length field itself + $lengthField = pack('N', $length); // 'N' for big-endian order + fwrite($this->connection, $lengthField . $xml); + + // Read the response + return $this->readResponse(); + } + + private function readResponse() { + // Read the 4-byte length field + $lengthField = fread($this->connection, 4); + $unpacked = unpack('N', $lengthField); + $length = reset($unpacked) - 4; // Subtract the 4 bytes of the length field + + // Read the message based on the length + $response = ''; + while ($length > 0 && !feof($this->connection)) { + $part = fread($this->connection, $length); + $response .= $part; + $length -= strlen($part); + } + + return $response; + } + + public function disconnect() { + fclose($this->connection); + } + + public function login($clientId, $password) { + $xmlRequest = ''.$clientId.'login-'.$this->generateUniqueClTRID().''; + echo $this->sendRequest($xmlRequest); + } + + public function logout() { + $xmlRequest = 'logout-'.$this->generateUniqueClTRID().''; + echo $this->sendRequest($xmlRequest); + } + + public function testDomainCheck() { + $xmlRequest = 'example.testexample.netverylongdomainnamethatisunlikelytobevalidandcausesprocessingdelays.testdomaincheck-'.$this->generateUniqueClTRID().''; + echo $this->sendRequest($xmlRequest); + } + + public function testInvalidCommand() { + $xmlRequest = ''; + echo $this->sendRequest($xmlRequest); + } + + public function testInvalidExtension() { + $xmlRequest = 'example.com'; + echo $this->sendRequest($xmlRequest); + } + + public function testBadXml() { + $xmlRequest = 'example.com'; + echo $this->sendRequest($xmlRequest); + } + + public function testSqlInj() { + $xmlRequest = "' OR '1'='1domaincheck-".$this->generateUniqueClTRID().""; + echo $this->sendRequest($xmlRequest); + } + + public function testUnusuallyFormattedCommands() { + $xmlRequest = "\n\n \n \n \n example.com\n \n \n \n"; + echo $this->sendRequest($xmlRequest); + } + + public function testBoundaryValues() { + $longDomainName = str_repeat("a", 255) . ".com"; // Adjust the length as needed + + $xmlRequest = << + + + + {$longDomainName} + + + + + XML; + echo $this->sendRequest($xmlRequest); + } + + public function testRepeatedLoginLogout() { + for ($i = 0; $i < 10; $i++) { // Adjust the number of iterations as needed + // Replace with actual login and logout XML requests + $loginRequest = "clientIDpassword"; + $logoutRequest = ""; + + $this->sendRequest($loginRequest); + $this->sendRequest($logoutRequest); + } + + echo "Repeated Login and Logout Test Completed.\n"; + } + + public function testMalformedUnicodeCharacters() { + echo "Running Malformed Unicode Characters Test...\n"; + + // Example: Malformed Unicode characters in the domain name + $malformedDomainName = "exämple.cöm"; // Contains unusual/malformed characters + + $xmlRequest = << + + + + {$malformedDomainName} + + + + + XML; + + $response = $this->sendRequest($xmlRequest); + echo "Response: " . $response . "\n"; + } + + public function testSimulatedNetworkInstability() { + echo "Running Simulated Network Instability Test...\n"; + + // Example: Introduce delays in sending requests + for ($i = 0; $i < 5; $i++) { + $xmlRequest = "example.com"; + + // Introducing a delay + sleep(rand(1, 5)); // Delay between 1 to 5 seconds + + $response = $this->sendRequest($xmlRequest); + echo "Response: " . $response . "\n"; + } + + echo "Simulated Network Instability Test Completed.\n"; + } + + public function testUnexpectedProtocolVersion() { + echo "Running Unexpected Protocol Version Test...\n"; + + $xmlRequest = "example.com"; + + $response = $this->sendRequest($xmlRequest); + echo "Response: " . $response . "\n"; + } + + public function testServerOverloadWithLongDuration() { + echo "Running Server Overload with Long Duration Requests Test...\n"; + + $startTime = time(); + $duration = 60; // Run the test for 60 seconds + + while (time() - $startTime < $duration) { + $xmlRequest = "example.com"; + + $response = $this->sendRequest($xmlRequest); + // Optionally process the response + } + + echo "Server Overload with Long Duration Requests Test Completed.\n"; + } + +} + +class EppTest { + private $client; + + public function __construct() { + // Initialize the EPP client with your server's details + $this->client = new EppClient('epp.server.com', 700, 'cert.pem', 'key.pem'); + } + + public function runTests() { + echo "Starting EPP Tests...\n"; + + // Connect to the EPP server + $this->client->connect(); + $this->client->login('clid', 'password'); + + // Run various tests + $this->client->testDomainCheck(); + $this->client->testInvalidCommand(); + //$this->client->testUnusuallyFormattedCommands(); + //$this->client->testInvalidExtension(); + //$this->client->testBadXml(); + //$this->client->testSqlInj(); + //$this->client->testBoundaryValues(); + //$this->client->testRepeatedLoginLogout(); + //$this->client->testMalformedUnicodeCharacters(); + //$this->client->testSimulatedNetworkInstability(); + //$this->client->testUnexpectedProtocolVersion(); + //$this->client->testServerOverloadWithLongDuration(); + + // Disconnect from the server + $this->client->logout(); + $this->client->disconnect(); + + echo "EPP Tests Completed.\n"; + } + +} + +$test = new EppTest(); +$test->runTests(); \ No newline at end of file diff --git a/tests/rdap.php b/tests/rdap.php new file mode 100644 index 0000000..5556c95 --- /dev/null +++ b/tests/rdap.php @@ -0,0 +1,78 @@ + "domain", "query" => "example.test", "expected" => "Not Found"], + ["type" => "nameserver", "query" => "ns1.example.test", "expected" => "Not Found"], + ["type" => "domain", "query" => "test.test", "expected" => "domain"], + + // Edge Cases + ["type" => "domain", "query" => "", "expected" => "Not Found"], + ["type" => "nameserver", "query" => "ns1.exa#mple.test", "expected" => "Nameserver invalid format"], + ["type" => "domain", "query" => "verylongdomainnametestingxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.test", "expected" => "Domain name is too long"], + ["type" => "domain", "query" => "special!@#$.test", "expected" => "Domain name invalid format"], + + // Security Cases + ["type" => "domain", "query" => "example.test; DROP TABLE users;", "expected" => "Domain name invalid format"], + ["type" => "domain", "query" => "", "expected" => "Domain name invalid format"], + ["type" => "domain", "query" => "; ls -la", "expected" => "Domain name invalid format"], + ["type" => "domain", "query" => str_repeat("A", 10000), "expected" => "Domain name is too long"], + + // Protocol and Compliance Cases + ["type" => "invalidpath", "query" => "example.test", "expected" => "Endpoint not found"], + + // Response and Format Cases + ["type" => "domain", "query" => "utf8testé.test", "expected" => "Domain name invalid IDN characters"], +]; + +// Function to send RDAP request +function sendRdapRequest($server, $type, $query) { + $url = $server . "/" . $type . "/" . urlencode($query); + $ch = curl_init($url); + + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HEADER, 0); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch, CURLOPT_MAXREDIRS, 10); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); + + $response = curl_exec($ch); + + // Check for cURL errors + if (curl_errno($ch)) { + throw new Exception('cURL error: ' . curl_error($ch)); + } + + curl_close($ch); + return $response; +} + +// Function to validate response +function validateResponse($response, $expected) { + $decodedResponse = json_decode($response, true); + + // Basic validation + if (isset($decodedResponse['objectClassName']) && $decodedResponse['objectClassName'] === $expected) { + return true; + } else if (isset($decodedResponse['title']) && $decodedResponse['title'] === $expected) { + return true; + } else if (isset($decodedResponse['error']) && $decodedResponse['error'] === $expected) { + return true; + } + + return false; +} + +// Main testing loop +foreach ($testCases as $testCase) { + $response = sendRdapRequest($rdapServer, $testCase['type'], $testCase['query']); + $isValid = validateResponse($response, $testCase['expected']); + // Logging and Reporting + echo "Test: " . $testCase['query'] . " - " . ($isValid ? "PASS" : "FAIL") . "\n"; +} \ No newline at end of file diff --git a/tests/whois.php b/tests/whois.php new file mode 100644 index 0000000..4558342 --- /dev/null +++ b/tests/whois.php @@ -0,0 +1,211 @@ +testBasicQueries(); + $this->testEdgeCases(); + $this->testSecurityCases(); + $this->testResponseTime(); + $this->testDataAccuracy(); + $this->testInvalidQueries(); + $this->testIDNSupport(); + $this->testRateLimiting(); + $this->testComplianceWithStandards(); + } + + private function testBasicQueries() { + echo "Running Basic Queries Test...\n"; + // List of domains to test + $domains = ['test.test', 'example.test', 'example.org']; + + foreach ($domains as $domain) { + $result = $this->queryWhoisServer($domain); + if ($result && (strpos($result, 'NOT FOUND') !== false || strpos($result, 'Invalid TLD') === 0 || strpos($result, 'Domain Name:') === 0)) { + echo "Test: $domain - PASS\n"; + } else { + echo "Test: $domain - FAIL\n"; + } + } + } + + private function testEdgeCases() { + echo "Running Edge Cases Test...\n"; + // Test non-existing domains, special characters, long domain names + $edgeCaseDomains = ['thisdomaindoesnotexist123456.test', 'example-.test', str_repeat('a', 75) . '.test']; + + foreach ($edgeCaseDomains as $domain) { + $result = $this->queryWhoisServer($domain); + // In edge cases, we expect failures or specific responses + if ($result && (strpos($result, 'NOT FOUND') !== false || strpos($result, 'Domain name invalid IDN characters') === 0 || strpos($result, 'domain name is too long') === 0)) { + echo "Test: $domain - PASS\n"; + } else { + echo "Test: $domain - FAIL\n"; + } + } + } + + private function testSecurityCases() { + echo "Running Security Cases Test...\n"; + // Test with potential security risk inputs + $securityTestDomains = [ + "; DROP TABLE domains;", // SQL Injection + "\">", // Basic XSS + "' OR '1'='1", // SQL Injection Variant + "| rm -rf /", // Command Injection + str_repeat("A", 10000), // Buffer Overflow + "", // XSS with HTML5 + "", // Server Side Includes (SSI) Injection + "(|(uid=*)(cn=*))", // LDAP Injection + "../etc/passwd", // Path Traversal + "<]]>baz", // XML Injection + ]; + + foreach ($securityTestDomains as $domain) { + $result = $this->queryWhoisServer($domain); + if ($result && (strpos($result, 'domain name invalid format') !== false || strpos($result, 'domain name is too long') === 0)) { + echo "Test: $domain - PASS\n"; + } else { + echo "Test: $domain - FAIL\n"; + } + } + } + + private function testResponseTime() { + echo "Testing Response Time...\n"; + $domains = ['example.test', 'test.test', 'example.org']; // Multiple domains for a broader test + $testCount = 5; // Number of tests per domain + $totalDuration = 0; + + foreach ($domains as $domain) { + $domainDurations = []; + + for ($i = 0; $i < $testCount; $i++) { + $startTime = microtime(true); + $this->queryWhoisServer($domain); + $endTime = microtime(true); + $duration = $endTime - $startTime; + $domainDurations[] = $duration; + $totalDuration += $duration; + } + + $averageDuration = array_sum($domainDurations) / count($domainDurations); + $variance = $this->calculateVariance($domainDurations, $averageDuration); + + echo "Average response time for $domain: " . number_format($averageDuration, 3) . " seconds\n"; + echo "Variance in response time for $domain: " . number_format($variance, 3) . "\n"; + } + + $overallAverage = $totalDuration / ($testCount * count($domains)); + echo "Overall average response time: " . number_format($overallAverage, 3) . " seconds\n"; + } + + private function testDataAccuracy() { + echo "Testing Data Accuracy...\n"; + $testDomain = 'test.test'; // Use a domain whose WHOIS info you know + $expectedRegistrar = 'LeoNet LLC'; // Replace with known data + $result = $this->queryWhoisServer($testDomain); + if (strpos($result, $expectedRegistrar) !== false) { + echo "Test: $testDomain - PASS\n"; + } else { + echo "Test: $testDomain - FAIL\n"; + } + } + + private function testInvalidQueries() { + echo "Testing Handling of Invalid Queries...\n"; + $invalidDomains = ['this is not a domain', '', '1234567890', 'invalid_domain.com']; + foreach ($invalidDomains as $domain) { + $result = $this->queryWhoisServer($domain); + if ($result && (strpos($result, 'domain name invalid format') !== false || strpos($result, 'please enter a domain name') === 0)) { + echo "Test: $domain - PASS\n"; + } else { + echo "Test: $domain - FAIL\n"; + } + } + } + + private function testIDNSupport() { + echo "Testing International Domain Name (IDN) Support...\n"; + $idnDomain = 'xn--exmple-cua.com'; // Punycode for an IDN domain + $result = $this->queryWhoisServer($idnDomain); + if ($result) { + echo "Test: $idnDomain - PASS\n"; + } else { + echo "Test: $idnDomain - FAIL\n"; + } + } + + private function testRateLimiting() { + echo "Testing Rate Limiting...\n"; + $domain = 'example.com'; + $successCount = 0; + $testCount = 10; // Number of requests to simulate rate limiting + + for ($i = 0; $i < $testCount; $i++) { + if ($this->queryWhoisServer($domain)) { + $successCount++; + } + sleep(1); // Sleep to avoid hitting the server too rapidly + } + + if ($successCount < $testCount) { + echo "Test: Rate limiting - PASS. Number of successful queries: $successCount\n"; + } else { + echo "Test: Rate limiting - FAIL\n"; + } + } + + private function testComplianceWithStandards() { + echo "Testing Compliance with WHOIS Protocol Standards...\n"; + $domain = 'test.test'; + $result = $this->queryWhoisServer($domain); + + // Check for a standard response format (this will depend on the specific standard) + if (strpos($result, 'Domain Name:') !== false && strpos($result, 'Registrar:') !== false) { + echo "Test: $domain - PASS\n"; + } else { + echo "Test: $fail - PASS\n"; + } + } + + private function queryWhoisServer($domain) { + $server = $this->whoisServer; // WHOIS server + $port = 43; // Standard WHOIS port + + // Open a connection to the WHOIS server + $connection = fsockopen($server, $port); + if (!$connection) { + return false; // Connection failed + } + + // Send the query + fputs($connection, "$domain\r\n"); + + // Read and store the response + $response = ''; + while (!feof($connection)) { + $response .= fgets($connection, 128); + } + + // Close the connection + fclose($connection); + + // Return the response + return $response; + } + + private function calculateVariance($durations, $mean) { + $sumOfSquares = 0; + foreach ($durations as $duration) { + $sumOfSquares += pow(($duration - $mean), 2); + } + return $sumOfSquares / count($durations); + } + +} + +$whoisTest = new WhoisTest(); +$whoisTest->runTests(); \ No newline at end of file