luci-app-olsr: handle empty result for non-status tables
[oweals/luci.git] / applications / luci-app-unbound / luasrc / model / cbi / unbound / configure.lua
1 -- Copyright 2008 Steven Barth <steven@midlink.org>
2 -- Copyright 2016 Eric Luehrsen <ericluehrsen@gmail.com>
3 -- Copyright 2016 Dan Luedtke <mail@danrl.com>
4 -- Licensed to the public under the Apache License 2.0.
5
6 local m1, s1
7 local ena, mcf, lci, lsv
8 local rlh, rpv, vld, nvd, eds, prt, tlm
9 local ctl, dlk, dom, dty, lfq, wfq, exa
10 local dp6, d64, pfx, qry, qrs
11 local pro, tgr, rsc, rsn, ag2, stt
12 local rpn, din, ath
13
14 local ut = require "luci.util"
15 local sy = require "luci.sys"
16 local ht = require "luci.http"
17 local ds = require "luci.dispatcher"
18 local ucl = luci.model.uci.cursor()
19 local valman = ucl:get_first("unbound", "unbound", "manual_conf")
20
21 m1 = Map("unbound")
22 s1 = m1:section(TypedSection, "unbound", translate("Recursive DNS"),
23     translatef("Unbound <a href=\"%s\" target=\"_blank\">(NLnet Labs)</a>"
24     .. " is a validating, recursive, and caching DNS resolver"
25     .. " <a href=\"%s\" target=\"_blank\">(help)</a>.",
26     "https://www.unbound.net/",
27     "https://github.com/openwrt/packages/blob/master/net/unbound/files/README.md"))
28
29 s1.addremove = false
30 s1.anonymous = true
31
32 --LuCI, Unbound, or Not
33 s1:tab("basic", translate("Basic"))
34
35
36 if (valman == "0") then
37     -- Not in manual configuration mode; show UCI
38     s1:tab("advanced", translate("Advanced"))
39     s1:tab("DHCP", translate("DHCP"))
40     s1:tab("resource", translate("Resource"))
41 end
42
43
44 --Basic Tab, unconditional pieces
45 ena = s1:taboption("basic", Flag, "enabled", translate("Enable Unbound"),
46     translate("Enable the initialization scripts for Unbound"))
47 ena.rmempty = false
48
49 mcf = s1:taboption("basic", Flag, "manual_conf", translate("Manual Conf"),
50     translate("Skip UCI and use /etc/unbound/unbound.conf"))
51 mcf.rmempty = false
52
53
54 if (valman == "0") then
55     -- Not in manual configuration mode; show UCI
56     --Basic Tab
57     lsv = s1:taboption("basic", Flag, "localservice",
58         translate("Local Service"),
59         translate("Accept queries only from local subnets"))
60     lsv.rmempty = false
61
62     vld = s1:taboption("basic", Flag, "validator",
63         translate("Enable DNSSEC"),
64         translate("Enable the DNSSEC validator module"))
65     vld.rmempty = false
66
67     nvd = s1:taboption("basic", Flag, "validator_ntp",
68         translate("DNSSEC NTP Fix"),
69         translate("Break the loop where DNSSEC needs NTP and NTP needs DNS"))
70     nvd.optional = true
71     nvd:depends("validator", true)
72
73     prt = s1:taboption("basic", Value, "listen_port",
74         translate("Listening Port"),
75         translate("Choose Unbounds listening port"))
76     prt.datatype = "port"
77     prt.placeholder = "53"
78
79     --Avanced Tab
80     rlh = s1:taboption("advanced", Flag, "rebind_localhost",
81         translate("Filter Localhost Rebind"),
82         translate("Protect against upstream response of 127.0.0.0/8"))
83     rlh.rmempty = false
84
85     rpv = s1:taboption("advanced", ListValue, "rebind_protection",
86         translate("Filter Private Rebind"),
87         translate("Protect against upstream responses within local subnets"))
88     rpv:value("0", translate("No Filter"))
89     rpv:value("1", translate("Filter Private Address"))
90     rpv:value("2", translate("Filter Entire Subnet"))
91     rpv.rmempty = false
92
93     d64 = s1:taboption("advanced", Flag, "dns64", translate("Enable DNS64"),
94         translate("Enable the DNS64 module"))
95     d64.rmempty = false
96
97     pfx = s1:taboption("advanced", Value, "dns64_prefix",
98         translate("DNS64 Prefix"),
99         translate("Prefix for generated DNS64 addresses"))
100     pfx.datatype = "ip6addr"
101     pfx.placeholder = "64:ff9b::/96"
102     pfx.optional = true
103     pfx:depends("dns64", true)
104
105     din = s1:taboption("advanced", DynamicList, "domain_insecure",
106         translate("Domain Insecure"),
107         translate("List domains to bypass checks of DNSSEC"))
108     din:depends("validator", true)
109
110     ag2 = s1:taboption("advanced", Value, "root_age",
111         translate("Root DSKEY Age"),
112         translate("Limit days between RFC5011 copies to reduce flash writes"))
113     ag2.datatype = "and(uinteger,min(1),max(99))"
114     ag2:value("3", "3")
115     ag2:value("9", "9 ("..translate("default")..")")
116     ag2:value("12", "12")
117     ag2:value("24", "24")
118     ag2:value("99", "99 ("..translate("never")..")")
119
120     tgr = s1:taboption("advanced", Value, "trigger_interface",
121         translate("Trigger Networks"),
122         translate("Networks that may trigger Unbound to reload (avoid wan6)"))
123     tgr.template = "cbi/network_netlist"
124     tgr.widget = "checkbox"
125     tgr.rmempty = true
126     tgr.cast = "string"
127     tgr.nocreate = true
128
129     --DHCP Tab
130     dlk = s1:taboption("DHCP", ListValue, "dhcp_link",
131         translate("DHCP Link"),
132         translate("Link to supported programs to load DHCP into DNS"))
133     dlk:value("none", translate("No Link"))
134     dlk:value("dnsmasq", "dnsmasq")
135     dlk:value("odhcpd", "odhcpd")
136     dlk.rmempty = false
137
138     dp6 = s1:taboption("DHCP", Flag, "dhcp4_slaac6",
139         translate("DHCPv4 to SLAAC"),
140         translate("Use DHCPv4 MAC to discover IP6 hosts SLAAC (EUI64)"))
141     dp6.optional = true
142     dp6:depends("dhcp_link", "odhcpd")
143
144     dom = s1:taboption("DHCP", Value, "domain",
145         translate("Local Domain"),
146         translate("Domain suffix for this router and DHCP clients"))
147     dom.placeholder = "lan"
148     dom.optional = true
149     dom:depends("dhcp_link", "none")
150     dom:depends("dhcp_link", "odhcpd")
151
152     dty = s1:taboption("DHCP", ListValue, "domain_type",
153         translate("Local Domain Type"),
154         translate("How to treat queries of this local domain"))
155     dty.optional = true
156     dty:value("deny", translate("Denied (nxdomain)"))
157     dty:value("refuse", translate("Refused"))
158     dty:value("static", translate("Static (local only)"))
159     dty:value("transparent", translate("Transparent (local/global)"))
160     dty:depends("dhcp_link", "none")
161     dty:depends("dhcp_link", "odhcpd")
162
163     lfq = s1:taboption("DHCP", ListValue, "add_local_fqdn",
164         translate("LAN DNS"),
165         translate("How to enter the LAN or local network router in DNS"))
166     lfq.optional = true
167     lfq:value("0", translate("No Entry"))
168     lfq:value("1", translate("Hostname, Primary Address"))
169     lfq:value("2", translate("Hostname, All Addresses"))
170     lfq:value("3", translate("Host FQDN, All Addresses"))
171     lfq:value("4", translate("Interface FQDN, All Addresses"))
172     lfq:depends("dhcp_link", "none")
173     lfq:depends("dhcp_link", "odhcpd")
174
175     wfq = s1:taboption("DHCP", ListValue, "add_wan_fqdn",
176         translate("WAN DNS"),
177         translate("Override the WAN side router entry in DNS"))
178     wfq.optional = true
179     wfq:value("0", translate("Use Upstream"))
180     wfq:value("1", translate("Hostname, Primary Address"))
181     wfq:value("2", translate("Hostname, All Addresses"))
182     wfq:value("3", translate("Host FQDN, All Addresses"))
183     wfq:value("4", translate("Interface FQDN, All Addresses"))
184     wfq:depends("dhcp_link", "none")
185     wfq:depends("dhcp_link", "odhcpd")
186
187     exa = s1:taboption("DHCP", ListValue, "add_extra_dns",
188         translate("Extra DNS"),
189         translate("Use extra DNS entries found in /etc/config/dhcp"))
190     exa.optional = true
191     exa:value("0", translate("Ignore"))
192     exa:value("1", translate("Host Records"))
193     exa:value("2", translate("Host/MX/SRV RR"))
194     exa:value("3", translate("Host/MX/SRV/CNAME RR"))
195     exa:depends("dhcp_link", "none")
196     exa:depends("dhcp_link", "odhcpd")
197
198     --TODO: dnsmasq needs to not reference resolve-file and get off port 53.
199
200     --Resource Tuning Tab
201     ctl = s1:taboption("resource", ListValue, "unbound_control",
202         translate("Unbound Control App"),
203         translate("Enable access for unbound-control"))
204     ctl.rmempty = false
205     ctl:value("0", translate("No Remote Control"))
206     ctl:value("1", translate("Local Host, No Encryption"))
207     ctl:value("2", translate("Local Host, Encrypted"))
208     ctl:value("3", translate("Local Subnet, Encrypted"))
209     ctl:value("4", translate("Local Subnet, Static Encryption"))
210
211     pro = s1:taboption("resource", ListValue, "protocol",
212         translate("Recursion Protocol"),
213         translate("Chose the IP versions used upstream and downstream"))
214     pro:value("default", translate("Default"))
215     pro:value("ip4_only", translate("IP4 Only"))
216     pro:value("ip6_local", translate("IP4 All and IP6 Local"))
217     pro:value("ip6_only", translate("IP6 Only*"))
218     pro:value("ip6_prefer", translate("IP6 Preferred"))
219     pro:value("mixed", translate("IP4 and IP6"))
220     pro.rmempty = false
221
222     rsc = s1:taboption("resource", ListValue, "resource",
223         translate("Memory Resource"),
224         translate("Use menu System/Processes to observe any memory growth"))
225     rsc:value("default", translate("Default"))
226     rsc:value("tiny", translate("Tiny"))
227     rsc:value("small", translate("Small"))
228     rsc:value("medium", translate("Medium"))
229     rsc:value("large", translate("Large"))
230     rsc.rmempty = false
231
232     rsn = s1:taboption("resource", ListValue, "recursion",
233         translate("Recursion Strength"),
234         translate("Recursion activity affects memory growth and CPU load"))
235     rsn:value("default", translate("Default"))
236     rsn:value("passive", translate("Passive"))
237     rsn:value("aggressive", translate("Aggressive"))
238     rsn.rmempty = false
239
240     qry = s1:taboption("resource", Flag, "query_minimize",
241         translate("Query Minimize"),
242         translate("Break down query components for limited added privacy"))
243     qry.optional = true
244     qry:depends("recursion", "passive")
245     qry:depends("recursion", "aggressive")
246
247     qrs = s1:taboption("resource", Flag, "query_min_strict",
248         translate("Strict Minimize"),
249         translate("Strict version of 'query minimize' but it can break DNS"))
250     qrs.optional = true
251     qrs:depends("query_minimize", true)
252
253     eds = s1:taboption("resource", Value, "edns_size",
254         translate("EDNS Size"),
255         translate("Limit extended DNS packet size"))
256     eds.datatype = "and(uinteger,min(512),max(4096))"
257     eds.placeholder = "1280"
258
259     tlm = s1:taboption("resource", Value, "ttl_min",
260         translate("TTL Minimum"),
261         translate("Prevent excessively short cache periods"))
262     tlm.datatype = "and(uinteger,min(0),max(1200))"
263     tlm.placeholder = "120"
264
265     stt = s1:taboption("resource", Flag, "extended_stats",
266         translate("Extended Statistics"),
267         translate("Extended statistics are printed from unbound-control"))
268     stt.rmempty = false
269
270 else
271     ag2 = s1:taboption("basic", Value, "root_age",
272         translate("Root DSKEY Age"),
273         translate("Limit days between RFC5011 copies to reduce flash writes"))
274     ag2.datatype = "and(uinteger,min(1),max(99))"
275     ag2:value("3", "3")
276     ag2:value("9", "9 ("..translate("default")..")")
277     ag2:value("12", "12")
278     ag2:value("24", "24")
279     ag2:value("99", "99 ("..translate("never")..")")
280
281     tgr = s1:taboption("basic", Value, "trigger_interface",
282         translate("Trigger Networks"),
283         translate("Networks that may trigger Unbound to reload (avoid wan6)"))
284     tgr.template = "cbi/network_netlist"
285     tgr.widget = "checkbox"
286     tgr.rmempty = true
287     tgr.cast = "string"
288     tgr.nocreate = true
289 end
290
291
292 function ena.cfgvalue(self, section)
293     return sy.init.enabled("unbound") and self.enabled or self.disabled
294 end
295
296
297 function ena.write(self, section, value)
298     if (value == "1") then
299         sy.init.enable("unbound")
300         sy.call("/etc/init.d/unbound start >/dev/null 2>&1")
301
302     else
303         sy.call("/etc/init.d/unbound stop >/dev/null 2>&1")
304         sy.init.disable("unbound")
305     end
306
307
308     return Flag.write(self, section, value)
309 end
310
311
312 function m1.on_commit(self)
313     if sy.init.enabled("unbound") then
314         -- Restart Unbound with configuration
315         sy.call("/etc/init.d/unbound restart >/dev/null 2>&1")
316
317     else
318         sy.call("/etc/init.d/unbound stop >/dev/null 2>&1")
319     end
320 end
321
322
323 function m1.on_apply(self)
324     -- reload the page because some options hide
325     ht.redirect(ds.build_url("admin", "services", "unbound", "configure"))
326 end
327
328
329 return m1
330