Project

General

Profile

Bug #119 ยป tinydns.inc

Modified tinydns.inc - Mike Jones, 10/23/2009 04:49 PM

 
1
<?php
2

    
3
/* $Id$ */
4
/*
5
	tinydns.inc
6
	Copyright (C) 2006, 2007, 2008, 2009 Scott Ullrich
7
	Parts Copyright (C) 2007 Goffredo Andreone
8
	part of pfSense
9
	All rights reserved.
10

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

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

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

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

    
33
if(!function_exists("filter_configure")) 
34
	require_once("filter.inc");
35

    
36
function tinydns_custom_php_install_command() {
37
	global $g, $config;
38
	conf_mount_rw();
39
	$fd = fopen("/usr/local/etc/rc.d/svscan.sh", "w");
40
	if(!$fd) {
41
		log_error("Could not open /usr/local/etc/rc.d/svscan.sh for writing.");
42
		return;
43
	}
44
	$ipaddress = $config['installedpackages']['tinydns']['config'][0]['ipaddress'];
45

    
46
	$minsegment = "10240";
47
	$maxfilesize = "10240";
48
	$maxsegment  = "20480";
49
	$maxfd = "100";
50
	$maxchild = "40";
51

    
52
	if($config['installedpackages']['tinydns']['config'][0]['minsegment'])
53
		$minsegment = $config['installedpackages']['tinydns']['config'][0]['minsegment'];
54

    
55
	if($config['installedpackages']['tinydns']['config'][0]['maxfilesize'])
56
		$maxfilesize = $config['installedpackages']['tinydns']['config'][0]['maxfilesize'];
57

    
58
	if($config['installedpackages']['tinydns']['config'][0]['maxsegment'])
59
		$maxsegment = $config['installedpackages']['tinydns']['config'][0]['maxsegment'];
60

    
61
	if($config['installedpackages']['tinydns']['config'][0]['maxfd'])
62
		$maxfd = $config['installedpackages']['tinydns']['config'][0]['maxfd'];
63

    
64
	if($config['installedpackages']['tinydns']['config'][0]['maxchild'])
65
		$maxchild = $config['installedpackages']['tinydns']['config'][0]['maxchild'];
66

    
67
	if($config['installedpackages']['tinydns']['config'][0]['refreshinterval'])
68
		$refreshinterval = $config['installedpackages']['tinydns']['config'][0]['refreshinterval'];
69

    
70
	$svscan = <<<EOD
71
#!/bin/sh
72

    
73
# PROVIDE: svscan
74
# REQUIRE: LOGIN
75
# KEYWORD: FreeBSD
76

    
77
. /etc/rc.subr
78

    
79
name="svscan"
80
rcvar=`set_rcvar`
81
command="/usr/local/bin/svscan"
82
svscan_enable=\${svscan_enable-"YES"}
83
svscan_servicedir=\${svscan_servicedir-"/service"}
84

    
85
start_cmd="svscan_start"
86
stop_postcmd="svscan_stop_post"
87

    
88
load_rc_config \$name
89

    
90
required_dirs="\${svscan_servicedir}"
91

    
92
svscan_start () {
93
        echo "Starting svscan."
94
        /usr/bin/env \
95
        PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
96
        /usr/sbin/daemon -f /bin/sh -c "\$command \$svscan_servicedir 2>&1 | /usr/local/bin/readproctitle service errors: ................................................................................................................................................................................................................................................................................................................................................................................................................ &" > /dev/null
97
        minicron {$refreshinterval} /var/run/ping_hosts.pid "/etc/ping_hosts.sh; cd /etc/tinydns/root && /usr/local/bin/tinydns-data"
98
}
99

    
100
svscan_stop_post () {
101
        echo "Stopping svscan."
102
        find -L "\$svscan_servicedir" -mindepth 1 -maxdepth 2 -type d \( \! -path "\$svscan_servicedir/*/*" -or -name 'log' \) -print0 | xargs -0 /usr/local/bin/svc -dx
103
        PIDTOKILL=`cat /var/run/ping_hosts.pid`
104
        kill $PIDTOKILL
105
}
106

    
107
run_rc_command "\$1"
108

    
109
EOD;
110

    
111
	fwrite($fd, $svscan);
112
	fclose($fd);
113
	conf_mount_ro();
114
	filter_configure();
115

    
116
	tinydns_custom_php_changeip_command();
117
	
118
	exec("/usr/local/etc/rc.d/svscan.sh start");
