# HG changeset patch # User Ivo Smits # Date 1415895732 -3600 # Node ID caa68b5023134ec767cea8c4bcb7897d5d99409a # Parent 3ac7bd7495fd6bb6a192d8a4ba4b5339144e5e11 Added the MARC DNS server (and small fixes in marcus and anoclaims) diff -r 3ac7bd7495fd -r caa68b502313 anoclaims.php --- a/anoclaims.php Sat Nov 08 22:22:42 2014 +0100 +++ b/anoclaims.php Thu Nov 13 17:22:12 2014 +0100 @@ -21,6 +21,7 @@ $label = argtolabel($argv, $argi); $resource = $database->GetResource($label); if (!$resource) $resource = array('label' => $label, 'value' => array()); + else $resource = $resource->ToArray(); if (!$database->UpdateResource($resource, $key)) throw new Exception('Could not update resource'); break; case 'SETNS': diff -r 3ac7bd7495fd -r caa68b502313 marcus.php --- a/marcus.php Sat Nov 08 22:22:42 2014 +0100 +++ b/marcus.php Thu Nov 13 17:22:12 2014 +0100 @@ -262,9 +262,9 @@ case 'DOM': case 'DOMAIN': return chr(4).strtolower(trim($argv[$argi++], '.')); default: - if (preg_match('/^AS[0-9]{1-9}$/', $t)) return chr(3).marc_decode_int32be(substr($argv[$argi++], 2)); - if (preg_match('_^[0-9]{1-3}\.[0-9]{1-3}\.[0-9]{1-3}\.[0-9]{1-3}/[0-9]{1-2}$_', $t)) return ipv4tolabel($t); - if (preg_match('_^(((?=.*(::))(?!.*\3.+\3))\3?|([\dA-F]{1,4}(\3|:\b|$)|\2))(?4){5}((?4){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})\z/[0-9]{1-3}_i', $t)) return ipv6tolabel($t); + if (preg_match('/^AS[0-9]{1-9}$/', $t)) return chr(3).marc_decode_int32be(substr($t, 2)); + if (preg_match('_^[0-9]{1-3}\.[0-9]{1-3}\.[0-9]{1-3}\.[0-9]{1-3}/[0-9]{1-2}$_', $t)) return ipnettolabel($t); + if (preg_match('_^(((?=.*(::))(?!.*\3.+\3))\3?|([\dA-F]{1,4}(\3|:\b|$)|\2))(?4){5}((?4){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})\z/[0-9]{1-3}_i', $t)) return ipnettolabel($t); if (preg_match('/^[a-f0-9]{64}$/i', $t)) return chr(0).hex2bin($t); if (preg_match('/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}$/i', $t)) return chr(4).strtolower(trim($t, '.')); throw new Exception('Could not detect label type for '.$t); diff -r 3ac7bd7495fd -r caa68b502313 marns.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/marns.php Thu Nov 13 17:22:12 2014 +0100 @@ -0,0 +1,360 @@ + 'QUERY', 1 => 'IQUERY', 2 => 'STATUS'); +$dns_responsecode = array(0 => 'NOERROR', 1 => 'FORMERR', 2 => 'SERVFAIL', 3 => 'NXDOMAIN', 4 => 'NOTIMPL', 5 => 'REFUSED'); +$dns_rr_class = array(1 => 'IN', 2 => 'CS', 3 => 'CH', 4 => 'HS', 255 => 'ANY'); +$dns_rr_type = array(1 => 'A', 2 => 'NS', 5 => 'CNAME', 6 => 'SOA', 10 => 'NULL', 12 => 'PTR', 15 => 'MX', 16 => 'TXT', 28 => 'AAAA', 252 => 'AXFR', 255 => 'ANY'); +$dns_opcode_rev = array_flip($dns_opcode); +$dns_responsecode_rev = array_flip($dns_responsecode); +$dns_rr_class_rev = array_flip($dns_rr_class); +$dns_rr_type_rev = array_flip($dns_rr_type); + +error_reporting(E_ALL); + +if (!isset($argv)) $argv = $_SERVER['argv']; +if (count($argv) < 3) { + echo "Usage: ".$argv[0]." [marc-flatfile-database-filename] [listen-ip-address] [listen-port-number]\n"; + die(); +} + +$dbfile = $argv[1]; +$listenaddress = $argv[2]; +$listenport = $argv[3]; + +$database = new MARCDatabaseFlatFile(); + +function dbreload() { + global $database, $dbfile; + $database->Open($dbfile); + $database->Close(); +} + +dbreload(); + +$socket = socket_create(strpos($listenaddress, ':') === FALSE ? AF_INET : AF_INET6, SOCK_DGRAM, SOL_UDP); +socket_bind($socket, $listenaddress, $listenport); + +while (true) { + $buffer = $remoteaddr = $remoteport = NULL; + $readlen = socket_recvfrom($socket, $buffer, 0xFFFF, 0, $remoteaddr, $remoteport); + if ($readlen === FALSE) continue; + $buffer = dns_process($buffer, $remoteaddr); + if ($buffer === FALSE) continue; + socket_sendto($socket, $buffer, strlen($buffer), 0, $remoteaddr, $remoteport); +} + +function dns_process($reqbuffer, $remoteaddr) { + $req = dns_decode($reqbuffer); + if ($req['isresponse']) return; + $res = array('id' => $req['id'], 'isresponse' => true, 'opcode' => $req['opcode'], 'question' => $req['question'], 'responsecode' => 'NOERROR', 'answer' => array(), 'authority' => array(), 'additional' => array()); + if ($req['opcode'] != 'QUERY') { + $res['responsecode'] = 'NOTIMPL'; + } else { + foreach ($req['question'] as $q) { + if ($q['class'] == 'IN') { + if (!dns_get_response($res, $q)) $res['responsecode'] = 'NXDOMAIN'; + } else if ($q['class'] == 'CH' && $q['type'] == 'TXT') { + switch ($q['name']) { + case 'version.bind': $res['answer'][] = array('name' => $q['name'], 'class' => $q['class'], 'type' => $q['type'], 'value' => 'UCIS MARNS - http://www.ucis.nl'); break; + case 'reload.marns': + if ($remoteaddr == '127.0.0.1' || $remoteaddr == '::1' || $remoteaddr == $GLOBALS['listenaddress']) { + dbreload(); + $res['answer'][] = array('name' => $q['name'], 'class' => $q['class'], 'type' => $q['type'], 'value' => 'RELOAD OK'); + } else { + $res['responsecode'] = 'REFUSED'; + } + break; + default: $res['responsecode'] = 'NOTIMPL'; break; + } + } else { + $res['responsecode'] = 'NOTIMPL'; + } + } + } + return dns_encode($res); +} + +function dns_find_zone(&$name, &$zone) { + global $database; + $zone = strtolower(rtrim($name, '.')); + $name = ''; + while (strlen($zone)) { + $dbzone = $database->GetResource(chr(4).$zone); + if ($dbzone != NULL) return $dbzone; + $i = strpos($zone, '.'); + if ($i === FALSE) break; + if (strlen($name)) $name .= '.'; + $name .= substr($zone, 0, $i); + $zone = substr($zone, $i + 1); + } + return NULL; +} +function dns_convert_record($item, $realname, $zonename) { + if (isset($item['class']) && strcasecmp($item['class'], 'IN')) return FALSE; + if (!is_array($item) || !isset($item['type'])) return FALSE; + if (strlen($realname)) $realname .= '.'; + $realname .= $zonename; + $type = strtoupper($item['type']); + $rr = array('name' => $realname, 'type' => $type); + if (($type == 'A' || $type == 'AAAA') && isset($item['target'])) $rr['address'] = $item['target']; + elseif (($type == 'CNAME' || $type == 'NS' || $type == 'PTR') && isset($item['target'])) $rr['target'] = $item['target']; + elseif ($type == 'MX' && isset($item['target'])) { $rr['target'] = $item['target']; if (isset($item['priority'])) $rr['priority'] = $item['priority']; } + else return FALSE; + if (isset($rr['target'])) { + if (!strlen($rr['target'])) $rr['target'] = $zonename; + elseif ($rr['target'][strlen($rr['target']) - 1] != '.') $rr['target'] .= '.'.$zonename; + } + $rr['ttl'] = isset($item['ttl']) ? $item['ttl'] : 3600; + return $rr; +} +function dns_find_records_real(&$response, $dbzone, $findname, $realname, $zonename, $qtype) { + if (!isset($dbzone[$findname])) return FALSE; + foreach ($dbzone[$findname] as $item) { + if (!is_array($item) || !isset($item['type'])) continue; + if (isset($item['class']) && strcasecmp($item['class'], 'IN')) continue; + if (strcasecmp($item['type'], $qtype) && strcasecmp($qtype, 'ANY') && strcasecmp($item['type'], 'CNAME')) continue; + $record = dns_convert_record($item, $realname, $zonename); + if ($record === FALSE) continue; + $response[] = $record; + } + return TRUE; +} +function dns_find_ns_records(&$res, $dbzone, $findname, $realname, $zonename) { + if (!isset($dbzone[$findname])) return FALSE; + $foundns = FALSE; + foreach ($dbzone[$findname] as $item) { + if (!is_array($item) || !isset($item['type'])) continue; + if (isset($item['class']) && strcasecmp($item['class'], 'IN')) continue; + if (strcasecmp($item['type'], 'NS')) continue; + $record = dns_convert_record($item, $realname, $zonename); + if ($record === FALSE) continue; + $res['authority'][] = $record; + if (strcasecmp(substr($record['target'], -strlen($zonename)), $zonename) == 0) dns_find_records_realwild($res['additional'], $dbzone, rtrim(substr($record['target'], 0, -strlen($zonename)), '.'), $zonename, 'ANY'); + $foundns = TRUE; + } + return $foundns; +} +function dns_find_records_realwild(&$res, $dbzone, $name, $zonename, $qtype) { + if (dns_find_records_real($res, $dbzone, $name, $name, $zonename, $qtype)) return TRUE; + $nameparts = strlen($name) ? explode('.', strtolower($name)) : array(); + for ($i = 1; $i < count($nameparts); $i++) { + if (dns_find_records_real($res, $dbzone, '*.'.implode('.', array_slice($nameparts, $i)), $name, $zonename, $qtype)) return TRUE; + } + if (dns_find_records_real($res, $dbzone, '*', $name, $zonename, $qtype)) return TRUE; + return FALSE; +} +function dns_find_records(&$res, $dbzone, $name, $zone, $qtype) { + if (!isset($dbzone->value['dnsrecords']) || !is_array($dbzone->value['dnsrecords'])) return FALSE; + $dbzone = $dbzone->value['dnsrecords']; + $nameparts = strlen($name) ? explode('.', strtolower($name)) : array(); + for ($i = count($nameparts) - 1; $i >= 0; $i--) { + $realname = implode('.', array_slice($nameparts, $i)); + if (dns_find_ns_records($res, $dbzone, $realname, $realname, $zone)) return TRUE; + if (dns_find_ns_records($res, $dbzone, '*.'.implode('.', array_slice($nameparts, $i + 1)), $realname, $zone)) return TRUE; + } + return dns_find_records_realwild($res['answer'], $dbzone, $name, $zone, $qtype); +} +function dns_find_authority(&$res, $dbzone, $zone, $nsquery = FALSE) { + if (!isset($dbzone->value['ns']) || !is_array($dbzone->value['ns'])) return; + foreach ($dbzone->value['ns'] as $nsname => $nsglues) { + $rr = array('name' => $zone, 'type' => 'NS', 'ttl' => 3600, 'target' => (strlen($nsname) && $nsname[strlen($nsname) - 1] != '.') ? $nsname.'.'.$zone : $nsname); + if ($nsquery) $res['answer'][] = $rr; + else $res['authority'][] = $rr; + if (is_array($nsglues)) { + foreach ($nsglues as $glue) { + $glue = inet_pton($glue); + if (!strlen($glue)) continue; + $res['additional'][] = array('name' => $rr['target'], 'ttl' => 3600, 'type' => strlen($glue) == 4 ? 'A' : 'AAAA', 'data' => $glue); + } + } + } +} + +function dns_get_response(&$res, $q) { + $name = $q['name']; + $zone = ''; + $dbzone = dns_find_zone($name, $zone); + if ($dbzone == NULL) return; + dns_find_records($res, $dbzone, $name, $zone, $q['type']); + //dns_find_authority($res, $dbzone, $zone, $q['type'] == 'NS'); +} + +function dns_decode_uint16be($data, $i = 0) { return (ord($data[$i+0]) << 8) | ord($data[$i+1]); } +function dns_decode_uint32be($data, $i = 0) { return (ord($data[$i]) << 24) | (ord($data[$i+1]) << 16) | (ord($data[$i+2]) << 8) | ord($data[$i+3]); } +function dns_encode_uint32be($v) { return pack("N", $v); } +function dns_encode_uint16be($v) { return pack("n", $v); } + +function dns_decode($packet) { + global $dns_opcode, $dns_responsecode; + $dp = array( + 'id' => dns_decode_uint16be($packet, 0), + 'isresponse' => ($packet[2] & 128) != 0, + 'opcode' => ($packet[2] >> 3) & 0x0F, + 'authoritative' => ($packet[2] & 4) != 0, + 'truncation' => ($packet[2] & 2) != 0, + 'recursiondesired' => ($packet[2] & 1) != 0, + 'recursionavailable' => ($packet[3] & 128) != 0, + 'responsecode' => ($packet[3] & 0x0F), + 'qdcount' => dns_decode_uint16be($packet, 4), + 'ancount' => dns_decode_uint16be($packet, 6), + 'nscount' => dns_decode_uint16be($packet, 8), + 'arcount' => dns_decode_uint16be($packet, 10), + ); + if (isset($dns_opcode[$dp['opcode']])) $dp['opcode'] = $dns_opcode[$dp['opcode']]; + if (isset($dns_responsecode[$dp['responsecode']])) $dp['responsecode'] = $dns_responsecode[$dp['responsecode']]; + $ptr = 12; + $dp['question'] = dns_decode_rrs($packet, $ptr, false, $dp['qdcount']); + $dp['answer'] = dns_decode_rrs($packet, $ptr, true, $dp['ancount']); + $dp['authority'] = dns_decode_rrs($packet, $ptr, true, $dp['nscount']); + $dp['additional'] = dns_decode_rrs($packet, $ptr, true, $dp['arcount']); + return $dp; +} +function dns_decode_rrs($packet, &$ptr, $isanswer, $count) { + $ret = array(); + for ($i = 0; $i < $count; $i++) $ret[] = dns_decode_rr($packet, $ptr, $isanswer); + return $ret; +} +function dns_decode_rr($packet, &$ptr, $isanswer) { + global $dns_rr_class, $dns_rr_type; + $rr = array(); + $rr['name'] = dns_decode_name($packet, $ptr); + $rr['type'] = dns_decode_uint16be($packet, $ptr); $ptr += 2; + $rr['class'] = dns_decode_uint16be($packet, $ptr); $ptr += 2; + if (isset($dns_rr_class[$rr['class']])) $rr['class'] = $dns_rr_class[$rr['class']]; + if (isset($dns_rr_type[$rr['type']])) $rr['type'] = $dns_rr_type[$rr['type']]; + if ($isanswer) { + $rr['ttl'] = dns_decode_uint32be($packet, $ptr); $ptr += 4; + $rrdatalength = dns_decode_uint16be($packet, $ptr); $ptr += 2; + $rr['data'] = substr($packet, $ptr, $rrdatalength); + $dataptr = $ptr; + $ptr += $rrdatalength; + if ($rr['class'] == 'IN') { + switch ($rr['type']) { + case 'A': + case 'AAAA': + $rr['address'] = inet_ntop($rr['data']); + break; + case 'CNAME': + case 'NS': + case 'PTR': + $rr['target'] = dns_decode_name($packet, $dataptr); + break; + case 'MX': + $rr['priority'] = dns_decode_uint16be($packet, $dataptr); $dataptr += 2; + $rr['target'] = dns_decode_name($packet, $dataptr); + break; + case 'TXT': + $rr['value'] = dns_decode_name($packet, $dataptr); + break; + } + } + } + return $rr; +} +function dns_decode_name($packet, &$ptr) { + $name = ''; + $canrecurse = 64; + while ($ptr < strlen($packet)) { + $l = ord($packet[$ptr++]); + if ($l == 0) break; + if (($l & 0xC0) == 0) { + $name .= substr($packet, $ptr, $l).'.'; + $ptr += $l; + } else if (($l & 0xC0) == 0xC0 && $canrecurse > 0) { + $newptr = (($l & 0x3F) << 8) | ord($packet[$ptr++]); + $ptr = &$newptr; + $canrecurse--; + } else { + return FALSE; + } + } + return substr($name, 0, -1); +} + +function dns_encode($dp) { + global $dns_opcode_rev, $dns_responsecode_rev; + $data = ''; + $qdcount = isset($dp['question']) ? count($dp['question']) : 0; + $ancount = isset($dp['answer']) ? count($dp['answer']) : 0; + $nscount = isset($dp['authority']) ? count($dp['authority']) : 0; + $arcount = isset($dp['additional']) ? count($dp['additional']) : 0; + $opcode = isset($dp['opcode']) ? $dp['opcode'] : 0; + $responsecode = isset($dp['responsecode']) ? $dp['responsecode'] : 0; + if (isset($dns_opcode_rev[$opcode])) $opcode = $dns_opcode_rev[$opcode]; + if (isset($dns_responsecode_rev[$responsecode])) $responsecode = $dns_responsecode_rev[$responsecode]; + $isresponse = $ancount || $nscount || $arcount || $responsecode != 0 || (isset($dp['isresponse']) && $dp['isresponse']); + $data .= dns_encode_uint16be($dp['id']); + $data .= chr(($isresponse ? 128 : 0) | (($opcode & 0x0F) << 3) | ((isset($dp['authoritative']) && $dp['authoritative']) ? 4 : 0) | ((isset($dp['truncation']) && $dp['truncation']) ? 2 : 0) | ((isset($dp['recursiondesired']) && $dp['recursiondesired']) ? 1 : 0)); + $data .= chr(((isset($dp['recursionavailable']) && $dp['recursionavailable']) ? 128 : 0) | ($responsecode & 0xF)); + $data .= dns_encode_uint16be($qdcount); + $data .= dns_encode_uint16be($ancount); + $data .= dns_encode_uint16be($nscount); + $data .= dns_encode_uint16be($arcount); + $labels = array(); + if ($qdcount) dns_encode_rrs($data, $dp['question'], false, $labels); + if ($ancount) dns_encode_rrs($data, $dp['answer'], true, $labels); + if ($nscount) dns_encode_rrs($data, $dp['authority'], true, $labels); + if ($arcount) dns_encode_rrs($data, $dp['additional'], true, $labels); + return $data; +} +function dns_encode_rrs(&$data, $rrs, $isanswer, &$labels) { + global $dns_rr_type_rev, $dns_rr_class_rev; + foreach ($rrs as $rr) { + $rrtype = $rr['type']; + $rrclass = isset($rr['class']) ? $rr['class'] : 1; + if (isset($dns_rr_type_rev[$rrtype])) $rrtype = $dns_rr_type_rev[$rrtype]; + if (isset($dns_rr_class_rev[$rrclass])) $rrclass = $dns_rr_class_rev[$rrclass]; + $data .= dns_encode_name($rr['name'], $labels, strlen($data)); + $data .= dns_encode_uint16be($rrtype); + $data .= dns_encode_uint16be($rrclass); + if ($isanswer) { + $rrdata = isset($rr['data']) ? $rr['data'] : ''; + if (($rrtype == 5 || $rrtype == 2 || $rrtype == 12) && isset($rr['target'])) $rrdata = dns_encode_name($rr['target'], $labels, strlen($data) + 6); + elseif (($rrtype == 16) && isset($rr['value'])) $rrdata = dns_encode_txt($rr['value']); + elseif ($rrtype == 15 && isset($rr['target'])) $rrdata = dns_encode_uint16be(isset($rr['priority']) ? $rr['priority'] : 0).dns_encode_name($rr['target'], $labels, strlen($data) + 8); + elseif ($rrclass == 1) { + if (($rrtype == 1 || $rrtype == 28) && isset($rr['address'])) $rrdata = inet_pton($rr['address']); + } + $rrdata = (string)$rrdata; + $data .= dns_encode_uint32be(isset($rr['ttl']) ? $rr['ttl'] : 0); + $data .= dns_encode_uint16be(strlen($rrdata)); + $data .= $rrdata; + } + } +} +function dns_encode_name($name, &$labels, $offset = FALSE) { + $name = rtrim($name, '.'); + $data = ''; + while (strlen($name)) { + $lname = strtolower($name); + if (isset($labels[$lname])) { + $ptr = $labels[$lname]; + return $data.chr(0xC0 | (($ptr >> 8) & 0x3F)).chr($ptr & 0xFF); + } else { + $dot = strpos($name, '.'); + if ($dot === FALSE) { + $part = $name; + $name = ''; + } else { + $part = substr($name, 0, $dot); + $name = substr($name, $dot + 1); + } + if (strlen($part) > 63) $part = substr($part, 0, 63); + if ($offset !== FALSE && $offset + strlen($data) <= 0x3FFF) $labels[$lname] = strlen($data) + $offset; + $data .= chr(strlen($part)).$part; + } + } + return $data.chr(0); +} +function dns_encode_txt($parts) { + $data = ''; + if (!is_array($parts)) $parts = array($parts); + foreach ($parts as $part) { + $part = (string)$part; + if (strlen($part) > 63) $part = substr($part, 0, 63); + $data .= chr(strlen($part)).$part; + } + return $data; +} + diff -r 3ac7bd7495fd -r caa68b502313 marns_import.php --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/marns_import.php Thu Nov 13 17:22:12 2014 +0100 @@ -0,0 +1,128 @@ + strlen($orgzonename) && strcasecmp(substr($token, -strlen($orgzonename)-1), '.'.$orgzonename) == 0) $name = substr($token, 0, -strlen($orgzonename)-1); + elseif (strlen($token) && $token[strlen($token) - 1] == '.') continue; + else $name = $token; + $name = strtolower($name); + $token = strtok(" \t"); + } + $rr = array(); + if (is_numeric($token)) { + $rr['ttl'] = intval($token); + $token = strtok(" \t"); + if ($token == 'IN') { + $rr['class'] = $token; + $token = strtok(" \t"); + } + } else if ($token == 'IN') { + $rr['class'] = $token; + $token = strtok(" \t"); + if (is_numeric($token)) { + $rr['ttl'] = intval($token); + $token = strtok(" \t"); + } + } + $rr['type'] = strtoupper($token); + switch ($rr['type']) { + case 'A': + case 'AAAA': + $rr['target'] = strtok(" \t"); + break; + case 'CNAME': + case 'NS': + case 'PTR': + $rr['target'] = strtok(" \t"); + break; + case 'MX': + $rr['priority'] = strtok(" \t"); + $rr['target'] = strtok(" \t"); + break; + default: + $rr = NULL; + } + if ($rr === NULL) continue; + if (!isset($dnsrecords[$name])) $dnsrecords[$name] = array(); + $dnsrecords[$name][] = $rr; +} +fclose($stdin); + +print_r($dnsrecords); + +$skey = $argv[3]; +if (file_exists($skey)) { + $skey = file_get_contents($skey); +} else if (strlen($skey) == 64) { + $skey = bin2hex($skey); +} else if ($skey == '-') { + nacl_crypto_sign_ed25519_keypair($skey); +} +if (strlen($skey) != 32 && strlen($skey) != 64) throw new Exception('Invalid signing key specified'); + +error_reporting(E_ALL); +$database = new MARCDatabaseFlatFile(); +$database->Open($argv[1]); +$label = chr(4).'ucis-test.ano'; +$resource = $database->GetResource($marclabel); +if ($resource) { + $resource = $resource->ToArray(); + unset($resource['key']); +} else { + $resource = array('label' => $marclabel, 'value' => array(), 'transfer' => '', 'expiration' => time() + 3600 * 24 * 7); +} +$resource['value']['dnsrecords'] = $dnsrecords; +if (!$database->UpdateResource($resource, $skey)) throw new Exception('Could not update resource'); +$database->Save(); +$database->Close(); + +function argtolabel($t) { + if (preg_match('_^[0-9]{1-3}\.[0-9]{1-3}\.[0-9]{1-3}\.[0-9]{1-3}/[0-9]{1-2}$_', $t)) return ipnettolabel($t); + if (preg_match('_^(((?=.*(::))(?!.*\3.+\3))\3?|([\dA-F]{1,4}(\3|:\b|$)|\2))(?4){5}((?4){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})\z/[0-9]{1-3}_i', $t)) return ipnettolabel($t); + if (preg_match('/^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,6}$/i', $t)) return chr(4).strtolower(trim($t, '.')); + throw new Exception('Could not detect label type for '.$t); +} +function ipnettolabel($s) { + $ip = inet_pton(strtok($s, '/')); + $pl = intval(strtok('/')); + if ($pl == 0) throw new Exception('Invalid IP network specified'); + if (strlen($ip) == 4) return chr(1).$ip.chr($pl); + if (strlen($ip) == 16) return chr(2).$ip.chr($pl); +}