Mercurial > hg > marc_php
diff marcus.php @ 0:3ac7bd7495fd draft
Initial commit
author | Ivo Smits <Ivo@UCIS.nl> |
---|---|
date | Sat, 08 Nov 2014 22:22:42 +0100 |
parents | |
children | caa68b502313 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/marcus.php Sat Nov 08 22:22:42 2014 +0100 @@ -0,0 +1,371 @@ +<?php +require_once './marccore.php'; + +error_reporting(E_ALL); + +if (!isset($argv)) $argv = $_SERVER['argv']; +$argi = 1; + +class FilteredResourceIterator implements Iterator { + private $source, $filtertype, $filtervalue; + public function __construct($source, $filtertype, $filtervalue) { + $this->source = $source; + $this->filtertype = $filtertype; + $this->filtervalue = $filtervalue; + } + public function current() { $r = $this->source->current(); return $r; } + public function key() { return $this->source->key(); } + public function valid() { return $this->source->valid(); } + public function next() { $this->source->next(); $this->findnext(); } + public function rewind() { $this->source->rewind(); $this->findnext(); } + private function findnext() { + while ($this->source->valid()) { + $c = $this->source->current(); + if ($this->filter($c)) break; + $this->source->next(); + } + } + private function filter($c) { + switch ($this->filtertype) { + case 'OWNER': return isset($c['value']['owner']) && is_scalar($c['value']['owner']) && strcasecmp($c['value']['owner'], $this->filtervalue) == 0; + case 'KEY': return $c['key'] == hex2bin($this->filtervalue); + case 'DOMEXT': return ord($c['label'][0]) == 4 && substr_compare($c['label'], $this->filtervalue, -strlen($this->filtervalue), strlen($this->filtervalue), TRUE) == 0; + default: return FALSE; + } + } +} + +$database = new MARCDatabaseFlatFile(); +$key = NULL; +$resource = NULL; +$reschanged = FALSE; + +while ($argi < count($argv)) { + switch (strtoupper($argv[$argi++])) { + case 'OPEN': + if ($reschanged) echo "Warning: selected resource has not been updated.\n"; + if ($database->IsChanged()) echo "Warning: database has unsaved changes.\n"; + $database->Open($argv[$argi++]); + $reschanged = FALSE; + break; + case 'OPENSQLITE': + $database = new MARCDatabaseSQLite($argv[$argi++]); + break; + case 'OPENDBA': + $database = new MARCDatabaseDBA($argv[$argi++]); + break; + case 'SAVE': + $database->Save(); + break; + case 'SAVEAS': + $database->SaveAs($argv[$argi]); + break; + case 'SYNC': + $database->SyncHTTP($argv[$argi++]); + break; + case 'KEY': + switch (strtoupper($argv[$argi++])) { + case 'CREATE': + $key = array('store' => TRUE); + $key['pk'] = nacl_crypto_sign_ed25519_keypair($key['sk'], randombytes(32)); + $dbchanged = TRUE; + echo 'Created public key '.bin2hex($key['pk'])."\n"; + break; + case 'FORGET': + $key['store'] = FALSE; + $dbchanged = TRUE; + break; + case 'STORE': + $key['store'] = TRUE; + $dbchanged = TRUE; + break; + case 'USE': + $key = array('store' => FALSE, 'pk' => $resource['key']); + if (isset($resource['value']['seckey'])) $key['sk'] = $resource['value']['seckey']; + if (isset($resource['value']['seckeyenc'])) $key['locked'] = $resource['value']['seckeyenc']; + break; + case 'IMPORT': + $key = array('store' => FALSE); + $key['pk'] = nacl_crypto_sign_ed25519_keypair($key['sk'], hex2bin($argv[$argi++])); + $dbchanged = TRUE; + break; + case 'UNLOCK': + if (!isset($key['locked'])) throw new Exception('The key is not locked'); + $ret = hash('sha512', $key['pk'].$argv[$argi++], TRUE); + $key['sk'] = ''; + for ($i = 0; $i < 32; $i++) $key['sk'] .= chr(ord($key['locked'][$i]) ^ ord($ret[$i])); + $ret = nacl_crypto_sign_ed25519_keypair($key['sk'], $key['sk']); + if ($ret != $key['pk']) throw new Exception('Key password is not valid'); + echo 'Unlocked public key '.bin2hex($key['pk'])."\n"; + break; + default: + throw new Exception('Unknown key operation '.$argv[$argi-1]); + } + break; + case 'LIST': + foreach ($database->GetResources() as $ret) echo labeltoname($ret['label'])."\n"; + break; + case 'FIND': + foreach (filterresources($database->GetResources(), $argv, $argi) as $ret) echo labeltoname($ret['label'])."\n"; + break; + case 'DELETE': + $database->DeleteResource($resource['label']); + break; + case 'DUMP': + dumpresource($resource); + break; + case 'UPDATE': + if (!isset($resource['key'])) $resource['key'] = $key['pk']; + unset($resource['serial']); + $res = $database->UpdateResource($resource, $key['sk']); + if (!$res) throw new Exception('Could not update resource'); + $resource = $res->ToArray(); + $reschanged = FALSE; + break; + case 'SET': + switch (strtoupper($argv[$argi++])) { + case 'OWNER': + if (!isset($resource['value']) || !is_array($resource['value'])) $resource['value'] = array(); + $resource['value']['owner'] = $argv[$argi++]; + $reschanged = TRUE; + break; + case 'DESC': + case 'DESCR': + case 'DESCRIPTION': + if (!isset($resource['value']) || !is_array($resource['value'])) $resource['value'] = array(); + $resource['value']['descr'] = $argv[$argi++]; + $reschanged = TRUE; + break; + case 'PWAUTH': + if (!isset($key['sk'])) throw new Exception('The key is not available'); + $ret = hash('sha512', $key['pk'].$argv[$argi++], TRUE); + $key['locked'] = ''; + for ($i = 0; $i < 32; $i++) $key['locked'] .= chr(ord($key['sk'][$i]) ^ ord($ret[$i])); + if (!isset($resource['value']) || !is_array($resource['value'])) $resource['value'] = array(); + $resource['value']['seckeyenc'] = $key['locked']; + $reschanged = TRUE; + break; + case 'TRANSFER': + $ret = $argv[$argi++]; + $resource['value']['transfer'] = (strtolower($ret) == 'any') ? '' : hex2bin($ret); + $reschanged = TRUE; + break; + default: + throw new Exception('Unknown set operation '.$argv[$argi-1]); + } + break; + case 'ADD': + switch (strtoupper($argv[$argi++])) { + case 'NS': + if (!isset($resource['value']) || !is_array($resource['value'])) $resource['value'] = array(); + if (!isset($resource['value']['ns']) || !is_array($resource['value']['ns'])) $resource['value']['ns'] = array(); + $nsname = $argv[$argi++]; + $nsglue = (strlen($nsname) && $nsname[strlen($nsname)-1] != '.') ? $argv[$argi++] : NULL; + if (!isset($resource['value']['ns'][$nsname]) || !is_array($resource['value']['ns'][$nsname])) $resource['value']['ns'][$nsname] = array(); + if ($nsglue !== NULL) $resource['value']['ns'][$nsname][] = $nsglue; + $reschanged = TRUE; + break; + default: + throw new Exception('Unknown add operation '.$argv[$argi-1]); + } + break; + case 'RESET': + switch (strtoupper($argv[$argi++])) { + case 'OWNER': + if (!is_array($resource['value'])) $resource['value'] = array(); + unset($resource['value']['owner']); + $reschanged = TRUE; + break; + case 'DESC': + case 'DESCR': + case 'DESCRIPTION': + if (!is_array($resource['value'])) $resource['value'] = array(); + unset($resource['value']['descr']); + $reschanged = TRUE; + break; + case 'PWAUTH': + if (!is_array($resource['value'])) $resource['value'] = array(); + unset($resource['value']['seckeyenc']); + $reschanged = TRUE; + break; + case 'NS': + if (!is_array($resource['value'])) $resource['value'] = array(); + unset($resource['value']['ns']); + $reschanged = TRUE; + break; + case 'VALUE': + $resource['value'] = array(); + $reschanged = TRUE; + break; + case 'TRANSFER': + unset($resource['transfer']); + $reschanged = TRUE; + break; + case 'EXPIRATION': + unset($resource['expiration']); + $reschanged = TRUE; + break; + default: + throw new Exception('Unknown reset operation '.$argv[$argi-1]); + } + break; + case 'CREATE': + if ($reschanged) echo "Warning: selected resource has not been updated.\n"; + $reschanged = TRUE; + $resource = array('label' => argtolabel($argv, $argi)); + break; + case 'SELECT': + if ($reschanged) echo "Warning: selected resource has not been updated.\n"; + $reschanged = FALSE; + $label = argtolabel($argv, $argi); + $resource = $database->GetResource($label); + if (!$resource) echo "Warning: resource ".labeltoname($label)." does not exist.\n"; + else $resource = $resource->ToArray(); + break; + case 'HELP': + print_help(); + break; + default: + throw new Exception('Unknown operation '.$argv[$argi-1]); + } +} +if ($reschanged) echo "Warning: selected resource has not been updated.\n"; +if ($database->IsChanged()) echo "Warning: database has unsaved changes.\n"; +$database->Close(); + +function filterresources($iterator, $argv, &$argi) { + $filtertype = strtoupper($argv[$argi++]); + switch ($filtertype) { + case 'OWNER': + case 'KEY': + case 'DOMEXT': + $filtervalue = $argv[$argi++]; + break; + default: + throw new Exception('Unknown filter type '.$t); + } + return new FilteredResourceIterator($iterator, $filtertype, $filtervalue); +} +function argtolabel($argv, &$argi) { + $t = $argv[$argi++]; + switch (strtoupper($t)) { + case 'LABEL': return hex2bin($argv[$argi++]); + case 'CURRENTKEY': return chr(0).$GLOBALS['key']['pk']; + case 'RESOURCEKEY': return chr(0).$GLOBALS['resource']['key']; + case 'KEY': return chr(0).hex2bin($argv[$argi++]); + case 'IP': + case 'IP4': + case 'IPV4': + case 'IP6': + case 'IPV6': return ipnettolabel($argv[$argi++]); + case 'AS': return chr(3).marc_decode_int32be($argv[$argi++]); + 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('/^[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); + } +} +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); +} +function labeltoname($l) { + switch (ord($l)) { + case 0: return 'KEY '.bin2hex(substr($l, 1)); + case 1: if (strlen($l) == 6) return 'IPv4 '.inet_ntop(substr($l, 1, 4)).'/'.ord($l[5]); else return 'LABEL '.bin2hex($l); + case 2: if (strlen($l) == 18) return 'IPv6 '.inet_ntop(substr($l, 1, 16)).'/'.ord($l[17]); else return 'LABEL '.bin2hex($l); + case 3: if (strlen($l) == 5) return 'AS '.marc_decode_int32be(substr($l, 1, 4)); else return 'LABEL '.bin2hex($l); + case 4: if (strlen($l) > 1) return 'DOM '.substr($l, 1); else return 'LABEL '.bin2hex($l); + default: return 'LABEL '.bin2hex($l); + } +} +function dumpresource($r, $p = '') { + if (is_null($r)) { + echo "NULL\n"; + } else if (is_string($r)) { + $bin = FALSE; + for ($i = 0; $i < strlen($r); $i++) $bin |= ord($r[$i]) < 32 || ord($r[$i]) > 126; + if ($bin) echo '0x'.bin2hex($r)."\n"; + else echo '"'.$r.'"'."\n"; + } else if (is_scalar($r)) { + echo $r."\n"; + } else if (is_array($r)) { + echo "array(\n"; + foreach ($r as $key => $value) { + echo $p.' ['.$key.'] => '; + dumpresource($value, $p.' '); + } + echo $p." )\n"; + } else { + print_r($r); + } +} +function randombytes($n) { + $b = ''; + $file = fopen('/dev/urandom', 'r'); + for ($i = 0; $i < $n; $i++) $b .= fgetc($file); + fclose($file); + return $b; +} + +function print_help() { + echo 'Usage: marcus.php [operation] [arguments] [operation] [arguments]... +open [filename.mdb] - opens the specified database file +save - saves the current database file +saveas [filename.mdb] - saves the current data to a new databse file +sync [url] - synchronize database with remote server +key create - create a new key pair +key forget - do not store the current key pair in the local database +key store - store the current key pair in the local database +key use - use the key pair from the currently selected resource +key import [secretkey] - import the key pair defined by the given secret key +key unlock [password] - unlock a password protected key pair +list - list registered resources +find [type] [value] - list registered sources matching filter (type=OWNER|KEY|DOMEXT) +create [identifier] - create given resource +create currentkey - create resource for current key pair +create ip|ip4|ipv4 [ipv4network] - create resource for IPv4 network +create ip|ip6|ipv6 [ipv6network] - create resource for IPv6 network +create dom|domain [ipv6network] - create resource for domain name +select [identifier] - select resource given by identifier +select currentkey - select key resource for current key pair +select resourcekey - select key resource for the key that signed the currently selected resource +select label [identifier] - select resource by hexadecimal label +select key [publickey] - select key resource (hexadecimal) +select ip|ip4|ipv4 [ipv4network] - select resource for IPv4 network +select ip|ip6|ipv6 [ipv6network] - select resource for IPv6 network +select dom|domain [ipv6network] - select resource for domain name +delete - delete currently selected resource +dump - display currently selected resource +update - update selected resource in the local database +set owner [name] - set the owner name for the selected resource +set descr|desc|description [text] - set the description for the selected resource +set pwauth [password] - store the current key pair in the selected resource for password authentication +set transfer any - allow anyone to take over the resource +set transfer [key] - transfer the resource to given key +TODO: set expiration [value] +add ns [name] [glue] - add in-zone nameserver with glue record (name is the part of the nameserver name before the domain name, glue is an IPv4 or IPv6 address) +add ns [name]. - add an external nameserver +reset owner - clear the owner +reset descr|desc|description - clear the description +reset pwauth - remove the key pair from the recourse, disabling password authentication +reset ns - clear the nameserver records +reset transfer - disable resource transfers +reset expiration - disable explicit expiration +reset value - clear the owner, description, password authentication and nameserver records + +Examples: +OPEN marc.mdb KEY create CREATE currentkey SET owner "Your name" SET pwauth yourpassword UPDATE KEY forget CREATE yourdomain.ano SET owner "Your name" UPDATE SAVE +OPEN marc.mdb SELECT yourdomain.ano SELECT resourcekey KEY use KEY unlock yourpassword SELECT yourdomain.ano ADD ns ns1 1.2.3.4 UPDATE SAVE +OPEN marc.mdb SELECT yourdomain.ano SELECT resourcekey KEY use KEY unlock yourpassword CREATE 1.2.3.0/24 SET owner "Your name" ADD ns ns1.yourdomain.ano. UPDATE SAVE +OPEN marc.mdb SYNC http://marc.ucis.ano/ SAVE +'; +}