summaryrefslogtreecommitdiffhomepage
path: root/applications/luci-app-ddns/root
diff options
context:
space:
mode:
authorAnsuel Smith <ansuelsmth@gmail.com>2019-11-06 01:33:53 +0100
committerAnsuel Smith <ansuelsmth@gmail.com>2019-11-11 20:47:16 +0100
commit34fa5122f98af4ac635dfc26a64f6d2d3e4fafcb (patch)
tree39183ec3b008cd16a23e3fa0a6ab544602aa4408 /applications/luci-app-ddns/root
parent0731f7e5e43f490c39e364e43988a59ee2be9576 (diff)
luci-app-ddns: convert to client side implementation
Signed-off-by: Ansuel Smith <ansuelsmth@gmail.com>
Diffstat (limited to 'applications/luci-app-ddns/root')
-rw-r--r--applications/luci-app-ddns/root/usr/libexec/rpcd/luci.ddns321
-rw-r--r--applications/luci-app-ddns/root/usr/share/rpcd/acl.d/luci-app-ddns.json17
2 files changed, 338 insertions, 0 deletions
diff --git a/applications/luci-app-ddns/root/usr/libexec/rpcd/luci.ddns b/applications/luci-app-ddns/root/usr/libexec/rpcd/luci.ddns
new file mode 100644
index 000000000..46209b908
--- /dev/null
+++ b/applications/luci-app-ddns/root/usr/libexec/rpcd/luci.ddns
@@ -0,0 +1,321 @@
+#!/usr/bin/env lua
+
+local json = require "luci.jsonc"
+local nixio = require "nixio"
+local fs = require "nixio.fs"
+local UCI = require "luci.model.uci"
+local sys = require "luci.sys"
+local util = require "luci.util"
+
+local luci_helper = "/usr/lib/ddns/dynamic_dns_lucihelper.sh"
+local srv_name = "ddns-scripts"
+
+-- convert epoch date to given format
+local function epoch2date(epoch, format)
+ if not format or #format < 2 then
+ local uci = UCI.cursor()
+ format = uci:get("ddns", "global", "ddns_dateformat") or "%F %R"
+ uci:unload("ddns")
+ end
+ format = format:gsub("%%n", "<br />") -- replace newline
+ format = format:gsub("%%t", " ") -- replace tab
+ return os.date(format, epoch)
+end
+
+-- function to calculate seconds from given interval and unit
+local function calc_seconds(interval, unit)
+ if not tonumber(interval) then
+ return nil
+ elseif unit == "days" then
+ return (tonumber(interval) * 86400) -- 60 sec * 60 min * 24 h
+ elseif unit == "hours" then
+ return (tonumber(interval) * 3600) -- 60 sec * 60 min
+ elseif unit == "minutes" then
+ return (tonumber(interval) * 60) -- 60 sec
+ elseif unit == "seconds" then
+ return tonumber(interval)
+ else
+ return nil
+ end
+end
+
+local methods = {
+ get_services_log = {
+ args = { service_name = "service_name" },
+ call = function(args)
+ local result = "File not found or empty"
+ local uci = UCI.cursor()
+
+ local dirlog = uci:get('ddns', 'global', 'ddns_logdir') or "/var/log/ddns"
+
+ -- Fallback to default logdir with unsecure path
+ if dirlog:match('%.%.%/') then dirlog = "/var/log/ddns" end
+
+ if args and args.service_name and fs.access("%s/%s.log" % { dirlog, args.service_name }) then
+ result = fs.readfile("%s/%s.log" % { dirlog, args.service_name })
+ end
+
+ uci.unload()
+
+ return { result = result }
+ end
+ },
+ get_services_status = {
+ call = function()
+ local uci = UCI.cursor()
+
+ local rundir = uci:get("ddns", "global", "ddns_rundir") or "/var/run/ddns"
+ local date_format = uci:get("ddns", "global", "ddns_dateformat")
+ local res = {}
+
+ uci:foreach("ddns", "service", function (s)
+ local ip, last_update, next_update
+ local section = s[".name"]
+ if fs.access("%s/%s.ip" % { rundir, section }) then
+ ip = fs.readfile("%s/%s.ip" % { rundir, section })
+ else
+ local dnsserver = s["dns_server"] or ""
+ local force_ipversion = tonumber(s["force_ipversion"] or 0)
+ local force_dnstcp = tonumber(s["force_dnstcp"] or 0)
+ local is_glue = tonumber(s["is_glue"] or 0)
+ local command = { luci_helper , [[ -]] }
+ local lookup_host = s["lookup_host"] or "_nolookup_"
+
+ if (use_ipv6 == 1) then command[#command+1] = [[6]] end
+ if (force_ipversion == 1) then command[#command+1] = [[f]] end
+ if (force_dnstcp == 1) then command[#command+1] = [[t]] end
+ if (is_glue == 1) then command[#command+1] = [[g]] end
+ command[#command+1] = [[l ]]
+ command[#command+1] = lookup_host
+ command[#command+1] = [[ -S ]]
+ command[#command+1] = section
+ if (#dnsserver > 0) then command[#command+1] = [[ -d ]] .. dnsserver end
+ command[#command+1] = [[ -- get_registered_ip]]
+ line = util.exec(table.concat(command))
+ end
+
+ local last_update = tonumber(fs.readfile("%s/%s.update" % { rundir, section } ) or 0)
+ local next_update, converted_last_update
+ local pid = tonumber(fs.readfile("%s/%s.pid" % { rundir, section } ) or 0)
+
+ if pid > 0 and not nixio.kill(pid, 0) then
+ pid = 0
+ end
+
+ local uptime = sys.uptime()
+
+ local force_seconds = calc_seconds(
+ tonumber(s["force_interval"]) or 72,
+ s["force_unit"] or "hours" )
+
+ -- process running but update needs to happen
+ -- problems if force_seconds > uptime
+ force_seconds = (force_seconds > uptime) and uptime or force_seconds
+
+ if last_update > 0 then
+ local epoch = os.time() - uptime + last_update + force_seconds
+ -- use linux date to convert epoch
+ converted_last_update = epoch2date(epoch,date_format)
+ next_update = epoch2date(epoch + force_seconds)
+ end
+
+ if pid > 0 and ( last_update + force_seconds - uptime ) <= 0 then
+ next_update = "Verify"
+
+ -- run once
+ elseif force_seconds == 0 then
+ next_update = "Run once"
+
+ -- no process running and NOT enabled
+ elseif pid == 0 and s['enabled'] == '0' then
+ next_update = "Disabled"
+
+ -- no process running and enabled
+ elseif pid == 0 and s['enabled'] ~= '0' then
+ next_update = "Stopped"
+ end
+
+ res[section] = {
+ ip = ip and ip:gsub("\n","") or nil,
+ last_update = last_update ~= 0 and converted_last_update or nil,
+ next_update = next_update or nil,
+ pid = pid or nil,
+ }
+ end
+ )
+
+ uci:unload("ddns")
+
+ return res
+
+ end
+ },
+ get_ddns_state = {
+ call = function()
+ local ipkg = require "luci.model.ipkg"
+ local uci = UCI.cursor()
+ local dateformat = uci:get("ddns", "global", "ddns_dateformat") or "%F %R"
+ uci:unload("ddns")
+ local ver, srv_ver_cmd
+ local res = {}
+
+ if ipkg then
+ ver = ipkg.info(srv_name)[srv_name].Version
+ else
+ srv_ver_cmd = luci_helper .. " -V | awk {'print $2'} "
+ ver = util.exec(srv_ver_cmd)
+ end
+
+ res['_version'] = ver and #ver > 0 and ver or nil
+ res['_enabled'] = sys.init.enabled("ddns")
+ res['_curr_dateformat'] = os.date(dateformat)
+
+ return res
+ end
+ },
+ get_env = {
+ call = function()
+ local res = {}
+ local cache = {}
+
+ local function has_wget()
+ return (sys.call( [[which wget >/dev/null 2>&1]] ) == 0)
+ end
+
+ local function has_wgetssl()
+ if cache['has_wgetssl'] then return cache['has_wgetssl'] end
+ local res = (sys.call( [[which wget-ssl >/dev/null 2>&1]] ) == 0)
+ cache['has_wgetssl'] = res
+ return res
+ end
+
+ local function has_curlssl()
+ return (sys.call( [[$(which curl) -V 2>&1 | grep -qF "https"]] ) == 0)
+ end
+
+ local function has_fetch()
+ if cache['has_fetch'] then return cache['has_fetch'] end
+ local res = (sys.call( [[which uclient-fetch >/dev/null 2>&1]] ) == 0)
+ cache['has_fetch'] = res
+ return res
+ end
+
+ local function has_fetchssl()
+ return fs.access("/lib/libustream-ssl.so")
+ end
+
+ local function has_curl()
+ if cache['has_curl'] then return cache['has_curl'] end
+ local res = (sys.call( [[which curl >/dev/null 2>&1]] ) == 0)
+ cache['has_curl'] = res
+ return res
+ end
+
+ local function has_curlpxy()
+ return (sys.call( [[grep -i "all_proxy" /usr/lib/libcurl.so* >/dev/null 2>&1]] ) == 0)
+ end
+
+ local function has_bbwget()
+ return (sys.call( [[$(which wget) -V 2>&1 | grep -iqF "busybox"]] ) == 0)
+ end
+
+ res['has_wget'] = has_wget() or false
+ res['has_curl'] = has_curl() or false
+
+ res['has_ssl'] = has_wgetssl() or has_curlssl() or (has_fetch() and has_fetchssl()) or false
+ res['has_proxy'] = has_wgetssl() or has_curlpxy() or has_fetch() or has_bbwget or false
+ res['has_forceip'] = has_wgetssl() or has_curl() or has_fetch() or false
+ res['has_bindnet'] = has_curl() or has_wgetssl() or false
+
+ local function has_bindhost()
+ if (sys.call( [[which host >/dev/null 2>&1]] ) == 0) then return true end
+ if (sys.call( [[which khost >/dev/null 2>&1]] ) == 0) then return true end
+ if (sys.call( [[which drill >/dev/null 2>&1]] ) == 0) then return true end
+ return false
+ end
+
+ local function has_hostip()
+ return (sys.call( [[which hostip >/dev/null 2>&1]] ) == 0)
+ end
+
+ local function has_nslookup()
+ return (sys.call( [[which nslookup >/dev/null 2>&1]] ) == 0)
+ end
+
+ res['has_dnsserver'] = has_bindhost() or has_hostip() or has_nslookup() or false
+
+ local function check_certs()
+ local _, v = fs.glob("/etc/ssl/certs/*.crt")
+ if ( v == 0 ) then _, v = NXFS.glob("/etc/ssl/certs/*.pem") end
+ return (v > 0)
+ end
+
+ res['has_cacerts'] = check_certs() or false
+
+ res['has_ipv6'] = (fs.access("/proc/net/ipv6_route") and fs.access("/usr/sbin/ip6tables"))
+
+ return res
+ end
+ }
+}
+
+local function parseInput()
+ local parse = json.new()
+ local done, err
+
+ while true do
+ local chunk = io.read(4096)
+ if not chunk then
+ break
+ elseif not done and not err then
+ done, err = parse:parse(chunk)
+ end
+ end
+
+ if not done then
+ print(json.stringify({ error = err or "Incomplete input" }))
+ os.exit(1)
+ end
+
+ return parse:get()
+end
+
+local function validateArgs(func, uargs)
+ local method = methods[func]
+ if not method then
+ print(json.stringify({ error = "Method not found" }))
+ os.exit(1)
+ end
+
+ if type(uargs) ~= "table" then
+ print(json.stringify({ error = "Invalid arguments" }))
+ os.exit(1)
+ end
+
+ uargs.ubus_rpc_session = nil
+
+ local k, v
+ local margs = method.args or {}
+ for k, v in pairs(uargs) do
+ if margs[k] == nil or
+ (v ~= nil and type(v) ~= type(margs[k]))
+ then
+ print(json.stringify({ error = "Invalid arguments" }))
+ os.exit(1)
+ end
+ end
+
+ return method
+end
+
+if arg[1] == "list" then
+ local _, method, rv = nil, nil, {}
+ for _, method in pairs(methods) do rv[_] = method.args or {} end
+ print((json.stringify(rv):gsub(":%[%]", ":{}")))
+elseif arg[1] == "call" then
+ local args = parseInput()
+ local method = validateArgs(arg[2], args)
+ local result, code = method.call(args)
+ print((json.stringify(result):gsub("^%[%]$", "{}")))
+ os.exit(code or 0)
+end
diff --git a/applications/luci-app-ddns/root/usr/share/rpcd/acl.d/luci-app-ddns.json b/applications/luci-app-ddns/root/usr/share/rpcd/acl.d/luci-app-ddns.json
new file mode 100644
index 000000000..aa68bdf03
--- /dev/null
+++ b/applications/luci-app-ddns/root/usr/share/rpcd/acl.d/luci-app-ddns.json
@@ -0,0 +1,17 @@
+{
+ "luci-app-ddns": {
+ "description": "Grant access to ddns procedures",
+ "read": {
+ "ubus": {
+ "luci.ddns": [ "get_services_status", "get_ddns_state", "get_env", "get_services_log" ],
+ "luci": [ "setInitAction" ],
+ },
+ "file": {
+ "/etc/ddns/services": [ "read" ],
+ "/etc/ddns/services_ipv6": [ "read" ],
+ "/usr/lib/ddns/dynamic_dns_lucihelper.sh": [ "exec" ]
+ },
+ "uci": [ "ddns" ]
+ }
+ }
+}