Project

General

Profile

Download (21.8 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 unbound_generate_config() {
107
	global $config, $g;
108

    
109
	// Setup optimization
110
	$optimization = unbound_optimization();
111

    
112
	// Setup DNSSEC support
113
	if (isset($config['unbound']['dnssec'])) {
114
		$module_config = "validator iterator";
115
		$anchor_file = "auto-trust-anchor-file: {$g['unbound_chroot_path']}/root.key";
116
	} else {
117
		$module_config = "iterator";
118
	}
119

    
120
	// Setup DNS Rebinding
121
	if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
122
		// Private-addresses for DNS Rebinding
123
		$private_addr = <<<EOF
124
# For DNS Rebinding prevention
125
private-address: 10.0.0.0/8
126
private-address: 172.16.0.0/12
127
private-address: 169.254.0.0/16
128
private-address: 192.168.0.0/16
129
private-address: fd00::/8
130
private-address: fe80::/10
131
EOF;
132
	}
133

    
134
	// Determine interfaces to run on
135
	$bindints = "";
136
	if (!empty($config['unbound']['active_interface'])) {
137
		$active_interfaces = explode(",", $config['unbound']['active_interface']);
138
		if (in_array("all", $active_interfaces, true)) {
139
			$bindints .= "interface: 0.0.0.0\n";
140
			$bindints .= "interface: ::0\n";
141
			$bindints .= "interface-automatic: yes\n";
142
		} else {
143
			foreach ($active_interfaces as $ubif) {
144
				if (is_ipaddr($ubif)) {
145
					//$bindints .= "interface: $ubif\n";  -- until redmine #4062 is fixed, then uncomment this.
146
				} else {
147
					$intip = get_interface_ip($ubif);
148
					if (is_ipaddrv4($intip)) {
149
						$bindints .= "interface: $intip\n";
150
					}
151
					$intip = get_interface_ipv6($ubif);
152
					if (is_ipaddrv6($intip)) {
153
						if (!is_linklocal($intip)) {  // skipping link local for the moment to not break people's configs: https://redmine.pfsense.org/issues/4062
154
							$bindints .= "interface: $intip\n";
155
						}
156
					}
157
				}
158
			}
159
		}
160
	} else {
161
		$bindints .= "interface: 0.0.0.0\n";
162
		$bindints .= "interface: ::0\n";
163
		/* 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. */
164
		$bindints .= "interface-automatic: yes\n";
165
	}
166

    
167
	// Determine interfaces to run on
168
	$outgoingints = "";
169
	if (!empty($config['unbound']['outgoing_interface'])) {
170
		$outgoingints = "# Outgoing interfaces to be used\n";
171
		$outgoing_interfaces = explode(",", $config['unbound']['outgoing_interface']);
172
		foreach ($outgoing_interfaces as $outif) {
173
			$outip = get_interface_ip($outif);
174
			if (is_ipaddr($outip)) {
175
				$outgoingints .= "outgoing-interface: $outip\n";
176
			}
177
			$outip = get_interface_ipv6($outif);
178
			if (is_ipaddrv6($outip)) {
179
				$outgoingints .= "outgoing-interface: $outip\n";
180
			}
181
		}
182
	}
183

    
184
	// Allow DNS Rebind for forwarded domains
185
	if (isset($config['unbound']['domainoverrides']) && is_array($config['unbound']['domainoverrides'])) {
186
		if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
187
			$private_domains = "# Set private domains in case authoritative name server returns a Private IP address\n";
188
			$private_domains .= unbound_add_domain_overrides("private");
189
		}
190
		$reverse_zones .= unbound_add_domain_overrides("reverse");
191
	}
192

    
193
	// Configure static Host entries
194
	unbound_add_host_entries();
195

    
196
	// Configure Domain Overrides
197
	unbound_add_domain_overrides();
198

    
199
	// Configure Unbound statistics
200
	$statistics = unbound_statistics();
201

    
202
	// Configure Unbound access-lists
203
	unbound_acls_config();
204

    
205
	// Add custom Unbound options
206
	if ($config['unbound']['custom_options']) {
207
		$custom_options_source = explode("\n", base64_decode($config['unbound']['custom_options']));
208
		$custom_options = "# Unbound custom options\n";
209
		foreach ($custom_options_source as $ent) {
210
			$custom_options .= $ent."\n";
211
		}
212
	}
