diff --git a/docs/DPD-DLZ-INET-Chanages.md b/docs/DPD-DLZ-INET-Chanages.md new file mode 100644 index 00000000..091983db --- /dev/null +++ b/docs/DPD-DLZ-INET-Chanages.md @@ -0,0 +1,133 @@ +OpenNetAdmin - dpd's BIND-DLZ w/ v6 and INET function compatibility +============ + +ONA allowed for BIND-DLZ extension to perform dynamic lookups via your +ONA MySQL database. However, this didn't support PTR (very well) or V6 +and required a really convoluted view that made it very difficult to fix +the v6 et al issues that arrose. + +So, I have design a very small delta patch, that should allow for v6 and +and both v4 and v6 PTR, NS, MX and SOA records to be severed correctly, +with a horrible long, but easier to use query. + +The PHP code is changes, but two install scripts need to be run manually. + +--- +**Changes** + + * `install/inet-functions.sql` + Very minimal schema changes, just adding one column to interfaces, for the VARBINARY(16), so the the inet6_atoi/inet6_ntoi and php's inet_pton/inet_ntop can be used. Also adds three indexes for fields that DLZ uses to search by. + * `install/inet-functions.php` base script copied from 2-to-3.php. This will do a few things + * Finds/Creates all domains and creates a meta "SOA" type row in the DNS table. Because of how DLZ queries (without a "type", just the string of lookup) - it is much easier to have this via one table, and use cases in the sql to change up the data formating. So a fake SOA record is here, pretty much just linking the domain ID and type together. All other fields are ignored. Doesn't break backwards compat, ONA seems to ignore the SOA type. Code changes provides the creation of SOAs upon domain creation. + * Updates the dns.name of all PTR records. Again, how Bind DLZ does queries, there was no good way to do reverse lookups. ONA already ignored the dns.name field, for v4 PTRs, so, in this case overloading dns.name with the last two octets. Again, a strange things with BIND-DLZ ... is it will not lookup the first octet + in-arpa for the domain name ($zone$, ex 10.in-addr.arpa, but it will search 2.10.in-addr.arpa ) and DLZ will only successfully search the first octet of .ip6.arpa), with the $record$ slowly building out in reverse octets. + * add in all two-octet in-addr.arpa domains, as encounter by IP. Because of the above, will auto create all the needed in-addr.arpa for v4. Should also handle v6 as well. + * Populate the ip_addr_inet field. Will scan all interfaces, and update the IP address for both v4 and v6. ip_addr_inet is not used by ONA, other than to updated durning edit or insert (code changes provided). + +**Notable Code Changes** + * Fixed undefined PHP warnings using isset() checks, especially running in CLI. It was hard to see my own debugging output. By far, not extensive fixes, but proper PHP syntax/usage/style. + * Allow AAAA to be AAAA. Removed a few stops where v6 quad-a records are remapped to A. Was required for some reason. + * random PHP 7.x fix. (functions_gui.inc.php) (Testing & developed using PHP 7.2.3 on FreeBSD, w/ file based sessions) + * modules/ona/domain.inc.php - adds the SOA dns record. + * modules/ona/interface.inc.php - populates and updates `ip_addr_inet` base on ip_addr & ip_mangle() + * modules/ona/dns_record.inc.php - various tweaks for v6, as well as setting dns.name for PTR records. + + + +--- +**named.conf for BIND-DLZ** +The machine running bind-dlz, if you change /etc/resolv.conf to +point to localhost, you may want to use an IP address for the host. + +And yes, this format and syntax below works with Bind 9.12.0 and FreeBSD 11.1-stable. + +NOTE: Zone Transfers haven't be tested yet. + +``` +dlz "ONA Default" { + database "mysql + {host=a.b.c.d dbname=ona_ixsystems user=ona pass=xxx ssl=true} + {select name as zone from domains where name = '$zone$' limit 1} +{select + case + when dns.ttl = 0 + then domains.default_ttl + end as ttl, + dns.type as type, + CASE + when lower(dns.type)='mx' + then + dns.mx_preference + else '' + end as mx_priority, + case + when lower(dns.type)='a' or lower(dns.type)='aaaa' + then inet6_ntoa(interfaces.ip_addr_inet) + when lower(dns.type) in ( 'ptr', 'cname', 'mx', 'ns') + then (select + CASE + WHEN SUBSTRING(dns2.name, -1) = '.' + THEN dns2.name + ELSE concat(dns2.name, '.', domains.name, '.') + end + from dns as dns2 inner join domains on domains.id = dns2.domain_id where dns.dns_id = dns2.id) + when lower(dns.type)='txt' + then concat('\"', dns.txt, '\"') + when lower(dns.type)='srv' + then concat('\"', + srv_pri ,' ', srv_weight,' ', srv_port, ' ', + concat ( dns.name, '.' , domains.name), + '\"') + when lower(dns.type) in ('mx', 'ns') + then (select concat(dns2.name, '.', domains.name, '.') from dns as dns2 inner join domains on domains.id = dns2.domain_id where dns.dns_id = dns2.id) + else concat(dns.name, '.' , domains.name) + end as data + from interfaces, dns, domains + where + dns.name = if ('$record$' like '@', '', '$record$') + and domains.name = '$zone$' + and dns.interface_id = interfaces.id + and dns.domain_id = domains.id + and upper(dns.type) not in ('SOA', 'NS')} + {select + case + when dns.ttl = 0 + then domains.default_ttl + end as ttl, + dns.type as type, + CASE + when lower(dns.type)='mx' + then + dns.mx_preference + else '' + end as mx_priority, + case + when lower(dns.type) in ( 'ptr', 'cname', 'mx', 'ns') + then (select + CASE + WHEN SUBSTRING(dns2.name, -1) = '.' + THEN dns2.name + ELSE concat(dns2.name, '.', domains.name, '.') + end + from dns as dns2 inner join domains on domains.id = dns2.domain_id where dns.dns_id = dns2.id) + when lower(dns.type) in ('soa') + then concat( + domains.primary_master, '. ', + domains.admin_email, '. ', + domains.serial, ' ', + domains.refresh, ' ', + domains.retry, ' ', + domains.expiry, ' ', + domains.minimum ) + else concat(dns.name, '.' , domains.name) + end as data + from interfaces, dns, domains + where + domains.name = '$zone$' + and dns.interface_id = interfaces.id + and dns.domain_id = domains.id + and upper(dns.type) in ('SOA', 'NS') order by type DESC} + {} + {select name as zone from domains where name = '$zone$' and '$client$' like '10.%'} + {}"; +}; +``` \ No newline at end of file diff --git a/install/inet-functions.php b/install/inet-functions.php new file mode 100644 index 00000000..d0f89214 --- /dev/null +++ b/install/inet-functions.php @@ -0,0 +1,132 @@ + Couldn't find include folder {$include}!\n"; exit; } +require_once($base . '/config/config.inc.php'); +/* --------------------------------------------------------- */ + +global $conf, $self, $onadb; + +// Uncomment the following to get a ton o' debug +//$conf['debug'] = 6; +echo "Checking for SOA meta records:\n"; + +list($status, $rows, $domains) = db_get_records($onadb, 'domains', 'id > 0'); +#print_r ( $domains ); +foreach ( $domains as $d ) { + list($status, $rows, $soa) = db_get_records($onadb, 'dns', "type = 'SOA' and domain_id = '" . $d['id'] . "' "); + if ( $rows == 0 ) { + printf ("{%36s} - doesn't have an SOA DNS Record. \n", $d['name'] ); + // Add the dns record + $dns_id = ona_get_next_id('dns'); + list($status, $rows) = db_insert_record( + $onadb, + 'dns', + array( + 'id' => $dns_id, + 'domain_id' => $d['id'], + 'interface_id' => 0, + 'dns_id' => 0, + 'type' => 'SOA', + 'ttl' => 0, + 'name' => '', + 'mx_preference' => '0', + 'txt' => '', + 'srv_pri' => 0, + 'srv_weight' => 0, + 'srv_port' => 0, + 'notes' => '', + 'dns_view_id' => 0 + ) + ); + if ($status or !$rows) { + printf ("{%36s} - SOA DNS Record insert FAILED. \n", $d['name'] ); + } else { + printf ("{%36s} - SOA DNS Record created. \n", $d['name'] ); + } + + } else { + printf ("{%36s} - has an SOA DNS Record. \n", $d['name'] ); + } +} + +// Get the PTR records that dont have a domain_id +list($status, $rows, $ptrs) = db_get_records($onadb, 'dns', "type = 'PTR'", ''); +echo "Found {$rows} PTR with ptr records - will update all of them\n"; + +foreach ($ptrs as $ptr) { + list($status, $rows, $interface) = ona_get_interface_record(array('id' => $ptr['interface_id'])); + + // Print an error if it doesnt find an IP + if (!$interface['ip_addr']) { + echo "Possible orphan PTR record in dns table at ID: {$ptr['id']}. You should delete this record manually.\n"; + continue; + } + + $ipflip = ip_mangle($interface['ip_addr'],'flip'); + $octets = explode(".",$ipflip); + if (count($octets) > 4) { + $arpa = 'ip6.arpa'; + $octcount = 31; + $a = array_reverse ( $octets ); + $domain = implode (".", $a); + $_name = ''; + $domain = implode (".", array ($a[0], $arpa) ); + } else { + $arpa = 'in-addr.arpa'; + $octcount = 3; + $domain = $octets[2] . "." . $octets[3] . "." . $arpa; + $_name = $octets[0] . "." . $octets[1]; + } + // Find a pointer zone for this record to associate with. + echo " Searching for $domain \n"; + list($status, $prows, $ptrdomain) = ona_find_domain($domain); + echo " => Found for $domain => " . $ptrdomain['id'] . " " . $ptrdomain['name'] ."\n" ; + + // CRAPPY security cludge + $_SESSION['ona']['auth']['user']['username'] = 'PTRFIX'; + $_SESSION['ona']['auth']['perms']['advanced'] = 'Y'; + $_SESSION['ona']['auth']['perms']['host_modify'] = 'Y'; + + if (!$ptrdomain['id'] or ($domain != $ptrdomain['name']) ) { + echo " {$interface['ip_addr_text']}: Unable to find a pointer domain for this IP! Creating the following DNS domain: {$domain} \n"; + list($status, $output) = run_module('domain_add', array('name' => $domain)); + if ($status) { + echo "ERROR => {$output}\n"; + exit($status); + } + list($status, $rows, $ptrdomain) = ona_find_domain($domain); + } + + // Found a domain to put them in. + echo " Updating PTR for IP {$interface['ip_addr_text']} to $ipflip.in-addr.arpa\n"; + + // Change the actual DNS record + list($status, $rows) = db_update_record($onadb, 'dns', array('id' => $ptr['id']), array( 'name' => $_name, 'domain_id' => $ptrdomain['id'] )); + // if ($status or !$rows) { + // echo "ERROR => SQL Query failed updating dns record: " . $self['error'] . " $status \n" ; + // exit(2); + // } + + +} + +list($status, $rows, $interfaces) = db_get_records($onadb, 'interfaces', "ip_addr is not null", ''); +foreach ($interfaces as $interface ) { + echo " Adding inet6_atoi field " . $interface['id'] . "\n"; + list($status, $rows) = db_update_record($onadb, 'interfaces', array('id' => $interface['id']), array ('ip_addr_inet' => inet_format($interface['ip_addr']) )); + +} + +exit(0); + +?> diff --git a/install/inet-functions.sql b/install/inet-functions.sql new file mode 100644 index 00000000..b184c41c --- /dev/null +++ b/install/inet-functions.sql @@ -0,0 +1,5 @@ +alter table interfaces add `ip_addr_inet` varbinary(16) default NULL; +CREATE INDEX domains_name_index ON domains (name); +CREATE INDEX dns_name_index ON dns (name); +CREATE INDEX type_index ON dns (type); +INSERT INTO interfaces (id, subnet_id, host_id, nat_interface_id, ip_addr, mac_addr, name, description, last_response, ip_addr_inet) VALUES (0, 0, 0, 0, 0, '0', '0', '0', '2018-04-20 15:14:24', null); diff --git a/www/config/config.inc.php b/www/config/config.inc.php index 47be897a..d990b011 100644 --- a/www/config/config.inc.php +++ b/www/config/config.inc.php @@ -30,9 +30,9 @@ // Get any query info -parse_str($_SERVER['QUERY_STRING']); - - +if ( isset($_SERVER['QUERY_STRING']) ) { + parse_str($_SERVER['QUERY_STRING']); +} // Many of these settings serve as defaults. They can be overridden by the settings in // the table "sys_config" @@ -58,6 +58,7 @@ "inc_functions_gui" => "$include/functions_gui.inc.php", "inc_functions_db" => "$include/functions_db.inc.php", "inc_functions_auth" => "$include/functions_auth.inc.php", + "inc_functions_inetformat" => "$include/functions_inet_format.php", "inc_db_sessions" => "$include/adodb_sessions.inc.php", "inc_adodb" => "$include/adodb/adodb.inc.php", "inc_adodb_xml" => "$include/adodb/adodb-xmlschema03.inc.php", @@ -69,12 +70,23 @@ "plugin_dir" => "$base/local/plugins", /* Defaults for some user definable options normally in sys_config table */ - "debug" => "2", + "debug" => "0", "syslog" => "0", "stdout" => "0", "log_to_db" => "0", "logfile" => "/var/log/ona.log", + /* + Allow MX, NS and CNAME records to point to external domains + For example, this is needed for Gmail/GSuite MX Records. + */ + "allow_external_pointsto" => 0, + + /* + Allow Duplicate A,AAA records for Round-Robin DNS + */ + "allow_duplicate_arecords" => 0, + /* The output charset to be used in htmlentities() and htmlspecialchars() filtering */ "charset" => "utf8", "php_charset" => "UTF-8", @@ -105,7 +117,7 @@ ); // If the server port is 443 then this is a secure page // This is basically used to put a padlock icon on secure pages. -if ($_SERVER['SERVER_PORT'] == 443) { $self['secure'] = 1; } +if (isset ($_SERVER['SERVER_PORT']) && ($_SERVER['SERVER_PORT'] == 443) ) { $self['secure'] = 1; } @@ -168,6 +180,7 @@ // Include the basic system functions // any $conf settings used in this "require" should not be user adjusted in the sys_config table require_once($conf['inc_functions']); +require_once($conf['inc_functions_inetformat']); // Include the basic database functions require_once($conf['inc_functions_db']); @@ -212,12 +225,17 @@ // These will override any of the defaults set above list($status, $rows, $records) = db_get_records($onadb, 'sys_config', 'name like "%"', 'name'); foreach ($records as $record) { - printmsg("INFO => Loaded config item from database: {$record['name']}=''{$record['value']}''",5); $conf[$record['name']] = $record['value']; } +// Do this twice, so to reflect the sys_config database debug value; +if ( $conf['debug'] > 4 ) { + foreach ($records as $record) { + printmsg("INFO => Loaded config item from database: {$record['name']}=''{$record['value']}''",5); + } +} // Include functions that replace the default session handler with one that uses MySQL as a backend -require_once($conf['inc_db_sessions']); +# require_once($conf['inc_db_sessions']); // Include the GUI functions require_once($conf['inc_functions_gui']); @@ -225,24 +243,30 @@ // Include the AUTH functions require_once($conf['inc_functions_auth']); +// Set session inactivity threshold - before you start the session. +if ( isset ($_SERVER['REQUEST_METHOD']) ) { + ini_set("session.gc_maxlifetime", $conf['cookie_life']); +} + // Start the session handler (this calls a function defined in functions_general) startSession(); -// Set session inactivity threshold -ini_set("session.gc_maxlifetime", $conf['cookie_life']); // if search_results_per_page is in the session, set the $conf variable to it. this fixes the /rows command if (isset($_SESSION['search_results_per_page'])) $conf['search_results_per_page'] = $_SESSION['search_results_per_page']; // Set up our page to https if requested for our URL links -if (@($conf['force_https'] == 1) or ($_SERVER['SERVER_PORT'] == 443)) { +if (@($conf['force_https'] == 1) or (isset ($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443)) { $https = "https://{$_SERVER['SERVER_NAME']}"; } else { - if ($_SERVER['SERVER_PORT'] != 80) { - $https = "http://{$_SERVER['SERVER_NAME']}:{$_SERVER['SERVER_PORT']}"; - } else { - $https = "http://{$_SERVER['SERVER_NAME']}"; + if ( isset ($_SERVER['SERVER_PORT'] ) ) + { + if ( $_SERVER['SERVER_PORT'] != 80) { + $https = "http://{$_SERVER['SERVER_NAME']}:{$_SERVER['SERVER_PORT']}"; + } else { + $https = "http://{$_SERVER['SERVER_NAME']}"; + } } } diff --git a/www/include/functions_db.inc.php b/www/include/functions_db.inc.php index 5be4e9e6..5cb18fe4 100644 --- a/www/include/functions_db.inc.php +++ b/www/include/functions_db.inc.php @@ -1140,15 +1140,20 @@ function ona_get_domain_record($array='', $order='') { // Returns an additional "fqdn" field for some dns records function ona_get_dns_record($array='', $order='') { + global $conf; list($status, $rows, $record) = ona_get_record($array, 'dns', $order); - - if ($record['type'] == 'A' or $record['type'] == 'TXT') { - $record['fqdn'] = $record['name'].'.'.ona_build_domain_name($record['domain_id']); - $record['domain_fqdn'] = ona_build_domain_name($record['domain_id']); - } - if ($record['type'] == 'CNAME') { - $record['fqdn'] = $record['name'].'.'.ona_build_domain_name($record['domain_id']); - $record['domain_fqdn'] = ona_build_domain_name($record['domain_id']); + if ($record['type'] == 'A' or $record['type'] == 'AAAA' or $record['type'] == 'TXT' or $record['type'] == 'CNAME') { + if ( $conf['allow_external_pointsto'] && preg_match ( '/\.$/', $record['name']) ) + { + $record['name'] = preg_replace ( '/\.$/', '', $record['name'] ); + $record['fqdn'] = $record['name']; + $a = array_reverse ( explode(".", $record['name']) ) ; + $record['domain_fqdn_old'] = $a[2] . "." . $a[1]; + $record['domain_fqdn'] = ''; + } else { + $record['fqdn'] = $record['name'].'.'.ona_build_domain_name($record['domain_id']); + $record['domain_fqdn'] = ona_build_domain_name($record['domain_id']); + } } return(array($status, $rows, $record)); } @@ -1672,6 +1677,9 @@ function ona_find_domain($fqdn="", $returndefault=0) { // } // FIXME: MP rows is not right here.. need to look at fixing it.. rowsa/rowsb above doesnt translate.. do I even need that? + if ( !isset($rows) or !is_int($rows) ) { + $rows = 0; + } return(array($status, $rows, $domain)); } diff --git a/www/include/functions_general.inc.php b/www/include/functions_general.inc.php index 99adbffc..eb90b6f7 100644 --- a/www/include/functions_general.inc.php +++ b/www/include/functions_general.inc.php @@ -3,9 +3,18 @@ // Debugging: lets print what's in $_REQUEST -if ( 6 <= $conf['debug'] ) { - printmsg("Get/Post vars:", 6); - foreach (array_keys($_REQUEST) as $key) printmsg("Name: $key Value: $_REQUEST[$key]", 6); +if ( $conf['debug'] > 5 ) { + printmsg("Get/Post vars: . ", 6); + // this was getting "Array to string conversion" notice in PHP error logs. + // So, fix that for nested arrays, Serialize with JSON. + foreach ($_REQUEST as $key => $value) { + if ( is_array ($value) ) { + $v = json_encode ( $value ); + } else { + $v = $value; + } + printmsg("Name: $key Value: $v", 6); + } } // MP: moved this stuff to config.inc.php @@ -145,7 +154,18 @@ function ona_logmsg($message, $logfile="") { } // Build the exact line we want to write to the file - $logdata = date("M j G:i:s ") . "{$uname['nodename']} {$username}@{$_SERVER['REMOTE_ADDR']}: [{$self['context_name']}] {$message}\n"; + // Prevent Undefined index notice when running CLI. + if (isset ( $_SERVER['REMOTE_ADDR'] ) ) { + $_remote_addr = $_SERVER['REMOTE_ADDR']; + } else { + $_remote_addr = ''; + } + if ( isset($self['context_name']) ) { + $_cn = $self['context_name']; + } else { + $_cn = ''; + } + $logdata = date("M j G:i:s ") . "{$uname['nodename']} {$username}@{$_remote_addr}: [{$_cn}] {$message}\n"; // Write the line to the file if (!fwrite($file, $logdata)) { @@ -635,7 +655,10 @@ function ipv6gz($ip) { // If we get here, then the input must be in numeric format (1) else { - $ip = gmp_init(strval($ip), 10); + // FIXME: "PHP Warning: gmp_init(): Unable to convert variable to GMP" + // This happens when $ip == NULL, but fixing this, created even more warnings + // down the code. I'm going to suppress warnings with @. + $ip = @gmp_init(strval($ip), 10); if ($format == "default") { if(is_ipv4($ip)) $format = "dotted"; @@ -1222,7 +1245,7 @@ function startSession() { global $conf; // If the command line agent, dcm.pl, is making the request, don't really start a session. - if (preg_match('/console-module-interface/', $_SERVER['HTTP_USER_AGENT'])) { + if ( isset ($_SERVER['HTTP_USER_AGENT']) && preg_match('/console-module-interface/', $_SERVER['HTTP_USER_AGENT'])) { // Pretend to log them in if (preg_match('/unix_username=([^&]+?)(&|$)/', $_REQUEST['options'], $matches)) { diff --git a/www/include/functions_gui.inc.php b/www/include/functions_gui.inc.php index cd4f4162..dc4995ec 100644 --- a/www/include/functions_gui.inc.php +++ b/www/include/functions_gui.inc.php @@ -141,7 +141,7 @@ function workspace_plugin_loader($modulename, $record=array(), $extravars=array( global $conf, $self, $base, $images, $color, $style, $onadb; $modhtml = ''; $modjs = ''; - $modwsmenu = ''; + $modwsmenu = array(); $modbodyhtml = ''; $ws_plugin_dir = "{$base}/workspace_plugins"; @@ -350,7 +350,7 @@ function get_host_suggestions($q, $max_results=10) { list($status, $rows, $view) = db_get_record($onadb, 'dns_views', array('id' => $record['dns_view_id'])); $viewname = $view['name'].'/'; } - $results[] = $viewname.$record[$field].".".$domain['name']; + $results[] = $viewname.$record[$field].".".ona_build_domain_name ( $record['domain_id'] ); } } diff --git a/www/include/functions_inet_format.php b/www/include/functions_inet_format.php new file mode 100644 index 00000000..dd0be4fa --- /dev/null +++ b/www/include/functions_inet_format.php @@ -0,0 +1,17 @@ + diff --git a/www/modules/ona/dns_record.inc.php b/www/modules/ona/dns_record.inc.php index 7812d15b..2d14b400 100644 --- a/www/modules/ona/dns_record.inc.php +++ b/www/modules/ona/dns_record.inc.php @@ -136,7 +136,7 @@ function dns_record_add($options="") { $add_srv_port = 0; // force AAAA to A to keep it consistant.. we'll display it properly as needed - if ($options['type'] == 'AAAA') $options['type'] = 'A'; + // if ($options['type'] == 'AAAA') $options['type'] = 'A'; // If the name we were passed has a leading or trailing . in it then remove the dot. $options['name'] = preg_replace("/^\./", '', $options['name']); @@ -234,11 +234,13 @@ function dns_record_add($options="") { } // Validate that there isn't already any dns record named $hostname in the domain $domain_id. - list($d_status, $d_rows, $d_record) = ona_get_dns_record(array('name' => $hostname, 'domain_id' => $domain['id'],'interface_id' => $interface['id'],'type' => 'A', 'dns_view_id' => $add_viewid)); - if ($d_status or $d_rows) { + list($d_status, $d_rows, $d_record) = ona_get_dns_record(array('name' => $hostname, 'domain_id' => $domain['id'],'interface_id' => $interface['id'],'type' => $options['type'], 'dns_view_id' => $add_viewid)); + if (($d_status or $d_rows) and !$conf['allow_duplicate_arecords']) { printmsg("ERROR => Another DNS A record named {$hostname}.{$domain['fqdn']} with IP {$interface['ip_addr_text']} already exists!{$viewmsg}",3); $self['error'] = "ERROR => Another DNS A record named {$hostname}.{$domain['fqdn']} with IP {$interface['ip_addr_text']} already exists!{$viewmsg}"; return(array(5, $self['error'] . "\n")); + } else if ($conf['allow_duplicate_arecords']) { + printmsg("DEBUG => Allowing duplicate A/AAA records for {$hostname}.{$domain['fqdn']} with IP {$interface['ip_addr_text']}; Input : IP: " . $options['ip'] . "Hostname: $hostname ; domain:" . $domain['name'] , 3); } // Validate that there are no CNAMES already with this fqdn @@ -310,7 +312,16 @@ function dns_record_add($options="") { // Find the dns record that it will point to list($status, $rows, $arecord) = ona_get_dns_record(array('name' => $hostname, 'domain_id' => $domain['id'],'interface_id' => $interface['id'], 'type' => 'A','dns_view_id' => $add_viewid)); - if ($status or !$rows) { + list($status2, $rows2, $arecord2) = ona_get_dns_record(array('name' => $hostname, 'domain_id' => $domain['id'],'interface_id' => $interface['id'], 'type' => 'AAAA','dns_view_id' => $add_viewid)); + if ( !isset($arecord['id']) ) + { + if ( isset($arecord2['id'] ) ) { + $arecord = $arecord2; + $rows = $rows2; + $status = $status2; + } + } + if ($status or $status2 or !$rows or !$rows2) { printmsg("ERROR => Unable to find DNS A record to point PTR entry to! Check that the IP you chose is associated with the name you chose.{$viewmsg}",3); $self['error'] = "ERROR => Unable to find DNS A record to point PTR entry to! Check that the IP you chose is associated with the name you chose.{$viewmsg}"; @@ -320,16 +331,15 @@ function dns_record_add($options="") { // MP: since there could be multiple A records, I'm going to fail out if there is not JUST ONE A record. // this is limiting in a way but allows cleaner data. list($status, $rows, $arecord) = ona_get_dns_record(array('name' => $hostname, 'domain_id' => $domain['id'], 'type' => 'A','dns_view_id' => $add_viewid)); - if (($rows > 1)) { + if (($rows > 1) and !$conf['allow_duplicate_arecords']) { printmsg("ERROR => Unable to find a SINGLE DNS A record to point PTR entry to! In this case, you are only allowed to do this if there is one A record using this name.{$viewmsg}",3); $self['error'] = "ERROR => Unable to find a SINGLE DNS A record to point PTR entry to! In this case, you are only allowed to do this if there is one A record using this name.{$viewmsg}"; } - if ($rows != 1) + if ($rows == 0) return(array(66, $self['error'] . "\n")); } - $ipflip = ip_mangle($interface['ip_addr'],'flip'); $octets = explode(".",$ipflip); if (count($octets) > 4) { @@ -353,7 +363,12 @@ function dns_record_add($options="") { } // PTR records dont need a name set. - $add_name = ''; + // but we are going to add it, so that Bind DLZ can query it + // name has to be the ip flip, sans the in-addr arap string and hex/octets of the IP. + + $add_name = preg_replace ('/\.' . $ptrdomain['name'] . '/', '', $ipflip.$arpa ); + // $add_name = $ipflip.$arpa; + // PTR records should not have domain_ids $add_domainid = $ptrdomain['id']; $add_interfaceid = $interface['id']; @@ -433,10 +448,44 @@ function dns_record_add($options="") { // Find the dns record that it will point to list($status, $rows, $pointsto_record) = ona_get_dns_record(array('name' => $phostname, 'domain_id' => $pdomain['id'], 'type' => 'A','dns_view_id' => $add_viewid)); + if ($status or !$rows) { - printmsg("ERROR => Unable to find DNS A record to point CNAME entry to!{$viewmsg}",3); - $self['error'] = "ERROR => Unable to find DNS A record to point CNAME entry to!{$viewmsg}"; - return(array(5, $self['error'] . "\n")); + if ( !$conf['allow_external_pointsto']) { + printmsg("ERROR => Unable to find DNS A record to point CNAME entry to!{$viewmsg}",3); + $self['error'] = "ERROR => Unable to find DNS A record to point CNAME entry to!{$viewmsg}"; + return(array(5, $self['error'] . "\n")); + } else { + printmsg("DEBUG => Allowing an external pointer, Using 'pointsto' as given, hostname: {$options['pointsto']}, for domain { " . $domain['id'] . ":" . $domain['name'] . " } Domain ID: {$pdomain['id']}", 3); + if ( !preg_match ( '/\.$/', $options['pointsto']) ) + { + $name = $options['pointsto'] . '.'; + } else { + $name = $options['pointsto']; + } + $dns_id = ona_get_next_id('dns'); + $pointsto_record['interface_id'] = 0; + $pointsto_record['id'] = $dns_id; + list($status, $rows) = db_insert_record( + $onadb, + 'dns', + array( + 'id' => $dns_id, + 'domain_id' => $domain['id'], + 'interface_id' => 0, + 'dns_id' => 0, + 'type' => 'A', + 'ttl' => 0, + 'name' => $name, + 'mx_preference' => '0', + 'txt' => '', + 'srv_pri' => 0, + 'srv_weight' => 0, + 'srv_port' => 0, + 'notes' => '', + 'dns_view_id' => 0 + ) + ); + } } @@ -502,12 +551,43 @@ function dns_record_add($options="") { // Validate that there are no NS already with this domain and host list($status, $rows, $record) = ona_get_dns_record(array('dns_id' => $pointsto_record['id'], 'domain_id' => $domain['id'],'type' => 'NS','dns_view_id' => $add_viewid)); if ($rows or $status) { - printmsg("ERROR => Another DNS NS record for {$domain['fqdn']} pointing to {$options['pointsto']} already exists!{$viewmsg}",3); - $self['error'] = "ERROR => Another DNS NS record for {$domain['fqdn']} pointing to {$options['pointsto']} already exists!{$viewmsg}"; - return(array(5, $self['error'] . "\n")); - } - - + if ( !$conf['allow_external_pointsto']) { + printmsg("ERROR => Another DNS NS record for {$domain['fqdn']} pointing to {$options['pointsto']} already exists!{$viewmsg}",3); + $self['error'] = "ERROR => Another DNS NS record for {$domain['fqdn']} pointing to {$options['pointsto']} already exists!{$viewmsg}"; + return(array(5, $self['error'] . "\n")); + } else { + printmsg("DEBUG => Allowing an external pointer, Using 'pointsto' as given, hostname: {$options['pointsto']}, for domain { " . $domain['id'] . ":" . $domain['name'] . " } Domain ID: {$pdomain['id']}", 3); + if ( !preg_match ( '/\.$/', $options['pointsto']) ) + { + $name = $options['pointsto'] . '.'; + } else { + $name = $options['pointsto']; + } + $dns_id = ona_get_next_id('dns'); + $pointsto_record['interface_id'] = 0; + $pointsto_record['id'] = $dns_id; + list($status, $rows) = db_insert_record( + $onadb, + 'dns', + array( + 'id' => $dns_id, + 'domain_id' => $domain['id'], + 'interface_id' => 0, + 'dns_id' => 0, + 'type' => 'A', + 'ttl' => 0, + 'name' => $name, + 'mx_preference' => '0', + 'txt' => '', + 'srv_pri' => 0, + 'srv_weight' => 0, + 'srv_port' => 0, + 'notes' => '', + 'dns_view_id' => 0 + ) + ); + } + } $add_name = ''; //$options['name']; $add_domainid = $domain['id']; $add_interfaceid = $pointsto_record['interface_id']; @@ -569,12 +649,44 @@ function dns_record_add($options="") { // Find the dns record that it will point to list($status, $rows, $pointsto_record) = ona_get_dns_record(array('name' => $phostname, 'domain_id' => $pdomain['id'], 'type' => 'A','dns_view_id' => $add_viewid)); if ($status or !$rows) { - printmsg("ERROR => Unable to find DNS A record to point NS entry to!{$viewmsg}",3); - $self['error'] = "ERROR => Unable to find DNS A record to point NS entry to!{$viewmsg}"; - return(array(5, $self['error'] . "\n")); + if ( !$conf['allow_external_pointsto'] ) { + printmsg("ERROR => Unable to find DNS A record to point NS entry to!{$viewmsg}",3); + $self['error'] = "ERROR => Unable to find DNS A record to point NS entry to!{$viewmsg}"; + return(array(5, $self['error'] . "\n")); + } else { + printmsg("DEBUG => Allowing an external pointer, Using 'pointsto' as given, hostname: {$options['pointsto']}, for domain { " . $domain['id'] . ":" . $domain['name'] . " } Domain ID: {$pdomain['id']}", 3); + if ( !preg_match ( '/\.$/', $options['pointsto']) ) + { + $name = $options['pointsto'] . '.'; + } else { + $name = $options['pointsto']; + } + $dns_id = ona_get_next_id('dns'); + $pointsto_record['interface_id'] = 0; + $pointsto_record['id'] = $dns_id; + list($status, $rows) = db_insert_record( + $onadb, + 'dns', + array( + 'id' => $dns_id, + 'domain_id' => $domain['id'], + 'interface_id' => 0, + 'dns_id' => 0, + 'type' => 'A', + 'ttl' => 0, + 'name' => $name, + 'mx_preference' => '0', + 'txt' => '', + 'srv_pri' => 0, + 'srv_weight' => 0, + 'srv_port' => 0, + 'notes' => '', + 'dns_view_id' => 0 + ) + ); + } } - $add_name = $hostname; $add_domainid = $domain['id']; $add_interfaceid = $pointsto_record['interface_id']; @@ -790,7 +902,7 @@ function dns_record_add($options="") { 'srv_port' => $add_srv_port, 'ebegin' => $options['ebegin'], 'notes' => $options['notes'], - 'dns_view_id' => $add_viewid + 'dns_view_id' => $add_viewid ) ); if ($status or !$rows) { @@ -802,7 +914,7 @@ function dns_record_add($options="") { $text = ''; // If it is an A record and they have specified to auto add the PTR record for it. - if ($options['addptr'] == 'Y' and $options['type'] == 'A') { + if ($options['addptr'] == 'Y' and ( $options['type'] == 'A' or $options['type'] == 'AAAA' ) ) { printmsg("DEBUG => Auto adding a PTR record for {$options['name']}.", 4); // Run dns_record_add as a PTR type list($status, $output) = run_module('dns_record_add', array('name' => $options['name'],'domain' => $domain['fqdn'],'ip' => $options['ip'],'ebegin' => $options['ebegin'],'type' => 'PTR','view' => $add_viewid)); @@ -951,7 +1063,7 @@ function dns_record_modify($options="") { $options['set_name'] = preg_replace("/^\./", '', $options['set_name']); // Find the DNS record from $options['name'] - list($status, $rows, $dns) = ona_find_dns_record($options['name']); + list($status, $rows, $dns) = ona_find_dns_record($options['server'] . "." . $domain['fqdn']); printmsg("DEBUG => dns_record_modify() DNS record: {$dns['fqdn']}", 3); if ($rows > 1) { printmsg("DEBUG => Found more than one DNS record for: {$options['name']}",3); @@ -1176,8 +1288,8 @@ function dns_record_modify($options="") { // Find the dns record that it will point to list($status, $rows, $pointsto_record) = ona_get_dns_record(array('name' => $phostname, 'domain_id' => $pdomain['id'], 'type' => 'A','dns_view_id' => $check_dns_view_id)); if ($status or !$rows) { - printmsg("ERROR => Unable to find DNS A record to point {$options['set_type']} entry to!{$viewmsg}",3); - $self['error'] = "ERROR => Unable to find DNS A record to point {$options['set_type']} entry to!{$viewmsg}"; + printmsg("ERROR => Unable to find DNS A record to point {$options['set_type']} entry to!{$viewmsg} " ,3); + $self['error'] = "ERROR => Unable to find DNS A record to point {$options['set_type']} entry to!{$viewmsg} "; return(array(5, $self['error'] . "\n")); } @@ -1249,7 +1361,7 @@ function dns_record_modify($options="") { // If it is an A record and they have specified to auto add the PTR record for it. - if ($options['set_addptr'] == 'Y' and $options['set_type'] == 'A') { + if ($options['set_addptr'] == 'Y' and ( $options['set_type'] == 'A' || $options['set_type'] == 'AAAA' ) ) { printmsg("DEBUG => Auto adding a PTR record for {$options['set_name']}.", 0); // Run dns_record_add as a PTR type // Always use the $current_name variable as the name might change during the update @@ -1346,7 +1458,7 @@ function dns_record_modify($options="") { // Make sure we us A type for both A and AAAA - if ($SET['type'] == 'AAAA') $SET['type'] = 'A'; + // if ($SET['type'] == 'AAAA') $SET['type'] = 'A'; // Change the actual DNS record list($status, $rows) = db_update_record($onadb, 'dns', array('id' => $dns['id']), $SET); diff --git a/www/modules/ona/domain.inc.php b/www/modules/ona/domain.inc.php index 9e8ab250..b534d92b 100644 --- a/www/modules/ona/domain.inc.php +++ b/www/modules/ona/domain.inc.php @@ -209,6 +209,34 @@ function domain_add($options="") { $self['error'] = "ERROR => domain_add() SQL Query failed: " . $self['error']; printmsg($self['error'],0); return(array(7, $self['error'] . "\n")); + } else { + // FIXME: HACK for Bind-DLZ - need an SOA record in + // interface ID doesn't matter + $dns_id = ona_get_next_id('dns'); + + // Add the dns record + list($status, $rows) = db_insert_record( + $onadb, + 'dns', + array( + 'id' => $dns_id, + 'domain_id' => $id, + 'interface_id' => 0, + 'dns_id' => 0, + 'type' => 'SOA', + 'ttl' => 0, + 'name' => '', + 'mx_preference' => '0', + 'txt' => '', + 'srv_pri' => 0, + 'srv_weight' => 0, + 'srv_port' => 0, + 'notes' => '', + 'dns_view_id' => 0 + ) + ); + + } diff --git a/www/modules/ona/domain_server.inc.php b/www/modules/ona/domain_server.inc.php index f187d910..b3e6ddda 100644 --- a/www/modules/ona/domain_server.inc.php +++ b/www/modules/ona/domain_server.inc.php @@ -80,7 +80,7 @@ function domain_server_add($options="") { printmsg("DEBUG => domain_server_add(): Found domain, {$domain['name']}", 3); // Determine the server is valid - list($status, $rows, $ns_dns) = ona_find_dns_record($options['server']); + list($status, $rows, $ns_dns) = ona_find_dns_record($options['server'] . "." . $domain['fqdn']); list($status, $rows, $interface) = ona_find_interface($ns_dns['interface_id']); $host['id'] = $interface['host_id']; @@ -169,7 +169,7 @@ function domain_server_add($options="") { if (!$dnsrows) { printmsg("DEBUG => Auto adding a NS record for {$options['server']}.", 0); // Run dns_record_add as a NS type - list($status, $output) = run_module('dns_record_add', array('name' => $domain['fqdn'],'pointsto' => $options['server'], 'type' => 'NS')); + list($status, $output) = run_module('dns_record_add', array('name' => $domain['fqdn'],'pointsto' => $options['server'] . "." . $domain['fqdn'], 'type' => 'NS')); if ($status) return(array($status, $output)); $add_to_error .= $output; diff --git a/www/modules/ona/interface.inc.php b/www/modules/ona/interface.inc.php index 83b722a8..23566cb5 100644 --- a/www/modules/ona/interface.inc.php +++ b/www/modules/ona/interface.inc.php @@ -196,6 +196,7 @@ interface_add-v{$version} 'host_id' => $host['id'], 'subnet_id' => $subnet['id'], 'ip_addr' => $options['ip'], + 'ip_addr_inet' => inet_format($options['ip']), 'mac_addr' => $options['mac'], 'name' => trim($options['name']), 'description' => trim($options['description']) @@ -477,8 +478,10 @@ interface=ID or IP or MAC interface ID or IP address // Everything looks ok, add it to $SET if($interface['subnet_id'] != $subnet['id']) $SET['subnet_id'] = $subnet['id']; - if($interface['ip_addr'] != $options['set_ip']) - $SET['ip_addr'] = $options['set_ip']; + if($interface['ip_addr'] != $options['set_ip']) { + $SET['ip_addr'] = $options['set_ip']; + $SET['ip_addr_inet'] = inet_format($options['set_ip']); + } } diff --git a/www/winc/display_domain.inc.php b/www/winc/display_domain.inc.php index 6ca8e8e7..7f4fea04 100644 --- a/www/winc/display_domain.inc.php +++ b/www/winc/display_domain.inc.php @@ -66,8 +66,18 @@ function ws_display($window_name, $form='') { EOL; // Escape data for display in html - foreach(array_keys($record) as $key) { $record[$key] = htmlentities($record[$key], ENT_QUOTES, $conf['php_charset']); } - foreach(array_keys((array)$parent_domain) as $key) { $parent_domain[$key] = htmlentities($parent_domain[$key], ENT_QUOTES, $conf['php_charset']); } + if (is_array ($record) ) { + foreach($record as $key => $v) { + $record[$key] = htmlentities($v, ENT_QUOTES, $conf['php_charset']); + } + } + if (is_array ($parent_domain) ) { + foreach($parent_domain as $key => $v) { + printmsg("DEBUG => {" . __FILE__ . ":" . __LINE__ . "} parent_domain key/value : " . $key . " : " . $value , 6); + // Cannot assign an empty string to a string offset + $parent_domain[$key] = htmlentities($v, ENT_QUOTES, $conf['php_charset']); + } + } $html .= << EOL; - if ($parent_domain['id']) { + if (isset ($parent_domain['id']) && $parent_domain['id']) { $html .= << Parent Domain