Project

General

Profile

Download (22.5 KB) Statistics
| Branch: | Tag: | Revision:
1 f20afeb6 Warren Baker
<?php
2
/*
3 fe9d4894 Renato Botelho
	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 f20afeb6 Warren Baker
*/
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 fe9d4894 Renato Botelho
	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 f20afeb6 Warren Baker
98
}
99
100
function unbound_generate_config() {
101 fe9d4894 Renato Botelho
	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 f20afeb6 Warren Baker
# 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 fe9d4894 Renato Botelho
	}
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 75e6d1b2 Warren Baker
# Forwarding
225 be5aa310 Warren Baker
forward-zone:
226 fe9d4894 Renato Botelho
	name: "."
227 be5aa310 Warren Baker
228
EOD;
229 fe9d4894 Renato Botelho
			foreach($dnsservers as $dnsserver)
230
				$forward_conf .= "\tforward-addr: $dnsserver\n";
231
		}
232
	} else
233
		$forward_conf = "";
234 be5aa310 Warren Baker
235 fe9d4894 Renato Botelho
	$unboundconf = <<<EOD
236 f20afeb6 Warren Baker
##########################
237
# Unbound Configuration
238
##########################
239
240
##
241
# Server configuration
242
##
243
server:
244
chroot: {$g['unbound_chroot_path']}
245
username: "unbound"
246 56a87b19 Warren Baker
directory: "{$g['unbound_chroot_path']}"
247 f20afeb6 Warren Baker
pidfile: "/var/run/unbound.pid"
248
use-syslog: yes
249 d12889b0 Warren Baker
port: {$port}
250 56a87b19 Warren Baker
verbosity: {$verbosity}
251 f20afeb6 Warren Baker
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 56a87b19 Warren Baker
cache-max-ttl: {$cache_max_ttl}
268
cache-min-ttl: {$cache_min_ttl}
269 f20afeb6 Warren Baker
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 56a87b19 Warren Baker
prefetch: {$prefetch}
281
prefetch-key: {$prefetch_key}
282 f20afeb6 Warren Baker
# Statistics
283
{$statistics}
284
# Interface IP(s) to bind to
285 16a3108f Warren Baker
{$bindints}
286 6374fb57 Warren Baker
{$outgoingints}
287 f20afeb6 Warren Baker
288
# DNS Rebinding
289
{$private_addr}
290
{$private_domains}
291
292 4e8e8cc8 Warren Baker
# Access lists
293
include: {$g['unbound_chroot_path']}/access_lists.conf
294
295 f20afeb6 Warren Baker
# Static host entries
296 56a87b19 Warren Baker
include: {$g['unbound_chroot_path']}/host_entries.conf
297 f20afeb6 Warren Baker
298
# Domain overrides
299 ae5bbb64 Warren Baker
include: {$g['unbound_chroot_path']}/domainoverrides.conf
300 be5aa310 Warren Baker
{$forward_conf}
301
302 f20afeb6 Warren Baker
{$custom_options}
303
304
###
305
# Remote Control Config
306
###
307 56a87b19 Warren Baker
include: {$g['unbound_chroot_path']}/remotecontrol.conf
308 f20afeb6 Warren Baker
309
EOD;
310
311 fe9d4894 Renato Botelho
	file_put_contents("{$g['unbound_chroot_path']}/unbound.conf", $unboundconf);
312 f20afeb6 Warren Baker
313 fe9d4894 Renato Botelho
	return 0;
314 f20afeb6 Warren Baker
}
315
316
function unbound_remote_control_setup() {
317 fe9d4894 Renato Botelho
	global $g;
318 f20afeb6 Warren Baker
319 fe9d4894 Renato Botelho
	if (!file_exists("{$g['unbound_chroot_path']}/remotecontrol.conf") || !file_exists("{$g['unbound_chroot_path']}/unbound_control.key")) {
320
		$remotcfg = <<<EOF
321 f20afeb6 Warren Baker
remote-control:
322 fe9d4894 Renato Botelho
	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 56a87b19 Warren Baker
330 f20afeb6 Warren Baker
EOF;
331
332 fe9d4894 Renato Botelho
		file_put_contents("{$g['unbound_chroot_path']}/remotecontrol.conf", $remotcfg);
333 f20afeb6 Warren Baker
334 fe9d4894 Renato Botelho
		// Generate our keys
335
		do_as_unbound_user("unbound-control-setup");
336 f20afeb6 Warren Baker
337 fe9d4894 Renato Botelho
	}
338 56a87b19 Warren Baker
}
339 f20afeb6 Warren Baker
340
341 56a87b19 Warren Baker
// Read /etc/hosts
342 f20afeb6 Warren Baker
function read_hosts() {
343
344 fe9d4894 Renato Botelho
	/* 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 f20afeb6 Warren Baker
}
366
367
function sync_unbound_service() {
368 fe9d4894 Renato Botelho
	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 f20afeb6 Warren Baker
386
}
387
388
function unbound_acl_id_used($id) {
389 fe9d4894 Renato Botelho
	global $config;
390 f20afeb6 Warren Baker
391 fe9d4894 Renato Botelho
	if (is_array($config['unbound']['acls']))
392
		foreach($config['unbound']['acls'] as & $acls)
393
			if ($id == $acls['aclid'])
394
				return true;
395 f20afeb6 Warren Baker
396 fe9d4894 Renato Botelho
	return false;
397 f20afeb6 Warren Baker
}
398
399
function unbound_get_next_id() {
400 fe9d4894 Renato Botelho
	$aclid = 0;
401
	while(unbound_acl_id_used($aclid))
402
		$aclid++;
403
	return $aclid;
404 f20afeb6 Warren Baker
}
405
406
// Execute commands as the user unbound
407
function do_as_unbound_user($cmd) {
408 fe9d4894 Renato Botelho
	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 f20afeb6 Warren Baker
}
427
428
function unbound_add_domain_overrides($pvt=false) {
429 fe9d4894 Renato Botelho
	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 f20afeb6 Warren Baker
}
462
463
function unbound_add_host_entries() {
464 fe9d4894 Renato Botelho
	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 f20afeb6 Warren Baker
}
564
565 56a87b19 Warren Baker
function unbound_control($action) {
566 fe9d4894 Renato Botelho
	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 56a87b19 Warren Baker
}
603
604
// Generation of Unbound statistics
605
function unbound_statistics() {
606 fe9d4894 Renato Botelho
	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 56a87b19 Warren Baker
# Unbound Statistics
623
statistics-interval: {$stats_interval}
624
extended-statistics: yes
625
statistics-cumulative: yes
626
627
EOF;
628
629 fe9d4894 Renato Botelho
	return $stats;
630 56a87b19 Warren Baker
}
631
632 8fccab67 Warren Baker
// Unbound Access lists
633
function unbound_acls_config() {
634 fe9d4894 Renato Botelho
	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 58d00e65 Warren Baker
674 8fccab67 Warren Baker
}
675
676 f6248774 Warren Baker
// Generate hosts and reload services
677
function unbound_hosts_generate() {
678 fe9d4894 Renato Botelho
	// Generate our hosts file
679
	unbound_add_host_entries();
680 f6248774 Warren Baker
681 fe9d4894 Renato Botelho
	// Reload our service to read the updates
682
	unbound_control("reload");
683 f6248774 Warren Baker
}
684
685 3f0c20c3 Renato Botelho
?>