Project

General

Profile

Download (22.5 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	unbound.inc
4
	part of the pfSense project (https://www.pfsense.org)
5
	Copyright (C) 2014 Warren Baker <warren@decoy.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/sbin/unbound  /usr/sbin/unbound-anchor    /usr/sbin/unbound-checkconf
30
	pfSense_BUILDER_BINARIES:   /usr/sbin/unbound-control    /usr/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

    
38
/* Optimize Unbound for environment */
39
function unbound_optimization() {
40
	global $config;
41

    
42
	$optimization_settings = array();
43

    
44
	/* 
45
	 * Set the number of threads equal to number of CPUs.
46
	 * Use 1 to disable threading, if for some reason this sysctl fails.
47
	 */
48
	$numprocs = intval(get_single_sysctl('kern.smp.cpus'));
49
	if ($numprocs > 0)
50
		$optimization['number_threads'] = "num-threads: {$numprocs}";
51
	else
52
		$optimization['number_threads'] = "num-threads: 1";
53

    
54
	// Slabs to help reduce lock contention.
55
	if ($numprocs > 4) {
56
		$optimization['msg_cache_slabs'] = "msg-cache-slabs: {$numprocs}";
57
		$optimization['rrset_cache_slabs'] = "rrset-cache-slabs: {$numprocs}";
58
		$optimization['infra_cache_slabs'] = "infra-cache-slabs: {$numprocs}";
59
		$optimization['key_cache_slabs'] = "key-cache-slabs: {$numprocs}";
60
	} else {
61
		$optimization['msg_cache_slabs'] = "msg-cache-slabs: 4";
62
		$optimization['rrset_cache_slabs'] = "rrset-cache-slabs: 4";
63
		$optimization['infra_cache_slabs'] = "infra-cache-slabs: 4";
64
		$optimization['key_cache_slabs'] = "key-cache-slabs: 4";
65
	}
66

    
67
	// Memory usage default of 4MB
68
	$optimization['msg_cache_size'] = "msg-cache-size: 4m";
69
	$optimization['rrset_cache_size'] = "rrset-cache-size: 8m";
70

    
71
	// More outgoing connections per thread otherwise assign a default of 4096 for a single thread
72
	if ($numprocs > 0) {
73
		$or = (1024/$numprocs) - 50;
74
		$optimization['outgoing_range'] = "outgoing-range: {$or}";
75
	} else
76
		$optimization['outgoing_range'] = "outgoing-range: {4096}";
77

    
78
	/*
79
	 * Larger socket buffer for busy servers
80
	 * Check that it is set to 4MB (by default the OS has it configured to 4MB)
81
	 */
82
	foreach ($config['sysctl']['item'] as $tunable) {
83
		if ($tunable['tunable'] == 'kern.ipc.maxsockbuf') {
84
			$so = floor(($tunable['value']/1024/1024)-1);
85
			// Check to ensure that the number is not a negative
86
			if ($so > 0)
87
				$optimization['so_rcvbuf'] = "so-rcvbuf: {$so}m";
88
			else
89
				unset($optimization['so_rcvbuf']);
90
		}
91
	}
92
	// Safety check in case kern.ipc.maxsockbuf is not available.
93
	if (!isset($optimization['so_rcvbuf']))
94
		$optimization['so_rcvbuf'] = "#so-rcvbuf: 4m";
95

    
96
	return $optimization;
97

    
98
}
99

    
100
function unbound_generate_config() {
101
	global $config, $g;
102

    
103
	// Setup optimization
104
	$optimization = unbound_optimization();
105

    
106
	// Setup DNSSEC support
107
	if (isset($config['unbound']['dnssec'])) {
108
		$module_config = "validator iterator";
109
		$anchor_file = "auto-trust-anchor-file: {$g['unbound_chroot_path']}/root.key";
110
	} else
111
		$module_config = "iterator";
112

    
113
	// Setup DNS Rebinding
114
	if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
115
		// Private-addresses for DNS Rebinding
116
		$private_addr = <<<EOF
117
# For DNS Rebinding prevention
118
private-address: 10.0.0.0/8
119
private-address: 172.16.0.0/12
120
private-address: 192.168.0.0/16
121
private-address: 192.254.0.0/16
122
private-address: fd00::/8
123
private-address: fe80::/10
124
EOF;
125
	}
126

    
127
	// Determine interfaces to run on
128
	$bindints = "";
129
	if (!empty($config['unbound']['active_interface'])) {
130
		$active_interfaces = explode(",", $config['unbound']['active_interface']);
131
		foreach($active_interfaces as $ubif) {
132
			$intip = get_interface_ip($ubif);
133
			if (!is_null($intip))
134
				$bindints .= "interface: $intip\n";
135
			$intip = get_interface_ipv6($ubif);
136
			if (!is_null($intip))
137
				$bindints .= "interface: $intip\n";
138
		}
139
	} else {
140
		$bindints .= "interface: 0.0.0.0\n";
141
		$bindints .= "interface: ::0\n";
142
	}
143

    
144
	// Determine interfaces to run on
145
	$outgoingints = "";
146
	if (!empty($config['unbound']['outgoing_interface'])) {
147
		$outgoingints = "# Outgoing interfaces to be used\n";
148
		$outgoing_interfaces = explode(",", $config['unbound']['outgoing_interface']);
149
		foreach($outgoing_interfaces as $outif) {
150
			$outip = get_interface_ip($outif);
151
			if (!is_null($outip))
152
				$outgoingints .= "outgoing-interface: $outip\n";
153
			$outip = get_interface_ipv6($outif);
154
			if (!is_null($outip))
155
				$outgoingints .= "outgoing-interface: $outip\n";
156
		}
157
	}
158

    
159
	// Allow DNS Rebind for forwarded domains
160
	if ((isset($config['unbound']['domainoverrides']) && is_array($config['unbound']['domainoverrides'])) && !isset($config['system']['webgui']['nodnsrebindcheck'])) {
161
		$private_domains = "# Set private domains in case authoritative name server returns a Private IP address\n";
162
		$private_domains .= unbound_add_domain_overrides(true);
163
	}
164

    
165
	// Configure static Host entries
166
	unbound_add_host_entries();
167

    
168
	// Configure Domain Overrides
169
	unbound_add_domain_overrides();
170

    
171
	// Configure Unbound statistics
172
	$statistics = unbound_statistics();
173

    
174
	// Configure Unbound access-lists
175
	unbound_acls_config();
176

    
177
	// Add custom Unbound options
178
	if ($config['unbound']['custom_options']) {
179
		$custom_option = "# Unbound custom option";
180
		foreach (preg_split('/\s+/', $config['unbound']['custom_options']) as $ent)
181
			$custom_option .= $ent."\n";
182
	}
183

    
184
	// Server configuration variables
185
	$port = (is_port($config['unbound']['port'])) ? $config['unbound']['port'] : "53";
186
	$hide_id = ($config['unbound']['hide_id'] == "on") ? "yes" : "no";
187
	$hide_version = ($config['unbound']['hide_version'] == "on") ? "yes" : "no";
188
	$harden_glue = ($config['unbound']['harden_glue'] == "on") ? "yes" : "no";
189
	$harden_dnssec_stripped = ($config['unbound']['harden_dnssec_stripped'] == "on") ? "yes" : "no";
190
	$prefetch = ($config['unbound']['prefetch'] == "on") ? "yes" : "no";
191
	$prefetch_key = ($config['unbound']['prefetch_key'] == "on") ? "yes" : "no";
192
	$outgoing_num_tcp = (!empty($config['unbound']['outgoing_num_tcp'])) ? $config['unbound']['outgoing_num_tcp'] : "10";
193
	$incoming_num_tcp = (!empty($config['unbound']['incoming_num_tcp'])) ? $config['unbound']['incoming_num_tcp'] : "10";
194
	$edns_buffer_size = (!empty($config['unbound']['edns_buffer_size'])) ? $config['unbound']['edns_buffer_size'] : "4096";
195
	$num_queries_per_thread = (!empty($config['unbound']['num_queries_per_thread'])) ? $config['unbound']['num_queries_per_thread'] : "4096";
196
	$jostle_timeout = (!empty($config['unbound']['jostle_timeout'])) ? $config['unbound']['jostle_timeout'] : "200";
197
	$cache_max_ttl = (!empty($config['unbound']['cache_max_ttl'])) ? $config['unbound']['cache_max_ttl'] : "86400";
198
	$cache_min_ttl = (!empty($config['unbound']['cache_min_ttl'])) ? $config['unbound']['cache_min_ttl'] : "0";
199
	$infra_host_ttl = (!empty($config['unbound']['infra_host_ttl'])) ? $config['unbound']['infra_host_ttl'] : "900";
200
	$infra_lame_ttl = (!empty($config['unbound']['infra_lame_ttl'])) ? $config['unbound']['infra_lame_ttl'] : "900";
201
	$infra_cache_numhosts = (!empty($config['unbound']['infra_cache_numhosts'])) ? $config['unbound']['infra_cache_numhosts'] : "10000";
202
	$unwanted_reply_threshold = (!empty($config['unbound']['unwanted_reply_threshold'])) ? $config['unbound']['unwanted_reply_threshold'] : "0";
203
	$verbosity = isset($config['unbound']['loglevel']) ? $config['unbound']['loglevel'] : 1;
204

    
205
	// Set up forwarding if it configured
206
	if (isset($config['unbound']['forwarding'])) {
207
		$dnsservers = array();
208
		if (isset($config['system']['dnsallowoverride'])) {
209
			$ns = array_unique(get_nameservers());
210
			foreach($ns as $nameserver) {
211
				if ($nameserver)
212
					$dnsservers[] = $nameserver;
213
			}
214
		} else {
215
			$ns = array_unique(get_dns_servers());
216
			foreach($ns as $nameserver) {
217
				if ($nameserver)
218
					$dnsservers[] = $nameserver;
219
			}
220
		}
221

    
222
		if (!empty($dnsservers)) {
223
			$forward_conf .=<<<EOD
224
# Forwarding
225
forward-zone:
226
	name: "."
227

    
228
EOD;
229
			foreach($dnsservers as $dnsserver)
230
				$forward_conf .= "\tforward-addr: $dnsserver\n";
231
		}
232
	} else
233
		$forward_conf = "";
234

    
235
	$unboundconf = <<<EOD
236
##########################
237
# Unbound Configuration
238
##########################
239

    
240
##
241
# Server configuration
242
##
243
server:
244
chroot: {$g['unbound_chroot_path']}
245
username: "unbound"
246
directory: "{$g['unbound_chroot_path']}"
247
pidfile: "/var/run/unbound.pid"
248
use-syslog: yes
249
port: {$port}
250
verbosity: {$verbosity}
251
harden-referral-path: no
252
do-ip4: yes
253
do-ip6: yes
254
do-udp: yes
255
do-tcp: yes
256
do-daemonize: yes
257
module-config: "{$module_config}"
258
unwanted-reply-threshold: 0
259
num-queries-per-thread: 1024
260
jostle-timeout: 200
261
infra-host-ttl: 900
262
infra-lame-ttl: 900
263
infra-cache-numhosts: 10000
264
outgoing-num-tcp: 10
265
incoming-num-tcp: 10
266
edns-buffer-size: 4096
267
cache-max-ttl: {$cache_max_ttl}
268
cache-min-ttl: {$cache_min_ttl}
269
harden-dnssec-stripped: yes
270
{$optimization['number_threads']}
271
{$optimization['msg_cache_slabs']}
272
{$optimization['rrset_cache_slabs']}
273
{$optimization['infra_cache_slabs']}
274
{$optimization['key_cache_slabs']}
275
{$optimization['msg_cache_size']}
276
{$optimization['rrset_cache_size']}
277
{$optimization['outgoing_range']}
278
{$optimization['so_rcvbuf']}
279
{$anchor_file}
280
prefetch: {$prefetch}
281
prefetch-key: {$prefetch_key}
282
# Statistics
283
{$statistics}
284
# Interface IP(s) to bind to
285
{$bindints}
286
{$outgoingints}
287

    
288
# DNS Rebinding
289
{$private_addr}
290
{$private_domains}
291

    
292
# Access lists
293
include: {$g['unbound_chroot_path']}/access_lists.conf
294

    
295
# Static host entries
296
include: {$g['unbound_chroot_path']}/host_entries.conf
297

    
298
# Domain overrides
299
include: {$g['unbound_chroot_path']}/domainoverrides.conf
300
{$forward_conf}
301

    
302
{$custom_options}
303

    
304
###
305
# Remote Control Config
306
###
307
include: {$g['unbound_chroot_path']}/remotecontrol.conf
308

    
309
EOD;
310

    
311
	file_put_contents("{$g['unbound_chroot_path']}/unbound.conf", $unboundconf);
312

    
313
	return 0;
314
}
315

    
316
function unbound_remote_control_setup() {
317
	global $g;
318

    
319
	if (!file_exists("{$g['unbound_chroot_path']}/remotecontrol.conf") || !file_exists("{$g['unbound_chroot_path']}/unbound_control.key")) {
320
		$remotcfg = <<<EOF
321
remote-control:
322
	control-enable: yes
323
	control-interface: 127.0.0.1
324
	control-port: 953
325
	server-key-file: "{$g['unbound_chroot_path']}/unbound_server.key"
326
	server-cert-file: "{$g['unbound_chroot_path']}/unbound_server.pem"
327
	control-key-file: "{$g['unbound_chroot_path']}/unbound_control.key"
328
	control-cert-file: "{$g['unbound_chroot_path']}/unbound_control.pem"
329

    
330
EOF;
331

    
332
		file_put_contents("{$g['unbound_chroot_path']}/remotecontrol.conf", $remotcfg);
333

    
334
		// Generate our keys
335
		do_as_unbound_user("unbound-control-setup");
336

    
337
	}
338
}
339

    
340

    
341
// Read /etc/hosts
342
function read_hosts() {
343

    
344
	/* Open /etc/hosts and extract the only dhcpleases info
345
	 * XXX - to convert to an unbound C library which reads /etc/hosts automatically
346
	 */
347
	$etc_hosts = array();
348
	foreach (file('/etc/hosts') as $line) {
349
		$d = preg_split('/\s/', $line, -1, PREG_SPLIT_NO_EMPTY);
350
		if (empty($d) || substr(reset($d), 0, 1) == "#")
351
			continue;
352
		if ($d[3] == "#") {
353
			$ip = array_shift($d);
354
			$fqdn = array_shift($d);
355
			$name = array_shift($d);
356
			if ($fqdn != "empty") {
357
				if ($name != "empty")
358
					array_push($etc_hosts, array(ipaddr => "$ip", fqdn => "$fqdn", name => "$name"));
359
				else
360
					array_push($etc_hosts, array(ipaddr => "$ip", fqdn => "$fqdn"));
361
			}
362
		}
363
	}
364
	return $etc_hosts;
365
}
366

    
367
function sync_unbound_service() {
368
	global $config, $g;
369

    
370
	// Configure chroot
371
	if (!is_dir($g['unbound_chroot_path'])) {
372
		mkdir($g['unbound_chroot_path']);
373
		chown($g['unbound_chroot_path'], "unbound");
374
		chgrp($g['unbound_chroot_path'], "unbound");
375
	}
376

    
377
	// Configure our Unbound service
378
	do_as_unbound_user("unbound-anchor");
379
	unbound_remote_control_setup();
380
	unbound_generate_config();
381
	do_as_unbound_user("start");
382
	require_once("service-utils.inc");
383
	if (is_service_running("unbound"))
384
		do_as_unbound_user("restore_cache");
385

    
386
}
387

    
388
function unbound_acl_id_used($id) {
389
	global $config;
390

    
391
	if (is_array($config['unbound']['acls']))
392
		foreach($config['unbound']['acls'] as & $acls)
393
			if ($id == $acls['aclid'])
394
				return true;
395

    
396
	return false;
397
}
398

    
399
function unbound_get_next_id() {
400
	$aclid = 0;
401
	while(unbound_acl_id_used($aclid))
402
		$aclid++;
403
	return $aclid;
404
}
405

    
406
// Execute commands as the user unbound
407
function do_as_unbound_user($cmd) {
408
	global $g;
409

    
410
	switch ($cmd) {
411
	case "start":
412
		mwexec("/usr/sbin/unbound -c {$g['unbound_chroot_path']}/unbound.conf");
413
		break;
414
	case "stop":
415
		mwexec("echo '/usr/sbin/unbound-control stop' | /usr/bin/su -m unbound", true);
416
		break;
417
	case "unbound-anchor":
418
		mwexec("echo '/usr/sbin/unbound-anchor -a {$g['unbound_chroot_path']}/root.key' | /usr/bin/su -m unbound", true);
419
		break;
420
	case "unbound-control-setup":
421
		mwexec("echo '/usr/sbin/unbound-control-setup -d {$g['unbound_chroot_path']}' | /usr/bin/su -m unbound", true);
422
		break;
423
	default:
424
		break;
425
	}
426
}
427

    
428
function unbound_add_domain_overrides($pvt=false) {
429
	global $config, $g;
430

    
431
	$domains = $config['unbound']['domainoverrides'];
432

    
433
	$sorted_domains = msort($domains, "domain");
434
	$result = array();
435
	foreach($sorted_domains as $domain) {
436
		$domain_key = current($domain);
437
		if (!isset($result[$domain_key]))
438
			$result[$domain_key] = array();
439
		$result[$domain_key][] = $domain['ip'];
440
	}
441

    
442
	// Domain overrides that have multiple entries need multiple stub-addr: added
443
	$domain_entries = "";
444
	foreach($result as $domain=>$ips) {
445
		if ($pvt == true) {
446
			$domain_entries .= "private-domain: \"$domain\"\n";
447
			$domain_entries .= "domain-insecure: \"$domain\"\n";
448
		} else {
449
			$domain_entries .= "stub-zone:\n";
450
			$domain_entries .= "\tname: \"$domain\"\n";
451
			foreach($ips as $ip)
452
				$domain_entries .= "\tstub-addr: $ip\n";
453
			$domain_entries .= "\tstub-prime: no\n";
454
		}
455
	}
456

    
457
	if ($pvt == true)
458
		return $domain_entries;
459
	else
460
		file_put_contents("{$g['unbound_chroot_path']}/domainoverrides.conf", $domain_entries);
461
}
462

    
463
function unbound_add_host_entries() {
464
	global $config, $g;
465

    
466
	$unbound_entries = "local-zone: \"{$config['system']['domain']}\" transparent\n";
467
	// IPv4 entries
468
	$unbound_entries .= "local-data-ptr: \"127.0.0.1 localhost\"\n";
469
	$unbound_entries .= "local-data: \"localhost A 127.0.0.1\"\n";
470
	$unbound_entries .= "local-data: \"localhost.{$config['system']['domain']} A 127.0.0.1\"\n";
471
	// IPv6 entries
472
	$unbound_entries .= "local-data-ptr: \"::1 localhost\"\n";
473
	$unbound_entries .= "local-data: \"localhost AAAA ::1\"\n";
474
	$unbound_entries .= "local-data: \"localhost.{$config['system']['domain']} AAAA ::1\"\n";
475

    
476
	$listen_addresses = "";
477
	if (isset($config['unbound']['interface'])) {
478
		$interfaces = explode(",", $config['unbound']['interface']);
479
		foreach ($interfaces as $interface) {
480
			if (is_ipaddrv4($interface)) {
481
				$unbound_entries .= "local-data-ptr: \"{$interface} {$config['system']['hostname']}.{$config['system']['domain']}\"\n";
482
				$unbound_entries .= "local-data: \"{$config['system']['hostname']}.{$config['system']['domain']} A {$interface}\"\n";
483
				$unbound_entries .= "local-data: \"{$config['system']['hostname']} A {$interface}\"\n";
484
			} else if (is_ipaddrv6($interface)) {
485
				$unbound_entries .= "local-data: \"{$config['system']['hostname']}.{$config['system']['domain']} AAAA {$interface}\"\n";
486
				$unbound_entries .= "local-data: \"{$config['system']['hostname']} AAAA {$interface}\"\n";
487
			} else {
488
				$if = get_real_interface($interface);
489
				if (does_interface_exist($if)) {
490
					$laddr = find_interface_ip($if);
491
					if (is_ipaddrv4($laddr)) {
492
						$unbound_entries .= "local-data-ptr: \"{$laddr} {$config['system']['hostname']}.{$config['system']['domain']}\"\n";
493
						$unbound_entries .= "local-data: \"{$config['system']['hostname']}.{$config['system']['domain']} A {$laddr}\"\n";
494
						$unbound_entries .= "local-data: \"{$config['system']['hostname']} A {$laddr}\"\n";
495
					}
496
					$laddr6 = find_interface_ipv6($if);
497
					if (is_ipaddrv6($laddr6) && !isset($config['dnsmasq']['strictbind'])) {
498
						$unbound_entries .= "local-data-ptr: \"{$laddr6} {$config['system']['hostname']}.{$config['system']['domain']}\"\n";
499
						$unbound_entries .= "local-data: \"{$config['system']['hostname']}.{$config['system']['domain']} AAAA {$laddr}\"\n";
500
						$unbound_entries .= "local-data: \"{$config['system']['hostname']} AAAA {$laddr}\"\n";
501
					}
502
				}
503
			}
504
		}
505
	}
506

    
507
	// Static Host entries
508
	if (isset($config['unbound']['hosts'])) {
509
		$host_entries = "";
510
		$added_item = array();
511
		foreach($config['unbound']['hosts'] as $host) {
512
			$current_host = $host['host'];
513
			if ($host['host'] != "")
514
				$host['host'] = $host['host'].".";
515
			if (!$added_item[$current_host]) {
516
				$host_entries .= "local-data-ptr: \"{$host['ip']} {$host['host']}{$host['domain']}\"\n";
517
				if (is_ipaddrv6($host['ip']))
518
					$host_entries .= "local-data: \"{$host['host']}{$host['domain']} IN AAAA {$host['ip']}\"\n";
519
				else
520
					$host_entries .= "local-data: \"{$host['host']}{$host['domain']} IN A {$host['ip']}\"\n";
521
				if (!empty($host['descr']) && isset($config['unbound']['txtsupport']))
522
					$host_entries .= "local-data: '{$host['host']}{$host['domain']} TXT \"".addslashes($host['descr'])."\"'\n";
523

    
524
				// Do not add duplicate entries
525
				$added_item[$current_host] = true;
526
			}
527
		}
528
		$unbound_entries .= $host_entries;
529
	}
530

    
531
	// Static DHCP entries
532
	$host_entries = "";
533
	if (isset($config['unbound']['regdhcpstatic']) && is_array($config['dhcpd'])) {
534
		foreach ($config['dhcpd'] as $dhcpif => $dhcpifconf)
535
			if (is_array($dhcpifconf['staticmap']) && isset($dhcpifconf['enable']))
536
				foreach ($dhcpifconf['staticmap'] as $host)
537
					if ($host['ipaddr'] && $host['hostname']) {
538
						$host_entries .= "local-data-ptr: \"{$host['ipaddr']} {$host['hostname']}.{$config['system']['domain']}\"\n";
539
						$host_entries .= "local-data: \"{$host['hostname']}.{$config['system']['domain']} IN A {$host['ipaddr']}\"\n";
540
						if (!empty($host['descr']) && $unboundcfg['txtsupport'] == 'on')
541
							$host_entries .= "local-data: '{$host['hostname']}.{$config['system']['domain']} TXT \"".addslashes($host['descr'])."\"'\n";
542
					}
543
		$unbound_entries .= $host_entries;
544
	}
545

    
546
	// Handle DHCPLeases added host entries
547
	$dhcplcfg = read_hosts();
548
	$host_entries = "";
549
	if (is_array($dhcplcfg)) {
550
		foreach($dhcplcfg as $key=>$host) {
551
			$host_entries .= "local-data-ptr: \"{$host['ipaddr']} {$host['fqdn']}\"\n";
552
			$host_entries .= "local-data: \"{$host['fqdn']} IN A {$host['ipaddr']}\"\n";
553
			if (!empty($host['name'])) {
554
				$host_entries .= "local-data-ptr: \"{$host['ipaddr']} {$host['name']}\"\n";
555
				$host_entries .= "local-data: \"{$host['name']} IN A {$host['ipaddr']}\"\n";
556
			}
557
		}
558
		$unbound_entries .= $host_entries;
559
	}
560

    
561
	// Write out entries
562
	file_put_contents("{$g['unbound_chroot_path']}/host_entries.conf", $unbound_entries);
563
}
564

    
565
function unbound_control($action) {
566
	global $config, $g;
567

    
568
	$cache_dumpfile = "/var/tmp/unbound_cache";
569

    
570
	switch ($action) {
571
	case "start":
572
		// Start Unbound
573
		if ($config['unbound']['enable'] == "on") {
574
			if (!is_service_running("unbound"))
575
				do_as_unbound_user("start");
576
		}
577
		break;
578
	case "stop":
579
		if ($config['unbound']['enable'] == "on")
580
			do_as_unbound_user("stop");
581
		break;
582
	case "reload":
583
		if ($config['unbound']['enable'] == "on")
584
			do_as_unbound_user("reload");
585
		break;
586
	case "dump_cache":
587
		// Dump Unbound's Cache
588
		if ($config['unbound']['dumpcache'] == "on")
589
			do_as_unbound_user("dump_cache");
590
		break;
591
	case "restore_cache":
592
		// Restore Unbound's Cache
593
		if ((is_service_running("unbound")) && ($config['unbound']['dumpcache'] == "on")) {
594
			if (file_exists($cache_dumpfile) && filesize($cache_dumpfile) > 0)
595
				do_as_unbound_user("load_cache < /var/tmp/unbound_cache");
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
	} else {
616
		$stats_interval = "0";
617
		$cumulative_stats = "no";
618
		$extended_stats = "no";
619
	}
620
	/* XXX To do - add RRD graphs */
621
	$stats = <<<EOF
622
# Unbound Statistics
623
statistics-interval: {$stats_interval}
624
extended-statistics: yes
625
statistics-cumulative: yes
626

    
627
EOF;
628

    
629
	return $stats;
630
}
631

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

    
636
	$aclcfg = "access-control: 127.0.0.1/32 allow\n";
637
	$aclcfg .= "access-control: ::1 allow\n";
638
	// Add our networks for active interfaces including localhost
639
	if (!empty($config['unbound']['active_interface']))
640
		$active_interfaces = array_flip(explode(",", $config['unbound']['active_interface']));
641
	else
642
		$active_interfaces = get_configured_interface_with_descr();
643

    
644
	$bindints = "";
645
	foreach($active_interfaces as $ubif => $ifdesc) {
646
		$ifip = get_interface_ip($ubif);
647
		if (!is_null($ifip)) {
648
			$subnet_bits = get_interface_subnet($ubif);
649
			$subnet_ip = gen_subnet($ifip, $subnet_bits);
650
			$aclcfg .= "access-control: {$subnet_ip}/{$subnet_bits} allow\n";
651
		}
652
		$ifip = get_interface_ipv6($ubif);
653
		if (!is_null($ifip)) {
654
			$subnet_bits = get_interface_subnetv6($ubif);
655
			$subnet_ip = gen_subnetv6($ifip, $subnet_bits);
656
			$aclcfg .= "access-control: {$subnet_ip}/{$subnet_bits} allow\n";
657
		}
658
	}
659

    
660
	// Configure the custom ACLs
661
	if (is_array($config['unbound']['acls'])) {
662
		foreach($config['unbound']['acls'] as $unbound_acl) {
663
			$aclcfg .= "#{$unbound_acl['aclname']}\n";
664
			foreach($unbound_acl['row'] as $network) {
665
				if ($unbound_acl['aclaction'] == "allow snoop")
666
					$unbound_acl['aclaction'] = "allow_snoop";
667
				$aclcfg .= "access-control: {$network['acl_network']}/{$network['mask']} {$unbound_acl['aclaction']}\n";
668
			}
669
		}
670
	}
671
	// Write out Access list
672
	file_put_contents("{$g['unbound_chroot_path']}/access_lists.conf", $aclcfg);
673

    
674
}
675

    
676
// Generate hosts and reload services
677
function unbound_hosts_generate() {
678
	// Generate our hosts file
679
	unbound_add_host_entries();
680

    
681
	// Reload our service to read the updates
682
	unbound_control("reload");
683
}
684

    
685
?>
(54-54/68)