-- Copyright 2017-2018 Dirk Brenken (dev@brenken.org) -- This is free software, licensed under the Apache License, Version 2.0 local fs = require("nixio.fs") local uci = require("luci.model.uci").cursor() local util = require("luci.util") local date = require("luci.http.protocol.date") local res_input = "/usr/share/dnscrypt-proxy/dnscrypt-resolvers.csv" local res_dir = fs.dirname(res_input) local dump = util.ubus("network.interface", "dump", {}) local plug_cnt = tonumber(luci.sys.exec("env -i /usr/sbin/dnscrypt-proxy --version | grep 'Support for plugins: present' | wc -l")) local res_list = {} local url = "https://raw.githubusercontent.com/dyne/dnscrypt-proxy/master/dnscrypt-resolvers.csv" if not fs.access(res_input) then if not fs.access("/lib/libustream-ssl.so") then m = SimpleForm("error", nil, translate("No default resolver list and no SSL support available.
") .. translate("Please install a resolver list to '/usr/share/dnscrypt-proxy/dnscrypt-resolvers.csv' to use this package.")) m.submit = false m.reset = false return m else luci.sys.call("env -i /bin/uclient-fetch --no-check-certificate -O " .. res_input .. " " .. url .. " >/dev/null 2>&1") end end if not uci:get_first("dnscrypt-proxy", "global") then uci:add("dnscrypt-proxy", "global") uci:save("dnscrypt-proxy") uci:commit("dnscrypt-proxy") end if fs.access(res_input) then for line in io.lines(res_input) or {} do local name, location, dnssec, nolog = line:match("^([^,]+),.-,\".-\",\"*(.-)\"*,.-,[0-9],\"*([yesno]+)\"*,\"*([yesno]+)\"*,.*") if name ~= "" and name ~= "Name" then if location == "" then location = "-" end if dnssec == "" then dnssec = "-" end if nolog == "" then nolog = "-" end res_list[#res_list + 1] = { name = name, location = location, dnssec = dnssec, nolog = nolog } end end end m = Map("dnscrypt-proxy", translate("DNSCrypt-Proxy"), translate("Configuration of the DNSCrypt-Proxy package. ") .. translatef("For further information " .. "" .. "see the wiki online", "https://wiki.openwrt.org/inbox/dnscrypt")) m:chain("dhcp") function m.on_after_commit(self) function d1.validate(self, value, s1) if value == "1" then uci:commit("dnscrypt-proxy") uci:set("dhcp", s1, "noresolv", 1) if not fs.access("/etc/resolv-crypt.conf") or fs.stat("/etc/resolv-crypt.conf").size == 0 then uci:set("dhcp", s1, "resolvfile", "/tmp/resolv.conf.auto") else uci:set("dhcp", s1, "resolvfile", "/etc/resolv-crypt.conf") end local server_list = {} local cnt = 1 uci:foreach("dnscrypt-proxy", "dnscrypt-proxy", function(s) server_list[cnt] = s['address'] .. "#" .. s['port'] cnt = cnt + 1 end) server_list[cnt] = "/pool.ntp.org/8.8.8.8" uci:set_list("dhcp", s1, "server", server_list) if cnt > 2 then uci:set("dhcp", s1, "allservers", 1) else uci:set("dhcp", s1, "allservers", 0) end uci:save("dhcp") uci:commit("dhcp") end return value end luci.sys.call("env -i /etc/init.d/dnscrypt-proxy restart >/dev/null 2>&1") luci.sys.call("env -i /etc/init.d/dnsmasq restart >/dev/null 2>&1") end s = m:section(TypedSection, "global", translate("General Options")) s.anonymous = true -- Main dnscrypt-proxy resource list o1 = s:option(DummyValue, "", translate("Default Resolver List")) o1.template = "dnscrypt-proxy/res_options" o1.value = res_input o2 = s:option(DummyValue, "", translate("File Date")) o2.template = "dnscrypt-proxy/res_options" if fs.access(res_input) then o2.value = date.to_http(fs.stat(res_input).mtime) else o2.value = "-" end o3 = s:option(DummyValue, "", translate("File Checksum")) o3.template = "dnscrypt-proxy/res_options" if fs.access(res_input) then o3.value = luci.sys.exec("sha256sum " .. res_input .. " | awk '{print $1}'") else o3.value = "-" end if fs.access("/lib/libustream-ssl.so") then btn1 = s:option(Button, "", translate("Refresh Resolver List"), translate("Download the current resolver list from 'github.com/dyne/dnscrypt-proxy'.")) btn1.inputtitle = translate("Refresh List") btn1.inputstyle = "apply" btn1.disabled = false function btn1.write() if not fs.access(res_dir) then fs.mkdir(res_dir) end luci.sys.call("env -i /bin/uclient-fetch --no-check-certificate -O " .. res_input .. " " .. url .. " >/dev/null 2>&1") luci.http.redirect(luci.dispatcher.build_url("admin", "services", "dnscrypt-proxy")) end else btn1 = s:option(Button, "", translate("Refresh Resolver List"), translate("No SSL support available.
") .. translate("Please install a 'libustream-ssl' library to download the current resolver list from 'github.com/dyne/dnscrypt-proxy'.")) btn1.inputtitle = translate("-------") btn1.inputstyle = "button" btn1.disabled = true end if not fs.access("/etc/resolv-crypt.conf") or fs.stat("/etc/resolv-crypt.conf").size == 0 then btn2 = s:option(Button, "", translate("Create Custom Config File"), translate("Create '/etc/resolv-crypt.conf' with 'options timeout:1' to reduce DNS upstream timeouts with multiple DNSCrypt instances.
") .. translatef("For further information " .. "" .. "see the wiki online", "https://wiki.openwrt.org/inbox/dnscrypt")) btn2.inputtitle = translate("Create Config File") btn2.inputstyle = "apply" btn2.disabled = false function btn2.write() luci.sys.call("env -i echo 'options timeout:1' > '/etc/resolv-crypt.conf'") luci.http.redirect(luci.dispatcher.build_url("admin", "services", "dnscrypt-proxy")) end else btn2 = s:option(Button, "", translate("Create Custom Config File"), translate("The config file '/etc/resolv-crypt.conf' already exist.
") .. translate("Please edit the file manually in the 'Advanced' section.")) btn2.inputtitle = translate("-------") btn2.inputstyle = "button" btn2.disabled = true end -- Trigger settings t = s:option(ListValue, "procd_trigger", translate("Startup Trigger"), translate("By default the DNSCrypt-Proxy startup will be triggered by ifup events of 'All' available network interfaces.
") .. translate("To restrict the trigger, select only the relevant network interface. Usually the 'wan' interface should work for most users.")) t:value("", "All") if dump then local i, v for i, v in ipairs(dump.interface) do if v.interface ~= "loopback" then t:value(v.interface) end end end t.default = procd_trigger or "All" t.rmempty = true -- Mandatory options per instance s = m:section(TypedSection, "dnscrypt-proxy", translate("Instance Options")) s.anonymous = true s.addremove = true i1 = s:option(Value, "address", translate("IP Address"), translate("The local IPv4 or IPv6 address. The latter one should be specified within brackets, e.g. '[::1]'.")) i1.default = address or "127.0.0.1" i1.rmempty = false i2 = s:option(Value, "port", translate("Port"), translate("The listening port for DNS queries.")) i2.datatype = "port" i2.default = port i2.rmempty = false i3 = s:option(ListValue, "resolver", translate("Resolver (LOC/SEC/NOLOG)"), translate("Name of the remote DNS service for resolving queries incl. Location, DNSSEC- and NOLOG-Flag.")) i3.datatype = "hostname" i3.widget = "select" local i, v for i, v in ipairs(res_list) do if v.name then i3:value(v.name, v.name .. " (" .. v.location .. "/" .. v.dnssec .. "/" .. v.nolog .. ")") end end i3.default = resolver i3.rmempty = false -- Extra options per instance e1 = s:option(Value, "resolvers_list", translate("Alternate Resolver List"), translate("Specify a non-default Resolver List.")) e1.datatype = "file" e1.optional = true e2 = s:option(Value, "ephemeral_keys", translate("Ephemeral Keys"), translate("Improve privacy by using an ephemeral public key for each query. ") .. translate("This option requires extra CPU cycles and is useless with most DNSCrypt server.")) e2.datatype = "bool" e2.value = 1 e2.optional = true if plug_cnt > 0 then e3 = s:option(DynamicList, "blacklist", translate("Blacklist"), translate("Local blacklists allow you to block abuse sites by domains or ip addresses. ") .. 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'.")) e3.optional = true e4 = s:option(Value, "block_ipv6", translate("Block IPv6"), translate("Disable IPv6 to speed up DNSCrypt-Proxy.")) e4.datatype = "bool" e4.value = 1 e4.optional = true e5 = s:option(Value, "local_cache", translate("Local Cache"), translate("Enable Caching to speed up DNSCcrypt-Proxy.")) e5.datatype = "bool" e5.value = 1 e5.optional = true e6 = s:option(Value, "query_log_file", translate("DNS Query Logfile"), translate("Log the received DNS queries to a file, so you can watch in real-time what is happening on the network.")) e6.optional = true end -- Dnsmasq options m1 = Map("dhcp") s1 = m1:section(TypedSection, "dnsmasq", translate("Dnsmasq Options")) s1.anonymous = true d1 = s1:option(Flag, "", translate("Transfer Options To Dnsmasq"), translate("Apply DNSCrypt-Proxy specific settings to the Dnsmasq configuration.
") .. translate("Please note: This may change the values for 'noresolv', 'resolvfile', 'allservers' and the list 'server' settings.")) d1.default = d1.enabled d1.rmempty = false return m, m1