Project

General

Profile

Bug #4553 » bind.inc.txt

Alex B, 03/31/2015 04:54 AM

 
1
<?PHP
2
/* $Id$ */
3
/*
4
	bind.inc
5
	part of the Bind package for pfSense
6
	Copyright (C) 2013 Juliano Oliveira/Adriano Brancher
7
	Copyright (C) 2013 Marcello Coutinho  
8
	All rights reserved.
9

    
10
	Redistribution and use in source and binary forms, with or without
11
	modification, are permitted provided that the following conditions are met:
12

    
13
	1. Redistributions of source code must retain the above copyright notice,
14
	   this list of conditions and the following disclaimer.
15

    
16
	2. Redistributions in binary form must reproduce the above copyright
17
	   notice, this list of conditions and the following disclaimer in the
18
	   documentation and/or other materials provided with the distribution.
19

    
20
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
21
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
24
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
	POSSIBILITY OF SUCH DAMAGE.
30

    
31
*/
32
$shortcut_section = "bind";
33
require_once('globals.inc');
34
require_once('config.inc');
35
require_once('util.inc');
36
require_once('pfsense-utils.inc');
37
require_once('pkg-utils.inc');
38
require_once('service-utils.inc');
39
if(!function_exists("filter_configure")) 
40
	require_once("filter.inc");
41

    
42
$pf_version=substr(trim(file_get_contents("/etc/version")),0,3);
43
if ($pf_version > 2.0)
44
	define('BIND_LOCALBASE', '/usr/pbi/bind-' . php_uname("m"));
45
else
46
	define('BIND_LOCALBASE','/usr/local');
