diff --git a/luci-app-nginx-ha/root/etc/uci-defaults/42_luci-nginx-ha b/luci-app-nginx-ha/root/etc/uci-defaults/42_luci-nginx-ha index 459404901..b86d3b1d7 100644 --- a/luci-app-nginx-ha/root/etc/uci-defaults/42_luci-nginx-ha +++ b/luci-app-nginx-ha/root/etc/uci-defaults/42_luci-nginx-ha @@ -7,6 +7,8 @@ uci -q batch <<-EOF >/dev/null commit ucitrack EOF +/etc/init.d/nginx stop >/dev/null 2>&1 +/etc/init.d/nginx disable >/dev/null 2>&1 /etc/init.d/nginx-ha enable >/dev/null 2>&1 rm -f /tmp/luci-indexcache diff --git a/luci-base/Makefile b/luci-base/Makefile index 7d2166ba8..da29b58ba 100644 --- a/luci-base/Makefile +++ b/luci-base/Makefile @@ -13,10 +13,12 @@ LUCI_BASENAME:=base LUCI_TITLE:=LuCI core libraries LUCI_DEPENDS:=+lua +libuci-lua +luci-lib-nixio +luci-lib-ip +rpcd +libubus-lua +luci-lib-jsonc +LUCI_EXTRA_DEPENDS:=libuci-lua (>= 2018-01-01) PKG_SOURCE:=LuaSrcDiet-0.12.1.tar.bz2 PKG_SOURCE_URL:=https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/luasrcdiet -PKG_MD5SUM:=ed7680f2896269ae8633756e7edcf09050812f78c8f49e280e63c30d14f35aea +PKG_HASH:=ed7680f2896269ae8633756e7edcf09050812f78c8f49e280e63c30d14f35aea +PKG_LICENSE:=Apache-2.0 HOST_BUILD_DIR:=$(BUILD_DIR_HOST)/LuaSrcDiet-0.12.1 @@ -25,6 +27,7 @@ include $(INCLUDE_DIR)/host-build.mk define Package/luci-base/conffiles /etc/luci-uploads /etc/config/luci +/etc/config/ucitrack endef include ../luci/luci.mk diff --git a/luci-base/htdocs/luci-static/resources/cbi.js b/luci-base/htdocs/luci-static/resources/cbi.js index 0a362affb..d40ec34bc 100644 --- a/luci-base/htdocs/luci-static/resources/cbi.js +++ b/luci-base/htdocs/luci-static/resources/cbi.js @@ -23,6 +23,62 @@ function Dec(x) { return (/^-?\d+(?:\.\d+)?$/.test(x) ? +x : NaN); } +function IPv4(x) { + if (!x.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/)) + return null; + + if (RegExp.$1 > 255 || RegExp.$2 > 255 || RegExp.$3 > 255 || RegExp.$4 > 255) + return null; + + return [ +RegExp.$1, +RegExp.$2, +RegExp.$3, +RegExp.$4 ]; +} + +function IPv6(x) { + if (x.match(/^([a-fA-F0-9:]+):(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/)) { + var v6 = RegExp.$1, v4 = IPv4(RegExp.$2); + + if (!v4) + return null; + + x = v6 + ':' + (v4[0] * 256 + v4[1]).toString(16) + + ':' + (v4[2] * 256 + v4[3]).toString(16); + } + + if (!x.match(/^[a-fA-F0-9:]+$/)) + return null; + + var prefix_suffix = x.split(/::/); + + if (prefix_suffix.length > 2) + return null; + + var prefix = (prefix_suffix[0] || '0').split(/:/); + var suffix = prefix_suffix.length > 1 ? (prefix_suffix[1] || '0').split(/:/) : []; + + if (suffix.length ? (prefix.length + suffix.length > 7) : (prefix.length > 8)) + return null; + + var i, word; + var words = []; + + for (i = 0, word = parseInt(prefix[0], 16); i < prefix.length; word = parseInt(prefix[++i], 16)) + if (prefix[i].length <= 4 && !isNaN(word) && word <= 0xFFFF) + words.push(word); + else + return null; + + for (i = 0; i < (8 - prefix.length - suffix.length); i++) + words.push(0); + + for (i = 0, word = parseInt(suffix[0], 16); i < suffix.length; word = parseInt(suffix[++i], 16)) + if (suffix[i].length <= 4 && !isNaN(word) && word <= 0xFFFF) + words.push(word); + else + return null; + + return words; +} + var cbi_validators = { 'integer': function() @@ -53,69 +109,63 @@ var cbi_validators = { 'ip4addr': function() { - if (this.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(\/(\S+))?$/)) - { - return (RegExp.$1 >= 0) && (RegExp.$1 <= 255) && - (RegExp.$2 >= 0) && (RegExp.$2 <= 255) && - (RegExp.$3 >= 0) && (RegExp.$3 <= 255) && - (RegExp.$4 >= 0) && (RegExp.$4 <= 255) && - ((RegExp.$6.indexOf('.') < 0) - ? ((RegExp.$6 >= 0) && (RegExp.$6 <= 32)) - : (cbi_validators.ip4addr.apply(RegExp.$6))) - ; - } - - return false; + var m = this.match(/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:\/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|\/(\d{1,2}))?$/); + return !!(m && IPv4(m[1]) && (m[2] ? IPv4(m[2]) : (m[3] ? cbi_validators.ip4prefix.apply(m[3]) : true))); }, 'ip6addr': function() { - if( this.match(/^([a-fA-F0-9:.]+)(\/(\d+))?$/) ) - { - if( !RegExp.$2 || ((RegExp.$3 >= 0) && (RegExp.$3 <= 128)) ) - { - var addr = RegExp.$1; + var m = this.match(/^([0-9a-fA-F:.]+)(?:\/(\d{1,3}))?$/); + return !!(m && IPv6(m[1]) && (m[2] ? cbi_validators.ip6prefix.apply(m[2]) : true)); + }, - if( addr == '::' ) - { - return true; - } + 'ip4prefix': function() + { + return !isNaN(this) && this >= 0 && this <= 32; + }, - if( addr.indexOf('.') > 0 ) - { - var off = addr.lastIndexOf(':'); + 'ip6prefix': function() + { + return !isNaN(this) && this >= 0 && this <= 128; + }, - if( !(off && cbi_validators.ip4addr.apply(addr.substr(off+1))) ) - return false; + 'cidr': function() + { + return cbi_validators.cidr4.apply(this) || + cbi_validators.cidr6.apply(this); + }, - addr = addr.substr(0, off) + ':0:0'; - } + 'cidr4': function() + { + var m = this.match(/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(\d{1,2})$/); + return !!(m && IPv4(m[1]) && cbi_validators.ip4prefix.apply(m[2])); + }, - if( addr.indexOf('::') >= 0 ) - { - var colons = 0; - var fill = '0'; + 'cidr6': function() + { + var m = this.match(/^([0-9a-fA-F:.]+)\/(\d{1,3})$/); + return !!(m && IPv6(m[1]) && cbi_validators.ip6prefix.apply(m[2])); + }, - for( var i = 1; i < (addr.length-1); i++ ) - if( addr.charAt(i) == ':' ) - colons++; + 'ipnet4': function() + { + var m = this.match(/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/); + return !!(m && IPv4(m[1]) && IPv4(m[2])); + }, - if( colons > 7 ) - return false; + 'ipnet6': function() + { + var m = this.match(/^([0-9a-fA-F:.]+)\/([0-9a-fA-F:.]+)$/); + return !!(m && IPv6(m[1]) && IPv6(m[2])); + }, - for( var i = 0; i < (7 - colons); i++ ) - fill += ':0'; + 'ip6hostid': function() + { + if (this == "eui64" || this == "random") + return true; - if (addr.match(/^(.*?)::(.*?)$/)) - addr = (RegExp.$1 ? RegExp.$1 + ':' : '') + fill + - (RegExp.$2 ? ':' + RegExp.$2 : ''); - } - - return (addr.match(/^(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$/) != null); - } - } - - return false; + var v6 = IPv6(this); + return !(!v6 || v6[0] || v6[1] || v6[2] || v6[3]); }, 'ipmask': function() @@ -126,40 +176,16 @@ var cbi_validators = { 'ipmask4': function() { - var ip = this, mask = 32; - - if (ip.match(/^(\S+)\/(\S+)$/)) - { - ip = RegExp.$1; - mask = RegExp.$2; - } - - if (!isNaN(mask) && (mask < 0 || mask > 32)) - return false; - - if (isNaN(mask) && !cbi_validators.ip4addr.apply(mask)) - return false; - - return cbi_validators.ip4addr.apply(ip); + return cbi_validators.cidr4.apply(this) || + cbi_validators.ipnet4.apply(this) || + cbi_validators.ip4addr.apply(this); }, 'ipmask6': function() { - var ip = this, mask = 128; - - if (ip.match(/^(\S+)\/(\S+)$/)) - { - ip = RegExp.$1; - mask = RegExp.$2; - } - - if (!isNaN(mask) && (mask < 0 || mask > 128)) - return false; - - if (isNaN(mask) && !cbi_validators.ip6addr.apply(mask)) - return false; - - return cbi_validators.ip6addr.apply(ip); + return cbi_validators.cidr6.apply(this) || + cbi_validators.ipnet6.apply(this) || + cbi_validators.ip6addr.apply(this); }, 'port': function() @@ -481,8 +507,9 @@ function cbi_d_check(deps) { istat = (istat && cbi_d_checkvalue(j, deps[i][j])) } } - if (istat) { - return !reverse; + + if (istat ^ reverse) { + return true; } } return def; @@ -648,9 +675,6 @@ function cbi_combobox(id, values, def, man, focus) { var dt = obj.getAttribute('cbi_datatype'); var op = obj.getAttribute('cbi_optional'); - if (dt) - cbi_validate_field(sel, op == 'true', dt); - if (!values[obj.value]) { if (obj.value == "") { var optdef = document.createElement("option"); @@ -685,6 +709,9 @@ function cbi_combobox(id, values, def, man, focus) { obj.style.display = "none"; + if (dt) + cbi_validate_field(sel, op == 'true', dt); + cbi_bind(sel, "change", function() { if (sel.selectedIndex == sel.options.length - 1) { obj.style.display = "inline"; @@ -727,7 +754,7 @@ function cbi_filebrowser(id, defpath) { browser.focus(); } -function cbi_browser_init(id, defpath) +function cbi_browser_init(id, resource, defpath) { function cbi_browser_btnclick(e) { cbi_filebrowser(id, defpath); @@ -738,7 +765,7 @@ function cbi_browser_init(id, defpath) var btn = document.createElement('img'); btn.className = 'cbi-image-button'; - btn.src = cbi_strings.path.resource + '/cbi/folder.gif'; + btn.src = (resource || cbi_strings.path.resource) + '/cbi/folder.gif'; field.parentNode.insertBefore(btn, field.nextSibling); cbi_bind(btn, 'click', cbi_browser_btnclick); @@ -805,7 +832,7 @@ function cbi_dynlist_init(parent, datatype, optional, choices) parent.appendChild(b); if (datatype == 'file') { - cbi_browser_init(t.id, parent.getAttribute('data-browser-path')); + cbi_browser_init(t.id, null, parent.getAttribute('data-browser-path')); } parent.appendChild(document.createElement('br')); diff --git a/luci-base/htdocs/luci-static/resources/xhr.js b/luci-base/htdocs/luci-static/resources/xhr.js index 701c12ac1..3385f8f23 100644 --- a/luci-base/htdocs/luci-static/resources/xhr.js +++ b/luci-base/htdocs/luci-static/resources/xhr.js @@ -91,8 +91,6 @@ XHR = function() xhr.open('POST', url, true); xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); - xhr.setRequestHeader('Content-length', code.length); - xhr.setRequestHeader('Connection', 'close'); xhr.send(code); } diff --git a/luci-base/luasrc/cbi/datatypes.lua b/luci-base/luasrc/cbi/datatypes.lua index 036d6ff5e..55cdf8a74 100644 --- a/luci-base/luasrc/cbi/datatypes.lua +++ b/luci-base/luasrc/cbi/datatypes.lua @@ -1,4 +1,5 @@ -- Copyright 2010 Jo-Philipp Wich +-- Copyright 2017 Dan Luedtke -- Licensed to the public under the Apache License 2.0. local fs = require "nixio.fs" @@ -131,43 +132,50 @@ function ip6prefix(val) return ( val and val >= 0 and val <= 128 ) end +function cidr4(val) + local ip, mask = val:match("^([^/]+)/([^/]+)$") + + return ip4addr(ip) and ip4prefix(mask) +end + +function cidr6(val) + local ip, mask = val:match("^([^/]+)/([^/]+)$") + + return ip6addr(ip) and ip6prefix(mask) +end + +function ipnet4(val) + local ip, mask = val:match("^([^/]+)/([^/]+)$") + + return ip4addr(ip) and ip4addr(mask) +end + +function ipnet6(val) + local ip, mask = val:match("^([^/]+)/([^/]+)$") + + return ip6addr(ip) and ip6addr(mask) +end + function ipmask(val) return ipmask4(val) or ipmask6(val) end function ipmask4(val) - local ip, mask = val:match("^([^/]+)/([^/]+)$") - local bits = tonumber(mask) - - if bits and (bits < 0 or bits > 32) then - return false - end - - if not bits and mask and not ip4addr(mask) then - return false - end - - return ip4addr(ip or val) + return cidr4(val) or ipnet4(val) or ip4addr(val) end function ipmask6(val) - local ip, mask = val:match("^([^/]+)/([^/]+)$") - local bits = tonumber(mask) - - if bits and (bits < 0 or bits > 128) then - return false - end - - if not bits and mask and not ip6addr(mask) then - return false - end - - return ip6addr(ip or val) + return cidr6(val) or ipnet6(val) or ip6addr(val) end function ip6hostid(val) - if val and val:match("^[a-fA-F0-9:]+$") and (#val > 2) then - return (ip6addr("2001:db8:0:0" .. val) or ip6addr("2001:db8:0:0:" .. val)) + if val == "eui64" or val == "random" then + return true + else + local addr = ip.IPv6(val) + if addr and addr:prefix() == 128 and addr:lower("::1:0:0:0:0") then + return true + end end return false @@ -188,23 +196,7 @@ function portrange(val) end function macaddr(val) - if val and val:match( - "^[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+:" .. - "[a-fA-F0-9]+:[a-fA-F0-9]+:[a-fA-F0-9]+$" - ) then - local parts = util.split( val, ":" ) - - for i = 1,6 do - parts[i] = tonumber( parts[i], 16 ) - if parts[i] < 0 or parts[i] > 255 then - return false - end - end - - return true - end - - return false + return ip.checkmac(val) and true or false end function hostname(val) @@ -301,7 +293,7 @@ function string(val) return true -- Everything qualifies as valid string end -function directory( val, seen ) +function directory(val, seen) local s = fs.stat(val) seen = seen or { } @@ -317,7 +309,7 @@ function directory( val, seen ) return false end -function file( val, seen ) +function file(val, seen) local s = fs.stat(val) seen = seen or { } @@ -333,7 +325,7 @@ function file( val, seen ) return false end -function device( val, seen ) +function device(val, seen) local s = fs.stat(val) seen = seen or { } @@ -468,4 +460,3 @@ function dateyyyymmdd(val) end return false end - diff --git a/luci-base/luasrc/dispatcher.lua b/luci-base/luasrc/dispatcher.lua index 0bd19456f..16b32548e 100644 --- a/luci-base/luasrc/dispatcher.lua +++ b/luci-base/luasrc/dispatcher.lua @@ -14,8 +14,6 @@ uci = require "luci.model.uci" i18n = require "luci.i18n" _M.fs = fs -authenticator = {} - -- Index table local index = nil @@ -101,24 +99,6 @@ function error500(message) return false end -function authenticator.htmlauth(validator, accs, default, template) - local user = http.formvalue("luci_username") - local pass = http.formvalue("luci_password") - - if user and validator(user, pass) then - return user - end - - require("luci.i18n") - require("luci.template") - context.path = {} - http.status(403, "Forbidden") - luci.template.render(template or "sysauth", {duser=default, fuser=user}) - - return false - -end - function httpdispatch(request, prefix) http.context.request = request @@ -188,6 +168,53 @@ function test_post_security() return true end +local function session_retrieve(sid, allowed_users) + local sdat = util.ubus("session", "get", { ubus_rpc_session = sid }) + + if type(sdat) == "table" and + type(sdat.values) == "table" and + type(sdat.values.token) == "string" and + (not allowed_users or + util.contains(allowed_users, sdat.values.username)) + then + return sid, sdat.values + end + + return nil, nil +end + +local function session_setup(user, pass, allowed_users) + if util.contains(allowed_users, user) then + local login = util.ubus("session", "login", { + username = user, + password = pass, + timeout = tonumber(luci.config.sauth.sessiontime) + }) + + local rp = context.requestpath + and table.concat(context.requestpath, "/") or "" + + if type(login) == "table" and + type(login.ubus_rpc_session) == "string" + then + util.ubus("session", "set", { + ubus_rpc_session = login.ubus_rpc_session, + values = { token = sys.uniqueid(16) } + }) + + io.stderr:write("luci: accepted login on /%s for %s from %s\n" + %{ rp, user, http.getenv("REMOTE_ADDR") or "?" }) + + return session_retrieve(login.ubus_rpc_session) + end + + io.stderr:write("luci: failed login on /%s for %s from %s\n" + %{ rp, user, http.getenv("REMOTE_ADDR") or "?" }) + end + + return nil, nil +end + function dispatch(request) --context._disable_memtrace = require "luci.debug".trap_memtrace("l") local ctx = context @@ -201,10 +228,19 @@ function dispatch(request) local lang = conf.main.lang or "auto" if lang == "auto" then local aclang = http.getenv("HTTP_ACCEPT_LANGUAGE") or "" - for lpat in aclang:gmatch("[%w-]+") do - lpat = lpat and lpat:gsub("-", "_") - if conf.languages[lpat] then - lang = lpat + for aclang in aclang:gmatch("[%w_-]+") do + local country, culture = aclang:match("^([a-z][a-z])[_-]([a-zA-Z][a-zA-Z])$") + if country and culture then + local cc = "%s_%s" %{ country, culture:lower() } + if conf.languages[cc] then + lang = cc + break + elseif conf.languages[country] then + lang = country + break + end + elseif conf.languages[aclang] then + lang = aclang break end end @@ -331,75 +367,66 @@ function dispatch(request) "https://github.com/openwrt/luci/issues" ) - if track.sysauth then - local authen = type(track.sysauth_authenticator) == "function" - and track.sysauth_authenticator - or authenticator[track.sysauth_authenticator] + if track.sysauth and not ctx.authsession then + local authen = track.sysauth_authenticator + local _, sid, sdat, default_user, allowed_users - local def = (type(track.sysauth) == "string") and track.sysauth - local accs = def and {track.sysauth} or track.sysauth - local sess = ctx.authsession - if not sess then - sess = http.getcookie("sysauth") - sess = sess and sess:match("^[a-f0-9]*$") + if type(authen) == "string" and authen ~= "htmlauth" then + error500("Unsupported authenticator %q configured" % authen) + return end - local sdat = (util.ubus("session", "get", { ubus_rpc_session = sess }) or { }).values - local user, token - - if sdat then - user = sdat.user - token = sdat.token + if type(track.sysauth) == "table" then + default_user, allowed_users = nil, track.sysauth else - local eu = http.getenv("HTTP_AUTH_USER") - local ep = http.getenv("HTTP_AUTH_PASS") - if eu and ep and sys.user.checkpasswd(eu, ep) then - authen = function() return eu end - end + default_user, allowed_users = track.sysauth, { track.sysauth } end - if not util.contains(accs, user) then - if authen then - local user, sess = authen(sys.user.checkpasswd, accs, def, track.sysauth_template) - local token - if not user or not util.contains(accs, user) then - return - else - if not sess then - local sdat = util.ubus("session", "create", { timeout = tonumber(luci.config.sauth.sessiontime) }) - if sdat then - token = sys.uniqueid(16) - util.ubus("session", "set", { - ubus_rpc_session = sdat.ubus_rpc_session, - values = { - user = user, - token = token, - section = sys.uniqueid(16) - } - }) - sess = sdat.ubus_rpc_session - end - end + if type(authen) == "function" then + _, sid = authen(sys.user.checkpasswd, allowed_users) + else + sid = http.getcookie("sysauth") + end - if sess and token then - http.header("Set-Cookie", 'sysauth=%s; path=%s' %{ sess, build_url() }) + sid, sdat = session_retrieve(sid, allowed_users) - ctx.authsession = sess - ctx.authtoken = token - ctx.authuser = user + if not (sid and sdat) and authen == "htmlauth" then + local user = http.getenv("HTTP_AUTH_USER") + local pass = http.getenv("HTTP_AUTH_PASS") + + if user == nil and pass == nil then + user = http.formvalue("luci_username") + pass = http.formvalue("luci_password") + end + + sid, sdat = session_setup(user, pass, allowed_users) + + if not sid then + local tmpl = require "luci.template" + + context.path = {} - http.redirect(build_url(unpack(ctx.requestpath))) - end - end - else http.status(403, "Forbidden") + tmpl.render(track.sysauth_template or "sysauth", { + duser = default_user, + fuser = user + }) + return end - else - ctx.authsession = sess - ctx.authtoken = token - ctx.authuser = user + + http.header("Set-Cookie", 'sysauth=%s; path=%s' %{ sid, build_url() }) + http.redirect(build_url(unpack(ctx.requestpath))) end + + if not sid or not sdat then + http.status(403, "Forbidden") + return + end + + ctx.authsession = sid + ctx.authtoken = sdat.token + ctx.authuser = sdat.username end if c and require_post_security(c.target) then diff --git a/luci-base/luasrc/http.lua b/luci-base/luasrc/http.lua index 8795dfc4b..9cc985786 100644 --- a/luci-base/luasrc/http.lua +++ b/luci-base/luasrc/http.lua @@ -224,7 +224,15 @@ function write(content, src_err) header("Cache-Control", "no-cache") header("Expires", "0") end - + if not context.headers["x-frame-options"] then + header("X-Frame-Options", "SAMEORIGIN") + end + if not context.headers["x-xss-protection"] then + header("X-XSS-Protection", "1; mode=block") + end + if not context.headers["x-content-type-options"] then + header("X-Content-Type-Options", "nosniff") + end context.eoh = true coroutine.yield(3) diff --git a/luci-base/luasrc/http/protocol.lua b/luci-base/luasrc/http/protocol.lua index 061c6ad54..0a8b2fbab 100644 --- a/luci-base/luasrc/http/protocol.lua +++ b/luci-base/luasrc/http/protocol.lua @@ -264,7 +264,7 @@ function header_source( sock ) end -- Content-Type. Stores all extracted data associated with its parameter name --- in the params table withing the given message object. Multiple parameter +-- in the params table within the given message object. Multiple parameter -- values are stored as tables, ordinary ones as strings. -- If an optional file callback function is given then it is feeded with the -- file contents chunk by chunk and only the extracted file name is stored @@ -433,7 +433,7 @@ function mimedecode_message_body( src, msg, filecb ) end -- Content-Type. Stores all extracted data associated with its parameter name --- in the params table withing the given message object. Multiple parameter +-- in the params table within the given message object. Multiple parameter -- values are stored as tables, ordinary ones as strings. function urldecode_message_body( src, msg ) diff --git a/luci-base/luasrc/http/protocol.luadoc b/luci-base/luasrc/http/protocol.luadoc index 67a60d9e7..19a0a3419 100644 --- a/luci-base/luasrc/http/protocol.luadoc +++ b/luci-base/luasrc/http/protocol.luadoc @@ -69,7 +69,7 @@ data line by line with the trailing \r\n stripped of. Decode a mime encoded http message body with multipart/form-data Content-Type. Stores all extracted data associated with its parameter name -in the params table withing the given message object. Multiple parameter +in the params table within the given message object. Multiple parameter values are stored as tables, ordinary ones as strings. If an optional file callback function is given then it is feeded with the file contents chunk by chunk and only the extracted file name is stored @@ -92,7 +92,7 @@ with three arguments: Decode an urlencoded http message body with application/x-www-urlencoded Content-Type. Stores all extracted data associated with its parameter name -in the params table withing the given message object. Multiple parameter +in the params table within the given message object. Multiple parameter values are stored as tables, ordinary ones as strings. @class function @name urldecode_message_body diff --git a/luci-base/luasrc/model/firewall.lua b/luci-base/luasrc/model/firewall.lua index 5573a9b86..feff0855c 100644 --- a/luci-base/luasrc/model/firewall.lua +++ b/luci-base/luasrc/model/firewall.lua @@ -498,11 +498,13 @@ function forwarding.dest(self) end function forwarding.src_zone(self) - return zone(self:src()) + local z = zone(self:src()) + return z.sid and z end function forwarding.dest_zone(self) - return zone(self:dest()) + local z = zone(self:dest()) + return z.sid and z end diff --git a/luci-base/luasrc/model/network.lua b/luci-base/luasrc/model/network.lua index 49d91b875..056fc67b1 100644 --- a/luci-base/luasrc/model/network.lua +++ b/luci-base/luasrc/model/network.lua @@ -6,14 +6,12 @@ local type, next, pairs, ipairs, loadfile, table, select local tonumber, tostring, math = tonumber, tostring, math -local require = require +local pcall, require, setmetatable = pcall, require, setmetatable local nxo = require "nixio" local nfs = require "nixio.fs" local ipc = require "luci.ip" -local sys = require "luci.sys" local utl = require "luci.util" -local dsp = require "luci.dispatcher" local uci = require "luci.model.uci" local lng = require "luci.i18n" local jsc = require "luci.jsonc" @@ -108,6 +106,58 @@ function _set(c, s, o, v) end end +local function _wifi_state() + if not next(_ubuswificache) then + _ubuswificache = utl.ubus("network.wireless", "status", {}) or {} + end + return _ubuswificache +end + +local function _wifi_state_by_sid(sid) + local t1, n1 = _uci:get("wireless", sid) + if t1 == "wifi-iface" and n1 ~= nil then + local radioname, radiostate + for radioname, radiostate in pairs(_wifi_state()) do + if type(radiostate) == "table" and + type(radiostate.interfaces) == "table" + then + local netidx, netstate + for netidx, netstate in ipairs(radiostate.interfaces) do + if type(netstate) == "table" and + type(netstate.section) == "string" + then + local t2, n2 = _uci:get("wireless", netstate.section) + if t1 == t2 and n1 == n2 then + return radioname, radiostate, netstate + end + end + end + end + end + end +end + +local function _wifi_state_by_ifname(ifname) + if type(ifname) == "string" then + local radioname, radiostate + for radioname, radiostate in pairs(_wifi_state()) do + if type(radiostate) == "table" and + type(radiostate.interfaces) == "table" + then + local netidx, netstate + for netidx, netstate in ipairs(radiostate.interfaces) do + if type(netstate) == "table" and + type(netstate.ifname) == "string" and + netstate.ifname == ifname + then + return radioname, radiostate, netstate + end + end + end + end + end +end + function _wifi_iface(x) local _, p for _, p in ipairs(IFACE_PATTERNS_WIRELESS) do @@ -118,59 +168,111 @@ function _wifi_iface(x) return false end -function _wifi_state(key, val, field) - local radio, radiostate, ifc, ifcstate +local function _wifi_iwinfo_by_ifname(ifname, force_phy_only) + local stat, iwinfo = pcall(require, "iwinfo") + local iwtype = stat and type(ifname) == "string" and iwinfo.type(ifname) + local is_nonphy_op = { + bitrate = true, + quality = true, + quality_max = true, + mode = true, + ssid = true, + bssid = true, + assoclist = true, + encryption = true + } - if not next(_ubuswificache) then - _ubuswificache = utl.ubus("network.wireless", "status", {}) or {} + if iwtype then + -- if we got a type but no real netdev, we're referring to a phy + local phy_only = force_phy_only or (ipc.link(ifname).type ~= 1) - -- workaround extended section format - for radio, radiostate in pairs(_ubuswificache) do - for ifc, ifcstate in pairs(radiostate.interfaces) do - if ifcstate.section and ifcstate.section:sub(1, 1) == '@' then - local s = _uci:get_all('wireless.%s' % ifcstate.section) - if s then - ifcstate.section = s['.name'] - end + return setmetatable({}, { + __index = function(t, k) + if k == "ifname" then + return ifname + elseif phy_only and is_nonphy_op[k] then + return nil + elseif iwinfo[iwtype][k] then + return iwinfo[iwtype][k](ifname) end end - end + }) end +end - for radio, radiostate in pairs(_ubuswificache) do - for ifc, ifcstate in pairs(radiostate.interfaces) do - if ifcstate[key] == val then - return ifcstate[field] - end +local function _wifi_sid_by_netid(netid) + if type(netid) == "string" then + local radioname, netidx = netid:match("^(%w+)%.network(%d+)$") + if radioname and netidx then + local i, n = 0, nil + + netidx = tonumber(netidx) + _uci:foreach("wireless", "wifi-iface", + function(s) + if s.device == radioname then + i = i + 1 + if i == netidx then + n = s[".name"] + return false + end + end + end) + + return n end end end -function _wifi_lookup(ifn) - -- got a radio#.network# pseudo iface, locate the corresponding section - local radio, ifnidx = ifn:match("^(%w+)%.network(%d+)$") - if radio and ifnidx then - local sid = nil - local num = 0 - - ifnidx = tonumber(ifnidx) - _uci:foreach("wireless", "wifi-iface", - function(s) - if s.device == radio then - num = num + 1 - if num == ifnidx then - sid = s['.name'] - return false - end - end - end) - +function _wifi_sid_by_ifname(ifn) + local sid = _wifi_sid_by_netid(ifn) + if sid then return sid - - -- looks like wifi, try to locate the section via ubus state - elseif _wifi_iface(ifn) then - return _wifi_state("ifname", ifn, "section") end + + local _, _, netstate = _wifi_state_by_ifname(ifn) + if netstate and type(netstate.section) == "string" then + return netstate.section + end +end + +local function _wifi_netid_by_sid(sid) + local t, n = _uci:get("wireless", sid) + if t == "wifi-iface" and n ~= nil then + local radioname = _uci:get("wireless", n, "device") + if type(radioname) == "string" then + local i, netid = 0, nil + + _uci:foreach("wireless", "wifi-iface", + function(s) + if s.device == radioname then + i = i + 1 + if s[".name"] == n then + netid = "%s.network%d" %{ radioname, i } + return false + end + end + end) + + return netid, radioname + end + end +end + +local function _wifi_netid_by_netname(name) + local netid = nil + + _uci:foreach("wireless", "wifi-iface", + function(s) + local net + for net in utl.imatch(s.network) do + if net == name then + netid = _wifi_netid_by_sid(s[".name"]) + return false + end + end + end) + + return netid end function _iface_virtual(x) @@ -228,7 +330,7 @@ function init(cursor) if i.family == "packet" then _interfaces[name].flags = i.flags _interfaces[name].stats = i.data - _interfaces[name].macaddr = i.addr + _interfaces[name].macaddr = ipc.checkmac(i.addr) elseif i.family == "inet" then _interfaces[name].ipaddrs[#_interfaces[name].ipaddrs+1] = ipc.IPv4(i.addr, i.netmask) elseif i.family == "inet6" then @@ -441,6 +543,9 @@ end function del_network(self, n) local r = _uci:delete("network", n) if r then + _uci:delete_all("luci", "ifstate", + function(s) return (s.interface == n) end) + _uci:delete_all("network", "alias", function(s) return (s.interface == n) end) @@ -524,20 +629,8 @@ function get_interface(self, i) if _interfaces[i] or _wifi_iface(i) then return interface(i) else - local ifc - local num = { } - _uci:foreach("wireless", "wifi-iface", - function(s) - if s.device then - num[s.device] = num[s.device] and num[s.device] + 1 or 1 - if s['.name'] == i then - ifc = interface( - "%s.network%d" %{s.device, num[s.device] }) - return false - end - end - end) - return ifc + local netid = _wifi_netid_by_netname(i) + return netid and interface(netid) end end @@ -644,7 +737,7 @@ function get_wifidevs(self) end function get_wifinet(self, net) - local wnet = _wifi_lookup(net) + local wnet = _wifi_sid_by_ifname(net) if wnet then return wifinet(wnet) end @@ -660,7 +753,7 @@ function add_wifinet(self, net, options) end function del_wifinet(self, net) - local wnet = _wifi_lookup(net) + local wnet = _wifi_sid_by_ifname(net) if wnet then _uci:delete("wireless", wnet) return true @@ -784,22 +877,7 @@ function protocol.ifname(self) ifname = self:_ubus("device") end if not ifname then - local num = { } - _uci:foreach("wireless", "wifi-iface", - function(s) - if s.device then - num[s.device] = num[s.device] - and num[s.device] + 1 or 1 - - local net - for net in utl.imatch(s.network) do - if net == self.sid then - ifname = "%s.network%d" %{ s.device, num[s.device] } - return false - end - end - end - end) + ifname = _wifi_netid_by_netname(self.sid) end return ifname end @@ -923,7 +1001,15 @@ function protocol.ip6addrs(self) if type(addrs) == "table" then for n, addr in ipairs(addrs) do - rv[#rv+1] = "%s1/%d" %{ addr.address, addr.mask } + if type(addr["local-address"]) == "table" and + type(addr["local-address"].mask) == "number" and + type(addr["local-address"].address) == "string" + then + rv[#rv+1] = "%s/%d" %{ + addr["local-address"].address, + addr["local-address"].mask + } + end end end @@ -981,24 +1067,17 @@ function protocol.is_empty(self) if self:is_floating() then return false else - local rv = true + local empty = true if (self:_get("ifname") or ""):match("%S+") then - rv = false + empty = false end - _uci:foreach("wireless", "wifi-iface", - function(s) - local n - for n in utl.imatch(s.network) do - if n == self.sid then - rv = false - return false - end - end - end) + if empty and _wifi_netid_by_netname(self.sid) then + empty = false + end - return rv + return empty end end @@ -1006,7 +1085,7 @@ function protocol.add_interface(self, ifname) ifname = _M:ifnameof(ifname) if ifname and not self:is_floating() then -- if its a wifi interface, change its network option - local wif = _wifi_lookup(ifname) + local wif = _wifi_sid_by_ifname(ifname) if wif then _append("wireless", wif, "network", self.sid) @@ -1021,7 +1100,7 @@ function protocol.del_interface(self, ifname) ifname = _M:ifnameof(ifname) if ifname and not self:is_floating() then -- if its a wireless interface, clear its network option - local wif = _wifi_lookup(ifname) + local wif = _wifi_sid_by_ifname(ifname) if wif then _filter("wireless", wif, "network", self.sid) end -- remove the interface @@ -1043,21 +1122,7 @@ function protocol.get_interface(self) ifn = ifn:match("^[^:/]+") return ifn and interface(ifn, self) end - ifn = nil - _uci:foreach("wireless", "wifi-iface", - function(s) - if s.device then - num[s.device] = num[s.device] and num[s.device] + 1 or 1 - - local net - for net in utl.imatch(s.network) do - if net == self.sid then - ifn = "%s.network%d" %{ s.device, num[s.device] } - return false - end - end - end - end) + ifn = _wifi_netid_by_netname(self.sid) return ifn and interface(ifn, self) end end @@ -1077,18 +1142,17 @@ function protocol.get_interfaces(self) ifaces[#ifaces+1] = nfs[ifn] end - local num = { } local wfs = { } _uci:foreach("wireless", "wifi-iface", function(s) if s.device then - num[s.device] = num[s.device] and num[s.device] + 1 or 1 - local net for net in utl.imatch(s.network) do if net == self.sid then - ifn = "%s.network%d" %{ s.device, num[s.device] } - wfs[ifn] = interface(ifn, self) + ifn = _wifi_netid_by_sid(s[".name"]) + if ifn then + wfs[ifn] = interface(ifn, self) + end end end end @@ -1119,7 +1183,7 @@ function protocol.contains_interface(self, ifname) end end - local wif = _wifi_lookup(ifname) + local wif = _wifi_sid_by_ifname(ifname) if wif then local n for n in utl.imatch(_uci:get("wireless", wif, "network")) do @@ -1134,17 +1198,18 @@ function protocol.contains_interface(self, ifname) end function protocol.adminlink(self) - return dsp.build_url("admin", "network", "network", self.sid) + local stat, dsp = pcall(require, "luci.dispatcher") + return stat and dsp.build_url("admin", "network", "network", self.sid) end interface = utl.class() function interface.__init__(self, ifname, network) - local wif = _wifi_lookup(ifname) + local wif = _wifi_sid_by_ifname(ifname) if wif then self.wif = wifinet(wif) - self.ifname = _wifi_state("section", wif, "ifname") + self.ifname = self.wif:ifname() end self.ifname = self.ifname or ifname @@ -1168,8 +1233,7 @@ function interface.name(self) end function interface.mac(self) - local mac = self:_ubus("macaddr") - return mac and mac:upper() + return ipc.checkmac(self:_ubus("macaddr")) end function interface.ipaddrs(self) @@ -1332,9 +1396,14 @@ end wifidev = utl.class() -function wifidev.__init__(self, dev) - self.sid = dev - self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { } +function wifidev.__init__(self, name) + local t, n = _uci:get("wireless", name) + if t == "wifi-device" and n ~= nil then + self.sid = n + self.iwinfo = _wifi_iwinfo_by_ifname(self.sid, true) + end + self.sid = self.sid or name + self.iwinfo = self.iwinfo or { ifname = self.sid } end function wifidev.get(self, opt) @@ -1362,8 +1431,6 @@ function wifidev.get_i18n(self) local t = "Generic" if self.iwinfo.type == "wl" then t = "Broadcom" - elseif self.iwinfo.type == "madwifi" then - t = "Atheros" end local m = "" @@ -1389,7 +1456,7 @@ function wifidev.get_wifinet(self, net) if _uci:get("wireless", net) == "wifi-iface" then return wifinet(net) else - local wnet = _wifi_lookup(net) + local wnet = _wifi_sid_by_ifname(net) if wnet then return wifinet(wnet) end @@ -1423,7 +1490,7 @@ function wifidev.del_wifinet(self, net) if utl.instanceof(net, wifinet) then net = net.sid elseif _uci:get("wireless", net) ~= "wifi-iface" then - net = _wifi_lookup(net) + net = _wifi_sid_by_ifname(net) end if net and _uci:get("wireless", net, "device") == self.sid then @@ -1437,49 +1504,50 @@ end wifinet = utl.class() -function wifinet.__init__(self, net, data) - self.sid = net - - local n = 0 - local num = { } - local netid, sid - _uci:foreach("wireless", "wifi-iface", - function(s) - n = n + 1 - if s.device then - num[s.device] = num[s.device] and num[s.device] + 1 or 1 - if s['.name'] == self.sid then - sid = "@wifi-iface[%d]" % n - netid = "%s.network%d" %{ s.device, num[s.device] } - return false - end - end - end) +function wifinet.__init__(self, name, data) + local sid, netid, radioname, radiostate, netstate + -- lookup state by radio#.network# notation + sid = _wifi_sid_by_netid(name) if sid then - local _, k, r, i - for k, r in pairs(_ubuswificache) do - if type(r) == "table" and - type(r.interfaces) == "table" - then - for _, i in ipairs(r.interfaces) do - if type(i) == "table" and i.section == sid then - self._ubusdata = { - radio = k, - dev = r, - net = i - } - end + netid = name + radioname, radiostate, netstate = _wifi_state_by_sid(sid) + else + -- lookup state by ifname (e.g. wlan0) + radioname, radiostate, netstate = _wifi_state_by_ifname(name) + if radioname and radiostate and netstate then + sid = netstate.section + netid = _wifi_netid_by_sid(sid) + else + -- lookup state by uci section id (e.g. cfg053579) + radioname, radiostate, netstate = _wifi_state_by_sid(name) + if radioname and radiostate and netstate then + sid = name + netid = _wifi_netid_by_sid(sid) + else + -- no state available, try to resolve from uci + netid, radioname = _wifi_netid_by_sid(name) + if netid and radioname then + sid = name end end end end - local dev = _wifi_state("section", self.sid, "ifname") or netid + local iwinfo = + (netstate and _wifi_iwinfo_by_ifname(netstate.ifname)) or + (radioname and _wifi_iwinfo_by_ifname(radioname)) or + { ifname = (netid or sid or name) } - self.netid = netid - self.wdev = dev - self.iwinfo = dev and sys.wifi.getiwinfo(dev) or { } + self.sid = sid or name + self.wdev = iwinfo.ifname + self.iwinfo = iwinfo + self.netid = netid + self._ubusdata = { + radio = radioname, + dev = radiostate, + net = netstate + } end function wifinet.ubus(self, ...) @@ -1666,7 +1734,8 @@ function wifinet.get_i18n(self) end function wifinet.adminlink(self) - return dsp.build_url("admin", "network", "wireless", self.netid) + local stat, dsp = pcall(require, "luci.dispatcher") + return dsp and dsp.build_url("admin", "network", "wireless", self.netid) end function wifinet.get_network(self) diff --git a/luci-base/luasrc/sys.lua b/luci-base/luasrc/sys.lua index b8380756f..12b20e4c3 100644 --- a/luci-base/luasrc/sys.lua +++ b/luci-base/luasrc/sys.lua @@ -7,6 +7,7 @@ local table = require "table" local nixio = require "nixio" local fs = require "nixio.fs" local uci = require "luci.model.uci" +local ntm = require "luci.model.network" local luci = {} luci.util = require "luci.util" @@ -117,45 +118,12 @@ end net = {} --- The following fields are defined for arp entry objects: --- { "IP address", "HW address", "HW type", "Flags", "Mask", "Device" } -function net.arptable(callback) - local arp = (not callback) and {} or nil - local e, r, v - if fs.access("/proc/net/arp") then - for e in io.lines("/proc/net/arp") do - local r = { }, v - for v in e:gmatch("%S+") do - r[#r+1] = v - end - - if r[1] ~= "IP" then - local x = { - ["IP address"] = r[1], - ["HW type"] = r[2], - ["Flags"] = r[3], - ["HW address"] = r[4], - ["Mask"] = r[5], - ["Device"] = r[6] - } - - if callback then - callback(x) - else - arp = arp or { } - arp[#arp+1] = x - end - end - end - end - return arp -end - local function _nethints(what, callback) local _, k, e, mac, ip, name local cur = uci.cursor() local ifn = { } local hosts = { } + local lookup = { } local function _add(i, ...) local k = select(i, ...) @@ -178,9 +146,14 @@ local function _nethints(what, callback) if fs.access("/etc/ethers") then for e in io.lines("/etc/ethers") do - mac, ip = e:match("^([a-f0-9]%S+) (%S+)") - if mac and ip then - _add(what, mac:upper(), ip, nil, nil) + mac, name = e:match("^([a-fA-F0-9:-]+)%s+(%S+)") + mac = luci.ip.checkmac(mac) + if mac and name then + if luci.ip.checkip4(name) then + _add(what, mac, name, nil, nil) + else + _add(what, mac, nil, nil, name) + end end end end @@ -190,8 +163,9 @@ local function _nethints(what, callback) if s.leasefile and fs.access(s.leasefile) then for e in io.lines(s.leasefile) do mac, ip, name = e:match("^%d+ (%S+) (%S+) (%S+)") + mac = luci.ip.checkmac(mac) if mac and ip then - _add(what, mac:upper(), ip, nil, name ~= "*" and name) + _add(what, mac, ip, nil, name ~= "*" and name) end end end @@ -201,7 +175,10 @@ local function _nethints(what, callback) cur:foreach("dhcp", "host", function(s) for mac in luci.util.imatch(s.mac) do - _add(what, mac:upper(), s.ip, nil, s.name) + mac = luci.ip.checkmac(mac) + if mac then + _add(what, mac, s.ip, nil, s.name) + end end end) @@ -224,8 +201,20 @@ local function _nethints(what, callback) end end + for _, e in pairs(hosts) do + lookup[#lookup+1] = (what > 1) and e[what] or (e[2] or e[3]) + end + + if #lookup > 0 then + lookup = luci.util.ubus("network.rrdns", "lookup", { + addrs = lookup, + timeout = 250, + limit = 1000 + }) or { } + end + for _, e in luci.util.kspairs(hosts) do - callback(e[1], e[2], e[3], e[4]) + callback(e[1], e[2], e[3], lookup[e[2]] or lookup[e[3]] or e[4]) end end @@ -234,17 +223,17 @@ end function net.mac_hints(callback) if callback then _nethints(1, function(mac, v4, v6, name) - name = name or nixio.getnameinfo(v4 or v6, nil, 100) or v4 + name = name or v4 if name and name ~= mac then - callback(mac, name or nixio.getnameinfo(v4 or v6, nil, 100) or v4) + callback(mac, name or v4) end end) else local rv = { } _nethints(1, function(mac, v4, v6, name) - name = name or nixio.getnameinfo(v4 or v6, nil, 100) or v4 + name = name or v4 if name and name ~= mac then - rv[#rv+1] = { mac, name or nixio.getnameinfo(v4 or v6, nil, 100) or v4 } + rv[#rv+1] = { mac, name or v4 } end end) return rv @@ -256,7 +245,7 @@ end function net.ipv4_hints(callback) if callback then _nethints(2, function(mac, v4, v6, name) - name = name or nixio.getnameinfo(v4, nil, 100) or mac + name = name or mac if name and name ~= v4 then callback(v4, name) end @@ -264,7 +253,7 @@ function net.ipv4_hints(callback) else local rv = { } _nethints(2, function(mac, v4, v6, name) - name = name or nixio.getnameinfo(v4, nil, 100) or mac + name = name or mac if name and name ~= v4 then rv[#rv+1] = { v4, name } end @@ -278,7 +267,7 @@ end function net.ipv6_hints(callback) if callback then _nethints(3, function(mac, v4, v6, name) - name = name or nixio.getnameinfo(v6, nil, 100) or mac + name = name or mac if name and name ~= v6 then callback(v6, name) end @@ -286,7 +275,7 @@ function net.ipv6_hints(callback) else local rv = { } _nethints(3, function(mac, v4, v6, name) - name = name or nixio.getnameinfo(v6, nil, 100) or mac + name = name or mac if name and name ~= v6 then rv[#rv+1] = { v6, name } end @@ -369,8 +358,10 @@ end function net.devices() local devs = {} + local seen = {} for k, v in ipairs(nixio.getifaddrs()) do - if v.family == "packet" then + if v.name and not seen[v.name] then + seen[v.name] = true devs[#devs+1] = v.name end end @@ -378,145 +369,6 @@ function net.devices() end -function net.deviceinfo() - local devs = {} - for k, v in ipairs(nixio.getifaddrs()) do - if v.family == "packet" then - local d = v.data - d[1] = d.rx_bytes - d[2] = d.rx_packets - d[3] = d.rx_errors - d[4] = d.rx_dropped - d[5] = 0 - d[6] = 0 - d[7] = 0 - d[8] = d.multicast - d[9] = d.tx_bytes - d[10] = d.tx_packets - d[11] = d.tx_errors - d[12] = d.tx_dropped - d[13] = 0 - d[14] = d.collisions - d[15] = 0 - d[16] = 0 - devs[v.name] = d - end - end - return devs -end - - --- The following fields are defined for route entry tables: --- { "dest", "gateway", "metric", "refcount", "usecount", "irtt", --- "flags", "device" } -function net.routes(callback) - local routes = { } - - for line in io.lines("/proc/net/route") do - - local dev, dst_ip, gateway, flags, refcnt, usecnt, metric, - dst_mask, mtu, win, irtt = line:match( - "([^%s]+)\t([A-F0-9]+)\t([A-F0-9]+)\t([A-F0-9]+)\t" .. - "(%d+)\t(%d+)\t(%d+)\t([A-F0-9]+)\t(%d+)\t(%d+)\t(%d+)" - ) - - if dev then - gateway = luci.ip.Hex( gateway, 32, luci.ip.FAMILY_INET4 ) - dst_mask = luci.ip.Hex( dst_mask, 32, luci.ip.FAMILY_INET4 ) - dst_ip = luci.ip.Hex( - dst_ip, dst_mask:prefix(dst_mask), luci.ip.FAMILY_INET4 - ) - - local rt = { - dest = dst_ip, - gateway = gateway, - metric = tonumber(metric), - refcount = tonumber(refcnt), - usecount = tonumber(usecnt), - mtu = tonumber(mtu), - window = tonumber(window), - irtt = tonumber(irtt), - flags = tonumber(flags, 16), - device = dev - } - - if callback then - callback(rt) - else - routes[#routes+1] = rt - end - end - end - - return routes -end - --- The following fields are defined for route entry tables: --- { "source", "dest", "nexthop", "metric", "refcount", "usecount", --- "flags", "device" } -function net.routes6(callback) - if fs.access("/proc/net/ipv6_route", "r") then - local routes = { } - - for line in io.lines("/proc/net/ipv6_route") do - - local dst_ip, dst_prefix, src_ip, src_prefix, nexthop, - metric, refcnt, usecnt, flags, dev = line:match( - "([a-f0-9]+) ([a-f0-9]+) " .. - "([a-f0-9]+) ([a-f0-9]+) " .. - "([a-f0-9]+) ([a-f0-9]+) " .. - "([a-f0-9]+) ([a-f0-9]+) " .. - "([a-f0-9]+) +([^%s]+)" - ) - - if dst_ip and dst_prefix and - src_ip and src_prefix and - nexthop and metric and - refcnt and usecnt and - flags and dev - then - src_ip = luci.ip.Hex( - src_ip, tonumber(src_prefix, 16), luci.ip.FAMILY_INET6, false - ) - - dst_ip = luci.ip.Hex( - dst_ip, tonumber(dst_prefix, 16), luci.ip.FAMILY_INET6, false - ) - - nexthop = luci.ip.Hex( nexthop, 128, luci.ip.FAMILY_INET6, false ) - - local rt = { - source = src_ip, - dest = dst_ip, - nexthop = nexthop, - metric = tonumber(metric, 16), - refcount = tonumber(refcnt, 16), - usecount = tonumber(usecnt, 16), - flags = tonumber(flags, 16), - device = dev, - - -- lua number is too small for storing the metric - -- add a metric_raw field with the original content - metric_raw = metric - } - - if callback then - callback(rt) - else - routes[#routes+1] = rt - end - end - end - - return routes - end -end - -function net.pingtest(host) - return os.execute("ping -c1 '"..host:gsub("'", '').."' >/dev/null 2>&1") -end - - process = {} function process.info(key) @@ -609,37 +461,19 @@ end wifi = {} function wifi.getiwinfo(ifname) - local stat, iwinfo = pcall(require, "iwinfo") + ntm.init() - if ifname then - local d, n = ifname:match("^(%w+)%.network(%d+)") - local wstate = luci.util.ubus("network.wireless", "status") or { } - - d = d or ifname - n = n and tonumber(n) or 1 - - if type(wstate[d]) == "table" and - type(wstate[d].interfaces) == "table" and - type(wstate[d].interfaces[n]) == "table" and - type(wstate[d].interfaces[n].ifname) == "string" - then - ifname = wstate[d].interfaces[n].ifname - else - ifname = d - end - - local t = stat and iwinfo.type(ifname) - local x = t and iwinfo[t] or { } - return setmetatable({}, { - __index = function(t, k) - if k == "ifname" then - return ifname - elseif x[k] then - return x[k](ifname) - end - end - }) + local wnet = ntm:get_wifinet(ifname) + if wnet and wnet.iwinfo then + return wnet.iwinfo end + + local wdev = ntm:get_wifidev(ifname) + if wdev and wdev.iwinfo then + return wdev.iwinfo + end + + return { ifname = ifname } end diff --git a/luci-base/luasrc/sys/zoneinfo/tzdata.lua b/luci-base/luasrc/sys/zoneinfo/tzdata.lua index 48ae5747c..6668dad83 100644 --- a/luci-base/luasrc/sys/zoneinfo/tzdata.lua +++ b/luci-base/luasrc/sys/zoneinfo/tzdata.lua @@ -51,7 +51,7 @@ TZ = { { 'Africa/Nouakchott', 'GMT0' }, { 'Africa/Ouagadougou', 'GMT0' }, { 'Africa/Porto-Novo', 'WAT-1' }, - { 'Africa/Sao Tome', 'GMT0' }, + { 'Africa/Sao Tome', 'WAT-1' }, { 'Africa/Tripoli', 'EET-2' }, { 'Africa/Tunis', 'CET-1' }, { 'Africa/Windhoek', 'CAT-2' }, @@ -85,7 +85,7 @@ TZ = { { 'America/Bogota', '<-05>5' }, { 'America/Boise', 'MST7MDT,M3.2.0,M11.1.0' }, { 'America/Cambridge Bay', 'MST7MDT,M3.2.0,M11.1.0' }, - { 'America/Campo Grande', '<-04>4<-03>,M10.3.0/0,M2.3.0/0' }, + { 'America/Campo Grande', '<-04>4<-03>,M11.1.0/0,M2.3.0/0' }, { 'America/Cancun', 'EST5' }, { 'America/Caracas', '<-04>4' }, { 'America/Cayenne', '<-03>3' }, @@ -94,7 +94,7 @@ TZ = { { 'America/Chihuahua', 'MST7MDT,M4.1.0,M10.5.0' }, { 'America/Costa Rica', 'CST6' }, { 'America/Creston', 'MST7' }, - { 'America/Cuiaba', '<-04>4<-03>,M10.3.0/0,M2.3.0/0' }, + { 'America/Cuiaba', '<-04>4<-03>,M11.1.0/0,M2.3.0/0' }, { 'America/Curacao', 'AST4' }, { 'America/Danmarkshavn', 'GMT0' }, { 'America/Dawson', 'PST8PDT,M3.2.0,M11.1.0' }, @@ -181,7 +181,7 @@ TZ = { { 'America/Santarem', '<-03>3' }, { 'America/Santiago', '<-04>4<-03>,M8.2.6/24,M5.2.6/24' }, { 'America/Santo Domingo', 'AST4' }, - { 'America/Sao Paulo', '<-03>3<-02>,M10.3.0/0,M2.3.0/0' }, + { 'America/Sao Paulo', '<-03>3<-02>,M11.1.0/0,M2.3.0/0' }, { 'America/Scoresbysund', '<-01>1<+00>,M3.5.0/0,M10.5.0/1' }, { 'America/Sitka', 'AKST9AKDT,M3.2.0,M11.1.0' }, { 'America/St Barthelemy', 'AST4' }, diff --git a/luci-base/luasrc/tools/status.lua b/luci-base/luasrc/tools/status.lua index 95ff46df1..501211181 100644 --- a/luci-base/luasrc/tools/status.lua +++ b/luci-base/luasrc/tools/status.lua @@ -4,6 +4,7 @@ module("luci.tools.status", package.seeall) local uci = require "luci.model.uci".cursor() +local ipc = require "luci.ip" local function dhcp_leases_common(family) local rv = { } @@ -31,7 +32,7 @@ local function dhcp_leases_common(family) if family == 4 and not ip:match(":") then rv[#rv+1] = { expires = (expire ~= 0) and os.difftime(expire, os.time()), - macaddr = mac, + macaddr = ipc.checkmac(mac) or "00:00:00:00:00:00", ipaddr = ip, hostname = (name ~= "*") and name } @@ -74,19 +75,9 @@ local function dhcp_leases_common(family) hostname = (name ~= "-") and name } elseif ip and iaid == "ipv4" and family == 4 then - local mac, mac1, mac2, mac3, mac4, mac5, mac6 - if duid and type(duid) == "string" then - mac1, mac2, mac3, mac4, mac5, mac6 = duid:match("^(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)$") - end - if not (mac1 and mac2 and mac3 and mac4 and mac5 and mac6) then - mac = "FF:FF:FF:FF:FF:FF" - else - mac = mac1..":"..mac2..":"..mac3..":"..mac4..":"..mac5..":"..mac6 - end rv[#rv+1] = { expires = (expire >= 0) and os.difftime(expire, os.time()), - macaddr = duid, - macaddr = mac:lower(), + macaddr = ipc.checkmac(duid:gsub("^(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)(%x%x)$", "%1:%2:%3:%4:%5:%6")) or "00:00:00:00:00:00", ipaddr = ip, hostname = (name ~= "-") and name } diff --git a/luci-base/luasrc/util.luadoc b/luci-base/luasrc/util.luadoc index 805eeb7f8..949aeb21c 100644 --- a/luci-base/luasrc/util.luadoc +++ b/luci-base/luasrc/util.luadoc @@ -109,13 +109,13 @@ Remove leading and trailing whitespace from given string value. ]] ---[[ -Count the occurences of given substring in given string. +Count the occurrences of given substring in given string. @class function @name cmatch @param str String to search in @param pattern String containing pattern to find -@return Number of found occurences +@return Number of found occurrences ]] ---[[ diff --git a/luci-base/luasrc/view/cbi/firewall_zoneforwards.htm b/luci-base/luasrc/view/cbi/firewall_zoneforwards.htm index 2a433b569..546fd8e85 100644 --- a/luci-base/luasrc/view/cbi/firewall_zoneforwards.htm +++ b/luci-base/luasrc/view/cbi/firewall_zoneforwards.htm @@ -43,11 +43,12 @@  ⇒  <% for _, fwd in ipairs(zone:get_forwardings_by("src")) do fz = fwd:dest_zone() - empty = false %> + if fz then + empty = false %>   - <% end %> + <% end end %> <% if empty then %>