1 -- Copyright 2017-2018 Dirk Brenken (dev@brenken.org)
2 -- This is free software, licensed under the Apache License, Version 2.0
4 local fs = require("nixio.fs")
5 local uci = require("luci.model.uci").cursor()
6 local util = require("luci.util")
7 local res_input = "/usr/share/dnscrypt-proxy/dnscrypt-resolvers.csv"
8 local res_dir = fs.dirname(res_input)
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"))
12 local url = "https://raw.githubusercontent.com/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv"
14 local _, date = pcall(require, "luci.http.date")
16 _, date = pcall(require, "luci.http.protocol.date")
19 if not fs.access(res_input) then
20 if not fs.access("/lib/libustream-ssl.so") then
21 m = SimpleForm("error", nil, translate("No default resolver list and no SSL support available.<br />")
22 .. translate("Please install a resolver list to '/usr/share/dnscrypt-proxy/dnscrypt-resolvers.csv' to use this package."))
27 luci.sys.call("env -i /bin/uclient-fetch --no-check-certificate -O " .. res_input .. " " .. url .. " >/dev/null 2>&1")
31 if not uci:get_first("dnscrypt-proxy", "global") then
32 uci:add("dnscrypt-proxy", "global")
33 uci:save("dnscrypt-proxy")
34 uci:commit("dnscrypt-proxy")
37 if fs.access(res_input) then
38 for line in io.lines(res_input) or {} do
42 nolog = line:match("^([^,]+),.-,\".-\",\"*(.-)\"*,.-,[0-9],\"*([yesno]+)\"*,\"*([yesno]+)\"*,.*")
43 if name ~= "" and name ~= "Name" then
44 if location == "" then
53 res_list[#res_list + 1] = { name = name, location = location, dnssec = dnssec, nolog = nolog }
58 m = Map("dnscrypt-proxy", translate("DNSCrypt-Proxy"),
59 translate("Configuration of the DNSCrypt-Proxy package. ")
60 .. translatef("For further information "
61 .. "<a href=\"%s\" target=\"_blank\">"
62 .. "see the wiki online</a>", "https://wiki.openwrt.org/inbox/dnscrypt"))
65 function m.on_after_commit(self)
66 function d1.validate(self, value, s1)
68 uci:commit("dnscrypt-proxy")
69 uci:set("dhcp", s1, "noresolv", 1)
70 if not fs.access("/etc/resolv-crypt.conf") or fs.stat("/etc/resolv-crypt.conf").size == 0 then
71 uci:set("dhcp", s1, "resolvfile", "/tmp/resolv.conf.auto")
73 uci:set("dhcp", s1, "resolvfile", "/etc/resolv-crypt.conf")
75 local server_list = {}
77 uci:foreach("dnscrypt-proxy", "dnscrypt-proxy", function(s)
78 server_list[cnt] = s['address'] .. "#" .. s['port']
81 server_list[cnt] = "/pool.ntp.org/8.8.8.8"
82 uci:set_list("dhcp", s1, "server", server_list)
84 uci:set("dhcp", s1, "allservers", 1)
86 uci:set("dhcp", s1, "allservers", 0)
93 luci.sys.call("env -i /etc/init.d/dnscrypt-proxy restart >/dev/null 2>&1")
94 luci.sys.call("env -i /etc/init.d/dnsmasq restart >/dev/null 2>&1")
97 s = m:section(TypedSection, "global", translate("General Options"))
100 -- Main dnscrypt-proxy resource list
102 o1 = s:option(DummyValue, "", translate("Default Resolver List"))
103 o1.template = "dnscrypt-proxy/res_options"
106 o2 = s:option(DummyValue, "", translate("File Date"))
107 o2.template = "dnscrypt-proxy/res_options"
108 if fs.access(res_input) then
109 o2.value = date.to_http(fs.stat(res_input).mtime)
114 o3 = s:option(DummyValue, "", translate("File Checksum"))
115 o3.template = "dnscrypt-proxy/res_options"
116 if fs.access(res_input) then
117 o3.value = luci.sys.exec("sha256sum " .. res_input .. " | awk '{print $1}'")
122 if fs.access("/lib/libustream-ssl.so") then
123 btn1 = s:option(Button, "", translate("Refresh Resolver List"),
124 translate("Download the current resolver list from 'github.com/dyne/dnscrypt-proxy'."))
125 btn1.inputtitle = translate("Refresh List")
126 btn1.inputstyle = "apply"
127 btn1.disabled = false
128 function btn1.write()
129 if not fs.access(res_dir) then
132 luci.sys.call("env -i /bin/uclient-fetch --no-check-certificate -O " .. res_input .. " " .. url .. " >/dev/null 2>&1")
133 luci.http.redirect(luci.dispatcher.build_url("admin", "services", "dnscrypt-proxy"))
136 btn1 = s:option(Button, "", translate("Refresh Resolver List"),
137 translate("No SSL support available.<br />")
138 .. translate("Please install a 'libustream-ssl' library to download the current resolver list from 'github.com/dyne/dnscrypt-proxy'."))
139 btn1.inputtitle = translate("-------")
140 btn1.inputstyle = "button"
144 if not fs.access("/etc/resolv-crypt.conf") or fs.stat("/etc/resolv-crypt.conf").size == 0 then
145 btn2 = s:option(Button, "", translate("Create Custom Config File"),
146 translate("Create '/etc/resolv-crypt.conf' with 'options timeout:1' to reduce DNS upstream timeouts with multiple DNSCrypt instances.<br />")
147 .. translatef("For further information "
148 .. "<a href=\"%s\" target=\"_blank\">"
149 .. "see the wiki online</a>", "https://wiki.openwrt.org/inbox/dnscrypt"))
150 btn2.inputtitle = translate("Create Config File")
151 btn2.inputstyle = "apply"
152 btn2.disabled = false
153 function btn2.write()
154 luci.sys.call("env -i echo 'options timeout:1' > '/etc/resolv-crypt.conf'")
155 luci.http.redirect(luci.dispatcher.build_url("admin", "services", "dnscrypt-proxy"))
158 btn2 = s:option(Button, "", translate("Create Custom Config File"),
159 translate("The config file '/etc/resolv-crypt.conf' already exist.<br />")
160 .. translate("Please edit the file manually in the 'Advanced' section."))
161 btn2.inputtitle = translate("-------")
162 btn2.inputstyle = "button"
168 t = s:option(ListValue, "procd_trigger", translate("Startup Trigger"),
169 translate("By default the DNSCrypt-Proxy startup will be triggered by ifup events of 'All' available network interfaces.<br />")
170 .. translate("To restrict the trigger, select only the relevant network interface. Usually the 'wan' interface should work for most users."))
174 for i, v in ipairs(dump.interface) do
175 if v.interface ~= "loopback" then
180 t.default = procd_trigger or "All"
183 -- Mandatory options per instance
185 s = m:section(TypedSection, "dnscrypt-proxy", translate("Instance Options"))
189 i1 = s:option(Value, "address", translate("IP Address"),
190 translate("The local IPv4 or IPv6 address. The latter one should be specified within brackets, e.g. '[::1]'."))
191 i1.default = address or "127.0.0.1"
194 i2 = s:option(Value, "port", translate("Port"),
195 translate("The listening port for DNS queries."))
200 i3 = s:option(ListValue, "resolver", translate("Resolver (LOC/SEC/NOLOG)"),
201 translate("Name of the remote DNS service for resolving queries incl. Location, DNSSEC- and NOLOG-Flag."))
202 i3.datatype = "hostname"
205 for i, v in ipairs(res_list) do
207 i3:value(v.name, v.name .. " (" .. v.location .. "/" .. v.dnssec .. "/" .. v.nolog .. ")")
210 i3.default = resolver
213 -- Extra options per instance
215 e1 = s:option(Value, "resolvers_list", translate("Alternate Resolver List"),
216 translate("Specify a non-default Resolver List."))
220 e2 = s:option(Value, "ephemeral_keys", translate("Ephemeral Keys"),
221 translate("Improve privacy by using an ephemeral public key for each query. ")
222 .. translate("This option requires extra CPU cycles and is useless with most DNSCrypt server."))
228 e3 = s:option(DynamicList, "blacklist", translate("Blacklist"),
229 translate("Local blacklists allow you to block abuse sites by domains or ip addresses. ")
230 .. 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'."))
233 e4 = s:option(Value, "block_ipv6", translate("Block IPv6"),
234 translate("Disable IPv6 to speed up DNSCrypt-Proxy."))
239 e5 = s:option(Value, "local_cache", translate("Local Cache"),
240 translate("Enable Caching to speed up DNSCcrypt-Proxy."))
245 e6 = s:option(Value, "query_log_file", translate("DNS Query Logfile"),
246 translate("Log the received DNS queries to a file, so you can watch in real-time what is happening on the network."))
254 s1 = m1:section(TypedSection, "dnsmasq", translate("Dnsmasq Options"))
257 d1 = s1:option(Flag, "", translate("Transfer Options To Dnsmasq"),
258 translate("Apply DNSCrypt-Proxy specific settings to the Dnsmasq configuration.<br />")
259 .. translate("Please note: This may change the values for 'noresolv', 'resolvfile', 'allservers' and the list 'server' settings."))
260 d1.default = d1.enabled