1 -- Copyright 2017 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 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://download.dnscrypt.org/dnscrypt-proxy/dnscrypt-resolvers.csv"
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."))
22 luci.sys.call("env -i /bin/uclient-fetch --no-check-certificate -O " .. res_input .. " " .. url .. " >/dev/null 2>&1")
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")
32 for line in io.lines(res_input) do
36 nolog = line:match("^([^,]+),.-,\".-\",\"*(.-)\"*,.-,[0-9],\"*([yesno]+)\"*,\"*([yesno]+)\"*,.*")
37 if name ~= "" and name ~= "Name" then
38 if location == "" then
47 res_list[#res_list + 1] = { name = name, location = location, dnssec = dnssec, nolog = nolog }
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"))
58 function m.on_after_commit(self)
59 function d1.validate(self, value, s1)
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")
66 uci:set("dhcp", s1, "resolvfile", "/etc/resolv-crypt.conf")
68 local server_list = {}
70 uci:foreach("dnscrypt-proxy", "dnscrypt-proxy", function(s)
71 server_list[cnt] = s['address'] .. "#" .. s['port']
74 server_list[cnt] = "/pool.ntp.org/8.8.8.8"
75 uci:set_list("dhcp", s1, "server", server_list)
77 uci:set("dhcp", s1, "allservers", 1)
79 uci:set("dhcp", s1, "allservers", 0)
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")
90 s = m:section(TypedSection, "global", translate("General Options"))
93 -- Main dnscrypt-proxy resource list
95 o1 = s:option(DummyValue, "", translate("Default Resolver List"))
96 o1.template = "dnscrypt-proxy/res_options"
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)
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}'")
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"))
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"
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"))
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"
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."))
156 for i, v in ipairs(dump.interface) do
157 if v.interface ~= "loopback" then
162 t.default = procd_trigger or "All"
165 -- Mandatory options per instance
167 s = m:section(TypedSection, "dnscrypt-proxy", translate("Instance Options"))
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"
176 i2 = s:option(Value, "port", translate("Port"),
177 translate("The listening port for DNS queries."))
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"
187 for i, v in ipairs(res_list) do
189 i3:value(v.name, v.name .. " (" .. v.location .. "/" .. v.dnssec .. "/" .. v.nolog .. ")")
192 i3.default = resolver
195 -- Extra options per instance
197 e1 = s:option(Value, "resolvers_list", translate("Alternate Resolver List"),
198 translate("Specify a non-default Resolver List."))
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."))
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'."))
215 e4 = s:option(Value, "block_ipv6", translate("Block IPv6"),
216 translate("Disable IPv6 to speed up DNSCrypt-Proxy."))
221 e5 = s:option(Value, "local_cache", translate("Local Cache"),
222 translate("Enable Caching to speed up DNSCcrypt-Proxy."))
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."))
236 s1 = m1:section(TypedSection, "dnsmasq", translate("Dnsmasq Options"))
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