119
}
120

    
121
function tinydns_custom_php_deinstall_command() {
122
	global $g, $config;
123
	conf_mount_rw();
124
	/* destroy all daemontools items */
125
	exec("/usr/bin/killall supervise");
126
	exec("/usr/bin/killall tinydns");	
127
	exec("/usr/sbin/pw groupdel Gtinydns");
128
	exec("/usr/sbin/pw groupdel Gdnscache");
129
	exec("/usr/sbin/pw groupdel Gdnslog");
130
	exec("/usr/sbin/pw userdel Gtinydns");
131
	exec("/usr/sbin/pw userdel Gdnscache");
132
	exec("/usr/sbin/pw userdel Gdnslog");
133
	exec("/usr/sbin/pw groupdel Gaxfrdns");
134
	exec("rm /usr/local/www/*tinydns*");
135
	exec("rm /usr/local/pkg/*tinydns*");
136
	exec("rm /usr/local/pkg/pf/*tinydns*");
137
	exec("rm -rf /etc/tinydns");
138
	filter_configure();
139
	conf_mount_ro();
140
}
141

    
142
function tinydns_custom_php_changeip_command() {
143
	global $g, $config;
144
	conf_mount_rw();
145

    
146
	$ip = $config['interfaces']['lan']['ipaddr'];
147
	$ipmask = $config['interfaces']['lan']['subnet'];
148
	$arr = tinydns_get_ip_subnet_arpa($ip, $ipmask);
149
	$dnscacheip = $ip;
150
	$dnsuserip = $arr[0];
151

    
152
	/* For now force $dnsserverip to to 127.0.0.1 unless a separate IP is specified */
153
	$localhost = "127.0.0.1";
154
	$dnsserverip = $localhost;
155
	if($config['installedpackages']['tinydns']['config'][0]['ipaddress'] != $localhost AND $config['installedpackages']['tinydns']['config'][0]['ipaddress'] != "")
156
		$dnsserverip = $config['installedpackages']['tinydns']['config'][0]['ipaddress'];
157
	if($config['installedpackages']['tinydns']['config'][0]['regdhcpstatic'] OR $config['installedpackages']['tinydns']['config'][0]['regdhcp']) 
158
		$dnsserverip = $localhost;
159
	$config['installedpackages']['tinydns']['config'][0]['ipaddress'] = $dnsserverip;
160

    
161
	$updatecron = $config['installedpackages']['tinydns']['config'][0]['updatecron'];
162

    
163
	/* Populate Zone Transfer array */
164
	$ztipaddress = populate_zt_array();
165

    
166
	/* setup daemon tools service area */
167
	if(!is_dir("/service"))	
168
		exec("/bin/mkdir /service");
169

    
170
	exec("/usr/sbin/pw useradd Gtinydns");
171
	exec("/usr/sbin/pw useradd Gdnslog");
172
	exec("/usr/sbin/pw useradd Gdnscache");
173
	exec("/usr/sbin/pw useradd Gaxfrdns");
174

    
175
	/* TinyDNS Server */
176
	exec("/usr/local/bin/tinydns-conf Gtinydns Gdnslog /etc/tinydns {$dnsserverip}");
177
	exec("/bin/ln -s /etc/tinydns /service/");
178

    
179
	/* AXFRDNS - Zone transfers */
180
	if(is_array($ztipaddress)) {
181
		exec("/usr/local/bin/axfrdns-conf Gaxfrdns Gdnslog /etc/axfrdns /etc/tinydns {$dnsserverip}");
182
                tinydns_setup_axfrdns();
183
        }
184
	exec("/bin/ln -s /etc/axfrdns /service/");
185

    
186
	exec("echo {$dnsserverip} > /etc/tinydns/env/IP");
187
	exec("/usr/bin/killall -9 tinydns");
188

    
189
	if($config['installedpackages']['tinydns']['config'][0]['enableforwarding']) {
190
		if(!is_dir("/service/dnscache")) {
191
			exec("/usr/sbin/pw useradd Gdnscache");
192
			exec("/usr/local/bin/dnscache-conf Gdnscache Gdnslog /etc/dnscache {$dnscacheip}");
193
			exec("/bin/ln -s /etc/dnscache /service/");
194
			exec("/bin/cp /var/etc/resolv.conf /var/etc/resolv.conf.original");
195
			exec("/bin/cp /var/etc/resolv.conf /var/etc/resolv.conf.dnscache");
196
		}
197
		exec("echo {$dnscacheip} > /etc/dnscache/env/IP");
198
		tinydns_dnscache_forwarding_servers();
199
		exec("touch /etc/dnscache/root/ip/{$dnsuserip}");
200
		tinydns_create_soa_domain_list($dnsserverip);
201
		exec("echo domain {$config['system']['domain']} > /var/etc/resolv.conf");
202
		exec("echo nameserver {$dnscacheip} >> /var/etc/resolv.conf");
203
		exec("/usr/bin/killall -9 dnscache");
204
	} else {
205
		if(file_exists("/var/etc/resolv.conf.original"))
206
			exec("/bin/cp /var/etc/resolv.conf.original /var/etc/resolv.conf");
207
		if(is_dir("/etc/dnscache")) {
208
			dnscache_use_root_servers();
209
			exec("/usr/bin/killall -9 dnscache");
210
		}
211
	}
212
	conf_mount_ro();
213
	filter_configure();
214
}
215

    
216
function populate_zt_array() {
217
	global $g, $config;	
218
	/* Populate Zone Transfer array */
219
	if($config['installedpackages']['tinydns']['config'][0]['row']) {
220
		$ztipaddress = array();
221
		foreach($config['installedpackages']['tinydns']['config'][0]['row'] as $zt) {
222
			$tmp = array();
223
			$tmp['ztipaddress'] = $zt['ztipaddress'];
224
			$tmp['dnszone'] = $zt['dnszone'];
225
			$ztipaddress[] = $tmp;
226
		}
227
	}
228
	return $ztipaddress;
229
}
230

    
231
function tinydns_setup_axfrdns() {
232
	global $g, $config;
233
	/* Populate Zone Transfer array */
234
	$ztipaddress = populate_zt_array();
235
	if(!is_array($ztipaddress)) 
236
		return;
237
	$fd = fopen("/etc/axfrdns/tcp","w");
238
	if(!$fd) {
239
		log_error("Could not open /etc/axfrdns/tcp for writing");
240
		return;
241
	}
242
        
243
        // Add a default deny rule
244
        fwrite($fd, ":deny\n");
245
        
246
	foreach($ztipaddress as $zt) {
247
		if($zt['ztipaddress'] && $zt['dnszone'])
248
			$zonet = "{$zt['ztipaddress']}:allow";
249
			if($zt['dnszone'] <> "*")
250
				$zonet .= ",AXFR=\"{$zt['dnszone']}\"";
251
			fwrite($fd, $zonet . "\n");
252
	}
253
	fclose($fd);
254
	// Recompile database
255
	exec("cd /service/axfrdns && tcprules tcp.cdb tcp.tmp < tcp");
256
}
257

    
258
function tinydns_get_record_status($record, $pingthreshold = "", $wanpingthreshold = "") {
259
	global $g, $config;
260
	if(file_exists("/var/db/pingstatus/{$record}")) {
261
		$status = "";
262
		$status = file_get_contents("/var/db/pingstatus/{$record}");
263
		if(stristr($status,"DOWN"))
264
			return "DOWN";
265
	}
266
	if($pingthreshold) {
267
		$current_ms = "";
268
		if(file_exists("var/db/pingmsstatus/$record"))
269
			$current_ms = file_get_contents("/var/db/pingmsstatus/$record");
270
		if($pingthreshold > $current_ms)
271
			return "DOWN";
272
	}
273
	if($wanpingthreshold) {
274
		$current_avg = "";
275
		if(file_exists("/var/db/wanaverage"))
276
			$current_avg = file_get_contents("/var/db/wanaverage");
277
		if($wanpingthreshold > $current_avg)
278
			return "DOWN";
279
	}
280
	return "UP";
281
}
282

    
283
function tinydns_get_backup_record($record) {
284
	global $g, $config;
285
	if($config['installedpackages']['tinydnsdomains']) {
286
		foreach($config['installedpackages']['tinydnsdomains']['config'] as $domain) {
287
			if($domain['ipaddress'] == $record) {
288
				/* if no failover host exists, simply return original record */
289
				if(!$domain['row'])
290
					return $record;
291
				foreach($domain['row'] as $row) {
292
					$status = tinydns_get_record_status($row['failoverip']);
293
					if($status == "UP")
294
						return $row['failoverip'];
295
				}
296
			}
297
		}
298
	}
299
	return $record;
300
}
301

    
302
function tinydns_setup_ping_items() {
303
	global $g, $config;
304
	if(!$config['installedpackages']['tinydnsdomains'])
305
		return;
306
	$wanif = get_real_wan_interface();
307
	$ip = find_interface_ip($wanif);
308
	conf_mount_rw();
309
	$processed = array();
310
	/* XXX: make this work with other packages */
311
	$fd = fopen("/var/db/pkgpinghosts", "w");
312
	if(!$fd) {
313
		log_error("Could not open /var/db/pkgpinghosts for writing.");
314
		return;
315
	}
316
	config_lock();
317
	/*   write out each ip address so ping_hosts.sh can begin monitoring ip
318
	 *   status and create a database of the status information that we can use.
319
	 */
320
	foreach($config['installedpackages']['tinydnsdomains']['config'] as $domain) {
321
		if(!in_array($domain['ipaddress'], $processed)) {
322
			fwrite($fd, $ip . "|" . $domain['ipaddress'] . "|1|/usr/local/pkg/tinydns_down.php|/usr/local/pkg/tinydns_up.php\n");
323
			$processed[] = $domain['ipaddress'];
324
		}
325
		if($domain['monitorip'] <> "")
326
			$monitorip = $domain['monitorip'];
327
		if($domain['row']) {
328
			foreach($domain['row'] as $row) {
329
				if($row['pingthreshold'])
330
					$pingthreshold = $row['pingthreshold'];
331
				else
332
					$row['pingthreshold'] = "";
333
				if($row['monitorip']) {
334
					if(!in_array($row['monitorip'], $processed)) {
335
						fwrite($fd, $ip . "|" . $row['monitorip'] . "|1|/usr/local/pkg/tinydns_down.php|/usr/local/pkg/tinydns_up.php|{$pingthreshold}\n");
336
						$processed[] = $row['monitorip'];
337
					}
338
				} else {
339
					if(!in_array($monitorip, $processed)) {
340
						fwrite($fd, $ip . "|" . $monitorip . "|1|/usr/local/pkg/tinydns_down.php|/usr/local/pkg/tinydns_up.php|{$pingthreshold}\n");
341
						$processed[] = $monitorip;
342
					}
343
				}
344
			}
345
		}
346
		if($domain['monitorip']) {
347
			if(!in_array($domain['monitorip'], $processed)) {
348
				fwrite($fd, $ip . "|" . $domain['monitorip'] . "|1|/usr/local/pkg/tinydns_down.php|/usr/local/pkg/tinydns_up.php|{$pingthreshold}\n");
349
				$processed[] = $domain['monitorip'];
350
			}
351
		} else {
352
			if(!in_array($row['failoverip'], $processed)) {
353
				fwrite($fd, $ip . "|" . $row['failoverip'] . "|1|/usr/local/pkg/tinydns_down.php|/usr/local/pkg/tinydns_up.php|{$pingthreshold}\n");
354
				$processed[] = $row['failoverip'];
355
			}
356
		}
357
	}
358
	fclose($fd);
359
	config_unlock();
360
	conf_mount_ro();
361
}
362

    
363
function tinydns_create_zone_file() {
364
	global $g, $config;
365
	conf_mount_rw();
366
	if(file_exists("/tmp/config.cache"))
367
		unlink("/tmp/config.cache");
368
	parse_config(true);
369
	config_lock();
370
	if(file_exists("/service/tinydns/root/data"))
371
		exec("rm -f /service/tinydns/root/data");
372
	if(!is_dir("/service/tinydns/root"))
373
		return;
374
	$fd = fopen("/service/tinydns/root/data", "w");
375
	if(!$fd) {
376
		log_error("Could not open /service/tinydns/root/data for writing.");
377
		return;
378
	}
379
	
380
	/* For now do not allow registration of 'local' DNS data if tinyDNS not bound to 127.0.0.1 */
381
	if($config['installedpackages']['tinydns']['config'][0]['ipaddress'] == "127.0.0.1") {
382
		/* Load the root servers if Forwarding is enabled  */
383
		/* Register LAN IP and SOA Forward and Reverse DNS recors in TinyDNS Server*/
384
		if($config['installedpackages']['tinydns']['config'][0]['enableforwarding']) {
385
			$forwardingservers = tinydns_register_root_servers();
386
			if($forwardingservers) 
387
				fwrite($fd, $forwardingservers);
388
			if($config['system']['hostname']['domain']) {
389
				$dhcpdhostname = $config['system']['hostname'];		
390
				if($config['dhcpd']['lan'])
391
					$dhcpddomain = $config['system']['domain'];
392
				$dhcpdlanip = $config['interfaces']['lan']['ipaddr'];		
393
				$dhcpdipmask = $config['interfaces']['lan']['subnet'];		
394
				$dhcpdfqdn = "{$dhcpdhostname}.{$dhcpddomain}";
395
				tinydns_complete_soa_record($fd, $dhcpdlanip, $dhcpdipmask, $dhcpdhostname, $dhcpddomain);
396
			}
397
		}
398
	
399
		/* Register Static IPs */
400
        if($config['installedpackages']['tinydns']['config'][0]['regdhcpstatic']) {
401
                foreach($config['dhcpd'] as $zone_key => $zone ) {
402
                        $dhcpdhostname = $config['system']['hostname'];
403
                        if ($zone['ddnsdomain']) 
404
                                $dhcpddomain = $zone['ddnsdomain'];
405
                        else
406
                                $dhcpddomain = $config['system']['domain'];
407
                        $dhcpdlanip = $config['interfaces'][$zone_key]['ipaddr'];
408
                        $dhcpdipmask = $config['interfaces'][$zone_key]['subnet'];
409
                        $dhcpdfqdn = "{$dhcpdhostname}.{$dhcpddomain}";
410
                        tinydns_complete_soa_record($fd, $dhcpdlanip, $dhcpdipmask, $dhcpdhostname, $dhcpddomain);
411
       
412
                        if(is_array($zone['staticmap'])) {
413
                                foreach($zone['staticmap'] as $dhcpdstatic) {
414
                                        $dhcpdhostname = $dhcpdstatic['hostname'];
415
                                        $dhcpdfqdn = "{$dhcpdhostname}.{$dhcpddomain}";
416
                                        $dhcpdlanip = $dhcpdstatic['ipaddr'];
417
                                        $dhcpda = "={$dhcpdfqdn}:{$dhcpdlanip}";
418
                                        if($dhcpdhostname)
419
                                                fwrite($fd, $dhcpda . "\n");
420
                                }
421
                        }
422
                }
423
        }
424
	
425
		/* Register Dynamic IPs */
426
		if($config['installedpackages']['tinydns']['config'][0]['regdhcp']) {
427
			$leasesfile = "{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases";
428
			$fl = fopen($leasesfile, "r");
429
			if(!$fl) {
430
				log_error("Could not open {$leasesfile} for reading.");
431
				return;
432
			}
433
			tinydns_add_active_leases($fl,$fd, $dhcpddomain);
434
		}
435
	}
436
		
437
	if($config['installedpackages']['tinydnsdomains']) {
438
		for($x=0; $x< count($config['installedpackages']['tinydnsdomains']['config']); $x++) {
439
			$domain = $config['installedpackages']['tinydnsdomains']['config'][$x];
440
			$record_data = "";
441
			$hostname = $domain['hostname'];
442
			$ipaddress = $domain['ipaddress'];
443
			$ttl = $domain['ttl'];
444
			$dist = $domain['dist'];
445
			/*   check record status, if it is down request
446
			 *   backup server if defined.
447
			 */
448
			if($domain['monitorip'])
449
				$monitorip = $domain['monitorip'];
450
			if($monitorip) {
451
				$status = tinydns_get_record_status($monitorip);
452
				if($status == "DOWN") {
453
					if($debug)
454
						log_error("$ipaddress monitor ip $monitorip is offline.");
455
					$ipaddress = tinydns_get_backup_record($ipaddress);
456
					if($debug)
457
						log_error("tinydns_get_backup_record returned $ipaddress ");
458
				}
459
			}
460
			$record_data = tinydns_get_rowline_data($ipaddress, $domain['recordtype'], $ttl, $hostname, $domain['rdns'], $dist);
461
			if($record_data) 
462
				fwrite($fd, $record_data . "\n");
463
			/* process load balanced items */
464
			if($domain['row']) {
465
				foreach($domain['row'] as $row) {
466
					if($row['loadbalance']) {
467
						if($row['pingthreshold'])
468
							$pingthreshold = $row['pingthreshold'];
469
						else
470
							$pingthreshold = "";
471
						if($row['wanpingthreshold'])
472
							$wanpingthreshold =	$row['wanpingthreshold'];
473
						else
474
							$wanpingthreshold = "";
475
						$status = tinydns_get_record_status($row['failoverip'], $pingthreshold, $wanpingthreshold);
476
						if($status == "DOWN") {
477
							$record_data = tinydns_get_rowline_data($row['failoverip'], $domain['recordtype'], $ttl, $hostname, "");
478
							fwrite($fd, $record_data . "\n");
479
						}
480
					}
481
				}
482
			}
483
		}
484
	}
485
	fclose($fd);
486
	/* tell tinydns to reload zone file */
487
	exec("cd /service/tinydns/root && /usr/local/bin/tinydns-data");
488
	config_unlock();
489
	conf_mount_ro();
490
}
491

    
492
function tinydns_sync_on_changes() {
493
	global $g, $config;
494
	log_error("[tinydns] tinydns_xmlrpc_sync.php is starting.");
495
	$synconchanges = $config['installedpackages']['tinydnssync']['config'][0]['synconchanges'];	
496
	if(!$synconchanges) 
497
		return;
498
	$sync_hosts = $config['installedpackages']['tinydnssync']['config'];
499
	$previous_ip = "";
500
	$x=0;
501
	$sh = $config['installedpackages']['tinydnssync']['config'][0];
502
	for($x=1; $x<5; $x++) {
503
		if($x > 1) 
504
			$counter = $x;
505
		else 
506
			$counter = "";
507
		$sync_to_ip = "";
508
		$password = "";
509
		if($sh['ipaddress' . $counter]) {
510
			$sync_to_ip = $sh['ipaddress' . $counter];
511
			$password   = $sh['password'  . $counter];
512
		}
513
		if($password && $sync_to_ip)
514
			tinydns_do_xmlrpc_sync($sync_to_ip, $password);
515
	}
516
	tinydns_create_zone_file();
517
	tinydns_setup_ping_items();
518
	log_error("[tinydns] tinydns_xmlrpc_sync.php is ending.");
519
}
520

    
521
function tinydns_do_xmlrpc_sync($sync_to_ip, $password) {
522
	global $config, $g;
523

    
524
	if(!$password)
525
		return;
526

    
527
	if(!$sync_to_ip)
528
		return;
529

    
530
	$xmlrpc_sync_neighbor = $sync_to_ip;
531
    if($config['system']['webgui']['protocol'] != "") {
532
		$synchronizetoip = $config['system']['webgui']['protocol'];
533
		$synchronizetoip .= "://";
534
    }
535
    $port = $config['system']['webgui']['port'];
536
    /* if port is empty lets rely on the protocol selection */
537
    if($port == "") {
538
		if($config['system']['webgui']['protocol'] == "http") 
539
			$port = "80";
540
		else 
541
			$port = "443";
542
    }
543
	$synchronizetoip .= $sync_to_ip;
544

    
545
	/* xml will hold the sections to sync */
546
	$xml = array();
547
	$xml['tinydnsdomains'] = $config['installedpackages']['tinydnsdomains'];
548

    
549
	/* assemble xmlrpc payload */
550
	$params = array(
551
		XML_RPC_encode($password),
552
		XML_RPC_encode($xml)
553
	);
554

    
555
	/* set a few variables needed for sync code borrowed from filter.inc */
556
	$url = $synchronizetoip;
557
	log_error("Beginning TinyDNS XMLRPC sync to {$url}:{$port}.");
558
	$method = 'pfsense.merge_installedpackages_section_xmlrpc';
559
	$msg = new XML_RPC_Message($method, $params);
560
	$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
561
	$cli->setCredentials('admin', $password);
562
	if($g['debug'])
563
		$cli->setDebug(1);
564
	/* send our XMLRPC message and timeout after 250 seconds */
565
	$resp = $cli->send($msg, "250");
566
	if(!$resp) {
567
		$error = "A communications error occured while attempting tinydns XMLRPC sync with {$url}:{$port}.";
568
		log_error($error);
569
		file_notice("sync_settings", $error, "tinydns Settings Sync", "");
570
	} elseif($resp->faultCode()) {
571
		$cli->setDebug(1);
572
		$resp = $cli->send($msg, "250");
573
		$error = "An error code was received while attempting tinydns XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
574
		log_error($error);
575
		file_notice("sync_settings", $error, "tinydns Settings Sync", "");
576
	} else {
577
		log_error("tinydns XMLRPC sync successfully completed with {$url}:{$port}.");
578
	}
579
	
580
	/* tell tinydns to reload our settings on the destionation sync host. */
581
	$method = 'pfsense.exec_php';
582
	$execcmd  = "require_once('/usr/local/pkg/tinydns.inc');\n";
583
	$execcmd .= "tinydns_custom_php_changeip_command();\n";
584
	$execcmd .= "tinydns_create_zone_file();\n";
585
	$execcmd .= "tinydns_setup_ping_items();\n";
586
	
587
	/* assemble xmlrpc payload */
588
	$params = array(
589
		XML_RPC_encode($password),
590
		XML_RPC_encode($execcmd)
591
	);
592

    
593
	log_error("tinydns XMLRPC reload data {$url}:{$port}.");
594
	$msg = new XML_RPC_Message($method, $params);
595
	$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
596
	$cli->setCredentials('admin', $password);
597
	$resp = $cli->send($msg, "250");
598
	if(!$resp) {
599
		$error = "A communications error occured while attempting tinydns XMLRPC sync with {$url}:{$port} (pfsense.exec_php).";
600
		log_error($error);
601
		file_notice("sync_settings", $error, "tinydns Settings Sync", "");
602
	} elseif($resp->faultCode()) {
603
		$cli->setDebug(1);
604
		$resp = $cli->send($msg, "250");
605
		$error = "An error code was received while attempting tinydns XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
606
		log_error($error);
607
		file_notice("sync_settings", $error, "tinydns Settings Sync", "");
608
	} else {
609
		log_error("tinydns XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php).");
610
	}
611

    
612
}
613

    
614
/* formats data as a tinydns data row item */
615
function tinydns_get_rowline_data($recordip, $recordtype, $ttl, $hostname, $rdns, $dist) {
616
		if($ttl)
617
			$ttl_string = ":{$ttl}";
618
		else
619
			$ttl_string = "";
620

    
621
		switch ($recordtype) {
622

    
623
			/* Note that some of these are simplistic versions of TinyDNS record handling.  Uber-users can always do "raw" entries...  */
624
			case "SOA":	
625
				//     .fqdn:ip:x:ttl:timestamp:lo
626
				$record_data = ".{$hostname}::{$recordip}{$ttl_string}";
627
				break;
628
			case "NS":
629
				//      &serious.panic.mil:1.8.248.6:a
630
				$record_data = "&{$hostname}:{$recordip}{$ttl_string}";
631
				break;
632
			case "MX":
633
				//      @fqdn:ip:x:dist:ttl:timestamp:lo
634
				if(!$dist)
635
					$dist = "10"; // default to 10 if no preference has been defined.  0 is ugly.
636
				$record_data = "@{$hostname}:{$recordip}::{$dist}{$ttl_string}";
637
				break;
638
			case "PTR":
639
				/* "^" creates "PTR" record only to allow reverse DNS */
640
				//		^fqdn:p:ttl:timestamp:lo
641
				$record_data = "^{$hostname}:{$recordip}{$ttl_string}";
642
				break;
643
			case "A":
644
				/* "=" creates both "A" and "PTR" records to allow both forward and reverse DNS */
645
				if($rdns) {
646
					//		=fqdn:ip:ttl:timestamp:lo
647
					$record_data = "={$hostname}:{$recordip}{$ttl_string}";
648
				} else {
649
					/* "+" creates "A" records only to allow forward DNS */
650
					//		+fqdn:ip:ttl:timestamp:lo
651
					$record_data = "+{$hostname}:{$recordip}{$ttl_string}";
652
				}
653
				break;
654
			case "CNAME":
655
				//     Cfqdn:p:ttl:timestamp:lo
656
				$record_data = "C{$hostname}:{$recordip}{$ttl_string}";
657
				break;
658
			case "TXT":
659
				/* "'" creates "TXT" record                                                             */
660
				/* ":" creates a generic record entry, (and record code 16 below makes it a TXT record) */
661
				/* Q: Why bother with generic?                                                          */
662
				/* A: TinyDNS TXT records get split up every 127 chars and some clients have trouble re-assembling them.                               */
663
				/* TinyDNS generic records allow up to the maximum DNS record size of 255 chars but it is a hard limit, no splitting of larger strings */
664
				/* ...so try to always create the best record for the need                                                                             */
665
				/* Initial cleanup required for TXT records in TinyDNS where we substitute Octal escape codes for certain chars*/
666
				$saferecordip = str_replace(":", "\\072", $recordip);
667
				$saferecordip = str_replace(" ", "\\040", $saferecordip);
668
				$saferecordip = str_replace("\r", "\\015", $saferecordip);
669
				$saferecordip = str_replace("\n", "\\012", $saferecordip);
670
				/* Logically this should be comparing against 127 and 255 but PHP has a boundary error?                 */
671
				/* Boundary errors or not, 128 and 256 at least evaluate properly!!!                                    */
672
				/* Also note that reclen checks against the original string and not the "safe" one we generated above.  */
673
				$reclen = mb_strlen($recordip, '8bit');
674
				if($reclen > 128 && $reclen <= 256) {	
675
					/* TinyDNS generic records require an escaped Octal string length padded to three chars before the actual string!    */
676
					/* The logic here shouldn't ever require padding but including it anyway in case somebody changes code down the road */
677
					$reclen = str_pad(decoct($reclen),3,"0",STR_PAD_LEFT);
678
					//		:fqdn:n:rdata:ttl:timestamp:lo
679
					$record_data = ":{$hostname}:16:\\{$reclen}{$saferecordip}{$ttl_string}";
680
				} else {
681
					//		'fqdn:s:ttl:timestamp:lo
682
					$record_data = "'{$hostname}:{$saferecordip}{$ttl_string}";
683
				}	
684
				break;
685
			case "raw":
686
				/* We don't know or care what is in a raw entry, just pass it along as-is */
687
				$record_data = "{$recordip}";
688
				break;				
689
		}
690
		return $record_data;
691
}
692

    
693
/* Returns the last IP byte and the Trimmed IP*/
694
function tinydns_get_lastip_byte($ipsub) {
695
	$len= strlen($ipsub); 	
696
	$pos = strrpos($ipsub, ".");
697
	$last_byte = "";
698
	if ($pos === false) { 
699
			$last_byte = $ipsub; 
700
			return array ($last_byte,$ipsub); 
701
	} 
702
	$last_byte = substr($ipsub,$pos + 1);
703
	$ipsub = substr($ipsub,0,$pos);
704
	return array ($last_byte,$ipsub);
705
}
706

    
707
/* in-add.arpa IP calculated from D.C.B.A and Mask to A.B.C.D.in-addr.arpa */
708
/* subnet IP calculated from A.B.C.D and Mask */
709
function tinydns_get_ip_subnet_arpa($ip, $ipmask) {
710
	$ipsub = $ip;	
711
	$arpaip = "";	
712
	$array = tinydns_get_lastip_byte($ipsub);
713
	$a = $array[0];
714
	$ipsub = $array[1];
715
	$array = tinydns_get_lastip_byte($ipsub);
716
	$b = $array[0];
717
	$ipsub = $array[1];
718
	$array = tinydns_get_lastip_byte($ipsub);
719
	$c = $array[0];
720
	$ipsub = $array[1];
721
	$array = tinydns_get_lastip_byte($ipsub);
722
	$d = $array[0];
723
	$ipsub = $array[1];
724
	switch ($ipmask) {
725
		case ($ipmask <= 32 AND $ipmask > 24):
726
			$s = 32 - $ipmask;
727
			$a >> $s;
728
			$arpaip = "{$a}.{$b}.{$c}.{$d}.in-addr.arpa";
729
			$subnet = "{$d}.{$c}.{$b}.{$a}";
730
			break;
731
		case ($ipmask <= 24 AND $ipmask > 16):
732
			$s = 24 - $ipmask;
733
			$b >> $s;
734
			$arpaip = "{$b}.{$c}.{$d}.in-addr.arpa";
735
			$subnet = "{$d}.{$c}.{$b}";
736
			break;
737
		case ($ipmask <= 16 AND $ipmask > 8):
738
			$s = 16 - $ipmask;
739
			$c >> $s;
740
			$arpaip = "{$c}.{$d}.in-addr.arpa";
741
			$subnet = "{$d}.{$c}";
742
			break;
743
		case ($ipmask <= 8 AND $ipmask > 0):
744
			$s = 8 - $ipmask;
745
			$d >> $s;
746
			$arpaip = "{$d}.in-addr.arpa";
747
			$subnet = "{$d}";
748
			break;
749
		}
750
	return 	array($subnet,$arpaip);
751
}
752

    
753
/* Create a Forward and a Reverse DNS (SOA, A, PTR) records for Fully Qualififed Domain Name*/
754
function tinydns_complete_soa_record($fd, $ip, $ipmask, $nsname, $domain) {
755
	$fqdn = "{$nsname}.{$domain}";
756
	$rip = tinydns_get_ip_subnet_arpa($ip, $ipmask);
757
	$soa = ".{$domain}::{$fqdn}";
758
	$rsoa = ".{$rip[1]}::{$fqdn}";
759
	$a = "={$fqdn}:{$ip}";
760
	if($fqdn)
761
		fwrite($fd, $soa . "\n");
762
	if($rip) 
763
		fwrite($fd, $rsoa . "\n");
764
	if($nsname) 
765
		fwrite($fd, $a . "\n");
766
}
767

    
768
/* Search for active leases in the dhcpd.leases file and add them to tinyDNS */
769
/* Currently it will add duplicate leases that are ignored by thee tinyDNS server*/
770
/* Should duplicate leases be purged by DCHCP server in a remove stale records operation? */
771
function tinydns_add_active_leases($fl,$fd, $leasedomain) {
772
	$i = 0;
773
	$lip = strlen("lease") + 1;
774
	$lis = strlen("binding state active");
775
	$lic = strlen("client-hostname");
776
	$leaseip = "";
777
	$leasestatus = "";
778
	$leasehostname = "";
779
	while (!feof($fl)) {
780
		$leases = fgets($fl, 4096);
781
		$discard = ($leases[0] == "#") OR ($leases[0] == "\n"); 
782
		if(!$discard) {
783
			if($leaseip == "") {
784
				if ($leaseip = strstr($leases,"lease")) {
785
					$leaseip = substr($leaseip,$lip,strpos($leases,"{") - $lip - 1);
786
				}
787
			}
788
			elseif($leasestatus == FALSE) {
789
				if (stristr($leases,"binding state active")) {
790
					$leasestatus = TRUE;
791
				}
792
			}
793
			elseif($leasestatus == TRUE AND $leasehostname == "") {
794
				if($leasehostname = stristr($leases,"client-hostname")) {
795
					$qstrt = strpos($leasehostname,'"') + 1;
796
					$qlen  = strrpos($leasehostname,'"') - $qstrt;
797
					$leasehostname = substr($leasehostname,$qstrt,$qlen);
798
				}
799
			}
800
			if($leases[0] == "}") {
801
				$leasefqdn = "{$leasehostname}.{$leasedomain}";
802
				$leasea = "={$leasefqdn}:{$leaseip}";
803
				if($leasehostname AND $leasestatus)fwrite($fd, $leasea . "\n");
804
				$leaseip = "";
805
				$leasehostname = "";
806
				$leasestatus = FALSE;
807
			}
808
			$i = $i + 1;
809
		}
810
	}
811
	fclose($fl);
812
	$leaselines = $i;
813
}
814

    
815
function tinydns_get_dns_record_type($tinydnsrecord) {
816
	$rtype = "";
817
	$rtype2 = "";
818
	$rdns = "";
819
	switch ($tinydnsrecord) {
820
		case($tinydnsrecord[0] == "."):
821
			$rtype = "SOA";
822
			$rtype2 = "NS";
823
			break;
824
		case($tinydnsrecord[0] == "="):
825
			$rtype = "A";
826
			$rtype2 = "PTR";
827
			$rdns = "on";
828
			break;
829
		case($tinydnsrecord[0] == "+"):
830
			$rtype = "A";
831
			break;
832
		case($tinydnsrecord[0] == "@"):
833
			$rtype = "MX";
834
			break;
835
		case($tinydnsrecord[0] == "^"):
836
			$rtype = "PTR";
837
			$rdns = "on";
838
			break;
839
		case($tinydnsrecord[0] == "&"):
840
			$rtype = "NS";
841
			break;
842
		case($tinydnsrecord[0] == "'"):
843
			$rtype = "TXT";
844
			break;
845
		case($tinydnsrecord[0] == "C"):
846
			$rtype = "CNAME";
847
			break;
848
		case($tinydnsrecord[0] == "Z"):
849
			$rtype = "SOA";
850
			break;
851
		default:
852
			$rtype = "";
853
	}
854
	return array ($rtype, $rtype2, $rdns);
855
}
856

    
857
/* This function will be replaced by an auto detect DNS cache servers routine */
858
/* At the moment there is no tagging of DNSroute to a WAN port. It needs to be added  */
859
function tinydns_dnscache_forwarding_servers() {
860
	$fr = fopen("/var/etc/resolv.conf.dnscache", "r");
861
	if (! $fr) {
862
		printf("Error: cannot open resolv.conf.dnscache in tinydns_register_forwarding_servers().\n");
863
		return 1;
864
	}
865
	$lip = strlen("nameserver") + 1;
866
	$j = 0;
867
	$iprecords = "";
868
	while (!feof($fr)) {
869
		$routers = fgets($fr, 4096);
870
		$discard = ($routers[0] == "\n"); 
871
		if(!$discard) {
872
			if ($routerip = strstr($routers,"nameserver")) {
873
				$routerip = substr($routerip,$lip);
874
				if($routerip) {
875
					$j += 1;
876
					$routera = "{$routerip}";
877
					$iprecords .= $routera;
878
				}	
879
			}
880
		}	
881
	}
882
	fclose($fr);
883
	exec("echo 1 > /etc/dnscache/env/FORWARDONLY");
884
	if(is_dir("/etc/dnscache/root/servers/")) 
885
		exec("rm -R /etc/dnscache/root/servers/");
886
	exec("mkdir /etc/dnscache/root/servers/");
887
	$fr = fopen("/etc/dnscache/root/servers/@", "w");
888
	if (! $fr) {
889
		printf("Error: cannot write to /etc/dnscache/root/servers/@ in tinydns_dnscache_forwarding_servers().\n");
890
		return 1;
891
	}
892
	if($iprecords)
893
		fwrite($fr, $iprecords);
894
	fclose($fr);
895
}
896

    
897
/* This routine adds filenames to /etc/dnscache/root/servers/ with the contents pointing to the tinyDNS server */
898
function tinydns_create_soa_domain_list($dnsserverip) {
899
	if(file_exists("/service/tinydns/root/data"))
900
		$tinydns_data = file_get_contents("/service/tinydns/root/data");
901
	else
902
		$tinydns_data = "";
903
	$datalen = strlen($tinydns_data);
904
	$startofrecord = 0;
905
	while ($startofrecord < $datalen ) {	
906
		$endofrecord = strpos($tinydns_data,"\n",$startofrecord);
907
		$dnsrecord = substr($tinydns_data,$startofrecord,$endofrecord-$startofrecord);
908
		$startofrecord = $endofrecord + 1;
909
		
910
		$col1 = strpos($dnsrecord,":");
911
		$fqdn = substr($dnsrecord,1,$col1-1);
912
		if($fqdn) {
913
			$rtypes = tinydns_get_dns_record_type($dnsrecord);
914
			if($rtypes[0] == "SOA") {
915
				$fr = fopen("/etc/dnscache/root/servers/{$fqdn}", "w");
916
				if (! $fr) {
917
					printf("Error: cannot open /etc/dnscache/root/servers/{$fqdn} in tinydns_create_soa_domain_list().\n");
918
					return 1;
919
				}
920
				if($fqdn)fwrite($fr, $dnsserverip);
921
				fclose($fr);
922
			}
923
		}	
924
	}	
925
}
926

    
927
/* This function is not called */
928
/* At the moment there is no tagging of DNSroute to a WAN port. It needs to be added  */
929
function tinydns_register_forwarding_servers() {
930
	$fr = fopen("/var/etc/resolv.conf", "r");
931
	if (! $fr) {
932
		printf("Error: cannot open resolv.conf in tinydns_register_forwarding_servers().\n");
933
		return 1;
934
	}
935
	$lip = strlen("nameserver") + 1;
936
	$j = 0;
937
	$nsrecords = "";
938
	$arecords = "";
939
	while (!feof($fr)) {
940
		$routers = fgets($fr, 4096);
941
		$discard = ($routers[0] == "\n"); 
942
		if(!$discard) {
943
			if ($routerip = strstr($routers,"nameserver")) {
944
				$routerip = substr($routerip,$lip);
945
				if($routerip) {
946
					$j += 1;
947
					$routerfqdn = "DNSroute-{$j}.wan{$j}";
948
					$routerns = "&::{$routerfqdn}";
949
					$routera = "={$routerfqdn}:{$routerip}";
950
					$nsrecords .= $routerns . "\n";
951
					$arecords .= $routera;
952
				}	
953
			}
954
		}	
955
	}
956
	fclose($fr);
957
	$dnsroutes ="{$nsrecords}{$arecords}";
958
	return $dnsroutes;
959
}
960

    
961
function tinydns_register_root_servers() {
962
	$rootservers =<<<EOD
963
&::a.root-servers.net
964
&::b.root-servers.net
965
&::c.root-servers.net
966
&::d.root-servers.net
967
&::e.root-servers.net
968
&::f.root-servers.net
969
&::g.root-servers.net
970
&::h.root-servers.net
971
&::i.root-servers.net
972
&::j.root-servers.net
973
&::k.root-servers.net
974
&::l.root-servers.net
975
&::m.root-servers.net
976
=a.root-servers.net:198.41.0.4
977
=b.root-servers.net:192.228.79.201
978
=c.root-servers.net:192.33.4.12
979
=d.root-servers.net:128.8.10.90
980
=e.root-servers.net:192.203.230.10
981
=f.root-servers.net:192.5.5.241
982
=g.root-servers.net:192.112.36.4
983
=h.root-servers.net:128.63.2.53
984
=i.root-servers.net:192.36.148.17
985
=j.root-servers.net:192.58.128.30
986
=k.root-servers.net:193.0.14.129
987
=l.root-servers.net:199.7.83.42
988
=m.root-servers.net:202.12.27.33
989

    
990
EOD;
991
	return $rootservers;
992
}
993

    
994
function dnscache_use_root_servers() {
995
	$rootservers =<<<EOD
996
198.41.0.4
997
192.228.79.201
998
192.33.4.12
999
128.8.10.90
1000
192.203.230.10
1001
192.5.5.241
1002
192.112.36.4
1003
128.63.2.53
1004
192.36.148.17
1005
192.58.128.30
1006
193.0.14.129
1007
199.7.83.42
1008
202.12.27.33
1009

    
1010
EOD;
1011

    
1012
	exec("echo 0 > /etc/dnscache/env/FORWARDONLY");
1013
	if(is_dir("/etc/dnscache/root/servers/")) 
1014
		exec("rm -R /etc/dnscache/root/servers/");
1015
	exec("mkdir /etc/dnscache/root/servers/");
1016
	$fr = fopen("/etc/dnscache/root/servers/@", "w");
1017
	if (! $fr) {
1018
		printf("Error: cannot write to /etc/dnscache/root/servers/@ in dnscache_use_root_servers().\n");
1019
		return 1;
1020
	}
1021
	fwrite($fr, $rootservers);
1022
	fclose($fr);
1023
}
1024

    
1025
function tinydns_cleanup_addedit_form_record() {
1026
	/* Clean some things up and simplify per limited subset of TinyDNS record syntax before saving.  */
1027
	if($_POST['recordtype'] == "TXT") {
1028
		/* TinyDNS provides surrounding quotes for TXT records automatically so we check & remove them here */
1029
		if(substr($_POST['ipaddress'],-1) == "\"")
1030
			$_POST['ipaddress'] = substr($_POST['ipaddress'],0,-1);		
1031
		if(substr($_POST['ipaddress'],0,1) == "\"")
1032
			$_POST['ipaddress'] = substr($_POST['ipaddress'],1);
1033
		if(substr($_POST['ipaddress'],0,5) == "v=spf") {
1034
			/* more cleanup specific to SPF records - strip newlines and carriage returns) */
1035
			$_POST['ipaddress'] = str_replace("\r", "", $_POST['ipaddress']);
1036
			$_POST['ipaddress'] = str_replace("\n", "", $_POST['ipaddress']);		
1037
		}
1038
	}
1039
}
1040

    
1041
?>
    (1-1/1)