213

    
214
	// Server configuration variables
215
	$port = (is_port($config['unbound']['port'])) ? $config['unbound']['port'] : "53";
216
	$hide_identity = isset($config['unbound']['hideidentity']) ? "yes" : "no";
217
	$hide_version = isset($config['unbound']['hideversion']) ? "yes" : "no";
218
	$harden_dnssec_stripped = isset($config['unbound']['dnssecstripped']) ? "yes" : "no";
219
	$prefetch = isset($config['unbound']['prefetch']) ? "yes" : "no";
220
	$prefetch_key = isset($config['unbound']['prefetchkey']) ? "yes" : "no";
221
	$outgoing_num_tcp = (!empty($config['unbound']['outgoing_num_tcp'])) ? $config['unbound']['outgoing_num_tcp'] : "10";
222
	$incoming_num_tcp = (!empty($config['unbound']['incoming_num_tcp'])) ? $config['unbound']['incoming_num_tcp'] : "10";
223
	$edns_buffer_size = (!empty($config['unbound']['edns_buffer_size'])) ? $config['unbound']['edns_buffer_size'] : "4096";
224
	$num_queries_per_thread = (!empty($config['unbound']['num_queries_per_thread'])) ? $config['unbound']['num_queries_per_thread'] : "4096";
225
	$jostle_timeout = (!empty($config['unbound']['jostle_timeout'])) ? $config['unbound']['jostle_timeout'] : "200";
226
	$cache_max_ttl = (!empty($config['unbound']['cache_max_ttl'])) ? $config['unbound']['cache_max_ttl'] : "86400";
227
	$cache_min_ttl = (!empty($config['unbound']['cache_min_ttl'])) ? $config['unbound']['cache_min_ttl'] : "0";
228
	$infra_host_ttl = (!empty($config['unbound']['infra_host_ttl'])) ? $config['unbound']['infra_host_ttl'] : "900";
229
	$infra_cache_numhosts = (!empty($config['unbound']['infra_cache_numhosts'])) ? $config['unbound']['infra_cache_numhosts'] : "10000";
230
	$unwanted_reply_threshold = (!empty($config['unbound']['unwanted_reply_threshold'])) ? $config['unbound']['unwanted_reply_threshold'] : "0";
231
	if ($unwanted_reply_threshold == "disabled") {
232
		$unwanted_reply_threshold = "0";
233
	}
234
	$msg_cache_size = (!empty($config['unbound']['msgcachesize'])) ? $config['unbound']['msgcachesize'] : "4";
235
	$verbosity = isset($config['unbound']['log_verbosity']) ? $config['unbound']['log_verbosity'] : 1;
236
	$use_caps = isset($config['unbound']['use_caps']) ? "yes" : "no";
237

    
238
	// Set up forwarding if it is configured
239
	if (isset($config['unbound']['forwarding'])) {
240
		$dnsservers = array();
241
		if (isset($config['system']['dnsallowoverride'])) {
242
			$ns = array_unique(get_nameservers());
243
			foreach ($ns as $nameserver) {
244
				if ($nameserver) {
245
					$dnsservers[] = $nameserver;
246
				}
247
			}
248
		} else {
249
			$ns = array();
250
		}
251
		$sys_dnsservers = array_unique(get_dns_servers());
252
		foreach ($sys_dnsservers as $sys_dnsserver) {
253
			if ($sys_dnsserver && (!in_array($sys_dnsserver, $ns))) {
254
				$dnsservers[] = $sys_dnsserver;
255
			}
256
		}
257

    
258
		if (!empty($dnsservers)) {
259
			$forward_conf .=<<<EOD
260
# Forwarding
261
forward-zone:
262
	name: "."
263

    
264
EOD;
265
			foreach ($dnsservers as $dnsserver) {
266
				$forward_conf .= "\tforward-addr: $dnsserver\n";
267
			}
268
		}
269
	} else {
270
		$forward_conf = "";
271
	}
272

    
273
	// Size of the RRset cache == 2 * msg-cache-size per Unbound's recommendations
274
	$rrset_cache_size = $msg_cache_size * 2;
