Project

General

Profile

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

    
29
##|+PRIV
30
##|*IDENT=page-services-dnsforwarder
31
##|*NAME=Services: DNS Forwarder
32
##|*DESCR=Allow access to the 'Services: DNS Forwarder' page.
33
##|*MATCH=services_dnsmasq.php*
34
##|-PRIV
35

    
36
require_once("guiconfig.inc");
37
require_once("functions.inc");
38
require_once("filter.inc");
39
require_once("pfsense-utils.inc");
40
require_once("shaper.inc");
41
require_once("system.inc");
42

    
43
// Sort host entries for display in alphabetical order
44
function hostcmp($a, $b) {
45
	return strcasecmp($a['host'], $b['host']);
46
}
47

    
48
function hosts_sort() {
49
	global $a_hosts;
50

    
51
	if (!is_array($a_hosts)) {
52
		return;
53
	}
54

    
55
	uasort($a_hosts, "hostcmp");
56
}
57

    
58
// Sort domain entries for display in alphabetical order
59
function domaincmp($a, $b) {
60
	return strcasecmp($a['domain'], $b['domain']);
61
}
62

    
63
function domains_sort() {
64
	global $a_domainOverrides;
65

    
66
	if (!is_array($a_domainOverrides)) {
67
		return;
68
	}
69

    
70
	uasort($a_domainOverrides, "domaincmp");
71
}
72

    
73
$pconfig['enable'] = isset($config['dnsmasq']['enable']);
74
$pconfig['regdhcp'] = isset($config['dnsmasq']['regdhcp']);
75
$pconfig['regdhcpstatic'] = isset($config['dnsmasq']['regdhcpstatic']);
76
$pconfig['dhcpfirst'] = isset($config['dnsmasq']['dhcpfirst']);
77
$pconfig['strict_order'] = isset($config['dnsmasq']['strict_order']);
78
$pconfig['domain_needed'] = isset($config['dnsmasq']['domain_needed']);
79
$pconfig['no_private_reverse'] = isset($config['dnsmasq']['no_private_reverse']);
80
$pconfig['port'] = $config['dnsmasq']['port'];
81
$pconfig['custom_options'] = $config['dnsmasq']['custom_options'];
82
$pconfig['strictbind'] = isset($config['dnsmasq']['strictbind']);
83

    
84
if (!empty($config['dnsmasq']['interface'])) {
85
	$pconfig['interface'] = explode(",", $config['dnsmasq']['interface']);
86
} else {
87
	$pconfig['interface'] = array();
88
}
89

    
90
init_config_arr(array('dnsmasq', 'hosts'));
91
$a_hosts = &$config['dnsmasq']['hosts'];
92

    
93
// Add a temporary index so we don't lose the order after sorting
94
for ($idx=0; $idx<count($a_hosts); $idx++) {
95
	$a_hosts[$idx]['idx'] = $idx;
96
}
97

    
98
hosts_sort();
99

    
100
init_config_arr(array('dnsmasq', 'domainoverrides'));
101
$a_domainOverrides = &$config['dnsmasq']['domainoverrides'];
102

    
103
// Add a temporary index so we don't lose the order after sorting
104
for ($idx=0; $idx<count($a_domainOverrides); $idx++) {
105
	$a_domainOverrides[$idx]['idx'] = $idx;
106
}
107

    
108
domains_sort();
109

    
110

    
111
if ($_POST['apply']) {
112
	$retval = 0;
113
	$retval |= services_dnsmasq_configure();
114

    
115
	// Reload filter (we might need to sync to CARP hosts)
116
	filter_configure();
117
	/* Update resolv.conf in case the interface bindings exclude localhost. */
118
	system_resolvconf_generate();
119
	/* Start or restart dhcpleases when it's necessary */
120
	system_dhcpleases_configure();
121

    
122
	if ($retval == 0) {
123
		clear_subsystem_dirty('hosts');
124
	}
125
}
126

    
127
if ($_POST['save']) {
128
	$pconfig = $_POST;
129
	unset($input_errors);
130

    
131
	$config['dnsmasq']['enable'] = ($_POST['enable']) ? true : false;
132
	$config['dnsmasq']['regdhcp'] = ($_POST['regdhcp']) ? true : false;
133
	$config['dnsmasq']['regdhcpstatic'] = ($_POST['regdhcpstatic']) ? true : false;
134
	$config['dnsmasq']['dhcpfirst'] = ($_POST['dhcpfirst']) ? true : false;
135
	$config['dnsmasq']['strict_order'] = ($_POST['strict_order']) ? true : false;
136
	$config['dnsmasq']['domain_needed'] = ($_POST['domain_needed']) ? true : false;
137
	$config['dnsmasq']['no_private_reverse'] = ($_POST['no_private_reverse']) ? true : false;
138
	$config['dnsmasq']['custom_options'] = str_replace("\r\n", "\n", $_POST['custom_options']);
139
	$config['dnsmasq']['strictbind'] = ($_POST['strictbind']) ? true : false;
140

    
141
	if (isset($_POST['enable']) && isset($config['unbound']['enable'])) {
142
		if ($_POST['port'] == $config['unbound']['port']) {
143
			$input_errors[] = gettext("The DNS Resolver is enabled using this port. Choose a non-conflicting port, or disable DNS Resolver.");
144
		}
145
	}
146

    
147
	if ((isset($_POST['regdhcp']) || isset($_POST['regdhcpstatic']) || isset($_POST['dhcpfirst'])) && !is_dhcp_server_enabled()) {
148
		$input_errors[] = gettext("DHCP Server must be enabled for DHCP Registration to work in DNS Forwarder.");
149
	}
150

    
151
	if ($_POST['port']) {
152
		if (is_port($_POST['port'])) {
153
			$config['dnsmasq']['port'] = $_POST['port'];
154
		} else {
155
			$input_errors[] = gettext("A valid port number must be specified.");
156
		}
157
	} else if (isset($config['dnsmasq']['port'])) {
158
		unset($config['dnsmasq']['port']);
159
	}
160

    
161
	if (is_array($_POST['interface'])) {
162
		$config['dnsmasq']['interface'] = implode(",", $_POST['interface']);
163
	} elseif (isset($config['dnsmasq']['interface'])) {
164
		unset($config['dnsmasq']['interface']);
165
	}
166

    
167
	if ($config['dnsmasq']['custom_options']) {
168
		$args = '';
169
		foreach (preg_split('/\s+/', $config['dnsmasq']['custom_options']) as $c) {
170
			$args .= escapeshellarg("--{$c}") . " ";
171
		}
172
		exec("/usr/local/sbin/dnsmasq --test $args", $output, $rc);
173
		if ($rc != 0) {
174
			$input_errors[] = gettext("Invalid custom options");
175
		}
176
	}
177

    
178
	if (!$input_errors) {
179
		write_config("DNS Forwarder settings saved");
180
		mark_subsystem_dirty('hosts');
181
	}
182
}
183

    
184

    
185
if ($_POST['act'] == "del") {
186
	if ($_POST['type'] == 'host') {
187
		if ($a_hosts[$_POST['id']]) {
188
			unset($a_hosts[$_POST['id']]);
189
			write_config("DNS Forwarder host override deleted");
190
			mark_subsystem_dirty('hosts');
191
			header("Location: services_dnsmasq.php");
192
			exit;
193
		}
194
	} elseif ($_POST['type'] == 'doverride') {
195
		if ($a_domainOverrides[$_POST['id']]) {
196
			unset($a_domainOverrides[$_POST['id']]);
197
			write_config("DNS Forwarder domain override deleted");
198
			mark_subsystem_dirty('hosts');
199
			header("Location: services_dnsmasq.php");
200
			exit;
201
		}
202
	}
203
}
204

    
205
function build_if_list() {
206
	global $pconfig;
207

    
208
	$interface_addresses = get_possible_listen_ips(true);
209
	$iflist = array('options' => array(), 'selected' => array());
210

    
211
	$iflist['options'][""]	= "All";
212
	if (empty($pconfig['interface']) || empty($pconfig['interface'][0])) {
213
		array_push($iflist['selected'], "");
214
	}
215

    
216
	foreach ($interface_addresses as $laddr => $ldescr) {
217
		$iflist['options'][$laddr] = htmlspecialchars($ldescr);
218

    
219
		if ($pconfig['interface'] && in_array($laddr, $pconfig['interface'])) {
220
			array_push($iflist['selected'], $laddr);
221
		}
222
	}
223

    
224
	unset($interface_addresses);
225

    
226
	return($iflist);
227
}
228

    
229
$pgtitle = array(gettext("Services"), gettext("DNS Forwarder"));
230
$shortcut_section = "forwarder";
231
include("head.inc");
232

    
233
if ($input_errors) {
234
	print_input_errors($input_errors);
235
}
236

    
237
if ($_POST['apply']) {
238
	print_apply_result_box($retval);
239
}
240

    
241
if (is_subsystem_dirty('hosts')) {
242
	print_apply_box(gettext("The DNS forwarder configuration has been changed.") . "<br />" . gettext("The changes must be applied for them to take effect."));
243
}
244

    
245
$form = new Form();
246

    
247
$section = new Form_Section('General DNS Forwarder Options');
248

    
249
$section->addInput(new Form_Checkbox(
250
	'enable',
251
	'Enable',
252
	'Enable DNS forwarder',
253
	$pconfig['enable']
254
))->toggles('.toggle-dhcp', 'disable');
255

    
256
$section->addInput(new Form_Checkbox(
257
	'regdhcp',
258
	'DHCP Registration',
259
	'Register DHCP leases in DNS forwarder',
260
	$pconfig['regdhcp']
261
))->setHelp('If this option is set machines that specify'.
262
			' their hostname when requesting a DHCP lease will be registered'.
263
			' in the DNS forwarder, so that their name can be resolved.'.
264
			' The domain in %1$sSystem: General Setup%2$s should also'.
265
			' be set to the proper value.', '<a href="system.php">', '</a>')
