6 var callHostHints
, callDUIDHints
, callDHCPLeases
, CBILeaseStatus
;
8 callHostHints
= rpc
.declare({
10 method
: 'getHostHints',
14 callDUIDHints
= rpc
.declare({
16 method
: 'getDUIDHints',
20 callDHCPLeases
= rpc
.declare({
22 method
: 'getDHCPLeases',
24 expect
: { dhcp_leases
: [] }
27 CBILeaseStatus
= form
.DummyValue
.extend({
28 renderWidget: function(section_id
, option_id
, cfgvalue
) {
30 E('h4', _('Active DHCP Leases')),
31 E('div', { 'id': 'lease_status_table', 'class': 'table' }, [
32 E('div', { 'class': 'tr table-titles' }, [
33 E('div', { 'class': 'th' }, _('Hostname')),
34 E('div', { 'class': 'th' }, _('IPv4-Address')),
35 E('div', { 'class': 'th' }, _('MAC-Address')),
36 E('div', { 'class': 'th' }, _('Leasetime remaining'))
38 E('div', { 'class': 'tr placeholder' }, [
39 E('div', { 'class': 'td' }, E('em', _('Collecting data...')))
46 return L
.view
.extend({
54 render: function(hosts_duids
) {
55 var hosts
= hosts_duids
[0],
56 duids
= hosts_duids
[1],
59 m
= new form
.Map('dhcp', _('DHCP and DNS'), _('Dnsmasq is a combined <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr>-Server and <abbr title="Domain Name System">DNS</abbr>-Forwarder for <abbr title="Network Address Translation">NAT</abbr> firewalls'));
61 s
= m
.section(form
.TypedSection
, 'dnsmasq', _('Server Settings'));
65 s
.tab('general', _('General Settings'));
66 s
.tab('files', _('Resolv and Hosts Files'));
67 s
.tab('tftp', _('TFTP Settings'));
68 s
.tab('advanced', _('Advanced Settings'));
69 s
.tab('leases', _('Static Leases'));
71 s
.taboption('general', form
.Flag
, 'domainneeded',
73 _('Don\'t forward <abbr title="Domain Name System">DNS</abbr>-Requests without <abbr title="Domain Name System">DNS</abbr>-Name'));
75 s
.taboption('general', form
.Flag
, 'authoritative',
77 _('This is the only <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr> in the local network'));
80 s
.taboption('files', form
.Flag
, 'readethers',
81 _('Use <code>/etc/ethers</code>'),
82 _('Read <code>/etc/ethers</code> to configure the <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr>-Server'));
84 s
.taboption('files', form
.Value
, 'leasefile',
86 _('file where given <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr>-leases will be stored'));
88 s
.taboption('files', form
.Flag
, 'noresolv',
89 _('Ignore resolve file')).optional
= true;
91 o
= s
.taboption('files', form
.Value
, 'resolvfile',
93 _('local <abbr title="Domain Name System">DNS</abbr> file'));
95 o
.depends('noresolv', '');
99 s
.taboption('files', form
.Flag
, 'nohosts',
100 _('Ignore <code>/etc/hosts</code>')).optional
= true;
102 s
.taboption('files', form
.DynamicList
, 'addnhosts',
103 _('Additional Hosts files')).optional
= true;
105 o
= s
.taboption('advanced', form
.Flag
, 'quietdhcp',
106 _('Suppress logging'),
107 _('Suppress logging of the routine operation of these protocols'));
110 o
= s
.taboption('advanced', form
.Flag
, 'sequential_ip',
111 _('Allocate IP sequentially'),
112 _('Allocate IP addresses sequentially, starting from the lowest available address'));
115 o
= s
.taboption('advanced', form
.Flag
, 'boguspriv',
117 _('Do not forward reverse lookups for local networks'));
118 o
.default = o
.enabled
;
120 s
.taboption('advanced', form
.Flag
, 'filterwin2k',
122 _('Do not forward requests that cannot be answered by public name servers'));
125 s
.taboption('advanced', form
.Flag
, 'localise_queries',
126 _('Localise queries'),
127 _('Localise hostname depending on the requesting subnet if multiple IPs are available'));
129 //local have_dnssec_support = luci.util.checklib('/usr/sbin/dnsmasq', 'libhogweed.so');
130 var have_dnssec_support
= true;
132 if (have_dnssec_support
) {
133 o
= s
.taboption('advanced', form
.Flag
, 'dnssec',
137 o
= s
.taboption('advanced', form
.Flag
, 'dnsseccheckunsigned',
138 _('DNSSEC check unsigned'),
139 _('Requires upstream supports DNSSEC; verify unsigned domain responses really come from unsigned domains'));
143 s
.taboption('general', form
.Value
, 'local',
145 _('Local domain specification. Names matching this domain are never forwarded and are resolved from DHCP or hosts files only'));
147 s
.taboption('general', form
.Value
, 'domain',
149 _('Local domain suffix appended to DHCP names and hosts file entries'));
151 s
.taboption('advanced', form
.Flag
, 'expandhosts',
153 _('Add local domain suffix to names served from hosts files'));
155 s
.taboption('advanced', form
.Flag
, 'nonegcache',
156 _('No negative cache'),
157 _('Do not cache negative replies, e.g. for not existing domains'));
159 s
.taboption('advanced', form
.Value
, 'serversfile',
160 _('Additional servers file'),
161 _('This file may contain lines like \'server=/domain/1.2.3.4\' or \'server=1.2.3.4\' for domain-specific or full upstream <abbr title="Domain Name System">DNS</abbr> servers.'));
163 s
.taboption('advanced', form
.Flag
, 'strictorder',
165 _('<abbr title="Domain Name System">DNS</abbr> servers will be queried in the order of the resolvfile')).optional
= true;
167 s
.taboption('advanced', form
.Flag
, 'allservers',
169 _('Query all available upstream <abbr title="Domain Name System">DNS</abbr> servers')).optional
= true;
171 o
= s
.taboption('advanced', form
.DynamicList
, 'bogusnxdomain', _('Bogus NX Domain Override'),
172 _('List of hosts that supply bogus NX domain results'));
175 o
.placeholder
= '67.215.65.132';
178 s
.taboption('general', form
.Flag
, 'logqueries',
180 _('Write received DNS requests to syslog')).optional
= true;
182 o
= s
.taboption('general', form
.DynamicList
, 'server', _('DNS forwardings'),
183 _('List of <abbr title="Domain Name System">DNS</abbr> servers to forward requests to'));
186 o
.placeholder
= '/example.org/10.1.2.3';
189 o
= s
.taboption('general', form
.DynamicList
, 'address', _('Addresses'),
190 _('List of domains to force to an IP address.'));
193 o
.placeholder
= '/router.local/192.168.0.1';
196 o
= s
.taboption('general', form
.Flag
, 'rebind_protection',
197 _('Rebind protection'),
198 _('Discard upstream RFC1918 responses'));
203 o
= s
.taboption('general', form
.Flag
, 'rebind_localhost',
204 _('Allow localhost'),
205 _('Allow upstream responses in the 127.0.0.0/8 range, e.g. for RBL services'));
207 o
.depends('rebind_protection', '1');
210 o
= s
.taboption('general', form
.DynamicList
, 'rebind_domain',
211 _('Domain whitelist'),
212 _('List of domains to allow RFC1918 responses for'));
215 o
.depends('rebind_protection', '1');
216 o
.datatype
= 'host(1)';
217 o
.placeholder
= 'ihost.netflix.com';
220 o
= s
.taboption('advanced', form
.Value
, 'port',
221 _('<abbr title="Domain Name System">DNS</abbr> server port'),
222 _('Listening port for inbound DNS queries'));
229 o
= s
.taboption('advanced', form
.Value
, 'queryport',
230 _('<abbr title="Domain Name System">DNS</abbr> query port'),
231 _('Fixed source port for outbound DNS queries'));
235 o
.placeholder
= _('any');
238 o
= s
.taboption('advanced', form
.Value
, 'dhcpleasemax',
239 _('<abbr title="maximal">Max.</abbr> <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr> leases'),
240 _('Maximum allowed number of active DHCP leases'));
243 o
.datatype
= 'uinteger';
244 o
.placeholder
= _('unlimited');
247 o
= s
.taboption('advanced', form
.Value
, 'ednspacket_max',
248 _('<abbr title="maximal">Max.</abbr> <abbr title="Extension Mechanisms for Domain Name System">EDNS0</abbr> packet size'),
249 _('Maximum allowed size of EDNS.0 UDP packets'));
252 o
.datatype
= 'uinteger';
253 o
.placeholder
= 1280;
256 o
= s
.taboption('advanced', form
.Value
, 'dnsforwardmax',
257 _('<abbr title="maximal">Max.</abbr> concurrent queries'),
258 _('Maximum allowed number of concurrent DNS queries'));
261 o
.datatype
= 'uinteger';
264 o
= s
.taboption('advanced', form
.Value
, 'cachesize',
265 _('Size of DNS query cache'),
266 _('Number of cached DNS entries (max is 10000, 0 is no caching)'));
268 o
.datatype
= 'range(0,10000)';
271 s
.taboption('tftp', form
.Flag
, 'enable_tftp',
272 _('Enable TFTP server')).optional
= true;
274 o
= s
.taboption('tftp', form
.Value
, 'tftp_root',
275 _('TFTP server root'),
276 _('Root directory for files served via TFTP'));
279 o
.depends('enable_tftp', '1');
283 o
= s
.taboption('tftp', form
.Value
, 'dhcp_boot',
284 _('Network boot image'),
285 _('Filename of the boot image advertised to clients'));
288 o
.depends('enable_tftp', '1');
289 o
.placeholder
= 'pxelinux.0';
291 o
= s
.taboption('general', form
.Flag
, 'localservice',
292 _('Local Service Only'),
293 _('Limit DNS service to subnets interfaces on which we are serving DNS.'));
297 o
= s
.taboption('general', form
.Flag
, 'nonwildcard',
299 _('Bind dynamically to interfaces rather than wildcard address (recommended as linux default)'));
303 o
= s
.taboption('general', form
.DynamicList
, 'interface',
304 _('Listen Interfaces'),
305 _('Limit listening to these interfaces, and loopback.'));
308 o
= s
.taboption('general', form
.DynamicList
, 'notinterface',
309 _('Exclude interfaces'),
310 _('Prevent listening on these interfaces.'));
313 o
= s
.taboption('leases', form
.SectionValue
, '__leases__', form
.GridSection
, 'host', null,
314 _('Static leases are used to assign fixed IP addresses and symbolic hostnames to DHCP clients. They are also required for non-dynamic interface configurations where only hosts with a corresponding lease are served.') + '<br />' +
315 _('Use the <em>Add</em> Button to add a new lease entry. The <em>MAC-Address</em> identifies the host, the <em>IPv4-Address</em> specifies the fixed address to use, and the <em>Hostname</em> is assigned as a symbolic name to the requesting host. The optional <em>Lease time</em> can be used to set non-standard host-specific lease time, e.g. 12h, 3d or infinite.'));
322 so
= ss
.option(form
.Value
, 'name', _('Hostname'));
323 so
.datatype
= 'hostname("strict")';
325 so
.write = function(section
, value
) {
326 uci
.set('dhcp', section
, 'name', value
);
327 uci
.set('dhcp', section
, 'dns', '1');
329 so
.remove = function(section
) {
330 uci
.unset('dhcp', section
, 'name');
331 uci
.unset('dhcp', section
, 'dns');
334 so
= ss
.option(form
.Value
, 'mac', _('<abbr title="Media Access Control">MAC</abbr>-Address'));
335 so
.datatype
= 'list(unique(macaddr))';
337 so
.cfgvalue = function(section
) {
338 var macs
= uci
.get('dhcp', section
, 'mac'),
341 if (!Array
.isArray(macs
))
342 macs
= (macs
!= null && macs
!= '') ? macs
.split(/\ss+/) : [];
344 for (var i
= 0, mac
; (mac
= macs
[i
]) != null; i
++)
345 if (/^([0-9a-fA-F]{1,2}):([0-9a-fA-F]{1,2}):([0-9a-fA-F]{1,2}):([0-9a-fA-F]{1,2}):([0-9a-fA-F]{1,2}):([0-9a-fA-F]{1,2})$/.test(mac
))
346 result
.push('%02X:%02X:%02X:%02X:%02X:%02X'.format(
347 parseInt(RegExp
.$1, 16), parseInt(RegExp
.$2, 16),
348 parseInt(RegExp
.$3, 16), parseInt(RegExp
.$4, 16),
349 parseInt(RegExp
.$5, 16), parseInt(RegExp
.$6, 16)));
351 return result
.length
? result
.join(' ') : null;
353 so
.renderWidget = function(section_id
, option_index
, cfgvalue
) {
354 var node
= form
.Value
.prototype.renderWidget
.apply(this, [section_id
, option_index
, cfgvalue
]),
355 ipopt
= this.section
.children
.filter(function(o
) { return o
.option
== 'ip' })[0];
357 node
.addEventListener('cbi-dropdown-change', L
.bind(function(ipopt
, section_id
, ev
) {
358 var mac
= ev
.detail
.value
.value
;
359 if (mac
== null || mac
== '' || !hosts
[mac
] || !hosts
[mac
].ipv4
)
362 var ip
= ipopt
.formvalue(section_id
);
363 if (ip
!= null && ip
!= '')
366 var node
= ipopt
.map
.findElement('id', ipopt
.cbid(section_id
));
368 L
.dom
.callClassMethod(node
, 'setValue', hosts
[mac
].ipv4
);
369 }, this, ipopt
, section_id
));
373 Object
.keys(hosts
).forEach(function(mac
) {
374 var hint
= hosts
[mac
].name
|| hosts
[mac
].ipv4
;
375 so
.value(mac
, hint
? '%s (%s)'.format(mac
, hint
) : mac
);
378 so
= ss
.option(form
.Value
, 'ip', _('<abbr title="Internet Protocol Version 4">IPv4</abbr>-Address'));
379 so
.datatype
= 'or(ip4addr,"ignore")';
380 so
.validate = function(section
, value
) {
381 var mac
= this.map
.lookupOption('mac', section
),
382 name
= this.map
.lookupOption('name', section
),
383 m
= mac
? mac
[0].formvalue(section
) : null,
384 n
= name
? name
[0].formvalue(section
) : null;
386 if ((m
== null || m
== '') && (n
== null || n
== ''))
387 return _('One of hostname or mac address must be specified!');
391 Object
.keys(hosts
).forEach(function(mac
) {
392 if (hosts
[mac
].ipv4
) {
393 var hint
= hosts
[mac
].name
;
394 so
.value(hosts
[mac
].ipv4
, hint
? '%s (%s)'.format(hosts
[mac
].ipv4
, hint
) : hosts
[mac
].ipv4
);
398 so
= ss
.option(form
.Value
, 'leasetime', _('Lease time'));
401 so
= ss
.option(form
.Value
, 'duid', _('<abbr title="The DHCP Unique Identifier">DUID</abbr>'));
402 so
.datatype
= 'and(rangelength(20,36),hexstring)';
403 Object
.keys(duids
).forEach(function(duid
) {
404 so
.value(duid
, '%s (%s)'.format(duid
, duids
[duid
].name
|| '?'));
407 so
= ss
.option(form
.Value
, 'hostid', _('<abbr title="Internet Protocol Version 6">IPv6</abbr>-Suffix (hex)'));
409 o
= s
.taboption('leases', CBILeaseStatus
, '__status__');
411 return m
.render().then(function(mapEl
) {
412 L
.Poll
.add(function() {
413 return callDHCPLeases(4).then(function(leases
) {
414 cbi_update_table(mapEl
.querySelector('#lease_status_table'),
415 leases
.map(function(lease
) {
418 if (lease
.expires
=== false)
419 exp
= E('em', _('unlimited'));
420 else if (lease
.expires
<= 0)
421 exp
= E('em', _('expired'));
423 exp
= '%t'.format(lease
.expires
);
426 lease
.hostname
|| '?',
432 E('em', _('There are no active leases')));