diff --git a/epp/EppWriter.php b/epp/EppWriter.php index 6565a63..fdebebf 100644 --- a/epp/EppWriter.php +++ b/epp/EppWriter.php @@ -842,7 +842,7 @@ class EppWriter { $writer->writeAttribute('xmlns:host', 'urn:ietf:params:xml:ns:host-1.0'); $writer->writeAttribute('xsi:schemaLocation', 'urn:ietf:params:xml:ns:host-1.0 host-1.0.xsd'); $writer->writeElement('host:name', $resp['name']); - $writer->writeElement('host:crDate', $resp['crDate']); + $writer->writeElement('host:crDate', gmdate('Y-m-d\TH:i:s\.0\Z', strtotime($resp['crDate']))); $writer->endElement(); // End of 'host:creData' $writer->endElement(); // End of 'resData' } diff --git a/epp/epp-create.php b/epp/epp-create.php index 156c0a7..30bad2c 100644 --- a/epp/epp-create.php +++ b/epp/epp-create.php @@ -388,4 +388,172 @@ function processContactCreate($conn, $db, $xml, $clid, $database_type) { $epp = new EPP\EppWriter(); $xml = $epp->epp_writer($response); sendEppResponse($conn, $xml); +} + +function processHostCreate($conn, $db, $xml, $clid, $database_type) { + $hostName = $xml->command->create->children('urn:ietf:params:xml:ns:host-1.0')->create->name; + $clTRID = (string) $xml->command->clTRID; + + $hostName = strtoupper($hostName); + 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 = $db->query("SELECT id FROM host WHERE name = '$hostName' LIMIT 1")->fetchColumn(); + if ($host_id_already_exist) { + sendEppError($conn, 2302, 'host:name already exists'); + return; + } + } else { + sendEppError($conn, 2005, 'Invalid host:name'); + return; + } + + $host_addr_list = $xml->xpath('//addr'); + if (count($host_addr_list) > 13) { + sendEppError($conn, 2306, 'Parameter value policy error'); + return; + } + + $stmt = $db->prepare("SELECT id FROM registrar WHERE clid = :clid LIMIT 1"); + $stmt->bindParam(':clid', $clid, PDO::PARAM_STR); + $stmt->execute(); + $clid = $stmt->fetch(PDO::FETCH_ASSOC); + $clid = $clid['id']; + + $nsArr = []; + + foreach ($host_addr_list as $node) { + $addr = (string)$node; + $addr_type = (string) $node['ip'] ?? 'v4'; + + if ($addr_type === 'v6') { + $addr = normalize_v6_address($addr); + } else { + $addr = normalize_v4_address($addr); + } + + // v6 IP validation + if ($addr_type === 'v6' && !filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + sendEppError($conn, 2005, 'Invalid host:addr v6'); + return; + } + + // v4 IP validation + if ($addr_type !== 'v6' && !filter_var($addr, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { + sendEppError($conn, 2005, 'Invalid host:addr v4'); + return; + } + + // check for duplicate IPs + if (isset($nsArr[$addr_type][$addr])) { + sendEppError($conn, 2306, 'Duplicated host:addr'); + return; + } + + $nsArr[$addr_type][$addr] = $addr; + } + + $internal_host = false; + + $query = "SELECT tld FROM domain_tld"; + foreach ($db->query($query) as $row) { + if (preg_match("/" . preg_quote(strtoupper($row['tld']), '/') . "$/i", $hostName)) { + $internal_host = true; + break; + } + } + + if ($internal_host) { + $domain_exist = false; + $clid_domain = 0; + $superordinate_dom = 0; + + $stmt = $db->prepare("SELECT id,clid,name FROM domain"); + $stmt->execute(); + + while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { + if (strpos($hostName, $row['name']) !== false) { + $domain_exist = true; + $clid_domain = $row['clid']; + $superordinate_dom = $row['id']; + break; + } + } + + if (!$domain_exist) { + sendEppError($conn, 2303, 'Object does not exist'); + return; + } + + if ($clid != $clid_domain) { + sendEppError($conn, 2201, 'Authorization error'); + return; + } + + $stmt = $db->prepare("INSERT INTO host (name,domain_id,clid,crid,crdate) VALUES(?,?,?,?,CURRENT_TIMESTAMP)"); + $stmt->execute([$hostName, $superordinate_dom, $clid, $clid]); + $host_id = $db->lastInsertId(); + + $host_addr_list = $xml->xpath('host:addr'); + + foreach ($host_addr_list as $node) { + $addr = (string) $node; + $addr_type = isset($node['ip']) ? (string) $node['ip'] : 'v4'; + + if ($addr_type == 'v6') { + $addr = normalize_v6_address($addr); + } else { + $addr = normalize_v4_address($addr); + } + + $addr_type = ($addr_type == 'v6') ? 6 : 4; + + $stmt = $db->prepare("INSERT INTO host_addr (host_id,addr,ip) VALUES(?,?,?)"); + $stmt->execute([$host_id, $addr, $addr_type]); + } + + $stmt = $db->prepare("SELECT crdate FROM host WHERE name = ? LIMIT 1"); + $stmt->execute([$hostName]); + $crdate = $stmt->fetchColumn(); + + $response = [ + 'command' => 'create_host', + 'resultCode' => 1000, + 'lang' => 'en-US', + 'message' => 'Command completed successfully', + 'name' => $hostName, + 'crDate' => $crdate, + 'clTRID' => $clTRID, + 'svTRID' => generateSvTRID(), + ]; + + $epp = new EPP\EppWriter(); + $xml = $epp->epp_writer($response); + sendEppResponse($conn, $xml); + + } else { + + $stmt = $db->prepare("INSERT INTO host (name,clid,crid,crdate) VALUES(?,?,?,CURRENT_TIMESTAMP)"); + $stmt->execute([$hostName, $clid, $clid]); + + $host_id = $db->lastInsertId(); + + $stmt = $db->prepare("SELECT crdate FROM host WHERE name = ? LIMIT 1"); + $stmt->execute([$hostName]); + $crdate = $stmt->fetchColumn(); + + $response = [ + 'command' => 'create_host', + 'resultCode' => 1000, + 'lang' => 'en-US', + 'message' => 'Command completed successfully', + 'name' => $hostName, + 'crDate' => $crdate, + 'clTRID' => $clTRID, + 'svTRID' => generateSvTRID(), + ]; + + $epp = new EPP\EppWriter(); + $xml = $epp->epp_writer($response); + sendEppResponse($conn, $xml); + } + } \ No newline at end of file diff --git a/epp/epp.php b/epp/epp.php index 36a6d90..9fc0ac2 100644 --- a/epp/epp.php +++ b/epp/epp.php @@ -212,6 +212,17 @@ $server->handle(function (Connection $conn) use ($table, $db, $c) { break; } + case isset($xml->command->create) && isset($xml->command->create->children('urn:ietf:params:xml:ns:host-1.0')->create): + { + $data = $table->get($connId); + if (!$data || $data['logged_in'] !== 1) { + sendEppError($conn, 2202, 'Authorization error'); + $conn->close(); + } + processHostCreate($conn, $db, $xml, $data['clid'], $c['db_type']); + break; + } + case isset($xml->command->info) && isset($xml->command->info->children('urn:ietf:params:xml:ns:host-1.0')->info): { $data = $table->get($connId); diff --git a/epp/helpers.php b/epp/helpers.php index 0eb88e7..9fd000a 100644 --- a/epp/helpers.php +++ b/epp/helpers.php @@ -164,4 +164,41 @@ function validate_label($label, $pdo) { $server->send($fd, "Domain name invalid format"); return 'Invalid domain name format, please review registry policy about accepted labels'; } +} + +function normalize_v4_address($v4) { + // Remove leading zeros from the first octet + $v4 = preg_replace('/^0+(\d)/', '$1', $v4); + + // Remove leading zeros from successive octets + $v4 = preg_replace('/\.0+(\d)/', '.$1', $v4); + + return $v4; +} + +function normalize_v6_address($v6) { + // Upper case any alphabetics + $v6 = strtoupper($v6); + + // Remove leading zeros from the first word + $v6 = preg_replace('/^0+([\dA-F])/', '$1', $v6); + + // Remove leading zeros from successive words + $v6 = preg_replace('/:0+([\dA-F])/', ':$1', $v6); + + // Introduce a :: if there isn't one already + if (strpos($v6, '::') === false) { + $v6 = preg_replace('/:0:0:/', '::', $v6); + } + + // Remove initial zero word before a :: + $v6 = preg_replace('/^0+::/', '::', $v6); + + // Remove other zero words before a :: + $v6 = preg_replace('/(:0)+::/', '::', $v6); + + // Remove zero words following a :: + $v6 = preg_replace('/:(:0)+/', ':', $v6); + + return $v6; } \ No newline at end of file