2.0) define('BIND_LOCALBASE', '/usr/pbi/bind-' . php_uname("m")); else define('BIND_LOCALBASE','/usr/local'); define('CHROOT_LOCALBASE','/cf/named'); function bind_zone_validate($post, &$input_errors){ if (key_exists("mail",$_POST)) $_POST['mail']=preg_replace("/@/",".",$post['mail']); switch ($_POST['type']){ case 'slave': if( $_POST['slaveip'] == "") $input_errors[] = 'The field \'Master Zone IP\' is required for slave zones.'; break; case 'forward': if( $_POST['forwarders'] == "") $input_errors[] = 'The field \'Forwarders\' is required for forward zones.'; break; case 'redirect': $_POST['tll']=300; $_POST['refresh']=0; $_POST['serial']=0; $_POST['retry']=0; $_POST['expire']=0; $_POST['minimum']=0; if($_POST['mail']=='') $input_errors[] = "The field 'Mail Admin Zone' is required for {$_POST['type']} zones."; default: if($_POST['nameserver']=='') $input_errors[] = "The field 'Name server' is required for {$_POST['type']} zones."; for ($i=0;$i < count($_POST);$i++){ if (key_exists("hostname$i",$_POST)){ if ($_POST['reverso']=="on"){ $_POST["hostvalue$i"]=""; if (!preg_match("/(PTR|NS)/",$_POST["hosttype$i"])) $input_errors[] = 'On reverse zones, valid record types are NS or PTR'; } if (preg_match("/(MX|NS)/",$_POST["hosttype$i"])) $_POST["hostname$i"]=""; if (!preg_match("/(MX|NS)/",$_POST["hosttype$i"]) && $_POST["hostname$i"]=="") $input_errors[] = 'Record cannot be empty for '.$_POST["hosttype$i"].' type '; if ($_POST["hosttype$i"]=="MX" && $_POST["hostvalue$i"]=="") $_POST["hostvalue$i"]="10"; if ($_POST["hosttype$i"]!="MX" && $_POST["hostvalue$i"]!="") $_POST["hostvalue$i"]=""; if ($_POST["hostdst$i"]=="") $input_errors[] = 'Alias or IP address cannot be empty.'; } } } } function bind_sync(){ global $config; conf_mount_rw(); //create rndc $rndc_confgen="/usr/local/sbin/rndc-confgen"; if (!file_exists(BIND_LOCALBASE."/etc/rndc-confgen.pfsense") && file_exists($rndc_confgen)){ exec("$rndc_confgen ",$rndc_conf); foreach($rndc_conf as $line) $confgen_file.="$line\n"; file_put_contents(BIND_LOCALBASE."/etc/rndc-confgen.pfsense",$confgen_file); } if (file_exists(BIND_LOCALBASE."/etc/rndc-confgen.pfsense")){ $rndc_conf=file(BIND_LOCALBASE."/etc/rndc-confgen.pfsense"); $confgen="rndc.conf"; $rndc_bindconf=""; foreach ($rndc_conf as $line){ if ($confgen =="rndc.conf"){ if (!preg_match ("/^#/",$line)) $rndc_file.=$line; } else{ if (!preg_match ("/named.conf/",$line)) $rndc_bindconf.=preg_replace('/#/',"",$line); } if (preg_match("/named.conf/",$line)){ $confgen="named.conf"; file_put_contents(BIND_LOCALBASE."/etc/rndc.conf",$rndc_file); } } } $bind = $config["installedpackages"]["bind"]["config"][0]; $bind_enable = $bind['enable_bind']; $bind_forwarder = $bind['bind_forwarder']; $forwarder_ips = $bind['bind_forwarder_ips']; $ram_limit = ($bind['bind_ram_limit']?$bind['bind_ram_limit']:"256M"); $hide_version = $bind['bind_hide_version']; $bind_notify = $bind['bind_notify']; $custom_options = base64_decode($bind['bind_custom_options']); $bind_logging = $bind['bind_logging']; $bind_conf ="#Bind pfsense configuration\n"; $bind_conf .="#Do not edit this file!!!\n\n"; $bind_conf .= "$rndc_bindconf\n"; $bind_conf .= <<$bind_listenonv6 $bind_listenon"; if (key_exists("ipv6allow",$config['system'])){ $bind_conf .="\t\tlisten-on-v6 { $bind_listenonv6 };\n"; } $bind_conf .="\t\tlisten-on { $bind_listenon };\n"; #forwarder config if ($bind_forwarder == on) $bind_conf .="\t\tforwarders { $forwarder_ips };\n"; if ($bind_notify == on) $bind_conf .="\t\tnotify yes;\n"; if ($hide_version == on) $bind_conf .="\t\tversion none;\n"; $bind_conf .= preg_replace("/^/m","\t\t",$custom_options); $bind_conf .= "\n\t};\n\n"; if ($bind_logging == on){ //check if bind is included on syslog $syslog_files=array("/etc/inc/system.inc","/var/etc/syslog.conf"); $restart_syslog=0; foreach ($syslog_files as $syslog_file){ $syslog_file_data=file_get_contents($syslog_file); if ( !preg_match("/dnsmasq,named,filterdns/",$syslog_file_data) || !preg_match("/'dnsmasq','named','filterdns'/",$syslog_file_data) ) { $syslog_file_data=preg_replace("/dnsmasq,filterdns/","dnsmasq,named,filterdns",$syslog_file_data); $syslog_file_data=preg_replace("/'dnsmasq','filterdns'/","'dnsmasq','named','filterdns'",$syslog_file_data); file_put_contents($syslog_file,$syslog_file_data); $restart_syslog++; } } if ($restart_syslog > 0){ system("/usr/bin/killall -HUP syslogd"); } $log_categories=explode(",",$bind['log_options']); $log_severity=($bind['log_severity']?$bind['log_severity']:'default'); if (sizeof($log_categories) > 0 && $log_categories[0]!=""){ $bind_conf .= <<"none","description"=>"BIND Built-in ACL","row"=>array("value"=>"","description"=>"")); $config["installedpackages"]["bindacls"]["config"][] = array("name"=>"any","description"=>"BIND Built-in ACL","row"=>array("value"=>"","description"=>"")); $config["installedpackages"]["bindacls"]["config"][] = array("name"=>"localhost","description"=>"BIND Built-in ACL","row"=>array("value"=>"","description"=>"")); $config["installedpackages"]["bindacls"]["config"][] = array("name"=>"localnets","description"=>"BIND Built-in ACL","row"=>array("value"=>"","description"=>"")); write_config("Create BIND Built-in ACLs"); } $bindacls = $config["installedpackages"]["bindacls"]["config"]; for ($i=0; $i $dhcpifconf) { if (!isset($dhcpifconf['enable']) || !is_array($dhcpifconf['staticmap'])) { continue; } foreach ($dhcpifconf['staticmap'] as $host) { if (is_domain($host['domain'])) { $domain = $host['domain']; } elseif (is_domain($dhcpifconf['domain'])) { $domain = $dhcpifconf['domain']; } elseif (is_domain($config['system']['domain'])) { $domain = $config['system']['domain']; } else { continue; } if (!is_hostname($host['hostname']) || !is_ipaddr($host['ipaddr'])) { continue; } if ($zonereverso == "on") { $parts = explode('.',$host['ipaddr']); $intersect = array_intersect_assoc($parts,$zoneparts); if (count($zoneparts) == count($intersect)) { $diff = array_diff_assoc($parts,$zoneparts); $shortaddr = implode('.',array_reverse($diff)); $zone_conf .= "{$shortaddr}\tIN PTR\t{$host['hostname']}.{$domain}.\n"; } } else { $parts = array_reverse(explode('.',$domain)); $diff = array_diff_assoc($parts,$zoneparts); if (count($diff) == 0) { $zone_conf .= "{$host['hostname']}\tIN A\t{$host['ipaddr']}\n"; } } } } } if ($zone['customzonerecords']!=""){ $zone_conf .= "\n\n;\n;custom zone records\n;\n".base64_decode($zone['customzonerecords'])."\n"; } file_put_contents(CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview/$zonename.DB", $zone_conf); $config["installedpackages"]["bindzone"]["config"][$x][resultconfig]=base64_encode($zone_conf); $write_config++; //check dnssec keys creation for master zones if($zone['dnssec']=="on"){ $zone_found=0; foreach (glob(CHROOT_LOCALBASE."/etc/namedb/keys/*{$zonename}*key",GLOB_NOSORT) as $filename){ $zone_found++; } if ($zone_found==0){ $key_restored=0; if(is_array($config['installedpackages']['dnsseckeys']) && is_array($config['installedpackages']['dnsseckeys']['config'])){ foreach ($config['installedpackages']['dnsseckeys']['config']as $filer) if (preg_match ("/K$zonename\.+/",$filer['fullfile'])){ file_put_contents($filer['fullfile'],base64_decode($filer['filedata']),LOCK_EX); chmod($filer['fullfile'],0700); chown($filer['fullfile'],"bind"); $key_restored++; } } if ($key_restored > 0){ log_error("[bind] {$key_restored} DNSSEC keys restored from XML backup for {$zonename} zone."); } $dnssec_bin="/usr/local/sbin/dnssec-keygen"; if (file_exists($dnssec_bin) && $key_restored==0){ exec("{$dnssec_bin} -K ".CHROOT_LOCALBASE."/etc/namedb/keys {$zonename}",$kout); exec("{$dnssec_bin} -K ".CHROOT_LOCALBASE."/etc/namedb/keys -fk {$zonename}",$kout); foreach($kout as $filename){ chown(CHROOT_LOCALBASE."/etc/namedb/keys/{$filename}.key","bind"); chown(CHROOT_LOCALBASE."/etc/namedb/keys/{$filename}.private","bind"); } log_error("[bind] DNSSEC keys for {$zonename} created."); } } //get ds keys $dsfromkey="/usr/local/sbin/dnssec-dsfromkey"; foreach (glob(CHROOT_LOCALBASE."/etc/namedb/keys/*{$zonename}*key",GLOB_NOSORT) as $filename) { $zone_key=file_get_contents($filename); if (preg_match("/IN DNSKEY 257 /",$zone_key) && file_exists($dsfromkey)){ exec("$dsfromkey $filename",$dsset); $config["installedpackages"]["bindzone"]["config"][$x]['dsset']=base64_encode(array_pop($dsset)."\n".array_pop($dsset)); $write_config++; } } //save dnssec keys to xml if($zone['backupkeys']=="on"){ $dnssec_keys=0; foreach (glob(CHROOT_LOCALBASE."/etc/namedb/keys/*{$zonename}*",GLOB_NOSORT) as $filename){ $file_found=0; if(is_array($config['installedpackages']['dnsseckeys']) && is_array($config['installedpackages']['dnsseckeys']['config'])){ foreach ($config['installedpackages']['dnsseckeys']['config']as $filer){ if ($filer['fullfile']==$filename) $file_found++; } } if ($file_found==0){ $config['installedpackages']['dnsseckeys']['config'][]=array('fullfile'=> $filename, 'description'=> "bind {$zonename} DNSSEC backup file", 'filedata'=> base64_encode(file_get_contents($filename))); $write_config++; $dnssec_keys++; } } if($dnssec_keys>0){ log_error("[bind] {$dnssec_keys} DNSSEC keys for {$zonename} zone saved on XML config."); } } } break; case "slave": //check/update slave dir permission chown(CHROOT_LOCALBASE."/etc/namedb/$zonetype","bind"); chown(CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview","bind"); //check if exists slave zone file $rsconfig=""; if ($zone['dnssec']=="on"){ if (file_exists(CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview/$zonename.DB.signed")) exec("/usr/local/sbin/named-checkzone -D -f raw -o - {$zonename} ".CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview/$zonename.DB.signed",$slave_file); } else{ if (file_exists(CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview/$zonename.DB")) $slave_file=file(CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview/$zonename.DB"); } if (is_array($slave_file)){ foreach ($slave_file as $zfile) $rsconfig.= $zfile; $config["installedpackages"]["bindzone"]["config"][$x][resultconfig]=base64_encode($rsconfig); $write_config++; } break; } } } if (!$custom_root_zone[$i]){ $bind_conf .="\tzone \".\" {\n"; $bind_conf .="\t\ttype hint;\n"; $bind_conf .="\t\tfile \"/etc/namedb/named.root\";\n"; $bind_conf .= "\t};\n\n"; } if($write_config > 0){ write_config("save result config file for zone on xml"); } $bind_conf .= "};\n"; } $dirs=array("/etc/namedb/keys","/var/run/named","/var/dump","/var/log","/var/stats","/dev"); foreach ($dirs as $dir){ if (!is_dir(CHROOT_LOCALBASE .$dir)) mkdir(CHROOT_LOCALBASE .$dir,0755,true); } //dev dirs for chroot $bind_dev_dir=CHROOT_LOCALBASE."/dev"; if (!file_exists("$bind_dev_dir/random")){ $dev_dirs=array("null","zero","random","urandom"); exec("/sbin/mount -t devfs devfs {$bind_dev_dir}",$dout); exec("/sbin/devfs -m {$bind_dev_dir} ruleset 1",$dout); exec("/sbin/devfs -m {$bind_dev_dir} rule add hide",$dout); foreach ($dev_dirs as $dev_dir) exec("/sbin/devfs -m {$bind_dev_dir} rule add path $dev_dir unhide",$dout); exec("/sbin/devfs -m {$bind_dev_dir} rule applyset",$dout); } //http://www.unixwiz.net/techtips/bind9-chroot.html file_put_contents(CHROOT_LOCALBASE.'/etc/namedb/named.conf', $bind_conf); file_put_contents(CHROOT_LOCALBASE.'/etc/namedb/rndc.conf', $rndc_file); if (!file_exists(CHROOT_LOCALBASE."/etc/namedb/named.root")){ //dig +tcp @a.root-servers.net > CHROOT_LOCALBASE."/etc/namedb/named.root" $named_root=file_get_contents("http://www.internic.net/domain/named.root"); file_put_contents(CHROOT_LOCALBASE."/etc/namedb/named.root",$named_root,LOCK_EX); } if (!file_exists(CHROOT_LOCALBASE."/etc/localtime")){ copy("/etc/localtime", CHROOT_LOCALBASE."/etc/localtime"); } bind_write_rcfile(); chown(CHROOT_LOCALBASE."/etc/namedb/keys","bind"); chown(CHROOT_LOCALBASE."/etc/namedb","bind"); chown(CHROOT_LOCALBASE."/var/log","bind"); chown(CHROOT_LOCALBASE."/var/run/named","bind"); chgrp(CHROOT_LOCALBASE."/var/log","bind"); $bind_sh="/usr/local/etc/rc.d/named.sh"; if($bind_enable == "on"){ chmod ($bind_sh,0755); mwexec("{$bind_sh} restart"); } elseif (is_service_running('named')){ mwexec("{$bind_sh} stop"); chmod ($bind_sh,0644); } //sync to backup servers bind_sync_on_changes(); conf_mount_ro(); } function bind_print_javascript_type_zone(){ ?> on_type_zone_changed();document.iform.resultconfig.disabled = 1;document.iform.dsset.disabled = 1;\n"); } function bind_write_rcfile() { $rc = array(); $BIND_LOCALBASE = "/usr/local"; $rc['file'] = 'named.sh'; $rc['start'] = <</dev/null sleep 2 EOD; $rc['restart'] = <</dev/null sleep 3 {$BIND_LOCALBASE}/sbin/named -c /etc/namedb/named.conf -u bind -t /cf/named/ fi EOD; conf_mount_rw(); write_rcfile($rc); conf_mount_ro(); } /* Uses XMLRPC to synchronize the changes to a remote node */ function bind_sync_on_changes() { global $config, $g; if (is_array($config['installedpackages']['bindsync']['config'])){ $bind_sync=$config['installedpackages']['bindsync']['config'][0]; $synconchanges = $bind_sync['synconchanges']; $synctimeout = $bind_sync['synctimeout']; $master_zone_ip=$bind_sync['masterip']; switch ($synconchanges){ case "manual": if (is_array($bind_sync[row])){ $rs=$bind_sync[row]; } else{ log_error("[bind] xmlrpc sync is enabled but there is no hosts to push on bind config."); return; } break; case "auto": if (is_array($config['hasync'])){ $hasync=$config['hasync'][0]; $rs[0]['ipaddress']=$hasync['synchronizetoip']; $rs[0]['username']=$hasync['username']; $rs[0]['password']=$hasync['password']; } else{ log_error("[bind] xmlrpc sync is enabled but there is no system backup hosts to push bind config."); return; } break; default: return; break; } if (is_array($rs)){ log_error("[bind] xmlrpc sync is starting."); foreach($rs as $sh){ $sync_to_ip = $sh['ipaddress']; $password = $sh['password']; if($sh['username']) $username = $sh['username']; else $username = 'admin'; if($password && $sync_to_ip) bind_do_xmlrpc_sync($sync_to_ip, $username, $password,$synctimeout,$master_zone_ip); } log_error("[bind] xmlrpc sync is ending."); } } } /* Do the actual XMLRPC sync */ function bind_do_xmlrpc_sync($sync_to_ip, $username, $password, $synctimeout,$master_zone_ip) { global $config, $g; if(!$username) return; if(!$password) return; if(!$sync_to_ip) return; if(!$synctimeout) $synctimeout=25; $xmlrpc_sync_neighbor = $sync_to_ip; if($config['system']['webgui']['protocol'] != "") { $synchronizetoip = $config['system']['webgui']['protocol']; $synchronizetoip .= "://"; } $port = $config['system']['webgui']['port']; /* if port is empty lets rely on the protocol selection */ if($port == "") { if($config['system']['webgui']['protocol'] == "http") $port = "80"; else $port = "443"; } $synchronizetoip .= $sync_to_ip; /* xml will hold the sections to sync */ $xml = array(); $xml['bind'] = $config['installedpackages']['bind']; $xml['bindacls'] = $config['installedpackages']['bindacls']; $xml['bindviews'] = $config['installedpackages']['bindviews']; $xml['bindzone'] = $config['installedpackages']['bindzone']; if (is_array($config['installedpackages']['dnsseckeys'])) $xml['dnsseckeys']=$config['installedpackages']['dnsseckeys']; //change master zone to slave on backup servers if(is_array($xml['bindzone']["config"])) for ($x=0; $xsetCredentials($username, $password); if($g['debug']) $cli->setDebug(1); /* send our XMLRPC message and timeout after defined sync timeout value*/ $resp = $cli->send($msg, $synctimeout); if(!$resp) { $error = "A communications error occurred while attempting BIND XMLRPC sync with {$url}:{$port}."; log_error($error); file_notice("sync_settings", $error, "bind Settings Sync", ""); } elseif($resp->faultCode()) { $cli->setDebug(1); $resp = $cli->send($msg, $synctimeout); $error = "An error code was received while attempting BIND XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString(); log_error($error); file_notice("sync_settings", $error, "bind Settings Sync", ""); } else { log_error("[bind] XMLRPC sync successfully completed with {$url}:{$port}."); } /* tell bind to reload our settings on the destination sync host. */ $method = 'pfsense.exec_php'; $execcmd = "require_once('/usr/local/pkg/bind.inc');\n"; $execcmd .= "bind_sync('yes');"; /* assemble xmlrpc payload */ $params = array( XML_RPC_encode($password), XML_RPC_encode($execcmd) ); log_error("[bind] XMLRPC reload data {$url}:{$port}."); $msg = new XML_RPC_Message($method, $params); $cli = new XML_RPC_Client('/xmlrpc.php', $url, $port); $cli->setCredentials($username, $password); $resp = $cli->send($msg, $synctimeout); if(!$resp) { $error = "A communications error occurred while attempting BIND XMLRPC sync with {$url}:{$port} (pfsense.exec_php)."; log_error($error); file_notice("sync_settings", $error, "Bind Settings Sync", ""); } elseif($resp->faultCode()) { $cli->setDebug(1); $resp = $cli->send($msg, $synctimeout); $error = "[Bind] An error code was received while attempting BIND XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString(); log_error($error); file_notice("sync_settings", $error, "bind Settings Sync", ""); } else { log_error("BIND XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php)."); } } ?>