266
	->addClass('toggle-dhcp');
267

    
268
$section->addInput(new Form_Checkbox(
269
	'regdhcpstatic',
270
	'Static DHCP',
271
	'Register DHCP static mappings in DNS forwarder',
272
	$pconfig['regdhcpstatic']
273
))->setHelp('If this option is set, IPv4 DHCP static mappings will '.
274
					'be registered in the DNS forwarder so that their name can be '.
275
					'resolved. The domain in %1$sSystem: General Setup%2$s should also '.
276
					'be set to the proper value.', '<a href="system.php">', '</a>')
277
	->addClass('toggle-dhcp');
278

    
279
$section->addInput(new Form_Checkbox(
280
	'dhcpfirst',
281
	'Prefer DHCP',
282
	'Resolve DHCP mappings first',
283
	$pconfig['dhcpfirst']
284
))->setHelp("If this option is set DHCP mappings will ".
285
					"be resolved before the manual list of names below. This only ".
286
					"affects the name given for a reverse lookup (PTR).")
287
	->addClass('toggle-dhcp');
288

    
289
$group = new Form_Group('DNS Query Forwarding');
290

    
291
$group->add(new Form_Checkbox(
292
	'strict_order',
293
	'DNS Query Forwarding',
294
	'Query DNS servers sequentially',
295
	$pconfig['strict_order']
296
))->setHelp('If this option is set %1$s DNS Forwarder (dnsmasq) will '.
297
					'query the DNS servers sequentially in the order specified (%2$sSystem - General Setup - DNS Servers%3$s), '.
298
					'rather than all at once in parallel. ', $g['product_label'], '<i>', '</i>');
