Project

General

Profile

Download (16.3 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
/* Optimize Unbound for environment */
76
function unbound_optimization() {
77
	global $config;
78

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

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

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

    
132
	return $optimization;
133
}
134

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

    
139
	$hints = "{$g['unbound_chroot_path']}/etc/root.hints";
140
	if (@filesize($hints) == 0) {
141
		$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}");
142

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

    
152
/* Fetch root name servers hints file */
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
/* Generate Unbound configuration */
220
function unbound_generate_config() {
221
	global $config, $g;
222

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
337
{$custom_options}
338

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

    
344
EOD;
345

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

    
348
	return 0;
349
}
350

    
351
function unbound_remote_control_setup() {
352
	global $g;
353

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

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

    
370
function unbound_add_host_entries() {
371
	global $config, $g;
372

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

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

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

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

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

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

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

    
467
/* Read /etc/hosts */
468
function read_hosts() {
469

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

    
493
function unbound_setup() {
494
	global $config, $g;
495

    
496
	unbound_anchor_setup();
497
	unbound_remote_control_setup();
498
	unbound_keys_setup();
499
	unbound_fetch_root_hints();
500
	unbound_resync_config();
501
}
502

    
503
function unbound_acl_id_used($id) {
504
	global $config;
505

    
506
	if (is_array($config['installedpackages']['unboundacls']['config']))
507
		foreach ($config['installedpackages']['unboundacls']['config'] as & $acls)
508
			if ($id == $acls['aclid'])
509
				return true;
510

    
511
	return false;
512
}
513

    
514
function unbound_get_next_id() {
515
	$aclid = 0;
516
	while(unbound_acl_id_used($aclid))
517
		$aclid++;
518
	return $aclid;
519
}
520

    
521
?>
(51-51/65)