Project

General

Profile

Download (15.8 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/* $Id$ */
3
/*
4
	unbound.inc
5
	part of the pfSense project (http://www.pfsense.com)
6
	Copyright (C) 2011	Warren Baker
7
	All rights reserved.
8

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

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

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

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

    
31
/*
32
	pfSense_BUILDER_BINARIES:	/usr/local/sbin/unbound /usr/local/sbin/unbound-anchor
33
	pfSense_BUILDER_BINARIES:	/usr/local/sbin/unbound-checkconf /usr/local/sbin/unbound-control
34
	pfSense_BUILDER_BINARIES:	/usr/local/sbin/unbound-control-setup /usr/local/sbin/unbound-host 
35
*/
36

    
37

    
38
/* Handle Domain overrides and DNS Rebinding domains */
39
function unbound_add_domain_overrides($pvt=false) {
40
	global $config, $g;
41

    
42
	$domains = $config['unbound']['domainoverrides'];
43

    
44
	$sorted_domains = msort($domains, "domain");
45
	$result = array();		
46
	foreach($sorted_domains as $domain) {
47
		$domain_key = current($domain);
48
		if(!isset($result[$domain_key])) {
49
			$result[$domain_key] = array();
50
		}
51
		$result[$domain_key][] = $domain['ip'];
52
	}
53

    
54
	// Domain overrides that have multiple entries need multiple stub-addr: added
55
	$domain_entries = "";
56
	foreach($result as $domain=>$ips) {
57
		if($pvt == true) {
58
			$domain_entries .= "private-domain: \"$domain\"\n";
59
			$domain_entries .= "domain-insecure: \"$domain\"\n";
60
		} else {
61
			$domain_entries .= "stub-zone:\n";
62
			$domain_entries .= "\tname: \"$domain\"\n";
63
			foreach($ips as $ip) {
64
				$domain_entries .= "\tstub-addr: $ip\n";
65
			}
66
			$domain_entries .= "\tstub-prime: no\n";
67
		}
68
	}
69
	if($pvt == true)
70
		return $domain_entries;
71
	else
72
		file_put_contents("{$g['unbound_chroot_path']}/etc/domainoverrides.conf", $domain_entries);
73
}
74

    
75

    
76
/* Optimize Unbound for environment */
77
function unbound_optimization() {
78
	global $config;
79

    
80
	$optimization_settings = array();
81
	
82
	/* Set the number of threads equal to number of CPUs.
83
	 * Use 1 to disable threading, if for some reason this sysctl fails.
84
	 */
85
	$numprocs = intval(trim(`/sbin/sysctl kern.smp.cpus | /usr/bin/cut -d" " -f2`));
86
	if($numprocs > 0)
87
		$optimization['number_threads'] = "num-threads: {$numprocs}";
88
	else
89
		$optimization['number_threads'] = "num-threads: 1";
90
	
91
	/* Slabs to help reduce lock contention. */
92
	if ($numprocs > 4) {
93
		$optimization['msg_cache_slabs'] = "msg-cache-slabs: {$numprocs}";
94
		$optimization['rrset_cache_slabs'] = "rrset-cache-slabs: {$numprocs}";
95
		$optimization['infra_cache_slabs'] = "infra-cache-slabs: {$numprocs}";
96
		$optimization['key_cache_slabs'] = "key-cache-slabs: {$numprocs}";
97
	} else {
98
		$optimization['msg_cache_slabs'] = "msg-cache-slabs: 4";
99
		$optimization['rrset_cache_slabs'] = "rrset-cache-slabs: 4";
100
		$optimization['infra_cache_slabs'] = "infra-cache-slabs: 4";
101
		$optimization['key_cache_slabs'] = "key-cache-slabs: 4";
102
	}
103
	
104
	/* Memory usage default of 4MB */
105
	$optimization['msg_cache_size'] = "msg-cache-size: 4m";
106
	$optimization['rrset_cache_size'] = "rrset-cache-size: 8m";
107

    
108
	/* More outgoing connections per thread otherwise assign a default of 4096 for a single thread */
109
	if($numprocs > 0) {
110
		$or = (1024/$numprocs) - 50;
111
		$optimization['outgoing_range'] = "outgoing-range: {$or}";
112
	} else {
113
		$optimization['outgoing_range'] = "outgoing-range: {4096}";
114
	}
115

    
116
	/* Larger socket buffer for busy servers
117
	 * Check that it is set to 4MB (by default the OS has it configured to 4MB)
118
	 */
119
	foreach ($config['sysctl']['item'] as $tunable) {
120
		if ($tunable['tunable'] == 'kern.ipc.maxsockbuf') {
121
			$so = floor(($tunable['value']/1024/1024)-1);
122
			// Check to ensure that the number is not a negative
123
			if ($so > 0)
124
				$optimization['so_rcvbuf'] = "so-rcvbuf: {$so}m";
125
			else
126
				unset($optimization['so_rcvbuf']);
127
		}
128
	}
129
	/* Safety check in case kern.ipc.maxsockbuf is not available. */
130
	if(!isset($optimization['so_rcvbuf']))
131
		$optimization['so_rcvbuf'] = "#so-rcvbuf: 4m";
132

    
133
	return $optimization;
134
}
135

    
136
/* Fetch root name servers hints file */
137
function unbound_fetch_root_hints_using_dig() {
138
	global $g;
139

    
140
	$hints = "{$g['unbound_chroot_path']}/etc/root.hints";
141
	if (@filesize($hints) == 0) {
142
		$returnvar = mwexec("/usr/bin/dig +tcp +nocmd +answer +time=1 +tries=1 +retry=1 @`/usr/bin/dig +nocmd +noall +answer +short +time=1 +tries=1 +retry=1 . NS | /usr/bin/head -1` . NS > {$hints}");
143

    
144
		if ($returnvar != 0) {
145
			mwexec("/bin/rm -f {$hints}");
146
			return false;
147
		} else
148
			return true;
149
	} else
150
		return true;
151
}
152

    
153
function unbound_fetch_root_hints() {
154
	global $g;
155

    
156
	$destination_file = "{$g['unbound_chroot_path']}/etc/root.hints";
157
	if (@filesize($destination_file) == 0 ) {
158
		$fout = fopen($destination_file, "w");
159
		$url = "ftp://ftp.internic.net/domain/named.cache";
160

    
161
		$ch = curl_init();
162
		curl_setopt($ch, CURLOPT_URL, $url);
163
		curl_setopt($ch,CURLOPT_RETURNTRANSFER, 1);
164
		curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, '5');
165
		$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
166
		$data = curl_exec($ch);
167
		curl_close($ch);
168

    
169
		fwrite($fout, $data);
170
		fclose($fout);
171

    
172
		return ($http_code == 200) ? true : $http_code;
173
	} else
174
		return false;
175
}
176

    
177
/* Configure initial anchor to support DNSSEC */
178
function unbound_anchor_setup() {
179
	global $g;
180

    
181
	$conf = <<<EOD
182
. IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5
183
EOD;
184

    
185
	file_put_contents("{$g['unbound_chroot_path']}/etc/root-trust-anchor", $conf);
186
	@chown("{$g['unbound_chroot_path']}/etc/root-trust-anchor", "unbound");
187
	@chgrp("{$g['unbound_chroot_path']}/etc/root-trust-anchor", "wheel");
188
	@chmod("{$g['unbound_chroot_path']}/etc/root-trust-anchor", 0600);
189
	mwexec("/usr/local/sbin/unbound-anchor -a {$g['unbound_chroot_path']}/etc/root-trust-anchor", true);
190
}
191

    
192
/* Setup Unbound Remote Control SSL keys */
193
function unbound_keys_setup() {
194
	global $g;
195

    
196
	if (!file_exists("{$g['unbound_chroot_path']}/unbound_control.key")) {
197
		mwexec("/usr/local/sbin/unbound-control-setup -d {$g['unbound_chroot_path']}/etc");
198
		@chown("{$g['unbound_chroot_path']}/etc/unbound_*", "unbound");
199
		@chgrp("{$g['unbound_chroot_path']}/etc/unbound_*", "wheel");
200
	}
201
}
202

    
203
/* Generation of Unbound statistics */
204
function unbound_statistics() {
205
	global $config;
206

    
207
	/* XXX To do - add RRD graphs */
208
	$stats = <<<EOF
209
# Unbound Statistics
210
statistics-interval: {$config['unbound']['stats_interval']}
211
extended-statistics: yes
212
statistics-cumulative: yes
213

    
214
EOF;
215

    
216
	return $stats;
217
}
218

    
219
function unbound_resync_config() {
220
	global $config,$g;
221

    
222
	$unboundcfg = $config['unbound'];
223

    
224
	/* Setup optimization */
225
	$optimization = unbound_optimization();
226

    
227
	/* Setup DNSSEC support */
228
	if($unbound_config['dnssec_status'] == "on") {
229
		$module_config = "validator iterator";
230
		$anchor_file = "auto-trust-anchor-file: /etc/root-trust-anchor";
231
	} else
232
		$module_config = "iterator";
233

    
234
	/* Setup DNS Rebinding */
235
	if(!isset($config['system']['webgui']['nodnsrebindcheck'])) {
236
		// Private-addresses for DNS Rebinding
237
		$private_addr = <<<EOF
238
# For DNS Rebinding prevention
239
private-address: 10.0.0.0/8
240
private-address: 172.16.0.0/12
241
private-address: 192.168.0.0/16
242
private-address: 192.254.0.0/16
243
private-address: fd00::/8
244
private-address: fe80::/10
245
EOF;
246
	}
247

    
248
	/* Allow DNS Rebind for forwarded domains */
249
	if (isset($config['unbound']['domainoverrides']) && is_array($config['unbound']['domainoverrides'])) {
250
		if(!isset($config['system']['webgui']['nodnsrebindcheck'])) {
251
			$private_domains = "# Set private domains in case authoritative name server returns a Private IP address";
252
			$private_domains .= unbound_add_domain_overrides(true);
253
		}
254
	}
255

    
256
	/* Configure static Host entries */
257
	$host_entries = unbound_add_host_entries();
258

    
259
	/* Configure Domain Overrides */
260
	$domain_overrides = unbound_add_domain_overrides();
261

    
262
	/* Configure Unbound statistics */
263
	$statistics = unbound_statistics();
264

    
265
	/* Add custom Unbound options */
266
	if ($config['unbound']['custom_options']) {
267
		$custom_option = "# Unbound custom option";
268
		foreach (preg_split('/\s+/', $config['dns']['custom_options']) as $ent)
269
			$custom_option .= $ent."\n";
270
	}
271

    
272
	$unboundconf = <<<EOD
273
##########################
274
# Unbound Configuration
275
##########################
276

    
277
##
278
# Server configuration
279
##
280
server:
281
chroot: {$g['unbound_chroot_path']}
282
username: "unbound"
283
directory: "{$g['unbound_chroot_path']}/etc"
284
root-hints: "root.hints"
285
pidfile: "/var/run/unbound.pid"
286
use-syslog: yes
287
port: 53
288
verbosity: {$unboundcfg['loglevel']}
289
harden-referral-path: no
290
do-ip4: yes
291
do-ip6: yes
292
do-udp: yes
293
do-tcp: yes
294
do-daemonize: yes
295
module-config: "{$module_config}"
296
unwanted-reply-threshold: 0
297
num-queries-per-thread: 1024
298
jostle-timeout: 200
299
infra-host-ttl: 900
300
infra-lame-ttl: 900
301
infra-cache-numhosts: 10000
302
outgoing-num-tcp: 10
303
incoming-num-tcp: 10
304
edns-buffer-size: 4096
305
cache-max-ttl: {$unboundcfg['cache_max_ttl']}
306
cache-min-ttl: {$unboundcfg['cache_min_ttl']}
307
harden-dnssec-stripped: yes
308
{$optimization['number_threads']}
309
{$optimization['msg_cache_slabs']}
310
{$optimization['rrset_cache_slabs']}
311
{$optimization['infra_cache_slabs']}
312
{$optimization['key_cache_slabs']}
313
{$optimization['msg_cache_size']}
314
{$optimization['rrset_cache_size']}
315
{$optimization['outgoing_range']}
316
{$optimization['so_rcvbuf']}
317
{$anchor_file}
318
prefetch: {$unboundcfg['prefetch']}
319
prefetch-key: {$unboundcfg['prefetch_key']}
320
# Statistics
321
{$statistics}
322
# Interface IP(s) to bind to
323
interface: 0.0.0.0
324
interface: ::0
325

    
326
# DNS Rebinding
327
{$private_addr}
328
{$private_domains}
329

    
330
# Static host entries
331
include: {$g['unbound_chroot_path']}/etc/host_entries.conf
332

    
333
# Domain overrides
334
include: {$g['unbound_chroot_path']}/etc/domainoverrides.conf
335

    
336
{$custom_options}
337

    
338
###
339
# Remote Control Config
340
###
341
include: {$g['unbound_chroot_path']}/etc/remotecontrol.conf
342

    
343
EOD;
344

    
345
	file_put_contents("{$g['unbound_chroot_path']}/etc/unbound.conf", $unboundconf);
346

    
347
}
348

    
349
function unbound_remote_control_setup() {
350
	global $g;
351

    
352
	if(!file_exists("{$g['unbound_chroot_path']}/etc/remotecontrol.conf")) {
353
		$remotcfg = <<<EOF
354
remote-control:
355
control-enable: yes
356
control-interface: 127.0.0.1
357
control-port: 953
358
server-key-file: "{$g['unbound_chroot_path']}/etc/unbound_server.key"
359
server-cert-file: "{$g['unbound_chroot_path']}/etc/unbound_server.pem"
360
control-key-file: "{$g['unbound_chroot_path']}/etc/unbound_control.key"
361
control-cert-file: "{$g['unbound_chroot_path']}/etc/unbound_control.pem"
362
EOF;
363

    
364
		file_put_contents("{$g['unbound_chroot_path']}/etc/remotecontrol.conf", $remotcfg);
365
	}
366
}
367

    
368
function unbound_add_host_entries() {
369
	global $config, $g;
370

    
371
	/* XXX: break this out into a separate config file and make use of include */
372
	$syscfg = $config['system'];
373
	$dnscfg = $config['unbound'];
374

    
375
	$dns_entries = "local-zone: \"{$syscfg['domain']}\" transparent\n";
376
	// IPv4 entries
377
	$dns_entries .= "local-data-ptr: \"127.0.0.1 localhost\"\n";
378
	$dns_entries .= "local-data: \"localhost A 127.0.0.1\"\n";
379
	$dns_entries .= "local-data: \"localhost.{$syscfg['domain']} A 127.0.0.1\"\n";
380
	// IPv6 entries
381
	$dns_entries .= "local-data-ptr: \"::1 localhost\"\n";
382
	$dns_entries .= "local-data: \"localhost AAAA ::1\"\n";
383
	$dns_entries .= "local-data: \"localhost.{$syscfg['domain']} AAAA ::1\"\n";
384

    
385
	/*if ($config['interfaces']['lan']) {
386
		$cfgip = get_interface_ip("lan");
387
		if (is_ipaddr($cfgip)) {
388
			$unbound_entries .= "local-data-ptr: \"{$cfgip} {$syscfg['hostname']}.{$syscfg['domain']}\"\n";
389
			$unbound_entries .= "local-data: \"{$syscfg['hostname']}.{$syscfg['domain']} A {$cfgip}\"\n";
390
			$unbound_entries .= "local-data: \"{$syscfg['hostname']} A {$cfgip}\"\n";
391
		}
392
	} else {
393
		$sysiflist = get_configured_interface_list();
394
		foreach ($sysiflist as $sysif) {
395
			if (!interface_has_gateway($sysif)) {
396
				$cfgip = get_interface_ip($sysif);
397
				if (is_ipaddr($cfgip)) {
398
					$unbound_entries .= "local-data-ptr: \"{$cfgip} {$syscfg['hostname']}.{$syscfg['domain']}\"\n";
399
					$unbound_entries .= "local-data: \"{$syscfg['hostname']}.{$syscfg['domain']} A {$cfgip}\"\n";
400
					$unbound_entries .= "local-data: \"{$syscfg['hostname']} A {$cfgip}\"\n";
401
					break;
402
				}
403
			}
404
		}
405
	}*/
406

    
407
	/* Static Host entries */
408
	if (isset($dnscfg['hosts'])) {
409
		$hosts = $dnscfg['hosts'];
410
		$host_entries = "";
411
		$added_item = array();
412
		foreach ($hosts as $host) {
413
			$current_host = $host['host'];
414
			if ($host['host'] != "")
415
				$host['host'] = $host['host'].".";
416
			if(!$added_item[$current_host]) {
417
				$host_entries .= "local-data-ptr: \"{$host['ip']} {$host['host']}{$host['domain']}\"\n";
418
				if (is_ipaddrv6($host['ip']))
419
					$host_entries .= "local-data: \"{$host['host']}{$host['domain']} IN AAAA {$host['ip']}\"\n";
420
				else
421
					$host_entries .= "local-data: \"{$host['host']}{$host['domain']} IN A {$host['ip']}\"\n";
422
				if (!empty($host['descr']) && $dnscfg['txtsupport'] == 'on')
423
					$host_entries .= "local-data: '{$host['host']}{$host['domain']} TXT \"".addslashes($host['descr'])."\"'\n";
424

    
425
				// Do not add duplicate entries
426
				$added_item[$current_host] = true;
427
			}
428
		}
429
		$unbound_entries .= $host_entries;
430
	}
431
	// Static DHCP entries
432
	$host_entries = "";
433
	if (isset($dnscfg['regdhcpstatic']) && is_array($config['dhcpd'])) {
434
		foreach ($config['dhcpd'] as $dhcpif => $dhcpifconf)
435
			if(is_array($dhcpifconf['staticmap']) && isset($dhcpifconf['enable']))
436
				foreach ($dhcpifconf['staticmap'] as $host)
437
					if ($host['ipaddr'] && $host['hostname']) {
438
						$host_entries .= "local-data-ptr: \"{$host['ipaddr']} {$host['hostname']}.{$syscfg['domain']}\"\n";
439
						$host_entries .= "local-data: \"{$host['hostname']}.{$syscfg['domain']} IN A {$host['ipaddr']}\"\n";
440
						if (!empty($host['descr']) && $unboundcfg['txtsupport'] == 'on')
441
							$host_entries .= "local-data: '{$host['hostname']}.{$syscfg['domain']} TXT \"".addslashes($host['descr'])."\"'\n";
442
					}
443
		$unbound_entries .= $host_entries;
444
    }
445

    
446
	// Handle DHCPLeases added host entries
447
	$dhcplcfg = read_hosts();
448
	$host_entries = "";
449
	if(is_array($dhcplcfg)) {
450
		foreach($dhcplcfg as $key=>$host) {
451
			$host_entries .= "local-data-ptr: \"{$host['ipaddr']} {$host['fqdn']}\"\n";
452
			$host_entries .= "local-data: \"{$host['fqdn']} IN A {$host['ipaddr']}\"\n";
453
			if (!empty($host['name'])) {
454
				$host_entries .= "local-data-ptr: \"{$host['ipaddr']} {$host['name']}\"\n";
455
				$host_entries .= "local-data: \"{$host['name']} IN A {$host['ipaddr']}\"\n";
456
			}
457
		}
458
		$unbound_entries .= $host_entries;
459
	}
460

    
461
	/* Write out entries */
462
	file_put_contents("{$g['unbound_chroot_path']}/etc/host_entries.conf", $unbound_entries);
463
}
464

    
465
/* Read /etc/hosts */
466
function read_hosts() {
467

    
468
	/* Open /etc/hosts and extract the only dhcpleases info
469
	 * XXX - to convert to an unbound C library which reads /etc/hosts automatically
470
	 */
471
	$etc_hosts = array();
472
	foreach (file('/etc/hosts') as $line) {
473
		$d = preg_split('/\s/', $line, -1, PREG_SPLIT_NO_EMPTY);
474
		if (empty($d) || substr(reset($d), 0, 1) == "#")
475
			continue;
476
		if ($d[3] == "#") {
477
			$ip = array_shift($d);
478
			$fqdn = array_shift($d);
479
			$name = array_shift($d);
480
			if ($fqdn != "empty") {
481
				if ($name != "empty")
482
					array_push($etc_hosts, array(ipaddr => "$ip", fqdn => "$fqdn", name => "$name"));
483
				else
484
					array_push($etc_hosts, array(ipaddr => "$ip", fqdn => "$fqdn"));
485
			}
486
		}
487
	}
488
	return $etc_hosts;
489
}
490

    
491
function unbound_setup() {
492
	global $config, $g;
493

    
494
	unbound_anchor_setup();
495
	unbound_remote_control_setup();
496
	unbound_keys_setup();
497
	unbound_fetch_root_hints();
498
	unbound_resync_config();
499
}
500

    
501
?>
(50-50/64)