Project

General

Profile

Download (22.6 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	unbound.inc
4
	part of the pfSense project (https://www.pfsense.org)
5
	Copyright (C) 2015 Warren Baker <warren@percol8.co.za>
6
	All rights reserved.
7

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

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

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

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

    
29
	pfSense_BUILDER_BINARIES:   /usr/local/sbin/unbound  /usr/local/sbin/unbound-anchor    /usr/local/sbin/unbound-checkconf
30
	pfSense_BUILDER_BINARIES:   /usr/local/sbin/unbound-control    /usr/local/sbin/unbound-control-setup
31
	pfSense_MODULE: unbound
32
*/
33

    
34
/* include all configuration functions */
35
require_once("config.inc");
36
require_once("functions.inc");
37
require_once("filter.inc");
38
require_once("shaper.inc");
39

    
40
function create_unbound_chroot_path() {
41
	global $config, $g;
42

    
43
	// Configure chroot
44
	if (!is_dir($g['unbound_chroot_path'])) {
45
		mkdir($g['unbound_chroot_path']);
46
		chown($g['unbound_chroot_path'], "unbound");
47
		chgrp($g['unbound_chroot_path'], "unbound");
48
	}
49

    
50
}
51

    
52
/* Optimize Unbound for environment */
53
function unbound_optimization() {
54
	global $config;
55

    
56
	$optimization_settings = array();
57

    
58
	/*
59
	 * Set the number of threads equal to number of CPUs.
60
	 * Use 1 to disable threading, if for some reason this sysctl fails.
61
	 */
62
	$numprocs = intval(get_single_sysctl('kern.smp.cpus'));
63
	if ($numprocs > 1) {
64
		$optimization['number_threads'] = "num-threads: {$numprocs}";
65
		$optimize_num = pow(2, floor(log($numprocs, 2)));
66
	} else {
67
		$optimization['number_threads'] = "num-threads: 1";
68
		$optimize_num = 4;
69
	}
70

    
71
	// Slabs to help reduce lock contention.
72
	$optimization['msg_cache_slabs'] = "msg-cache-slabs: {$optimize_num}";
73
	$optimization['rrset_cache_slabs'] = "rrset-cache-slabs: {$optimize_num}";
74
	$optimization['infra_cache_slabs'] = "infra-cache-slabs: {$optimize_num}";
75
	$optimization['key_cache_slabs'] = "key-cache-slabs: {$optimize_num}";
76

    
77
	/*
78
	 * Larger socket buffer for busy servers
79
	 * Check that it is set to 4MB (by default the OS has it configured to 4MB)
80
	 */
81
	if (is_array($config['sysctl']) && is_array($config['sysctl']['item'])) {
82
		foreach ($config['sysctl']['item'] as $tunable) {
83
			if ($tunable['tunable'] == 'kern.ipc.maxsockbuf') {
84
				$so = floor(($tunable['value']/1024/1024)-4);
85
				// Check to ensure that the number is not a negative
86
				if ($so >= 4) {
87
					// Limit to 32MB, users might set maxsockbuf very high for other reasons.
88
					// We do not want unbound to fail because of that.
89
					$so = min($so, 32);
90
					$optimization['so_rcvbuf'] = "so-rcvbuf: {$so}m";
91
				} else {
92
					unset($optimization['so_rcvbuf']);
93
				}
94
			}
95
		}
96
	}
97
	// Safety check in case kern.ipc.maxsockbuf is not available.
98
	if (!isset($optimization['so_rcvbuf'])) {
99
		$optimization['so_rcvbuf'] = "#so-rcvbuf: 4m";
100
	}
101

    
102
	return $optimization;
103

    
104
}
105

    
106
function test_unbound_config($unboundcfg, &$output) {
107
	global $g;
108

    
109
	$cfgfile = "{$g['unbound_chroot_path']}/unbound.test.conf";
110
	$unboundcfgtxt = unbound_generate_config_text($unboundcfg);
111
	file_put_contents($cfgfile, $unboundcfgtxt);
112

    
113
	$rv = 0;
114
	exec("/usr/local/sbin/unbound-checkconf {$cfgfile} 2>&1", $output, $rv);
115
	unlink_if_exists($cfgfile);
116

    
117
	return $rv;
118
}
119

    
120

    
121
function unbound_generate_config() {
122
	global $g;
123

    
124
	$unboundcfgtxt = unbound_generate_config_text();
125

    
126
	// Configure static Host entries
127
	unbound_add_host_entries();
128

    
129
	// Configure Domain Overrides
130
	unbound_add_domain_overrides();
131

    
132
	// Configure Unbound access-lists
133
	unbound_acls_config();
134

    
135
	create_unbound_chroot_path();
136
	file_put_contents("{$g['unbound_chroot_path']}/unbound.conf", $unboundcfgtxt);
137
}
138

    
139

    
140
function unbound_generate_config_text($unboundcfg=NULL) {
141

    
142
	global $config, $g;
143
	if (is_null($unboundcfg)) {
144
		$unboundcfg = $config['unbound'];
145
	}
146

    
147
	// Setup optimization
148
	$optimization = unbound_optimization();
149

    
150
	// Setup DNSSEC support
151
	if (isset($unboundcfg['dnssec'])) {
152
		$module_config = "validator iterator";
153
		$anchor_file = "auto-trust-anchor-file: {$g['unbound_chroot_path']}/root.key";
154
	} else {
155
		$module_config = "iterator";
156
	}
157

    
158
	// Setup DNS Rebinding
159
	if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
160
		// Private-addresses for DNS Rebinding
161
		$private_addr = <<<EOF
162
# For DNS Rebinding prevention
163
private-address: 10.0.0.0/8
164
private-address: 172.16.0.0/12
165
private-address: 169.254.0.0/16
166
private-address: 192.168.0.0/16
167
private-address: fd00::/8
168
private-address: fe80::/10
169
EOF;
170
	}
171

    
172
	// Determine interfaces to run on
173
	$bindints = "";
174
	if (!empty($unboundcfg['active_interface'])) {
175
		$active_interfaces = explode(",", $unboundcfg['active_interface']);
176
		if (in_array("all", $active_interfaces, true)) {
177
			$bindints .= "interface: 0.0.0.0\n";
178
			$bindints .= "interface: ::0\n";
179
			$bindints .= "interface-automatic: yes\n";
180
		} else {
181
			foreach ($active_interfaces as $ubif) {
182
				if (is_ipaddr($ubif)) {
183
					//$bindints .= "interface: $ubif\n";  -- until redmine #4062 is fixed, then uncomment this.
184
				} else {
185
					$intip = get_interface_ip($ubif);
186
					if (is_ipaddrv4($intip)) {
187
						$bindints .= "interface: $intip\n";
188
					}
189
					$intip = get_interface_ipv6($ubif);
190
					if (is_ipaddrv6($intip)) {
191
						if (!is_linklocal($intip)) {  // skipping link local for the moment to not break people's configs: https://redmine.pfsense.org/issues/4062
192
							$bindints .= "interface: $intip\n";
193
						}
194
					}
195
				}
196
			}
197
		}
198
	} else {
199
		$bindints .= "interface: 0.0.0.0\n";
200
		$bindints .= "interface: ::0\n";
201
		/* If the active interface array is empty, treat it the same as "All" as is done above. Otherwise it breaks CARP with a default config. */
202
		$bindints .= "interface-automatic: yes\n";
203
	}
204

    
205
	// Determine interfaces to run on
206
	$outgoingints = "";
207
	if (!empty($unboundcfg['outgoing_interface'])) {
208
		$outgoingints = "# Outgoing interfaces to be used\n";
209
		$outgoing_interfaces = explode(",", $unboundcfg['outgoing_interface']);
210
		foreach ($outgoing_interfaces as $outif) {
211
			$outip = get_interface_ip($outif);
212
			if (is_ipaddr($outip)) {
213
				$outgoingints .= "outgoing-interface: $outip\n";
214
			}
215
			$outip = get_interface_ipv6($outif);
216
			if (is_ipaddrv6($outip)) {
217
				$outgoingints .= "outgoing-interface: $outip\n";
218
			}
219
		}
220
	}
221

    
222
	// Allow DNS Rebind for forwarded domains
223
	if (isset($unboundcfg['domainoverrides']) && is_array($unboundcfg['domainoverrides'])) {
224
		if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
225
			$private_domains = "# Set private domains in case authoritative name server returns a Private IP address\n";
226
			$private_domains .= unbound_add_domain_overrides("private");
227
		}
228
		$reverse_zones .= unbound_add_domain_overrides("reverse");
229
	}
230

    
231
	// Configure Unbound statistics
232
	$statistics = unbound_statistics();
233

    
234
	// Add custom Unbound options
235
	if ($unboundcfg['custom_options']) {
236
		$custom_options_source = explode("\n", base64_decode($unboundcfg['custom_options']));
237
		$custom_options = "# Unbound custom options\n";
238
		foreach ($custom_options_source as $ent) {
239
			$custom_options .= $ent."\n";
240
		}
241
	}
242

    
243
	// Server configuration variables
244
	$port = (is_port($unboundcfg['port'])) ? $unboundcfg['port'] : "53";
245
	$hide_identity = isset($unboundcfg['hideidentity']) ? "yes" : "no";
246
	$hide_version = isset($unboundcfg['hideversion']) ? "yes" : "no";
247
	$harden_dnssec_stripped = isset($unboundcfg['dnssecstripped']) ? "yes" : "no";
248
	$prefetch = isset($unboundcfg['prefetch']) ? "yes" : "no";
249
	$prefetch_key = isset($unboundcfg['prefetchkey']) ? "yes" : "no";
250
	$outgoing_num_tcp = (!empty($unboundcfg['outgoing_num_tcp'])) ? $unboundcfg['outgoing_num_tcp'] : "10";
251
	$incoming_num_tcp = (!empty($unboundcfg['incoming_num_tcp'])) ? $unboundcfg['incoming_num_tcp'] : "10";
252
	$edns_buffer_size = (!empty($unboundcfg['edns_buffer_size'])) ? $unboundcfg['edns_buffer_size'] : "4096";
253
	$num_queries_per_thread = (!empty($unboundcfg['num_queries_per_thread'])) ? $unboundcfg['num_queries_per_thread'] : "4096";
254
	$jostle_timeout = (!empty($unboundcfg['jostle_timeout'])) ? $unboundcfg['jostle_timeout'] : "200";
255
	$cache_max_ttl = (!empty($unboundcfg['cache_max_ttl'])) ? $unboundcfg['cache_max_ttl'] : "86400";
256
	$cache_min_ttl = (!empty($unboundcfg['cache_min_ttl'])) ? $unboundcfg['cache_min_ttl'] : "0";
257
	$infra_host_ttl = (!empty($unboundcfg['infra_host_ttl'])) ? $unboundcfg['infra_host_ttl'] : "900";
258
	$infra_cache_numhosts = (!empty($unboundcfg['infra_cache_numhosts'])) ? $unboundcfg['infra_cache_numhosts'] : "10000";
259
	$unwanted_reply_threshold = (!empty($unboundcfg['unwanted_reply_threshold'])) ? $unboundcfg['unwanted_reply_threshold'] : "0";
260
	if ($unwanted_reply_threshold == "disabled") {
261
		$unwanted_reply_threshold = "0";
262
	}
263
	$msg_cache_size = (!empty($unboundcfg['msgcachesize'])) ? $unboundcfg['msgcachesize'] : "4";
264
	$verbosity = isset($unboundcfg['log_verbosity']) ? $unboundcfg['log_verbosity'] : 1;
265
	$use_caps = isset($unboundcfg['use_caps']) ? "yes" : "no";
266

    
267
	// Set up forwarding if it is configured
268
	if (isset($unboundcfg['forwarding'])) {
269
		$dnsservers = array();
270
		if (isset($config['system']['dnsallowoverride'])) {
271
			$ns = array_unique(get_nameservers());
272
			foreach ($ns as $nameserver) {
273
				if ($nameserver) {
274
					$dnsservers[] = $nameserver;
275
				}
276
			}
277
		} else {
278
			$ns = array();
279
		}
280
		$sys_dnsservers = array_unique(get_dns_servers());
281
		foreach ($sys_dnsservers as $sys_dnsserver) {
282
			if ($sys_dnsserver && (!in_array($sys_dnsserver, $ns))) {
283
				$dnsservers[] = $sys_dnsserver;
284
			}
285
		}
286

    
287
		if (!empty($dnsservers)) {
288
			$forward_conf .=<<<EOD
289
# Forwarding
290
forward-zone:
291
	name: "."
292

    
293
EOD;
294
			foreach ($dnsservers as $dnsserver) {
295
				$forward_conf .= "\tforward-addr: $dnsserver\n";
296
			}
297
		}
298
	} else {
299
		$forward_conf = "";
300
	}
301

    
302
	// Size of the RRset cache == 2 * msg-cache-size per Unbound's recommendations
303
	$rrset_cache_size = $msg_cache_size * 2;
304

    
305
	$unboundconf = <<<EOD
306
##########################
307
# Unbound Configuration
308
##########################
309

    
310
##
311
# Server configuration
312
##
313
server:
314
{$reverse_zones}
315
chroot: {$g['unbound_chroot_path']}
316
username: "unbound"
317
directory: "{$g['unbound_chroot_path']}"
318
pidfile: "/var/run/unbound.pid"
319
use-syslog: yes
320
port: {$port}
321
verbosity: {$verbosity}
322
hide-identity: {$hide_identity}
323
hide-version: {$hide_version}
324
harden-glue: yes
325
do-ip4: yes
326
do-ip6: yes
327
do-udp: yes
328
do-tcp: yes
329
do-daemonize: yes
330
module-config: "{$module_config}"
331
unwanted-reply-threshold: {$unwanted_reply_threshold}
332
num-queries-per-thread: {$num_queries_per_thread}
333
jostle-timeout: {$jostle_timeout}
334
infra-host-ttl: {$infra_host_ttl}
335
infra-cache-numhosts: {$infra_cache_numhosts}
336
outgoing-num-tcp: {$outgoing_num_tcp}
337
incoming-num-tcp: {$incoming_num_tcp}
338
edns-buffer-size: {$edns_buffer_size}
339
cache-max-ttl: {$cache_max_ttl}
340
cache-min-ttl: {$cache_min_ttl}
341
harden-dnssec-stripped: {$harden_dnssec_stripped}
342
msg-cache-size: {$msg_cache_size}m
343
rrset-cache-size: {$rrset_cache_size}m
344

    
345
{$optimization['number_threads']}
346
{$optimization['msg_cache_slabs']}
347
{$optimization['rrset_cache_slabs']}
348
{$optimization['infra_cache_slabs']}
349
{$optimization['key_cache_slabs']}
350
outgoing-range: 4096
351
{$optimization['so_rcvbuf']}
352
{$anchor_file}
353
prefetch: {$prefetch}
354
prefetch-key: {$prefetch_key}
355
use-caps-for-id: {$use_caps}
356
# Statistics
357
{$statistics}
358
# Interface IP(s) to bind to
359
{$bindints}
360
{$outgoingints}
361

    
362
# DNS Rebinding
363
{$private_addr}
364
{$private_domains}
365

    
366
# Access lists
367
include: {$g['unbound_chroot_path']}/access_lists.conf
368

    
369
# Static host entries
370
include: {$g['unbound_chroot_path']}/host_entries.conf
371

    
372
# dhcp lease entries
373
include: {$g['unbound_chroot_path']}/dhcpleases_entries.conf
374

    
375
# Domain overrides
376
include: {$g['unbound_chroot_path']}/domainoverrides.conf
377
{$forward_conf}
378

    
379
{$custom_options}
380

    
381
###
382
# Remote Control Config
383
###
384
include: {$g['unbound_chroot_path']}/remotecontrol.conf
385

    
386
EOD;
387

    
388
	return $unboundconf;
389
}
390

    
391
function unbound_remote_control_setup() {
392
	global $g;
393

    
394
	if (!file_exists("{$g['unbound_chroot_path']}/remotecontrol.conf") || !file_exists("{$g['unbound_chroot_path']}/unbound_control.key")) {
395
		$remotcfg = <<<EOF
396
remote-control:
397
	control-enable: yes
398
	control-interface: 127.0.0.1
399
	control-port: 953
400
	server-key-file: "{$g['unbound_chroot_path']}/unbound_server.key"
401
	server-cert-file: "{$g['unbound_chroot_path']}/unbound_server.pem"
402
	control-key-file: "{$g['unbound_chroot_path']}/unbound_control.key"
403
	control-cert-file: "{$g['unbound_chroot_path']}/unbound_control.pem"
404

    
405
EOF;
406

    
407
		create_unbound_chroot_path();
408
		file_put_contents("{$g['unbound_chroot_path']}/remotecontrol.conf", $remotcfg);
409

    
410
		// Generate our keys
411
		do_as_unbound_user("unbound-control-setup");
412

    
413
	}
414
}
415

    
416
// Read /etc/hosts
417
function read_hosts() {
418

    
419
	/* Open /etc/hosts and extract the only dhcpleases info
420
	 * XXX - to convert to an unbound C library which reads /etc/hosts automatically
421
	 */
422
	$etc_hosts = array();
423
	foreach (file('/etc/hosts') as $line) {
424
		if (strpos($line, "dhcpleases automatically entered")) {
425
			break;
426
		}
427
		$d = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
428
		if (empty($d) || substr(reset($d), 0, 1) == "#") {
429
			continue;
430
		}
431
		$ip = array_shift($d);
432
		$fqdn = array_shift($d);
433
		$name = array_shift($d);
434
		if (!empty($fqdn) && $fqdn != "empty") {
435
			if (!empty($name) && $name != "empty") {
436
				array_push($etc_hosts, array(ipaddr => "$ip", fqdn => "$fqdn", name => "$name"));
437
			} else {
438
				array_push($etc_hosts, array(ipaddr => "$ip", fqdn => "$fqdn"));
439
			}
440
		}
441
	}
442
	return $etc_hosts;
443
}
444

    
445
function sync_unbound_service() {
446
	global $config, $g;
447

    
448
	create_unbound_chroot_path();
449

    
450
	// Configure our Unbound service
451
	do_as_unbound_user("unbound-anchor");
452
	unbound_remote_control_setup();
453
	unbound_generate_config();
454
	do_as_unbound_user("start");
455
	require_once("service-utils.inc");
456
	if (is_service_running("unbound")) {
457
		do_as_unbound_user("restore_cache");
458
	}
459

    
460
}
461

    
462
function unbound_acl_id_used($id) {
463
	global $config;
464

    
465
	if (is_array($config['unbound']['acls'])) {
466
		foreach ($config['unbound']['acls'] as & $acls) {
467
			if ($id == $acls['aclid']) {
468
				return true;
469
			}
470
		}
471
	}
472

    
473
	return false;
474
}
475

    
476
function unbound_get_next_id() {
477
	$aclid = 0;
478
	while (unbound_acl_id_used($aclid)) {
479
		$aclid++;
480
	}
481
	return $aclid;
482
}
483

    
484
// Execute commands as the user unbound
485
function do_as_unbound_user($cmd) {
486
	global $g;
487

    
488
	switch ($cmd) {
489
		case "start":
490
			mwexec("/usr/local/sbin/unbound -c {$g['unbound_chroot_path']}/unbound.conf");
491
			break;
492
		case "stop":
493
			mwexec("echo '/usr/local/sbin/unbound-control stop' | /usr/bin/su -m unbound", true);
494
			break;
495
		case "reload":
496
			mwexec("echo '/usr/local/sbin/unbound-control reload' | /usr/bin/su -m unbound", true);
497
			break;
498
		case "unbound-anchor":
499
			// sanity check root.key because unbound-anchor will fail without manual removal otherwise. redmine #5334
500
			if (file_exists("{$g['unbound_chroot_path']}/root.key")) {
501
				$rootkeycheck = mwexec("/usr/bin/grep 'autotrust trust anchor file' {$g['unbound_chroot_path']}/root.key", true);
502
				if ($rootkeycheck != "0") {
503
					log_error("Unbound root.key file is corrupt, removing and recreating.");
504
					unlink_if_exists("{$g['unbound_chroot_path']}/root.key");
505
				}
506
			}
507
			mwexec("echo '/usr/local/sbin/unbound-anchor -a {$g['unbound_chroot_path']}/root.key' | /usr/bin/su -m unbound", true);
508
			pfSense_fsync("{$g['unbound_chroot_path']}/root.key");
509
			break;
510
		case "unbound-control-setup":
511
			mwexec("echo '/usr/local/sbin/unbound-control-setup -d {$g['unbound_chroot_path']}' | /usr/bin/su -m unbound", true);
512
			break;
513
		default:
514
			break;
515
	}
516
}
517

    
518
function unbound_add_domain_overrides($pvt_rev="") {
519
	global $config, $g;
520

    
521
	$domains = $config['unbound']['domainoverrides'];
522

    
523
	$sorted_domains = msort($domains, "domain");
524
	$result = array();
525
	foreach ($sorted_domains as $domain) {
526
		$domain_key = current($domain);
527
		if (!isset($result[$domain_key])) {
528
			$result[$domain_key] = array();
529
		}
530
		$result[$domain_key][] = $domain['ip'];
531
	}
532

    
533
	// Domain overrides that have multiple entries need multiple stub-addr: added
534
	$domain_entries = "";
535
	foreach ($result as $domain=>$ips) {
536
		if ($pvt_rev == "private") {
537
			$domain_entries .= "private-domain: \"$domain\"\n";
538
			$domain_entries .= "domain-insecure: \"$domain\"\n";
539
		} else if ($pvt_rev == "reverse") {
540
			if ((substr($domain, -14) == ".in-addr.arpa.") || (substr($domain, -13) == ".in-addr.arpa")) {
541
				$domain_entries .= "local-zone: \"$domain\" typetransparent\n";
542
			}
543
		} else {
544
			$domain_entries .= "stub-zone:\n";
545
			$domain_entries .= "\tname: \"$domain\"\n";
546
			foreach ($ips as $ip) {
547
				$domain_entries .= "\tstub-addr: $ip\n";
548
			}
549
			$domain_entries .= "\tstub-prime: no\n";
550
		}
551
	}
552

    
553
	if ($pvt_rev != "") {
554
		return $domain_entries;
555
	} else {
556
		create_unbound_chroot_path();
557
		file_put_contents("{$g['unbound_chroot_path']}/domainoverrides.conf", $domain_entries);
558
	}
559
}
560

    
561
function unbound_add_host_entries() {
562
	global $config, $g;
563

    
564
	$unbound_entries = "local-zone: \"{$config['system']['domain']}\" transparent\n";
565

    
566
	$hosts = read_hosts();
567
	$added_ptr = array();
568
	foreach ($hosts as $host) {
569
		if (is_ipaddrv4($host['ipaddr'])) {
570
			$type = 'A';
571
		} else if (is_ipaddrv6($host['ipaddr'])) {
572
			$type = 'AAAA';
573
		} else {
574
			continue;
575
		}
576

    
577
		if (!$added_ptr[$host['ipaddr']]) {
578
			$unbound_entries .= "local-data-ptr: \"{$host['ipaddr']} {$host['fqdn']}\"\n";
579
			$added_ptr[$host['ipaddr']] = true;
580
		}
581
		$unbound_entries .= "local-data: \"{$host['fqdn']} {$type} {$host['ipaddr']}\"\n";
582
		if (isset($host['name'])) {
583
			$unbound_entries .= "local-data: \"{$host['name']} {$type} {$host['ipaddr']}\"\n";
584
		}
585
	}
586

    
587
	// Write out entries
588
	create_unbound_chroot_path();
589
	file_put_contents("{$g['unbound_chroot_path']}/host_entries.conf", $unbound_entries);
590

    
591
	/* dhcpleases will write to this config file, make sure it exists */
592
	@touch("{$g['unbound_chroot_path']}/dhcpleases_entries.conf");
593
}
594

    
595
function unbound_control($action) {
596
	global $config, $g;
597

    
598
	$cache_dumpfile = "/var/tmp/unbound_cache";
599

    
600
	switch ($action) {
601
	case "start":
602
		// Start Unbound
603
		if ($config['unbound']['enable'] == "on") {
604
			if (!is_service_running("unbound")) {
605
				do_as_unbound_user("start");
606
			}
607
		}
608
		break;
609
	case "stop":
610
		if ($config['unbound']['enable'] == "on") {
611
			do_as_unbound_user("stop");
612
		}
613
		break;
614
	case "reload":
615
		if ($config['unbound']['enable'] == "on") {
616
			do_as_unbound_user("reload");
617
		}
618
		break;
619
	case "dump_cache":
620
		// Dump Unbound's Cache
621
		if ($config['unbound']['dumpcache'] == "on") {
622
			do_as_unbound_user("dump_cache");
623
		}
624
		break;
625
	case "restore_cache":
626
		// Restore Unbound's Cache
627
		if ((is_service_running("unbound")) && ($config['unbound']['dumpcache'] == "on")) {
628
			if (file_exists($cache_dumpfile) && filesize($cache_dumpfile) > 0) {
629
				do_as_unbound_user("load_cache < /var/tmp/unbound_cache");
630
			}
631
		}
632
		break;
633
	default:
634
		break;
635

    
636
	}
637
}
638

    
639
// Generation of Unbound statistics
640
function unbound_statistics() {
641
	global $config;
642

    
643
	if ($config['stats'] == "on") {
644
		$stats_interval = $config['unbound']['stats_interval'];
645
		$cumulative_stats = $config['cumulative_stats'];
646
		if ($config['extended_stats'] == "on") {
647
			$extended_stats = "yes";
648
		} else {
649
			$extended_stats = "no";
650
		}
651
	} else {
652
		$stats_interval = "0";
653
		$cumulative_stats = "no";
654
		$extended_stats = "no";
655
	}
656
	/* XXX To do - add RRD graphs */
657
	$stats = <<<EOF
658
# Unbound Statistics
659
statistics-interval: {$stats_interval}
660
extended-statistics: yes
661
statistics-cumulative: yes
662

    
663
EOF;
664

    
665
	return $stats;
666
}
667

    
668
// Unbound Access lists
669
function unbound_acls_config() {
670
	global $g, $config;
671

    
672
	if (!isset($config['unbound']['disable_auto_added_access_control'])) {
673
		$aclcfg = "access-control: 127.0.0.1/32 allow\n";
674
		$aclcfg .= "access-control: ::1 allow\n";
675
		// Add our networks for active interfaces including localhost
676
		if (!empty($config['unbound']['active_interface'])) {
677
			$active_interfaces = array_flip(explode(",", $config['unbound']['active_interface']));
678
			if (in_array("all", $active_interfaces)) {
679
				$active_interfaces = get_configured_interface_with_descr();
680
			}
681
		} else {
682
			$active_interfaces = get_configured_interface_with_descr();
683
		}
684

    
685
		$bindints = "";
686
		foreach ($active_interfaces as $ubif => $ifdesc) {
687
			$ifip = get_interface_ip($ubif);
688
			if (is_ipaddrv4($ifip)) {
689
				// IPv4 is handled via NAT networks below
690
			}
691
			$ifip = get_interface_ipv6($ubif);
692
			if (is_ipaddrv6($ifip)) {
693
				if (!is_linklocal($ifip)) {
694
					$subnet_bits = get_interface_subnetv6($ubif);
695
					$subnet_ip = gen_subnetv6($ifip, $subnet_bits);
696
					// only add LAN-type interfaces
697
					if (!interface_has_gateway($ubif)) {
698
						$aclcfg .= "access-control: {$subnet_ip}/{$subnet_bits} allow\n";
699
					}
700
				}
701
				// add for IPv6 static routes to local networks
702
				// for safety, we include only routes reachable on an interface with no
703
				// gateway specified - read: not an Internet connection.
704
				$static_routes = get_staticroutes();
705
				foreach ($static_routes as $route) {
706
					if ((lookup_gateway_interface_by_name($route['gateway']) == $ubif) && !interface_has_gateway($ubif)) {
707
						// route is on this interface, interface doesn't have gateway, add it
708
						$aclcfg .= "access-control: {$route['network']} allow\n";
709
					}
710
				}
711
			}
712
		}
713

    
714
		// Generate IPv4 access-control entries using the same logic as automatic outbound NAT
715
		if (empty($FilterIflist)) {
716
			filter_generate_optcfg_array();
717
		}
718
		$natnetworks_array = array();
719
		$natnetworks_array = filter_nat_rules_automatic_tonathosts();
720
		foreach ($natnetworks_array as $allowednet) {
721
			$aclcfg .= "access-control: $allowednet allow \n";
722
		}
723
	}
724

    
725
	// Configure the custom ACLs
726
	if (is_array($config['unbound']['acls'])) {
727
		foreach ($config['unbound']['acls'] as $unbound_acl) {
728
			$aclcfg .= "#{$unbound_acl['aclname']}\n";
729
			foreach ($unbound_acl['row'] as $network) {
730
				if ($unbound_acl['aclaction'] == "allow snoop") {
731
					$unbound_acl['aclaction'] = "allow_snoop";
732
				}
733
				$aclcfg .= "access-control: {$network['acl_network']}/{$network['mask']} {$unbound_acl['aclaction']}\n";
734
			}
735
		}
736
	}
737
	// Write out Access list
738
	create_unbound_chroot_path();
739
	file_put_contents("{$g['unbound_chroot_path']}/access_lists.conf", $aclcfg);
740

    
741
}
742

    
743
// Generate hosts and reload services
744
function unbound_hosts_generate() {
745
	// Generate our hosts file
746
	unbound_add_host_entries();
747

    
748
	// Reload our service to read the updates
749
	unbound_control("reload");
750
}
751

    
752
?>
(54-54/67)