[ 'friendlyName' => 'Username', 'validators' => [ 'required' => 'required', ] ], 'password' => [ 'friendlyName' => 'User Password', 'type' => 'password', 'validators' => [ 'required' => 'required', ] ], 'hostname' => [ 'friendlyName' => 'Hostname/IP', 'validators' => [ 'required' => 'required', ] ], 'database' => [ 'friendlyName' => 'Database', 'validators' => [ 'required' => 'required', ] ], 'ssh_host' => [ 'friendlyName' => 'SSH Host', 'type' => 'text', ], 'ssh_username' => [ 'friendlyName' => 'SSH User', 'type' => 'text', ], 'ssh_password' => [ 'friendlyName' => 'SSH Password', 'type' => 'password', ], 'ssh_port' => [ 'friendlyName' => 'SSH Port', 'type' => 'text' ], 'ssh_public_key' => [ 'friendlyName' => 'SSH Public Key', 'type' => 'text', ], 'ssh_private_key' => [ 'friendlyName' => 'SSH Private Key', 'type' => 'text', ], 'pdnsutil_path' => [ 'friendlyName' => 'Path To PDNSUTIL', 'type' => 'text', ], 'default_ip' => [ 'friendlyName' => 'Default IP', 'validators' => [ 'required' => 'required', 'pattern' => Patterns::IP4_OR_IP6, ] ], 'soa_name' => [ 'friendlyName' => 'Name', 'help' => '{$domain} will be repleaced with name of the domain', ], 'domain_type' => [ 'friendlyName' => 'Domain Type', 'type' => 'select', 'options' => ['MASTER' => 'MASTER', 'SLAVE' => 'SLAVE', 'NATIVE' => 'NATIVE'], ], 'soa_primary' => [ 'friendlyName' => 'Primary', ], 'soa_hostmaster' => [ 'friendlyName' => 'Hostmaster', ], 'soa_refresh' => [ 'friendlyName' => 'Refresh', ], 'soa_retry' => [ 'friendlyName' => 'Retry', ], 'soa_expire' => [ 'friendlyName' => 'Expire', ], 'soa_default_ttl' => [ 'friendlyName' => 'Default TTL', 'type' => 'number', ], 'soa_ttl' => [ 'friendlyName' => 'TTL', 'type' => 'number', 'value' => '555', ], 'soa_edit' => [ 'friendlyName' => 'Allow To Edit SOA Records', 'type' => 'yesno' ], 'disable_validation' => [ 'friendlyName' => 'Disable Records Validation', 'type' => 'yesno' ], 'disable_doubled' => [ 'friendlyName' => 'Double Records', 'type' => 'yesno', 'help' => 'Select to enable creating double records' ], 'zone_account' => [ 'friendlyName' => 'Zone Account', ], 'update_notified_serial' => [ 'friendlyName' => 'Update Notified Serial', 'type' => 'yesno' ], ]; private $error; private $ssh = null; private $remote_db = null; /** available records types * */ public $availableTypes = ['A', 'AAAA', 'NS', 'MX', 'CNAME', 'DNAME', 'TXT', 'SPF', 'SRV', 'PTR', 'SOA', 'CAA', 'AFSDB', 'ALIAS', 'DNSKEY', 'DS', 'HINFO', 'LOC', 'MINFO', 'MR', 'NAPTR', 'RP', 'RRSIG', 'WKS', 'URI', 'URL']; //LOC public $typesCantBeDoubled = ['A', 'AAAA', 'SPF', 'SRV', 'PTR', 'SOA']; public function __destruct() { $this->disconnect(); } protected function validateIP() { if(empty($this->ip)) return false; return (ip2long($this->ip) !== false); } private function mysql_safequery($query, $params = [] ) { try { $this->connect(); $qsql = main\mgLibs\MySQL\query::debugQuery($query, $params); $sql_query = main\mgLibs\MySQL\query::query($query, $params, 'pdns'); $this->disconnect(); $this->log('SQL: ' . $qsql); } catch( Exception $ex) { $this->disconnect(); throw new exceptions\DNSSubmoduleException($ex, dns\SubmoduleExceptionCodes::COMMAND_ERROR); } return ($sql_query); } private function connect() { try { main\mgLibs\MySQL\query::createInstance([ 'host' => $this->config['hostname'], 'user' => $this->config['username'], 'pass' => $this->config['password'], 'name' => $this->config['database'] ]); } catch( Exception $ex) { throw new exceptions\DNSSubmoduleException('Unable to connect to the Power DNS database', dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM); } return true; } private function disconnect() { main\mgLibs\MySQL\query::dropInstance('pdns'); } public function testConnection() { $this->log('TEST CONNECTION'); $this->connect(); $this->mysql_safequery('SELECT * FROM domains '); $this->disconnect(); } private function updateSoaSerial() { if (in_array($this->config['domain_type'], ['MASTER','NATIVE']) || preg_match('/in-addr.arpa$/', $this->domain)) { $q = $this->mysql_safequery("SELECT r.id as record_id, r.content FROM records r JOIN domains d ON(r.domain_id = d.id) WHERE r.type = 'SOA' AND d.name = ?", [$this->domain]); $row = $q->fetch(); $record_id = $row['record_id']; $content = explode(' ', $row['content']); if ($content[2]) { $inc = (int) substr($content[2], 8, 2); $current_date = date('Ymd'); $serial_current_date = substr($content[2], 0, 8); $date = $serial_current_date === $current_date ? substr($content[2], 0, 8) : $current_date; if ($serial_current_date !== $current_date) { $serial = $current_date . '00'; } elseif ($inc === 99) { $serial = date('Ymd', strtotime($date) + (60 * 60 * 24)) . '00'; } else { $inc++; $serial = $date . ($inc <= 9 ? '0' . $inc : $inc); } } if (!empty($serial)) { $content[2] = $serial; $this->mysql_safequery('UPDATE records SET content = ? WHERE id = ?', [implode(' ', $content), $record_id]); } } } private function updateNotifiedSerial() { if ($this->config['update_notified_serial'] === 'on') { $q = $this->mysql_safequery('SELECT notified_serial FROM domains WHERE name = ?', [$this->domain]); $row = $q->fetch(); if ($row !== false) { $notified_serial = $row['notified_serial']; if (empty($notified_serial)) { $current_date = date('Ymd'); $serial = $current_date . '01'; } else { $inc = (int) substr($notified_serial, 8, 2); $current_date = date('Ymd'); $serial_current_date = substr($notified_serial, 0, 8); $date = $serial_current_date == $current_date ? substr($notified_serial, 0, 8) : $current_date; if ($serial_current_date !== $current_date) { $serial = $current_date . '01'; } elseif ($inc === 99) { $serial = date('Ymd', strtotime($date) + (60 * 60 * 24)) . '01'; } else { $inc += 2; $serial = $date . ($inc <= 9 ? '0' . $inc : $inc); } } $this->mysql_safequery('UPDATE domains SET notified_serial = ? WHERE name = ?', [$serial, $this->domain]); } } } public function zoneExists() { $this->log('CHECK ZONE EXIST'); $this->domain = main\mgLibs\custom\helpers\IdnaHelper::idnaEncode($this->domain); if($this->connect()) { $q = $this->mysql_safequery('SELECT name, account FROM domains WHERE name = ?', [$this->domain]); $row = $q->fetch(); $this->disconnect(); return strtolower($row['name']) === strtolower($this->domain); } return false; } public function activateZone() { $this->log('ACTIVATE ZONE'); $this->domain = main\mgLibs\custom\helpers\IdnaHelper::idnaEncode($this->domain); if ($this->zoneExists()) { throw new exceptions\DNSSubmoduleException('Domain name already exists!', dns\SubmoduleExceptionCodes::COMMAND_ERROR); } if (empty($this->domain)) { throw new exceptions\DNSSubmoduleException('Domain name is not valid!', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS); } if (preg_match('/in-addr.arpa$/', $this->domain)) { $this->config['domain_type'] = 'MASTER'; } if ($this->config['domain_type'] === 'SLAVE') { $ip = $this->ip == '' ? $this->config['default_ip'] : $this->ip; if (!filter_var($ip, FILTER_VALIDATE_IP)) { throw new exceptions\DNSSubmoduleException('This is not a valid IPv4 or IPv6 address.', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS); } } if ($this->config['update_notified_serial'] === 'on' ) { $current_date = date('Ymd'); $serial = $current_date . '00'; } else { $serial = 0; } if ($this->connect()) { if (in_array($this->config['domain_type'], ['MASTER','NATIVE']) || preg_match('/in-addr.arpa$/', $this->domain) || preg_match('/ip6.arpa$/', $this->domain)) { if (!empty($this->config['zone_account'])) { $args = [$this->domain, $serial, $this->config['zone_account']]; $query = "INSERT INTO domains (name, type, notified_serial, account) VALUES( ?, '" . $this->config['domain_type'] . "', ?, ? )"; } else { $args = [$this->domain, $serial]; $query = "INSERT INTO domains (name, type, notified_serial) VALUES( ?, '" . $this->config['domain_type'] . "', ? )"; } } elseif(!empty($this->config['zone_account'])) { $args = [$this->domain, $ip, $serial, $this->config['zone_account']]; $query = "INSERT INTO domains (name, type, master, notified_serial, account) VALUES( ?, 'SLAVE', ?, ?, ? )"; } else { $args = [$this->domain, $ip, $serial]; $query = "INSERT INTO domains (name, type, master, notified_serial) VALUES( ?, 'SLAVE', ?, ?)"; } if ($this->mysql_safequery($query, $args)) { if ($this->config['domain_type'] === 'MASTER' || $this->config['domain_type'] === 'NATIVE' || preg_match('/in-addr.arpa$/', $this->domain) || preg_match('/ip6.arpa$/', $this->domain)) { $to_search = ['{$domain}']; $to_replace = [$this->domain]; $name = preg_match('/in-addr.arpa$/', $this->domain) || preg_match('/ip6.arpa$/', $this->domain) ? $this->domain : str_replace($to_search, $to_replace, $this->config['soa_name']); if (preg_match('/in-addr.arpa$/', $this->domain) || preg_match('/ip6.arpa$/', $this->domain)) { $content = $this->domain . ' ' . 'hostmaster.' . $this->domain . ' ' . date('Ymd') . '00' . ' ' . $this->config['soa_refresh'] . ' ' . $this->config['soa_retry'] . ' ' . $this->config['soa_expire'] . ' ' . $this->config['soa_default_ttl']; } else { $content = str_replace($to_search, $to_replace, $this->config['soa_primary']) . ' ' . str_replace($to_search, $to_replace, $this->config['soa_hostmaster']) . ' ' . date('Ymd') . '00' . ' ' . $this->config['soa_refresh'] . ' ' . $this->config['soa_retry'] . ' ' . $this->config['soa_expire'] . ' ' . $this->config['soa_default_ttl']; } $input = [ $this->domain, $name, 'SOA', $this->config['soa_ttl'], time(), $content ]; $this->mysql_safequery('INSERT INTO records (domain_id, name, type, ttl, prio, change_date, content) VALUES((SELECT id FROM domains WHERE name = ?), ?, ?, ?, 0, ?, ?)', $input); } $this->disconnect(); return true; } else { $this->disconnect(); throw new exceptions\DNSSubmoduleException($this->remote_db->error, dns\SubmoduleExceptionCodes::COMMAND_ERROR); } } return false; } public function terminateZone() { $this->domain = main\mgLibs\custom\helpers\IdnaHelper::idnaEncode($this->domain); if (!$this->zoneExists()) { throw new exceptions\DNSSubmoduleException('Domain name not exists!', dns\SubmoduleExceptionCodes::COMMAND_ERROR); } $this->log('TERMINATE ZONE'); if (empty($this->domain)) { throw new exceptions\DNSSubmoduleException('Domain name is not valid!', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS); } if ($this->connect()) { $q = $this->mysql_safequery('SELECT id FROM domains WHERE name = ?', [$this->domain]); $row = $q->fetch(); if($this->mysql_safequery('DELETE FROM domains WHERE name = ?', [$this->domain])) { $this->mysql_safequery('DELETE FROM records WHERE domain_id = ?', [$row['id']]); $this->disconnect(); return true; } else { $this->disconnect(); throw new exceptions\DNSSubmoduleException($this->remote_db->error, dns\SubmoduleExceptionCodes::COMMAND_ERROR); } } return false; } public function getRecords($recordType = false) { $this->log('GET RECORDS'); $out = []; $this->domain = main\mgLibs\custom\helpers\IdnaHelper::idnaEncode($this->domain); if ($this->connect()) { $q = $this->mysql_safequery('SELECT r.id as record_id, r.name, r.type, r.ttl, r.prio, r.content FROM records r JOIN domains d ON(r.domain_id = d.id) WHERE d.name = ?', [$this->domain]); while ($row = $q->fetch()) { if ( !$recordType && $this->config['soa_edit'] !== 'on' && $row['type'] === 'SOA') { continue; } if ( in_array($row['type'], $recordType ? [strtoupper($recordType)] : $this->getAvailableRecordTypes(), true) ) { $record = new dns\record\Record(); $record->line = $row['record_id']; $record->name = rtrim($row['name'],'.') .'.'; $record->type = $row['type']; $record->ttl = $row['ttl']; $record->createRDATAObject(); if($record['type'] !=='TXT') { $row['content'] = main\mgLibs\custom\helpers\IdnaHelper::idnaDecode($row['content']); } switch ($row['type']) { case 'SRV': $content = preg_split('/\s+/', $row['content']); $record->rdata->priority = $row['prio']; $record->rdata->weight = $content[0]; $record->rdata->port = $content[1]; $record->rdata->target = $content[2]; break; case 'MX': case 'URI': $record->rdata->fromString($row['prio'] . ' ' . $row['content']); break; case 'DNSKEY': $exploded = preg_split('/\s+/', $row['content']); $newRecord['protocol'] = $exploded[0]; $newRecord['flags'] = $exploded[1]; $newRecord['algorithm'] = $exploded[2]; unset($exploded[0], $exploded[1], $exploded[2]); $newRecord['publickey'] = implode(' ', $exploded); $record->rdata->setDataFromArray($newRecord); break; default: $record->rdata->fromString($row['content']); break; } $out[] = $record; } } $this->disconnect(); } return $out; } public function addRecord(dns\record\Record $record) { $this->domain = main\mgLibs\custom\helpers\IdnaHelper::idnaEncode($this->domain); if ( $record->type !== 'PTR' && (preg_match('/in-addr.arpa$/', $this->domain) || preg_match('/ip6.arpa$/', $this->domain)) ) { throw new exceptions\DNSSubmoduleException('This record type (' . $record->type . ') is not allowe for rDNS zone', dns\SubmoduleExceptionCodes::COMMAND_ERROR); } switch ($record->type) { case 'MX': $prio = $record->rdata->preference; $value = $record->rdata->exchange; break; case 'SRV': $prio = $record->rdata->priority; $value = $record->rdata->toString(['weight', 'port', 'target']); break; case 'URI': $prio = $record->rdata->priority; $value = $record->rdata->toString(['weight', 'target']); break; case 'CNAME': $prio = 0; $value = rtrim($record->rdata->toString(), '.'); break; default: $prio = 0; $value = $record->rdata->toString(); break; } $value = str_replace("\t", ' ', $value); $value = main\mgLibs\custom\helpers\IdnaHelper::idnaEncode($value); $input = [ strtolower($this->domain), main\mgLibs\custom\helpers\IdnaHelper::idnaEncode($record->nameToAbsolute($this->domain, false)), $record->type, $record->ttl, $prio, time(), $value ]; $record->name = main\mgLibs\custom\helpers\IdnaHelper::idnaEncode($record->name); $valid = $this->validate_input(false, $record->type, $value, $record->name, $prio, $record->ttl); if ($valid === true) { $this->log('ADD RECORDS'); if ($this->connect()) { if(empty($this->config['disable_doubled']) && in_array($record->type, $this->typesCantBeDoubled)) { if($this->checkIfRecordSameExist($record)) { $this->disconnect(); throw new exceptions\DNSSubmoduleException('Such record alredy exists', dns\SubmoduleExceptionCodes::COMMAND_ERROR); } } if ($this->mysql_safequery('INSERT INTO records (domain_id, name, type, ttl, prio, change_date, content) VALUES((SELECT id FROM domains WHERE LOWER(name) = ?), ?, ?, ?, ?, ?, ?)', $input)) { $this->updateSoaSerial(); $this->updateNotifiedSerial(); $this->disconnect(); try { if($this->isSigned()) { $this->rectify(); } } catch( Exception $ex) {} return true; } else { $this->disconnect(); throw new exceptions\DNSSubmoduleException($this->remote_db->error, dns\SubmoduleExceptionCodes::COMMAND_ERROR); } } else { $this->disconnect(); throw new exceptions\DNSSubmoduleException('Connection problem', dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM); } } else { $this->disconnect(); throw new exceptions\DNSSubmoduleException('Validation error: ' . $this->error, dns\SubmoduleExceptionCodes::INVALID_PARAMETERS); } } public function editRecord(dns\record\Record $record) //Prawie ctr + c -> ctrl + v { $this->domain = main\mgLibs\custom\helpers\IdnaHelper::idnaEncode($this->domain); $record->name = main\mgLibs\custom\helpers\IdnaHelper::idnaEncode($record->name); switch ($record->type) { case 'MX': $prio = $record->rdata->preference; $value = $record->rdata->exchange; break; case 'SRV': $prio = $record->rdata->priority; $value = $record->rdata->toString(['weight', 'port' , 'target']); break; case 'URI': $prio = $record->rdata->priority; $value = $record->rdata->toString(['weight', 'target']); break; case 'CNAME': $prio = 0; $value = rtrim($record->rdata->toString(), '.'); break; default: $prio = 0; $value = $record->rdata->toString(); break; } $value = main\mgLibs\custom\helpers\IdnaHelper::idnaEncode($value); $input = [ main\mgLibs\custom\helpers\IdnaHelper::idnaEncode( $record->nameToAbsolute($this->domain, false)), $record->type, $record->ttl, $prio, time(), $value, $record->line ]; $valid = $this->validate_input($record->line, $record->type, $value, $input[0], $prio, $record->ttl); if ($valid === true) { $this->log('MODIFY RECORDS'); if ($this->connect()) { if(empty($this->config['disable_doubled']) && in_array($record->type, $this->typesCantBeDoubled)) { if($this->checkIfRecordSameExist($record)) { $this->disconnect(); throw new exceptions\DNSSubmoduleException('You cannot add two the same ' .$record->type. ' records', dns\SubmoduleExceptionCodes::COMMAND_ERROR); } } if ($this->mysql_safequery('UPDATE records SET name = ?, type = ?, ttl = ?, prio = ?, change_date = ?, content = ? WHERE id = ?', $input)) { $this->updateSoaSerial(); $this->updateNotifiedSerial(); $this->disconnect(); try { if($this->isSigned()) { $this->rectify(); } } catch( Exception $ex) {} return true; } else { $this->disconnect(); throw new exceptions\DNSSubmoduleException($this->remote_db->error, dns\SubmoduleExceptionCodes::COMMAND_ERROR); } } else { $this->disconnect(); throw new exceptions\DNSSubmoduleException('Connection problem', dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM); } } else { $this->disconnect(); throw new exceptions\DNSSubmoduleException('Validation error: ' . $this->error, dns\SubmoduleExceptionCodes::INVALID_PARAMETERS); } } public function deleteRecord(dns\record\Record $record) { $this->domain = main\mgLibs\custom\helpers\IdnaHelper::idnaEncode($this->domain); if ($this->connect()) { if (strtoupper($record->type) == 'SOA') { $this->disconnect(); throw new exceptions\DNSSubmoduleException('You can not remove the SOA record', dns\SubmoduleExceptionCodes::COMMAND_ERROR); } else { $q = $this->mysql_safequery('DELETE FROM records WHERE id = ?', [$record->line]); $this->log('DELETE RECORD'); if ($q) { $this->updateSoaSerial(); $this->updateNotifiedSerial(); $this->disconnect(); try { if($this->isSigned()) { $this->rectify(); } } catch( Exception $ex) {} return true; } else { $this->disconnect(); throw new exceptions\DNSSubmoduleException('Error occured: ' . $this->remote_db->error, dns\SubmoduleExceptionCodes::COMMAND_ERROR); } } } else { $this->disconnect(); throw new exceptions\DNSSubmoduleException('Connection problem', dns\SubmoduleExceptionCodes::CONNECTION_PROBLEM); } } public function getZones() { $this->connect(); $q = $this->mysql_safequery('SELECT name, master FROM domains'); $this->log('GET ZONES'); $out = []; while ($row = $q->fetch()) { $out[$row['name']] = $row['master']; } $this->disconnect(); return $out; } /* * **************************************** * * VALIDATION * * **************************************** */ private function validate_input( $rid = false, $type, $content, $name, $prio, $ttl) { if ($this->config['disable_validation'] === 'on' || $type === 'PTR') return true; switch ($type) { case 'A': if (!$this->is_valid_ipv4($content)) return false; if (!$this->is_valid_rr_cname_exists($name, $rid)) return false; if (!$this->is_valid_hostname_fqdn($name, 1)) return false; break; case 'AAAA': if (!$this->is_valid_ipv6($content)) return false; if (!$this->is_valid_rr_cname_exists($name, $rid)) return false; if (!$this->is_valid_hostname_fqdn($name, 1)) return false; break; case 'CNAME': if (!$this->is_valid_rr_cname_name($name)) return false; if (!$this->is_valid_rr_cname_unique($name, $rid)) return false; if (!$this->is_valid_hostname_fqdn($name, 1)) return false; if (!$this->is_valid_hostname_fqdn($content, 0)) return false; if (!$this->is_not_empty_cname_rr($name, $this->domain)) return false; break; case 'NS': case 'MX': if (!$this->is_valid_hostname_fqdn($content, 0)) return false; if (!$this->is_valid_hostname_fqdn($name, 1)) return false; if (!$this->is_valid_non_alias_target($content)) return false; break; case 'SOA': if (!$this->is_valid_rr_soa_name($name, $this->domain)) return false; if (!$this->is_valid_hostname_fqdn($name, 1)) return false; if (!$this->is_valid_rr_soa_content($content)) return false; break; case 'SRV': if (!$this->is_valid_rr_srv_name($name)) return false; if (!$this->is_valid_rr_srv_content($content)) return false; break; case 'SPF': case 'TXT': if (!$this->is_valid_printable($name)) return false; if (!$this->is_valid_printable(main\mgLibs\custom\helpers\IdnaHelper::idnaEncode(trim($content,'"')))) return false; break; case 'LOC': if (!$this->is_valid_loc($content)) return false; if (!$this->is_valid_hostname_fqdn($name, 1)) return false; break; case 'CAA': if(!$this->is_valid_caa($content)) return false; break; //2.9.0 case 'ALIAS': if (!$this->is_valid_hostname_fqdn($name, 0)) return false; if (!$this->is_valid_caa_target($content)) return false; break; case 'AFSDB': if (!$this->is_valid_hostname_fqdn($name, 0)) return false; if (!$this->is_valid_afsdb_record($content)) return false; break; case 'WKS': if (!$this->is_valid_hostname_fqdn($name, 0)) return false; if (!$this->is_valid_wks_record($content)) return false; break; case 'RP': case 'NAPTR': case 'MR': case 'MINFO': case 'HINFO': case 'DS': case 'DNSKEY': case 'DNAME': case 'RRSIG': if (!$this->is_valid_hostname_fqdn($name, 0)) return false; break; case 'URL': break; default: $this->error = ERR_DNS_RR_TYPE; return false; } if (!$this->is_valid_rr_prio($prio, $type)) return false; if (!$this->is_valid_rr_ttl($ttl)) return false; return true; } private function is_valid_hostname_fqdn($hostname, $wildcard) { $hostname = main\mgLibs\custom\helpers\IdnaHelper::idnaEncode($hostname); $dns_strict_tld_check = false; $valid_tlds = false; $hostname = preg_replace("/\.$/", '', $hostname); # The full domain name may not exceed a total length of 253 characters. if (strlen($hostname) > 253) { $this->error = ERR_DNS_HN_TOO_LONG; return false; } $hostname_labels = explode('.', $hostname); $label_count = count($hostname_labels); if ($label_count == 1) { $this->error = ERR_DNS_HN_LENGTH; return false; } foreach ($hostname_labels as $hostname_label) { if ($wildcard == 1 && !isset($first)) { if (!preg_match('/^(\*|[\w\-\/]+)$/', $hostname_label)) { $this->error = ERR_DNS_HN_INV_CHARS; return false; } $first = 1; } else { if (!preg_match('/^[\w\-\/]+$/', $hostname_label)) { $this->error = ERR_DNS_HN_INV_CHARS; return false; } } if ( strpos($hostname_label, '-') === 0 ) { $this->error = ERR_DNS_HN_DASH; return false; } if (substr($hostname_label, -1, 1) === '-' ) { $this->error = ERR_DNS_HN_DASH; return false; } if ( $hostname_label === '' || strlen($hostname_label) > 63) { $this->error = ERR_DNS_HN_LENGTH; return false; } } if ($hostname_labels[$label_count - 1] === 'arpa' && (substr_count($hostname_labels[0], '/') === 1 XOR substr_count($hostname_labels[1], '/') === 1)) { if (substr_count($hostname_labels[0], '/') === 1) { $array = explode('/', $hostname_labels[0]); } else { $array = explode('/', $hostname_labels[1]); } if (count($array) !== 2) { $this->error = ERR_DNS_HOSTNAME; return false; } if (!is_numeric($array[0]) || $array[0] < 0 || $array[0] > 255) { $this->error = ERR_DNS_HOSTNAME; return false; } if (!is_numeric($array[1]) || $array[1] < 25 || $array[1] > 31) { $this->error = ERR_DNS_HOSTNAME; return false; } } else { if (substr_count($hostname, '/') > 0) { $this->error = ERR_DNS_HN_SLASH; return false; } } if ($dns_strict_tld_check && !in_array(strtolower($hostname_labels[$label_count - 1]), $valid_tlds)) { $this->error = ERR_DNS_INV_TLD; return false; } return true; } private function is_valid_ipv4($ipv4, $answer = true) { if (!preg_match('/^[0-9.]{7,15}$/', $ipv4)) { if ($answer) { $this->error = ERR_DNS_IPV4; } return false; } $quads = explode('.', $ipv4); $numquads = count($quads); if ($numquads !== 4) { if ($answer) { $this->error = ERR_DNS_IPV4; } return false; } for ($i = 0; $i < 4; $i++) { if ($quads[$i] > 255) { if ($answer) { $this->error = ERR_DNS_IPV4; } return false; } } return true; } private function is_valid_ipv6($ipv6, $answer = true) { if (!preg_match('/^[0-9a-f]{0,4}:([0-9a-f]{0,4}:){0,6}[0-9a-f]{0,4}$/i', $ipv6)) { if ($answer) { $this->error = ERR_DNS_IPV6; } return false; } $quads = explode(':', $ipv6); $numquads = count($quads); if ($numquads > 8 || $numquads < 3) { if ($answer) { $this->error = ERR_DNS_IPV6; } return false; } $emptyquads = 0; for ($i = 1; $i < $numquads - 1; $i++) { if ($quads[$i] == '' ) $emptyquads++; } if ($emptyquads > 1) { if ($answer) { $this->error = ERR_DNS_IPV6; } return false; } if ($emptyquads === 0 && $numquads !== 8) { if ($answer) { $this->error = ERR_DNS_IPV6; } return false; } return true; } private function is_valid_printable($string) { if (!preg_match('/^[[:print:]]+$/', trim($string))) { $this->error = ERR_DNS_PRINTABLE; return false; } return true; } private function is_valid_afsdb_record($string) { $record = explode(' ', $string); if (!$this->is_valid_hostname_fqdn($record[1], 1)){ return false; } if(!$this->is_valid_subtype($record[0])){ return false; } return true; } private function is_valid_subtype($type){ if($type == 1 || $type == 2){ return true; } $this->error = ERR_DNS_SUBTYPE; return false; } private function is_valid_wks_record($string) { $record = explode(' ', $string); if (!$this->is_valid_ipv4($record[0])){ return false; } return true; } private function is_valid_rr_cname_name($name) { if ($this->connect()) { $q = $this->mysql_safequery("SELECT type, content FROM records WHERE content = ? AND (type = 'MX' OR type = 'NS')", [ $name ]); if ($q !== false) { if($q->numRows() > 0) { $this->error = ERR_DNS_CNAME; return false; } } else { $this->error = 'Power DNS database error: ' . $this->remote_db->error; return false; } $this->disconnect(); } else { $this->error = 'Unable connect to Power DNS database'; return false; } return true; } private function is_valid_rr_cname_exists($name, $rid) { if ($this->connect()) { $args[] = $name; if ($rid > 0) { $where = ' AND id != ? '; $args[] = $rid; } else { $where = ''; } $q = $this->mysql_safequery('SELECT type, name FROM records WHERE name = ?' . $where . " AND TYPE = 'CNAME'", $args); if($q !== false) { if($q->numRows() > 0) { $this->error = ERR_DNS_CNAME_EXISTS; return false; } } else { $this->error = 'Power DNS database error: ' . $this->remote_db->error; return false; } $this->disconnect(); } else { $this->error = 'Unable connect to Power DNS database'; return false; } return true; } private function is_valid_rr_cname_unique($name, $rid) { if ($this->connect()) { $args[] = $name; if ($rid > 0) { $where = ' AND id != ? '; $args[] = $rid; } else { $where = ''; } $q = $this->mysql_safequery('SELECT * FROM records WHERE name = ? ' . $where . " AND TYPE IN ('A', 'AAAA', 'CNAME')", $args); if ($q !== false) { if($q->numRows() > 0) { $this->error = ERR_DNS_CNAME_UNIQUE; return false; } } else { $this->error = 'Power DNS database error: ' . $this->remote_db->error; return false; } $this->disconnect(); } else { $this->error = 'Unable connect to Power DNS database'; return false; } return true; } private function is_not_empty_cname_rr($name, $zone) { if ($name == $zone) { $this->error = ERR_DNS_CNAME_EMPTY; return false; } return true; } private function is_valid_non_alias_target($target) { if ($this->connect()) { $q = $this->mysql_safequery("SELECT type, name FROM records WHERE name = ? AND TYPE = 'CNAME'", [ $target ]); if ($q !== false) { if($q->numRows() > 0) { $this->error = ERR_DNS_NON_ALIAS_TARGET; return false; } } else { $this->error = 'Power DNS database error: ' . $this->remote_db->error; return false; } $this->disconnect(); } else { $this->error = 'Unable connect to Power DNS database'; return false; } return true; } private function is_valid_rr_soa_content($content) { $fields = preg_split("/\s+/", trim($content)); $field_count = count($fields); if ($field_count == 0 || $field_count > 7) { return false; } else { if (!$this->is_valid_hostname_fqdn($fields[0], 0) || preg_match('/\.arpa\.?$/', $fields[0])) { return false; } $final_soa = $fields[0]; if (isset($fields[1])) { $addr_input = $fields[1]; } else { global $dns_hostmaster; $addr_input = $dns_hostmaster; } if (!preg_match('/@/', $addr_input)) { $addr_input = preg_split('/(?is_valid_email($addr_to_check)) { return false; } else { $addr_final = explode('@', $addr_to_check, 2); $final_soa .= ' ' . str_replace('.', "\\.", $addr_final[0]) . '.' . $addr_final[1]; } if (isset($fields[2])) { if (!is_numeric($fields[2])) { return false; } $final_soa .= ' ' . $fields[2]; } else { $final_soa .= ' 0'; } if ($field_count == 7) { for ($i = 3; ($i < 7); $i++) { if (!is_numeric($fields[$i])) { return false; } else { $final_soa .= ' ' . $fields[$i]; } } } } $content = $final_soa; return true; } private function is_valid_email($addr) { if (filter_var($addr, FILTER_VALIDATE_EMAIL) === false) { $this->error = ERR_INV_EMAIL; return false; } else { return true; } } private function is_valid_rr_soa_name($name, $zone) { $zone = rtrim($zone,'.').'.'; $name = rtrim($name,'.').'.'; if ($name != $zone) { $this->error = ERR_DNS_SOA_NAME; return false; } return true; } private function is_valid_rr_prio(&$prio, $type) { if ($type == 'MX' || $type == 'SRV' ) { if (!is_numeric($prio) || $prio < 0 || $prio > 65535) { $this->error = ERR_DNS_INV_PRIO; return false; } } else { $prio = 0; } return true; } private function is_valid_rr_srv_name(&$name) { if (strlen($name) > 255) { $this->error = ERR_DNS_HN_TOO_LONG; return false; } $fields = explode('.', $name, 3); if (!preg_match('/^_[\w-]+$/', $fields[0])) { $this->error = ERR_DNS_SRV_NAME; return false; } if (!preg_match('/^_[\w]+$/', $fields[1])) { $this->error = ERR_DNS_SRV_NAME; return false; } if (!$this->is_valid_hostname_fqdn($fields[2], 0)) { $this->error = ERR_DNS_SRV_NAME; return false; } $name = implode('.', $fields); return true; } private function is_valid_rr_srv_content(&$content) { $fields = preg_split("/\s+/", trim($content), 3); if (!is_numeric($fields[0]) || $fields[0] < 0 || $fields[0] > 65535) { $this->error = ERR_DNS_SRV_WGHT; return false; } if (!is_numeric($fields[1]) || $fields[1] < 0 || $fields[1] > 65535) { $this->error = ERR_DNS_SRV_PORT; return false; } if ($fields[2] === '' || ($fields[2] !== '.' && !$this->is_valid_hostname_fqdn($fields[2], 0))) { $this->error = ERR_DNS_SRV_TRGT; return false; } $content = implode(' ', $fields); return true; } private function is_valid_rr_ttl(&$ttl) { if (!isset($ttl) || $ttl == '' ) { global $dns_ttl; $ttl = $dns_ttl; } if (!is_numeric($ttl) || $ttl < 0 || $ttl > 2147483647) { $this->error = ERR_DNS_INV_TTL; return false; } return true; } private function is_valid_loc($content) { $regex = "^(90|[1-8]\d|0?\d)( ([1-5]\d|0?\d)( ([1-5]\d|0?\d)(\.\d{1,3})?)?)? [NS] (180|1[0-7]\d|[1-9]\d|0?\d)( ([1-5]\d|0?\d)( ([1-5]\d|0?\d)(\.\d{1,3})?)?)? [EW] (-(100000(\.00)?|\d{1,5}(\.\d\d)?)|([1-3]?\d{1,7}(\.\d\d)?|4([01][0-9]{6}|2([0-7][0-9]{5}|8([0-3][0-9]{4}|4([0-8][0-9]{3}|9([0-5][0-9]{2}|6([0-6][0-9]|7[01]))))))(\.\d\d)?|42849672(\.([0-8]\d|9[0-5]))?))[m]?( (\d{1,7}|[1-8]\d{7})(\.\d\d)?[m]?){0,3}$^"; if (!preg_match($regex, $content)) { return false; } else { return true; } } private function is_valid_caa($content) { $vars = explode(' ',$content); if($this->is_valid_caa_flag($vars[0]) && $this->is_valid_caa_tag($vars[1]) && $this->is_valid_caa_target($vars[2])) { return true; } return false; } private function is_valid_caa_flag($flag) { if(is_numeric($flag) && $flag >= 0 && $flag <= 255) { return true; } $this->error = ERR_DNS_CAA_FLAG; return false; } private function is_valid_caa_tag($tag) { return is_string($tag); } private function is_valid_caa_target($target) { if($target[0] === '"' && $target[strlen($target) - 1] === '"') { return true; } $this->error = ERR_DNS_CAA_TARGET; return false; } /********************************************** * DNS SEC *********************************************/ public function generateSignKeys() { } /** * * @return DnsSec */ public function getSignKeys() { $dnssec = new DnsSec(); $command = $this->pdnsutil("show-zone $this->domain"); $keys = array_filter(explode("\n", substr($command['stdio'], strpos($command['stdio'], 'keys:')+5))); foreach($keys as $index => $key) { if(strpos($key, 'DS') === 0) { $ex = preg_split('/\s+/', $key); $ds = new dns\record\type\DS(); $ds->setKeytag($ex[5]); $ds->setAlgorithm($ex[6]); $ds->setDigestType($ex[7]); $ds->setDigest($ex[8]); $dnssec->addDs($ds); } elseif(strpos($key, 'CSK DNSKEY') === 0) { $ex = preg_split('/\s+/', $key); $dnskey = new dns\record\type\DNSKEY(); $dnskey->setFlags($ex[6]); $dnskey->setProtocol($ex[7]); $dnskey->setAlgorithm($ex[8]); $dnskey->setPublicKey($ex[9]); $ex = array_map(function($field){return explode('=', $field);}, explode(',', $keys[$index-1])); $ex = array_map(function($field){return [trim($field[0]) => trim($field[1])];}, $ex); $zoneKey = new dns\dnssec\CSK(); $zoneKey->setId((int)$ex[2]['tag']); $zoneKey->setBits((int)$ex[4]['bits']); $zoneKey->setDnsKey($dnskey); $dnssec->addKey($zoneKey); } elseif(strpos($key, 'KSK DNSKEY') === 0) { $ex = preg_split('/\s+/', $key); $dnskey = new dns\record\type\DNSKEY(); $dnskey->setFlags($ex[6]); $dnskey->setProtocol($ex[7]); $dnskey->setAlgorithm($ex[8]); $dnskey->setPublicKey($ex[9]); $dnskey2 = new dns\record\type\DNSKEY(); $dnskey2->setFlags($ex[6]); $dnskey2->setProtocol($ex[7]); $dnskey2->setAlgorithm($ex[8]); $ex = array_map(function($field){return explode('=', $field);}, explode(',', $keys[$index-1])); $ex = array_map(function($field){return [trim($field[0]) => trim($field[1])];}, $ex); $ksk = new dns\dnssec\KSK(); $ksk->setId((int)$ex[2]['tag']); $ksk->setBits((int)$ex[4]['bits']); $ksk->setDnsKey($dnskey); $ex = array_map(function($field){return explode('=', $field);}, explode(',', $keys[$index-2])); $ex = array_map(function($field){return [trim($field[0]) => trim($field[1])];}, $ex); $zsk = new dns\dnssec\ZSK(); $zsk->setId((int)$ex[2]['tag']); $zsk->setBits((int)$ex[4]['bits']); $zsk->setDnsKey($dnskey2); $dnssec->addKey($ksk); $dnssec->addKey($zsk); } } return $dnssec; } public function isSigned() { $command = $this->pdnsutil("show-zone $this->domain"); if(stripos($command['stderr'], 'No keys for zone') !== false) { return false; } return true; } public function sign() { $command = $this->pdnsutil("secure-zone $this->domain", false); $match = preg_match("/Zone {$this->domain}\.? secured/", $command['stdio']); if(!$match && $command['stderr']) { throw new exceptions\DNSSubmoduleException($command['stderr'], dns\SubmoduleExceptionCodes::COMMAND_ERROR); } } public function unsign() { $command = $this->pdnsutil("disable-dnssec $this->domain"); if(!empty($command['stderr'])) { throw new exceptions\DNSSubmoduleException($command['stderr'], dns\SubmoduleExceptionCodes::COMMAND_ERROR); } } public function rectify() { $command = $this->pdnsutil("rectify-zone $this->domain"); if(!empty($command['stderr']) && stripos($command['stderr'], 'Adding NSEC') === false) { throw new exceptions\DNSSubmoduleException($command['stderr'], dns\SubmoduleExceptionCodes::COMMAND_ERROR); } } protected function connectSSH() { if(!function_exists('ssh2_connect')) { throw new exceptions\DNSSubmoduleException('Cannot find SSH2 for PHP. Please install SSH2 extension', dns\SubmoduleExceptionCodes::COMMAND_ERROR); } if(empty($this->config['ssh_host'])) { throw new exceptions\DNSSubmoduleException('SSH Host field is empty. Cannot connect to server', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS); } if(empty($this->config['ssh_username'])) { throw new exceptions\DNSSubmoduleException('SSH Username field is empty. Cannot connect to server', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS); } if(empty($this->config['ssh_password']) && (empty($this->config['ssh_public_key']) && empty($this->config['ssh_private_key']))) { throw new exceptions\DNSSubmoduleException('SSH Password or SSH Key field is empty. Cannot connect to server', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS); } if(empty($this->config['ssh_port'])) { throw new exceptions\DNSSubmoduleException('SSH Port field is empty. Cannot connect to server', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS); } $this->ssh = ssh2_connect($this->config['ssh_host'], $this->config['ssh_port']); if(!$this->ssh) { throw new exceptions\DNSSubmoduleException('Cannot connect to SSH', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS); } if(!empty($this->config['ssh_public_key']) && !empty($this->config['ssh_private_key'])) { $sshPublicKey = $this->config['ssh_public_key'][0] === '/' ? $this->config['ssh_public_key'] : ROOTDIR.DIRECTORY_SEPARATOR.'ssh_keys'.DIRECTORY_SEPARATOR.$this->config['ssh_public_key']; $shhPrivateKey = $this->config['ssh_private_key'][0] === '/' ? $this->config['ssh_private_key'] : ROOTDIR.DIRECTORY_SEPARATOR.'ssh_keys'.DIRECTORY_SEPARATOR.$this->config['ssh_private_key']; if(!file_get_contents($sshPublicKey)) { throw new exceptions\DNSSubmoduleException('SSH public key file does not exist at path: ' . $sshPublicKey, dns\SubmoduleExceptionCodes::INVALID_PARAMETERS); } if(!file_get_contents($shhPrivateKey)) { throw new exceptions\DNSSubmoduleException('SSH private key file does not exist at path: ' . $shhPrivateKey, dns\SubmoduleExceptionCodes::INVALID_PARAMETERS); } $auth = ssh2_auth_pubkey_file($this->ssh, $this->config['ssh_username'], $sshPublicKey, $shhPrivateKey); if(!$auth) { $this->ssh = null; throw new exceptions\DNSSubmoduleException('Cannot login to server (SSH). Check your credentials', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS); } } else { $auth = ssh2_auth_password($this->ssh, $this->config['ssh_username'], $this->config['ssh_password']); if(!$auth) { $this->ssh = null; throw new exceptions\DNSSubmoduleException('Cannot login to server. Check your credentials', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS); } } } protected function pdnsutil($command = '', $throwErrorWhenStdErr = true) { if(!$this->ssh) { $this->connectSSH(); } if(!$this->config['pdnsutil_path']) { throw new exceptions\DNSSubmoduleException('PDNSUTIL path empty. Cannot run command', dns\SubmoduleExceptionCodes::INVALID_PARAMETERS); } $pdnsutil = $this->config['pdnsutil_path']; $stream = ssh2_exec($this->ssh, $pdnsutil.' '.$command); $errorStream = ssh2_fetch_stream($stream, SSH2_STREAM_STDERR); stream_set_blocking($errorStream, true); stream_set_blocking($stream, true); $streamContent = stream_get_contents($stream); $errorStreamContent = stream_get_contents($errorStream); // Close the streams fclose($errorStream); fclose($stream); return [ 'stdio' => $streamContent, 'stderr' => $errorStreamContent ]; } public function checkIfRecordSameExist($record) { if(empty($record->line)) { $res = $this->mysql_safequery('SELECT id, name, content FROM records WHERE domain_id = (SELECT id FROM domains WHERE LOWER(name) = ?) AND type = ? AND name = ?', [strtolower($this->domain), $record->type, $record->name]); } else { $res = $this->mysql_safequery('SELECT id, name, content FROM records WHERE domain_id = (SELECT id FROM domains WHERE name = ?) AND type = ? AND name = ? AND id != ?', [$this->domain, $record->type, $record->name, $record->line]); } return $res->numRows() > 0; } public function validateRecord($record) { if($this->server->getModuleConfiguration()['disable_validation'] !== 'on') { parent::validateRecord($record); } } } $messages_file = substr(__DIR__, 0, strpos(__DIR__, '' . DIRECTORY_SEPARATOR . 'includes')) . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . 'addons' . DIRECTORY_SEPARATOR . 'dns_manager' . DIRECTORY_SEPARATOR . 'powerdns_messages.php'; if (file_exists($messages_file)) { require_once($messages_file); } else { define('ERR_PERM_SEARCH', 'You do not have the permission to perform searches.'); define('ERR_PERM_ADD_RECORD', 'You do not have the permission to add a record to this zone.'); define('ERR_PERM_EDIT_RECORD', 'You do not have the permission to edit this record.'); define('ERR_PERM_VIEW_RECORD', 'You do not have the permission to view this record.'); define('ERR_PERM_DEL_RECORD', 'You do not have the permission to delete this record.'); define('ERR_PERM_ADD_ZONE_MASTER', 'You do not have the permission to add a master zone.'); define('ERR_PERM_ADD_ZONE_SLAVE', 'You do not have the permission to add a slave zone.'); define('ERR_PERM_DEL_ZONE', 'You do not have the permission to delete a zone.'); define('ERR_PERM_VIEW_COMMENT', 'You do not have the permission to view this comment.'); define('ERR_PERM_EDIT_COMMENT', 'You do not have the permission to edit this comment.'); define('ERR_PERM_DEL_SM', 'You do not have the permission to delete a supermaster.'); define('ERR_PERM_VIEW_ZONE', 'You do not have the permission to view this zone.'); define('ERR_PERM_EDIT_USER', 'You do not have the permission to edit this user.'); define('ERR_PERM_EDIT_PERM_TEMPL', 'You do not have the permission to edit permission templates.'); define('ERR_PERM_DEL_PERM_TEMPL', 'You do not have the permission to delete permission templates.'); define('ERR_PERM_ADD_USER', 'You do not have the permission to add a new user.'); define('ERR_PERM_DEL_USER', 'You do not have the permission to delete this user.'); define('ERR_PERM_EDIT_ZONE_TEMPL', 'You do not have the permission to edit zone templates.'); define('ERR_PERM_DEL_ZONE_TEMPL', 'You do not have the permission to delete zone templates.'); define('ERR_PERM_ADD_ZONE_TEMPL', 'You do not have the permission to add a zone template.'); /* DOMAIN STUFF */ define('ERR_DOMAIN_INVALID', 'This is an invalid zone name.'); define('ERR_SM_EXISTS', 'There is already a supermaster with this IP address and hostname.'); define('ERR_DOMAIN_EXISTS', 'There is already a zone with this name.'); /* USER STUFF */ define('ERR_USER_EXIST', 'Username exist already, please choose another one.'); define('ERR_USER_NOT_EXIST', 'User does not exist.'); define('ERR_USER_WRONG_CURRENT_PASS', 'You did not enter the correct current password.'); define('ERR_USER_MATCH_NEW_PASS', 'The two new password fields do not match.'); define('ERR_PERM_TEMPL_ASSIGNED', 'This template is assigned to at least one user.'); /* OTHER */ define('ERR_INV_INPUT', 'Invalid or unexpected input given.'); define('ERR_INV_ARG', 'Invalid argument(s) given to function %s'); define('ERR_INV_ARGC', 'Invalid argument(s) given to function %s %s'); define('ERR_UNKNOWN', 'Unknown error.'); define('ERR_INV_EMAIL', 'Enter a valid email address.'); define('ERR_ZONE_NOT_EXIST', 'There is no zone with this ID.'); define('ERR_REVERS_ZONE_NOT_EXIST', 'There is no matching reverse-zone for: %s.'); define('ERR_ZONE_TEMPL_NOT_EXIST', 'There is no zone template with this ID.'); define('ERR_INSTALL_DIR_EXISTS', 'The install/ directory exists, you must remove it first before proceeding.'); define('ERR_ZONE_TEMPL_EXIST', 'Zone template with this name already exists, please choose another one.'); define('ERR_ZONE_TEMPL_IS_EMPTY', 'Template name can\'t be an empty string.'); define('ERR_DEFAULT_CRYPTOKEY_USED', 'Default session encryption key is used, please set it in your configuration file.'); define('ERR_LOCALE_FAILURE', 'Failed to set locale.'); define('ERR_ZONE_UPD', 'Zone has not been updated successfully.'); define('ERR_EXEC_NOT_ALLOWED', 'Failed to call function exec. Make sure that exec is not listed in disable_functions at php.ini'); /* DATABASE */ define('ERR_DB_NO_DB_NAME', 'No database name has been set in config.inc.php.'); define('ERR_DB_NO_DB_HOST', 'No database host has been set in config.inc.php.'); define('ERR_DB_NO_DB_USER', 'No database username has been set in config.inc.php.'); define('ERR_DB_NO_DB_PASS', 'No database password has been set in config.inc.php.'); define('ERR_DB_NO_DB_TYPE', 'No or unknown database type has been set in config.inc.php.'); define('ERR_DB_NO_DB_FILE', 'No database file has been set in config.inc.php.'); define('ERR_DB_NO_DB_UPDATE', 'It seems that you forgot to update the database after Poweradmin upgrade to new version.'); define('ERR_DB_UNK_TYPE', 'Unknown database type.'); /* DNS */ define('ERR_DNS_CONTENT', 'Your content field doesnt have a legit value.'); define('ERR_DNS_HOSTNAME', 'Invalid hostname.'); define('ERR_DNS_HN_INV_CHARS', 'You have invalid characters in your hostname.'); define('ERR_DNS_HN_INV_CONTENT', 'Invalid content.'); define('ERR_DNS_HN_DASH', 'A hostname can not start or end with a dash.'); define('ERR_DNS_HN_LENGTH', 'Given hostname or one of the labels is too short or too long.'); define('ERR_DNS_HN_SLASH', 'Given hostname has too many slashes.'); define('ERR_DNS_RR_TYPE', 'Unknown record type.'); define('ERR_DNS_IP', 'This is not a valid IPv4 or IPv6 address.'); define('ERR_DNS_IPV6', 'This is not a valid IPv6 address.'); define('ERR_DNS_IPV4', 'This is not a valid IPv4 address.'); define('ERR_DNS_CNAME', 'This is not a valid CNAME. Did you assign an MX or NS record to the record?'); define('ERR_DNS_CNAME_EXISTS', 'This is not a valid record. There is already exists a CNAME with this name.'); define('ERR_DNS_CNAME_UNIQUE', 'This is not a valid CNAME. There is already exists an A, AAAA or CNAME with this name.'); define('ERR_DNS_CNAME_EMPTY', 'Empty CNAME records are not allowed.'); define('ERR_DNS_NON_ALIAS_TARGET', 'You can not point a NS or MX record to a CNAME record. Remove or rame the CNAME record first, or take another name.'); define('ERR_DNS_NS_HNAME', 'NS records must be a hostnames.'); define('ERR_DNS_MX_PRIO', 'A prio field should be numeric.'); define('ERR_DNS_SOA_NAME', 'Invalid value for name field of SOA record. It should be the name of the zone.'); define('ERR_DNS_SOA_MNAME', 'You have an error in the MNAME field of the SOA record.'); define('ERR_DNS_HINFO_INV_CONTENT', 'Invalid value for content field of HINFO record.'); define('ERR_DNS_HN_TOO_LONG', 'The hostname is too long.'); define('ERR_DNS_INV_TLD', 'You are using an invalid top level domain.'); define('ERR_DNS_INV_TTL', 'Invalid value for TTL field. It should be numeric.'); define('ERR_DNS_INV_PRIO', 'Invalid value for prio field. It should be numeric.'); define('ERR_DNS_SRV_NAME', 'Invalid value for name field of SRV record.'); define('ERR_DNS_SRV_WGHT', 'Invalid value for the priority field of the SRV record.'); define('ERR_DNS_SRV_PORT', 'Invalid value for the weight field of the SRV record.'); define('ERR_DNS_SRV_TRGT', 'Invalid SRV target.'); define('ERR_DNS_PRINTABLE', 'Invalid characters have been used in this record.'); define('ERR_DNS_CAA_FLAG', 'Invalid flag value.'); define('ERR_DNS_CAA_TARGET', 'Target has to be in " "'); /* PDNSSEC */ define('ERR_EXEC_PDNSSEC', 'Failed to call pdnssec utility.'); define('ERR_EXEC_PDNSSEC_SECURE_ZONE', 'Failed to secure zone.'); define('ERR_EXEC_PDNSSEC_RECTIFY_ZONE', 'Failed to rectify zone.'); /* GOOD! */ define('SUC_ZONE_ADD', 'Zone has been added successfully.'); define('SUC_ZONE_DEL', 'Zone has been deleted successfully.'); define('SUC_ZONE_UPD', 'Zone has been updated successfully.'); define('SUC_ZONES_UPD', 'Zones have been updated successfully.'); define('SUC_USER_UPD', 'The user has been updated successfully.'); define('SUC_USER_ADD', 'The user has been created successfully.'); define('SUC_USER_DEL', 'The user has been deleted successfully.'); define('SUC_RECORD_UPD', 'The record has been updated successfully.'); define('SUC_RECORD_DEL', 'The record has been deleted successfully.'); define('SUC_COMMENT_UPD', 'The comment has been updated successfully.'); define('SUC_SM_DEL', 'The supermaster has been deleted successfully.'); define('SUC_SM_ADD', 'The supermaster has been added successfully.'); define('SUC_PERM_TEMPL_ADD', 'The permission template has been added successfully.'); define('SUC_PERM_TEMPL_UPD', 'The permission template has been updated successfully.'); define('SUC_PERM_TEMPL_DEL', 'The permission template has been deleted successfully.'); define('SUC_ZONE_TEMPL_ADD', 'Zone template has been added successfully.'); define('SUC_ZONE_TEMPL_UPD', 'Zone template has been updated successfully.'); define('SUC_ZONE_TEMPL_DEL', 'Zone template has been deleted successfully.'); define('SUC_EXEC_PDNSSEC_RECTIFY_ZONE', 'Zone has been rectified successfully.'); //2.9.0 define('ERR_DNS_SUBTYPE', 'This is not a valid subtype. Valid type is 1 or 2 depending on whether the endpoint is an AFS Volume Location Server or DCE Authentication Server.'); }