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 date = require("luci.http.protocol.date")
8 local res_input = "/usr/share/dnscrypt-proxy/dnscrypt-resolvers.csv"
9 local res_dir = fs.dirname(res_input)
10 local dump = util.ubus("network.interface", "dump", {})
11 local plug_cnt = tonumber(luci.sys.exec("env -i /usr/sbin/dnscrypt-proxy --version | grep 'Support for plugins: present' | wc -l"))
13 local url = "https://raw.githubusercontent.com/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv"
15 if not fs.access(res_input) then
16 if not fs.access("/lib/libustream-ssl.so") then
17 m = SimpleForm("error", nil, translate("No default resolver list and no SSL support available.<br />")
18 .. translate("Please install a resolver list to '/usr/share/dnscrypt-proxy/dnscrypt-resolvers.csv' to use this package."))
23 luci.sys.call("env -i /bin/uclient-fetch --no-check-certificate -O " .. res_input .. " " .. url .. " >/dev/null 2>&1")
27 if not uci:get_first("dnscrypt-proxy", "global") then
28 uci:add("dnscrypt-proxy", "global")
29 uci:save("dnscrypt-proxy")
30 uci:commit("dnscrypt-proxy")
33 if fs.access(res_input) then
34 for line in io.lines(res_input) or {} do
38 nolog = line:match("^([^,]+),.-,\".-\",\"*(.-)\"*,.-,[0-9],\"*([yesno]+)\"*,\"*([yesno]+)\"*,.*")
39 if name ~= "" and name ~= "Name" then
40 if location == "" then
49 res_list[#res_list + 1] = { name = name, location = location, dnssec = dnssec, nolog = nolog }
54 m = Map("dnscrypt-proxy", translate("DNSCrypt-Proxy"),
55 translate("Configuration of the DNSCrypt-Proxy package. ")
56 .. translatef("For further information "
57 .. "<a href=\"%s\" target=\"_blank\">"
58 .. "see the wiki online</a>", "https://wiki.openwrt.org/inbox/dnscrypt"))
61 function m.on_after_commit(self)
62 function d1.validate(self, value, s1)
64 uci:commit("dnscrypt-proxy")
65 uci:set("dhcp", s1, "noresolv", 1)
66 if not fs.access("/etc/resolv-crypt.conf") or fs.stat("/etc/resolv-crypt.conf").size == 0 then
67 uci:set("dhcp", s1, "resolvfile", "/tmp/resolv.conf.auto")
69 uci:set("dhcp", s1, "resolvfile", "/etc/resolv-crypt.conf")
71 local server_list = {}
73 uci:foreach("dnscrypt-proxy", "dnscrypt-proxy", function(s)
74 server_list[cnt] = s['address'] .. "#" .. s['port']
77 server_list[cnt] = "/pool.ntp.org/8.8.8.8"
78 uci:set_list("dhcp", s1, "server", server_list)
80 uci:set("dhcp", s1, "allservers", 1)
82 uci:set("dhcp", s1, "allservers", 0)
89 luci.sys.call("env -i /etc/init.d/dnscrypt-proxy restart >/dev/null 2>&1")
90 luci.sys.call("env -i /etc/init.d/dnsmasq restart >/dev/null 2>&1")
93 s = m:section(TypedSection, "global", translate("General Options"))
96 -- Main dnscrypt-proxy resource list
98 o1 = s:option(DummyValue, "", translate("Default Resolver List"))
99 o1.template = "dnscrypt-proxy/res_options"
102 o2 = s:option(DummyValue, "", translate("File Date"))
103 o2.template = "dnscrypt-proxy/res_options"
104 if fs.access(res_input) then
105 o2.value = date.to_http(fs.stat(res_input).mtime)
110 o3 = s:option(DummyValue, "", translate("File Checksum"))
111 o3.template = "dnscrypt-proxy/res_options"
112 if fs.access(res_input) then
113 o3.value = luci.sys.exec("sha256sum " .. res_input .. " | awk '{print $1}'")
118 if fs.access("/lib/libustream-ssl.so") then
119 btn1 = s:option(Button, "", translate("Refresh Resolver List"),
120 translate("Download the current resolver list from 'github.com/dyne/dnscrypt-proxy'."))
121 btn1.inputtitle = translate("Refresh List")
122 btn1.inputstyle = "apply"
123 btn1.disabled = false
124 function btn1.write()
125 if not fs.access(res_dir) then
128 luci.sys.call("env -i /bin/uclient-fetch --no-check-certificate -O " .. res_input .. " " .. url .. " >/dev/null 2>&1")
129 luci.http.redirect(luci.dispatcher.build_url("admin", "services", "dnscrypt-proxy"))
132 btn1 = s:option(Button, "", translate("Refresh Resolver List"),
133 translate("No SSL support available.<br />")
134 .. translate("Please install a 'libustream-ssl' library to download the current resolver list from 'github.com/dyne/dnscrypt-proxy'."))
135 btn1.inputtitle = translate("-------")
136 btn1.inputstyle = "button"
140 if not fs.access("/etc/resolv-crypt.conf") or fs.stat("/etc/resolv-crypt.conf").size == 0 then
141 btn2 = s:option(Button, "", translate("Create Custom Config File"),
142 translate("Create '/etc/resolv-crypt.conf' with 'options timeout:1' to reduce DNS upstream timeouts with multiple DNSCrypt instances.<br />")
143 .. translatef("For further information "
144 .. "<a href=\"%s\" target=\"_blank\">"
145 .. "see the wiki online</a>", "https://wiki.openwrt.org/inbox/dnscrypt"))
146 btn2.inputtitle = translate("Create Config File")
147 btn2.inputstyle = "apply"
148 btn2.disabled = false
149 function btn2.write()
150 luci.sys.call("env -i echo 'options timeout:1' > '/etc/resolv-crypt.conf'")
151 luci.http.redirect(luci.dispatcher.build_url("admin", "services", "dnscrypt-proxy"))
154 btn2 = s:option(Button, "", translate("Create Custom Config File"),
155 translate("The config file '/etc/resolv-crypt.conf' already exist.<br />")
156 .. translate("Please edit the file manually in the 'Advanced' section."))
157 btn2.inputtitle = translate("-------")
158 btn2.inputstyle = "button"
164 t = s:option(ListValue, "procd_trigger", translate("Startup Trigger"),
165 translate("By default the DNSCrypt-Proxy startup will be triggered by ifup events of 'All' available network interfaces.<br />")
166 .. translate("To restrict the trigger, select only the relevant network interface. Usually the 'wan' interface should work for most users."))
170 for i, v in ipairs(dump.interface) do
171 if v.interface ~= "loopback" then
176 t.default = procd_trigger or "All"
179 -- Mandatory options per instance
181 s = m:section(TypedSection, "dnscrypt-proxy", translate("Instance Options"))
185 i1 = s:option(Value, "address", translate("IP Address"),
186 translate("The local IPv4 or IPv6 address. The latter one should be specified within brackets, e.g. '[::1]'."))
187 i1.default = address or "127.0.0.1"
190 i2 = s:option(Value, "port", translate("Port"),
191 translate("The listening port for DNS queries."))
196 i3 = s:option(ListValue, "resolver", translate("Resolver (LOC/SEC/NOLOG)"),
197 translate("Name of the remote DNS service for resolving queries incl. Location, DNSSEC- and NOLOG-Flag."))
198 i3.datatype = "hostname"
201 for i, v in ipairs(res_list) do
203 i3:value(v.name, v.name .. " (" .. v.location .. "/" .. v.dnssec .. "/" .. v.nolog .. ")")
206 i3.default = resolver
209 -- Extra options per instance
211 e1 = s:option(Value, "resolvers_list", translate("Alternate Resolver List"),
212 translate("Specify a non-default Resolver List."))
216 e2 = s:option(Value, "ephemeral_keys", translate("Ephemeral Keys"),
217 translate("Improve privacy by using an ephemeral public key for each query. ")
218 .. translate("This option requires extra CPU cycles and is useless with most DNSCrypt server."))
224 e3 = s:option(DynamicList, "blacklist", translate("Blacklist"),
225 translate("Local blacklists allow you to block abuse sites by domains or ip addresses. ")
226 .. 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'."))
229 e4 = s:option(Value, "block_ipv6", translate("Block IPv6"),
230 translate("Disable IPv6 to speed up DNSCrypt-Proxy."))
235 e5 = s:option(Value, "local_cache", translate("Local Cache"),
236 translate("Enable Caching to speed up DNSCcrypt-Proxy."))
241 e6 = s:option(Value, "query_log_file", translate("DNS Query Logfile"),
242 translate("Log the received DNS queries to a file, so you can watch in real-time what is happening on the network."))
250 s1 = m1:section(TypedSection, "dnsmasq", translate("Dnsmasq Options"))
253 d1 = s1:option(Flag, "", translate("Transfer Options To Dnsmasq"),
254 translate("Apply DNSCrypt-Proxy specific settings to the Dnsmasq configuration.<br />")
255 .. translate("Please note: This may change the values for 'noresolv', 'resolvfile', 'allservers' and the list 'server' settings."))
256 d1.default = d1.enabled