diff --git a/includes/config.inc.php-dist b/includes/config.inc.php-dist
index dc173ef..9efc436 100644
--- a/includes/config.inc.php-dist
+++ b/includes/config.inc.php-dist
@@ -4,7 +4,7 @@ $apiuser = ''; # The PowerDNS API username
$apipass = ''; # The PowerDNS API-user password
$apiip = ''; # The IP of the PowerDNS API
$apiport = '8081'; # The port of the PowerDNS API
-$apisid = ''; # PowerDNS's :server_id
+$apisid = 'localhost'; # PowerDNS's :server_id
$allowzoneadd = FALSE; # Allow normal users to add zones
@@ -27,7 +27,7 @@ $templates[] = array(
'owner' => 'username', # Set to 'public' to make it available to all users
'records' => array(
array(
- 'label' => '',
+ 'name' => '',
'type' => 'MX',
'content' => 'mx2.tuxis.nl',
'priority' => '200')
@@ -35,6 +35,7 @@ $templates[] = array(
);
*/
+$defaults['soa_edit'] = 'INCEPTION-INCREMENT';
$defaults['soa_edit_api'] = 'INCEPTION-INCREMENT';
$defaults['defaulttype'] = 'Master'; # Choose between 'Native' or 'Master'
$defaults['primaryns'] = 'unconfigured.primaryns'; # The value of the first NS-record
diff --git a/includes/misc.inc.php b/includes/misc.inc.php
index b41d808..a321832 100644
--- a/includes/misc.inc.php
+++ b/includes/misc.inc.php
@@ -2,6 +2,22 @@
include('config.inc.php');
+function string_starts_with($string, $prefix)
+{
+ $length = strlen($prefix);
+ return (substr($string, 0, $length) === $prefix);
+}
+
+function string_ends_with($string, $suffix)
+{
+ $length = strlen($suffix);
+ if ($length == 0) {
+ return true;
+ }
+
+ return (substr($string, -$length) === $suffix);
+}
+
function get_db() {
global $authdb;
@@ -11,17 +27,11 @@ function get_db() {
return $db;
}
-function gen_pw() {
- $password = exec('/usr/bin/pwgen -s -B -c -n 10 -1');
- return $password;
-}
-
function get_all_users() {
$db = get_db();
$r = $db->query('SELECT id, emailaddress, isadmin FROM users');
$ret = array();
- while ($row = $r->fetchArray()) {
- $row['emailaddress'] = htmlspecialchars($row['emailaddress']);
+ while ($row = $r->fetchArray(SQLITE3_ASSOC)) {
array_push($ret, $row);
}
@@ -39,6 +49,10 @@ function get_user_info($u) {
return $userinfo;
}
+function user_exists($u) {
+ return (bool) get_user_info($u);
+}
+
function do_db_auth($u, $p) {
$db = get_db();
$q = $db->prepare('SELECT * FROM users WHERE emailaddress = ?');
@@ -46,40 +60,52 @@ function do_db_auth($u, $p) {
$result = $q->execute();
$userinfo = $result->fetchArray(SQLITE3_ASSOC);
$db->close();
- if (isset($userinfo['password']) and (crypt($p, $userinfo['password']) == $userinfo['password'])) {
+
+ if ($userinfo and $userinfo['password'] and (crypt($p, $userinfo['password']) === $userinfo['password'])) {
return TRUE;
}
return FALSE;
}
-function get_pw($username) {
- $db = get_db();
- $q = $db->prepare('SELECT password FROM users WHERE emailaddress = ? LIMIT 1');
- $q->bindValue(1, $username, SQLITE_TEXT);
- $result = $q->execute();
- $pw = $result->fetchArray(SQLITE3_ASSOC);
- $db->close();
- if (isset($pw['password'])) {
- return $pw['password'];
+function add_user($username, $isadmin = FALSE, $password = '') {
+ if (!$password) {
+ $password = bin2hex(openssl_random_pseudo_bytes(32));
}
-
- return FALSE;
-}
-
-function add_user($username, $isadmin = '0', $password = FALSE) {
- if ($password === FALSE or $password == "") {
- $password = get_pw($username);
- } elseif (!preg_match('/\$6\$/', $password)) {
+ if (!string_starts_with($password, '$6$')) {
$salt = bin2hex(openssl_random_pseudo_bytes(16));
$password = crypt($password, '$6$'.$salt);
}
$db = get_db();
- $q = $db->prepare('INSERT OR REPLACE INTO users (emailaddress, password, isadmin) VALUES (?, ?, ?)');
+ $q = $db->prepare('INSERT INTO users (emailaddress, password, isadmin) VALUES (?, ?, ?)');
$q->bindValue(1, $username, SQLITE3_TEXT);
$q->bindValue(2, $password, SQLITE3_TEXT);
- $q->bindValue(3, $isadmin, SQLITE3_INTEGER);
+ $q->bindValue(3, (int)(bool) $isadmin, SQLITE3_INTEGER);
+ $ret = $q->execute();
+ $db->close();
+
+ return $ret;
+}
+
+function update_user($username, $isadmin, $password) {
+ if ($password && !preg_match('/\$6\$/', $password)) {
+ $salt = bin2hex(openssl_random_pseudo_bytes(16));
+ $password = crypt($password, '$6$'.$salt);
+ }
+
+ $db = get_db();
+
+ if ($password) {
+ $q = $db->prepare('UPDATE users SET isadmin = ?, password = ? WHERE emailaddress = ?');
+ $q->bindValue(1, (int)(bool)$isadmin, SQLITE3_INTEGER);
+ $q->bindValue(2, $password, SQLITE3_TEXT);
+ $q->bindValue(3, $username, SQLITE3_TEXT);
+ } else {
+ $q = $db->prepare('UPDATE users SET isadmin = ? WHERE emailaddress = ?');
+ $q->bindValue(1, (int)(bool)$isadmin, SQLITE3_INTEGER);
+ $q->bindValue(2, $username, SQLITE3_TEXT);
+ }
$ret = $q->execute();
$db->close();
@@ -96,6 +122,10 @@ function delete_user($id) {
return $ret;
}
+function valid_user($name) {
+ return ( bool ) preg_match( "/^[a-z0-9@_.-]+$/i" , $name );
+}
+
function jtable_respond($records, $method = 'multiple', $msg = 'Undefined errormessage') {
$jTableResult = array();
if ($method == 'error') {
@@ -119,13 +149,31 @@ function jtable_respond($records, $method = 'multiple', $msg = 'Undefined errorm
$jTableResult['RecordCount'] = count($records);
}
+ header('Content-Type: application/json');
print json_encode($jTableResult);
exit(0);
}
-function valid_user($name) {
- return ( bool ) preg_match( "/^[a-z0-9@_.-]+$/i" , $name );
+function user_template_list() {
+ global $templates;
+
+ $templatelist = array();
+ foreach ($templates as $template) {
+ if (is_adminuser()
+ or (isset($template['owner'])
+ and ($template['owner'] == get_sess_user() or $template['owner'] == 'public'))) {
+ array_push($templatelist, $template);
+ }
+ }
+ return $templatelist;
}
+function user_template_names() {
+ $templatenames = array('None' => 'None');
+ foreach (user_template_list() as $template) {
+ $templatenames[$template['name']] = $template['name'];
+ }
+ return $templatenames;
+}
?>
diff --git a/index.php b/index.php
index ea2f477..48f6257 100644
--- a/index.php
+++ b/index.php
@@ -76,19 +76,6 @@ if (!is_logged_in()) {
exit(0);
}
-foreach ($templates as $template) {
- if (is_adminuser() or (isset($template['owner']) && $template['owner'] == get_sess_user()) or ($template['owner'] == 'public')) {
- $templatelist[] = "'" . $template['name'] . "':'" . $template['name'] . "'";
- }
-}
-
-if (isset($templatelist)) {
- $tmpllist = ',';
- $tmpllist .= join(',', $templatelist);
-} else {
- $tmpllist = '';
-}
-
?>
@@ -157,7 +144,10 @@ $(document).ready(function () {
listAction: 'zones.php?action=listslaves',
if (is_adminuser() or $allowzoneadd === TRUE) { ?>
createAction: 'zones.php?action=create',
- deleteAction: 'zones.php?action=delete'
+ deleteAction: 'zones.php?action=delete',
+ } ?>
+ if (is_adminuser()) { ?>
+ updateAction: 'zones.php?action=update'
} ?>
},
fields: {
@@ -323,7 +313,7 @@ $(document).ready(function () {
},
template: {
title: 'Template',
- options: {'None': 'None' echo $tmpllist; ?>},
+ options: echo json_encode(user_template_names()); ?>,
list: false,
create: true,
edit: false
diff --git a/users.php b/users.php
index 2933c01..7dc7548 100644
--- a/users.php
+++ b/users.php
@@ -5,48 +5,96 @@ include_once('includes/session.inc.php');
include_once('includes/misc.inc.php');
if (!is_logged_in()) {
- header("Location: index.php");
+ header('Status: 403');
+ header('Location: ./index.php');
+ jtable_respond(null, 'error', "Authentication required");
}
if (!is_adminuser()) {
+ header('Status: 403');
jtable_respond(null, 'error', "You need adminprivileges to get here");
}
-if (isset($_GET['action'])) {
- $action = $_GET['action'];
-} else {
+if (!isset($_GET['action'])) {
+ header('Status: 400');
jtable_respond(null, 'error', 'No action given');
}
-if ($action == "list") {
+switch ($_GET['action']) {
+
+case "list":
$users = get_all_users();
jtable_respond($users);
-} elseif ($action == "listoptions") {
+ break;
+
+case "listoptions":
$users = get_all_users();
$retusers = array();
foreach ($users as $user) {
- $retusers[] = array (
+ $retusers[] = array(
'DisplayText' => $user['emailaddress'],
'Value' => $user['emailaddress']);
}
jtable_respond($retusers, 'options');
-} elseif ($action == "create" or $action == "update") {
- if (valid_user($_POST['emailaddress']) === FALSE) {
+ break;
+
+case "create":
+ $emailaddress = isset($_POST['emailaddress']) ? $_POST['emailaddress'] : '';
+ $isadmin = isset($_POST['isadmin']) ? $_POST['isadmin'] : '0';
+ $password = isset($_POST['password']) ? $_POST['password'] : '';
+
+ if (!valid_user($emailaddress)) {
jtable_respond(null, 'error', "Please only use ^[a-z0-9@_.-]+$ for usernames");
}
- $isadmin = $_POST['isadmin'] ? $_POST['isadmin'] : '0';
- if (add_user($_POST['emailaddress'], $isadmin, $_POST['password']) !== FALSE) {
- unset($_POST['password']);
- jtable_respond($_POST, 'single');
- } else {
- jtable_respond(null, 'error', 'Could not add/change this user');
+
+ if (!$password) {
+ jtable_respond(null, 'error', 'Cannot create user without password');
}
-} elseif ($action == "delete") {
+
+ if (user_exists($emailaddress)) {
+ jtable_respond(null, 'error', 'User already exists');
+ }
+
+ if (add_user($emailaddress, $isadmin, $password)) {
+ $result = array('emailaddress' => $emailaddress, 'isadmin' => $isadmin);
+ jtable_respond($result, 'single');
+ } else {
+ jtable_respond(null, 'error', 'Could not create user');
+ }
+ break;
+
+case "update":
+ $emailaddress = isset($_POST['emailaddress']) ? $_POST['emailaddress'] : '';
+ $isadmin = isset($_POST['isadmin']) ? $_POST['isadmin'] : '0';
+ $password = isset($_POST['password']) ? $_POST['password'] : '';
+
+ if (!valid_user($emailaddress)) {
+ jtable_respond(null, 'error', "Please only use ^[a-z0-9@_.-]+$ for usernames");
+ }
+
+ if (!user_exists($emailaddress)) {
+ jtable_respond(null, 'error', 'Cannot update not existing user');
+ }
+
+ if (update_user($emailaddress, $isadmin, $password)) {
+ $result = array('emailaddress' => $emailaddress, 'isadmin' => $isadmin);
+ jtable_respond($result, 'single');
+ } else {
+ jtable_respond(null, 'error', 'Could not update user');
+ }
+ break;
+
+case "delete":
if (delete_user($_POST['id']) !== FALSE) {
jtable_respond(null, 'delete');
} else {
- jtable_respond(null, 'error', 'Could not delete this user');
+ jtable_respond(null, 'error', 'Could not delete user');
}
+ break;
+
+default:
+ jtable_respond(null, 'error', 'Invalid action');
+ break;
}
?>
diff --git a/zones.php b/zones.php
index 9ef30d7..b2d48f6 100644
--- a/zones.php
+++ b/zones.php
@@ -1,105 +1,210 @@
= 300) {
+ jtable_respond(null, 'error', "API Error: $code");
}
+ return $json;
}
-function _valid_label($name) {
- return ( bool ) preg_match( "/^([-.a-z0-9_\/\*]+)?$/i" , $name );
+function zones_api_request($opts = null, $type = 'POST') {
+ global $apisid;
+
+ return api_request("/servers/${apisid}/zones", $opts, $type);
}
-function _create_record($name, $records, $input, $zoneurl) {
- global $defaults;
+function get_all_zones() {
+ return zones_api_request();
+}
- $content = ($input['type'] == "TXT") ? '"'.$input['content'].'"' : $input['content'];
+function _get_zone_by_key($key, $value) {
+ if ($value !== '') {
+ foreach (get_all_zones() as $zone) {
+ if ($zone[$key] === $value) {
+ $zone['owner'] = get_zone_owner($zone['name'], 'admin');
- if (_valid_label($input['name']) === FALSE) {
- jtable_respond(null, 'error', "Please only use [a-z0-9_/.-]");
- }
- if (is_ascii($content) === FALSE or is_ascii($input['name']) === FALSE) {
- jtable_respond(null, 'error', "Please only use ASCII-characters in your fields");
+ if (!check_owner($zone)) {
+ jtable_respond(null, 'error', 'Access denied');
+ }
+ return $zone;
+ }
+ }
}
+ header('Status: 404 Not found');
+ jtable_respond(null, 'error', "Zone not found");
+}
- if (preg_match('/^TXT$/', $input['type'])) {
- $content = stripslashes($input['content']);
- $content = preg_replace('/(^"|"$)/', '', $content);
- $content = addslashes($content);
- $content = '"'.$content.'"';
- }
+function get_zone_by_url($zoneurl) {
+ return _get_zone_by_key('url', $zoneurl);
+}
- array_push($records, array(
- 'disabled' => false,
- 'type' => $input['type'],
- 'name' => $name,
- 'ttl' => isset($input['ttl']) ? $input['ttl'] : $defaults['ttl'],
- 'priority' => $input['priority'] ? $input['priority'] : $defaults['priority'],
- 'content' => $content));
+function get_zone_by_id($zoneid) {
+ return _get_zone_by_key('id', $zoneid);
+}
- $patch = array();
- $patch['rrsets'] = array();
- array_push($patch['rrsets'], array(
- 'comments' => array(),
- 'records' => $records,
- 'changetype'=> "REPLACE",
- 'type' => $input['type'],
- 'name' => $name));
- _do_curl($zoneurl, $patch, 'patch');
-
- return $records;
+function get_zone_by_name($zonename) {
+ return _get_zone_by_key('name', $zonename);
}
/* This function is taken from:
http://pageconfig.com/post/how-to-validate-ascii-text-in-php and got fixed by
#powerdns */
-function is_ascii( $string = '' ) {
+function is_ascii($string) {
return ( bool ) ! preg_match( '/[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x80-\\xff]/' , $string );
}
-function getrecords_by_name_type($zoneurl, $name, $type) {
- $zone = json_decode(_do_curl($zoneurl), 1);
+function _valid_label($name) {
+ return is_ascii($name) && ( bool ) preg_match("/^([-.a-z0-9_\/\*]+)?$/i", $name );
+}
+
+function make_record($zone, $input) {
+ global $defaults;
+
+ $name = isset($input['name']) ? $input['name'] : '';
+
+ if ('' == $name) {
+ $name = $zone['name'];
+ } elseif (string_ends_with($name, '.')) {
+ # "absolute" name, shouldn't append zone[name] - but check.
+ $name = substr($name, 0, -1);
+ if (!string_ends_with($name, $zone['name'])) {
+ jtable_respond(null, 'error', "Name $name not in zone ".$zone['name']);
+ }
+ } else if (!string_ends_with($name, $zone['name'])) {
+ $name = $name . '.' . $zone['name'];
+ }
+
+ $ttl = (int) ((isset($input['ttl']) && $input['ttl']) ? $input['ttl'] : $defaults['ttl']);
+ $priority = (int) ((isset($input['priority']) && $input['priority']) ? $input['priority'] : $defaults['priority']);
+ $type = isset($input['type']) ? $input['type'] : '';
+ $disabled = (bool) (isset($input['disabled']) && $input['disabled']);
+
+ $content = isset($input['content']) ? $input['content'] : '';
+
+ if ($type === 'TXT') {
+ # empty TXT records are ok, otherwise require surrounding quotes: "..."
+ if (strlen($content) == 1 || substr($content, 0, 1) !== '"' || substr($content, -1) !== '"') {
+ # fix quoting: first escape all \, then all ", then surround with quotes.
+ $content = '"'.str_replace('"', '\\"', str_replace('\\', '\\\\', $content)).'"';
+ }
+ }
+
+ if (!_valid_label($name)) {
+ jtable_respond(null, 'error', "Please only use [a-z0-9_/.-]");
+ }
+ if (!$type) {
+ jtable_respond(null, 'error', "Require a type");
+ }
+ if (!is_ascii($content)) {
+ jtable_respond(null, 'error', "Please only use ASCII-characters in your fields");
+ }
+
+ return array(
+ 'disabled' => $disabled,
+ 'type' => $type,
+ 'name' => $name,
+ 'ttl' => $ttl,
+ 'priority' => $priority,
+ 'content' => $content);
+}
+
+function update_records($zone, $name_and_type, $inputs) {
+ # need one "record" to extract name and type, in case we have no inputs
+ # (deletion of all records)
+ $name_and_type = make_record($zone, $name_and_type);
+ $name = $name_and_type['name'];
+ $type = $name_and_type['type'];
+
+ $records = array();
+ foreach ($inputs as $input) {
+ $record = make_record($zone, $input);
+ if ($record['name'] !== $name || $record['type'] !== $type) {
+ jtable_respond(null, 'error', "Records not matching");
+ }
+
+ array_push($records, $record);
+ }
+
+ if (!_valid_label($name)) {
+ jtable_respond(null, 'error', "Please only use [a-z0-9_/.-]");
+ }
+
+ $patch = array(
+ 'rrsets' => array(array(
+ 'name' => $name,
+ 'type' => $type,
+ 'changetype' => count($records) ? 'REPLACE' : 'DELETE',
+ 'records' => $records)));
+
+ api_request($zone['url'], $patch, 'PATCH');
+}
+
+function create_record($zone, $input) {
+ $record = make_record($zone, $input);
+ $records = get_records_by_name_type($zone, $record['name'], $record['type']);
+ array_push($records, $record);
+
+ $patch = array(
+ 'rrsets' => array(array(
+ 'name' => $record['name'],
+ 'type' => $record['type'],
+ 'changetype' => 'REPLACE',
+ 'records' => $records)));
+
+ api_request($zone['url'], $patch, 'PATCH');
+
+ return $record;
+}
+
+function get_records_by_name_type($zone, $name, $type) {
+ $zone = api_request($zone['url']);
$records = array();
foreach ($zone['records'] as $record) {
- if ($record['name'] == $name and
- $record['type'] == $type) {
+ if ($record['name'] == $name and $record['type'] == $type) {
array_push($records, $record);
}
}
@@ -107,74 +212,157 @@ function getrecords_by_name_type($zoneurl, $name, $type) {
return $records;
}
-function zonesort($a, $b) {
- return strnatcmp($a["name"], $b["name"]);
+function decode_record_id($id) {
+ $record = json_decode($id, 1);
+ if (!$record
+ || !isset($record['name'])
+ || !isset($record['type'])
+ || !isset($record['ttl'])
+ || !isset($record['priority'])
+ || !isset($record['content'])
+ || !isset($record['disabled'])) {
+ jtable_respond(null, 'error', "Invalid record id");
+ }
+ return $record;
}
-function add_db_zone($zone, $owner) {
- if (valid_user($owner) === FALSE) {
- jtable_respond(null, 'error', "$owner is not a valid username");
- }
- if (_valid_label($zone) === FALSE) {
- jtable_respond(null, 'error', "$zone is not a valid zonename");
+# get all records with same name and type but different id (content)
+# requires records with id to be present
+# SOA records match always, regardless of content.
+function get_records_except($zone, $exclude) {
+ $is_soa = ($exclude['type'] == 'SOA');
+
+ $found = false;
+ $zone = api_request($zone['url']);
+ $records = array();
+ foreach ($zone['records'] as $record) {
+ if ($record['name'] == $exclude['name'] and $record['type'] == $exclude['type']) {
+ if ($is_soa) {
+ # SOA changes all the time (serial); we can't match it in a sane way.
+ # OTOH we know it is unique anyway - just pretend we found a match.
+ $found = true;
+ } elseif ($record['content'] != $exclude['content']
+ or $record['ttl'] != $exclude['ttl']
+ or $record['priority'] != $exclude['priority']
+ or $record['disabled'] != $exclude['disabled']) {
+ array_push($records, $record);
+ } else {
+ $found = true;
+ }
+ }
}
- if (is_apiuser()) {
- if (!get_user_info($owner)) {
- add_user($owner);
+ if (!$found) {
+ header("Status: 404 Not Found");
+ jtable_respond(null, 'error', "Didn't find record with id");
+ }
+
+ return $records;
+}
+
+function compareName($a, $b) {
+ $a = array_reverse(explode('.', $a));
+ $b = array_reverse(explode('.', $b));
+ for ($i = 0; ; ++$i) {
+ if (!isset($a[$i])) {
+ return isset($b[$i]) ? -1 : 0;
+ } else if (!isset($b[$i])) {
+ return 1;
}
+ $cmp = strnatcasecmp($a[$i], $b[$i]);
+ if ($cmp) {
+ return $cmp;
+ }
+ }
+}
+
+function zone_compare($a, $b) {
+ if ($cmp = compareName($a['name'], $b['name'])) return $cmp;
+ return 0;
+}
+
+function rrtype_compare($a, $b) {
+ # sort specials before everything else
+ $specials = array('SOA', 'NS', 'MX');
+ $spa = array_search($a, $specials, true);
+ $spb = array_search($b, $specials, true);
+ if ($spa === false) {
+ return ($spb === false) ? strcmp($a, $b) : 1;
+ } else {
+ return ($spb === false) ? -1 : $spa - $spb;
+ }
+}
+
+function record_compare($a, $b) {
+ if ($cmp = compareName($a['name'], $b['name'])) return $cmp;
+ if ($cmp = rrtype_compare($a['type'], $b['type'])) return $cmp;
+ if ($cmp = ($a['priority'] - $b['priority'])) return $cmp;
+ if ($cmp = strnatcasecmp($a['content'], $b['content'])) return $cmp;
+ return 0;
+}
+
+function add_db_zone($zonename, $ownername) {
+ if (valid_user($ownername) === false) {
+ jtable_respond(null, 'error', "$ownername is not a valid username");
+ }
+ if (!_valid_label($zonename)) {
+ jtable_respond(null, 'error', "$zonename is not a valid zonename");
+ }
+
+ if (is_apiuser() && !user_exists($ownername)) {
+ add_user($ownername);
}
$db = get_db();
$q = $db->prepare("INSERT OR REPLACE INTO zones (zone, owner) VALUES (?, (SELECT id FROM users WHERE emailaddress = ?))");
- $q->bindValue(1, $zone, SQLITE3_TEXT);
- $q->bindValue(2, $owner, SQLITE3_TEXT);
+ $q->bindValue(1, $zonename, SQLITE3_TEXT);
+ $q->bindValue(2, $ownername, SQLITE3_TEXT);
$q->execute();
$db->close();
}
-function delete_db_zone($owner) {
- if (_valid_label($zone) === FALSE) {
- jtable_respond(null, 'error', "$zone is not a valid zonename");
+function delete_db_zone($zonename) {
+ if (!_valid_label($zonename)) {
+ jtable_respond(null, 'error', "$zonename is not a valid zonename");
}
$db = get_db();
$q = $db->prepare("DELETE FROM zones WHERE zone = ?");
- $q->bindValue(1, $zone, SQLITE3_TEXT);
+ $q->bindValue(1, $zonename, SQLITE3_TEXT);
$q->execute();
$db->close();
}
-function get_zone_owner($zone) {
- if (_valid_label($zone) === FALSE) {
- jtable_respond(null, 'error', "$zone is not a valid zonename");
+function get_zone_owner($zonename, $default) {
+ if (!_valid_label($zonename)) {
+ jtable_respond(null, 'error', "$zonename is not a valid zonename");
}
$db = get_db();
$q = $db->prepare("SELECT u.emailaddress FROM users u, zones z WHERE z.owner = u.id AND z.zone = ?");
- $q->bindValue(1, $zone, SQLITE3_TEXT);
+ $q->bindValue(1, $zonename, SQLITE3_TEXT);
$result = $q->execute();
$zoneinfo = $result->fetchArray(SQLITE3_ASSOC);
$db->close();
- if (isset($zoneinfo['emailaddress']) && $zoneinfo['emailaddress'] != NULL ) {
+ if (isset($zoneinfo['emailaddress']) && $zoneinfo['emailaddress'] != null ) {
return $zoneinfo['emailaddress'];
}
-
- return 'admin';
+
+ return $default;
}
function get_zone_keys($zone) {
$ret = array();
- foreach (json_decode(_do_curl("/servers/:serverid:/zones/".$zone."/cryptokeys"), 1) as $key) {
+ foreach (api_request($zone['url'] . "/cryptokeys") as $key) {
if (!isset($key['active']))
continue;
- $key['dstxt'] = $zone.' IN DNSKEY '.$key['dnskey']."\n\n";
+ $key['dstxt'] = $zone['name'] . ' IN DNSKEY '.$key['dnskey']."\n\n";
if (isset($key['ds'])) {
foreach ($key['ds'] as $ds) {
- $key['dstxt'] .= $zone.' IN DS '.$ds."\n";
+ $key['dstxt'] .= $zone['name'] . ' IN DS '.$ds."\n";
}
+ unset($key['ds']);
}
- unset($key['ds']);
$ret[] = $key;
}
@@ -182,15 +370,7 @@ function get_zone_keys($zone) {
}
function check_owner($zone) {
- if (is_adminuser() === TRUE) {
- return TRUE ;
- }
-
- if (get_zone_owner($zone) == get_sess_user()) {
- return TRUE;
- }
-
- return FALSE;
+ return is_adminuser() or ($zone['owner'] === get_sess_user());
}
if (isset($_GET['action'])) {
@@ -199,185 +379,231 @@ if (isset($_GET['action'])) {
jtable_respond(null, 'error', 'No action given');
}
-if ($action == "list" or $action== "listslaves") {
- $rows = json_decode(_do_curl('servers/:serverid:/zones'), 1);
+switch ($action) {
+
+case "list":
+case "listslaves":
$return = array();
- foreach ($rows as $zone) {
- if (check_owner($zone['name']) === FALSE)
+ $q = isset($_POST['domsearch']) ? $_POST['domsearch'] : false;
+ foreach (get_all_zones() as $zone) {
+ $zone['owner'] = get_zone_owner($zone['name'], 'admin');
+ if (!check_owner($zone))
continue;
- if (isset($_POST['domsearch'])) {
- $q = $_POST['domsearch'];
- if (!preg_match("/$q/", $zone['name']) == 1) {
- continue;
- }
+ if ($q && !preg_match("/$q/", $zone['name'])) {
+ continue;
}
- $zone['name'] = htmlspecialchars($zone['name']);
- $zone['owner'] = get_zone_owner($zone['name']);
if ($action == "listslaves" and $zone['kind'] == "Slave") {
array_push($return, $zone);
} elseif ($action == "list" and $zone['kind'] != "Slave") {
- if ($zone['dnssec'] == true) {
- $zone['keyinfo'] = get_zone_keys($zone['name']);
+ if ($zone['dnssec']) {
+ $zone['keyinfo'] = get_zone_keys($zone);
}
array_push($return, $zone);
}
}
- usort($return, "zonesort");
+ usort($return, "zone_compare");
jtable_respond($return);
-} elseif ($action == "create") {
- if (is_adminuser() !== TRUE and $allowzoneadd !== TRUE) {
+ break;
+
+case "create":
+ $zonename = isset($_POST['name']) ? $_POST['name'] : '';
+ $zonekind = isset($_POST['kind']) ? $_POST['kind'] : '';
+
+ if (!is_adminuser() and $allowzoneadd !== true) {
jtable_respond(null, 'error', "You are not allowed to add zones");
}
- if (_valid_label($_POST['name']) === FALSE) {
+ if (!_valid_label($zonename)) {
jtable_respond(null, 'error', "Please only use [a-z0-9_/.-]");
}
- if (is_ascii($_POST['name']) === FALSE) {
- jtable_respond(null, 'error', "Please only use ASCII-characters in your domainname");
+
+ if (!$zonename || !$zonekind) {
+ jtable_respond(null, 'error', "Not enough data");
}
- if ($_POST['kind'] != null and $_POST['name'] != null) {
- $nameservers = array();
- if ($_POST['kind'] != "Slave") {
- if (isset($_POST['nameserver1']) && $_POST['nameserver1'] != null) {
- array_push($nameservers, $_POST['nameserver1']);
- if (isset($_POST['nameserver2']) && $_POST['nameserver2'] != null) {
- array_push($nameservers, $_POST['nameserver2']);
- }
- } else {
- jtable_respond(null, 'error', "Not enough data: ".print_r($_POST, 1));
- }
- if (isset($defaults['soa_edit_api'])) {
- $vars['soa_edit_api'] = $defaults['soa_edit_api'];
- }
- }
- $vars['name'] = $_POST['name'];
- $vars['kind'] = $_POST['kind'];
- if (isset($_POST['zone'])) {
- $vars['zone'] = $_POST['zone'];
- $vars['nameservers'] = array();
- } else {
- $vars['nameservers'] = $nameservers;
- }
- _do_curl('/servers/:serverid:/zones', $vars);
- if (isset($_POST['owner']) and $_POST['owner'] != 'admin') {
- add_db_zone($vars['name'], $_POST['owner']);
- } else {
- add_db_zone($vars['name'], get_sess_user());
- }
- if (isset($_POST['template']) && $_POST['template'] != 'None') {
- foreach ($templates as $template) {
- if ($template['name'] == $_POST['template']) {
- $zoneurl = '/servers/:serverid:/zones/'.$vars['name'].'.';
- foreach ($template['records'] as $record) {
- if ($record['label'] != "") {
- $name = join('.', array($record['label'], $vars['name']));
- } else {
- $name = $vars['name'];
- }
- if (!isset($record['name'])) {
- $record['name'] = "";
- }
- $records = getrecords_by_name_type($zoneurl, $name, $record['type']);
- $records = _create_record($name, $records, $record, $zoneurl);
- }
- }
- }
- }
-
+
+ $createOptions = array(
+ 'name' => $zonename,
+ 'kind' => $zonekind,
+ );
+
+ $nameservers = array();
+ if (isset($_POST['nameserver1']) && $_POST['nameserver1'] != null) {
+ array_push($nameservers, $_POST['nameserver1']);
+ }
+ if (isset($_POST['nameserver2']) && $_POST['nameserver2'] != null) {
+ array_push($nameservers, $_POST['nameserver2']);
+ }
+
+ if ($zonekind != "Slave") {
if (!isset($_POST['zone'])) {
- $vars = $_POST;
- $vars['serial'] = 0;
- $vars['records'] = array();
- jtable_respond($vars, 'single');
- }
- $zoneurl = '/servers/:serverid:/zones/'.$vars['name'].'.';
- if (isset($_POST['owns'])) {
- $patch = array();
- $patch['rrsets'] = array();
- array_push($patch['rrsets'], array(
- 'comments' => array(),
- 'records' => array(),
- 'changetype'=> "REPLACE",
- 'type' => 'NS',
- 'name' => $vars['name']));
- _do_curl($zoneurl, $patch, 'patch');
- foreach ($nameservers as $ns) {
- $records = getrecords_by_name_type($zoneurl, $vars['name'], 'NS');
- $records = _create_record($vars['name'], $records, array('type' => 'NS', 'name' => '', 'content' => $ns), $zoneurl);
+ if (0 == count($nameservers)) {
+ jtable_respond(null, 'error', "Require nameservers");
}
- }
-
- $vars = _do_curl($zoneurl);
- jtable_respond($vars, 'single');
- } else {
- jtable_respond(null, 'error', "Not enough data: ".print_r($_POST, 1));
- }
-} elseif ($action == "listrecords" && $_GET['zoneurl'] != null) {
- $rows = json_decode(_do_curl($_GET['zoneurl']), 1);
- $soa = array();
- $ns = array();
- $mx = array();
- $any = array();
- foreach ($rows['records'] as $idx => $record) {
- $rows['records'][$idx]['id'] = json_encode($record);
- $rows['records'][$idx]['name'] = htmlspecialchars($record['name']);
- if ($record['type'] == 'SOA') { array_push($soa, $rows['records'][$idx]); }
- elseif ($record['type'] == 'NS') { array_push($ns, $rows['records'][$idx]); }
- elseif ($record['type'] == 'MX') { array_push($mx, $rows['records'][$idx]); }
- else {
- array_push($any, $rows['records'][$idx]);
- };
- }
- usort($any, "zonesort");
- $ret = array_merge($soa, $ns, $mx, $any);
- jtable_respond($ret);
-} elseif ($action == "delete") {
- _do_curl("/servers/:serverid:/zones/".$_POST['id'], array(), 'delete');
- $zone = preg_replace("/\.$/", "", $_POST['id']);
- delete_db_zone($zone);
- jtable_respond(null, 'delete');
-} elseif ($action == "createrecord" or $action == "editrecord") {
- $name = (!preg_match("/\.".$_POST['domain']."\.?$/", $_POST['name'])) ? $_POST['name'].'.'.$_POST['domain'] : $_POST['name'];
- $name = preg_replace("/\.$/", "", $name);
- $name = preg_replace("/^\./", "", $name);
- $records = array();
- if ($action == "createrecord") {
- $records = getrecords_by_name_type($_GET['zoneurl'], $name, $_POST['type']);
- }
-
- $records =_create_record($name, $records, $_POST, $_GET['zoneurl']);
- jtable_respond($records[sizeof($records)-1], 'single');
-} elseif ($action == "deleterecord") {
- $todel = json_decode($_POST['id'], 1);
- $records = getrecords_by_name_type($_GET['zoneurl'], $todel['name'], $todel['type']);
- $precords = array();
-
- foreach ($records as $record) {
- if (
- $record['content'] == $todel['content'] and
- $record['type'] == $todel['type'] and
- $record['prio'] == $todel['prio'] and
- $record['name'] == $todel['name']) {
- continue;
+ $createOptions['nameservers'] = $nameservers;
} else {
- array_push($precords, $record);
+ $createOptions['zone'] = $_POST['zone'];
+ }
+ if (isset($defaults['soa_edit_api'])) {
+ $createOptions['soa_edit_api'] = $defaults['soa_edit_api'];
+ }
+ if (isset($defaults['soa_edit'])) {
+ $createOptions['soa_edit'] = $defaults['soa_edit'];
+ }
+ } else { // Slave
+ if (isset($_POST['masters'])) {
+ $createOptions['masters'] = preg_split('/[,;\s]+/', $_POST['masters'], null, PREG_SPLIT_NO_EMPTY);
+ }
+ if (0 == count($createOptions['masters'])) {
+ jtable_respond(null, 'error', "Slave requires master servers");
}
}
- $patch = array();
- $patch['rrsets'] = array();
- array_push($patch['rrsets'], array(
- 'comments' => array(),
- 'records' => $precords,
- 'changetype'=> "REPLACE",
- 'type' => $todel['type'],
- 'name' => $todel['name']));
- _do_curl($_GET['zoneurl'], $patch, 'patch');
+
+ // only admin user and original owner can "recreate" zones that are already
+ // present in our own db but got lost in pdns.
+ if (!is_adminuser() && get_sess_user() !== get_zone_owner($zonename, get_sess_user())) {
+ jtable_respond(null, 'error', 'Zone already owned by someone else');
+ }
+
+ $zone = zones_api_request($createOptions);
+ $zonename = $zone['name'];
+
+ if (is_adminuser() && isset($_POST['owner'])) {
+ add_db_zone($zonename, $_POST['owner']);
+ } else {
+ add_db_zone($zonename, get_sess_user());
+ }
+
+ if (isset($_POST['template']) && $_POST['template'] != 'None') {
+ foreach (user_template_list() as $template) {
+ if ($template['name'] !== $_POST['template']) continue;
+
+ foreach ($template['records'] as $record) {
+ if (isset($record['label'])) {
+ $record['name'] = $record['label'];
+ unset($record['label']);
+ }
+ create_record($zone, $record);
+ }
+ break;
+ }
+ }
+
+ if (isset($_POST['zone']) && isset($_POST['owns']) && $_POST['owns'] && count($nameservers)) {
+ $records = array();
+ foreach ($nameservers as $ns) {
+ array_push($records, array('type' => 'NS', 'content' => $ns));
+ }
+ update_records($zone, $records[0], $records);
+ }
+
+ unset($zone['records']);
+ unset($zone['comments']);
+ jtable_respond($zone, 'single');
+ break;
+
+case "update":
+ $zone = get_zone_by_id(isset($_POST['id']) ? $_POST['id'] : '');
+
+ $zoneowner = isset($_POST['owner']) ? $_POST['owner'] : $zone['owner'];
+
+ if ($zone['owner'] !== $zoneowner) {
+ if (!is_adminuser()) {
+ header("Status: 403 Access denied");
+ jtable_respond(null, 'error', "Can't change owner");
+ } else {
+ add_db_zone($zone['name'], $zoneowner);
+ $zone['owner'] = $zoneowner;
+ }
+ }
+
+ $update = false;
+
+ if (isset($_POST['masters'])) {
+ $zone['masters'] = preg_split('/[,;\s]+/', $_POST['masters'], null, PREG_SPLIT_NO_EMPTY);
+ $update = true;
+ }
+
+ if ($update) {
+ $zoneUpdate = $zone;
+ unset($zoneUpdate['id']);
+ unset($zoneUpdate['url']);
+ unset($zoneUpdate['owner']);
+ $newZone = api_request($zone['url'], $zoneUpdate, 'PUT');
+ $newZone['owner'] = $zone['owner'];
+ } else {
+ $newZone = $zone;
+ }
+ unset($newZone['records']);
+ unset($newZone['comments']);
+
+ jtable_respond($newZone, 'single');
+ break;
+
+case "delete":
+ $zone = get_zone_by_id(isset($_POST['id']) ? $_POST['id'] : '');
+
+ api_request($zone['url'], array(), 'DELETE');
+ delete_db_zone($zone['name']);
jtable_respond(null, 'delete');
-} elseif ($action == "update") {
- add_db_zone($_POST['name'], $_POST['owner']);
- jtable_respond($_POST, 'single');
-} else {
+ break;
+
+case "listrecords":
+ $zone = get_zone_by_url(isset($_GET['zoneurl']) ? $_GET['zoneurl'] : '');
+
+ $records = api_request($zone['url'])['records'];
+ foreach ($records as &$record) {
+ $record['id'] = json_encode($record);
+ }
+ unset($record);
+ usort($records, "record_compare");
+ jtable_respond($records);
+ break;
+
+case "createrecord":
+ $zone = get_zone_by_url(isset($_GET['zoneurl']) ? $_GET['zoneurl'] : '');
+ $record = create_record($zone, $_POST);
+
+ $record['id'] = json_encode($record);
+ jtable_respond($record, 'single');
+ break;
+
+case "editrecord":
+ $zone = get_zone_by_url(isset($_GET['zoneurl']) ? $_GET['zoneurl'] : '');
+ $old_record = decode_record_id(isset($_POST['id']) ? $_POST['id'] : '');
+
+ $records = get_records_except($zone, $old_record);
+
+ $record = make_record($zone, $_POST);
+
+ if ($record['name'] !== $old_record['name']) {
+ # rename:
+ $newRecords = get_records_by_name_type($zone, $record['name'], $record['type']);
+ array_push($newRecords, $record);
+ update_records($zone, $old_record, $records); # remove from old list
+ update_records($zone, $record, $newRecords); # add to new list
+ } else {
+ array_push($records, $record);
+ update_records($zone, $record, $records);
+ }
+
+ $record['id'] = json_encode($record);
+ jtable_respond($record, 'single');
+ break;
+
+case "deleterecord":
+ $zone = get_zone_by_url(isset($_GET['zoneurl']) ? $_GET['zoneurl'] : '');
+ $old_record = decode_record_id(isset($_POST['id']) ? $_POST['id'] : '');
+
+ $records = get_records_except($zone, $old_record);
+
+ update_records($zone, $old_record, $records);
+ jtable_respond(null, 'delete');
+ break;
+
+default:
jtable_respond(null, 'error', 'No such action');
+ break;
}
+
?>