299

    
300
$group->add(new Form_Checkbox(
301
	'domain_needed',
302
	null,
303
	'Require domain',
304
	$pconfig['domain_needed']
305
))->setHelp("If this option is set %s DNS Forwarder (dnsmasq) will ".
306
					"not forward A or AAAA queries for plain names, without dots or domain parts, to upstream name servers.	 ".
307
					"If the name is not known from /etc/hosts or DHCP then a \"not found\" answer is returned. ", $g['product_label']);
308

    
309
$group->add(new Form_Checkbox(
310
	'no_private_reverse',
311
	null,
312
	'Do not forward private reverse lookups',
313
	$pconfig['no_private_reverse']
314
))->setHelp("If this option is set %s DNS Forwarder (dnsmasq) will ".
315
					"not forward reverse DNS lookups (PTR) for private addresses (RFC 1918) to upstream name servers.  ".
316
					"Any entries in the Domain Overrides section forwarding private \"n.n.n.in-addr.arpa\" names to a specific server are still forwarded. ".
317
					"If the IP to name is not known from /etc/hosts, DHCP or a specific domain override then a \"not found\" answer is immediately returned. ", $g['product_label']);
318

    
319
$section->add($group);
320

    
321
$section->addInput(new Form_Input(
322
	'port',
323
	'Listen Port',
324
	'number',
325
	$pconfig['port'],
326
	['placeholder' => '53']
327
))->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.');
328

    
329
$iflist = build_if_list();
330

    
331
$section->addInput(new Form_Select(
332
	'interface',
333
	'*Interfaces',
334
	$iflist['selected'],
335
	$iflist['options'],
336
	true
337
))->setHelp('Interface IPs used by the DNS Forwarder 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. ' .
338
			'The default behavior is to respond to queries on every available IPv4 and IPv6 address.');
