123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429 |
- -- Copyright 2014-2018 Christian Schoenebeck <christian dot schoenebeck at gmail dot com>
- -- Licensed to the public under the Apache License 2.0.
- module("luci.tools.ddns", package.seeall)
- local NX = require "nixio"
- local NXFS = require "nixio.fs"
- local UCI = require "luci.model.uci"
- local SYS = require "luci.sys"
- function env_info(type)
- if ( type == "has_ssl" ) or ( type == "has_proxy" ) or ( type == "has_forceip" )
- or ( type == "has_bindnet" ) or ( type == "has_fetch" )
- or ( type == "has_wgetssl" ) or ( type == "has_curl" )
- or ( type == "has_curlssl" ) or ( type == "has_curlpxy" )
- or ( type == "has_fetchssl" ) or ( type == "has_bbwget" ) then
- local function has_wgetssl()
- return (SYS.call( [[which wget-ssl >/dev/null 2>&1]] ) == 0) -- and true or nil
- end
- local function has_curlssl()
- return (SYS.call( [[$(which curl) -V 2>&1 | grep "Protocols:" | grep -qF "https"]] ) ~= 0)
- end
- local function has_fetch()
- return (SYS.call( [[which uclient-fetch >/dev/null 2>&1]] ) == 0)
- end
- local function has_fetchssl()
- return NXFS.access("/lib/libustream-ssl.so")
- end
- local function has_curl()
- return (SYS.call( [[which curl >/dev/null 2>&1]] ) == 0)
- 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
- if type == "has_wgetssl" then
- return has_wgetssl()
- elseif type == "has_curl" then
- return has_curl()
- elseif type == "has_curlssl" then
- return has_curlssl()
- elseif type == "has_curlpxy" then
- return has_curlpxy()
- elseif type == "has_fetch" then
- return has_fetch()
- elseif type == "has_fetchssl" then
- return has_fetchssl()
- elseif type == "has_bbwget" then
- return has_bbwget()
- elseif type == "has_ssl" then
- if has_wgetssl() then return true end
- if has_curlssl() then return true end
- if (has_fetch() and has_fetchssl()) then return true end
- return false
- elseif type == "has_proxy" then
- if has_wgetssl() then return true end
- if has_curlpxy() then return true end
- if has_fetch() then return true end
- if has_bbwget() then return true end
- return false
- elseif type == "has_forceip" then
- if has_wgetssl() then return true end
- if has_curl() then return true end
- if has_fetch() then return true end -- only really needed for transfer
- return false
- elseif type == "has_bindnet" then
- if has_curl() then return true end
- if has_wgetssl() then return true end
- return false
- end
- elseif ( type == "has_dnsserver" ) or ( type == "has_bindhost" ) or ( type == "has_hostip" ) or ( type == "has_nslookup" ) then
- 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) localhost 2>&1 | grep -qF "(null)"]] ) ~= 0)
- end
- if type == "has_bindhost" then
- return has_bindhost()
- elseif type == "has_hostip" then
- return has_hostip()
- elseif type == "has_nslookup" then
- return has_nslookup()
- elseif type == "has_dnsserver" then
- if has_bindhost() then return true end
- if has_hostip() then return true end
- if has_nslookup() then return true end
- return false
- end
- elseif type == "has_ipv6" then
- return (NXFS.access("/proc/net/ipv6_route") and NXFS.access("/usr/sbin/ip6tables"))
- elseif type == "has_cacerts" then
- --old _check_certs() local function
- local _, v = NXFS.glob("/etc/ssl/certs/*.crt")
- if ( v == 0 ) then _, v = NXFS.glob("/etc/ssl/certs/*.pem") end
- return (v > 0)
- else
- return
- end
- end
- -- function to calculate seconds from given interval and unit
- 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
- -- convert epoch date to given format
- 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
- -- read lastupdate from [section].update file
- function get_lastupd(section)
- local uci = UCI.cursor()
- local rdir = uci:get("ddns", "global", "ddns_rundir") or "/var/run/ddns"
- local etime = tonumber(NXFS.readfile("%s/%s.update" % { rdir, section } ) or 0 )
- uci:unload("ddns")
- return etime
- end
- -- read registered IP from [section].ip file
- function get_regip(section, chk_sec)
- local uci = UCI.cursor()
- local rdir = uci:get("ddns", "global", "ddns_rundir") or "/var/run/ddns"
- local ip = "NOFILE"
- if NXFS.access("%s/%s.ip" % { rdir, section }) then
- local ftime = NXFS.stat("%s/%s.ip" % { rdir, section }, "ctime") or 0
- local otime = os.time()
- -- give ddns-scripts time (9 sec) to update file
- if otime < (ftime + chk_sec + 9) then
- ip = NXFS.readfile("%s/%s.ip" % { rdir, section })
- end
- end
- uci:unload("ddns")
- return ip
- end
- -- read PID from run file and verify if still running
- function get_pid(section)
- local uci = UCI.cursor()
- local rdir = uci:get("ddns", "global", "ddns_rundir") or "/var/run/ddns"
- local pid = tonumber(NXFS.readfile("%s/%s.pid" % { rdir, section } ) or 0 )
- if pid > 0 and not NX.kill(pid, 0) then
- pid = 0
- end
- uci:unload("ddns")
- return pid
- end
- -- replacement of build-in read of UCI option
- -- modified AbstractValue.cfgvalue(self, section) from cbi.lua
- -- needed to read from other option then current value definition
- function read_value(self, section, option)
- local value
- if self.tag_error[section] then
- value = self:formvalue(section)
- else
- value = self.map:get(section, option)
- end
- if not value then
- return nil
- elseif not self.cast or self.cast == type(value) then
- return value
- elseif self.cast == "string" then
- if type(value) == "table" then
- return value[1]
- end
- elseif self.cast == "table" then
- return { value }
- end
- end
- -- replacement of build-in parse of "Value"
- -- modified AbstractValue.parse(self, section, novld) from cbi.lua
- -- validate is called if rmempty/optional true or false
- -- before write check if forcewrite, value eq default, and more
- function value_parse(self, section, novld)
- local fvalue = self:formvalue(section)
- local fexist = ( fvalue and (#fvalue > 0) ) -- not "nil" and "not empty"
- local cvalue = self:cfgvalue(section)
- local rm_opt = ( self.rmempty or self.optional )
- local eq_cfg -- flag: equal cfgvalue
- -- If favlue and cvalue are both tables and have the same content
- -- make them identical
- if type(fvalue) == "table" and type(cvalue) == "table" then
- eq_cfg = (#fvalue == #cvalue)
- if eq_cfg then
- for i=1, #fvalue do
- if cvalue[i] ~= fvalue[i] then
- eq_cfg = false
- end
- end
- end
- if eq_cfg then
- fvalue = cvalue
- end
- end
- -- removed parameter "section" from function call because used/accepted nowhere
- -- also removed call to function "transfer"
- local vvalue, errtxt = self:validate(fvalue)
- -- error handling; validate return "nil"
- if not vvalue then
- if novld then -- and "novld" set
- return -- then exit without raising an error
- end
- if fexist then -- and there is a formvalue
- self:add_error(section, "invalid", errtxt or self.title .. ": invalid")
- return -- so data are invalid
- elseif not rm_opt then -- and empty formvalue but NOT (rmempty or optional) set
- self:add_error(section, "missing", errtxt or self.title .. ": missing")
- return -- so data is missing
- elseif errtxt then
- self:add_error(section, "invalid", errtxt)
- return
- end
- -- error ("\n option: " .. self.option ..
- -- "\n fvalue: " .. tostring(fvalue) ..
- -- "\n fexist: " .. tostring(fexist) ..
- -- "\n cvalue: " .. tostring(cvalue) ..
- -- "\n vvalue: " .. tostring(vvalue) ..
- -- "\n vexist: " .. tostring(vexist) ..
- -- "\n rm_opt: " .. tostring(rm_opt) ..
- -- "\n eq_cfg: " .. tostring(eq_cfg) ..
- -- "\n eq_def: " .. tostring(eq_def) ..
- -- "\n novld : " .. tostring(novld) ..
- -- "\n errtxt: " .. tostring(errtxt) )
- end
- -- lets continue with value returned from validate
- eq_cfg = ( vvalue == cvalue ) -- update equal_config flag
- local vexist = ( vvalue and (#vvalue > 0) ) and true or false -- not "nil" and "not empty"
- local eq_def = ( vvalue == self.default ) -- equal_default flag
- -- (rmempty or optional) and (no data or equal_default)
- if rm_opt and (not vexist or eq_def) then
- if self:remove(section) then -- remove data from UCI
- self.section.changed = true -- and push events
- end
- return
- end
- -- not forcewrite and no changes, so nothing to write
- if not self.forcewrite and eq_cfg then
- return
- end
- -- we should have a valid value here
- assert (vvalue, "\n option: " .. self.option ..
- "\n fvalue: " .. tostring(fvalue) ..
- "\n fexist: " .. tostring(fexist) ..
- "\n cvalue: " .. tostring(cvalue) ..
- "\n vvalue: " .. tostring(vvalue) ..
- "\n vexist: " .. tostring(vexist) ..
- "\n rm_opt: " .. tostring(rm_opt) ..
- "\n eq_cfg: " .. tostring(eq_cfg) ..
- "\n eq_def: " .. tostring(eq_def) ..
- "\n errtxt: " .. tostring(errtxt) )
- -- write data to UCI; raise event only on changes
- if self:write(section, vvalue) and not eq_cfg then
- self.section.changed = true
- end
- end
- -----------------------------------------------------------------------------
- -- copied from https://svn.nmap.org/nmap/nselib/url.lua
- -- @author Diego Nehab
- -- @author Eddie Bell <ejlbell@gmail.com>
- --[[
- URI parsing, composition and relative URL resolution
- LuaSocket toolkit.
- Author: Diego Nehab
- RCS ID: $Id: url.lua,v 1.37 2005/11/22 08:33:29 diego Exp $
- parse_query and build_query added For nmap (Eddie Bell <ejlbell@gmail.com>)
- ]]--
- ---
- -- Parses a URL and returns a table with all its parts according to RFC 2396.
- --
- -- The following grammar describes the names given to the URL parts.
- -- <code>
- -- <url> ::= <scheme>://<authority>/<path>;<params>?<query>#<fragment>
- -- <authority> ::= <userinfo>@<host>:<port>
- -- <userinfo> ::= <user>[:<password>]
- -- <path> :: = {<segment>/}<segment>
- -- </code>
- --
- -- The leading <code>/</code> in <code>/<path></code> is considered part of
- -- <code><path></code>.
- -- @param url URL of request.
- -- @param default Table with default values for each field.
- -- @return A table with the following fields, where RFC naming conventions have
- -- been preserved:
- -- <code>scheme</code>, <code>authority</code>, <code>userinfo</code>,
- -- <code>user</code>, <code>password</code>, <code>host</code>,
- -- <code>port</code>, <code>path</code>, <code>params</code>,
- -- <code>query</code>, and <code>fragment</code>.
- -----------------------------------------------------------------------------
- function parse_url(url) --, default)
- -- initialize default parameters
- local parsed = {}
- -- for i,v in base.pairs(default or parsed) do
- -- parsed[i] = v
- -- end
- -- remove whitespace
- -- url = string.gsub(url, "%s", "")
- -- get fragment
- url = string.gsub(url, "#(.*)$",
- function(f)
- parsed.fragment = f
- return ""
- end)
- -- get scheme. Lower-case according to RFC 3986 section 3.1.
- url = string.gsub(url, "^([%w][%w%+%-%.]*)%:",
- function(s)
- parsed.scheme = string.lower(s);
- return ""
- end)
- -- get authority
- url = string.gsub(url, "^//([^/]*)",
- function(n)
- parsed.authority = n
- return ""
- end)
- -- get query stringing
- url = string.gsub(url, "%?(.*)",
- function(q)
- parsed.query = q
- return ""
- end)
- -- get params
- url = string.gsub(url, "%;(.*)",
- function(p)
- parsed.params = p
- return ""
- end)
- -- path is whatever was left
- parsed.path = url
- local authority = parsed.authority
- if not authority then
- return parsed
- end
- authority = string.gsub(authority,"^([^@]*)@",
- function(u)
- parsed.userinfo = u;
- return ""
- end)
- authority = string.gsub(authority, ":([0-9]*)$",
- function(p)
- if p ~= "" then
- parsed.port = p
- end;
- return ""
- end)
- if authority ~= "" then
- parsed.host = authority
- end
- local userinfo = parsed.userinfo
- if not userinfo then
- return parsed
- end
- userinfo = string.gsub(userinfo, ":([^:]*)$",
- function(p)
- parsed.password = p;
- return ""
- end)
- parsed.user = userinfo
- return parsed
- end
|