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
+ "