From e718223d12f6472caf507959febbb955e705c3e1 Mon Sep 17 00:00:00 2001 From: Pinga <121483313+getpinga@users.noreply.github.com> Date: Tue, 8 Aug 2023 17:26:01 +0300 Subject: [PATCH] Major update to the EPP server; basic commands work --- database/registry.sql | 2 +- epp/config.php | 15 +++ epp/epp.php | 298 ++++++++++++++++++++++++++++++------------ 3 files changed, 231 insertions(+), 84 deletions(-) create mode 100644 epp/config.php diff --git a/database/registry.sql b/database/registry.sql index c5a8fee..c9373de 100644 --- a/database/registry.sql +++ b/database/registry.sql @@ -51,7 +51,7 @@ CREATE TABLE IF NOT EXISTS `registry`.`registrar` ( `name` varchar(255) NOT NULL, `iana_id` int(5) DEFAULT NULL, `clid` varchar(16) NOT NULL, - `pw` varchar(64) NOT NULL, + `pw` varchar(256) NOT NULL, `prefix` char(2) NOT NULL, `email` varchar(255) NOT NULL, `whois_server` varchar(255) NOT NULL, diff --git a/epp/config.php b/epp/config.php new file mode 100644 index 0000000..cfd7a2b --- /dev/null +++ b/epp/config.php @@ -0,0 +1,15 @@ + 'localhost', + 'mysql_port' => 3306, + 'mysql_database' => 'your_database_name', + 'mysql_username' => 'your_username', + 'mysql_password' => 'your_password', + 'epp_host' => '0.0.0.0', + 'epp_port' => 700, + 'epp_pid' => '/var/run/epp.pid', + 'epp_greeting' => 'Namingo EPP Server 1.0', + 'ssl_cert' => '', + 'ssl_key' => '', +]; \ No newline at end of file diff --git a/epp/epp.php b/epp/epp.php index 0e22f7b..c5c0990 100644 --- a/epp/epp.php +++ b/epp/epp.php @@ -1,104 +1,194 @@ column('clid', Table::TYPE_STRING, 64); $table->column('logged_in', Table::TYPE_INT, 1); $table->create(); -$db = new PDO('mysql:host=localhost;dbname=epp', 'username', 'password'); +$dsn = "mysql:host={$c['mysql_host']};dbname={$c['mysql_database']};port={$c['mysql_port']}"; +$db = new PDO($dsn, $c['mysql_username'], $c['mysql_password']); +$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); -$server = new Server('0.0.0.0', 700); +$server = new Server($c['epp_host'], $c['epp_port']); +$server->set([ + 'enable_coroutine' => true, + 'worker_num' => swoole_cpu_num() * 4, + 'pid_file' => $c['epp_pid'], + 'tcp_user_timeout' => 10, + 'open_ssl' => true, + 'ssl_cert_file' => $c['ssl_cert'], + 'ssl_key_file' => $c['ssl_key'], + 'ssl_verify_peer' => false, + 'ssl_allow_self_signed' => false, + 'ssl_protocols' => SWOOLE_SSL_TLSv1_2 | SWOOLE_SSL_TLSv1_3, +]); $server->handle(function (Connection $conn) use ($table, $db) { - $lengthData = $conn->recv(4); - if ($lengthData === false || strlen($lengthData) !== 4) { - sendEppError($conn, 2100, 'Framing error'); - return; - } + echo "Client connected.\n"; + sendGreeting($conn); + + while (true) { + $data = $conn->recv(); + $connId = spl_object_id($conn); - $length = unpack('N', $lengthData)[1]; - $data = $conn->recv($length - 4); - - if ($data === false || strlen($data) !== $length - 4) { - sendEppError($conn, 2100, 'Framing error'); - return; - } - $xml = simplexml_load_string($data); - - if ($xml === false) { - sendEppError($conn, 2001, 'Invalid XML'); - return; - } - - $clID = (string) $xml->command->clTRID; - $isLoggedIn = $table->get($clID, 'logged_in'); - - // Parsing a login command - if ($xml->getName() == 'epp' && isset($xml->command->login)) { - $clID = (string) $xml->command->login->clID; - $pw = (string) $xml->command->login->pw; - - if (checkLogin($db, $clID, $pw)) { - $table->set($clID, ['logged_in' => 1]); - $conn->send('Login success!'); - } else { - sendEppError($conn, 2200, 'Authentication error'); + if ($data === false || strlen($data) < 4) { + sendEppError($conn, 2100, 'Data reception error'); + break; } - return; - } - // Parsing a logout command - if ($xml->getName() == 'epp' && isset($xml->command->logout)) { - $table->del($clID); - $conn->send('Logout success!'); - return; - } + $length = unpack('N', substr($data, 0, 4))[1]; + $xmlData = substr($data, 4, $length - 4); - if (!$isLoggedIn) { - sendEppError($conn, 2202, 'Authorization error'); - return; - } - - // Parsing a contact:create command - if ($xml->getName() == 'epp' && isset($xml->command->{'create'}->{'contact:create'})) { - processContactCreate($conn, $db, $xml); - return; - } - - // Parsing a contact:check command - if ($xml->getName() == 'epp' && isset($xml->command->{'check'}->{'contact:check'})) { - processContactCheck($conn, $db, $xml); - return; - } + $xml = simplexml_load_string($xmlData, 'SimpleXMLElement', LIBXML_DTDLOAD | LIBXML_NOENT); + $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'); + $xml->registerXPathNamespace('contact', 'urn:ietf:params:xml:ns:contact-1.0'); + $xml->registerXPathNamespace('host', 'urn:ietf:params:xml:ns:host-1.0'); - // Parsing a contact:info command - if ($xml->getName() == 'epp' && isset($xml->command->{'info'}->{'contact:info'})) { - processContactInfo($conn, $db, $xml); - return; - } + if ($xml === false) { + sendEppError($conn, 2001, 'Invalid XML'); + break; + } + + if ($xml->getName() != 'epp') { + continue; // Skip this iteration if not an EPP command + } - // Parsing a domain:info command - if ($xml->getName() == 'epp' && isset($xml->command->{'info'}->{'domain:info'})) { - processDomainInfo($conn, $db, $xml); - return; - } + switch (true) { + case isset($xml->command->login): + { + $clID = (string) $xml->command->login->clID; + $pw = (string) $xml->command->login->pw; - // Parsing a domain:check command - if ($xml->getName() == 'epp' && isset($xml->command->{'check'}->{'domain:check'})) { - processDomainCheck($conn, $db, $xml); - return; - } + if (checkLogin($db, $clID, $pw)) { + $table->set($connId, ['clid' => $clID, 'logged_in' => 1]); + $eppLoginResponse = ' + + + + Login successful + + + ABC-12345 + SRV-54321 + + + '; + + $length = strlen($eppLoginResponse) + 4; // Total length including the 4-byte header + $lengthData = pack('N', $length); // Pack the length into 4 bytes + + $conn->send($lengthData . $eppLoginResponse); + } else { + sendEppError($conn, 2200, 'Authentication error'); + } + break; + } + + case isset($xml->command->logout): + { + $table->del($connId); + $eppLogoutResponse = ' + + + + Logout successful + + + ABC-12345 + SRV-54321 + + + '; + + $length = strlen($eppLogoutResponse) + 4; // Total length including the 4-byte header + $lengthData = pack('N', $length); // Pack the length into 4 bytes + + $conn->send($lengthData . $eppLogoutResponse); + $conn->close(); + break; + } + + case isset($xml->command->check) && isset($xml->command->check->children('urn:ietf:params:xml:ns:contact-1.0')->check): + { + $data = $table->get($connId); + if (!$data || $data['logged_in'] !== 1) { + sendEppError($conn, 2202, 'Authorization error'); + $conn->close(); + } + processContactCheck($conn, $db, $xml); + break; + } + + case isset($xml->command->create) && isset($xml->command->create->children('urn:ietf:params:xml:ns:contact-1.0')->create): + { + $data = $table->get($connId); + if (!$data || $data['logged_in'] !== 1) { + sendEppError($conn, 2202, 'Authorization error'); + $conn->close(); + } + processContactCreate($conn, $db, $xml); + break; + } + + case isset($xml->command->info) && isset($xml->command->info->children('urn:ietf:params:xml:ns:contact-1.0')->info): + { + $data = $table->get($connId); + if (!$data || $data['logged_in'] !== 1) { + sendEppError($conn, 2202, 'Authorization error'); + $conn->close(); + } + processContactInfo($conn, $db, $xml); + break; + } + + case isset($xml->command->check) && isset($xml->command->check->children('urn:ietf:params:xml:ns:domain-1.0')->check): + { + $data = $table->get($connId); + if (!$data || $data['logged_in'] !== 1) { + sendEppError($conn, 2202, 'Authorization error'); + $conn->close(); + } + processDomainCheck($conn, $db, $xml); + break; + } + + case isset($xml->command->info) && isset($xml->command->info->children('urn:ietf:params:xml:ns:domain-1.0')->info): + { + $data = $table->get($connId); + if (!$data || $data['logged_in'] !== 1) { + sendEppError($conn, 2202, 'Authorization error'); + $conn->close(); + } + processDomainInfo($conn, $db, $xml); + break; + } + + default: + { + sendEppError($conn, 2102, 'Unrecognized command'); + break; + } + } + } sendEppError($conn, 2100, 'Unknown command'); + echo "Client disconnected.\n"; }); -$server->start(); +echo "Namingo EPP server started.\n"; +Swoole\Coroutine::create(function () use ($server) { + $server->start(); +}); function processContactCheck($conn, $db, $xml) { $contactIDs = $xml->command->{'check'}->{'contact:check'}->{'contact:id'}; @@ -140,7 +230,10 @@ function processContactCheck($conn, $db, $xml) { XML; - $conn->send($response); + $length = strlen($response) + 4; // Total length including the 4-byte header + $lengthData = pack('N', $length); // Pack the length into 4 bytes + + $conn->send($lengthData . $response); } function processContactInfo($conn, $db, $xml) { @@ -175,8 +268,10 @@ function processContactInfo($conn, $db, $xml) { XML; - // You can customize the response to include the specific details you want - $conn->send($response); + $length = strlen($response) + 4; // Total length including the 4-byte header + $lengthData = pack('N', $length); // Pack the length into 4 bytes + + $conn->send($lengthData . $response); } catch (PDOException $e) { sendEppError($conn, 2400, 'Database error'); @@ -234,7 +329,10 @@ function processContactCreate($conn, $db, $xml) { XML; - $conn->send($response); + $length = strlen($response) + 4; // Total length including the 4-byte header + $lengthData = pack('N', $length); // Pack the length into 4 bytes + + $conn->send($lengthData . $response); } catch (PDOException $e) { sendEppError($conn, 2400, 'Database error'); } @@ -299,13 +397,46 @@ XML; } function checkLogin($db, $clID, $pw) { - $stmt = $db->prepare("SELECT password FROM users WHERE username = :username"); + $stmt = $db->prepare("SELECT pw FROM registrar WHERE clid = :username"); $stmt->execute(['username' => $clID]); $hashedPassword = $stmt->fetchColumn(); return password_verify($pw, $hashedPassword); } +function sendGreeting($conn) { + global $c; + $currentDate = gmdate('Y-m-d\TH:i:s\Z'); + $greetingXml = << + + + {$c['epp_greeting']} + $currentDate + + 1.0 + en + urn:ietf:params:xml:ns:domain-1.0 + urn:ietf:params:xml:ns:contact-1.0 + + + + + + + + + + + + + +XML; + $length = strlen($greetingXml) + 4; // Total length including the 4-byte header + $lengthData = pack('N', $length); // Pack the length into 4 bytes + $conn->send($lengthData . $greetingXml); +} + function sendEppError($conn, $code, $msg) { $errorResponse = << @@ -317,6 +448,7 @@ function sendEppError($conn, $code, $msg) { XML; - - $conn->send($errorResponse); + $length = strlen($errorResponse) + 4; // Total length including the 4-byte header + $lengthData = pack('N', $length); // Pack the length into 4 bytes + $conn->send($lengthData . $errorResponse); } \ No newline at end of file