47

    
48
define('CHROOT_LOCALBASE','/cf/named');
49

    
50
function bind_zone_validate($post, &$input_errors){
51
	if (key_exists("mail",$_POST))
52
		$_POST['mail']=preg_replace("/@/",".",$post['mail']);
53
		
54
	switch ($_POST['type']){
55
		case 'slave':
56
			if( $_POST['slaveip'] == "")
57
				$input_errors[] = 'The field \'Master Zone IP\' is required for slave zones.';
58
		break;
59
		case 'forward':
60
			if( $_POST['forwarders'] == "")
61
			$input_errors[] = 'The field \'Forwarders\' is required for forward zones.';
62
		break;
63
		case 'redirect':
64
			$_POST['tll']=300;
65
			$_POST['refresh']=0;
66
			$_POST['serial']=0;
67
			$_POST['retry']=0;
68
			$_POST['expire']=0;
69
			$_POST['minimum']=0;
70
			if($_POST['mail']=='')
71
				$input_errors[] = "The field 'Mail Admin Zone' is required for {$_POST['type']} zones.";
72
			
73
		default:
74
			if($_POST['nameserver']=='')
75
				$input_errors[] = "The field 'Name server' is required for {$_POST['type']} zones.";
76
			for ($i=0;$i < count($_POST);$i++){
77
				if (key_exists("hostname$i",$_POST)){
78
					if ($_POST['reverso']=="on"){
79
						$_POST["hostvalue$i"]="";
80
						if (!preg_match("/(PTR|NS)/",$_POST["hosttype$i"]))
81
							$input_errors[] = 'On reverse zones, valid record types are NS or PTR';
82
						}
83
					if (preg_match("/(MX|NS)/",$_POST["hosttype$i"]))
84
						$_POST["hostname$i"]="";
85
					if (!preg_match("/(MX|NS)/",$_POST["hosttype$i"]) && $_POST["hostname$i"]=="")
86
						$input_errors[] = 'Record cannot be empty for '.$_POST["hosttype$i"].' type ';
87
					if ($_POST["hosttype$i"]=="MX" && $_POST["hostvalue$i"]=="")
88
						$_POST["hostvalue$i"]="10";
89
					if ($_POST["hosttype$i"]!="MX" && $_POST["hostvalue$i"]!="")
90
						$_POST["hostvalue$i"]="";
91
					if ($_POST["hostdst$i"]=="")
92
						$input_errors[] = 'Alias or IP address cannot be empty.';
93
				}
94
			}
95
		}
96
}
97

    
98
function bind_sync(){
99
	global $config;
100
	conf_mount_rw();
101
	//create rndc
102
	$rndc_confgen="/usr/local/sbin/rndc-confgen";
103
	if (!file_exists(BIND_LOCALBASE."/etc/rndc-confgen.pfsense") && file_exists($rndc_confgen)){
104
		exec("$rndc_confgen ",$rndc_conf);
105
		foreach($rndc_conf as $line)
106
			$confgen_file.="$line\n";
107
		file_put_contents(BIND_LOCALBASE."/etc/rndc-confgen.pfsense",$confgen_file);
108
		}
109
	if (file_exists(BIND_LOCALBASE."/etc/rndc-confgen.pfsense")){
110
		$rndc_conf=file(BIND_LOCALBASE."/etc/rndc-confgen.pfsense");
111
		$confgen="rndc.conf";
112
		$rndc_bindconf="";
113
		foreach ($rndc_conf as $line){
114
			if ($confgen =="rndc.conf"){
115
				if (!preg_match ("/^#/",$line))
116
					$rndc_file.=$line;
117
				}
118
			else{
119
				if (!preg_match ("/named.conf/",$line))
120
					$rndc_bindconf.=preg_replace('/#/',"",$line);
121
				}
122
			if (preg_match("/named.conf/",$line)){
123
				$confgen="named.conf";
124
				file_put_contents(BIND_LOCALBASE."/etc/rndc.conf",$rndc_file);
125
				}
126
		}
127
	}
128
	 
129
	$bind = $config["installedpackages"]["bind"]["config"][0];
130
	$bind_enable = $bind['enable_bind'];
131
	$bind_forwarder = $bind['bind_forwarder'];
132
	$forwarder_ips = $bind['bind_forwarder_ips'];
133
	$ram_limit = ($bind['bind_ram_limit']?$bind['bind_ram_limit']:"256M");
134
	$hide_version = $bind['bind_hide_version'];
135
	$bind_notify = $bind['bind_notify'];
136
	$custom_options = base64_decode($bind['bind_custom_options']);
137
	$bind_logging = $bind['bind_logging'];
138
	$bind_conf ="#Bind pfsense configuration\n";
139
	$bind_conf .="#Do not edit this file!!!\n\n";
140
	$bind_conf .= "$rndc_bindconf\n";
141
	$bind_conf .= <<<EOD
142

    
143
options {
144
	directory "/etc/namedb";
145
	pid-file "/var/run/named/pid";
146
	statistics-file "/var/log/named.stats";
147
	max-cache-size {$ram_limit};
148
	
149
EOD;
150
	// check response rate limit option
151
	//https://kb.isc.org/article/AA-01000/0/A-Quick-Introduction-to-Response-Rate-Limiting.html
152
	//http://ss.vix.su/~vjs/rl-arm.html
153
	if ($bind['rate_enabled']=="on"){
154
		$rate_limit=($bind['rate_limit']?$bind['rate_limit']:"15");
155
		$log_only=($bind['log_only']=="no"?"no":"yes");
156
		$bind_conf .= <<<EOD
157
		rate-limit {
158
			responses-per-second {$rate_limit};
159
			log-only {$log_only};
160
    		};
161
    	
162
EOD;
163
	}
164
	//check ips to listen on
165
	if (preg_match("/All/",$bind['listenon'])){
166
		$bind_listenonv6="any;";
167
		$bind_listenon="any;";
168
		}
169
	else{
170
		$bind_listenonv6="";
171
		$bind_listenon ="";
172
		foreach (explode(',',$bind['listenon']) as $listenon){
173
			if (is_ipaddrv6($listenon))
174
				$bind_listenonv6 .= $listenon."; ";
175
			elseif (is_ipaddr($listenon))
176
				$bind_listenon .= $listenon."; ";
177
			else{
178
				$listenon=(pfSense_get_interface_addresses(convert_friendly_interface_to_real_interface_name($listenon)));
179
				if (is_ipaddr($listenon['ipaddr']))
180
					$bind_listenon .= $listenon['ipaddr']."; ";
181
				if(is_ipaddrv6($listenon['ipaddr6']))
182
					$bind_listenonv6 .= $listenon['ipaddr6']."; ";
183
			}	
184
		}
185
	}
186
	$bind_listenonv6=($bind_listenonv6==""?"none;":$bind_listenonv6);
187
	$bind_listenon=($bind_listenon==""?"none;":$bind_listenon);
188
	//print "<PRE>$bind_listenonv6 $bind_listenon";
189
	if (key_exists("ipv6allow",$config['system'])){
190
		$bind_conf .="\t\tlisten-on-v6 { $bind_listenonv6 };\n";
191
		}
192
	$bind_conf .="\t\tlisten-on { $bind_listenon };\n";
193

    
194
	#forwarder config
195
	if ($bind_forwarder == on)
196
		$bind_conf .="\t\tforwarders { $forwarder_ips };\n";
197
	if ($bind_notify == on)
198
		$bind_conf .="\t\tnotify yes;\n"; 
199
	if ($hide_version == on)
200
		$bind_conf .="\t\tversion none;\n";
201

    
202
	$bind_conf .= preg_replace("/^/m","\t\t",$custom_options); 
203
	$bind_conf .= "\n\t};\n\n";
204
	
205
	if ($bind_logging == on){
206
		//check if bind is included on syslog
207
		$syslog_files=array("/etc/inc/system.inc","/var/etc/syslog.conf");
208
		$restart_syslog=0;
209
		foreach ($syslog_files as $syslog_file){
210
			$syslog_file_data=file_get_contents($syslog_file);
211
			if ( !preg_match("/dnsmasq,named,filterdns/",$syslog_file_data) || !preg_match("/'dnsmasq','named','filterdns'/",$syslog_file_data) ) {
212
				$syslog_file_data=preg_replace("/dnsmasq,filterdns/","dnsmasq,named,filterdns",$syslog_file_data);
213
				$syslog_file_data=preg_replace("/'dnsmasq','filterdns'/","'dnsmasq','named','filterdns'",$syslog_file_data);
214
				file_put_contents($syslog_file,$syslog_file_data);
215
				$restart_syslog++;
216
				}
217
			}
218
		if ($restart_syslog > 0){
219
			system("/usr/bin/killall -HUP syslogd");
220
		}
221
		$log_categories=explode(",",$bind['log_options']);
222
		$log_severity=($bind['log_severity']?$bind['log_severity']:'default');
223
		if (sizeof($log_categories) > 0 && $log_categories[0]!=""){
224
			$bind_conf .= <<<EOD
225
			
226
		logging {
227
			channel custom {
228
				syslog daemon;
229
				print-time no;
230
				print-severity yes;
231
				print-category yes;
232
				severity {$log_severity};
233
				};
234

    
235
EOD;
236
		foreach ($log_categories as $category)
237
			$bind_conf .="\t\t\tcategory $category\t{custom;};\n";
238
		$bind_conf .="\t\t};\n\n";				
239
				}
240
		 }
241
	else {
242
		$bind_conf .="\t\tlogging { category default { null; }; };\n\n";				
243
	}
244

    
245
	#Config Zone domain
246
	if(!is_array($config["installedpackages"]["bindacls"]) || !is_array($config["installedpackages"]["bindacls"]["config"])){
247
		$config["installedpackages"]["bindacls"]["config"][] =
248
			array("name"=>"none","description"=>"BIND Built-in ACL","row"=>array("value"=>"","description"=>""));
249
		$config["installedpackages"]["bindacls"]["config"][] =
250
			array("name"=>"any","description"=>"BIND Built-in ACL","row"=>array("value"=>"","description"=>""));
251
		$config["installedpackages"]["bindacls"]["config"][] =
252
			array("name"=>"localhost","description"=>"BIND Built-in ACL","row"=>array("value"=>"","description"=>""));
253
		$config["installedpackages"]["bindacls"]["config"][] =
254
			array("name"=>"localnets","description"=>"BIND Built-in ACL","row"=>array("value"=>"","description"=>""));
255
		write_config("Create BIND Built-in ACLs");
256
		}
257
	$bindacls = $config["installedpackages"]["bindacls"]["config"];
258
	for ($i=0; $i<sizeof($bindacls); $i++)
259
	{
260
		$aclname = $bindacls[$i]['name'];
261
		$aclhost = $bindacls[$i]['row'];
262
		if($aclname != "none" && $aclname != "any" && $aclname != "localhost" && $aclname != "localnets"){
263
			$bind_conf .= "acl \"$aclname\" {\n";	
264
			for ($u=0; $u<sizeof($aclhost); $u++)
265
			{
266
				$aclhostvalue = $aclhost[$u]['value'];
267
				$bind_conf .= "\t$aclhostvalue;\n";
268
			}
269
			$bind_conf .= "};\n\n";
270
		}	
271
	}
272
 
273
	if(is_array($config["installedpackages"]["bindviews"]))
274
		$bindview = $config["installedpackages"]["bindviews"]["config"];
275
	else
276
		$bindview =array();
277
		
278
	for ($i=0; $i<sizeof($bindview); $i++) 
279
	{
280
		$views = $config["installedpackages"]["bindviews"]["config"][$i];
281
		$viewname = $views['name'];
282
		$viewrecursion = $views['recursion']; 
283
		if($views['match-clients'] == '')
284
			$viewmatchclients = "none";
285
		else
286
			$viewmatchclients = str_replace(',','; ',$views['match-clients']); 	
287
		if($views['allow-recursion'] == '')
288
			$viewallowrecursion = "none";
289
		else
290
			$viewallowrecursion = str_replace(',','; ',$views['allow-recursion']); 	
291
		$viewcustomoptions = base64_decode($views['bind_custom_options']);
292
		
293
		$bind_conf .= "view \"$viewname\" { \n\n"; 
294
		$bind_conf .= "\trecursion $viewrecursion;\n"; 
295
		$bind_conf .= "\tmatch-clients { $viewmatchclients;};\n"; 
296
		$bind_conf .= "\tallow-recursion { $viewallowrecursion;};\n"; 
297
		$bind_conf .= "\t$viewcustomoptions\n\n"; 
298
			
299
		if(is_array($config["installedpackages"]["bindzone"])) 
300
			$bindzone = $config["installedpackages"]["bindzone"]["config"];
301
		else
302
			$bindzone =array();
303

    
304
		$write_config=0;
305
		for ($x=0; $x<sizeof($bindzone); $x++)
306
		{
307
			$zone = $bindzone[$x];
308
			if ($zone['disabled']=="on"){
309
				continue;
310
				}
311
			$zonename = $zone['name'];
312
			if ($zonename=="."){
313
				$custom_root_zone[$i]=true;
314
			}
315
			$zonetype = $zone['type'];
316
			$zoneview = $zone['view'];
317
			$zonecustom = base64_decode($zone['custom']);
318
			$zoneipslave = $zone['slaveip'];
319
			$zoneforwarders=$zone['forwarders'];
320
			$zonereverso = $zone['reverso'];
321
			$zonereversv6o = $zone['reversv6o'];
322
			
323
			if (!(is_dir(CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview")))
324
            	mkdir(CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview",0755,true);
325
            					
326
			if($zone['allowupdate'] == '')
327
				$zoneallowupdate = "none";
328
			else
329
				$zoneallowupdate = str_replace(',','; ',$zone['allowupdate']); 	
330
			if($zone['allowquery'] == '')
331
				$zoneallowquery = "none";
332
			else
333
				$zoneallowquery = str_replace(',','; ',$zone['allowquery']); 	
334
			if($zone['allowtransfer'] == '')
335
				$zoneallowtransfer = "none";
336
			else
337
				$zoneallowtransfer = str_replace(',','; ',$zone['allowtransfer']); 	
338

    
339
				if ($zoneview == $viewname){
340
					if($zonereverso == "on")
341
						if($zonereversv6o == "on")
342
					 		$bind_conf .= "\tzone \"$zonename.ip6.arpa\" {\n";
343
						else
344
							$bind_conf .= "\tzone \"$zonename.in-addr.arpa\" {\n";
345
				  	else
346
						$bind_conf .= "\tzone \"$zonename\" {\n";
347
        		
348
					$bind_conf .= "\t\ttype $zonetype;\n";
349
					if ($zonetype != "forward")
350
        				$bind_conf .= "\t\tfile \"/etc/namedb/$zonetype/$zoneview/$zonename.DB\";\n";
351
        			switch ($zonetype){
352
        				case "slave":
353
        				$bind_conf .= "\t\tmasters { $zoneipslave; };\n";
354
        				$bind_conf .= "\t\tallow-transfer { $zoneallowtransfer;};\n";
355
        				$bind_conf .= "\t\tnotify no;\n";
356
        				break;
357
        				case "forward":
358
        				$bind_conf .= "\t\tforward only;\n";
359
        				$bind_conf .= "\t\tforwarders { $zoneforwarders; };\n";
360
        				break;
361
        				case "redirect":
362
							$bind_conf .= "\t\t# While using redirect zones,NXDOMAIN Redirection will not override DNSSEC\n";
363
							$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";
364
							$bind_conf .= "\t\t# https://kb.isc.org/article/AA-00376/192/BIND-9.9-redirect-zones-for-NXDOMAIN-redirection.html\n";
365
        				break;
366
        				default:
367
						$bind_conf .= "\t\tallow-update { $zoneallowupdate;};\n"; 
368
						$bind_conf .= "\t\tallow-query { $zoneallowquery;};\n"; 
369
						$bind_conf .= "\t\tallow-transfer { $zoneallowtransfer;};\n";
370
	        			if ($zone['dnssec']=="on"){
371
	        				//https://kb.isc.org/article/AA-00626/
372
	        				$bind_conf .="\n\t\t# look for dnssec keys here:\n";
373
	        				$bind_conf .="\t\tkey-directory \"/etc/namedb/keys\";\n\n";
374
	        				$bind_conf .="\t\t# publish and activate dnssec keys:\n";
375
	        				$bind_conf .="\t\tauto-dnssec maintain;\n\n";
376
	        				$bind_conf .="\t\t# use inline signing:\n";
377
	        				$bind_conf .="\t\tinline-signing yes;\n\n";
378
	        				}
379
	        			}
380
				 	if ($zonecustom != '')
381
        				$bind_conf .= "\t\t$zonecustom\n";
382
        				
383
					$bind_conf .= "\t};\n\n";
384
        
385
					switch($zonetype){
386
					  case "redirect":
387
					  case "master":
388
					  	//check/update slave dir permission
389
						chown(CHROOT_LOCALBASE."/etc/namedb/$zonetype","bind");
390
						chown(CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview","bind");
391
						$zonetll = ($zone['tll']?$zone['tll']:"43200");
392
						$zonemail = ($zone['mail']?$zone['mail']:"zonemaster.{$zonename}");
393
						$zonemail = preg_replace("/@/",".",$zonemail);				
394
						$zoneserial = $zone['serial'];
395
						$zonerefresh = ($zone['refresh']?$zone['refresh']:"3600");		
396
						$zoneretry = ($zone['retry']?$zone['retry']:"600");
397
						$zoneexpire = ($zone['expire']?$zone['expire']:"86400");
398
						$zoneminimum = ($zone['minimum']?$zone['minimum']:"3600");
399
						$zonenameserver = $zone['nameserver'];
400
						$zoneipns = $zone['ipns'];
401
						$zonereverso = $zone['reverso'];		
402
						$zonereversv6o = $zone['reversv6o'];
403
						if($zone['allowupdate'] == '')
404
							$zoneallowupdate = "none";
405
						else
406
							$zoneallowupdate = str_replace(',','; ',$zone['allowupdate']);
407
						if($zone['allowquery'] == '')
408
							$zoneallowquery = "none";
409
						else
410
							$zoneallowquery = str_replace(',','; ',$zone['allowquery']);
411
						if($zone['allowtransfer'] == '')
412
							$zoneallowtransfer = "none";
413
						else
414
							$zoneallowtransfer = str_replace(',','; ',$zone['allowtransfer']);
415
						$zone_conf = "\$TTL {$zonetll}\n;\n";
416
						if($zonereverso == "on")
417
							if($zonereversv6o == "on")
418
								$zone_conf .= "\$ORIGIN {$zonename}.ip6.arpa.\n\n";
419
							else
420
								$zone_conf .= "\$ORIGIN {$zonename}.in-addr.arpa.\n\n";
421
						else
422
							$zone_conf .= "\$ORIGIN {$zonename}.\n\n";
423
						$zone_conf .= ";\tDatabase file {$zonename}.DB for {$zonename} zone.\n";
424
						$zone_conf .= ";\tDo not edit this file!!!\n";
425
						$zone_conf .= ";\tZone version {$zoneserial}\n;\n";
426
					 	if($zonereverso == "on" || $zonetype =="redirect")
427
							$zone_conf .= "@\t IN  SOA $zonenameserver. \t $zonemail. (\n";
428
						else
429
							$zone_conf .= "$zonename.\t IN  SOA $zonenameserver. \t $zonemail. (\n";
430

    
431
						$zone_conf .= "\t\t$zoneserial ; serial\n";
432
						$zone_conf .= "\t\t$zonerefresh ; refresh\n";
433
						$zone_conf .= "\t\t$zoneretry ; retry\n";
434
						$zone_conf .= "\t\t$zoneexpire ; expire\n";
435
						$zone_conf .= "\t\t$zoneminimum ; default_ttl\n\t\t)\n\n";
436
						$zone_conf .= ";\n; Zone Records\n;\n";
437

    
438
					 	if($zonereverso == "on")
439
							$zone_conf .= "\t IN NS \t$zonenameserver.\n";
440
						else{
441
							$zone_conf .= "@ \t IN NS \t$zonenameserver.\n";
442
							if ($zoneipns !="")
443
								$zone_conf .= "@ \t IN A \t$zoneipns\n";
444
						}
445
						for ($y=0; $y<sizeof($zone['row']); $y++)
446
						{
447
							$hostname = (preg_match("/(MX|NS)/",$zone['row'][$y]['hosttype'])?"@":$zone['row'][$y]['hostname']);
448
							$hosttype = $zone['row'][$y]['hosttype'];
449
							$hostdst = $zone['row'][$y]['hostdst'];
450
							if (preg_match("/[a-zA-Z]/",$hostdst) && !preg_match("/(TXT|SPF|AAAA)/",$hosttype))
451
								$hostdst .= ".";
452
							$hostvalue = $zone['row'][$y]['hostvalue'];
453
							
454
							$zone_conf .= "$hostname \t IN $hosttype $hostvalue \t$hostdst\n";
455
						}
456

    
457
						# Register DHCP static mappings
458
						if (($zone[regdhcpstatic] == 'on') && is_array($config['dhcpd'])) {
459
							$zoneparts = array_reverse(explode('.',$zonename));
460
							foreach ($config['dhcpd'] as $dhcpif => $dhcpifconf) {
461
								if (!isset($dhcpifconf['enable']) || !is_array($dhcpifconf['staticmap']))  {
462
									continue;
463
								}
464
								foreach ($dhcpifconf['staticmap'] as $host) {
465
									if (is_domain($host['domain'])) {
466
										$domain = $host['domain'];
467
									} elseif (is_domain($dhcpifconf['domain'])) {
468
										$domain = $dhcpifconf['domain'];
469
									} elseif (is_domain($config['system']['domain'])) {
470
										$domain = $config['system']['domain'];
471
									} else {
472
										continue;
473
									}
474
									if (!is_hostname($host['hostname']) || !is_ipaddr($host['ipaddr']))  {
475
										continue;
476
									}
477
									if ($zonereverso == "on") {
478
										$parts = explode('.',$host['ipaddr']);
479
										$intersect = array_intersect_assoc($parts,$zoneparts);
480
										if (count($zoneparts) == count($intersect)) {
481
											$diff = array_diff_assoc($parts,$zoneparts);
482
											$shortaddr = implode('.',array_reverse($diff));
483
											$zone_conf .= "{$shortaddr}\tIN PTR\t{$host['hostname']}.{$domain}.\n";
484
										}
485
									} else {
486
										$parts = array_reverse(explode('.',$domain));
487
										$diff = array_diff_assoc($parts,$zoneparts);
488
										if (count($diff) == 0) {
489
											$zone_conf .= "{$host['hostname']}\tIN A\t{$host['ipaddr']}\n";
490
										}
491
									}
492
								}
493
							}
494
						}
495

    
496
						if ($zone['customzonerecords']!=""){
497
							$zone_conf .= "\n\n;\n;custom zone records\n;\n".base64_decode($zone['customzonerecords'])."\n";
498
						}
499
						file_put_contents(CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview/$zonename.DB", $zone_conf);
500
						$config["installedpackages"]["bindzone"]["config"][$x][resultconfig]=base64_encode($zone_conf);
501
						$write_config++;
502
						//check dnssec keys creation for master zones
503
        				if($zone['dnssec']=="on"){
504
	        				$zone_found=0;
505
	        				foreach (glob(CHROOT_LOCALBASE."/etc/namedb/keys/*{$zonename}*key",GLOB_NOSORT) as $filename){
506
								$zone_found++;
507
	        					}
508
							if ($zone_found==0){
509
								$key_restored=0;
510
								if(is_array($config['installedpackages']['dnsseckeys']) && is_array($config['installedpackages']['dnsseckeys']['config'])){
511
									foreach ($config['installedpackages']['dnsseckeys']['config']as $filer)
512
										if (preg_match ("/K$zonename\.+/",$filer['fullfile'])){
513
											file_put_contents($filer['fullfile'],base64_decode($filer['filedata']),LOCK_EX);
514
											chmod($filer['fullfile'],0700);
515
											chown($filer['fullfile'],"bind");
516
											$key_restored++;
517
											}
518
										}
519
								if ($key_restored > 0){
520
									log_error("[bind] {$key_restored} DNSSEC keys restored from XML backup for {$zonename} zone.");
521
									}
522
								$dnssec_bin="/usr/local/sbin/dnssec-keygen";
523
								if (file_exists($dnssec_bin) && $key_restored==0){
524
									exec("{$dnssec_bin} -K ".CHROOT_LOCALBASE."/etc/namedb/keys {$zonename}",$kout);
525
									exec("{$dnssec_bin} -K ".CHROOT_LOCALBASE."/etc/namedb/keys -fk {$zonename}",$kout);
526
									foreach($kout as $filename){
527
										chown(CHROOT_LOCALBASE."/etc/namedb/keys/{$filename}.key","bind");
528
										chown(CHROOT_LOCALBASE."/etc/namedb/keys/{$filename}.private","bind");
529
										}
530
									log_error("[bind] DNSSEC keys for {$zonename} created.");
531
									}
532
	        					}
533
	        				//get ds keys
534
	        				$dsfromkey="/usr/local/sbin/dnssec-dsfromkey";
535
	        				foreach (glob(CHROOT_LOCALBASE."/etc/namedb/keys/*{$zonename}*key",GLOB_NOSORT) as $filename) {
536
	        						$zone_key=file_get_contents($filename);
537
	        						if (preg_match("/IN DNSKEY 257 /",$zone_key) && file_exists($dsfromkey)){
538
	        							exec("$dsfromkey $filename",$dsset);
539
	        							$config["installedpackages"]["bindzone"]["config"][$x]['dsset']=base64_encode(array_pop($dsset)."\n".array_pop($dsset));
540
	        							$write_config++;
541
	        							}
542
								}
543
							//save dnssec keys to xml
544
							
545
							if($zone['backupkeys']=="on"){
546
								$dnssec_keys=0;
547
								foreach (glob(CHROOT_LOCALBASE."/etc/namedb/keys/*{$zonename}*",GLOB_NOSORT) as $filename){
548
									$file_found=0;
549
									if(is_array($config['installedpackages']['dnsseckeys']) && is_array($config['installedpackages']['dnsseckeys']['config'])){
550
										foreach ($config['installedpackages']['dnsseckeys']['config']as $filer){
551
											if ($filer['fullfile']==$filename)
552
												$file_found++;
553
											}
554
										}
555
									if ($file_found==0){
556
										$config['installedpackages']['dnsseckeys']['config'][]=array('fullfile'=> $filename,
557
																								'description'=> "bind {$zonename} DNSSEC backup file",
558
																								'filedata'=> base64_encode(file_get_contents($filename)));
559
										$write_config++;
560
										$dnssec_keys++;
561
										}
562
									}
563
									if($dnssec_keys>0){
564
										log_error("[bind] {$dnssec_keys} DNSSEC keys for {$zonename} zone saved on XML config.");
565
									}
566
								}
567
        					}
568
					break;
569
					case "slave":
570
					//check/update slave dir permission
571
					chown(CHROOT_LOCALBASE."/etc/namedb/$zonetype","bind");
572
					chown(CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview","bind");
573
					//check if exists slave zone file
574
					$rsconfig="";
575
					if ($zone['dnssec']=="on"){
576
						if (file_exists(CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview/$zonename.DB.signed"))
577
						exec("/usr/local/sbin/named-checkzone -D -f raw -o - {$zonename} ".CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview/$zonename.DB.signed",$slave_file);
578
						}
579
					else{
580
						if (file_exists(CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview/$zonename.DB"))
581
							$slave_file=file(CHROOT_LOCALBASE."/etc/namedb/$zonetype/$zoneview/$zonename.DB");
582
						}
583
					if (is_array($slave_file)){
584
					foreach ($slave_file as $zfile)
585
						$rsconfig.= $zfile;
586
					$config["installedpackages"]["bindzone"]["config"][$x][resultconfig]=base64_encode($rsconfig);
587
					$write_config++;
588
					}
589
					break;
590
					}
591
				}
592
		}
593
		if (!$custom_root_zone[$i]){
594
			$bind_conf .="\tzone \".\" {\n";
595
			$bind_conf .="\t\ttype hint;\n";
596
			$bind_conf .="\t\tfile \"/etc/namedb/named.root\";\n";
597
			$bind_conf .= "\t};\n\n";
598
			}
599
		if($write_config > 0){
600
			write_config("save result config file for zone on xml");
601
		}
602
		$bind_conf .= "};\n";
603
	}
604
	$dirs=array("/etc/namedb/keys","/var/run/named","/var/dump","/var/log","/var/stats","/dev");
605
	foreach ($dirs as $dir){
606
		if (!is_dir(CHROOT_LOCALBASE .$dir))
607
			mkdir(CHROOT_LOCALBASE .$dir,0755,true);
608
		}
609
	//dev dirs for chroot
610
	$bind_dev_dir=CHROOT_LOCALBASE."/dev";
611
	if (!file_exists("$bind_dev_dir/random")){
612
		$dev_dirs=array("null","zero","random","urandom");
613
		exec("/sbin/mount -t devfs devfs {$bind_dev_dir}",$dout);
614
		exec("/sbin/devfs -m {$bind_dev_dir} ruleset 1",$dout);
615
		exec("/sbin/devfs -m {$bind_dev_dir} rule add hide",$dout);
616
		foreach ($dev_dirs as $dev_dir)
617
			exec("/sbin/devfs -m {$bind_dev_dir} rule add path $dev_dir unhide",$dout);
618
		exec("/sbin/devfs -m {$bind_dev_dir} rule applyset",$dout);
619
	}
620
	//http://www.unixwiz.net/techtips/bind9-chroot.html
621
    file_put_contents(CHROOT_LOCALBASE.'/etc/namedb/named.conf', $bind_conf);
622
    file_put_contents(CHROOT_LOCALBASE.'/etc/namedb/rndc.conf', $rndc_file);
623
    
624
	if (!file_exists(CHROOT_LOCALBASE."/etc/namedb/named.root")){
625
		//dig +tcp @a.root-servers.net > CHROOT_LOCALBASE."/etc/namedb/named.root"
626
		$named_root=file_get_contents("http://www.internic.net/domain/named.root");
627
		file_put_contents(CHROOT_LOCALBASE."/etc/namedb/named.root",$named_root,LOCK_EX);
628
	}
629
	if (!file_exists(CHROOT_LOCALBASE."/etc/localtime")){
630
		copy("/etc/localtime", CHROOT_LOCALBASE."/etc/localtime");
631
	}
632
	
633
	bind_write_rcfile();
634
	chown(CHROOT_LOCALBASE."/etc/namedb/keys","bind");
635
	chown(CHROOT_LOCALBASE."/etc/namedb","bind");
636
	chown(CHROOT_LOCALBASE."/var/log","bind");
637
	chown(CHROOT_LOCALBASE."/var/run/named","bind");
638
	chgrp(CHROOT_LOCALBASE."/var/log","bind");
639
	$bind_sh="/usr/local/etc/rc.d/named.sh";
640
 	if($bind_enable == "on"){
641
 		chmod ($bind_sh,0755);
642
 		mwexec("{$bind_sh} restart");
643
 		}
644
 	elseif (is_service_running('named')){
645
 		mwexec("{$bind_sh} stop");
646
		chmod ($bind_sh,0644); 		
647
 		}
648
 	//sync to backup servers
649
 	bind_sync_on_changes();
650
 	conf_mount_ro();
651
}
652

    
653
function bind_print_javascript_type_zone(){
654
?>
655
        <script language="JavaScript">
656
        <!--
657
        function on_type_zone_changed() {
658

    
659
		var field = document.iform.type;
660
       		var tipo = field.options[field.selectedIndex].value;
661
       			switch (tipo){
662
       			case 'master':
663
					document.iform.slaveip.disabled = 1;
664
					document.iform.tll.disabled = 0;
665
					document.iform.nameserver.disabled = 0;
666
					document.iform.reverso.disabled = 0;
667
					document.iform.forwarders.disabled = 1;
668
					document.iform.dnssec.disabled = 0;
669
					document.iform.backupkeys.disabled = 0;
670
					document.iform.regdhcpstatic.disabled = 0;
671
					document.iform.ipns.disabled = 0;
672
					document.iform.mail.disabled = 0;
673
					document.iform.serial.disabled = 0;
674
					document.iform.refresh.disabled = 0;
675
					document.iform.retry.disabled = 0;
676
					document.iform.expire.disabled = 0;
677
					document.iform.minimum.disabled = 0;
678
               	break;
679
               	case 'slave':
680
					document.iform.slaveip.disabled = 0;
681
					document.iform.tll.disabled = 1;
682
					document.iform.nameserver.disabled = 1;
683
					document.iform.reverso.disabled = 0;
684
					document.iform.forwarders.disabled = 1;
685
					document.iform.dnssec.disabled = 0;
686
					document.iform.backupkeys.disabled = 0;
687
					document.iform.regdhcpstatic.disabled = 0;
688
					document.iform.ipns.disabled = 1;
689
					document.iform.mail.disabled = 1;
690
					document.iform.serial.disabled = 1;
691
					document.iform.refresh.disabled = 1;
692
					document.iform.retry.disabled = 1;
693
					document.iform.expire.disabled = 1;
694
					document.iform.minimum.disabled = 1;
695
					break;
696
               	case 'forward':
697
					document.iform.slaveip.disabled = 1;
698
					document.iform.tll.disabled = 1;
699
					document.iform.nameserver.disabled = 1;
700
					document.iform.reverso.disabled = 1;
701
					document.iform.forwarders.disabled = 0;
702
					document.iform.dnssec.disabled = 1;
703
					document.iform.backupkeys.disabled = 1;
704
					document.iform.regdhcpstatic.disabled = 1;
705
					document.iform.ipns.disabled = 1;
706
					document.iform.mail.disabled = 1;
707
					document.iform.serial.disabled = 1;
708
					document.iform.refresh.disabled = 1;
709
					document.iform.retry.disabled = 1;
710
					document.iform.expire.disabled = 1;
711
					document.iform.minimum.disabled = 1;
712
				break;
713
               	case 'redirect':
714
					document.iform.slaveip.disabled = 1;
715
					document.iform.tll.disabled = 1;
716
					document.iform.nameserver.disabled = 0;
717
					document.iform.reverso.disabled = 1;
718
					document.iform.forwarders.disabled = 1;
719
					document.iform.dnssec.disabled = 1;
720
					document.iform.backupkeys.disabled = 1;
721
					document.iform.regdhcpstatic.disabled = 1;
722
					document.iform.ipns.disabled = 1;
723
					document.iform.mail.disabled = 0;
724
					document.iform.serial.disabled = 0;
725
					document.iform.refresh.disabled = 0;
726
					document.iform.retry.disabled = 0;
727
					document.iform.expire.disabled = 0;
728
					document.iform.minimum.disabled = 0;
729
				break;
730
       			}
731
        }
732
        -->
733
        </script>
734
<?php
735
}
736

    
737
function bind_print_javascript_type_zone2(){
738
        print("<script language=\"JavaScript\">on_type_zone_changed();document.iform.resultconfig.disabled = 1;document.iform.dsset.disabled = 1;</script>\n");
739
}
740

    
741
function bind_write_rcfile() {
742
        $rc = array();
743
        $BIND_LOCALBASE = "/usr/local";
744
        $rc['file'] = 'named.sh';
745
        $rc['start'] = <<<EOD
746
if [ -z "`ps auxw | grep "[n]amed -c /etc/namedb/named.conf"|awk '{print $2}'`" ];then
747
        {$BIND_LOCALBASE}/sbin/named -c /etc/namedb/named.conf -u bind -t /cf/named/
748
fi
749

    
750
EOD;
751
        $rc['stop'] = <<<EOD
752
killall -9 named 2>/dev/null
753
sleep 2
754
EOD;
755
        $rc['restart'] = <<<EOD
756
if [ -z "`ps auxw | grep "[n]amed -c /etc/namedb/named.conf"|awk '{print $2}'`" ];then
757
        	{$BIND_LOCALBASE}/sbin/named -c /etc/namedb/named.conf -u bind -t /cf/named/
758
        else
759
		killall -9 named 2>/dev/null
760
         	sleep 3	
761
        	{$BIND_LOCALBASE}/sbin/named -c /etc/namedb/named.conf -u bind -t /cf/named/
762
        fi
763

    
764
EOD;
765
        conf_mount_rw();
766
        write_rcfile($rc);
767
        conf_mount_ro();
768
}
769

    
770
/* Uses XMLRPC to synchronize the changes to a remote node */
771
function bind_sync_on_changes() {
772
	global $config, $g;
773
	if (is_array($config['installedpackages']['bindsync']['config'])){
774
		$bind_sync=$config['installedpackages']['bindsync']['config'][0];
775
		$synconchanges = $bind_sync['synconchanges'];
776
		$synctimeout = $bind_sync['synctimeout'];
777
		$master_zone_ip=$bind_sync['masterip'];
778
		switch ($synconchanges){
779
			case "manual":
780
				if (is_array($bind_sync[row])){
781
					$rs=$bind_sync[row];
782
					}
783
				else{
784
					log_error("[bind] xmlrpc sync is enabled but there is no hosts to push on bind config.");
785
					return;
786
					}
787
				break;
788
			case "auto":
789
					if (is_array($config['hasync'])){
790
						$hasync=$config['hasync'][0];
791
						$rs[0]['ipaddress']=$hasync['synchronizetoip'];
792
						$rs[0]['username']=$hasync['username'];
793
						$rs[0]['password']=$hasync['password'];
794
					}
795
					else{
796
						log_error("[bind] xmlrpc sync is enabled but there is no system backup hosts to push bind config.");
797
						return;
798
					}
799
				break;			
800
			default:
801
				return;
802
			break;
803
		}
804
		if (is_array($rs)){
805
			log_error("[bind] xmlrpc sync is starting.");
806
			foreach($rs as $sh){
807
				$sync_to_ip = $sh['ipaddress'];
808
				$password = $sh['password'];
809
				if($sh['username'])
810
					$username = $sh['username'];
811
				else
812
					$username = 'admin';
813
				if($password && $sync_to_ip)
814
					bind_do_xmlrpc_sync($sync_to_ip, $username, $password,$synctimeout,$master_zone_ip);
815
				}
816
			log_error("[bind] xmlrpc sync is ending.");
817
			}
818
 		}
819
}
820
/* Do the actual XMLRPC sync */
821
function bind_do_xmlrpc_sync($sync_to_ip, $username, $password, $synctimeout,$master_zone_ip) {
822
	global $config, $g;
823

    
824
	if(!$username)
825
		return;
826
		
827
	if(!$password)
828
		return;
829

    
830
	if(!$sync_to_ip)
831
		return;
832

    
833
	if(!$synctimeout)
834
		$synctimeout=25;
835
		
836
		
837
	$xmlrpc_sync_neighbor = $sync_to_ip;
838
    if($config['system']['webgui']['protocol'] != "") {
839
		$synchronizetoip = $config['system']['webgui']['protocol'];
840
		$synchronizetoip .= "://";
841
    }
842
    $port = $config['system']['webgui']['port'];
843
    /* if port is empty lets rely on the protocol selection */
844
    if($port == "") {
845
		if($config['system']['webgui']['protocol'] == "http") 
846
			$port = "80";
847
		else 
848
			$port = "443";
849
    }
850
	$synchronizetoip .= $sync_to_ip;
851

    
852
	/* xml will hold the sections to sync */
853
	$xml = array();
854
	$xml['bind'] = $config['installedpackages']['bind'];
855
	$xml['bindacls'] = $config['installedpackages']['bindacls'];
856
	$xml['bindviews'] = $config['installedpackages']['bindviews'];
857
	$xml['bindzone'] = $config['installedpackages']['bindzone'];
858
	if (is_array($config['installedpackages']['dnsseckeys']))
859
		$xml['dnsseckeys']=$config['installedpackages']['dnsseckeys'];
860
	//change master zone to slave on backup servers
861
	if(is_array($xml['bindzone']["config"])) 
862
		for ($x=0; $x<sizeof($xml['bindzone']["config"]); $x++){
863
			if ($xml['bindzone']["config"][$x]['type']=="master"){
864
				$xml['bindzone']["config"][$x]['type']="slave";
865
				$xml['bindzone']["config"][$x]['slaveip']=$master_zone_ip;
866
			}
867
			
868
		}
869
	/* assemble xmlrpc payload */
870
	$params = array(
871
		XML_RPC_encode($password),
872
		XML_RPC_encode($xml)
873
	);
874

    
875
	/* set a few variables needed for sync code borrowed from filter.inc */
876
	$url = $synchronizetoip;
877
	log_error("[bind] Beginning bind XMLRPC sync to {$url}:{$port}.");
878
	$method = 'pfsense.merge_installedpackages_section_xmlrpc';
879
	$msg = new XML_RPC_Message($method, $params);
880
	$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
881
	$cli->setCredentials($username, $password);
882
	if($g['debug'])
883
		$cli->setDebug(1);
884
	/* send our XMLRPC message and timeout after defined sync timeout value*/
885
	$resp = $cli->send($msg, $synctimeout);
886
	if(!$resp) {
887
		$error = "A communications error occurred while attempting BIND XMLRPC sync with {$url}:{$port}.";
888
		log_error($error);
889
		file_notice("sync_settings", $error, "bind Settings Sync", "");
890
	} elseif($resp->faultCode()) {
891
		$cli->setDebug(1);
892
		$resp = $cli->send($msg, $synctimeout);
893
		$error = "An error code was received while attempting BIND XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
894
		log_error($error);
895
		file_notice("sync_settings", $error, "bind Settings Sync", "");
896
	} else {
897
		log_error("[bind] XMLRPC sync successfully completed with {$url}:{$port}.");
898
	}
899
	
900
	/* tell bind to reload our settings on the destination sync host. */
901
	$method = 'pfsense.exec_php';
902
	$execcmd  = "require_once('/usr/local/pkg/bind.inc');\n";
903
	$execcmd .= "bind_sync('yes');";
904
	/* assemble xmlrpc payload */
905
	$params = array(
906
		XML_RPC_encode($password),
907
		XML_RPC_encode($execcmd)
908
	);
909
	
910
	log_error("[bind] XMLRPC reload data {$url}:{$port}.");
911
	$msg = new XML_RPC_Message($method, $params);
912
	$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
913
	$cli->setCredentials($username, $password);
914
	$resp = $cli->send($msg, $synctimeout);
915
	if(!$resp) {
916
		$error = "A communications error occurred while attempting BIND XMLRPC sync with {$url}:{$port} (pfsense.exec_php).";
917
		log_error($error);
918
		file_notice("sync_settings", $error, "Bind Settings Sync", "");
919
	} elseif($resp->faultCode()) {
920
		$cli->setDebug(1);
921
		$resp = $cli->send($msg, $synctimeout);
922
		$error = "[Bind] An error code was received while attempting BIND XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
923
		log_error($error);
924
		file_notice("sync_settings", $error, "bind Settings Sync", "");
925
	} else {
926
		log_error("BIND XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php).");
927
	}
928
	
929
}
930
?>
(1-1/2)