339

    
340
$section->addInput(new Form_Checkbox(
341
	'strictbind',
342
	'Strict binding',
343
	'Strict interface binding',
344
	$pconfig['strictbind']
345
))->setHelp('If this option is set, the DNS forwarder will only bind to the interfaces containing the IP addresses selected above, ' .
346
					'rather than binding to all interfaces and discarding queries to other addresses.%1$s' .
347
					'This option does NOT work with IPv6. If set, dnsmasq will not bind to IPv6 addresses.', '<br /><br />');
348

    
349
$section->addInput(new Form_Textarea(
350
	'custom_options',
351
	'Custom options',
352
	$pconfig['custom_options']
353
))->setHelp('Enter any additional options to add to the dnsmasq configuration here, separated by a space or newline.')
354
  ->addClass('advanced');
355

    
356
$form->add($section);
357
print($form);
358

    
359
?>
360
<div class="panel panel-default">
361
	<div class="panel-heading"><h2 class="panel-title"><?=gettext("Host Overrides")?></h2></div>
362
	<div class="panel-body table-responsive">
363
		<table class="table table-striped table-hover table-condensed sortable-theme-bootstrap table-rowdblclickedit" data-sortable>
364
			<thead>
365
				<tr>
366
					<th><?=gettext("Host")?></th>
367
					<th><?=gettext("Domain")?></th>
368
					<th><?=gettext("IP")?></th>
369
					<th><?=gettext("Description")?></th>
370
					<th><?=gettext("Actions")?></th>
371
				</tr>
372
			</thead>
373
			<tbody>
374
<?php
375
foreach ($a_hosts as $i => $hostent):
376
?>
377
				<tr>
378
					<td>
379
						<?=$hostent['host']?>
380
					</td>
381
					<td>
382
						<?=$hostent['domain']?>
383
					</td>
384
					<td>
385
						<?=$hostent['ip']?>
386
					</td>
387
					<td>
388
						<?=htmlspecialchars($hostent['descr'])?>
389
					</td>
390
					<td>
391
						<a class="fa fa-pencil"	title="<?=gettext('Edit host override')?>" 	href="services_dnsmasq_edit.php?id=<?=$hostent['idx']?>"></a>
392
						<a class="fa fa-trash"	title="<?=gettext('Delete host override')?>"	href="services_dnsmasq.php?type=host&amp;act=del&amp;id=<?=$hostent['idx']?>" usepost></a>
393
					</td>
394
				</tr>
395

    
396
<?php
397
	if ($hostent['aliases']['item'] && is_array($hostent['aliases']['item'])):
398
		foreach ($hostent['aliases']['item'] as $alias):
399
?>
400
				<tr>
401
					<td>
