Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dpd's inet dlz 20180419a #133

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
133 changes: 133 additions & 0 deletions docs/DPD-DLZ-INET-Chanages.md
Original file line number Diff line number Diff line change
@@ -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.%'}
{}";
};
```
132 changes: 132 additions & 0 deletions install/inet-functions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php
//
// This script is to be used when applying the inet[46]_[aton|ntoa] support for BIND-DLZ
// It will update all PTRs, so they can be served directly from the DNS tables
// Will add ONA unsupport DNS type of SOA in order to provide a complete solution
// for BIND DLZ without using views.
//
//
/* -------------------- COMMON HEADER ---------------------- */
$base = dirname(__FILE__)."/../www/";
while ($base and (!is_dir($base.'/include'))) $base = preg_replace('+/[^/]*$+', '', $base);
$include = $base . '/include';
if (!is_dir($include)) { print "ERROR => 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);

?>
5 changes: 5 additions & 0 deletions install/inet-functions.sql
Original file line number Diff line number Diff line change
@@ -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);
52 changes: 38 additions & 14 deletions www/config/config.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -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; }



Expand Down Expand Up @@ -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']);
Expand Down Expand Up @@ -212,37 +225,48 @@
// 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']);

// 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']}";
}
}
}

Expand Down
Loading