275

    
276
	$unboundconf = <<<EOD
277
##########################
278
# Unbound Configuration
279
##########################
280

    
281
##
282
# Server configuration
283
##
284
server:
285
{$reverse_zones}
286
chroot: {$g['unbound_chroot_path']}
287
username: "unbound"
288
directory: "{$g['unbound_chroot_path']}"
289
pidfile: "/var/run/unbound.pid"
290
use-syslog: yes
291
port: {$port}
292
verbosity: {$verbosity}
293
hide-identity: {$hide_identity}
294
hide-version: {$hide_version}
295
harden-glue: yes
296
do-ip4: yes
297
do-ip6: yes
298
do-udp: yes
299
do-tcp: yes
300
do-daemonize: yes
301
module-config: "{$module_config}"
302
unwanted-reply-threshold: {$unwanted_reply_threshold}
303
num-queries-per-thread: {$num_queries_per_thread}
304
jostle-timeout: {$jostle_timeout}
305
infra-host-ttl: {$infra_host_ttl}
306
infra-cache-numhosts: {$infra_cache_numhosts}
307
outgoing-num-tcp: {$outgoing_num_tcp}
308
incoming-num-tcp: {$incoming_num_tcp}
309
edns-buffer-size: {$edns_buffer_size}
310
cache-max-ttl: {$cache_max_ttl}
311
cache-min-ttl: {$cache_min_ttl}
312
harden-dnssec-stripped: {$harden_dnssec_stripped}
313
msg-cache-size: {$msg_cache_size}m
314
rrset-cache-size: {$rrset_cache_size}m
315

    
316
{$optimization['number_threads']}
317
{$optimization['msg_cache_slabs']}
318
{$optimization['rrset_cache_slabs']}
319
{$optimization['infra_cache_slabs']}
320
{$optimization['key_cache_slabs']}
321
outgoing-range: 4096
322
{$optimization['so_rcvbuf']}
323
{$anchor_file}
324
prefetch: {$prefetch}
325
prefetch-key: {$prefetch_key}
326
use-caps-for-id: {$use_caps}
327
# Statistics
328
{$statistics}
329
# Interface IP(s) to bind to
330
{$bindints}
331
{$outgoingints}
332

    
333
# DNS Rebinding
334
{$private_addr}
335
{$private_domains}
336

    
337
# Access lists
338
include: {$g['unbound_chroot_path']}/access_lists.conf
339

    
340
# Static host entries
341
include: {$g['unbound_chroot_path']}/host_entries.conf
342

    
343
# dhcp lease entries
344
include: {$g['unbound_chroot_path']}/dhcpleases_entries.conf
345

    
346
# Domain overrides
347
include: {$g['unbound_chroot_path']}/domainoverrides.conf
348
{$forward_conf}
349

    
350
{$custom_options}
351

    
352
###
353
# Remote Control Config
354
###
355
include: {$g['unbound_chroot_path']}/remotecontrol.conf
356

    
357
EOD;
358

    
359
	create_unbound_chroot_path();
360
	file_put_contents("{$g['unbound_chroot_path']}/unbound.conf", $unboundconf);
361

    
362
	return 0;
