Project

General

Profile

Download (19 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	services_unbound.php
4
*/
5
/* ====================================================================
6
 *	Copyright (c)  2004-2015  Electric Sheep Fencing, LLC. All rights reserved.
7
 *	Copyright (c)  2014 Warren Baker (warren@pfsense.org)
8
 *
9
 *	Redistribution and use in source and binary forms, with or without modification,
10
 *	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
17
 *		the documentation and/or other materials provided with the
18
 *		distribution.
19
 *
20
 *	3. All advertising materials mentioning features or use of this software
21
 *		must display the following acknowledgment:
22
 *		"This product includes software developed by the pfSense Project
23
 *		 for use in the pfSense software distribution. (http://www.pfsense.org/).
24
 *
25
 *	4. The names "pfSense" and "pfSense Project" must not be used to
26
 *		 endorse or promote products derived from this software without
27
 *		 prior written permission. For written permission, please contact
28
 *		 coreteam@pfsense.org.
29
 *
30
 *	5. Products derived from this software may not be called "pfSense"
31
 *		nor may "pfSense" appear in their names without prior written
32
 *		permission of the Electric Sheep Fencing, LLC.
33
 *
34
 *	6. Redistributions of any form whatsoever must retain the following
35
 *		acknowledgment:
36
 *
37
 *	"This product includes software developed by the pfSense Project
38
 *	for use in the pfSense software distribution (http://www.pfsense.org/).
39
 *
40
 *	THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
41
 *	EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
42
 *	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
43
 *	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
44
 *	ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
45
 *	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
46
 *	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
47
 *	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48
 *	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
49
 *	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
50
 *	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
51
 *	OF THE POSSIBILITY OF SUCH DAMAGE.
52
 *
53
 *	====================================================================
54
 *
55
 */
56

    
57
##|+PRIV
58
##|*IDENT=page-services-dnsresolver
59
##|*NAME=Services: DNS Resolver
60
##|*DESCR=Allow access to the 'Services: DNS Resolver' page.
61
##|*MATCH=services_unbound.php*
62
##|-PRIV
63

    
64
require_once("guiconfig.inc");
65
require_once("unbound.inc");
66
require_once("system.inc");
67

    
68
if (!is_array($config['unbound'])) {
69
	$config['unbound'] = array();
70
}
71

    
72
$a_unboundcfg =& $config['unbound'];
73

    
74
if (!is_array($a_unboundcfg['hosts'])) {
75
	$a_unboundcfg['hosts'] = array();
76
}
77

    
78
$a_hosts =& $a_unboundcfg['hosts'];
79

    
80
if (!is_array($a_unboundcfg['domainoverrides'])) {
81
	$a_unboundcfg['domainoverrides'] = array();
82
}
83

    
84
$a_domainOverrides = &$a_unboundcfg['domainoverrides'];
85

    
86
if (isset($a_unboundcfg['enable'])) {
87
	$pconfig['enable'] = true;
88
}
89
if (isset($a_unboundcfg['dnssec'])) {
90
	$pconfig['dnssec'] = true;
91
}
92
if (isset($a_unboundcfg['forwarding'])) {
93
	$pconfig['forwarding'] = true;
94
}
95
if (isset($a_unboundcfg['regdhcp'])) {
96
	$pconfig['regdhcp'] = true;
97
}
98
if (isset($a_unboundcfg['regdhcpstatic'])) {
99
	$pconfig['regdhcpstatic'] = true;
100
}
101

    
102
$pconfig['port'] = $a_unboundcfg['port'];
103
$pconfig['custom_options'] = base64_decode($a_unboundcfg['custom_options']);
104

    
105
if (empty($a_unboundcfg['active_interface'])) {
106
	$pconfig['active_interface'] = array();
107
} else {
108
	$pconfig['active_interface'] = explode(",", $a_unboundcfg['active_interface']);
109
}
110

    
111
if (empty($a_unboundcfg['outgoing_interface'])) {
112
	$pconfig['outgoing_interface'] = array();
113
} else {
114
	$pconfig['outgoing_interface'] = explode(",", $a_unboundcfg['outgoing_interface']);
115
}
116

    
117
if (empty($a_unboundcfg['system_domain_local_zone_type'])) {
118
	$pconfig['system_domain_local_zone_type'] = "transparent";
119
} else {
120
	$pconfig['system_domain_local_zone_type'] = $a_unboundcfg['system_domain_local_zone_type'];
121
}
122

    
123
if ($_POST) {
124
	if ($_POST['apply']) {
125
		$retval = services_unbound_configure();
126
		$savemsg = get_std_save_message($retval);
127
		if ($retval == 0) {
128
			clear_subsystem_dirty('unbound');
129
		}
130
		/* Update resolv.conf in case the interface bindings exclude localhost. */
131
		system_resolvconf_generate();
132
		/* Start or restart dhcpleases when it's necessary */
133
		system_dhcpleases_configure();
134
	} else {
135
		$pconfig = $_POST;
136
		unset($input_errors);
137

    
138
		if (isset($pconfig['enable']) && isset($config['dnsmasq']['enable'])) {
139
			if ($pconfig['port'] == $config['dnsmasq']['port']) {
140
				$input_errors[] = gettext("The DNS Forwarder is enabled using this port. Choose a non-conflicting port, or disable the DNS Forwarder.");
141
			}
142
		}
143

    
144
		// forwarding mode requires having valid DNS servers
145
		if (isset($pconfig['forwarding'])) {
146
			$founddns = false;
147
			if (isset($config['system']['dnsallowoverride'])) {
148
				$dns_servers = get_dns_servers();
149
				if (is_array($dns_servers)) {
150
					foreach ($dns_servers as $dns_server) {
151
						if (!ip_in_subnet($dns_server, "127.0.0.0/8")) {
152
							$founddns = true;
153
						}
154
					}
155
				}
156
			}
157
			if (is_array($config['system']['dnsserver'])) {
158
				foreach ($config['system']['dnsserver'] as $dnsserver) {
159
					if (is_ipaddr($dnsserver)) {
160
						$founddns = true;
161
					}
162
				}
163
			}
164
			if ($founddns == false) {
165
				$input_errors[] = gettext("At least one DNS server must be specified under System>General Setup to enable Forwarding mode.");
166
			}
167
		}
168

    
169
		if (empty($pconfig['active_interface'])) {
170
			$input_errors[] = gettext("One or more Network Interfaces must be selected for binding.");
171
		} else if (!isset($config['system']['dnslocalhost']) && (!in_array("lo0", $pconfig['active_interface']) && !in_array("all", $pconfig['active_interface']))) {
172
			$input_errors[] = gettext("This system is configured to use the DNS Resolver as its DNS server, so Localhost or All must be selected in Network Interfaces.");
173
		}
174

    
175
		if (empty($pconfig['outgoing_interface'])) {
176
			$input_errors[] = gettext("One or more Outgoing Network Interfaces must be selected.");
177
		}
178

    
179
		if ($pconfig['port'] && !is_port($pconfig['port'])) {
180
			$input_errors[] = gettext("You must specify a valid port number.");
181
		}
182

    
183
		if (is_array($pconfig['active_interface']) && !empty($pconfig['active_interface'])) {
184
			$display_active_interface = $pconfig['active_interface'];
185
			$pconfig['active_interface'] = implode(",", $pconfig['active_interface']);
186
		}
187

    
188
		$display_custom_options = $pconfig['custom_options'];
189
		$pconfig['custom_options'] = base64_encode(str_replace("\r\n", "\n", $pconfig['custom_options']));
190

    
191
		if (is_array($pconfig['outgoing_interface']) && !empty($pconfig['outgoing_interface'])) {
192
			$display_outgoing_interface = $pconfig['outgoing_interface'];
193
			$pconfig['outgoing_interface'] = implode(",", $pconfig['outgoing_interface']);
194
		}
195

    
196
		$test_output = array();
197
		if (test_unbound_config($pconfig, $test_output)) {
198
			$input_errors[] = gettext("The generated config file cannot be parsed by unbound. Please correct the following errors:");
199
			$input_errors = array_merge($input_errors, $test_output);
200
		}
201

    
202
		if (!$input_errors) {
203
			$a_unboundcfg['enable'] = isset($pconfig['enable']);
204
			$a_unboundcfg['port'] = $pconfig['port'];
205
			$a_unboundcfg['dnssec'] = isset($pconfig['dnssec']);
206
			$a_unboundcfg['forwarding'] = isset($pconfig['forwarding']);
207
			$a_unboundcfg['regdhcp'] = isset($pconfig['regdhcp']);
208
			$a_unboundcfg['regdhcpstatic'] = isset($pconfig['regdhcpstatic']);
209
			$a_unboundcfg['active_interface'] = $pconfig['active_interface'];
210
			$a_unboundcfg['outgoing_interface'] = $pconfig['outgoing_interface'];
211
			$a_unboundcfg['system_domain_local_zone_type'] = $pconfig['system_domain_local_zone_type'];
212
			$a_unboundcfg['custom_options'] = $pconfig['custom_options'];
213

    
214
			write_config(gettext("DNS Resolver configured."));
215
			mark_subsystem_dirty('unbound');
216
		}
217

    
218
		$pconfig['active_interface'] = $display_active_interface;
219
		$pconfig['outgoing_interface'] = $display_outgoing_interface;
220
		$pconfig['custom_options'] = $display_custom_options;
221
	}
222
}
223

    
224
if ($_GET['act'] == "del") {
225
	if ($_GET['type'] == 'host') {
226
		if ($a_hosts[$_GET['id']]) {
227
			unset($a_hosts[$_GET['id']]);
228
			write_config();
229
			mark_subsystem_dirty('unbound');
230
			header("Location: services_unbound.php");
231
			exit;
232
		}
233
	} elseif ($_GET['type'] == 'doverride') {
234
		if ($a_domainOverrides[$_GET['id']]) {
235
			unset($a_domainOverrides[$_GET['id']]);
236
			write_config();
237
			mark_subsystem_dirty('unbound');
238
			header("Location: services_unbound.php");
239
			exit;
240
		}
241
	}
242
}
243

    
244
function build_if_list($selectedifs) {
245
	$interface_addresses = get_possible_listen_ips(true);
246
	$iflist = array('options' => array(), 'selected' => array());
247

    
248
	$iflist['options']['all']	= gettext("All");
249
	if (empty($selectedifs) || empty($selectedifs[0]) || in_array("all", $selectedifs)) {
250
		array_push($iflist['selected'], "all");
251
	}
252

    
253
	foreach ($interface_addresses as $laddr => $ldescr) {
254
		$iflist['options'][$laddr] = htmlspecialchars($ldescr);
255

    
256
		if ($selectedifs && in_array($laddr, $selectedifs)) {
257
			array_push($iflist['selected'], $laddr);
258
		}
259
	}
260

    
261
	unset($interface_addresses);
262

    
263
	return($iflist);
264
}
265

    
266
$pgtitle = array(gettext("Services"), gettext("DNS Resolver"), gettext("General Settings"));
267
$shortcut_section = "resolver";
268

    
269
include_once("head.inc");
270

    
271
if ($input_errors) {
272
	print_input_errors($input_errors);
273
}
274

    
275
if ($savemsg) {
276
	print_info_box($savemsg, 'success');
277
}
278

    
279
if (is_subsystem_dirty('unbound')) {
280
	print_apply_box(gettext("The DNS resolver configuration has been changed.") . "<br />" . gettext("You must apply the changes in order for them to take effect."));
281
}
282

    
283
$tab_array = array();
284
$tab_array[] = array(gettext("General Settings"), true, "services_unbound.php");
285
$tab_array[] = array(gettext("Advanced Settings"), false, "services_unbound_advanced.php");
286
$tab_array[] = array(gettext("Access Lists"), false, "/services_unbound_acls.php");
287
display_top_tabs($tab_array, true);
288

    
289
$form = new Form();
290

    
291
$section = new Form_Section('General DNS Resolver Options');
292

    
293
$section->addInput(new Form_Checkbox(
294
	'enable',
295
	'Enable',
296
	'Enable DNS resolver',
297
	$pconfig['enable']
298
));
299

    
300
$section->addInput(new Form_Input(
301
	'port',
302
	'Listen Port',
303
	'number',
304
	$pconfig['port'],
305
	['placeholder' => '53']
306
))->setHelp('The port used for responding to DNS queries. It should normally be left blank unless another service needs to bind to TCP/UDP port 53.');
307

    
308
$activeiflist = build_if_list($pconfig['active_interface']);
309

    
310
$section->addInput(new Form_Select(
311
	'active_interface',
312
	'Network Interfaces',
313
	$activeiflist['selected'],
314
	$activeiflist['options'],
315
	true
316
))->addClass('general')->setHelp('Interface IPs used by the DNS Resolver for responding to queries from clients. If an interface has both IPv4 and IPv6 IPs, both are used. Queries to other interface IPs not selected below are discarded. ' .
317
			'The default behavior is to respond to queries on every available IPv4 and IPv6 address.');
318

    
319
$outiflist = build_if_list($pconfig['outgoing_interface']);
320

    
321
$section->addInput(new Form_Select(
322
	'outgoing_interface',
323
	'Outgoing Network Interfaces',
324
	$outiflist['selected'],
325
	$outiflist['options'],
326
	true
327
))->addClass('general')->setHelp('Utilize different network interface(s) that the DNS Resolver will use to send queries to authoritative servers and receive their replies. By default all interfaces are used.');
328

    
329
$section->addInput(new Form_Select(
330
	'system_domain_local_zone_type',
331
	'System Domain Local Zone Type',
332
	$pconfig['system_domain_local_zone_type'],
333
	unbound_local_zone_types()
334
))->setHelp('The local-zone type used for the pfSense system domain (System | General Setup | Domain).  Transparent is the default.  Local-Zone type descriptions are available in the unbound.conf(5) manual pages.');
335

    
336
$section->addInput(new Form_Checkbox(
337
	'dnssec',
338
	'DNSSEC',
339
	'Enable DNSSEC Support',
340
	$pconfig['dnssec']
341
));
342

    
343
$section->addInput(new Form_Checkbox(
344
	'forwarding',
345
	'DNS Query Forwarding',
346
	'Enable Forwarding Mode',
347
	$pconfig['forwarding']
348
));
349

    
350
$section->addInput(new Form_Checkbox(
351
	'regdhcp',
352
	'DHCP Registration',
353
	'Register DHCP leases in the DNS Resolver',
354
	$pconfig['regdhcp']
355
))->setHelp(sprintf('If this option is set, then machines that specify their hostname when requesting a DHCP lease will be registered'.
356
					' in the DNS Resolver, so that their name can be resolved.'.
357
					' You should also set the domain in %sSystem: General setup%s to the proper value.','<a href="system.php">','</a>'));
358

    
359
$section->addInput(new Form_Checkbox(
360
	'regdhcpstatic',
361
	'Static DHCP',
362
	'Register DHCP static mappings in the DNS Resolver',
363
	$pconfig['regdhcpstatic']
364
))->setHelp(sprintf('If this option is set, then DHCP static mappings will be registered in the DNS Resolver, so that their name can be '.
365
					'resolved. You should also set the domain in %s'.
366
					'System: General setup%s to the proper value.','<a href="system.php">','</a>'));
367

    
368
$btnadv = new Form_Button(
369
	'btnadvcustom',
370
	'Custom options',
371
	null,
372
	'fa-cog'
373
);
374

    
375
$btnadv->addClass('btn-info btn-sm');
376

    
377
$section->addInput(new Form_StaticText(
378
	'Display Custom Options',
379
	$btnadv
380
));
381

    
382
$section->addInput(new Form_Textarea (
383
	'custom_options',
384
	'Custom options',
385
	$pconfig['custom_options']
386
))->setHelp('Enter any additional configuration parameters to add to the DNS Resolver configuration here, separated by a newline');
387

    
388
$form->add($section);
389
print($form);
390
?>
391

    
392
<script type="text/javascript">
393
//<![CDATA[
394
events.push(function() {
395

    
396
	// Show advanced custom options ==============================================
397
	var showadvcustom = false;
398

    
399
	function show_advcustom(ispageload) {
400
		var text;
401
		// On page load decide the initial state based on the data.
402
		if (ispageload) {
403
			if ('<?=$pconfig['custom_options']?>' == '') {
404
				showadvcustom = false;
405
			} else {
406
				showadvcustom = true;
407
			}
408
		} else {
409
			// It was a click, swap the state.
410
			showadvcustom = !showadvcustom;
411
		}
412

    
413
		hideInput('custom_options', !showadvcustom);
414

    
415
		if (showadvcustom) {
416
			text = "<?=gettext('Hide Custom Options');?>";
417
		} else {
418
			text = "<?=gettext('Display Custom Options');?>";
419
		}
420
		$('#btnadvcustom').html('<i class="fa fa-cog"></i> ' + text);
421
	}
422

    
423
	// If the enable checkbox is not checked, hide all inputs
424
	function hideGeneral() {
425
		var hide = ! $('#enable').prop('checked');
426

    
427
		hideMultiClass('general', hide);
428
		hideInput('port', hide);
429
		hideSelect('system_domain_local_zone_type', hide);
430
		hideCheckbox('dnssec', hide);
431
		hideCheckbox('forwarding', hide);
432
		hideCheckbox('regdhcp', hide);
433
		hideCheckbox('regdhcpstatic', hide);
434
		hideInput('btnadvcustom', hide);
435
		hideInput('custom_options', hide || !showadvcustom);
436
	}
437

    
438
	// Make the 'additional options' button a plain button, not a submit button
439
	$("#btnadvcustom").prop('type','button');
440

    
441
	// Un-hide additional controls
442
	$('#btnadvcustom').click(function(event) {
443
		show_advcustom();
444
	});
445

    
446
	// When 'enable' is clicked, disable/enable the following hide inputs
447
	$('#enable').click(function() {
448
		hideGeneral();
449
	});
450

    
451
	// On initial load
452
	if ($('#custom_options').val().length == 0) {
453
		hideInput('custom_options', true);
454
	}
455

    
456
	hideGeneral();
457
	show_advcustom(true);
458

    
459
});
460
//]]>
461
</script>
462

    
463
<div class="panel panel-default">
464
	<div class="panel-heading"><h2 class="panel-title"><?=gettext("Host Overrides")?></h2></div>
465
	<div class="panel-body table-responsive">
466
		<table class="table table-striped table-hover table-condensed sortable-theme-bootstrap" data-sortable>
467
			<thead>
468
				<tr>
469
					<th><?=gettext("Host")?></th>
470
					<th><?=gettext("Domain")?></th>
471
					<th><?=gettext("IP")?></th>
472
					<th><?=gettext("Description")?></th>
473
					<th><?=gettext("Actions")?></th>
474
				</tr>
475
			</thead>
476
			<tbody>
477
<?php
478
$i = 0;
479
foreach ($a_hosts as $hostent):
480
?>
481
				<tr>
482
					<td>
483
						<?=$hostent['host']?>
484
					</td>
485
					<td>
486
						<?=$hostent['domain']?>
487
					</td>
488
					<td>
489
						<?=$hostent['ip']?>
490
					</td>
491
					<td>
492
						<?=htmlspecialchars($hostent['descr'])?>
493
					</td>
494
					<td>
495
						<a class="fa fa-pencil"	title="<?=gettext('Edit host override')?>" href="services_unbound_host_edit.php?id=<?=$i?>"></a>
496
						<a class="fa fa-trash"	title="<?=gettext('Delete host override')?>" href="services_unbound.php?type=host&amp;act=del&amp;id=<?=$i?>"></a>
497
					</td>
498
				</tr>
499

    
500
<?php
501
	if ($hostent['aliases']['item'] && is_array($hostent['aliases']['item'])):
502
		foreach ($hostent['aliases']['item'] as $alias):
503
?>
504
				<tr>
505
					<td>
506
						<?=$alias['host']?>
507
					</td>
508
					<td>
509
						<?=$alias['domain']?>
510
					</td>
511
					<td>
512
						<?=gettext("Alias for ");?><?=$hostent['host'] ? $hostent['host'] . '.' . $hostent['domain'] : $hostent['domain']?>
513
					</td>
514
					<td>
515
						<i class="fa fa-angle-double-right text-info"></i>
516
						<?=htmlspecialchars($alias['description'])?>
517
					</td>
518
					<td>
519
						<a a class="fa fa-pencil"	title="<?=gettext('Edit host override')?>" 	href="services_unbound_host_edit.php?id=<?=$i?>"></a>
520
					</td>
521
				</tr>
522
<?php
523
		endforeach;
524
	endif;
525
	$i++;
526
endforeach;
527
?>
528
			</tbody>
529
		</table>
530
	</div>
531
</div>
532

    
533
<nav class="action-buttons">
534
	<a href="services_unbound_host_edit.php" class="btn btn-sm btn-success">
535
		<i class="fa fa-plus icon-embed-btn"></i>
536
		<?=gettext('Add')?>
537
	</a>
538
</nav>
539

    
540
<div class="panel panel-default">
541
	<div class="panel-heading"><h2 class="panel-title"><?=gettext("Domain Overrides")?></h2></div>
542
	<div class="panel-body table-responsive">
543
		<table class="table table-striped table-hover table-condensed sortable-theme-bootstrap" data-sortable>
544
			<thead>
545
				<tr>
546
					<th><?=gettext("Domain")?></th>
547
					<th><?=gettext("IP")?></th>
548
					<th><?=gettext("Description")?></th>
549
					<th><?=gettext("Actions")?></th>
550
				</tr>
551
			</thead>
552

    
553
			<tbody>
554
<?php
555
$i = 0;
556
foreach ($a_domainOverrides as $doment):
557
?>
558
				<tr>
559
					<td>
560
						<?=$doment['domain']?>&nbsp;
561
					</td>
562
					<td>
563
						<?=$doment['ip']?>&nbsp;
564
					</td>
565
					<td>
566
						<?=htmlspecialchars($doment['descr'])?>&nbsp;
567
					</td>
568
					<td>
569
						<a class="fa fa-pencil"	title="<?=gettext('Edit domain override')?>" href="services_unbound_domainoverride_edit.php?id=<?=$i?>"></a>
570
						<a class="fa fa-trash"	title="<?=gettext('Delete domain override')?>" href="services_unbound.php?act=del&amp;type=doverride&amp;id=<?=$i?>"></a>
571
					</td>
572
				</tr>
573
<?php
574
	$i++;
575
endforeach;
576
?>
577
			</tbody>
578
		</table>
579
	</div>
580
</div>
581

    
582
<nav class="action-buttons">
583
	<a href="services_unbound_domainoverride_edit.php" class="btn btn-sm btn-success">
584
		<i class="fa fa-plus icon-embed-btn"></i>
585
		<?=gettext('Add')?>
586
	</a>
587
</nav>
588

    
589
<div class="infoblock">
590
	<?php print_info_box(sprintf(gettext("If the DNS Resolver is enabled, the DHCP".
591
		" service (if enabled) will automatically serve the LAN IP".
592
		" address as a DNS server to DHCP clients so they will use".
593
		" the DNS Resolver. If Forwarding is enabled, the DNS Resolver will use the DNS servers".
594
		" entered in %sSystem: General setup%s".
595
		" or those obtained via DHCP or PPP on WAN if &quot;Allow".
596
		" DNS server list to be overridden by DHCP/PPP on WAN&quot;".
597
		" is checked."), '<a href="system.php">', '</a>'), 'info', false); ?>
598
</div>
599

    
600
<?php include("foot.inc");
(141-141/225)