402
						<?=$alias['host']?>
403
					</td>
404
					<td>
405
						<?=$alias['domain']?>
406
					</td>
407
					<td>
408
						<?=gettext("Alias for ");?><?=$hostent['host'] ? $hostent['host'] . '.' . $hostent['domain'] : $hostent['domain']?>
409
					</td>
410
					<td>
411
						<i class="fa fa-angle-double-right text-info"></i>
412
						<?=htmlspecialchars($alias['description'])?>
413
					</td>
414
					<td>
415
						<a class="fa fa-pencil"	title="<?=gettext('Edit host override')?>" 	href="services_dnsmasq_edit.php?id=<?=$i?>"></a>
416
					</td>
417
				</tr>
418
<?php
419
		endforeach;
420
	endif;
421
endforeach;
422
?>
423
			</tbody>
424
		</table>
425
	</div>
426
</div>
427

    
428
<nav class="action-buttons">
429
	<a href="services_dnsmasq_edit.php" class="btn btn-sm btn-success btn-sm">
430
		<i class="fa fa-plus icon-embed-btn"></i>
431
		<?=gettext('Add')?>
432
	</a>
433
</nav>
434

    
435
<div class="panel panel-default">
436
	<div class="panel-heading"><h2 class="panel-title"><?=gettext("Domain Overrides")?></h2></div>
437
	<div class="panel-body table-responsive">
438
		<table class="table table-striped table-hover table-condensed sortable-theme-bootstrap table-rowdblclickedit" data-sortable>
439
			<thead>
440
				<tr>
441
					<th><?=gettext("Domain")?></th>
442
					<th><?=gettext("IP")?></th>
443
					<th><?=gettext("Description")?></th>
444
					<th><?=gettext("Actions")?></th>
445
				</tr>
446
			</thead>
447

    
448
			<tbody>
449
<?php
450
foreach ($a_domainOverrides as $i => $doment):
451
?>
452
				<tr>
453
					<td>
454
						<?=$doment['domain']?>
455
					</td>
456
					<td>
457
						<?=$doment['ip']?>
458
					</td>
459
					<td>
460
						<?=htmlspecialchars($doment['descr'])?>
461
					</td>
462
					<td>
463
						<a class="fa fa-pencil"	title="<?=gettext('Edit domain override')?>" href="services_dnsmasq_domainoverride_edit.php?id=<?=$doment['idx']?>"></a>
464
						<a class="fa fa-trash"	title="<?=gettext('Delete domain override')?>" href="services_dnsmasq.php?act=del&amp;type=doverride&amp;id=<?=$doment['idx']?>" usepost></a>
465
					</td>
466
				</tr>
467
<?php
468
endforeach;
469
?>
470
			</tbody>
471
		</table>
472
	</div>
473
</div>
474

    
475
<nav class="action-buttons">
476
	<a href="services_dnsmasq_domainoverride_edit.php" class="btn btn-sm btn-success btn-sm">
477
		<i class="fa fa-plus icon-embed-btn"></i>
478
		<?=gettext('Add')?>
479
	</a>
480
</nav>
481
<div class="infoblock">
482
<?php
483
print_info_box(
484
	'<p>' .
485
	gettext('If the DNS forwarder is enabled, the DHCP service (if enabled) will automatically' .
486
		    ' serve the LAN IP address as a DNS server to DHCP clients so they will use the forwarder.') . '</p><p>' .
487
	sprintf(gettext('The DNS forwarder will use the DNS servers entered in %1$sSystem > General Setup%2$s or' .
488
				    ' those obtained via DHCP or PPP on WAN if &quot;Allow DNS server list to be overridden by DHCP/PPP on WAN&quot; is checked.' .
489
				    ' If that option is not used (or if a static IP address is used on WAN),' .
490
				    ' at least one DNS server must be manually specified on the %1$sSystem > General Setup%2$s page.'),
491
			'<a href="system.php">',
492
			'</a>') .
493
	'</p>',
494
	'info',
495
	false
496
);
497
?>
498
</div>
499

    
500
<?php
501
include("foot.inc");
(124-124/229)