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 res_list[#res_list + 1] = { name = name, location = location, dnssec = dnssec, nolog = nolog }
40 m = Map("dnscrypt-proxy", translate("DNSCrypt-Proxy"),
41 translate("Configuration of the DNSCrypt-Proxy package. ")
42 .. translatef("For further information "
43 .. "<a href=\"%s\" target=\"_blank\">"
44 .. "see the wiki online</a>", "https://wiki.openwrt.org/inbox/dnscrypt"))
47 function m.on_after_commit(self)
48 function d1.validate(self, value, s1)
50 uci:commit("dnscrypt-proxy")
51 uci:set("dhcp", s1, "noresolv", 1)
52 if not fs.access("/etc/resolv-crypt.conf") or nixio.fs.stat("/etc/resolv-crypt.conf").size == 0 then
53 uci:set("dhcp", s1, "resolvfile", "/tmp/resolv.conf.auto")
55 uci:set("dhcp", s1, "resolvfile", "/etc/resolv-crypt.conf")
57 local server_list = {}
59 uci:foreach("dnscrypt-proxy", "dnscrypt-proxy", function(s)
60 server_list[cnt] = s['address'] .. "#" .. s['port']
63 server_list[cnt] = "/pool.ntp.org/8.8.8.8"
64 uci:set_list("dhcp", s1, "server", server_list)
66 uci:set("dhcp", s1, "allservers", 1)
68 uci:set("dhcp", s1, "allservers", 0)
75 luci.sys.call("env -i /etc/init.d/dnscrypt-proxy restart >/dev/null 2>&1")
76 luci.sys.call("env -i /etc/init.d/dnsmasq restart >/dev/null 2>&1")
79 s = m:section(TypedSection, "global", translate("General Options"))
82 -- Main dnscrypt-proxy resource list
84 o1 = s:option(DummyValue, "", translate("Default Resolver List"))
85 o1.template = "dnscrypt-proxy/res_options"
88 o2 = s:option(DummyValue, "", translate("File Date"))
89 o2.template = "dnscrypt-proxy/res_options"
90 o2.value = date.to_http(nixio.fs.stat(res_input).mtime)
92 o3 = s:option(DummyValue, "", translate("File Checksum"))
93 o3.template = "dnscrypt-proxy/res_options"
94 o3.value = luci.sys.exec("sha256sum " .. res_input .. " | awk '{print $1}'")
96 if fs.access("/lib/libustream-ssl.so") then
97 btn1 = s:option(Button, "", translate("Refresh Resolver List"),
98 translate("Download the current resolver list from 'download.dnscrypt.org'."))
99 btn1.inputtitle = translate("Refresh List")
100 btn1.inputstyle = "apply"
101 btn1.disabled = false
102 function btn1.write()
103 luci.sys.call("env -i /bin/uclient-fetch --no-check-certificate -O " .. res_input .. " " .. url .. " >/dev/null 2>&1")
104 luci.http.redirect(luci.dispatcher.build_url("admin", "services", "dnscrypt-proxy"))
107 btn1 = s:option(Button, "", translate("Refresh Resolver List"),
108 translate("No SSL support available.<br />")
109 .. translate("Please install a 'libustream-ssl' library to download the current resolver list from 'download.dnscrypt.org'."))
110 btn1.inputtitle = translate("-------")
111 btn1.inputstyle = "button"
115 if not fs.access("/etc/resolv-crypt.conf") or nixio.fs.stat("/etc/resolv-crypt.conf").size == 0 then
116 btn2 = s:option(Button, "", translate("Create Custom Config File"),
117 translate("Create '/etc/resolv-crypt.conf' with 'options timeout:1' to reduce DNS upstream timeouts with multiple DNSCrypt instances.<br />")
118 .. translatef("For further information "
119 .. "<a href=\"%s\" target=\"_blank\">"
120 .. "see the wiki online</a>", "https://wiki.openwrt.org/inbox/dnscrypt"))
121 btn2.inputtitle = translate("Create Config File")
122 btn2.inputstyle = "apply"
123 btn2.disabled = false
124 function btn2.write()
125 luci.sys.call("env -i echo 'options timeout:1' > '/etc/resolv-crypt.conf'")
126 luci.http.redirect(luci.dispatcher.build_url("admin", "services", "dnscrypt-proxy"))
129 btn2 = s:option(Button, "", translate("Create Custom Config File"),
130 translate("The config file '/etc/resolv-crypt.conf' already exist.<br />")
131 .. translate("Please edit the file manually in the 'Advanced' section."))
132 btn2.inputtitle = translate("-------")
133 btn2.inputstyle = "button"
139 t = s:option(ListValue, "procd_trigger", translate("Startup Trigger"),
140 translate("By default the DNSCrypt-Proxy startup will be triggered by ifup events of 'All' available network interfaces.<br />")
141 .. translate("To restrict the trigger, select only the relevant network interface. Usually the 'wan' interface should work for most users."))
145 for i, v in ipairs(dump.interface) do
146 if v.interface ~= "loopback" then
151 t.default = procd_trigger or "All"
154 -- Mandatory options per instance
156 s = m:section(TypedSection, "dnscrypt-proxy", translate("Instance Options"))
160 i1 = s:option(Value, "address", translate("IP Address"),
161 translate("The local IPv4 or IPv6 address. The latter one should be specified within brackets, e.g. '[::1]'."))
162 i1.default = address or "127.0.0.1"
165 i2 = s:option(Value, "port", translate("Port"),
166 translate("The listening port for DNS queries."))
171 i3 = s:option(ListValue, "resolver", translate("Resolver (LOC/SEC/NOLOG)"),
172 translate("Name of the remote DNS service for resolving queries incl. Location, DNSSEC- and NOLOG-Flag."))
173 i3.datatype = "hostname"
176 for i, v in ipairs(res_list) do
177 if v.name and v.location and v.dnssec and v.nolog and v.name ~= "Name" then
178 i3:value(v.name, v.name .. " (" .. v.location .. "/" .. v.dnssec .. "/" .. v.nolog .. ")")
181 i3.default = resolver
184 -- Extra options per instance
186 e1 = s:option(Value, "resolvers_list", translate("Alternate Resolver List"),
187 translate("Specify a non-default Resolver List."))
191 e2 = s:option(Value, "ephemeral_keys", translate("Ephemeral Keys"),
192 translate("Improve privacy by using an ephemeral public key for each query. ")
193 .. translate("This option requires extra CPU cycles and is useless with most DNSCrypt server."))
199 e3 = s:option(DynamicList, "blacklist", translate("Blacklist"),
200 translate("Local blacklists allow you to block abuse sites by domains or ip addresses. ")
201 .. 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'."))
204 e4 = s:option(Value, "block_ipv6", translate("Block IPv6"),
205 translate("Disable IPv6 to speed up DNSCrypt-Proxy."))
210 e5 = s:option(Value, "local_cache", translate("Local Cache"),
211 translate("Enable Caching to speed up DNSCcrypt-Proxy."))
216 e6 = s:option(Value, "query_log_file", translate("DNS Query Logfile"),
217 translate("Log the received DNS queries to a file, so you can watch in real-time what is happening on the network."))
225 s1 = m1:section(TypedSection, "dnsmasq", translate("Dnsmasq Options"))
228 d1 = s1:option(Flag, "", translate("Transfer Options To Dnsmasq"),
229 translate("Apply DNSCrypt-Proxy specific settings to the Dnsmasq configuration.<br />")
230 .. translate("Please note: This may change the values for 'noresolv', 'resolvfile', 'allservers' and the list 'server' settings."))
231 d1.default = d1.enabled