(2016-) * @license GPLv2 http://files.froxlor.org/misc/COPYING.txt * @package Panel * */ // This file is being included in admin_domains and customer_domains // and therefore does not need to require lib/init.php $domain_id = isset($_GET['domain_id']) ? (int) $_GET['domain_id'] : null; $record = isset($_POST['record']['record']) ? trim($_POST['record']['record']) : null; $type = isset($_POST['record']['type']) ? $_POST['record']['type'] : 'A'; $prio = isset($_POST['record']['prio']) ? (int) $_POST['record']['prio'] : null; $content = isset($_POST['record']['content']) ? trim($_POST['record']['content']) : null; $ttl = isset($_POST['record']['ttl']) ? (int) $_POST['record']['ttl'] : 18000; // get domain-name $dom_stmt = Database::prepare("SELECT domain, isbinddomain FROM `" . TABLE_PANEL_DOMAINS . "` WHERE id = :did"); $domain = Database::pexecute_first($dom_stmt, array( 'did' => $domain_id )); if ($domain['isbinddomain'] != '1') { standard_error('dns_domain_nodns'); } $domain = $idna_convert->decode($domain['domain']); // select all entries $sel_stmt = Database::prepare("SELECT * FROM `" . TABLE_DOMAIN_DNS . "` WHERE domain_id = :did"); Database::pexecute($sel_stmt, array( 'did' => $domain_id )); $dom_entries = $sel_stmt->fetchAll(PDO::FETCH_ASSOC); $errors = array(); $success_message = ""; // action for adding a new entry if ($action == 'add_record' && ! empty($_POST)) { // validation if (empty($record)) { $record = "@"; } $record = strtolower($record); if ($ttl <= 0) { $ttl = 18000; } if (empty($content)) { $errors[] = $lng['error']['dns_content_empty']; } // types if ($type == 'A' && filter_var($content, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) === false) { $errors[] = $lng['error']['dns_arec_noipv4']; } elseif ($type == 'AAAA' && filter_var($content, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) === false) { $errors[] = $lng['error']['dns_aaaarec_noipv6']; } elseif ($type == 'MX') { if ($prio === null || $prio < 0) { $errors[] = $lng['error']['dns_mx_prioempty']; } // check for trailing dot if (substr($content, - 1) == '.') { // remove it for checks $content = substr($content, 0, - 1); } if (! validateDomain($content)) { $errors[] = $lng['error']['dns_mx_needdom']; } else { // check whether there is a CNAME-record for the same resource foreach ($dom_entries as $existing_entries) { $fqdn = $existing_entries['record'] . '.' . $domain; if ($existing_entries['type'] == 'CNAME' && $fqdn == $content) { $errors[] = $lng['error']['dns_mx_noalias']; break; } } } // append trailing dot (again) $content .= '.'; } elseif ($type == 'CNAME') { // check for trailing dot if (substr($content, - 1) == '.') { // remove it for checks $content = substr($content, 0, - 1); } if (! validateDomain($content)) { $errors[] = $lng['error']['dns_cname_invaliddom']; } else { // check whether there are RR-records for the same resource foreach ($dom_entries as $existing_entries) { if (($existing_entries['type'] == 'A' || $existing_entries['type'] == 'AAAA' || $existing_entries['type'] == 'MX' || $existing_entries['type'] == 'NS') && $existing_entries['record'] == $record) { $errors[] = $lng['error']['dns_cname_nomorerr']; break; } } } // append trailing dot (again) $content .= '.'; } elseif ($type == 'NS') { // check for trailing dot if (substr($content, - 1) == '.') { // remove it for checks $content = substr($content, 0, - 1); } if (! validateDomain($content)) { $errors[] = $lng['error']['dns_ns_invaliddom']; } // append trailing dot (again) $content .= '.'; } elseif ($type == 'TXT' && ! empty($content)) { // check that TXT content is enclosed in " " if (substr($content, 0, 1) != '"') { $content = '"' . $content; } if (substr($content, - 1) != '"') { $content .= '"'; } } elseif ($type == 'SRV') { if ($prio === null || $prio < 0) { $errors[] = $lng['error']['dns_srv_prioempty']; } // check for trailing dot if (substr($content, - 1) == '.') { // remove it for checks $content = substr($content, 0, - 1); } // check only last part of content, as it can look like: // _service._proto.name. TTL class SRV priority weight port target. $_split_content = explode(" ", $content); // SRV content must be [weight] [port] [target] if (count($_split_content) != 3) { $errors[] = $lng['error']['dns_srv_invalidcontent']; } $target = trim($_split_content[count($_split_content) - 1]); if (! validateDomain($target)) { $errors[] = $lng['error']['dns_srv_needdom']; } else { // check whether there is a CNAME-record for the same resource foreach ($dom_entries as $existing_entries) { $fqdn = $existing_entries['record'] . '.' . $domain; if ($existing_entries['type'] == 'CNAME' && $fqdn == $target) { $errors[] = $lng['error']['dns_srv_noalias']; break; } } } // append trailing dot (again) $content .= '.'; } $new_entry = array( 'record' => $record, 'type' => $type, 'prio' => $prio, 'content' => $content, 'ttl' => $ttl, 'domain_id' => $domain_id ); ksort($new_entry); // check for duplicate foreach ($dom_entries as $existing_entry) { // compare serialized string of array $check_entry = $existing_entry; // new entry has no ID yet unset($check_entry['id']); // sort by key ksort($check_entry); // format integer fields to real integer (as they are read as string from the DB) $check_entry['prio'] = (int) $check_entry['prio']; $check_entry['ttl'] = (int) $check_entry['ttl']; $check_entry['domain_id'] = (int) $check_entry['domain_id']; // serialize both $check_entry = serialize($check_entry); $new = serialize($new_entry); // compare if ($check_entry === $new) { $errors[] = $lng['error']['dns_duplicate_entry']; unset($check_entry); break; } } if (empty($errors)) { $ins_stmt = Database::prepare(" INSERT INTO `" . TABLE_DOMAIN_DNS . "` SET `record` = :record, `type` = :type, `prio` = :prio, `content` = :content, `ttl` = :ttl, `domain_id` = :domain_id "); Database::pexecute($ins_stmt, $new_entry); $new_entry_id = Database::lastInsertId(); // add temporary to the entries-array (no reread of DB necessary) $new_entry['id'] = $new_entry_id; $dom_entries[] = $new_entry; // success message (inline) $success_message = $lng['success']['dns_record_added']; unset($record); unset($type); unset($prio); unset($content); unset($ttl); } else { // show $errors $errors = implode("
", $errors); } } elseif ($action == 'delete') { // remove entry $entry_id = isset($_GET['id']) ? (int) $_GET['id'] : 0; if ($entry_id > 0) { $del_stmt = Database::prepare("DELETE FROM `" . TABLE_DOMAIN_DNS . "` WHERE `id` = :id"); Database::pexecute($del_stmt, array( 'id' => $entry_id )); // remove deleted entry from internal data array (no reread of DB necessary) $_t = $dom_entries; foreach ($_t as $idx => $entry) { if ($entry['id'] == $entry_id) { unset($dom_entries[$idx]); break; } } unset($_t); // success message (inline) $success_message = $lng['success']['dns_record_deleted']; } } // show editor $record_list = ""; $existing_entries = ""; $type_select = ""; $entriescount = 0; if (! empty($dom_entries)) { $entriescount = count($dom_entries); foreach ($dom_entries as $entry) { $entry['content'] = wordwrap($entry['content'], 100, '
', true); eval("\$existing_entries.=\"" . getTemplate("dns_editor/entry_bit", true) . "\";"); } } // available types $type_select_values = array( 'A', 'AAAA', 'NS', 'MX', 'SRV', 'TXT', 'CNAME' ); asort($type_select_values); foreach ($type_select_values as $_type) { $type_select .= makeoption($_type, $_type, $type); } eval("\$record_list=\"" . getTemplate("dns_editor/list", true) . "\";"); eval("echo \"" . getTemplate("dns_editor/index", true) . "\";");