'use strict';'require view';'require dom';'require poll';'require rpc';'require uci';'require form';'require network';'require validation';'require tools.widgets as widgets';'require tools.dnsrecordhandlers as drh';const callHostHints=rpc.declare({object:'luci-rpc',method:'getHostHints',expect:{'':{}}});function validateHostname(sid,s){if(!s)return true;if(s.length>256)
return _('Expecting: %s').format(_('valid hostname'));const labels=s.replace(/^\*?\.?|\.$/g,'').split(/\./);for(const label of labels){if(!label.match(/^[a-z0-9_](?:[a-z0-9-]{0,61}[a-z0-9])?$/i))
return _('Expecting: %s').format(_('valid hostname'));}
return true;}
function validateAddressList(sid,s){if(!s)return true;const m=s.match(/^\/(.+)\/$/);const names=m?m[1].split(/\//):[s];for(const name of names){const res=validateHostname(sid,name);if(res!==true)
return res;}
return true;}
function validateServerSpec(sid,s){if(!s)return true;let m=s.match(/^(\/.*\/)?(.*)$/);if(!m)
return _('Expecting: %s').format(_('valid hostname'));if(m[1]!='//'&&m[1]!='/#/'){const res=validateAddressList(sid,m[1]);if(res!==true)
return res;}
if(m[2]==''||m[2]=='#')
return true;m=m[2].match(/^([0-9a-f:.]+)(?:%[^#@]+)?(?:#(\d+))?(?:@([0-9a-f:.]+)(?:@[^#]+)?(?:#(\d+))?)?$/);if(!m)
return _('Expecting: %s').format(_('valid IP address'));if(validation.parseIPv4(m[1])){if(m[3]!=null&&!validation.parseIPv4(m[3]))
return _('Expecting: %s').format(_('valid IPv4 address'));}
else if(validation.parseIPv6(m[1])){if(m[3]!=null&&!validation.parseIPv6(m[3]))
return _('Expecting: %s').format(_('valid IPv6 address'));}
else{return _('Expecting: %s').format(_('valid IP address'));}
if((m[2]!=null&&+m[2]>65535)||(m[4]!=null&&+m[4]>65535))
return _('Expecting: %s').format(_('valid port value'));return true;}
return view.extend({load(){return Promise.all([callHostHints(),uci.load('firewall')]);},render([hosts]){let m,s,o,ss,so,dnss;let noi18nstrings={etc_hosts:'<code>/etc/hosts</code>',localhost_v6:'<code>::1</code>',loopback_slash_8_v4:'<code>127.0.0.0/8</code>',not_found:'<code>Not found</code>',nxdomain:'<code>NXDOMAIN</code>',rfc_1918_link:'<a href="https://www.rfc-editor.org/rfc/rfc1918">RFC1918</a>',rfc_4193_link:'<a href="https://www.rfc-editor.org/rfc/rfc4193">RFC4193</a>',rfc_4291_link:'<a href="https://www.rfc-editor.org/rfc/rfc4291">RFC4291</a>',rfc_6303_link:'<a href="https://www.rfc-editor.org/rfc/rfc6303">RFC6303</a>',reverse_arpa:'<code>*.IN-ADDR.ARPA,*.IP6.ARPA</code>',servers_file_entry01:'<code>server=1.2.3.4</code>',servers_file_entry02:'<code>server=/domain/1.2.3.4</code>',};const recordtypes=['ANY','A','AAAA','ALIAS','CAA','CERT','CNAME','DS','HINFO','HIP','HTTPS','KEY','LOC','MX','NAPTR','NS','OPENPGPKEY','PTR','RP','SIG','SOA','SRV','SSHFP','SVCB','TLSA','TXT','URI',]
function customi18n(template,values){if(!values)
values=noi18nstrings;return template.replace(/\{(\w+)\}/g,(match,key)=>values[key]||match);};m=new form.Map('dhcp',_('DNS'));s=m.section(form.TypedSection,'dnsmasq');s.anonymous=false;s.addremove=true;s.addbtntitle=_('Add server instance','Dnsmasq instance');s.renderContents=function(){const renderTask=form.TypedSection.prototype.renderContents.apply(this,arguments);const sections=this.cfgsections();return Promise.resolve(renderTask).then(function(nodes){if(sections.length<2){nodes.querySelector('#cbi-dhcp-dnsmasq > h3').remove();nodes.querySelector('#cbi-dhcp-dnsmasq > .cbi-section-remove').remove();}
else{nodes.querySelectorAll('#cbi-dhcp-dnsmasq > .cbi-section-remove').forEach(function(div,i){const section=uci.get('dhcp',sections[i]);const hline=div.nextElementSibling;const btn=div.firstElementChild;if(!section||section['.anonymous']){hline.innerText=i?_('Unnamed instance #%d','Dnsmasq instance').format(i+1):_('Default instance','Dnsmasq instance');btn.innerText=i?_('Remove instance #%d','Dnsmasq instance').format(i+1):_('Remove default instance','Dnsmasq instance');}
else{hline.innerText=_('Instance "%q"','Dnsmasq instance').format(section['.name']);btn.innerText=_('Remove instance "%q"','Dnsmasq instance').format(section['.name']);}});}
nodes.querySelector('#cbi-dhcp-dnsmasq > .cbi-section-create input').placeholder=_('New instance name…','Dnsmasq instance');return nodes;});};s.tab('general',_('General'));s.tab('cache',_('Cache'));s.tab('devices',_('Devices &amp; Ports'));s.tab('dnsrecords',_('DNS Records'));s.tab('dnssecopt',_('DNSSEC'));s.tab('filteropts',_('Filter'));s.tab('forward',_('Forwards'));s.tab('limits',_('Limits'));s.tab('logging',_('Log'));s.tab('files',_('Resolv &amp; Hosts Files'));s.tab('ipsets',_('IP Sets'));o=s.taboption('general',form.Value,'local',_('Resolve these locally'),_('Never forward these matching domains or subdomains; resolve from DHCP or hosts files only.'));o.placeholder='/internal.example.com/private.example.com/example.org';s.taboption('general',form.Value,'domain',_('Local domain'),_('Local domain suffix appended to DHCP names and hosts file entries.'));s.taboption('general',form.Flag,'expandhosts',_('Expand hosts'),_('Add local domain suffix to names served from hosts files.'));o=s.taboption('general',form.DynamicList,'address',_('Addresses'),_('Resolve specified FQDNs to an IP.')+'<br />'+
customi18n(_('Syntax: {code_syntax}.'),{code_syntax:'<code>/fqdn[/fqdn…]/[ipaddr]</code>'})+'<br />'+
customi18n(_('{example_nx} returns {nxdomain}.','hint: <code>/example.com/</code> returns <code>NXDOMAIN</code>.'),{example_nx:'<code>/example.com/</code>',nxdomain:'<code>NXDOMAIN</code>'})+'<br />'+
customi18n(_('{any_domain} matches any domain (and returns {nxdomain}).','hint: <code>/#/</code> matches any domain (and returns NXDOMAIN).'),{any_domain:'<code>/#/</code>',nxdomain:'<code>NXDOMAIN</code>'})+'<br />'+
customi18n(_('{example_null} returns {null_addr} addresses ({null_ipv4}, {null_ipv6}) for {example_com} and its subdomains.','hint: <code>/example.com/#</code> returns NULL addresses (<code>0.0.0.0</code>, <code>::</code>) for example.com and its subdomains.'),{example_null:'<code>/example.com/#</code>',null_addr:'<code>NULL</code>',null_ipv4:'<code>0.0.0.0</code>',null_ipv6:'<code>::</code>',example_com:'<code>example.com</code>',}));o.optional=true;o.placeholder='/router.local/router.lan/192.168.0.1';o=s.taboption('general',form.Flag,'allservers',_('All servers'),_('Query all available upstream resolvers.')+' '+_('First answer wins.'));o.optional=true;o=s.taboption('cache',form.MultiValue,'cache_rr',_('Cache arbitrary RR'),_('By default, dnsmasq caches A, AAAA, CNAME and SRV DNS record types.')+'<br/>'+
_('This option adds additional record types to the cache.'));o.optional=true;o.create=true;o.multiple=true;o.display_size=5;recordtypes.forEach(r=>{o.value(r);});o=s.taboption('devices',form.Flag,'nonwildcard',_('Non-wildcard'),_('Bind only to configured interface addresses, instead of the wildcard address.'));o.default=o.enabled;o.optional=false;o.rmempty=true;o=s.taboption('devices',widgets.NetworkSelect,'interface',_('Listen interfaces'),_('Listen only on the specified interfaces, and loopback if not excluded explicitly.'));o.multiple=true;o.nocreate=true;o=s.taboption('devices',widgets.IPSelect,'listen_address',_('Listen addresses'),_('Listen only on the specified addresses.'));o.multiple=true;o=s.taboption('devices',widgets.NetworkSelect,'notinterface',_('Exclude interfaces'),_('Do not listen on the specified interfaces.'));o.loopback=true;o.multiple=true;o.nocreate=true;o=s.taboption('devices',form.Value,'port',_('DNS server port'),_('Listening port for inbound DNS queries.'));o.optional=true;o.datatype='port';o.placeholder=53;o=s.taboption('devices',form.Value,'queryport',_('DNS query port'),_('Fixed source port for outbound DNS queries.'));o.optional=true;o.datatype='port';o.placeholder=_('any');o=s.taboption('devices',form.Value,'minport',_('Minimum source port #'),_('Min valid value %s.').format('<code>1024</code>')+' '+_('Useful for systems behind firewalls.'));o.optional=true;o.datatype='port';o.placeholder=1024;o.depends('queryport','');o=s.taboption('devices',form.Value,'maxport',_('Maximum source port #'),_('Max valid value %s.').format('<code>65535</code>')+' '+_('Useful for systems behind firewalls.'));o.optional=true;o.datatype='port';o.placeholder=50000;o.depends('queryport','');o=s.taboption('dnsrecords',form.SectionValue,'__dnsrecords__',form.TypedSection,'__dnsrecords__');dnss=o.subsection;dnss.anonymous=true;dnss.cfgsections=function(){return['__dnsrecords__']};dnss.tab('hosts',_('Hostnames'));dnss.tab('srvhosts',_('SRV'));dnss.tab('mxhosts',_('MX'));dnss.tab('cnamehosts',_('CNAME'));dnss.tab('dnsrr',_('DNS-RR'));o=dnss.taboption('srvhosts',form.SectionValue,'__srvhosts__',form.TableSection,'srvhost',null,_('Bind service records to a domain name: specify the location of services. See <a href="%s">RFC2782</a>.').format('https://datatracker.ietf.org/doc/html/rfc2782')
+'<br />'+_('_service: _sip, _ldap, _imap, _stun, _xmpp-client, … . (Note: while _http is possible, no browsers support SRV records.)')
+'<br />'+_('_proto: _tcp, _udp, _sctp, _quic, … .')
+'<br />'+_('You may add multiple records for the same Target.')
+'<br />'+_('Larger weights (of the same prio) are given a proportionately higher probability of being selected.'));ss=o.subsection;ss.addremove=true;ss.anonymous=true;ss.sortable=true;ss.rowcolors=true;so=ss.option(form.Value,'srv',_('SRV'),_('Syntax:')+' '+'<code>_service._proto.example.com.</code>');so.rmempty=false;so.datatype='hostname';so.placeholder='_sip._tcp.example.com.';so=ss.option(form.Value,'target',_('Target'),_('CNAME or fqdn'));so.rmempty=false;so.datatype='hostname';so.placeholder='sip.example.com.';so=ss.option(form.Value,'port',_('Port'));so.rmempty=false;so.datatype='port';so.placeholder='5060';so=ss.option(form.Value,'class',_('Priority'),_('Ordinal: lower comes first.'));so.rmempty=true;so.datatype='range(0,65535)';so.placeholder='10';so=ss.option(form.Value,'weight',_('Weight'));so.rmempty=true;so.datatype='range(0,65535)';so.placeholder='50';o=dnss.taboption('mxhosts',form.SectionValue,'__mxhosts__',form.TableSection,'mxhost',null,_('Bind service records to a domain name: specify the location of services.')
+'<br />'+_('You may add multiple records for the same domain.'));ss=o.subsection;ss.addremove=true;ss.anonymous=true;ss.sortable=true;ss.rowcolors=true;ss.nodescriptions=true;so=ss.option(form.Value,'domain',_('Domain'));so.rmempty=false;so.datatype='hostname';so.placeholder='example.com.';so=ss.option(form.Value,'relay',_('Relay'));so.rmempty=false;so.datatype='hostname';so.placeholder='relay.example.com.';so=ss.option(form.Value,'pref',_('Priority'),_('Ordinal: lower comes first.'));so.rmempty=true;so.datatype='range(0,65535)';so.placeholder='0';o=dnss.taboption('cnamehosts',form.SectionValue,'__cname__',form.TableSection,'cname',null,_('Set an alias for a hostname.'));ss=o.subsection;ss.addremove=true;ss.anonymous=true;ss.sortable=true;ss.rowcolors=true;ss.nodescriptions=true;so=ss.option(form.Value,'cname',_('Domain'));so.rmempty=false;so.validate=validateHostname;so.placeholder='www.example.com.';so=ss.option(form.Value,'target',_('Target'));so.rmempty=false;so.datatype='hostname';so.placeholder='example.com.';o=dnss.taboption('hosts',form.SectionValue,'__hosts__',form.GridSection,'domain',null,_('Hostnames are used to bind a domain name to an IP address. This setting is redundant for hostnames already configured with static leases, but it can be useful to rebind an FQDN.'));ss=o.subsection;ss.addremove=true;ss.anonymous=true;ss.sortable=true;so=ss.option(form.Value,'name',_('Hostname'));so.rmempty=false;so.datatype='hostname';so=ss.option(form.Value,'ip',_('IP address'));so.rmempty=false;so.datatype='ipaddr("nomask")';const ipaddrs={};Object.keys(hosts).forEach(function(mac){for(const addr of L.toArray(hosts[mac].ipaddrs||hosts[mac].ipv4))
ipaddrs[addr]=hosts[mac].name||mac;});L.sortedKeys(ipaddrs,null,'addr').forEach(function(ipv4){so.value(ipv4,'%s (%s)'.format(ipv4,ipaddrs[ipv4]));});o=dnss.taboption('dnsrr',form.SectionValue,'__dnsrr__',form.GridSection,'dnsrr',null,_('Set an arbitrary resource record (RR) type.')+'<br/>'+
_('Hexdata is automatically en/decoded on save and load'));ss=o.subsection;ss.addremove=true;ss.anonymous=true;ss.sortable=true;ss.rowcolors=true;ss.nodescriptions=true;function hexdecodeload(section_id){let value=uci.get('dhcp',section_id,'hexdata')||'';value=value.replace(/[\s:]/g,'');let decodedString='';for(let i=0;i<value.length;i+=2){decodedString+=String.fromCharCode(parseInt(value.substr(i,2),16));}
return decodedString;}
function hexencodesave(section,value){if(!value||value.length===0){uci.unset('dhcp',section,'hexdata');return;}
const encodedArr=value.split('').map(c=>c.charCodeAt(0).toString(16).padStart(2,'0')).join('');uci.set('dhcp',section,this.option,encodedArr);}
so=ss.option(form.Value,'rrname',_('Resource Record Name'));so.rmempty=false;so.datatype='hostname';so.placeholder='svcb.example.com.';so=ss.option(form.Value,'rrnumber',_('Resource Record Number'));so.rmempty=false;so.datatype='uinteger';so.placeholder='64';so=ss.option(form.Value,'_hexdata',_('Raw Data'));so.rmempty=true;so.datatype='string';so.placeholder='free-form string';so.load=hexdecodeload;so.write=hexencodesave;so.modalonly=true;so.depends({rrnumber:'65','!reverse':true});so=ss.option(form.DummyValue,'hexdata',_('Hex Data'));so.width='50%';so.rawhtml=true;so.load=function(section_id){let hexdata=uci.get('dhcp',section_id,'hexdata')||'';hexdata=hexdata.replace(/[:]/g,'');return hexdata.replace(/(.{2})/g,'$1 ');};function writetype65(section_id,value){let rrnum=uci.get('dhcp',section_id,'rrnumber');if(rrnum!=='65')return;let priority=parseInt(this.section.formvalue(section_id,'_svc_priority'),10);let target=this.section.formvalue(section_id,'_svc_target')||'.';let params=value.trim().split('\n').map(l=>l.trim()).filter(Boolean);const hex=drh.buildSvcbHex(priority,target,params);uci.set('dhcp',section_id,'hexdata',hex);};function loadtype65(section_id){let rrnum=uci.get('dhcp',section_id,'rrnumber');if(rrnum!=='65')return null;let hexdata=uci.get('dhcp',section_id,'hexdata');return drh.parseSvcbHex(hexdata);};so=ss.option(form.Value,'_svc_priority',_('Svc Priority'));so.placeholder=1;so.datatype='and(uinteger,min(0),max(65535))'
so.modalonly=true;so.depends({rrnumber:'65'});so.write=writetype65;so.load=function(section_id){const parsed=loadtype65(section_id);return parsed?.priority?.toString()||'';};so=ss.option(form.Value,'_svc_target',_('Svc Target'));so.placeholder='svc.example.com.';so.dataype='hostname';so.modalonly=true;so.depends({rrnumber:'65'});so.write=writetype65;so.load=function(section_id){const parsed=loadtype65(section_id);return parsed?.target||'';};so=ss.option(form.TextValue,'_svc_params',_('Svc Parameters'));so.placeholder='alpn=h2,h3\nipv4hint=192.0.2.1,192.0.2.2\nipv6hint=2001:db8::1,2001:db8::2\nport=8000';so.modalonly=true;so.rows=4;so.depends({rrnumber:'65'});so.write=writetype65;so.load=function(section_id){const parsed=loadtype65(section_id);return parsed?.params?.join('\n')||'';};if(L.hasSystemFeature('dnsmasq','dnssec')){o=s.taboption('dnssecopt',form.Flag,'dnssec',_('DNSSEC'),_('Validate DNS replies and cache DNSSEC data, requires upstream to support DNSSEC.'));o.optional=true;o=s.taboption('dnssecopt',form.Flag,'dnsseccheckunsigned',_('DNSSEC check unsigned'),_('Verify unsigned domain responses really come from unsigned domains.'));o.default=o.enabled;o.optional=true;}
s.taboption('filteropts',form.Flag,'domainneeded',_('Domain required'),_('Never forward DNS queries which lack dots or domain parts.')+'<br />'+
customi18n(_('Names not in {etc_hosts} are answered {not_found}.')));o=s.taboption('filteropts',form.Flag,'rebind_protection',_('Rebind protection'),customi18n(_('Discard upstream responses containing {rfc_1918_link} addresses.'))+'<br />'+
customi18n(_('Discard also upstream responses containing {rfc_4193_link}, Link-Local and private IPv4-Mapped {rfc_4291_link} IPv6 Addresses.')));o.rmempty=false;o=s.taboption('filteropts',form.Flag,'rebind_localhost',_('Allow localhost'),customi18n(_('Exempt {loopback_slash_8_v4} and {localhost_v6} from rebinding checks, e.g. for <abbr title="Real-time Block List">RBL</abbr> services.')));o.depends('rebind_protection','1');o=s.taboption('filteropts',form.DynamicList,'rebind_domain',_('Domain whitelist'),customi18n(_('List of domains to allow {rfc_1918_link} responses for.')));o.depends('rebind_protection','1');o.optional=true;o.placeholder='ihost.netflix.com';o.validate=validateAddressList;o=s.taboption('filteropts',form.Flag,'localservice',_('Local service only'),_('Accept DNS queries only from hosts whose address is on a local subnet.'));o.optional=false;o.rmempty=false;o=s.taboption('filteropts',form.Flag,'boguspriv',_('Filter private'),customi18n(_('Reject reverse lookups to {rfc_6303_link} IP ranges ({reverse_arpa}) not in {etc_hosts}.')));o.default=o.enabled;s.taboption('filteropts',form.Flag,'filterwin2k',_('Filter SRV/SOA service discovery'),_('Filters SRV/SOA service discovery, to avoid triggering dial-on-demand links.')+'<br />'+
_('May prevent VoIP or other services from working.'));o=s.taboption('filteropts',form.Flag,'filter_aaaa',_('Filter IPv6 AAAA records'),_('Remove IPv6 addresses from the results and only return IPv4 addresses.')+'<br />'+
_('Can be useful if ISP has IPv6 nameservers but does not provide IPv6 routing.'));o.optional=true;o=s.taboption('filteropts',form.Flag,'filter_a',_('Filter IPv4 A records'),_('Remove IPv4 addresses from the results and only return IPv6 addresses.'));o.optional=true;o=s.taboption('filteropts',form.MultiValue,'filter_rr',_('Filter arbitrary RR'),_('Removes records of the specified type(s) from answers.'));o.optional=true;o.create=true;o.multiple=true;o.display_size=5;recordtypes.forEach(r=>{o.value(r);});s.taboption('filteropts',form.Flag,'localise_queries',_('Localise queries'),customi18n(_('Limit response records (from {etc_hosts}) to those that fall within the subnet of the querying interface.'))+'<br />'+
_('This prevents unreachable IPs in subnets not accessible to you.')+'<br />'+
_('Note: IPv4 only.'));s.taboption('filteropts',form.Flag,'nonegcache',_('No negative cache'),_('Do not cache negative replies, e.g. for non-existent domains.'));o=s.taboption('filteropts',form.DynamicList,'bogusnxdomain',customi18n(_('IPs to override with {nxdomain}')),customi18n(_('Transform replies which contain the specified addresses or subnets into {nxdomain} responses.')));o.optional=true;o.placeholder='64.94.110.11';o=s.taboption('forward',form.DynamicList,'server',_('DNS Forwards'),_('Forward specific domain queries to specific upstream servers.'));o.optional=true;o.placeholder='/*.example.org/10.1.2.3';o.validate=validateServerSpec;o=s.taboption('forward',form.Value,'serversfile',_('Additional servers file'),customi18n(_('File listing upstream resolvers, optionally domain-specific, e.g. {servers_file_entry01}, {servers_file_entry02}.')));o.placeholder='/etc/dnsmasq.servers';o=s.taboption('forward',form.Value,'addmac',_('Add requestor MAC'),_('Add the MAC address of the requestor to DNS queries which are forwarded upstream.')+' '+'<br />'+
_('%s uses the default MAC address format encoding').format('<code>enabled</code>')+' '+'<br />'+
_('%s uses an alternative encoding of the MAC as base64').format('<code>base64</code>')+' '+'<br />'+
_('%s uses a human-readable encoding of hex-and-colons').format('<code>text</code>'));o.optional=true;o.value('',_('off'));o.value('1',_('enabled (default)'));o.value('base64');o.value('text');s.taboption('forward',form.Flag,'stripmac',_('Remove MAC address before forwarding query'),_('Remove any MAC address information already in downstream queries before forwarding upstream.'));o=s.taboption('forward',form.Value,'addsubnet',_('Add subnet address to forwards'),_('Add a subnet address to the DNS queries which are forwarded upstream, leaving this value empty disables the feature.')+' '+
_('If an address is specified in the flag, it will be used, otherwise, the address of the requestor will be used.')+' '+
_('The amount of the address forwarded depends on the prefix length parameter: 32 (128 for IPv6) forwards the whole address, zero forwards none of it but still marks the request so that no upstream nameserver will add client address information either.')+' '+'<br />'+
_('The default (%s) is zero for both IPv4 and IPv6.').format('<code>0,0</code>')+' '+'<br />'+
_('%s adds the /24 and /96 subnets of the requestor for IPv4 and IPv6 requestors, respectively.').format('<code>24,96</code>')+' '+'<br />'+
_('%s adds 1.2.3.0/24 for IPv4 requestors and ::/0 for IPv6 requestors.').format('<code>1.2.3.4/24</code>')+' '+'<br />'+
_('%s adds 1.2.3.0/24 for both IPv4 and IPv6 requestors.').format('<code>1.2.3.4/24,1.2.3.4/24</code>'));o.optional=true;s.taboption('forward',form.Flag,'stripsubnet',_('Remove subnet address before forwarding query'),_('Remove any subnet address already present in a downstream query before forwarding it upstream.'));o=s.taboption('limits',form.Value,'ednspacket_max',_('Max. EDNS0 packet size'),_('Maximum allowed size of EDNS0 UDP packets.'));o.optional=true;o.datatype='uinteger';o.placeholder=1280;o=s.taboption('limits',form.Value,'dnsforwardmax',_('Max. concurrent queries'),_('Maximum allowed number of concurrent DNS queries.'));o.optional=true;o.datatype='uinteger';o.placeholder=150;o=s.taboption('limits',form.Value,'cachesize',_('Size of DNS query cache'),_('Number of cached DNS entries, 10000 is maximum, 0 is no caching.'));o.optional=true;o.datatype='range(0,10000)';o.placeholder=150;o=s.taboption('limits',form.Value,'min_cache_ttl',_('Min cache TTL'),_('Extend short TTL values to the seconds value given when caching them. Use with caution.')+
_(' (Max 1h == 3600)'));o.optional=true;o.placeholder=60;o=s.taboption('limits',form.Value,'max_cache_ttl',_('Max cache TTL'),_('Set a maximum seconds TTL value for entries in the cache.'));o.optional=true;o.placeholder=3600;o=s.taboption('logging',form.Flag,'logqueries',_('Log queries'),_('Write received DNS queries to syslog.')+' '+_('Dump cache on SIGUSR1, include requesting IP.'));o.optional=true;o=s.taboption('logging',form.Value,'logfacility',_('Log facility'),_('Set log class/facility for syslog entries.'));o.optional=true;o.value('KERN');o.value('USER');o.value('MAIL');o.value('DAEMON');o.value('AUTH');o.value('LPR');o.value('NEWS');o.value('UUCP');o.value('CRON');o.value('LOCAL0');o.value('LOCAL1');o.value('LOCAL2');o.value('LOCAL3');o.value('LOCAL4');o.value('LOCAL5');o.value('LOCAL6');o.value('LOCAL7');o.value('-',_('stderr'));o=s.taboption('files',form.Flag,'noresolv',_('Ignore resolv file'));o.optional=true;o=s.taboption('files',form.Value,'resolvfile',_('Resolv file'),_('File with upstream resolvers.'));o.depends('noresolv','0');o.placeholder='/tmp/resolv.conf.d/resolv.conf.auto';o.optional=true;o=s.taboption('files',form.Flag,'strictorder',_('Strict order'),_('Query upstream resolvers in the order they appear in the resolv file.'));o.optional=true;o=s.taboption('files',form.Flag,'ignore_hosts_dir',_('Ignore hosts files directory'),_('On: use instance specific hosts file only')+'<br/>'+
_('Off: use all files in the directory including the instance specific hosts file'));o.optional=true;o=s.taboption('files',form.Flag,'nohosts',customi18n(_('Ignore {etc_hosts} file')));o.optional=true;o=s.taboption('files',form.DynamicList,'addnhosts',_('Additional hosts files'));o.optional=true;o.placeholder='/etc/dnsmasq.hosts';o=s.taboption('ipsets',form.SectionValue,'__ipsets__',form.GridSection,'ipset',null,_('List of IP sets to populate with the IPs of DNS lookup results of the FQDNs also specified here.')+'<br />'+
_('The netfilter components below are only regarded when running fw4.'));ss=o.subsection;ss.addremove=true;ss.anonymous=true;ss.sortable=true;ss.rowcolors=true;ss.nodescriptions=true;ss.modaltitle=_('Edit IP set');so=ss.option(form.DynamicList,'name',_('Name of the set'));uci.sections('firewall','ipset',function(s){if(typeof(s.name)=='string')
so.value(s.name,s.comment?'%s (%s)'.format(s.name,s.comment):s.name);});so.rmempty=false;so.editable=false;so.datatype='string';so=ss.option(form.DynamicList,'domain',_('FQDN'));so.rmempty=false;so.editable=false;so.datatype='hostname';so=ss.option(form.Value,'table',_('Netfilter table name'),_('Defaults to fw4.'));so.editable=false;so.placeholder='fw4';so.rmempty=true;so=ss.option(form.ListValue,'table_family',_('Table IP family'),_('Defaults to IPv4+6.')+' '+_('Can be hinted by adding 4 or 6 to the name.')+'<br />'+
_('Adding an IPv6 to an IPv4 set and vice-versa silently fails.'));so.editable=false;so.rmempty=true;so.value('inet',_('IPv4+6'));so.value('ip',_('IPv4'));so.value('ip6',_('IPv6'));return m.render();}});