Project

General

Profile

Download (31.9 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
 * unbound.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6
 * Copyright (c) 2015 Warren Baker <warren@percol8.co.za>
7
 * Copyright (c) 2015-2016 Electric Sheep Fencing
8
 * Copyright (c) 2015-2020 Rubicon Communications, LLC (Netgate)
9
 * All rights reserved.
10
 *
11
 * originally part of m0n0wall (http://m0n0.ch/wall)
12
 * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
13
 * All rights reserved.
14
 *
15
 * Licensed under the Apache License, Version 2.0 (the "License");
16
 * you may not use this file except in compliance with the License.
17
 * You may obtain a copy of the License at
18
 *
19
 * http://www.apache.org/licenses/LICENSE-2.0
20
 *
21
 * Unless required by applicable law or agreed to in writing, software
22
 * distributed under the License is distributed on an "AS IS" BASIS,
23
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24
 * See the License for the specific language governing permissions and
25
 * limitations under the License.
26
 */
27

    
28
/* include all configuration functions */
29
require_once("config.inc");
30
require_once("functions.inc");
31
require_once("filter.inc");
32
require_once("shaper.inc");
33
require_once("interfaces.inc");
34
require_once("util.inc");
35

    
36
function create_unbound_chroot_path($cfgsubdir = "") {
37
	global $config, $g;
38

    
39
	// Configure chroot
40
	if (!is_dir($g['unbound_chroot_path'])) {
41
		mkdir($g['unbound_chroot_path']);
42
		chown($g['unbound_chroot_path'], "unbound");
43
		chgrp($g['unbound_chroot_path'], "unbound");
44
	}
45

    
46
	if ($cfgsubdir != "") {
47
		$cfgdir = $g['unbound_chroot_path'] . $cfgsubdir;
48
		if (!is_dir($cfgdir)) {
49
			mkdir($cfgdir);
50
			chown($cfgdir, "unbound");
51
			chgrp($cfgdir, "unbound");
52
		}
53
	}
54
}
55

    
56
/* Optimize Unbound for environment */
57
function unbound_optimization() {
58
	global $config;
59

    
60
	$optimization_settings = array();
61

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

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

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

    
106
	return $optimization;
107

    
108
}
109

    
110
function test_unbound_config($unboundcfg, &$output) {
111
	global $g;
112

    
113
	$cfgsubdir = "/test";
114
	$cfgdir = "{$g['unbound_chroot_path']}{$cfgsubdir}";
115
	rmdir_recursive($cfgdir);
116

    
117
	unbound_generate_config($unboundcfg, $cfgsubdir);
118
	unbound_remote_control_setup($cfgsubdir);
119
	do_as_unbound_user("unbound-anchor", $cfgsubdir);
120

    
121
	// Copy the Python files to the test folder
122
	if (isset($unboundcfg['python']) && !empty($unboundcfg['python_script'])) {
123
		$python_files = glob("{$g['unbound_chroot_path']}/{$unboundcfg['python_script']}.*");
124
		if (is_array($python_files)) {
125
			foreach ($python_files as $file) {
126
				$file = pathinfo($file, PATHINFO_BASENAME);
127
				@copy("{$g['unbound_chroot_path']}/{$file}", "{$cfgdir}/{$file}");
128
			}
129
		}
130
	}
131

    
132
	$rv = 0;
133
	exec("/usr/local/sbin/unbound-checkconf {$cfgdir}/unbound.conf 2>&1",
134
	    $output, $rv);
135

    
136
	if ($rv == 0) {
137
		rmdir_recursive($cfgdir);
138
	}
139

    
140
	return $rv;
141
}
142

    
143

    
144
function unbound_generate_config($unboundcfg = NULL, $cfgsubdir = "") {
145
	global $g;
146

    
147
	$unboundcfgtxt = unbound_generate_config_text($unboundcfg, $cfgsubdir);
148

    
149
	// Configure static Host entries
150
	unbound_add_host_entries($cfgsubdir);
151

    
152
	// Configure Domain Overrides
153
	unbound_add_domain_overrides("", $cfgsubdir);
154

    
155
	// Configure Unbound access-lists
156
	unbound_acls_config($cfgsubdir);
157

    
158
	create_unbound_chroot_path($cfgsubdir);
159
	file_put_contents("{$g['unbound_chroot_path']}{$cfgsubdir}/unbound.conf", $unboundcfgtxt);
160
}
161

    
162

    
163
function unbound_generate_config_text($unboundcfg = NULL, $cfgsubdir = "") {
164

    
165
	global $config, $g;
166
	if (is_null($unboundcfg)) {
167
		$unboundcfg = $config['unbound'];
168
	}
169

    
170
	// Setup optimization
171
	$optimization = unbound_optimization();
172

    
173
	$module_config = '';
174

    
175
	// Setup Python module (pre validator)
176
	if (isset($unboundcfg['python']) && !empty($unboundcfg['python_script']) && $unboundcfg['python_order'] == 'pre_validator') {
177
		$module_config .= 'python ';
178
	}
179

    
180
	// Setup DNS64 support
181
	if (isset($unboundcfg['dns64'])) {
182
		$module_config .= 'dns64 ';
183
		$dns64_conf = 'dns64-prefix: ';
184
		if (is_subnetv6($unboundcfg['dns64prefix'] . '/' . $unboundcfg['dns64netbits'])) {
185
			$dns64_conf .= $unboundcfg['dns64prefix'] . '/' . $unboundcfg['dns64netbits'];
186
		} else {
187
			$dns64_conf .= '64:ff9b::/96';
188
		}
189
	}
190

    
191
	// Setup DNSSEC support
192
	if (isset($unboundcfg['dnssec'])) {
193
		$module_config .= 'validator ';
194
		$anchor_file = "auto-trust-anchor-file: {$g['unbound_chroot_path']}{$cfgsubdir}/root.key";
195
	}
196

    
197
	// Setup Python module (post validator)
198
	if (isset($unboundcfg['python']) && !empty($unboundcfg['python_script']) && $unboundcfg['python_order'] == 'post_validator') {
199
		$module_config .= 'python ';
200
	}
201

    
202
	$module_config .= 'iterator';
203

    
204
	// Setup DNS Rebinding
205
	if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
206
		// Private-addresses for DNS Rebinding
207
		$private_addr = <<<EOF
208
# For DNS Rebinding prevention
209
private-address: 127.0.0.0/8
210
private-address: 10.0.0.0/8
211
private-address: ::ffff:a00:0/104
212
private-address: 172.16.0.0/12
213
private-address: ::ffff:ac10:0/108
214
private-address: 169.254.0.0/16
215
private-address: ::ffff:a9fe:0/112
216
private-address: 192.168.0.0/16
217
private-address: ::ffff:c0a8:0/112
218
private-address: fd00::/8
219
private-address: fe80::/10
220
EOF;
221
	}
222

    
223
	// Determine interfaces where unbound will bind
224
	$tlsport = is_numeric($unboundcfg['tlsport']) ? $unboundcfg['tlsport'] : "853";
225
	$bindintcfg = "";
226
	$bindints = array();
227
	$active_interfaces = explode(",", $unboundcfg['active_interface']);
228
	if (empty($unboundcfg['active_interface']) || in_array("all", $active_interfaces, true)) {
229
		$bindints[] = "0.0.0.0";
230
		$bindints[] = "::0";
231
		$bindintcfg .= "interface-automatic: " . (isset($unboundcfg['enablessl']) ? "no" : "yes") . "\n";
232
	} else {
233
		foreach ($active_interfaces as $ubif) {
234
			if (is_ipaddr($ubif)) {
235
				$bindints[] = $ubif;
236
			} else {
237
				$intip = get_interface_ip($ubif);
238
				if (is_ipaddrv4($intip)) {
239
					$bindints[] = $intip;
240
				}
241
				$intip = get_interface_ipv6($ubif);
242
				if (is_ipaddrv6($intip)) {
243
					$bindints[] = $intip;
244
				}
245
			}
246
		}
247
	}
248
	foreach ($bindints as $bindint) {
249
		$bindintcfg .= "interface: {$bindint}\n";
250
		if (isset($unboundcfg['enablessl'])) {
251
			$bindintcfg .= "interface: {$bindint}@{$tlsport}\n";
252
		}
253
	}
254

    
255
	// TLS Configuration
256
	$tlsconfig = "tls-cert-bundle: \"/etc/ssl/cert.pem\"\n";
257

    
258
	if (isset($unboundcfg['enablessl'])) {
259
		$tlscert_path = "{$g['unbound_chroot_path']}/sslcert.crt";
260
		$tlskey_path = "{$g['unbound_chroot_path']}/sslcert.key";
261

    
262
		// Enable SSL/TLS on the chosen or default port
263
		$tlsconfig .= "tls-port: {$tlsport}\n";
264

    
265
		// Lookup CA and Server Cert
266
		$cert = lookup_cert($unboundcfg['sslcertref']);
267
		$ca = ca_chain($cert);
268
		$cert_chain = base64_decode($cert['crt']);
269
		if (!empty($ca)) {
270
			$cert_chain .= "\n" . $ca;
271
		}
272

    
273
		// Write CA and Server Cert
274
		file_put_contents($tlscert_path, $cert_chain);
275
		chmod($tlscert_path, 0644);
276
		file_put_contents($tlskey_path, base64_decode($cert['prv']));
277
		chmod($tlskey_path, 0600);
278

    
279
		// Add config for CA and Server Cert
280
		$tlsconfig .= "tls-service-pem: \"{$tlscert_path}\"\n";
281
		$tlsconfig .= "tls-service-key: \"{$tlskey_path}\"\n";
282
	}
283

    
284
	// Determine interfaces to run on
285
	$outgoingints = "";
286
	if (!empty($unboundcfg['outgoing_interface'])) {
287
		$outgoingints = "# Outgoing interfaces to be used\n";
288
		$outgoing_interfaces = explode(",", $unboundcfg['outgoing_interface']);
289
		foreach ($outgoing_interfaces as $outif) {
290
			$outip = get_interface_ip($outif);
291
			if (is_ipaddr($outip)) {
292
				$outgoingints .= "outgoing-interface: $outip\n";
293
			}
294
			$outip = get_interface_ipv6($outif);
295
			if (is_ipaddrv6($outip)) {
296
				$outgoingints .= "outgoing-interface: $outip\n";
297
			}
298
		}
299
	}
300

    
301
	// Allow DNS Rebind for forwarded domains
302
	if (isset($unboundcfg['domainoverrides']) && is_array($unboundcfg['domainoverrides'])) {
303
		if (!isset($config['system']['webgui']['nodnsrebindcheck'])) {
304
			$private_domains = "# Set private domains in case authoritative name server returns a Private IP address\n";
305
			$private_domains .= unbound_add_domain_overrides("private");
306
		}
307
		$reverse_zones .= unbound_add_domain_overrides("reverse");
308
	}
309

    
310
	// Configure Unbound statistics
311
	$statistics = unbound_statistics();
312

    
313
	// Add custom Unbound options
314
	if ($unboundcfg['custom_options']) {
315
		$custom_options_source = explode("\n", base64_decode($unboundcfg['custom_options']));
316
		$custom_options = "# Unbound custom options\n";
317
		foreach ($custom_options_source as $ent) {
318
			$custom_options .= $ent."\n";
319
		}
320
	}
321

    
322
	// Server configuration variables
323
	$port = (is_port($unboundcfg['port'])) ? $unboundcfg['port'] : "53";
324
	$hide_identity = isset($unboundcfg['hideidentity']) ? "yes" : "no";
325
	$hide_version = isset($unboundcfg['hideversion']) ? "yes" : "no";
326
	$ipv6_allow = isset($config['system']['ipv6allow']) ? "yes" : "no";
327
	$harden_dnssec_stripped = isset($unboundcfg['dnssecstripped']) ? "yes" : "no";
328
	$prefetch = isset($unboundcfg['prefetch']) ? "yes" : "no";
329
	$prefetch_key = isset($unboundcfg['prefetchkey']) ? "yes" : "no";
330
	$dns_record_cache = isset($unboundcfg['dnsrecordcache']) ? "yes" : "no";
331
	$aggressivensec = isset($unboundcfg['aggressivensec']) ? "yes" : "no";
332
	$outgoing_num_tcp = isset($unboundcfg['outgoing_num_tcp']) ? $unboundcfg['outgoing_num_tcp'] : "10";
333
	$incoming_num_tcp = isset($unboundcfg['incoming_num_tcp']) ? $unboundcfg['incoming_num_tcp'] : "10";
334
	if (empty($unboundcfg['edns_buffer_size']) || ($unboundcfg['edns_buffer_size'] == 'auto')) {
335
		$edns_buffer_size = unbound_auto_ednsbufsize();
336
	} else {
337
		$edns_buffer_size = $unboundcfg['edns_buffer_size'];
338
	}
339
	$num_queries_per_thread = (!empty($unboundcfg['num_queries_per_thread'])) ? $unboundcfg['num_queries_per_thread'] : "4096";
340
	$jostle_timeout = (!empty($unboundcfg['jostle_timeout'])) ? $unboundcfg['jostle_timeout'] : "200";
341
	$cache_max_ttl = (!empty($unboundcfg['cache_max_ttl'])) ? $unboundcfg['cache_max_ttl'] : "86400";
342
	$cache_min_ttl = (!empty($unboundcfg['cache_min_ttl'])) ? $unboundcfg['cache_min_ttl'] : "0";
343
	$infra_host_ttl = (!empty($unboundcfg['infra_host_ttl'])) ? $unboundcfg['infra_host_ttl'] : "900";
344
	$infra_cache_numhosts = (!empty($unboundcfg['infra_cache_numhosts'])) ? $unboundcfg['infra_cache_numhosts'] : "10000";
345
	$unwanted_reply_threshold = (!empty($unboundcfg['unwanted_reply_threshold'])) ? $unboundcfg['unwanted_reply_threshold'] : "0";
346
	if ($unwanted_reply_threshold == "disabled") {
347
		$unwanted_reply_threshold = "0";
348
	}
349
	$msg_cache_size = (!empty($unboundcfg['msgcachesize'])) ? $unboundcfg['msgcachesize'] : "4";
350
	$verbosity = isset($unboundcfg['log_verbosity']) ? $unboundcfg['log_verbosity'] : 1;
351
	$use_caps = isset($unboundcfg['use_caps']) ? "yes" : "no";
352

    
353
	if (isset($unboundcfg['regovpnclients'])) {
354
		$openvpn_clients_conf .=<<<EOD
355
# OpenVPN client entries
356
include: {$g['unbound_chroot_path']}{$cfgsubdir}/openvpn.*.conf
357
EOD;
358
	} else {
359
		$openvpn_clients_conf = '';
360
	}
361

    
362
	// Set up forwarding if it is configured
363
	if (isset($unboundcfg['forwarding'])) {
364
		$dnsservers = array();
365
		if (isset($config['system']['dnsallowoverride'])) {
366
			$ns = array_unique(get_nameservers());
367
			foreach ($ns as $nameserver) {
368
				if ($nameserver) {
369
					$dnsservers[] = $nameserver;
370
				}
371
			}
372
		} else {
373
			$ns = array();
374
		}
375
		$sys_dnsservers = array_unique(get_dns_servers());
376
		foreach ($sys_dnsservers as $sys_dnsserver) {
377
			if ($sys_dnsserver && (!in_array($sys_dnsserver, $ns))) {
378
				$dnsservers[] = $sys_dnsserver;
379
			}
380
		}
381

    
382
		if (!empty($dnsservers)) {
383
			$forward_conf .=<<<EOD
384
# Forwarding
385
forward-zone:
386
	name: "."
387

    
388
EOD;
389
			if (isset($unboundcfg['forward_tls_upstream'])) {
390
				$forward_conf .= "\tforward-tls-upstream: yes\n";
391
			}
392

    
393
			/* Build DNS server hostname list. See https://redmine.pfsense.org/issues/8602 */
394
			$dns_hostnames = array();
395
			$dnshost_counter = 1;
396
			while (isset($config["system"]["dns{$dnshost_counter}host"])) {
397
				$pconfig_dnshost_counter = $dnshost_counter - 1;
398
				if (!empty($config["system"]["dns{$dnshost_counter}host"]) &&
399
				    isset($config["system"]["dnsserver"][$pconfig_dnshost_counter]))
400
				$dns_hostnames[$config["system"]["dnsserver"][$pconfig_dnshost_counter]] = $config["system"]["dns{$dnshost_counter}host"];
401
				$dnshost_counter++;
402
			}
403

    
404
			foreach ($dnsservers as $dnsserver) {
405
				$fwdport = "";
406
				$fwdhost = "";
407
				if (is_ipaddr($dnsserver) && !ip_in_subnet($dnsserver, "127.0.0.0/8")) {
408
					if (isset($unboundcfg['forward_tls_upstream'])) {
409
						$fwdport = "@853";
410
						if (array_key_exists($dnsserver, $dns_hostnames)) {
411
							$fwdhost = "#{$dns_hostnames[$dnsserver]}";
412
						}
413
					}
414
					$forward_conf .= "\tforward-addr: {$dnsserver}{$fwdport}{$fwdhost}\n";
415
				}
416
			}
417
		}
418
	} else {
419
		$forward_conf = "";
420
	}
421

    
422
	// Size of the RRset cache == 2 * msg-cache-size per Unbound's recommendations
423
	$rrset_cache_size = $msg_cache_size * 2;
424

    
425
	/* QNAME Minimization. https://redmine.pfsense.org/issues/8028
426
	 * Unbound uses the British style in the option name so the internal option
427
	 * name follows that, but the user-visible descriptions follow US English.
428
	 */
429
	$qname_min = "";
430
	if (isset($unboundcfg['qname-minimisation'])) {
431
		$qname_min = "qname-minimisation: yes\n";
432
		if (isset($unboundcfg['qname-minimisation-strict'])) {
433
			$qname_min .= "qname-minimisation-strict: yes\n";
434
		}
435
	}
436

    
437
	$python_module = '';
438
	if (isset($unboundcfg['python']) && !empty($unboundcfg['python_script'])) {
439
		$python_path = '';
440
		if (!empty($cfgsubdir)) {
441
			$python_path = "{$g['unbound_chroot_path']}{$cfgsubdir}/";
442
		}
443
		$python_module = "\n# Python Module\npython:\npython-script: {$python_path}{$unboundcfg['python_script']}.py";
444
	}
445

    
446
	$unboundconf = <<<EOD
447
##########################
448
# Unbound Configuration
449
##########################
450

    
451
##
452
# Server configuration
453
##
454
server:
455
{$reverse_zones}
456
chroot: {$g['unbound_chroot_path']}
457
username: "unbound"
458
directory: "{$g['unbound_chroot_path']}"
459
pidfile: "/var/run/unbound.pid"
460
use-syslog: yes
461
port: {$port}
462
verbosity: {$verbosity}
463
hide-identity: {$hide_identity}
464
hide-version: {$hide_version}
465
harden-glue: yes
466
do-ip4: yes
467
do-ip6: {$ipv6_allow}
468
do-udp: yes
469
do-tcp: yes
470
do-daemonize: yes
471
module-config: "{$module_config}"
472
unwanted-reply-threshold: {$unwanted_reply_threshold}
473
num-queries-per-thread: {$num_queries_per_thread}
474
jostle-timeout: {$jostle_timeout}
475
infra-host-ttl: {$infra_host_ttl}
476
infra-cache-numhosts: {$infra_cache_numhosts}
477
outgoing-num-tcp: {$outgoing_num_tcp}
478
incoming-num-tcp: {$incoming_num_tcp}
479
edns-buffer-size: {$edns_buffer_size}
480
cache-max-ttl: {$cache_max_ttl}
481
cache-min-ttl: {$cache_min_ttl}
482
harden-dnssec-stripped: {$harden_dnssec_stripped}
483
msg-cache-size: {$msg_cache_size}m
484
rrset-cache-size: {$rrset_cache_size}m
485
{$qname_min}
486
{$optimization['number_threads']}
487
{$optimization['msg_cache_slabs']}
488
{$optimization['rrset_cache_slabs']}
489
{$optimization['infra_cache_slabs']}
490
{$optimization['key_cache_slabs']}
491
outgoing-range: 4096
492
{$optimization['so_rcvbuf']}
493
{$anchor_file}
494
prefetch: {$prefetch}
495
prefetch-key: {$prefetch_key}
496
use-caps-for-id: {$use_caps}
497
serve-expired: {$dns_record_cache}
498
aggressive-nsec: {$aggressivensec}
499
# Statistics
500
{$statistics}
501
# TLS Configuration
502
{$tlsconfig}
503
# Interface IP(s) to bind to
504
{$bindintcfg}
505
{$outgoingints}
506
# DNS Rebinding
507
{$private_addr}
508
{$private_domains}
509
{$dns64_conf}
510

    
511
# Access lists
512
include: {$g['unbound_chroot_path']}{$cfgsubdir}/access_lists.conf
513

    
514
# Static host entries
515
include: {$g['unbound_chroot_path']}{$cfgsubdir}/host_entries.conf
516

    
517
# dhcp lease entries
518
include: {$g['unbound_chroot_path']}{$cfgsubdir}/dhcpleases_entries.conf
519

    
520
{$openvpn_clients_conf}
521

    
522
# Domain overrides
523
include: {$g['unbound_chroot_path']}{$cfgsubdir}/domainoverrides.conf
524
{$forward_conf}
525

    
526
{$custom_options}
527

    
528
###
529
# Remote Control Config
530
###
531
include: {$g['unbound_chroot_path']}{$cfgsubdir}/remotecontrol.conf
532
{$python_module}
533

    
534
EOD;
535

    
536
	return $unboundconf;
537
}
538

    
539
function unbound_remote_control_setup($cfgsubdir = "") {
540
	global $g;
541

    
542
	if (!file_exists("{$g['unbound_chroot_path']}{$cfgsubdir}/remotecontrol.conf") ||
543
	    (filesize("{$g['unbound_chroot_path']}{$cfgsubdir}/remotecontrol.conf") == 0) ||
544
	    !file_exists("{$g['unbound_chroot_path']}{$cfgsubdir}/unbound_control.key")) {
545
		$remotcfg = <<<EOF
546
remote-control:
547
	control-enable: yes
548
	control-interface: 127.0.0.1
549
	control-port: 953
550
	server-key-file: "{$g['unbound_chroot_path']}{$cfgsubdir}/unbound_server.key"
551
	server-cert-file: "{$g['unbound_chroot_path']}{$cfgsubdir}/unbound_server.pem"
552
	control-key-file: "{$g['unbound_chroot_path']}{$cfgsubdir}/unbound_control.key"
553
	control-cert-file: "{$g['unbound_chroot_path']}{$cfgsubdir}/unbound_control.pem"
554

    
555
EOF;
556

    
557
		create_unbound_chroot_path($cfgsubdir);
558
		file_put_contents("{$g['unbound_chroot_path']}{$cfgsubdir}/remotecontrol.conf", $remotcfg);
559

    
560
		// Generate our keys
561
		do_as_unbound_user("unbound-control-setup", $cfgsubdir);
562

    
563
	}
564
}
565

    
566
function sync_unbound_service() {
567
	global $config, $g;
568

    
569
	create_unbound_chroot_path();
570

    
571
	// Configure our Unbound service
572
	do_as_unbound_user("unbound-anchor");
573
	unbound_remote_control_setup();
574
	unbound_generate_config();
575
	do_as_unbound_user("start");
576
	require_once("service-utils.inc");
577
	if (is_service_running("unbound")) {
578
		do_as_unbound_user("restore_cache");
579
	}
580

    
581
}
582

    
583
function unbound_acl_id_used($id) {
584
	global $config;
585

    
586
	if (is_array($config['unbound']['acls'])) {
587
		foreach ($config['unbound']['acls'] as & $acls) {
588
			if ($id == $acls['aclid']) {
589
				return true;
590
			}
591
		}
592
	}
593

    
594
	return false;
595
}
596

    
597
function unbound_get_next_id() {
598
	$aclid = 0;
599
	while (unbound_acl_id_used($aclid)) {
600
		$aclid++;
601
	}
602
	return $aclid;
603
}
604

    
605
// Execute commands as the user unbound
606
function do_as_unbound_user($cmd, $param1 = "") {
607
	global $g;
608

    
609
	switch ($cmd) {
610
		case "start":
611
			mwexec("/usr/local/sbin/unbound -c {$g['unbound_chroot_path']}/unbound.conf");
612
			break;
613
		case "stop":
614
			mwexec("echo '/usr/local/sbin/unbound-control -c {$g['unbound_chroot_path']}/unbound.conf stop' | /usr/bin/su -m unbound", true);
615
			break;
616
		case "reload":
617
			mwexec("echo '/usr/local/sbin/unbound-control -c {$g['unbound_chroot_path']}/unbound.conf reload' | /usr/bin/su -m unbound", true);
618
			break;
619
		case "unbound-anchor":
620
			$root_key_file = "{$g['unbound_chroot_path']}{$param1}/root.key";
621
			// sanity check root.key because unbound-anchor will fail without manual removal otherwise. redmine #5334
622
			if (file_exists($root_key_file)) {
623
				$rootkeycheck = mwexec("/usr/bin/grep 'autotrust trust anchor file' {$root_key_file}", true);
624
				if ($rootkeycheck != "0") {
625
					log_error("Unbound {$root_key_file} file is corrupt, removing and recreating.");
626
					unlink_if_exists($root_key_file);
627
				}
628
			}
629
			mwexec("echo '/usr/local/sbin/unbound-anchor -a {$root_key_file}' | /usr/bin/su -m unbound", true);
630
			// Only sync the file if this is the real (default) one, not a test one.
631
			if ($param1 == "") {
632
				//pfSense_fsync($root_key_file);
633
			}
634
			break;
635
		case "unbound-control-setup":
636
			mwexec("echo '/usr/local/sbin/unbound-control-setup -d {$g['unbound_chroot_path']}{$param1}' | /usr/bin/su -m unbound", true);
637
			break;
638
		default:
639
			break;
640
	}
641
}
642

    
643
function unbound_add_domain_overrides($pvt_rev="", $cfgsubdir = "") {
644
	global $config, $g;
645

    
646
	$domains = $config['unbound']['domainoverrides'];
647

    
648
	$sorted_domains = msort($domains, "domain");
649
	$result = array();
650
	$tls_domains = array();
651
	$tls_hostnames = array();
652
	foreach ($sorted_domains as $domain) {
653
		$domain_key = current($domain);
654
		if (!isset($result[$domain_key])) {
655
			$result[$domain_key] = array();
656
		}
657
		$result[$domain_key][] = $domain['ip'];
658
		/* If any entry for a domain has TLS set, it will be active for all entries. */
659
		if (isset($domain['forward_tls_upstream'])) {
660
			$tls_domains[] = $domain_key;
661
			$tls_hostnames[$domain['ip']] = $domain['tls_hostname'];
662
		}
663
	}
664

    
665
	// Domain overrides that have multiple entries need multiple stub-addr: added
666
	$domain_entries = "";
667
	foreach ($result as $domain=>$ips) {
668
		if ($pvt_rev == "private") {
669
			$domain_entries .= "private-domain: \"$domain\"\n";
670
			$domain_entries .= "domain-insecure: \"$domain\"\n";
671
		} else if ($pvt_rev == "reverse") {
672
			if ((substr($domain, -14) == ".in-addr.arpa.") || (substr($domain, -13) == ".in-addr.arpa")) {
673
				$domain_entries .= "local-zone: \"$domain\" typetransparent\n";
674
			}
675
		} else {
676
			$use_tls = in_array($domain, $tls_domains);
677
			$domain_entries .= "forward-zone:\n";
678
			$domain_entries .= "\tname: \"$domain\"\n";
679
			$fwdport = "";
680
			/* Enable TLS forwarding for this domain if needed. */
681
			if ($use_tls) {
682
				$domain_entries .= "\tforward-tls-upstream: yes\n";
683
				$fwdport = "@853";
684
			}
685
			foreach ($ips as $ip) {
686
				$fwdhost = "";
687
				/* If an IP address already contains a port specification, do not add another. */
688
				if (strstr($ip, '@') !== false) {
689
					$fwdport = "";
690
				}
691
				if ($use_tls && array_key_exists($ip, $tls_hostnames)) {
692
					$fwdhost = "#{$tls_hostnames[$ip]}";
693
				}
694
				$domain_entries .= "\tforward-addr: {$ip}{$fwdport}{$fwdhost}\n";
695
			}
696
		}
697
	}
698

    
699
	if ($pvt_rev != "") {
700
		return $domain_entries;
701
	} else {
702
		create_unbound_chroot_path($cfgsubdir);
703
		file_put_contents("{$g['unbound_chroot_path']}{$cfgsubdir}/domainoverrides.conf", $domain_entries);
704
	}
705
}
706

    
707
function unbound_generate_zone_data($domain, $hosts, &$added_ptr, $zone_type = "transparent", $write_domain_zone_declaration = false, $always_add_short_names = false) {
708
	global $config;
709
	if ($write_domain_zone_declaration) {
710
		$zone_data = "local-zone: \"{$domain}.\" {$zone_type}\n";
711
	} else {
712
		$zone_data = "";
713
	}
714
	foreach ($hosts as $host) {
715
		if (is_ipaddrv4($host['ipaddr'])) {
716
			$type = 'A';
717
		} else if (is_ipaddrv6($host['ipaddr'])) {
718
			$type = 'AAAA';
719
		} else {
720
			continue;
721
		}
722
		if (!$added_ptr[$host['ipaddr']]) {
723
			$zone_data .= "local-data-ptr: \"{$host['ipaddr']} {$host['fqdn']}\"\n";
724
			$added_ptr[$host['ipaddr']] = true;
725
		}
726
		/* For the system localhost entry, write an entry for just the hostname. */
727
		if ((($host['name'] == "localhost") && ($domain == $config['system']['domain'])) || $always_add_short_names) {
728
			$zone_data .= "local-data: \"{$host['name']}. {$type} {$host['ipaddr']}\"\n";
729
		}
730
		/* Redirect zones must have a zone declaration that matches the
731
		 * local-data record exactly, it cannot have entries "under" the
732
		 * domain.
733
		 */
734
		if ($zone_type == "redirect") {
735
			$zone_data .= "local-zone: \"{$host['fqdn']}.\" {$zone_type}\n";;
736
		}
737
		$zone_data .= "local-data: \"{$host['fqdn']}. {$type} {$host['ipaddr']}\"\n";
738
	}
739
	return $zone_data;
740
}
741

    
742
function unbound_add_host_entries($cfgsubdir = "") {
743
	global $config, $g;
744

    
745
	$hosts = system_hosts_entries($config['unbound']);
746

    
747
	/* Pass 1: Build domain list and hosts inside domains */
748
	$hosts_by_domain = array();
749
	foreach ($hosts as $host) {
750
		if (!array_key_exists($host['domain'], $hosts_by_domain)) {
751
			$hosts_by_domain[$host['domain']] = array();
752
		}
753
		$hosts_by_domain[$host['domain']][] = $host;
754
	}
755

    
756
	$added_ptr = array();
757
	/* Build local zone data */
758
	// Check if auto add host entries is not set
759
	$system_domain_local_zone_type = "transparent";
760
	if (!isset($config['unbound']['disable_auto_added_host_entries'])) {
761
		// Make sure the config setting is a valid unbound local zone type.  If not use "transparent".
762
		if (array_key_exists($config['unbound']['system_domain_local_zone_type'], unbound_local_zone_types())) {
763
			$system_domain_local_zone_type = $config['unbound']['system_domain_local_zone_type'];
764
		}
765
	}
766
	/* Add entries for the system domain before all others */
767
	if (array_key_exists($config['system']['domain'], $hosts_by_domain)) {
768
		$unbound_entries .= unbound_generate_zone_data($config['system']['domain'],
769
					$hosts_by_domain[$config['system']['domain']],
770
					$added_ptr,
771
					$system_domain_local_zone_type,
772
					true);
773
		/* Unset this so it isn't processed again by the loop below. */
774
		unset($hosts_by_domain[$config['system']['domain']]);
775
	}
776

    
777
	/* Build zone data for other domain */
778
	foreach ($hosts_by_domain as $domain => $hosts) {
779
		$unbound_entries .= unbound_generate_zone_data($domain,
780
					$hosts,
781
					$added_ptr,
782
					"transparent",
783
					false,
784
					isset($config['unbound']['always_add_short_names']));
785
	}
786

    
787
	// Write out entries
788
	create_unbound_chroot_path($cfgsubdir);
789
	file_put_contents("{$g['unbound_chroot_path']}{$cfgsubdir}/host_entries.conf", $unbound_entries);
790

    
791
	/* dhcpleases will write to this config file, make sure it exists */
792
	@touch("{$g['unbound_chroot_path']}{$cfgsubdir}/dhcpleases_entries.conf");
793
}
794

    
795
function unbound_control($action) {
796
	global $config, $g;
797

    
798
	$cache_dumpfile = "/var/tmp/unbound_cache";
799

    
800
	switch ($action) {
801
	case "start":
802
		// Start Unbound
803
		if ($config['unbound']['enable'] == "on") {
804
			if (!is_service_running("unbound")) {
805
				do_as_unbound_user("start");
806
			}
807
		}
808
		break;
809
	case "stop":
810
		if ($config['unbound']['enable'] == "on") {
811
			do_as_unbound_user("stop");
812
		}
813
		break;
814
	case "reload":
815
		if ($config['unbound']['enable'] == "on") {
816
			do_as_unbound_user("reload");
817
		}
818
		break;
819
	case "dump_cache":
820
		// Dump Unbound's Cache
821
		if ($config['unbound']['dumpcache'] == "on") {
822
			do_as_unbound_user("dump_cache");
823
		}
824
		break;
825
	case "restore_cache":
826
		// Restore Unbound's Cache
827
		if ((is_service_running("unbound")) && ($config['unbound']['dumpcache'] == "on")) {
828
			if (file_exists($cache_dumpfile) && filesize($cache_dumpfile) > 0) {
829
				do_as_unbound_user("load_cache < /var/tmp/unbound_cache");
830
			}
831
		}
832
		break;
833
	default:
834
		break;
835

    
836
	}
837
}
838

    
839
// Generation of Unbound statistics
840
function unbound_statistics() {
841
	global $config;
842

    
843
	if ($config['stats'] == "on") {
844
		$stats_interval = $config['unbound']['stats_interval'];
845
		$cumulative_stats = $config['cumulative_stats'];
846
		if ($config['extended_stats'] == "on") {
847
			$extended_stats = "yes";
848
		} else {
849
			$extended_stats = "no";
850
		}
851
	} else {
852
		$stats_interval = "0";
853
		$cumulative_stats = "no";
854
		$extended_stats = "no";
855
	}
856
	/* XXX To do - add RRD graphs */
857
	$stats = <<<EOF
858
# Unbound Statistics
859
statistics-interval: {$stats_interval}
860
extended-statistics: yes
861
statistics-cumulative: yes
862

    
863
EOF;
864

    
865
	return $stats;
866
}
867

    
868
// Unbound Access lists
869
function unbound_acls_config($cfgsubdir = "") {
870
	global $g, $config;
871

    
872
	if (!isset($config['unbound']['disable_auto_added_access_control'])) {
873
		$aclcfg = "access-control: 127.0.0.1/32 allow_snoop\n";
874
		$aclcfg .= "access-control: ::1 allow_snoop\n";
875
		// Add our networks for active interfaces including localhost
876
		if (!empty($config['unbound']['active_interface'])) {
877
			$active_interfaces = array_flip(explode(",", $config['unbound']['active_interface']));
878
			if (in_array("all", $active_interfaces)) {
879
				$active_interfaces = get_configured_interface_with_descr();
880
			}
881
		} else {
882
			$active_interfaces = get_configured_interface_with_descr();
883
		}
884

    
885
		foreach ($active_interfaces as $ubif => $ifdesc) {
886
			$ifip = get_interface_ip($ubif);
887
			if (is_ipaddrv4($ifip)) {
888
				// IPv4 is handled via NAT networks below
889
			}
890
			$ifip = get_interface_ipv6($ubif);
891
			if (is_ipaddrv6($ifip)) {
892
				if (!is_linklocal($ifip)) {
893
					$subnet_bits = get_interface_subnetv6($ubif);
894
					$subnet_ip = gen_subnetv6($ifip, $subnet_bits);
895
					// only add LAN-type interfaces
896
					if (!interface_has_gateway($ubif)) {
897
						$aclcfg .= "access-control: {$subnet_ip}/{$subnet_bits} allow\n";
898
					}
899
				}
900
				// add for IPv6 static routes to local networks
901
				// for safety, we include only routes reachable on an interface with no
902
				// gateway specified - read: not an Internet connection.
903
				$static_routes = get_staticroutes(false, false, true); // Parameter 3 returnenabledroutesonly
904
				foreach ($static_routes as $route) {
905
					if ((lookup_gateway_interface_by_name($route['gateway']) == $ubif) && !interface_has_gateway($ubif)) {
906
						// route is on this interface, interface doesn't have gateway, add it
907
						$aclcfg .= "access-control: {$route['network']} allow\n";
908
					}
909
				}
910
			}
911
		}
912

    
913
		// OpenVPN IPv6 Tunnel Networks
914
		foreach (array('openvpn-client', 'openvpn-server') as $ovpnentry) {
915
			if (is_array($config['openvpn'][$ovpnentry])) {
916
				foreach ($config['openvpn'][$ovpnentry] as $ovpnent) {
917
					if (!isset($ovpnent['disable']) && !empty($ovpnent['tunnel_networkv6'])) {
918
						$aclcfg .= "access-control: {$ovpnent['tunnel_networkv6']} allow\n";
919
					}
920
				}
921
			}
922
		}
923
		// IPsec Mobile Virtual IPv6 Address Pool
924
		if ((isset($config['ipsec']['client']['enable'])) &&
925
		    (!empty($config['ipsec']['client']['pool_address_v6'])) &&
926
		    (!empty($config['ipsec']['client']['pool_netbits_v6']))) {
927
			$aclcfg .= "access-control: {$config['ipsec']['client']['pool_address_v6']}/{$config['ipsec']['client']['pool_netbits_v6']} allow\n";
928
		}
929

    
930
		// Generate IPv4 access-control entries using the same logic as automatic outbound NAT
931
		if (empty($FilterIflist)) {
932
			filter_generate_optcfg_array();
933
		}
934
		$natnetworks_array = array();
935
		$natnetworks_array = filter_nat_rules_automatic_tonathosts();
936
		foreach ($natnetworks_array as $allowednet) {
937
			$aclcfg .= "access-control: $allowednet allow \n";
938
		}
939
	}
940

    
941
	// Configure the custom ACLs
942
	if (is_array($config['unbound']['acls'])) {
943
		foreach ($config['unbound']['acls'] as $unbound_acl) {
944
			$aclcfg .= "#{$unbound_acl['aclname']}\n";
945
			foreach ($unbound_acl['row'] as $network) {
946
				if ($unbound_acl['aclaction'] == "allow snoop") {
947
					$unbound_acl['aclaction'] = "allow_snoop";
948
				} elseif ($unbound_acl['aclaction'] == "deny nonlocal") {
949
					$unbound_acl['aclaction'] = "deny_non_local";
950
				} elseif ($unbound_acl['aclaction'] == "refuse nonlocal") {
951
					$unbound_acl['aclaction'] = "refuse_non_local";
952
				}
953
				$aclcfg .= "access-control: {$network['acl_network']}/{$network['mask']} {$unbound_acl['aclaction']}\n";
954
			}
955
		}
956
	}
957
	// Write out Access list
958
	create_unbound_chroot_path($cfgsubdir);
959
	file_put_contents("{$g['unbound_chroot_path']}{$cfgsubdir}/access_lists.conf", $aclcfg);
960

    
961
}
962

    
963
// Generate hosts and reload services
964
function unbound_hosts_generate() {
965
	// Generate our hosts file
966
	unbound_add_host_entries();
967

    
968
	// Reload our service to read the updates
969
	unbound_control("reload");
970
}
971

    
972
// Array of valid unbound local zone types
973
function unbound_local_zone_types() {
974
	return array(
975
		"deny" => gettext("Deny"),
976
		"refuse" => gettext("Refuse"),
977
		"static" => gettext("Static"),
978
		"transparent" => gettext("Transparent"),
979
		"typetransparent" => gettext("Type Transparent"),
980
		"redirect" => gettext("Redirect"),
981
		"inform" => gettext("Inform"),
982
		"inform_deny" => gettext("Inform Deny"),
983
		"nodefault" => gettext("No Default")
984
	);
985
}
986

    
987
// Autoconfig EDNS buffer size
988
function unbound_auto_ednsbufsize() {
989
	global $config;
990

    
991
	$active_ipv6_inf = false;
992
	if ($config['unbound']['active_interface'] != 'all') {
993
		$active_interfaces = explode(",", $config['unbound']['active_interface']);
994
	} else {
995
		$active_interfaces = get_configured_interface_list();
996
	}
997

    
998
	$min_mtu = get_interface_mtu(get_real_interface($active_interfaces[0]));
999
	foreach ($active_interfaces as $ubif) {
1000
		$ubif_mtu = get_interface_mtu(get_real_interface($ubif));
1001
		if (get_interface_ipv6($ubif)) {
1002
			$active_ipv6_inf = true;
1003
		}
1004
		if ($ubif_mtu < $min_mtu) {
1005
			$min_mtu = $ubif_mtu;
1006
		}
1007
	}
1008

    
1009
	// maximum IPv4 + UDP header = 68 bytes
1010
	$min_mtu = $min_mtu - 68;
1011

    
1012
	if (($min_mtu < 1232) && $active_ipv6_inf) {
1013
		$min_mtu = 1232;
1014
	} elseif ($min_mtu < 512) {
1015
		$min_mtu = 512;
1016
	}	
1017

    
1018
	return $min_mtu;
1019
}
1020

    
1021
?>
(51-51/60)