363
}
364

    
365
function unbound_remote_control_setup() {
366
	global $g;
367

    
368
	if (!file_exists("{$g['unbound_chroot_path']}/remotecontrol.conf") || !file_exists("{$g['unbound_chroot_path']}/unbound_control.key")) {
369
		$remotcfg = <<<EOF
370
remote-control:
371
	control-enable: yes
372
	control-interface: 127.0.0.1
373
	control-port: 953
374
	server-key-file: "{$g['unbound_chroot_path']}/unbound_server.key"
375
	server-cert-file: "{$g['unbound_chroot_path']}/unbound_server.pem"
376
	control-key-file: "{$g['unbound_chroot_path']}/unbound_control.key"
377
	control-cert-file: "{$g['unbound_chroot_path']}/unbound_control.pem"
378

    
379
EOF;
380

    
381
		create_unbound_chroot_path();
382
		file_put_contents("{$g['unbound_chroot_path']}/remotecontrol.conf", $remotcfg);
383

    
384
		// Generate our keys
385
		do_as_unbound_user("unbound-control-setup");
386

    
387
	}
388
}
389

    
390
// Read /etc/hosts
391
function read_hosts() {
392

    
393
	/* Open /etc/hosts and extract the only dhcpleases info
394
	 * XXX - to convert to an unbound C library which reads /etc/hosts automatically
395
	 */
396
	$etc_hosts = array();
397
	foreach (file('/etc/hosts') as $line) {
398
		if (strpos($line, "dhcpleases automatically entered")) {
399
			break;
400
		}
401
		$d = preg_split('/\s+/', $line, -1, PREG_SPLIT_NO_EMPTY);
402
		if (empty($d) || substr(reset($d), 0, 1) == "#") {
403
			continue;
404
		}
405
		$ip = array_shift($d);
406
		$fqdn = array_shift($d);
407
		$name = array_shift($d);
408
		if (!empty($fqdn) && $fqdn != "empty") {
409
			if (!empty($name) && $name != "empty") {
410
				array_push($etc_hosts, array(ipaddr => "$ip", fqdn => "$fqdn", name => "$name"));
411
			} else {
412
				array_push($etc_hosts, array(ipaddr => "$ip", fqdn => "$fqdn"));
413
			}
414
		}
415
	}
416
	return $etc_hosts;
417
}
418

    
419
function sync_unbound_service() {
420
	global $config, $g;
421

    
422
	create_unbound_chroot_path();
423

    
424
	// Configure our Unbound service
425
	do_as_unbound_user("unbound-anchor");
426
	unbound_remote_control_setup();
427
	unbound_generate_config();
428
	do_as_unbound_user("start");
429
	require_once("service-utils.inc");
430
	if (is_service_running("unbound")) {
431
		do_as_unbound_user("restore_cache");
432
	}
433

    
434
}
435

    
436
function unbound_acl_id_used($id) {
437
	global $config;
438

    
439
	if (is_array($config['unbound']['acls'])) {
440
		foreach ($config['unbound']['acls'] as & $acls) {
441
			if ($id == $acls['aclid']) {
442
				return true;
443
			}
444
		}
445
	}
446

    
447
	return false;
448
}
449

    
450
function unbound_get_next_id() {
451
	$aclid = 0;
452
	while (unbound_acl_id_used($aclid)) {
453
		$aclid++;
454
	}
455
	return $aclid;
456
}
457

    
458
// Execute commands as the user unbound
459
function do_as_unbound_user($cmd) {
460
	global $g;
461

    
462
	switch ($cmd) {
463
		case "start":
464
			mwexec("/usr/local/sbin/unbound -c {$g['unbound_chroot_path']}/unbound.conf");
465
			break;
466
		case "stop":
467
			mwexec("echo '/usr/local/sbin/unbound-control stop' | /usr/bin/su -m unbound", true);
468
			break;
469
		case "reload":
470
			mwexec("echo '/usr/local/sbin/unbound-control reload' | /usr/bin/su -m unbound", true);
471
			break;
472
		case "unbound-anchor":
473
			mwexec("echo '/usr/local/sbin/unbound-anchor -a {$g['unbound_chroot_path']}/root.key' | /usr/bin/su -m unbound", true);
474
			break;
475
		case "unbound-control-setup":
476
			mwexec("echo '/usr/local/sbin/unbound-control-setup -d {$g['unbound_chroot_path']}' | /usr/bin/su -m unbound", true);
477
			break;
478
		default:
479
			break;
480
	}
481
}
482

    
483
function unbound_add_domain_overrides($pvt_rev="") {
484
	global $config, $g;
485

    
486
	$domains = $config['unbound']['domainoverrides'];
487

    
488
	$sorted_domains = msort($domains, "domain");
489
	$result = array();
490
	foreach ($sorted_domains as $domain) {
491
		$domain_key = current($domain);
492
		if (!isset($result[$domain_key])) {
493
			$result[$domain_key] = array();
494
		}
495
		$result[$domain_key][] = $domain['ip'];
496
	}
497

    
498
	// Domain overrides that have multiple entries need multiple stub-addr: added
499
	$domain_entries = "";
500
	foreach ($result as $domain=>$ips) {
501
		if ($pvt_rev == "private") {
502
			$domain_entries .= "private-domain: \"$domain\"\n";
503
			$domain_entries .= "domain-insecure: \"$domain\"\n";
504
		} else if ($pvt_rev == "reverse") {
505
			if ((substr($domain, -14) == ".in-addr.arpa.") || (substr($domain, -13) == ".in-addr.arpa")) {
506
				$domain_entries .= "local-zone: \"$domain\" typetransparent\n";
507
			}
508
		} else {
509
			$domain_entries .= "stub-zone:\n";
510
			$domain_entries .= "\tname: \"$domain\"\n";
511
			foreach ($ips as $ip) {
512
				$domain_entries .= "\tstub-addr: $ip\n";
513
			}
514
			$domain_entries .= "\tstub-prime: no\n";
515
		}
516
	}
517

    
518
	if ($pvt_rev != "") {
519
		return $domain_entries;
520
	} else {
521
		create_unbound_chroot_path();
522
		file_put_contents("{$g['unbound_chroot_path']}/domainoverrides.conf", $domain_entries);
523
	}
524
}
525

    
526
function unbound_add_host_entries() {
527
	global $config, $g;
528

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

    
531
	$hosts = read_hosts();
532
	$added_ptr = array();
533
	foreach ($hosts as $host) {
534
		if (is_ipaddrv4($host['ipaddr'])) {
535
			$type = 'A';
536
		} else if (is_ipaddrv6($host['ipaddr'])) {
537
			$type = 'AAAA';
538
		} else {
539
			continue;
540
		}
541

    
542
		if (!$added_ptr[$host['ipaddr']]) {
543
			$unbound_entries .= "local-data-ptr: \"{$host['ipaddr']} {$host['fqdn']}\"\n";
544
			$added_ptr[$host['ipaddr']] = true;
545
		}
546
		$unbound_entries .= "local-data: \"{$host['fqdn']} {$type} {$host['ipaddr']}\"\n";
547
		if (isset($host['name'])) {
548
			$unbound_entries .= "local-data: \"{$host['name']} {$type} {$host['ipaddr']}\"\n";
549
		}
550
	}
551

    
552
	// Write out entries
553
	create_unbound_chroot_path();
554
	file_put_contents("{$g['unbound_chroot_path']}/host_entries.conf", $unbound_entries);
555

    
556
	/* dhcpleases will write to this config file, make sure it exists */
557
	@touch("{$g['unbound_chroot_path']}/dhcpleases_entries.conf");
558
}
559

    
560
function unbound_control($action) {
561
	global $config, $g;
562

    
563
	$cache_dumpfile = "/var/tmp/unbound_cache";
564

    
565
	switch ($action) {
566
	case "start":
567
		// Start Unbound
568
		if ($config['unbound']['enable'] == "on") {
569
			if (!is_service_running("unbound")) {
570
				do_as_unbound_user("start");
571
			}
572
		}
573
		break;
574
	case "stop":
575
		if ($config['unbound']['enable'] == "on") {
576
			do_as_unbound_user("stop");
577
		}
578
		break;
579
	case "reload":
580
		if ($config['unbound']['enable'] == "on") {
581
			do_as_unbound_user("reload");
582
		}
583
		break;
584
	case "dump_cache":
585
		// Dump Unbound's Cache
586
		if ($config['unbound']['dumpcache'] == "on") {
587
			do_as_unbound_user("dump_cache");
588
		}
589
		break;
590
	case "restore_cache":
591
		// Restore Unbound's Cache
592
		if ((is_service_running("unbound")) && ($config['unbound']['dumpcache'] == "on")) {
593
			if (file_exists($cache_dumpfile) && filesize($cache_dumpfile) > 0) {
594
				do_as_unbound_user("load_cache < /var/tmp/unbound_cache");
595
			}
596
		}
597
		break;
598
	default:
599
		break;
600

    
601
	}
602
}
603

    
604
// Generation of Unbound statistics
605
function unbound_statistics() {
606
	global $config;
607

    
608
	if ($config['stats'] == "on") {
609
		$stats_interval = $config['unbound']['stats_interval'];
610
		$cumulative_stats = $config['cumulative_stats'];
611
		if ($config['extended_stats'] == "on") {
612
			$extended_stats = "yes";
613
		} else {
614
			$extended_stats = "no";
615
		}
616
	} else {
617
		$stats_interval = "0";
618
		$cumulative_stats = "no";
619
		$extended_stats = "no";
620
	}
621
	/* XXX To do - add RRD graphs */
622
	$stats = <<<EOF
623
# Unbound Statistics
624
statistics-interval: {$stats_interval}
625
extended-statistics: yes
626
statistics-cumulative: yes
627

    
628
EOF;
629

    
630
	return $stats;
631
}
632

    
633
// Unbound Access lists
634
function unbound_acls_config() {
635
	global $g, $config;
636

    
637
	if (!isset($config['unbound']['disable_auto_added_access_control'])) {
638
		$aclcfg = "access-control: 127.0.0.1/32 allow\n";
639
		$aclcfg .= "access-control: ::1 allow\n";
640
		// Add our networks for active interfaces including localhost
641
		if (!empty($config['unbound']['active_interface'])) {
642
			$active_interfaces = array_flip(explode(",", $config['unbound']['active_interface']));
643
			if (in_array("all", $active_interfaces)) {
644
				$active_interfaces = get_configured_interface_with_descr();
645
			}
646
		} else {
647
			$active_interfaces = get_configured_interface_with_descr();
648
		}
649

    
650
		$bindints = "";
651
		foreach ($active_interfaces as $ubif => $ifdesc) {
652
			$ifip = get_interface_ip($ubif);
653
			if (is_ipaddrv4($ifip)) {
654
				// IPv4 is handled via NAT networks below
655
			}
656
			$ifip = get_interface_ipv6($ubif);
657
			if (is_ipaddrv6($ifip)) {
658
				if (!is_linklocal($ifip)) {
659
					$subnet_bits = get_interface_subnetv6($ubif);
660
					$subnet_ip = gen_subnetv6($ifip, $subnet_bits);
661
					// only add LAN-type interfaces
662
					if (!interface_has_gateway($ubif)) {
663
						$aclcfg .= "access-control: {$subnet_ip}/{$subnet_bits} allow\n";
664
					}
665
				}
666
				// add for IPv6 static routes to local networks
667
				// for safety, we include only routes reachable on an interface with no
668
				// gateway specified - read: not an Internet connection.
669
				$static_routes = get_staticroutes();
670
				foreach ($static_routes as $route) {
671
					if ((lookup_gateway_interface_by_name($route['gateway']) == $ubif) && !interface_has_gateway($ubif)) {
672
						// route is on this interface, interface doesn't have gateway, add it
673
						$aclcfg .= "access-control: {$route['network']} allow\n";
674
					}
675
				}
676
			}
677
		}
678

    
679
		// Generate IPv4 access-control entries using the same logic as automatic outbound NAT
680
		if (empty($FilterIflist)) {
681
			filter_generate_optcfg_array();
682
		}
683
		$natnetworks_array = array();
684
		$natnetworks_array = filter_nat_rules_automatic_tonathosts();
685
		foreach ($natnetworks_array as $allowednet) {
686
			$aclcfg .= "access-control: $allowednet allow \n";
687
		}
688
	}
689

    
690
	// Configure the custom ACLs
691
	if (is_array($config['unbound']['acls'])) {
692
		foreach ($config['unbound']['acls'] as $unbound_acl) {
693
			$aclcfg .= "#{$unbound_acl['aclname']}\n";
694
			foreach ($unbound_acl['row'] as $network) {
695
				if ($unbound_acl['aclaction'] == "allow snoop") {
696
					$unbound_acl['aclaction'] = "allow_snoop";
697
				}
698
				$aclcfg .= "access-control: {$network['acl_network']}/{$network['mask']} {$unbound_acl['aclaction']}\n";
699
			}
700
		}
701
	}
702
	// Write out Access list
703
	create_unbound_chroot_path();
704
	file_put_contents("{$g['unbound_chroot_path']}/access_lists.conf", $aclcfg);
705

    
706
}
707

    
708
// Generate hosts and reload services
709
function unbound_hosts_generate() {
710
	// Generate our hosts file
711
	unbound_add_host_entries();
712

    
713
	// Reload our service to read the updates
714
	unbound_control("reload");
715
}
716

    
717
?>
(54-54/67)