luci-app-dnscrypt-proxy: fix resolver selection
[project/luci.git] / applications / luci-app-dnscrypt-proxy / luasrc / model / cbi / dnscrypt-proxy / overview_tab.lua
1 -- Copyright 2017 Dirk Brenken (dev@brenken.org)
2 -- This is free software, licensed under the Apache License, Version 2.0
3
4 local fs        = require("nixio.fs")
5 local uci       = require("luci.model.uci").cursor()
6 local util      = require("luci.util")
7 local date      = require("luci.http.protocol.date")
8 local res_input = "/usr/share/dnscrypt-proxy/dnscrypt-resolvers.csv"
9 local dump      = util.ubus("network.interface", "dump", {})
10 local plug_cnt  = tonumber(luci.sys.exec("env -i /usr/sbin/dnscrypt-proxy --version | grep 'Support for plugins: present' | wc -l"))
11 local res_list  = {}
12 local url       = "https://download.dnscrypt.org/dnscrypt-proxy/dnscrypt-resolvers.csv"
13
14 if not fs.access(res_input) then
15         if not fs.access("/lib/libustream-ssl.so") then
16                 m = SimpleForm("error", nil, translate("No default resolver list and no SSL support available.<br />")
17                         .. translate("Please install a resolver list to '/usr/share/dnscrypt-proxy/dnscrypt-resolvers.csv' to use this package."))
18                 m.submit = false
19                 m.reset = false
20                 return m
21         else
22                 luci.sys.call("env -i /bin/uclient-fetch --no-check-certificate -O " .. res_input .. " " .. url .. " >/dev/null 2>&1")
23         end
24 end
25
26 if not uci:get_first("dnscrypt-proxy", "global") then
27         uci:add("dnscrypt-proxy", "global")
28         uci:save("dnscrypt-proxy")
29         uci:commit("dnscrypt-proxy")
30 end
31
32 for line in io.lines(res_input) do
33         local name,
34         location,
35         dnssec,
36         nolog = line:match("^([^,]+),.-,\".-\",\"*(.-)\"*,.-,[0-9],\"*([yesno]+)\"*,\"*([yesno]+)\"*,.*")
37         if name ~= "" and name ~= "Name" then
38                 if location == "" then
39                         location = "-"
40                 end
41                 if dnssec == "" then
42                         dnssec = "-"
43                 end
44                 if nolog == "" then
45                         nolog = "-"
46                 end
47                 res_list[#res_list + 1] = { name = name, location = location, dnssec = dnssec, nolog = nolog }
48         end
49 end
50
51 m = Map("dnscrypt-proxy", translate("DNSCrypt-Proxy"),
52         translate("Configuration of the DNSCrypt-Proxy package. ")
53         .. translatef("For further information "
54         .. "<a href=\"%s\" target=\"_blank\">"
55         .. "see the wiki online</a>", "https://wiki.openwrt.org/inbox/dnscrypt"))
56 m:chain("dhcp")
57
58 function m.on_after_commit(self)
59         function d1.validate(self, value, s1)
60                 if value == "1" then
61                         uci:commit("dnscrypt-proxy")
62                         uci:set("dhcp", s1, "noresolv", 1)
63                         if not fs.access("/etc/resolv-crypt.conf") or nixio.fs.stat("/etc/resolv-crypt.conf").size == 0 then
64                                 uci:set("dhcp", s1, "resolvfile", "/tmp/resolv.conf.auto")
65                         else
66                                 uci:set("dhcp", s1, "resolvfile", "/etc/resolv-crypt.conf")
67                         end
68                         local server_list = {}
69                         local cnt = 1
70                         uci:foreach("dnscrypt-proxy", "dnscrypt-proxy", function(s)
71                                 server_list[cnt] = s['address'] .. "#" .. s['port']
72                                 cnt = cnt + 1
73                         end)
74                         server_list[cnt] = "/pool.ntp.org/8.8.8.8"
75                         uci:set_list("dhcp", s1, "server", server_list)
76                         if cnt > 2 then
77                                 uci:set("dhcp", s1, "allservers", 1)
78                         else
79                                 uci:set("dhcp", s1, "allservers", 0)
80                         end
81                         uci:save("dhcp")
82                         uci:commit("dhcp")
83                 end
84                 return value
85         end
86         luci.sys.call("env -i /etc/init.d/dnscrypt-proxy restart >/dev/null 2>&1")
87         luci.sys.call("env -i /etc/init.d/dnsmasq restart >/dev/null 2>&1")
88 end
89
90 s = m:section(TypedSection, "global", translate("General Options"))
91 s.anonymous = true
92
93 -- Main dnscrypt-proxy resource list
94
95 o1 = s:option(DummyValue, "", translate("Default Resolver List"))
96 o1.template = "dnscrypt-proxy/res_options"
97 o1.value = res_input
98
99 o2 = s:option(DummyValue, "", translate("File Date"))
100 o2.template = "dnscrypt-proxy/res_options"
101 o2.value = date.to_http(nixio.fs.stat(res_input).mtime)
102
103 o3 = s:option(DummyValue, "", translate("File Checksum"))
104 o3.template = "dnscrypt-proxy/res_options"
105 o3.value = luci.sys.exec("sha256sum " .. res_input .. " | awk '{print $1}'")
106
107 if fs.access("/lib/libustream-ssl.so") then
108         btn1 = s:option(Button, "", translate("Refresh Resolver List"),
109                 translate("Download the current resolver list from 'download.dnscrypt.org'."))
110         btn1.inputtitle = translate("Refresh List")
111         btn1.inputstyle = "apply"
112         btn1.disabled = false
113         function btn1.write()
114                 luci.sys.call("env -i /bin/uclient-fetch --no-check-certificate -O " .. res_input .. " " .. url .. " >/dev/null 2>&1")
115                 luci.http.redirect(luci.dispatcher.build_url("admin", "services", "dnscrypt-proxy"))
116         end
117 else
118         btn1 = s:option(Button, "", translate("Refresh Resolver List"),
119                 translate("No SSL support available.<br />")
120                 .. translate("Please install a 'libustream-ssl' library to download the current resolver list from 'download.dnscrypt.org'."))
121         btn1.inputtitle = translate("-------")
122         btn1.inputstyle = "button"
123         btn1.disabled = true
124 end
125
126 if not fs.access("/etc/resolv-crypt.conf") or nixio.fs.stat("/etc/resolv-crypt.conf").size == 0 then
127         btn2 = s:option(Button, "", translate("Create Custom Config File"),
128                 translate("Create '/etc/resolv-crypt.conf' with 'options timeout:1' to reduce DNS upstream timeouts with multiple DNSCrypt instances.<br />")
129                 .. translatef("For further information "
130                 .. "<a href=\"%s\" target=\"_blank\">"
131                 .. "see the wiki online</a>", "https://wiki.openwrt.org/inbox/dnscrypt"))
132         btn2.inputtitle = translate("Create Config File")
133         btn2.inputstyle = "apply"
134         btn2.disabled = false
135         function btn2.write()
136                 luci.sys.call("env -i echo 'options timeout:1' > '/etc/resolv-crypt.conf'")
137                 luci.http.redirect(luci.dispatcher.build_url("admin", "services", "dnscrypt-proxy"))
138         end
139 else
140         btn2 = s:option(Button, "", translate("Create Custom Config File"),
141                 translate("The config file '/etc/resolv-crypt.conf' already exist.<br />")
142                 .. translate("Please edit the file manually in the 'Advanced' section."))
143         btn2.inputtitle = translate("-------")
144         btn2.inputstyle = "button"
145         btn2.disabled = true
146 end
147
148 -- Trigger settings
149
150 t = s:option(ListValue, "procd_trigger", translate("Startup Trigger"),
151         translate("By default the DNSCrypt-Proxy startup will be triggered by ifup events of 'All' available network interfaces.<br />")
152         .. translate("To restrict the trigger, select only the relevant network interface. Usually the 'wan' interface should work for most users."))
153 t:value("", "All")
154 if dump then
155         local i, v
156         for i, v in ipairs(dump.interface) do
157                 if v.interface ~= "loopback" then
158                         t:value(v.interface)
159                 end
160         end
161 end
162 t.default = procd_trigger or "All"
163 t.rmempty = true
164
165 -- Mandatory options per instance
166
167 s = m:section(TypedSection, "dnscrypt-proxy", translate("Instance Options"))
168 s.anonymous = true
169 s.addremove = true
170
171 i1 = s:option(Value, "address", translate("IP Address"),
172         translate("The local IPv4 or IPv6 address. The latter one should be specified within brackets, e.g. '[::1]'."))
173 i1.default = address or "127.0.0.1"
174 i1.rmempty = false
175
176 i2 = s:option(Value, "port", translate("Port"),
177         translate("The listening port for DNS queries."))
178 i2.datatype = "port"
179 i2.default = port
180 i2.rmempty = false
181
182 i3 = s:option(ListValue, "resolver", translate("Resolver (LOC/SEC/NOLOG)"),
183         translate("Name of the remote DNS service for resolving queries incl. Location, DNSSEC- and NOLOG-Flag."))
184 i3.datatype = "hostname"
185 i3.widget = "select"
186 local i, v
187 for i, v in ipairs(res_list) do
188         if v.name then
189                 i3:value(v.name, v.name .. " (" .. v.location .. "/" .. v.dnssec .. "/" .. v.nolog .. ")")
190         end
191 end
192 i3.default = resolver
193 i3.rmempty = false
194
195 -- Extra options per instance
196
197 e1 = s:option(Value, "resolvers_list", translate("Alternate Resolver List"),
198         translate("Specify a non-default Resolver List."))
199 e1.datatype = "file"
200 e1.optional = true
201
202 e2 = s:option(Value, "ephemeral_keys", translate("Ephemeral Keys"),
203         translate("Improve privacy by using an ephemeral public key for each query. ")
204         .. translate("This option requires extra CPU cycles and is useless with most DNSCrypt server."))
205 e2.datatype = "bool"
206 e2.value = 1
207 e2.optional = true
208
209 if plug_cnt > 0 then
210         e3 = s:option(DynamicList, "blacklist", translate("Blacklist"),
211                 translate("Local blacklists allow you to block abuse sites by domains or ip addresses. ")
212                 .. translate("The value for this property is the blocklist type and path to the file, e.g.'domains:/path/to/dbl.txt' or 'ips:/path/to/ipbl.txt'."))
213         e3.optional = true
214
215         e4 = s:option(Value, "block_ipv6", translate("Block IPv6"),
216                 translate("Disable IPv6 to speed up DNSCrypt-Proxy."))
217         e4.datatype = "bool"
218         e4.value = 1
219         e4.optional = true
220
221         e5 = s:option(Value, "local_cache", translate("Local Cache"),
222                 translate("Enable Caching to speed up DNSCcrypt-Proxy."))
223         e5.datatype = "bool"
224         e5.value = 1
225         e5.optional = true
226         
227         e6 = s:option(Value, "query_log_file", translate("DNS Query Logfile"),
228         translate("Log the received DNS queries to a file, so you can watch in real-time what is happening on the network."))
229         e6.optional = true
230 end
231
232 -- Dnsmasq options
233
234 m1 = Map("dhcp")
235
236 s1 = m1:section(TypedSection, "dnsmasq", translate("Dnsmasq Options"))
237 s1.anonymous = true
238
239 d1 = s1:option(Flag, "", translate("Transfer Options To Dnsmasq"),
240         translate("Apply DNSCrypt-Proxy specific settings to the Dnsmasq configuration.<br />")
241         .. translate("Please note: This may change the values for 'noresolv', 'resolvfile', 'allservers' and the list 'server' settings."))
242 d1.default = d1.enabled
243 d1.rmempty = false
244
245 return m, m1