<?PHP /* $Id$ */ /* bind.inc part of the Bind package for pfSense Copyright (C) 2013 Juliano Oliveira/Adriano Brancher Copyright (C) 2013 Marcello Coutinho All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ $shortcut_section = "bind"; require_once('globals.inc'); require_once('config.inc'); require_once('util.inc'); require_once('pfsense-utils.inc'); require_once('pkg-utils.inc'); require_once('service-utils.inc'); if(!function_exists("filter_configure")) require_once("filter.inc"); $pf_version=substr(trim(file_get_contents("/etc/version")),0,3); if ($pf_version > 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 .= <<<EOD options { directory "/etc/namedb"; pid-file "/var/run/named/pid"; statistics-file "/var/log/named.stats"; max-cache-size {$ram_limit}; EOD; // check response rate limit option //https://kb.isc.org/article/AA-01000/0/A-Quick-Introduction-to-Response-Rate-Limiting.html //http://ss.vix.su/~vjs/rl-arm.html if ($bind['rate_enabled']=="on"){ $rate_limit=($bind['rate_limit']?$bind['rate_limit']:"15"); $log_only=($bind['log_only']=="no"?"no":"yes"); $bind_conf .= <<<EOD rate-limit { responses-per-second {$rate_limit}; log-only {$log_only}; }; EOD; } //check ips to listen on if (preg_match("/All/",$bind['listenon'])){ $bind_listenonv6="Any;"; $bind_listenon="Any;"; } else{ $bind_listenonv6=""; $bind_listenon =""; foreach (explode(',',$bind['listenon']) as $listenon){ if (is_ipaddrv6($listenon)) $bind_listenonv6 .= $listenon."; "; elseif (is_ipaddr($listenon)) $bind_listenon .= $listenon."; "; else{ $listenon=(pfSense_get_interface_addresses(convert_friendly_interface_to_real_interface_name($listenon))); if (is_ipaddr($listenon['ipaddr'])) $bind_listenon .= $listenon['ipaddr']."; "; if(is_ipaddrv6($listenon['ipaddr6'])) $bind_listenonv6 .= $listenon['ipaddr6']."; "; } } } $bind_listenonv6=($bind_listenonv6==""?"none;":$bind_listenonv6); $bind_listenon=($bind_listenon==""?"none;":$bind_listenon); //print "<PRE>$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 \"N/A\";\n"; $bind_conf .="\t\t$custom_options\n"; $bind_conf .= "\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 .= <<<EOD logging { channel custom { syslog daemon; print-time no; print-severity yes; print-category yes; severity {$log_severity}; }; EOD; foreach ($log_categories as $category) $bind_conf .="\t\t\tcategory $category\t{custom;};\n"; $bind_conf .="\t\t};\n\n"; } } else { $bind_conf .="\t\tlogging { category default { null; }; };\n\n"; } #Config Zone domain if(!is_array($config["installedpackages"]["bindacls"]) || !is_array($config["installedpackages"]["bindacls"]["config"])){ $config["installedpackages"]["bindacls"]["config"][] =array("name"=>"any","description"=>"Default Access list","row" => array("value"=> "","description"=>"")); write_config("Create Default bind acl 'Any'"); } $bindacls = $config["installedpackages"]["bindacls"]["config"]; for ($i=0; $i<sizeof($bindacls); $i++) { $aclname = $bindacls[$i]['name']; $aclhost = $bindacls[$i]['row']; if($aclname != "any"){ $bind_conf .= "acl \"$aclname\" {\n"; for ($u=0; $u<sizeof($aclhost); $u++) { $aclhostvalue = $aclhost[$u]['value']; $bind_conf .= "\t$aclhostvalue;\n"; } $bind_conf .= "};\n\n"; } } if(is_array($config["installedpackages"]["bindviews"])) $bindview = $config["installedpackages"]["bindviews"]["config"]; else $bindview =array(); for ($i=0; $i<sizeof($bindview); $i++) { $views = $config["installedpackages"]["bindviews"]["config"][$i]; $viewname = $views['name']; $viewrecursion = $views['recursion']; if($views['match-clients'] == '') $viewmatchclients = "none"; else $viewmatchclients = str_replace(',','; ',$views['match-clients']); if($views['allow-recursion'] == '') $viewallowrecursion = "none"; else $viewallowrecursion = str_replace(',','; ',$views['allow-recursion']); $viewcustomoptions = base64_decode($views['bind_custom_options']); $bind_conf .= "view \"$viewname\" { \n\n"; $bind_conf .= "\trecursion $viewrecursion;\n"; $bind_conf .= "\tmatch-clients { $viewmatchclients;};\n"; $bind_conf .= "\tallow-recursion { $viewallowrecursion;};\n"; $bind_conf .= "\t$viewcustomoptions\n\n"; if(is_array($config["installedpackages"]["bindzone"])) $bindzone = $config["installedpackages"]["bindzone"]["config"]; else $bindzone =array(); $write_config=0; for ($x=0; $x<sizeof($bindzone); $x++) { $zone = $bindzone[$x]; if ($zone['disabled']=="on"){ continue; } $zonename = $zone['name']; if ($zonename=="."){ $custom_root_zone[$i]=true; } $zonetype = $zone['type']; $zoneview = $zone['view']; $zonecustom = base64_decode($zone['custom']); $zoneipslave = $zone['slaveip']; $zoneforwarders=$zone['forwarders']; $zonereverso = $zone['reverso']; if (!(is_dir(CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview"))) mkdir(CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview",0755,true); if($zone['allowupdate'] == '') $zoneallowupdate = "none"; else $zoneallowupdate = str_replace(',','; ',$zone['allowupdate']); if($zone['allowquery'] == '') $zoneallowquery = "none"; else $zoneallowquery = str_replace(',','; ',$zone['allowquery']); if($zone['allowtransfer'] == '') $zoneallowtransfer = "none"; else $zoneallowtransfer = str_replace(',','; ',$zone['allowtransfer']); if ($zoneview == $viewname){ if($zonereverso == "on") $bind_conf .= "\tzone \"$zonename.in-addr.arpa\" {\n"; else $bind_conf .= "\tzone \"$zonename\" {\n"; $bind_conf .= "\t\ttype $zonetype;\n"; if ($zonetype != "forward") $bind_conf .= "\t\tfile \"/etc/namedb/$zonetype/$zoneview/$zonename.DB\";\n"; switch ($zonetype){ case "slave": $bind_conf .= "\t\tmasters { $zoneipslave; };\n"; $bind_conf .= "\t\tallow-transfer {none;};\n"; $bind_conf .= "\t\tnotify no;\n"; break; case "forward": $bind_conf .= "\t\tforward only;\n"; $bind_conf .= "\t\tforwarders { $zoneforwarders; };\n"; break; case "redirect": $bind_conf .= "\t\t# While using redirect zones,NXDOMAIN Redirection will not override DNSSEC\n"; $bind_conf .= "\t\t# If the client has requested DNSSEC records (DO=1) and the NXDOMAIN response is signed then no substitution will occur\n"; $bind_conf .= "\t\t# https://kb.isc.org/article/AA-00376/192/BIND-9.9-redirect-zones-for-NXDOMAIN-redirection.html\n"; break; default: $bind_conf .= "\t\tallow-update { $zoneallowupdate;};\n"; $bind_conf .= "\t\tallow-query { $zoneallowquery;};\n"; $bind_conf .= "\t\tallow-transfer { $zoneallowtransfer;};\n"; if ($zone['dnssec']=="on"){ //https://kb.isc.org/article/AA-00626/ $bind_conf .="\n\t\t# look for dnssec keys here:\n"; $bind_conf .="\t\tkey-directory \"/etc/namedb/keys\";\n\n"; $bind_conf .="\t\t# publish and activate dnssec keys:\n"; $bind_conf .="\t\tauto-dnssec maintain;\n\n"; $bind_conf .="\t\t# use inline signing:\n"; $bind_conf .="\t\tinline-signing yes;\n\n"; } } if ($zonecustom != '') $bind_conf .= "\t\t$zonecustom\n"; $bind_conf .= "\t};\n\n"; switch($zonetype){ case "redirect": case "master": //check/update slave dir permission chown(CHROOT_LOCALBASE."/etc/namedb/$zonetype","bind"); chown(CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview","bind"); $zonetll = ($zone['tll']?$zone['tll']:"43200"); $zonemail = ($zone['mail']?$zone['mail']:"zonemaster.{$zonename}"); $zonemail = preg_replace("/@/",".",$zonemail); $zoneserial = $zone['serial']; $zonerefresh = ($zone['refresh']?$zone['refresh']:"3600"); $zoneretry = ($zone['retry']?$zone['retry']:"600"); $zoneexpire = ($zone['expire']?$zone['expire']:"86400"); $zoneminimum = ($zone['minimum']?$zone['minimum']:"3600"); $zonenameserver = $zone['nameserver']; $zoneipns = $zone['ipns']; $zonereverso = $zone['reverso']; if($zone['allowupdate'] == '') $zoneallowupdate = "none"; else $zoneallowupdate = str_replace(',','; ',$zone['allowupdate']); if($zone['allowquery'] == '') $zoneallowquery = "none"; else $zoneallowquery = str_replace(',','; ',$zone['allowquery']); if($zone['allowtransfer'] == '') $zoneallowtransfer = "none"; else $zoneallowtransfer = str_replace(',','; ',$zone['allowtransfer']); $zone_conf = "\$TTL {$zonetll}\n;\n"; if($zonereverso == "on") $zone_conf .= "\$ORIGIN {$zonename}.in-addr.arpa.\n\n"; else $zone_conf .= "\$ORIGIN {$zonename}.\n\n"; $zone_conf .= ";\tDatabase file {$zonename}.DB for {$zonename} zone.\n"; $zone_conf .= ";\tDo not edit this file!!!\n"; $zone_conf .= ";\tZone version {$zoneserial}\n;\n"; if($zonereverso == "on" || $zonetype =="redirect") $zone_conf .= "@\t IN SOA $zonenameserver. \t $zonemail. (\n"; else $zone_conf .= "$zonename.\t IN SOA $zonenameserver. \t $zonemail. (\n"; $zone_conf .= "\t\t$zoneserial ; serial\n"; $zone_conf .= "\t\t$zonerefresh ; refresh\n"; $zone_conf .= "\t\t$zoneretry ; retry\n"; $zone_conf .= "\t\t$zoneexpire ; expire\n"; $zone_conf .= "\t\t$zoneminimum ; default_ttl\n\t\t)\n\n"; $zone_conf .= ";\n; Zone Records\n;\n"; if($zonereverso == "on") $zone_conf .= "\t IN NS \t$zonenameserver.\n"; else{ $zone_conf .= "@ \t IN NS \t$zonenameserver.\n"; if ($zoneipns !="") $zone_conf .= "@ \t IN A \t$zoneipns\n"; } for ($y=0; $y<sizeof($zone['row']); $y++) { $hostname = (preg_match("/(MX|NS)/",$zone['row'][$y]['hosttype'])?"@":$zone['row'][$y]['hostname']); $hosttype = $zone['row'][$y]['hosttype']; $hostdst = $zone['row'][$y]['hostdst']; if (preg_match("/[a-zA-Z]/",$hostdst) && !preg_match("/(TXT|SPF|AAAA)/",$hosttype)) $hostdst .= "."; $hostvalue = $zone['row'][$y]['hostvalue']; $zone_conf .= "$hostname \t IN $hosttype $hostvalue \t$hostdst\n"; } if (($zone[regdhcpstatic] == 'on') && is_array($config['dhcpd'])) { foreach ($config['dhcpd'] as $dhcpif => $dhcpifconf) if(is_array($dhcpifconf['staticmap']) && isset($dhcpifconf['enable'])) foreach ($dhcpifconf['staticmap'] as $host) if ($host['ipaddr'] && $host['hostname']) { if($zonereverso == "on") { $hostdomain = $dhcpifconf['domain']; if(strlen($hostdomain) == 0) { $hostdomain = $config['system']['domain']; } if(strlen($hostdomain) != 0) { $hostdomain .= '.'; } $zoneparts = array_reverse(explode('.',$zonename)); $addressparts = explode('.',$host['ipaddr']); $addressstart = 0; while($addressstart < count($zoneparts) && $addressstart < count($addressparts) && $zoneparts[$addressstart] == $addressparts[$addressstart]) { $addressstart++; } $shortaddress=''; for($addresspointer = count($addressparts)-1; $addresspointer >= $addressstart; $addresspointer--) { $shortaddress .= (strlen($shortaddress) > 0 ? '.' : '') . $addressparts[$addresspointer]; } $zone_conf .= "{$shortaddress}\tIN PTR\t{$host['hostname']}.{$hostdomain}\n"; } else { $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(){ ?> <script language="JavaScript"> <!-- function on_type_zone_changed() { var field = document.iform.type; var tipo = field.options[field.selectedIndex].value; switch (tipo){ case 'master': document.iform.slaveip.disabled = 1; document.iform.tll.disabled = 0; document.iform.nameserver.disabled = 0; document.iform.reverso.disabled = 0; document.iform.forwarders.disabled = 1; document.iform.dnssec.disabled = 0; document.iform.backupkeys.disabled = 0; document.iform.regdhcpstatic.disabled = 0; document.iform.ipns.disabled = 0; document.iform.mail.disabled = 0; document.iform.serial.disabled = 0; document.iform.refresh.disabled = 0; document.iform.retry.disabled = 0; document.iform.expire.disabled = 0; document.iform.minimum.disabled = 0; break; case 'slave': document.iform.slaveip.disabled = 0; document.iform.tll.disabled = 1; document.iform.nameserver.disabled = 1; document.iform.reverso.disabled = 0; document.iform.forwarders.disabled = 1; document.iform.dnssec.disabled = 0; document.iform.backupkeys.disabled = 0; document.iform.regdhcpstatic.disabled = 0; document.iform.ipns.disabled = 1; document.iform.mail.disabled = 1; document.iform.serial.disabled = 1; document.iform.refresh.disabled = 1; document.iform.retry.disabled = 1; document.iform.expire.disabled = 1; document.iform.minimum.disabled = 1; break; case 'forward': document.iform.slaveip.disabled = 1; document.iform.tll.disabled = 1; document.iform.nameserver.disabled = 1; document.iform.reverso.disabled = 1; document.iform.forwarders.disabled = 0; document.iform.dnssec.disabled = 1; document.iform.backupkeys.disabled = 1; document.iform.regdhcpstatic.disabled = 1; document.iform.ipns.disabled = 1; document.iform.mail.disabled = 1; document.iform.serial.disabled = 1; document.iform.refresh.disabled = 1; document.iform.retry.disabled = 1; document.iform.expire.disabled = 1; document.iform.minimum.disabled = 1; break; case 'redirect': document.iform.slaveip.disabled = 1; document.iform.tll.disabled = 1; document.iform.nameserver.disabled = 0; document.iform.reverso.disabled = 1; document.iform.forwarders.disabled = 1; document.iform.dnssec.disabled = 1; document.iform.backupkeys.disabled = 1; document.iform.regdhcpstatic.disabled = 1; document.iform.ipns.disabled = 1; document.iform.mail.disabled = 0; document.iform.serial.disabled = 0; document.iform.refresh.disabled = 0; document.iform.retry.disabled = 0; document.iform.expire.disabled = 0; document.iform.minimum.disabled = 0; break; } } --> </script> <?php } function bind_print_javascript_type_zone2(){ print("<script language=\"JavaScript\">on_type_zone_changed();document.iform.resultconfig.disabled = 1;document.iform.dsset.disabled = 1;</script>\n"); } function bind_write_rcfile() { $rc = array(); $BIND_LOCALBASE = "/usr/local"; $rc['file'] = 'named.sh'; $rc['start'] = <<<EOD if [ -z "`ps auxw | grep "[n]amed -c /etc/namedb/named.conf"|awk '{print $2}'`" ];then {$BIND_LOCALBASE}/sbin/named -c /etc/namedb/named.conf -u bind -t /cf/named/ fi EOD; $rc['stop'] = <<<EOD killall -9 named 2>/dev/null sleep 2 EOD; $rc['restart'] = <<<EOD if [ -z "`ps auxw | grep "[n]amed -c /etc/namedb/named.conf"|awk '{print $2}'`" ];then {$BIND_LOCALBASE}/sbin/named -c /etc/namedb/named.conf -u bind -t /cf/named/ else killall -9 named 2>/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; $x<sizeof($xml['bindzone']["config"]); $x++){ if ($xml['bindzone']["config"][$x]['type']=="master"){ $xml['bindzone']["config"][$x]['type']="slave"; $xml['bindzone']["config"][$x]['slaveip']=$master_zone_ip; } } /* assemble xmlrpc payload */ $params = array( XML_RPC_encode($password), XML_RPC_encode($xml) ); /* set a few variables needed for sync code borrowed from filter.inc */ $url = $synchronizetoip; log_error("[bind] Beginning bind XMLRPC sync to {$url}:{$port}."); $method = 'pfsense.merge_installedpackages_section_xmlrpc'; $msg = new XML_RPC_Message($method, $params); $cli = new XML_RPC_Client('/xmlrpc.php', $url, $port); $cli->setCredentials($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)."); } } ?>