diff options
Diffstat (limited to 'libs')
26 files changed, 1660 insertions, 248 deletions
diff --git a/libs/luci-lib-httpclient/Makefile b/libs/luci-lib-httpclient/Makefile index 1e7fd1bc85..b8bd428b28 100644 --- a/libs/luci-lib-httpclient/Makefile +++ b/libs/luci-lib-httpclient/Makefile @@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=HTTP(S) client library -LUCI_DEPENDS:=+luci-base +luci-lib-nixio +LUCI_DEPENDS:=+luci-base +luci-lib-nixio +luci-lib-httpprotoutils include ../../luci.mk diff --git a/libs/luci-lib-httpclient/luasrc/httpclient.lua b/libs/luci-lib-httpclient/luasrc/httpclient.lua index c76cc542ed..3e8d7277d7 100644 --- a/libs/luci-lib-httpclient/luasrc/httpclient.lua +++ b/libs/luci-lib-httpclient/luasrc/httpclient.lua @@ -7,8 +7,8 @@ local nixio = require "nixio" local ltn12 = require "luci.ltn12" local util = require "luci.util" local table = require "table" -local http = require "luci.http.protocol" -local date = require "luci.http.protocol.date" +local http = require "luci.http" +local date = require "luci.http.date" local type, pairs, ipairs, tonumber = type, pairs, ipairs, tonumber local unpack = unpack diff --git a/libs/luci-lib-httpprotoutils/Makefile b/libs/luci-lib-httpprotoutils/Makefile new file mode 100644 index 0000000000..851a362eb7 --- /dev/null +++ b/libs/luci-lib-httpprotoutils/Makefile @@ -0,0 +1,14 @@ +# +# Copyright (C) 2018 The LuCI Team <luci@lists.subsignal.org> +# +# This is free software, licensed under the Apache License, Version 2.0 . +# + +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=HTTP protocol utility functions +LUCI_DEPENDS:=+luci-base + +include ../../luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/libs/luci-lib-httpprotoutils/luasrc/http/conditionals.lua b/libs/luci-lib-httpprotoutils/luasrc/http/conditionals.lua new file mode 100644 index 0000000000..86b4db29cd --- /dev/null +++ b/libs/luci-lib-httpprotoutils/luasrc/http/conditionals.lua @@ -0,0 +1,110 @@ +-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org> +-- Licensed to the public under the Apache License 2.0. + +-- This class provides basic ETag handling and implements most of the +-- conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 . +module("luci.http.conditionals", package.seeall) + +local date = require("luci.http.date") + + +function mk_etag( stat ) + if stat ~= nil then + return string.format( '"%x-%x-%x"', stat.ino, stat.size, stat.mtime ) + end +end + +-- Test whether the given message object contains an "If-Match" header and +-- compare it against the given stat object. +function if_match( req, stat ) + local h = req.headers + local etag = mk_etag( stat ) + + -- Check for matching resource + if type(h['If-Match']) == "string" then + for ent in h['If-Match']:gmatch("([^, ]+)") do + if ( ent == '*' or ent == etag ) and stat ~= nil then + return true + end + end + + return false, 412 + end + + return true +end + +-- Test whether the given message object contains an "If-Modified-Since" header +-- and compare it against the given stat object. +function if_modified_since( req, stat ) + local h = req.headers + + -- Compare mtimes + if type(h['If-Modified-Since']) == "string" then + local since = date.to_unix( h['If-Modified-Since'] ) + + if stat == nil or since < stat.mtime then + return true + end + + return false, 304, { + ["ETag"] = mk_etag( stat ); + ["Date"] = date.to_http( os.time() ); + ["Last-Modified"] = date.to_http( stat.mtime ) + } + end + + return true +end + +-- Test whether the given message object contains an "If-None-Match" header and +-- compare it against the given stat object. +function if_none_match( req, stat ) + local h = req.headers + local etag = mk_etag( stat ) + local method = req.env and req.env.REQUEST_METHOD or "GET" + + -- Check for matching resource + if type(h['If-None-Match']) == "string" then + for ent in h['If-None-Match']:gmatch("([^, ]+)") do + if ( ent == '*' or ent == etag ) and stat ~= nil then + if method == "GET" or method == "HEAD" then + return false, 304, { + ["ETag"] = etag; + ["Date"] = date.to_http( os.time() ); + ["Last-Modified"] = date.to_http( stat.mtime ) + } + else + return false, 412 + end + end + end + end + + return true +end + +-- The If-Range header is currently not implemented due to the lack of general +-- byte range stuff in luci.http.protocol . This function will always return +-- false, 412 to indicate a failed precondition. +function if_range( req, stat ) + -- Sorry, no subranges (yet) + return false, 412 +end + +-- Test whether the given message object contains an "If-Unmodified-Since" +-- header and compare it against the given stat object. +function if_unmodified_since( req, stat ) + local h = req.headers + + -- Compare mtimes + if type(h['If-Unmodified-Since']) == "string" then + local since = date.to_unix( h['If-Unmodified-Since'] ) + + if stat ~= nil and since <= stat.mtime then + return false, 412 + end + end + + return true +end diff --git a/libs/luci-lib-httpprotoutils/luasrc/http/conditionals.luadoc b/libs/luci-lib-httpprotoutils/luasrc/http/conditionals.luadoc new file mode 100644 index 0000000000..7ce0b5ebe3 --- /dev/null +++ b/libs/luci-lib-httpprotoutils/luasrc/http/conditionals.luadoc @@ -0,0 +1,85 @@ +---[[ +LuCI http protocol implementation - HTTP/1.1 bits. + +This class provides basic ETag handling and implements most of the +conditional HTTP/1.1 headers specified in RFC2616 Sct. 14.24 - 14.28 . +]] +module "luci.http.conditionals" + +---[[ +Implement 14.19 / ETag. + +@class function +@name mk_etag +@param stat A file.stat structure +@return String containing the generated tag suitable for ETag headers +]] + +---[[ +14.24 / If-Match + +Test whether the given message object contains an "If-Match" header and +compare it against the given stat object. +@class function +@name if_match +@param req HTTP request message object +@param stat A file.stat object +@return Boolean indicating whether the precondition is ok +@return Alternative status code if the precondition failed +]] + +---[[ +14.25 / If-Modified-Since + +Test whether the given message object contains an "If-Modified-Since" header +and compare it against the given stat object. +@class function +@name if_modified_since +@param req HTTP request message object +@param stat A file.stat object +@return Boolean indicating whether the precondition is ok +@return Alternative status code if the precondition failed +@return Table containing extra HTTP headers if the precondition failed +]] + +---[[ +14.26 / If-None-Match + +Test whether the given message object contains an "If-None-Match" header and +compare it against the given stat object. +@class function +@name if_none_match +@param req HTTP request message object +@param stat A file.stat object +@return Boolean indicating whether the precondition is ok +@return Alternative status code if the precondition failed +@return Table containing extra HTTP headers if the precondition failed +]] + +---[[ +14.27 / If-Range + +The If-Range header is currently not implemented due to the lack of general +byte range stuff in luci.http.protocol . This function will always return +false, 412 to indicate a failed precondition. +@class function +@name if_range +@param req HTTP request message object +@param stat A file.stat object +@return Boolean indicating whether the precondition is ok +@return Alternative status code if the precondition failed +]] + +---[[ +14.28 / If-Unmodified-Since + +Test whether the given message object contains an "If-Unmodified-Since" +header and compare it against the given stat object. +@class function +@name if_unmodified_since +@param req HTTP request message object +@param stat A file.stat object +@return Boolean indicating whether the precondition is ok +@return Alternative status code if the precondition failed +]] + diff --git a/libs/luci-lib-httpprotoutils/luasrc/http/date.lua b/libs/luci-lib-httpprotoutils/luasrc/http/date.lua new file mode 100644 index 0000000000..72f1bdb577 --- /dev/null +++ b/libs/luci-lib-httpprotoutils/luasrc/http/date.lua @@ -0,0 +1,87 @@ +-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org> +-- Licensed to the public under the Apache License 2.0. + +-- This class contains functions to parse, compare and format http dates. +module("luci.http.date", package.seeall) + +require("luci.sys.zoneinfo") + + +MONTHS = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec" +} + +function tz_offset(tz) + + if type(tz) == "string" then + + -- check for a numeric identifier + local s, v = tz:match("([%+%-])([0-9]+)") + if s == '+' then s = 1 else s = -1 end + if v then v = tonumber(v) end + + if s and v then + return s * 60 * ( math.floor( v / 100 ) * 60 + ( v % 100 ) ) + + -- lookup symbolic tz + elseif luci.sys.zoneinfo.OFFSET[tz:lower()] then + return luci.sys.zoneinfo.OFFSET[tz:lower()] + end + + end + + -- bad luck + return 0 +end + +function to_unix(date) + + local wd, day, mon, yr, hr, min, sec, tz = date:match( + "([A-Z][a-z][a-z]), ([0-9]+) " .. + "([A-Z][a-z][a-z]) ([0-9]+) " .. + "([0-9]+):([0-9]+):([0-9]+) " .. + "([A-Z0-9%+%-]+)" + ) + + if day and mon and yr and hr and min and sec then + -- find month + local month = 1 + for i = 1, 12 do + if MONTHS[i] == mon then + month = i + break + end + end + + -- convert to epoch time + return tz_offset(tz) + os.time( { + year = yr, + month = month, + day = day, + hour = hr, + min = min, + sec = sec + } ) + end + + return 0 +end + +function to_http(time) + return os.date( "%a, %d %b %Y %H:%M:%S GMT", time ) +end + +function compare(d1, d2) + + if d1:match("[^0-9]") then d1 = to_unix(d1) end + if d2:match("[^0-9]") then d2 = to_unix(d2) end + + if d1 == d2 then + return 0 + elseif d1 < d2 then + return -1 + else + return 1 + end +end diff --git a/libs/luci-lib-httpprotoutils/luasrc/http/date.luadoc b/libs/luci-lib-httpprotoutils/luasrc/http/date.luadoc new file mode 100644 index 0000000000..6028fb4837 --- /dev/null +++ b/libs/luci-lib-httpprotoutils/luasrc/http/date.luadoc @@ -0,0 +1,46 @@ +---[[ +LuCI http protocol implementation - date helper class. + +This class contains functions to parse, compare and format http dates. +]] +module "luci.http.date" + +---[[ +Return the time offset in seconds between the UTC and given time zone. + +@class function +@name tz_offset +@param tz Symbolic or numeric timezone specifier +@return Time offset to UTC in seconds +]] + +---[[ +Parse given HTTP date string and convert it to unix epoch time. + +@class function +@name to_unix +@param data String containing the date +@return Unix epoch time +]] + +---[[ +Convert the given unix epoch time to valid HTTP date string. + +@class function +@name to_http +@param time Unix epoch time +@return String containing the formatted date +]] + +---[[ +Compare two dates which can either be unix epoch times or HTTP date strings. + +@class function +@name compare +@param d1 The first date or epoch time to compare +@param d2 The first date or epoch time to compare +@return -1 - if d1 is lower then d2 +@return 0 - if both dates are equal +@return 1 - if d1 is higher then d2 +]] + diff --git a/libs/luci-lib-httpprotoutils/luasrc/http/mime.lua b/libs/luci-lib-httpprotoutils/luasrc/http/mime.lua new file mode 100644 index 0000000000..0bcff8a36b --- /dev/null +++ b/libs/luci-lib-httpprotoutils/luasrc/http/mime.lua @@ -0,0 +1,78 @@ +-- Copyright 2008 Freifunk Leipzig / Jo-Philipp Wich <jow@openwrt.org> +-- Licensed to the public under the Apache License 2.0. + +-- This class provides functions to guess mime types from file extensions and +-- vice versa. +module("luci.http.mime", package.seeall) + +require("luci.util") + +MIME_TYPES = { + ["txt"] = "text/plain"; + ["js"] = "text/javascript"; + ["css"] = "text/css"; + ["htm"] = "text/html"; + ["html"] = "text/html"; + ["patch"] = "text/x-patch"; + ["c"] = "text/x-csrc"; + ["h"] = "text/x-chdr"; + ["o"] = "text/x-object"; + ["ko"] = "text/x-object"; + + ["bmp"] = "image/bmp"; + ["gif"] = "image/gif"; + ["png"] = "image/png"; + ["jpg"] = "image/jpeg"; + ["jpeg"] = "image/jpeg"; + ["svg"] = "image/svg+xml"; + + ["zip"] = "application/zip"; + ["pdf"] = "application/pdf"; + ["xml"] = "application/xml"; + ["xsl"] = "application/xml"; + ["doc"] = "application/msword"; + ["ppt"] = "application/vnd.ms-powerpoint"; + ["xls"] = "application/vnd.ms-excel"; + ["odt"] = "application/vnd.oasis.opendocument.text"; + ["odp"] = "application/vnd.oasis.opendocument.presentation"; + ["pl"] = "application/x-perl"; + ["sh"] = "application/x-shellscript"; + ["php"] = "application/x-php"; + ["deb"] = "application/x-deb"; + ["iso"] = "application/x-cd-image"; + ["tgz"] = "application/x-compressed-tar"; + + ["mp3"] = "audio/mpeg"; + ["ogg"] = "audio/x-vorbis+ogg"; + ["wav"] = "audio/x-wav"; + + ["mpg"] = "video/mpeg"; + ["mpeg"] = "video/mpeg"; + ["avi"] = "video/x-msvideo"; +} + +-- "application/octet-stream" if the extension is unknown. +function to_mime(filename) + if type(filename) == "string" then + local ext = filename:match("[^%.]+$") + + if ext and MIME_TYPES[ext:lower()] then + return MIME_TYPES[ext:lower()] + end + end + + return "application/octet-stream" +end + +-- given mime-type is unknown. +function to_ext(mimetype) + if type(mimetype) == "string" then + for ext, type in luci.util.kspairs( MIME_TYPES ) do + if type == mimetype then + return ext + end + end + end + + return nil +end diff --git a/libs/luci-lib-httpprotoutils/luasrc/http/mime.luadoc b/libs/luci-lib-httpprotoutils/luasrc/http/mime.luadoc new file mode 100644 index 0000000000..7751e2baf4 --- /dev/null +++ b/libs/luci-lib-httpprotoutils/luasrc/http/mime.luadoc @@ -0,0 +1,34 @@ +---[[ +LuCI http protocol implementation - mime helper class. + +This class provides functions to guess mime types from file extensions and +vice versa. +]] +module "luci.http.mime" + +---[[ +MIME mapping table containg extension - mimetype relations. + +@class table +]] + +---[[ +Extract extension from a filename and return corresponding mime-type or + +"application/octet-stream" if the extension is unknown. +@class function +@name to_mime +@param filename The filename for which the mime type is guessed +@return String containign the determined mime type +]] + +---[[ +Return corresponding extension for a given mime type or nil if the + +given mime-type is unknown. +@class function +@name to_ext +@param mimetype The mimetype to retrieve the extension from +@return String with the extension or nil for unknown type +]] + diff --git a/libs/luci-lib-ip/Makefile b/libs/luci-lib-ip/Makefile index eb80dcb258..15cb537cff 100644 --- a/libs/luci-lib-ip/Makefile +++ b/libs/luci-lib-ip/Makefile @@ -9,6 +9,8 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=Lua library for IP calculation and routing information LUCI_DEPENDS:=+liblua +libnl-tiny +PKG_LICENSE:=Apache-2.0 + include ../../luci.mk # call BuildPackage - OpenWrt buildroot signature diff --git a/libs/luci-lib-ip/src/ip.c b/libs/luci-lib-ip/src/ip.c index b91966c536..854a0c09c2 100644 --- a/libs/luci-lib-ip/src/ip.c +++ b/libs/luci-lib-ip/src/ip.c @@ -1,5 +1,5 @@ /* -Copyright 2015 Jo-Philipp Wich <jow@openwrt.org> +Copyright 2015-2018 Jo-Philipp Wich <jo@mein.io> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -42,6 +42,16 @@ limitations under the License. #define RTA_INT(x) (*(int *)RTA_DATA(x)) #define RTA_U32(x) (*(uint32_t *)RTA_DATA(x)) +#define AF_BITS(f) \ + ((f) == AF_INET ? 32 : \ + ((f) == AF_INET6 ? 128 : \ + ((f) == AF_PACKET ? 48 : 0))) + +#define AF_BYTES(f) \ + ((f) == AF_INET ? 4 : \ + ((f) == AF_INET6 ? 16 : \ + ((f) == AF_PACKET ? 6 : 0))) + static int hz = 0; static struct nl_sock *sock = NULL; @@ -49,11 +59,11 @@ typedef struct { union { struct in_addr v4; struct in6_addr v6; + struct ether_addr mac; + uint8_t u8[16]; } addr; - int len; - int bits; - int family; - bool exact; + uint16_t family; + int16_t bits; } cidr_t; struct dump_filter { @@ -70,6 +80,8 @@ struct dump_filter { cidr_t src; cidr_t dst; struct ether_addr mac; + bool from_exact; + bool dst_exact; }; struct dump_state { @@ -95,29 +107,68 @@ static cidr_t *L_checkcidr (lua_State *L, int index, cidr_t *p) return NULL; } -static bool parse_mask(int family, const char *mask, int *bits) +static bool parse_mac(const char *mac, struct ether_addr *ea) +{ + unsigned long int n; + char *e, sep = 0; + int i; + + for (i = 0; i < 6; i++) + { + if (i > 0) + { + if (sep == 0 && (mac[0] == ':' || mac[0] == '-')) + sep = mac[0]; + + if (sep == 0 || mac[0] != sep) + return false; + + mac++; + } + + n = strtoul(mac, &e, 16); + + if (n > 0xFF) + return false; + + mac += (e - mac); + ea->ether_addr_octet[i] = n; + } + + if (mac[0] != 0) + return false; + + return true; +} + +static bool parse_mask(int family, const char *mask, int16_t *bits) { char *e; - struct in_addr m; - struct in6_addr m6; + union { + struct in_addr v4; + struct in6_addr v6; + struct ether_addr mac; + uint8_t u8[16]; + } m; - if (family == AF_INET && inet_pton(AF_INET, mask, &m)) + if (family == AF_INET && inet_pton(AF_INET, mask, &m.v4)) { - for (*bits = 0, m.s_addr = ntohl(m.s_addr); - *bits < 32 && (m.s_addr << *bits) & 0x80000000; + for (*bits = 0, m.v4.s_addr = ntohl(m.v4.s_addr); + *bits < AF_BITS(AF_INET) && (m.v4.s_addr << *bits) & 0x80000000; ++*bits); } - else if (family == AF_INET6 && inet_pton(AF_INET6, mask, &m6)) + else if ((family == AF_INET6 && inet_pton(AF_INET6, mask, &m.v6)) || + (family == AF_PACKET && parse_mac(mask, &m.mac))) { for (*bits = 0; - *bits < 128 && (m6.s6_addr[*bits / 8] << (*bits % 8)) & 128; + *bits < AF_BITS(family) && (m.u8[*bits / 8] << (*bits % 8)) & 128; ++*bits); } else { *bits = strtoul(mask, &e, 10); - if (e == mask || *e != 0 || *bits > ((family == AF_INET) ? 32 : 128)) + if (e == mask || *e != 0 || *bits > AF_BITS(family)) return false; } @@ -127,7 +178,6 @@ static bool parse_mask(int family, const char *mask, int *bits) static bool parse_cidr(const char *dest, cidr_t *pp) { char *p, buf[INET6_ADDRSTRLEN * 2 + 2]; - uint8_t bitlen = 0; strncpy(buf, dest, sizeof(buf) - 1); @@ -137,17 +187,11 @@ static bool parse_cidr(const char *dest, cidr_t *pp) *p++ = 0; if (inet_pton(AF_INET, buf, &pp->addr.v4)) - { - bitlen = 32; pp->family = AF_INET; - pp->len = sizeof(struct in_addr); - } else if (inet_pton(AF_INET6, buf, &pp->addr.v6)) - { - bitlen = 128; pp->family = AF_INET6; - pp->len = sizeof(struct in6_addr); - } + else if (parse_mac(buf, &pp->addr.mac)) + pp->family = AF_PACKET; else return false; @@ -158,12 +202,45 @@ static bool parse_cidr(const char *dest, cidr_t *pp) } else { - pp->bits = bitlen; + pp->bits = AF_BITS(pp->family); } return true; } +static int format_cidr(lua_State *L, cidr_t *p) +{ + char buf[INET6_ADDRSTRLEN]; + + if (p->family == AF_PACKET) + { + snprintf(buf, sizeof(buf), "%02X:%02X:%02X:%02X:%02X:%02X", + p->addr.mac.ether_addr_octet[0], + p->addr.mac.ether_addr_octet[1], + p->addr.mac.ether_addr_octet[2], + p->addr.mac.ether_addr_octet[3], + p->addr.mac.ether_addr_octet[4], + p->addr.mac.ether_addr_octet[5]); + + if (p->bits < AF_BITS(AF_PACKET)) + lua_pushfstring(L, "%s/%d", buf, p->bits); + else + lua_pushstring(L, buf); + } + else + { + if (p->bits < AF_BITS(p->family)) + lua_pushfstring(L, "%s/%d", + inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf)), + p->bits); + else + lua_pushstring(L, + inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf))); + } + + return 1; +} + static int L_getint(lua_State *L, int index, const char *name) { int rv = 0; @@ -220,17 +297,21 @@ static void L_setaddr(struct lua_State *L, const char *name, if (family == AF_INET) { p->family = AF_INET; - p->bits = (bits < 0) ? 32 : bits; - p->len = sizeof(p->addr.v4); + p->bits = (bits < 0) ? AF_BITS(AF_INET) : bits; p->addr.v4 = *(struct in_addr *)addr; } - else + else if (family == AF_INET6) { p->family = AF_INET6; - p->bits = (bits < 0) ? 128 : bits; - p->len = sizeof(p->addr.v6); + p->bits = (bits < 0) ? AF_BITS(AF_INET6) : bits; p->addr.v6 = *(struct in6_addr *)addr; } + else + { + p->family = AF_PACKET; + p->bits = (bits < 0) ? AF_BITS(AF_PACKET) : bits; + p->addr.mac = *(struct ether_addr *)addr; + } luaL_getmetatable(L, LUCI_IP_CIDR); lua_setmetatable(L, -2); @@ -254,6 +335,7 @@ static void L_setdev(struct lua_State *L, const char *name, static int L_checkbits(lua_State *L, int index, cidr_t *p) { + int16_t s16; int bits; if (lua_gettop(L) < index || lua_isnil(L, index)) @@ -264,13 +346,15 @@ static int L_checkbits(lua_State *L, int index, cidr_t *p) { bits = lua_tointeger(L, index); - if (bits < 0 || bits > ((p->family == AF_INET) ? 32 : 128)) + if (bits < 0 || bits > AF_BITS(p->family)) return luaL_error(L, "Invalid prefix size"); } else if (lua_type(L, index) == LUA_TSTRING) { - if (!parse_mask(p->family, lua_tostring(L, index), &bits)) + if (!parse_mask(p->family, lua_tostring(L, index), &s16)) return luaL_error(L, "Invalid netmask format"); + + bits = s16; } else { @@ -293,20 +377,26 @@ static int _cidr_new(lua_State *L, int index, int family, bool mask) if (family == AF_INET6) { cidr.family = AF_INET6; - cidr.bits = 128; - cidr.len = sizeof(cidr.addr.v6); cidr.addr.v6.s6_addr[12] = n; cidr.addr.v6.s6_addr[13] = (n >> 8); cidr.addr.v6.s6_addr[14] = (n >> 16); cidr.addr.v6.s6_addr[15] = (n >> 24); } - else + else if (family == AF_INET) { cidr.family = AF_INET; - cidr.bits = 32; - cidr.len = sizeof(cidr.addr.v4); cidr.addr.v4.s_addr = n; } + else + { + cidr.family = AF_PACKET; + cidr.addr.mac.ether_addr_octet[2] = n; + cidr.addr.mac.ether_addr_octet[3] = (n >> 8); + cidr.addr.mac.ether_addr_octet[4] = (n >> 16); + cidr.addr.mac.ether_addr_octet[5] = (n >> 24); + } + + cidr.bits = AF_BITS(cidr.family); } else { @@ -346,6 +436,62 @@ static int cidr_ipv6(lua_State *L) return _cidr_new(L, 1, AF_INET6, true); } +static int cidr_mac(lua_State *L) +{ + return _cidr_new(L, 1, AF_PACKET, true); +} + +static int cidr_check(lua_State *L, int family) +{ + cidr_t cidr = { }, *cidrp; + const char *addr; + + if (lua_type(L, 1) == LUA_TSTRING) + { + addr = lua_tostring(L, 1); + + if (addr && parse_cidr(addr, &cidr) && cidr.family == family) + return format_cidr(L, &cidr); + } + else + { + cidrp = lua_touserdata(L, 1); + + if (cidrp == NULL) + return 0; + + if (!lua_getmetatable(L, 1)) + return 0; + + lua_getfield(L, LUA_REGISTRYINDEX, LUCI_IP_CIDR); + + if (!lua_rawequal(L, -1, -2)) + cidrp = NULL; + + lua_pop(L, 2); + + if (cidrp != NULL && cidrp->family == family) + return format_cidr(L, cidrp); + } + + return 0; +} + +static int cidr_checkip4(lua_State *L) +{ + return cidr_check(L, AF_INET); +} + +static int cidr_checkip6(lua_State *L) +{ + return cidr_check(L, AF_INET6); +} + +static int cidr_checkmac(lua_State *L) +{ + return cidr_check(L, AF_PACKET); +} + static int cidr_is4(lua_State *L) { cidr_t *p = L_checkcidr(L, 1, NULL); @@ -424,6 +570,34 @@ static int cidr_is6linklocal(lua_State *L) return 1; } +static int cidr_ismac(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + + lua_pushboolean(L, p->family == AF_PACKET); + return 1; +} + +static int cidr_ismacmcast(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + + lua_pushboolean(L, (p->family == AF_PACKET && + (p->addr.mac.ether_addr_octet[0] & 0x1))); + + return 1; +} + +static int cidr_ismaclocal(lua_State *L) +{ + cidr_t *p = L_checkcidr(L, 1, NULL); + + lua_pushboolean(L, (p->family == AF_PACKET && + (p->addr.mac.ether_addr_octet[0] & 0x2))); + + return 1; +} + static int _cidr_cmp(lua_State *L) { cidr_t *a = L_checkcidr(L, 1, NULL); @@ -432,7 +606,7 @@ static int _cidr_cmp(lua_State *L) if (a->family != b->family) return (a->family - b->family); - return memcmp(&a->addr.v6, &b->addr.v6, a->len); + return memcmp(&a->addr.v6, &b->addr.v6, AF_BYTES(a->family)); } static int cidr_lower(lua_State *L) @@ -475,24 +649,24 @@ static void _apply_mask(cidr_t *p, int bits, bool inv) if (bits <= 0) { - memset(&p->addr.v6, inv * 0xFF, p->len); + memset(&p->addr.u8, inv * 0xFF, AF_BYTES(p->family)); } - else if (p->family == AF_INET && bits <= 32) + else if (p->family == AF_INET && bits <= AF_BITS(AF_INET)) { if (inv) - p->addr.v4.s_addr |= ntohl((1 << (32 - bits)) - 1); + p->addr.v4.s_addr |= ntohl((1 << (AF_BITS(AF_INET) - bits)) - 1); else - p->addr.v4.s_addr &= ntohl(~((1 << (32 - bits)) - 1)); + p->addr.v4.s_addr &= ntohl(~((1 << (AF_BITS(AF_INET) - bits)) - 1)); } - else if (p->family == AF_INET6 && bits <= 128) + else if (bits <= AF_BITS(p->family)) { - for (i = 0; i < sizeof(p->addr.v6.s6_addr); i++) + for (i = 0; i < AF_BYTES(p->family); i++) { b = (bits > 8) ? 8 : bits; if (inv) - p->addr.v6.s6_addr[i] |= ~((uint8_t)(0xFF << (8 - b))); + p->addr.u8[i] |= ~((uint8_t)(0xFF << (8 - b))); else - p->addr.v6.s6_addr[i] &= (uint8_t)(0xFF << (8 - b)); + p->addr.u8[i] &= (uint8_t)(0xFF << (8 - b)); bits -= b; } } @@ -507,7 +681,7 @@ static int cidr_network(lua_State *L) return 0; *p2 = *p1; - p2->bits = (p1->family == AF_INET) ? 32 : 128; + p2->bits = AF_BITS(p1->family); _apply_mask(p2, bits, false); luaL_getmetatable(L, LUCI_IP_CIDR); @@ -524,7 +698,7 @@ static int cidr_host(lua_State *L) return 0; *p2 = *p1; - p2->bits = (p1->family == AF_INET) ? 32 : 128; + p2->bits = AF_BITS(p1->family); luaL_getmetatable(L, LUCI_IP_CIDR); lua_setmetatable(L, -2); @@ -539,7 +713,7 @@ static int cidr_mask(lua_State *L) if (!(p2 = lua_newuserdata(L, sizeof(*p2)))) return 0; - p2->bits = (p1->family == AF_INET) ? 32 : 128; + p2->bits = AF_BITS(p1->family); p2->family = p1->family; memset(&p2->addr.v6.s6_addr, 0xFF, sizeof(p2->addr.v6.s6_addr)); @@ -556,14 +730,14 @@ static int cidr_broadcast(lua_State *L) cidr_t *p2; int bits = L_checkbits(L, 2, p1); - if (p1->family == AF_INET6) + if (p1->family != AF_INET) return 0; if (!(p2 = lua_newuserdata(L, sizeof(*p2)))) return 0; *p2 = *p1; - p2->bits = (p1->family == AF_INET) ? 32 : 128; + p2->bits = AF_BITS(AF_INET); _apply_mask(p2, bits, true); luaL_getmetatable(L, LUCI_IP_CIDR); @@ -583,7 +757,7 @@ static int cidr_mapped4(lua_State *L) return 0; p2->family = AF_INET; - p2->bits = (p1->bits > 32) ? 32 : p1->bits; + p2->bits = (p1->bits > AF_BITS(AF_INET)) ? AF_BITS(AF_INET) : p1->bits; memcpy(&p2->addr.v4, p1->addr.v6.s6_addr + 12, sizeof(p2->addr.v4)); luaL_getmetatable(L, LUCI_IP_CIDR); @@ -591,6 +765,72 @@ static int cidr_mapped4(lua_State *L) return 1; } +static int cidr_tolinklocal(lua_State *L) +{ + cidr_t *p1 = L_checkcidr(L, 1, NULL); + cidr_t *p2; + int i; + + if (p1->family != AF_PACKET) + return 0; + + if (!(p2 = lua_newuserdata(L, sizeof(*p2)))) + return 0; + + p2->family = AF_INET6; + p2->bits = AF_BITS(AF_INET6); + p2->addr.u8[0] = 0xFE; + p2->addr.u8[1] = 0x80; + p2->addr.u8[8] = p1->addr.u8[0] ^ 0x02; + p2->addr.u8[9] = p1->addr.u8[1]; + p2->addr.u8[10] = p1->addr.u8[2]; + p2->addr.u8[11] = 0xFF; + p2->addr.u8[12] = 0xFE; + p2->addr.u8[13] = p1->addr.u8[3]; + p2->addr.u8[14] = p1->addr.u8[4]; + p2->addr.u8[15] = p1->addr.u8[5]; + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + +static int cidr_tomac(lua_State *L) +{ + cidr_t *p1 = L_checkcidr(L, 1, NULL); + cidr_t *p2; + int i; + + if (p1->family != AF_INET6 || + p1->addr.u8[0] != 0xFE || + p1->addr.u8[1] != 0x80 || + p1->addr.u8[2] != 0x00 || + p1->addr.u8[3] != 0x00 || + p1->addr.u8[4] != 0x00 || + p1->addr.u8[5] != 0x00 || + p1->addr.u8[6] != 0x00 || + p1->addr.u8[7] != 0x00 || + p1->addr.u8[11] != 0xFF || + p1->addr.u8[12] != 0xFE) + return 0; + + if (!(p2 = lua_newuserdata(L, sizeof(*p2)))) + return 0; + + p2->family = AF_PACKET; + p2->bits = AF_BITS(AF_PACKET); + p2->addr.u8[0] = p1->addr.u8[8] ^ 0x02; + p2->addr.u8[1] = p1->addr.u8[9]; + p2->addr.u8[2] = p1->addr.u8[10]; + p2->addr.u8[3] = p1->addr.u8[13]; + p2->addr.u8[4] = p1->addr.u8[14]; + p2->addr.u8[5] = p1->addr.u8[15]; + + luaL_getmetatable(L, LUCI_IP_CIDR); + lua_setmetatable(L, -2); + return 1; +} + static int cidr_contains(lua_State *L) { cidr_t *p1 = L_checkcidr(L, 1, NULL); @@ -603,15 +843,15 @@ static int cidr_contains(lua_State *L) _apply_mask(&a, p1->bits, false); _apply_mask(&b, p1->bits, false); - rv = !memcmp(&a.addr.v6, &b.addr.v6, a.len); + rv = !memcmp(&a.addr.v6, &b.addr.v6, AF_BYTES(a.family)); } lua_pushboolean(L, rv); return 1; } -#define S6_BYTE(a, i) \ - (a)->addr.v6.s6_addr[sizeof((a)->addr.v6.s6_addr) - (i) - 1] +#define BYTE(a, i) \ + (a)->addr.u8[AF_BYTES((a)->family) - (i) - 1] static int _cidr_add_sub(lua_State *L, bool add) { @@ -625,45 +865,45 @@ static int _cidr_add_sub(lua_State *L, bool add) if (p1->family == p2->family) { - if (p1->family == AF_INET6) + if (p1->family == AF_INET) + { + a = ntohl(p1->addr.v4.s_addr); + b = ntohl(p2->addr.v4.s_addr); + + /* would over/underflow */ + if ((add && (UINT_MAX - a) < b) || (!add && a < b)) + { + r.addr.v4.s_addr = add * 0xFFFFFFFF; + ok = false; + } + else + { + r.addr.v4.s_addr = add ? htonl(a + b) : htonl(a - b); + } + } + else { - for (i = 0, carry = 0; i < sizeof(r.addr.v6.s6_addr); i++) + for (i = 0, carry = 0; i < AF_BYTES(p1->family); i++) { if (add) { - S6_BYTE(&r, i) = S6_BYTE(p1, i) + S6_BYTE(p2, i) + carry; - carry = (S6_BYTE(p1, i) + S6_BYTE(p2, i) + carry) / 256; + BYTE(&r, i) = BYTE(p1, i) + BYTE(p2, i) + carry; + carry = (BYTE(p1, i) + BYTE(p2, i) + carry) / 256; } else { - S6_BYTE(&r, i) = (S6_BYTE(p1, i) - S6_BYTE(p2, i) - carry); - carry = (S6_BYTE(p1, i) < (S6_BYTE(p2, i) + carry)); + BYTE(&r, i) = (BYTE(p1, i) - BYTE(p2, i) - carry); + carry = (BYTE(p1, i) < (BYTE(p2, i) + carry)); } } /* would over/underflow */ if (carry) { - memset(&r.addr.v6, add * 0xFF, sizeof(r.addr.v6)); + memset(&r.addr.u8, add * 0xFF, AF_BYTES(r.family)); ok = false; } } - else - { - a = ntohl(p1->addr.v4.s_addr); - b = ntohl(p2->addr.v4.s_addr); - - /* would over/underflow */ - if ((add && (UINT_MAX - a) < b) || (!add && a < b)) - { - r.addr.v4.s_addr = add * 0xFFFFFFFF; - ok = false; - } - else - { - r.addr.v4.s_addr = add ? htonl(a + b) : htonl(a - b); - } - } } else { @@ -705,22 +945,22 @@ static int cidr_minhost(lua_State *L) _apply_mask(&r, r.bits, false); - if (r.family == AF_INET6 && r.bits < 128) + if (r.family == AF_INET && r.bits < AF_BITS(AF_INET)) + { + r.bits = AF_BITS(AF_INET); + r.addr.v4.s_addr = htonl(ntohl(r.addr.v4.s_addr) + 1); + } + else if (r.bits < AF_BITS(r.family)) { - r.bits = 128; + r.bits = AF_BITS(r.family); - for (i = 0, carry = 1; i < sizeof(r.addr.v6.s6_addr); i++) + for (i = 0, carry = 1; i < AF_BYTES(r.family); i++) { - rest = (S6_BYTE(&r, i) + carry) > 255; - S6_BYTE(&r, i) += carry; + rest = (BYTE(&r, i) + carry) > 255; + BYTE(&r, i) += carry; carry = rest; } } - else if (r.family == AF_INET && r.bits < 32) - { - r.bits = 32; - r.addr.v4.s_addr = htonl(ntohl(r.addr.v4.s_addr) + 1); - } if (!(p = lua_newuserdata(L, sizeof(*p)))) return 0; @@ -739,14 +979,14 @@ static int cidr_maxhost(lua_State *L) _apply_mask(&r, r.bits, true); - if (r.family == AF_INET && r.bits < 32) + if (r.family == AF_INET && r.bits < AF_BITS(AF_INET)) { - r.bits = 32; + r.bits = AF_BITS(AF_INET); r.addr.v4.s_addr = htonl(ntohl(r.addr.v4.s_addr) - 1); } - else if (r.family == AF_INET6) + else { - r.bits = 128; + r.bits = AF_BITS(r.family); } if (!(p = lua_newuserdata(L, sizeof(*p)))) @@ -766,31 +1006,17 @@ static int cidr_gc (lua_State *L) static int cidr_tostring (lua_State *L) { - char buf[INET6_ADDRSTRLEN]; cidr_t *p = L_checkcidr(L, 1, NULL); - - if ((p->family == AF_INET && p->bits < 32) || - (p->family == AF_INET6 && p->bits < 128)) - { - lua_pushfstring(L, "%s/%d", - inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf)), - p->bits); - } - else - { - lua_pushstring(L, inet_ntop(p->family, &p->addr.v6, buf, sizeof(buf))); - } - - return 1; + return format_cidr(L, p); } /* * route functions */ -static bool diff_prefix(int family, void *addr, int bits, cidr_t *p) +static bool diff_prefix(int family, void *addr, int bits, bool exact, cidr_t *p) { - uint8_t i, b, r; + uint8_t i, b, r, *a; uint32_t m; if (!p->family) @@ -799,28 +1025,27 @@ static bool diff_prefix(int family, void *addr, int bits, cidr_t *p) if (!addr || p->family != family || p->bits > bits) return true; - if (family == AF_INET6) + if (family == AF_INET) { - for (i = 0, r = p->bits; i < sizeof(struct in6_addr); i++) + m = p->bits ? htonl(~((1 << (AF_BITS(AF_INET) - p->bits)) - 1)) : 0; + + if ((((struct in_addr *)addr)->s_addr & m) != (p->addr.v4.s_addr & m)) + return true; + } + else + { + for (i = 0, a = addr, r = p->bits; i < AF_BYTES(p->family); i++) { b = r ? (0xFF << (8 - ((r > 8) ? 8 : r))) : 0; - if ((((struct in6_addr *)addr)->s6_addr[i] & b) != - (p->addr.v6.s6_addr[i] & b)) + if ((a[i] & b) != (p->addr.u8[i] & b)) return true; r -= ((r > 8) ? 8 : r); } } - else - { - m = p->bits ? htonl(~((1 << (32 - p->bits)) - 1)) : 0; - if ((((struct in_addr *)addr)->s_addr & m) != (p->addr.v4.s_addr & m)) - return true; - } - - return (p->exact && p->bits != bits); + return (exact && p->bits != bits); } static int cb_dump_route(struct nl_msg *msg, void *arg) @@ -848,7 +1073,7 @@ static int cb_dump_route(struct nl_msg *msg, void *arg) dst = tb[RTA_DST] ? RTA_DATA(tb[RTA_DST]) : &def; gw = tb[RTA_GATEWAY] ? RTA_DATA(tb[RTA_GATEWAY]) : NULL; - bitlen = (rt->rtm_family == AF_INET6) ? 128 : 32; + bitlen = AF_BITS(rt->rtm_family); if ((f->type && rt->rtm_type != f->type) || (f->family && rt->rtm_family != f->family) || @@ -857,10 +1082,14 @@ static int cb_dump_route(struct nl_msg *msg, void *arg) (f->iif && iif != f->iif) || (f->oif && oif != f->oif) || (f->table && table != f->table) || - diff_prefix(rt->rtm_family, from, rt->rtm_src_len, &f->from) || - diff_prefix(rt->rtm_family, dst, rt->rtm_dst_len, &f->dst) || - diff_prefix(rt->rtm_family, gw, bitlen, &f->gw) || - diff_prefix(rt->rtm_family, src, bitlen, &f->src)) + diff_prefix(rt->rtm_family, from, rt->rtm_src_len, + f->from_exact, &f->from) || + diff_prefix(rt->rtm_family, dst, rt->rtm_dst_len, + f->dst_exact, &f->dst) || + diff_prefix(rt->rtm_family, gw, bitlen, + false, &f->gw) || + diff_prefix(rt->rtm_family, src, bitlen, + false, &f->src)) goto out; if (s->callback) @@ -988,7 +1217,8 @@ static int _route_dump(lua_State *L, struct dump_filter *filter) nlmsg_append(msg, &rtm, sizeof(rtm), 0); if (filter->get) - nla_put(msg, RTA_DST, filter->dst.len, &filter->dst.addr.v6); + nla_put(msg, RTA_DST, AF_BYTES(filter->dst.family), + &filter->dst.addr.v6); nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, cb_dump_route, &s); nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, cb_done, &s); @@ -1063,10 +1293,10 @@ static int route_dump(lua_State *L) filter.dst = p; if ((s = L_getstr(L, 1, "from_exact")) != NULL && parse_cidr(s, &p)) - filter.from = p, filter.from.exact = true; + filter.from = p, filter.from_exact = true; if ((s = L_getstr(L, 1, "dest_exact")) != NULL && parse_cidr(s, &p)) - filter.dst = p, filter.dst.exact = true; + filter.dst = p, filter.dst_exact = true; } return _route_dump(L, &filter); @@ -1107,12 +1337,12 @@ static int cb_dump_neigh(struct nl_msg *msg, void *arg) mac = tb[NDA_LLADDR] ? RTA_DATA(tb[NDA_LLADDR]) : NULL; dst = tb[NDA_DST] ? RTA_DATA(tb[NDA_DST]) : NULL; - bitlen = (nd->ndm_family == AF_INET) ? 32 : 128; + bitlen = AF_BITS(nd->ndm_family); if ((f->family && nd->ndm_family != f->family) || (f->iif && nd->ndm_ifindex != f->iif) || (f->type && !(f->type & nd->ndm_state)) || - diff_prefix(nd->ndm_family, dst, bitlen, &f->dst) || + diff_prefix(nd->ndm_family, dst, bitlen, false, &f->dst) || diff_macaddr(mac, &f->mac)) goto out; @@ -1140,15 +1370,7 @@ static int cb_dump_neigh(struct nl_msg *msg, void *arg) L_setaddr(s->L, "dest", nd->ndm_family, dst, -1); if (mac) - { - snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", - mac->ether_addr_octet[0], mac->ether_addr_octet[1], - mac->ether_addr_octet[2], mac->ether_addr_octet[3], - mac->ether_addr_octet[4], mac->ether_addr_octet[5]); - - lua_pushstring(s->L, buf); - lua_setfield(s->L, -2, "mac"); - } + L_setaddr(s->L, "mac", AF_PACKET, mac, -1); s->index++; @@ -1241,7 +1463,7 @@ out: static int cb_dump_link(struct nl_msg *msg, void *arg) { - char *p, *addr, buf[48]; + char buf[48]; struct dump_state *s = arg; struct nlmsghdr *hdr = nlmsg_hdr(msg); struct ifinfomsg *ifm = NLMSG_DATA(hdr); @@ -1266,19 +1488,8 @@ static int cb_dump_link(struct nl_msg *msg, void *arg) if (tb[IFLA_MASTER]) L_setdev(s->L, "master", tb[IFLA_MASTER]); - if (tb[IFLA_ADDRESS]) - { - len = nla_len(tb[IFLA_ADDRESS]); - addr = nla_get_string(tb[IFLA_ADDRESS]); - - if ((len * 3) <= sizeof(buf)) - { - for (p = buf, i = 0; i < len; i++) - p += sprintf(p, "%s%02x", (i ? ":" : ""), (uint8_t)*addr++); - - L_setstr(s->L, "mac", buf); - } - } + if (tb[IFLA_ADDRESS] && nla_len(tb[IFLA_ADDRESS]) == AF_BYTES(AF_PACKET)) + L_setaddr(s->L, "mac", AF_PACKET, nla_get_string(tb[IFLA_ADDRESS]), -1); s->pending = 0; return NL_SKIP; @@ -1333,13 +1544,18 @@ static const luaL_reg ip_methods[] = { { "new", cidr_new }, { "IPv4", cidr_ipv4 }, { "IPv6", cidr_ipv6 }, + { "MAC", cidr_mac }, + + { "checkip4", cidr_checkip4 }, + { "checkip6", cidr_checkip6 }, + { "checkmac", cidr_checkmac }, { "route", route_get }, { "routes", route_dump }, { "neighbors", neighbor_dump }, - { "link", link_get }, + { "link", link_get }, { } }; @@ -1351,6 +1567,9 @@ static const luaL_reg ip_cidr_methods[] = { { "is6", cidr_is6 }, { "is6linklocal", cidr_is6linklocal }, { "is6mapped4", cidr_is6mapped4 }, + { "ismac", cidr_ismac }, + { "ismaclocal", cidr_ismaclocal }, + { "ismacmcast", cidr_ismacmcast }, { "lower", cidr_lower }, { "higher", cidr_higher }, { "equal", cidr_equal }, @@ -1360,7 +1579,9 @@ static const luaL_reg ip_cidr_methods[] = { { "mask", cidr_mask }, { "broadcast", cidr_broadcast }, { "mapped4", cidr_mapped4 }, - { "contains", cidr_contains }, + { "tomac", cidr_tomac }, + { "tolinklocal", cidr_tolinklocal }, + { "contains", cidr_contains }, { "add", cidr_add }, { "sub", cidr_sub }, { "minhost", cidr_minhost }, diff --git a/libs/luci-lib-ip/src/ip.luadoc b/libs/luci-lib-ip/src/ip.luadoc index e32ae72f40..b1ecae1453 100644 --- a/libs/luci-lib-ip/src/ip.luadoc +++ b/libs/luci-lib-ip/src/ip.luadoc @@ -27,6 +27,7 @@ addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17", "ffff:ffff:ffff:ffff::") addr6 = luci.ip.new("fe80::221:63ff:fe75:aa17/64", 128) -- override netmask` @see IPv4 @see IPv6 +@see MAC ]] ---[[ @@ -47,6 +48,7 @@ addr = luci.ip.IPv4("10.24.0.1/255.255.255.0") addr = luci.ip.IPv4("10.24.0.1", "255.255.255.0") -- separate netmask addr = luci.ip.IPv4("10.24.0.1/24", 16) -- override netmask` @see IPv6 +@see MAC ]] ---[[ @@ -67,12 +69,112 @@ addr6 = luci.ip.IPv6("fe80::221:63ff:fe75:aa17/ffff:ffff:ffff:ffff::") addr6 = luci.ip.IPv6("fe80::221:63ff:fe75:aa17", "ffff:ffff:ffff:ffff::") addr6 = luci.ip.IPv6("fe80::221:63ff:fe75:aa17/64", 128) -- override netmask` @see IPv4 +@see MAC ]] ---[[ -Determine the route leading to the given destination. +Construct a new MAC luci.ip.cidr instance. +Throws an error if the given string does not represent a valid ethernet MAC +address or if the given optional mask is of a different family. @class function @sort 4 +@name MAC +@param address String containing a valid ethernet MAC address, optionally with +prefix size (CIDR notation) or mask separated by slash. +@param netmask String containing a valid MAC address mask or number +containing a prefix size between `0` and `48` bit. +Overrides mask embedded in the first argument if specified. (optional) +@return A `luci.ip.cidr` object representing the given MAC address range. +@usage `intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/24") +intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/FF:FF:FF:0:0:0") +intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00", "FF:FF:FF:0:0:0") +intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/24", 48) -- override mask` +@see IPv4 +@see IPv6 +]] + +---[[ +Verify an IPv4 address. + +Checks whether given argument is a preexisting luci.ip.cidr IPv4 address +instance or a string literal convertible to an IPv4 address and returns a +plain Lua string containing the canonical representation of the address. + +If the argument is not a valid address, returns nothing. This function is +intended to aid in safely verifying address literals without having to deal +with exceptions. +@class function +@sort 5 +@name checkip4 +@param address String containing a valid IPv4 address or existing +luci.ip.cidr IPv4 instance. +@return A string representing the given IPv4 address. +@usage `ipv4 = luci.ip.checkip4(luci.ip.new("127.0.0.1")) -- "127.0.0.1" +ipv4 = luci.ip.checkip4("127.0.0.1") -- "127.0.0.1" +ipv4 = luci.ip.checkip4("nonesense") -- nothing +ipv4 = luci.ip.checkip4(123) -- nothing +ipv4 = luci.ip.checkip4(nil) -- nothing +ipv4 = luci.ip.checkip4() -- nothing` +@see checkip6 +@see checkmac +]] + +---[[ +Verify an IPv6 address. + +Checks whether given argument is a preexisting luci.ip.cidr IPv6 address +instance or a string literal convertible to an IPv6 address and returns a +plain Lua string containing the canonical representation of the address. + +If the argument is not a valid address, returns nothing. This function is +intended to aid in safely verifying address literals without having to deal +with exceptions. +@class function +@sort 6 +@name checkip6 +@param address String containing a valid IPv6 address or existing +luci.ip.cidr IPv6 instance. +@return A string representing the given IPv6 address. +@usage `ipv6 = luci.ip.checkip6(luci.ip.new("0:0:0:0:0:0:0:1")) -- "::1" +ipv6 = luci.ip.checkip6("0:0:0:0:0:0:0:1") -- "::1" +ipv6 = luci.ip.checkip6("nonesense") -- nothing +ipv6 = luci.ip.checkip6(123) -- nothing +ipv6 = luci.ip.checkip6(nil) -- nothing +ipv6 = luci.ip.checkip6() -- nothing` +@see checkip4 +@see checkmac +]] + +---[[ +Verify an ethernet MAC address. + +Checks whether given argument is a preexisting luci.ip.cidr MAC address +instance or a string literal convertible to an ethernet MAC and returns a +plain Lua string containing the canonical representation of the address. + +If the argument is not a valid address, returns nothing. This function is +intended to aid in safely verifying address literals without having to deal +with exceptions. +@class function +@sort 7 +@name checkmac +@param address String containing a valid MAC address or existing luci.ip.cidr +MAC address instance. +@return A string representing the given MAC address. +@usage `mac = luci.ip.checkmac(luci.ip.new("00-11-22-cc-dd-ee")) -- "00:11:22:CC:DD:EE" +mac = luci.ip.checkmac("00:11:22:cc:dd:ee") -- "00:11:22:CC:DD:EE" +mac = luci.ip.checkmac("nonesense") -- nothing +mac = luci.ip.checkmac(123) -- nothing +mac = luci.ip.checkmac(nil) -- nothing +mac = luci.ip.checkmac() -- nothing` +@see checkip4 +@see checkip6 +]] + +---[[ +Determine the route leading to the given destination. +@class function +@sort 8 @name route @param address A `luci.ip.cidr` instance or a string containing a valid IPv4 or IPv6 range as specified by `luci.ip.new()`. @@ -178,7 +280,7 @@ end`</li> ---[[ Fetch all routes, optionally matching the given criteria. @class function -@sort 5 +@sort 9 @name routes @param filter <p>Table containing one or more of the possible filter critera described below (optional)</p><table> @@ -258,7 +360,7 @@ end`</li> ---[[ Fetches entries from the IPv4 ARP and IPv6 neighbour kernel table @class function -@sort 6 +@sort 10 @name neighbors @param filter <p>Table containing one or more of the possible filter critera described below (optional)</p><table> @@ -306,7 +408,7 @@ A neighbour entry is a table containing the following fields: </tr> <tr> <td>`mac`</td> - <td>String containing the associated MAC address</td> + <td>MAC address `luci.ip.cidr` instance</td> </tr> <tr> <td>`router`</td> @@ -367,7 +469,7 @@ end)`</li> ---[[ Fetch basic device information @class function -@sort 7 +@sort 11 @name link @param device String containing the network device to query @return If the given interface is found, a table containing the fields @@ -403,8 +505,8 @@ described below is returned, else an empty table. </tr> <tr> <td>`mac`</td> - <td>String containing the link local address of the device in - dotted hex notation</td> + <td>MAC address `luci.ip.cidr` instance representing the device ethernet + address</td> </tr> </table> @usage <ul> @@ -430,6 +532,7 @@ Checks whether the CIDR instance is an IPv4 address range @sort 1 @name cidr.is4 @see cidr.is6 +@see cidr.ismac @return `true` if the CIDR is an IPv4 range, else `false` ]] @@ -470,6 +573,7 @@ Checks whether the CIDR instance is an IPv6 address range @sort 4 @name cidr.is6 @see cidr.is4 +@see cidr.ismac @return `true` if the CIDR is an IPv6 range, else `false` ]] @@ -502,13 +606,51 @@ end` ]] ---[[ +Checks whether the CIDR instance is an ethernet MAC address range + +@class function +@sort 7 +@name cidr.ismac +@see cidr.is4 +@see cidr.is6 +@return `true` if the CIDR is a MAC address range, else `false` +]] + +---[[ +Checks whether the CIDR instance is a locally administered (LAA) MAC address + +@class function +@sort 8 +@name cidr.ismaclocal +@return `true` if the MAC address sets the locally administered bit. +@usage `local mac = luci.ip.new("02:C0:FF:EE:00:01") +if mac:ismaclocal() then + print("Is an LAA MAC address") +end` +]] + +---[[ +Checks whether the CIDR instance is a multicast MAC address + +@class function +@sort 9 +@name cidr.ismacmcast +@return `true` if the MAC address sets the multicast bit. +@usage `local mac = luci.ip.new("01:00:5E:7F:00:10") +if addr:ismacmcast() then + print("Is a multicast MAC address") +end` +]] + +---[[ Checks whether this CIDR instance is lower than the given argument. The comparisation follows these rules: -<ul><li>An IPv4 address is always lower than an IPv6 address</li> +<ul><li>An IPv4 address is always lower than an IPv6 address and IPv6 addresses +are considered lower than MAC addresses</li> <li>Prefix sizes are ignored</li></ul> @class function -@sort 7 +@sort 10 @name cidr.lower @param addr A `luci.ip.cidr` instance or a string convertable by `luci.ip.new()` to compare against. @@ -518,7 +660,8 @@ The comparisation follows these rules: print(addr:lower(addr)) -- false print(addr:lower("10.10.10.10/24")) -- false print(addr:lower(luci.ip.new("::1"))) -- true -print(addr:lower(luci.ip.new("192.168.200.1"))) -- true` +print(addr:lower(luci.ip.new("192.168.200.1"))) -- true +print(addr:lower(luci.ip.new("00:14:22:01:23:45"))) -- true` @see cidr.higher @see cidr.equal ]] @@ -526,11 +669,12 @@ print(addr:lower(luci.ip.new("192.168.200.1"))) -- true` ---[[ Checks whether this CIDR instance is higher than the given argument. The comparisation follows these rules: -<ul><li>An IPv4 address is always lower than an IPv6 address</li> +<ul><li>An IPv4 address is always lower than an IPv6 address and IPv6 addresses +are considered lower than MAC addresses</li> <li>Prefix sizes are ignored</li></ul> @class function -@sort 8 +@sort 11 @name cidr.higher @param addr A `luci.ip.cidr` instance or a string convertable by `luci.ip.new()` to compare against. @@ -540,7 +684,8 @@ The comparisation follows these rules: print(addr:higher(addr)) -- false print(addr:higher("10.10.10.10/24")) -- true print(addr:higher(luci.ip.new("::1"))) -- false -print(addr:higher(luci.ip.new("192.168.200.1"))) -- false` +print(addr:higher(luci.ip.new("192.168.200.1"))) -- false +print(addr:higher(luci.ip.new("00:14:22:01:23:45"))) -- false` @see cidr.lower @see cidr.equal ]] @@ -549,7 +694,7 @@ print(addr:higher(luci.ip.new("192.168.200.1"))) -- false` Checks whether this CIDR instance is equal to the given argument. @class function -@sort 9 +@sort 12 @name cidr.equal @param addr A `luci.ip.cidr` instance or a string convertable by `luci.ip.new()` to compare against. @@ -562,7 +707,11 @@ print(addr:equal(luci.ip.new("::1"))) -- false local addr6 = luci.ip.new("::1") print(addr6:equal("0:0:0:0:0:0:0:1/64")) -- true -print(addr6:equal(luci.ip.new("fe80::221:63ff:fe75:aa17"))) -- false` +print(addr6:equal(luci.ip.new("fe80::221:63ff:fe75:aa17"))) -- false + +local mac = luci.ip.new("00:14:22:01:23:45") +print(mac:equal("0:14:22:1:23:45")) -- true +print(mac:equal(luci.ip.new("01:23:45:67:89:AB")) -- false` @see cidr.lower @see cidr.higher ]] @@ -573,11 +722,11 @@ If the optional mask parameter is given, the prefix size of this CIDR is altered else the current prefix size is returned. @class function -@sort 10 +@sort 13 @name cidr.prefix @param mask Either a number containing the number of bits (`0..32` - for IPv4, `0..128` for IPv6) or a string containing a valid - netmask (optional) + for IPv4, `0..128` for IPv6 or `0..48` for MAC addresses) or a string + containing a valid netmask (optional) @return Bit count of the current prefix size @usage `local range = luci.ip.new("192.168.1.1/255.255.255.0") print(range:prefix()) -- 24 @@ -597,11 +746,11 @@ with all host parts masked out. The used prefix size can be overridden by the optional mask parameter. @class function -@sort 11 +@sort 14 @name cidr.network @param mask Either a number containing the number of bits (`0..32` - for IPv4, `0..128` for IPv6) or a string containing a valid - netmask (optional) + for IPv4, `0..128` for IPv6 or `0..48` for MAC addresses) or a string + containing a valid netmask (optional) @return CIDR instance representing the network address @usage `local range = luci.ip.new("192.168.62.243/255.255.0.0") print(range:network()) -- "192.168.0.0" @@ -616,10 +765,10 @@ print(range6:network()) -- "fd9b:62b3:9cc5::"` Derive host address of CIDR instance. This function essentially constructs a copy of this CIDR with the prefix size -set to `32` for IPv4 and `128` for IPv6. +set to `32` for IPv4, `128` for IPv6 or `48` for MAC addresses. @class function -@sort 12 +@sort 15 @name cidr.host @return CIDR instance representing the host address @usage `local range = luci.ip.new("172.19.37.45/16") @@ -634,11 +783,11 @@ Constructs a CIDR instance representing the netmask of this instance. The used prefix size can be overridden by the optional mask parameter. @class function -@sort 13 +@sort 16 @name cidr.mask @param mask Either a number containing the number of bits (`0..32` - for IPv4, `0..128` for IPv6) or a string containing a valid - netmask (optional) + for IPv4, `0..128` for IPv6 or `0..48` for MAC addresses) or a string + containing a valid netmask (optional) @return CIDR instance representing the netmask @usage `local range = luci.ip.new("172.19.37.45/16") print(range:mask()) -- "255.255.0.0" @@ -652,15 +801,14 @@ Derive broadcast address of CIDR instance. Constructs a CIDR instance representing the broadcast address of this instance. The used prefix size can be overridden by the optional mask parameter. -This function has no effect on IPv6 instances, it will return nothing in this -case. +This function has no effect on IPv6 or MAC address instances, it will return +nothing in this case. @class function -@sort 14 +@sort 17 @name cidr.broadcast -@param mask Either a number containing the number of bits (`0..32` - for IPv4, `0..128` for IPv6) or a string containing a valid - netmask (optional) +@param mask Either a number containing the number of bits (`0..32` for IPv4) or + a string containing a valid netmask (optional) @return Return a new CIDR instance representing the broadcast address if this instance is an IPv4 range, else return nothing. @usage `local range = luci.ip.new("172.19.37.45/16") @@ -675,11 +823,11 @@ Derive mapped IPv4 address of CIDR instance. Constructs a CIDR instance representing the IPv4 address of the IPv6 mapped IPv4 address in this instance. -This function has no effect on IPv4 instances or IPv6 instances which are not a -mapped address, it will return nothing in this case. +This function has no effect on IPv4 instances, MAC address instances or IPv6 +instances which are not a mapped address, it will return nothing in this case. @class function -@sort 15 +@sort 18 @name cidr.mapped4 @return Return a new CIDR instance representing the IPv4 address if this instance is an IPv6 mapped IPv4 address, else return nothing. @@ -688,10 +836,46 @@ print(addr:mapped4()) -- "172.16.19.1"` ]] ---[[ +Derive MAC address of IPv6 link local CIDR instance. + +Constructs a CIDR instance representing the MAC address contained in the IPv6 +link local address of this instance. + +This function has no effect on IPv4 instances, MAC address instances or IPv6 +instances which are not a link local address, it will return nothing in this +case. + +@class function +@sort 19 +@name cidr.tomac +@return Return a new CIDR instance representing the MAC address if this + instance is an IPv6 link local address, else return nothing. +@usage `local addr = luci.ip.new("fe80::6666:b3ff:fe47:e1b9") +print(addr:tomac()) -- "64:66:B3:47:E1:B9"` +]] + +---[[ +Derive IPv6 link local address from MAC address CIDR instance. + +Constructs a CIDR instance representing the IPv6 link local address of the +MAC address represented by this instance. + +This function has no effect on IPv4 instances or IPv6 instances, it will return +nothing in this case. + +@class function +@sort 20 +@name cidr.tolinklocal +@return Return a new CIDR instance representing the IPv6 link local address. +@usage `local mac = luci.ip.new("64:66:B3:47:E1:B9") +print(mac:tolinklocal()) -- "fe80::6666:b3ff:fe47:e1b9"` +]] + +---[[ Test whether CIDR contains given range. @class function -@sort 16 +@sort 21 @name cidr.contains @param addr A `luci.ip.cidr` instance or a string convertable by `luci.ip.new()` to test. @@ -704,7 +888,11 @@ print(range:contains("10.0.0.0/8")) -- false local range6 = luci.ip.new("fe80::/10") print(range6:contains("fe80::221:63f:fe75:aa17/64")) -- true -print(range6:contains("fd9b:6b3:c5:0:221:63f:fe75:aa17/64")) -- false` +print(range6:contains("fd9b:6b3:c5:0:221:63f:fe75:aa17/64")) -- false + +local intel_macs = luci.ip.MAC("C0:B6:F9:00:00:00/24") +print(intel_macs:contains("C0:B6:F9:A3:C:11")) -- true +print(intel_macs:contains("64:66:B3:47:E1:B9")) -- false` ]] ---[[ @@ -712,7 +900,7 @@ Add given amount to CIDR instance. If the result would overflow the maximum address space, the result is set to the highest possible address. @class function -@sort 17 +@sort 22 @name cidr.add @param amount A numeric value between 0 and 0xFFFFFFFF, a `luci.ip.cidr` instance or a string convertable by @@ -726,32 +914,42 @@ address space, the result is set to the highest possible address. this instance plus the added amount or the highest possible address if the addition overflowed the available address space.</li></ul> @usage `local addr = luci.ip.new("192.168.1.1/24") -print(addr:add(250)) -- "192.168.1.251/24" -print(addr:add("0.0.99.0")) -- "192.168.100.1/24" +print(addr:add(250)) -- "192.168.1.251/24" +print(addr:add("0.0.99.0")) -- "192.168.100.1/24" -addr:add(256, true) -- true -print(addr) -- "192.168.2.1/24 +addr:add(256, true) -- true +print(addr) -- "192.168.2.1/24 -addr:add("255.0.0.0", true) -- false (overflow) -print(addr) -- "255.255.255.255/24 +addr:add("255.0.0.0", true) -- false (overflow) +print(addr) -- "255.255.255.255/24 local addr6 = luci.ip.new("fe80::221:63f:fe75:aa17/64") -print(addr6:add(256)) -- "fe80::221:63f:fe75:ab17/64" -print(addr6:add("::ffff:0")) -- "fe80::221:640:fe74:aa17/64" +print(addr6:add(256)) -- "fe80::221:63f:fe75:ab17/64" +print(addr6:add("::ffff:0")) -- "fe80::221:640:fe74:aa17/64" + +addr6:add(256, true) -- true +print(addr6) -- "fe80::221:63f:fe75:ab17/64 + +addr6:add("ffff::", true) -- false (overflow) +print(addr6) -- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64" -addr:add(256, true) -- true -print(addr) -- "fe80::221:63f:fe75:ab17/64 +local mac = luci.ip.new("00:14:22:01:23:45") +print(mac:add(256)) -- "00:14:22:01:24:45" +print(mac:add("0:0:0:0:FF:0") -- "00:14:22:02:22:45" -addr:add("ffff::", true) -- false (overflow) -print(addr) -- "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/64"` +mac:add(256, true) -- true +print(mac) -- "00:14:22:01:24:45" + +mac:add("FF:FF:0:0:0:0", true) -- false (overflow) +print(mac) -- "FF:FF:FF:FF:FF:FF"` ]] ---[[ -Substract given amount from CIDR instance. If the result would under, the lowest +Subtract given amount from CIDR instance. If the result would under, the lowest possible address is returned. @class function -@sort 18 +@sort 23 @name cidr.sub @param amount A numeric value between 0 and 0xFFFFFFFF, a `luci.ip.cidr` instance or a string convertable by @@ -759,11 +957,11 @@ possible address is returned. @param inplace If `true`, modify this instance instead of returning a new derived CIDR instance. @return <ul> - <li>When substracting inplace: Return `true` if the substraction - succeded or `false` when the substraction underflowed.</li> + <li>When subtracting inplace: Return `true` if the subtraction + succeeded or `false` when the subtraction underflowed.</li> <li>When deriving new CIDR: Return new instance representing the value of - this instance minus the substracted amount or the lowest address if - the substraction underflowed.</li></ul> + this instance minus the subtracted amount or the lowest address if + the subtraction underflowed.</li></ul> @usage `local addr = luci.ip.new("192.168.1.1/24") print(addr:sub(256)) -- "192.168.0.1/24" print(addr:sub("0.168.0.0")) -- "192.0.1.1/24" @@ -782,14 +980,24 @@ addr:sub(256, true) -- true print(addr) -- "fe80::221:63f:fe75:a917/64" addr:sub("ffff::", true) -- false (underflow) -print(addr) -- "::/64"` +print(addr) -- "::/64" + +local mac = luci.ip.new("00:14:22:01:23:45") +print(mac:sub(256)) -- "00:14:22:01:22:45" +print(mac:sub("0:0:0:0:FF:0") -- "00:14:22:00:24:45" + +mac:sub(256, true) -- true +print(mac) -- "00:14:22:01:22:45" + +mac:sub("FF:FF:0:0:0:0", true) -- false (overflow) +print(mac) -- "00:00:00:00:00:00"` ]] ---[[ Calculate the lowest possible host address within this CIDR instance. @class function -@sort 19 +@sort 24 @name cidr.minhost @return Returns a new CIDR instance representing the lowest host address within this range. @@ -797,14 +1005,17 @@ Calculate the lowest possible host address within this CIDR instance. print(addr:minhost()) -- "192.168.123.1" local addr6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64") -print(addr6:minhost()) -- "fd9b:62b3:9cc5::1"` +print(addr6:minhost()) -- "fd9b:62b3:9cc5::1" + +local mac = luci.ip.new("00:14:22:01:22:45/32") +print(mac:minhost()) -- "00:14:22:01:00:01"` ]] ---[[ Calculate the highest possible host address within this CIDR instance. @class function -@sort 20 +@sort 25 @name cidr.maxhost @return Returns a new CIDR instance representing the highest host address within this range. @@ -812,20 +1023,24 @@ Calculate the highest possible host address within this CIDR instance. print(addr:maxhost()) -- "192.168.123.254" (.255 is broadcast) local addr6 = luci.ip.new("fd9b:62b3:9cc5:0:221:63ff:fe75:aa17/64") -print(addr6:maxhost()) -- "fd9b:62b3:9cc5:0:ffff:ffff:ffff:ffff"` +print(addr6:maxhost()) -- "fd9b:62b3:9cc5:0:ffff:ffff:ffff:ffff" + +local mac = luci.ip.new("00:14:22:01:22:45/32") +print(mac:maxhost()) -- "00:14:22:01:FF:FF"` ]] ---[[ Convert CIDR instance into string representation. -If the prefix size of instance is less than 32 for IPv4 or 128 for IPv6, the -address is returned in the form "address/prefix" otherwise just "address". +If the prefix size of instance is less than 32 for IPv4, 128 for IPv6 or 48 for +MACs, the address is returned in the form "address/prefix" otherwise just +"address". It is usually not required to call this function directly as CIDR objects define it as __tostring function in the associated metatable. @class function -@sort 21 +@sort 26 @name cidr.string @return Returns a string representing the range or address of this CIDR instance ]] diff --git a/libs/luci-lib-json/Makefile b/libs/luci-lib-json/Makefile index 069886d5c3..ededc1f650 100644 --- a/libs/luci-lib-json/Makefile +++ b/libs/luci-lib-json/Makefile @@ -9,6 +9,8 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=LuCI JSON library LUCI_DEPENDS:= +PKG_LICENSE:=Apache-2.0 + include ../../luci.mk # call BuildPackage - OpenWrt buildroot signature diff --git a/libs/luci-lib-json/luasrc/json.lua b/libs/luci-lib-json/luasrc/json.lua index f7b57f916c..6d4d752b19 100644 --- a/libs/luci-lib-json/luasrc/json.lua +++ b/libs/luci-lib-json/luasrc/json.lua @@ -181,13 +181,18 @@ function Encoder.parse_iter(self, obj) end end +function Encoder.parse_udata(self, obj) + return self:parse_string(tostring(obj)) +end + Encoder.parsers = { ['nil'] = Encoder.parse_nil, ['table'] = Encoder.parse_iter, ['number'] = Encoder.parse_number, ['string'] = Encoder.parse_string, ['boolean'] = Encoder.parse_bool, - ['function'] = Encoder.parse_iter + ['function'] = Encoder.parse_iter, + ['userdata'] = Encoder.parse_udata, } diff --git a/libs/luci-lib-nixio/Makefile b/libs/luci-lib-nixio/Makefile index 41800fe54b..bec5f83cda 100644 --- a/libs/luci-lib-nixio/Makefile +++ b/libs/luci-lib-nixio/Makefile @@ -9,6 +9,8 @@ include $(TOPDIR)/rules.mk LUCI_TITLE:=NIXIO POSIX library LUCI_DEPENDS:=+PACKAGE_luci-lib-nixio_openssl:libopenssl +PACKAGE_luci-lib-nixio_cyassl:libcyassl +liblua +PKG_LICENSE:=Apache-2.0 + include ../../luci.mk # call BuildPackage - OpenWrt buildroot signature diff --git a/libs/luci-lib-nixio/docsrc/README.lua b/libs/luci-lib-nixio/docsrc/README.lua index ee3e3a216c..9860cf0919 100644 --- a/libs/luci-lib-nixio/docsrc/README.lua +++ b/libs/luci-lib-nixio/docsrc/README.lua @@ -33,7 +33,7 @@ module "nixio.README" -- <br />In general all functions are namend and behave like their POSIX API -- counterparts - where applicable - applying the following rules: -- <ul> --- <li>Functions should be named like the underlying POSIX API function ommiting +-- <li>Functions should be named like the underlying POSIX API function omitting -- prefixes or suffixes - especially when placed in an object-context ( -- lockf -> File:lock, fsync -> File:sync, dup2 -> dup, ...)</li> -- <li>If you are unclear about the behaviour of a function you should consult @@ -41,10 +41,10 @@ module "nixio.README" -- <li>If the name is significantly different from the POSIX-function, the -- underlying function(s) are stated in the documentation.</li> -- <li>Parameters should reflect those of the C-API, buffer length arguments and --- by-reference parameters should be ommitted for pratical purposes.</li> +-- by-reference parameters should be omitted for practical purposes.</li> -- <li>If a C function accepts a bitfield as parameter, it should be translated -- into lower case string flags representing the flags if the bitfield is the --- last parameter and also ommiting prefixes or suffixes. (e.g. waitpid +-- last parameter and also omitting prefixes or suffixes. (e.g. waitpid -- (pid, &s, WNOHANG | WUNTRACED) -> waitpid(pid, "nohang", "untraced"), -- getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) -> -- Socket:getopt("socket", "reuseaddr"), etc.) </li> diff --git a/libs/luci-lib-nixio/docsrc/nixio.Socket.lua b/libs/luci-lib-nixio/docsrc/nixio.Socket.lua index 7123393bf4..c970534ed0 100644 --- a/libs/luci-lib-nixio/docsrc/nixio.Socket.lua +++ b/libs/luci-lib-nixio/docsrc/nixio.Socket.lua @@ -71,7 +71,7 @@ module "nixio.Socket" --- Send a message on the socket. -- This function is identical to sendto except for the missing destination --- paramters. See the sendto description for a detailed description. +-- parameters. See the sendto description for a detailed description. -- @class function -- @name Socket.send -- @param buffer Buffer holding the data to be written. @@ -167,4 +167,4 @@ module "nixio.Socket" -- "mtu" (IP, IPv6), "hdrincl" (IP), "multicast_ttl" (IP), "multicast_loop" -- (IP, IPv6), "multicast_if" (IP, IPv6), "v6only" (IPv6), "multicast_hops" -- (IPv6), "add_membership" (IP, IPv6), "drop_membership" (IP, IPv6)] --- @return Value
\ No newline at end of file +-- @return Value diff --git a/libs/luci-lib-nixio/docsrc/nixio.UnifiedIO.lua b/libs/luci-lib-nixio/docsrc/nixio.UnifiedIO.lua index 6e407ff2ca..6d1d5ea8c6 100644 --- a/libs/luci-lib-nixio/docsrc/nixio.UnifiedIO.lua +++ b/libs/luci-lib-nixio/docsrc/nixio.UnifiedIO.lua @@ -24,15 +24,15 @@ module "nixio.UnifiedIO" -- @class function -- @name UnifiedIO.readall -- @usage This function uses the low-level read function of the descriptor. --- @usage If the length parameter is ommited, this function returns all data +-- @usage If the length parameter is omitted, this function returns all data -- that can be read before an end-of-file, end-of-stream, connection shutdown -- or similar happens. -- @usage If the descriptor is non-blocking this function may fail with EAGAIN. -- @param length Bytes to read (optional) --- @return data that was successfully read if no error occured +-- @return data that was successfully read if no error occurred -- @return - reserved for error code - -- @return - reserved for error message - --- @return data that was successfully read even if an error occured +-- @return data that was successfully read even if an error occurred --- Write a block of data and wait until all data is written. -- @class function @@ -40,10 +40,10 @@ module "nixio.UnifiedIO" -- @usage This function uses the low-level write function of the descriptor. -- @usage If the descriptor is non-blocking this function may fail with EAGAIN. -- @param block Bytes to write --- @return bytes that were successfully written if no error occured +-- @return bytes that were successfully written if no error occurred -- @return - reserved for error code - -- @return - reserved for error message - --- @return bytes that were successfully written even if an error occured +-- @return bytes that were successfully written even if an error occurred --- Create a line-based iterator. -- Lines may end with either \n or \r\n, these control chars are not included @@ -56,7 +56,7 @@ module "nixio.UnifiedIO" -- to stop reading line-based and want to use the read(all) functions instead -- you can pass "true" to the iterator which will flush the buffer -- and return the bufferd data. --- @usage If the limit parameter is ommited, this function uses the nixio +-- @usage If the limit parameter is omitted, this function uses the nixio -- buffersize (8192B by default). -- @usage If the descriptor is non-blocking the iterator may fail with EAGAIN. -- @usage The iterator can be used as an LTN12 source. @@ -69,7 +69,7 @@ module "nixio.UnifiedIO" -- @usage This function uses the low-level read function of the descriptor. -- @usage The blocksize given is only advisory and to be seen as an upper limit, -- if an underlying read returns less bytes the chunk is nevertheless returned. --- @usage If the limit parameter is ommited, the iterator returns data +-- @usage If the limit parameter is omitted, the iterator returns data -- until an end-of-file, end-of-stream, connection shutdown or similar happens. -- @usage The iterator will not buffer so it is safe to mix with calls to read. -- @usage If the descriptor is non-blocking the iterator may fail with EAGAIN. @@ -94,15 +94,15 @@ module "nixio.UnifiedIO" -- @name UnifiedIO.copy -- @usage This function uses the blocksource function of the source descriptor -- and the sink function of the target descriptor. --- @usage If the limit parameter is ommited, data is copied +-- @usage If the limit parameter is omitted, data is copied -- until an end-of-file, end-of-stream, connection shutdown or similar happens. -- @usage If the descriptor is non-blocking the function may fail with EAGAIN. -- @param fdout Target Descriptor -- @param size Bytes to copy (optional) --- @return bytes that were successfully written if no error occured +-- @return bytes that were successfully written if no error occurred -- @return - reserved for error code - -- @return - reserved for error message - --- @return bytes that were successfully written even if an error occured +-- @return bytes that were successfully written even if an error occurred --- Copy data from the current descriptor to another one using kernel-space -- copying if possible. @@ -111,19 +111,19 @@ module "nixio.UnifiedIO" -- @usage This function uses the sendfile() syscall to copy the data or the -- blocksource function of the source descriptor and the sink function -- of the target descriptor as a fallback mechanism. --- @usage If the limit parameter is ommited, data is copied +-- @usage If the limit parameter is omitted, data is copied -- until an end-of-file, end-of-stream, connection shutdown or similar happens. -- @usage If the descriptor is non-blocking the function may fail with EAGAIN. -- @param fdout Target Descriptor -- @param size Bytes to copy (optional) --- @return bytes that were successfully written if no error occured +-- @return bytes that were successfully written if no error occurred -- @return - reserved for error code - -- @return - reserved for error message - --- @return bytes that were successfully written even if an error occured +-- @return bytes that were successfully written even if an error occurred --- Close the descriptor. -- @class function -- @name UnifiedIO.close -- @usage If the descriptor is a TLS-socket the underlying descriptor is -- closed without touching the TLS connection. --- @return true
\ No newline at end of file +-- @return true diff --git a/libs/luci-lib-nixio/docsrc/nixio.fs.lua b/libs/luci-lib-nixio/docsrc/nixio.fs.lua index 5d08719e96..ef495f260a 100644 --- a/libs/luci-lib-nixio/docsrc/nixio.fs.lua +++ b/libs/luci-lib-nixio/docsrc/nixio.fs.lua @@ -47,7 +47,7 @@ module "nixio.fs" -- @name nixio.fs.rename -- @param src Source path -- @param dest Destination path --- @usage It is normally not possible to rename files accross fileystems. +-- @usage It is normally not possible to rename files across filesystems. -- @return true --- Remove an empty directory. @@ -262,4 +262,4 @@ module "nixio.fs" -- omit the basename even if source and destination basename are equal. -- @param src Source path -- @param dest Destination path --- @return true
\ No newline at end of file +-- @return true diff --git a/libs/luci-lib-nixio/docsrc/nixio.lua b/libs/luci-lib-nixio/docsrc/nixio.lua index 1b434d76de..1a0d69a054 100644 --- a/libs/luci-lib-nixio/docsrc/nixio.lua +++ b/libs/luci-lib-nixio/docsrc/nixio.lua @@ -118,7 +118,7 @@ module "nixio" -- @param flag1 First Flag ["append", "creat", "excl", "nonblock", "ndelay", -- "sync", "trunc", "rdonly", "wronly", "rdwr"] -- @param ... More Flags [-"-] --- @return flag to be used as second paramter to open +-- @return flag to be used as second parameter to open --- Duplicate a file descriptor. -- @class function @@ -167,7 +167,7 @@ module "nixio" --- Wait for some event on a file descriptor. -- poll() sets the revents-field of the tables provided by fds to a bitfield --- indicating the events that occured. +-- indicating the events that occurred. -- @class function -- @usage This function works in-place on the provided table and only -- writes the revents field, you can use other fields on your demand. @@ -303,7 +303,7 @@ module "nixio" --- Set or unset a environment variable. -- @class function -- @name nixio.setenv --- @usage The environment variable will be unset if value is ommited. +-- @usage The environment variable will be unset if value is omitted. -- @param variable Variable -- @param value Value (optional) -- @return true diff --git a/libs/luci-lib-nixio/src/fs.c b/libs/luci-lib-nixio/src/fs.c index 12ca111ac0..ba184ed119 100644 --- a/libs/luci-lib-nixio/src/fs.c +++ b/libs/luci-lib-nixio/src/fs.c @@ -355,7 +355,7 @@ static int nixio_stat(lua_State *L) { static int nixio_lstat(lua_State *L) { nixio_stat_t buf; - if (stat(luaL_checkstring(L, 1), &buf)) { + if (lstat(luaL_checkstring(L, 1), &buf)) { return nixio__perror(L); } else { nixio__push_stat(L, &buf); diff --git a/libs/luci-lib-px5g/Makefile b/libs/luci-lib-px5g/Makefile index 70b95e8505..eefee107e8 100644 --- a/libs/luci-lib-px5g/Makefile +++ b/libs/luci-lib-px5g/Makefile @@ -10,6 +10,7 @@ LUCI_TITLE:=RSA/X.509 Key Generator (required for LuCId SSL support) LUCI_DEPENDS:=+liblua PKG_USE_MIPS16:=0 +PKG_LICENSE:=LGPL-2.1 include ../../luci.mk diff --git a/libs/rpcd-mod-rrdns/Makefile b/libs/rpcd-mod-rrdns/Makefile new file mode 100644 index 0000000000..f0bf140a87 --- /dev/null +++ b/libs/rpcd-mod-rrdns/Makefile @@ -0,0 +1,47 @@ +# +# Copyright (C) 2016-2017 Jo-Philipp Wich <jo@mein.io> +# +# Licensed under the Apache License, Version 2.0. +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=rpcd-mod-rrdns +PKG_VERSION:=20170710 +PKG_MAINTAINER:=Jo-Philipp Wich <jo@mein.io> + +PKG_LICENSE:=Apache-2.0 + +PKG_BUILD_PARALLEL:=1 + +include $(INCLUDE_DIR)/package.mk +include $(INCLUDE_DIR)/cmake.mk + +define Build/Prepare + $(INSTALL_DIR) $(PKG_BUILD_DIR) + $(CP) ./src/* $(PKG_BUILD_DIR)/ +endef + +define Package/rpcd-mod-rrdns + SECTION:=libs + CATEGORY:=Libraries + TITLE:=Rapid reverse DNS rpcd module + DEPENDS:=+rpcd +libubox +libubus +endef + +define Package/rpcd-mod-rrdns/description + Provides rapid mass reverse DNS lookup functionality. +endef + +define Package/rpcd-mod-rrdns/install + $(INSTALL_DIR) $(1)/usr/lib/rpcd + $(INSTALL_BIN) $(PKG_BUILD_DIR)/rrdns.so $(1)/usr/lib/rpcd/ +endef + +define Package/rpcd-mod-rrdns/postinst +#!/bin/sh +killall -HUP rpcd 2>/dev/null +exit 0 +endef + +$(eval $(call BuildPackage,rpcd-mod-rrdns)) diff --git a/libs/rpcd-mod-rrdns/src/CMakeLists.txt b/libs/rpcd-mod-rrdns/src/CMakeLists.txt new file mode 100644 index 0000000000..ace6ac8664 --- /dev/null +++ b/libs/rpcd-mod-rrdns/src/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 2.6) + +PROJECT(rpcd-mod-rrdns C) + +ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations) + +SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") + +IF(APPLE) + INCLUDE_DIRECTORIES(/opt/local/include) + LINK_DIRECTORIES(/opt/local/lib) +ENDIF() + +FIND_LIBRARY(resolv NAMES resolv) +IF(resolv STREQUAL "LIBS-NOTFOUND") + SET(resolv "") +ENDIF() + +ADD_LIBRARY(rpcd-mod-rrdns MODULE rrdns.c) +TARGET_LINK_LIBRARIES(rpcd-mod-rrdns ubox ubus ${resolv}) +SET_TARGET_PROPERTIES(rpcd-mod-rrdns PROPERTIES OUTPUT_NAME rrdns PREFIX "") + +INSTALL(TARGETS rpcd-mod-rrdns LIBRARY DESTINATION lib) diff --git a/libs/rpcd-mod-rrdns/src/rrdns.c b/libs/rpcd-mod-rrdns/src/rrdns.c new file mode 100644 index 0000000000..691db9c8fa --- /dev/null +++ b/libs/rpcd-mod-rrdns/src/rrdns.c @@ -0,0 +1,389 @@ +/* + * rrdns - Rapid Reverse DNS lookup plugin for the UBUS RPC server + * + * Copyright (C) 2016 Jo-Philipp Wich <jow@openwrt.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> +#include <unistd.h> + +#include <arpa/nameser.h> +#include <arpa/inet.h> + +#include <sys/types.h> +#include <sys/socket.h> + +#include <resolv.h> + +#include <libubox/avl.h> +#include <libubox/usock.h> +#include <libubox/uloop.h> + +#include <rpcd/plugin.h> + +#include "rrdns.h" + + +enum { + RPC_L_ADDRS, + RPC_L_TIMEOUT, + RPC_L_SERVER, + RPC_L_PORT, + RPC_L_LIMIT, + __RPC_L_MAX, +}; + +static const struct blobmsg_policy rpc_lookup_policy[__RPC_L_MAX] = { + [RPC_L_ADDRS] = { .name = "addrs", .type = BLOBMSG_TYPE_ARRAY }, + [RPC_L_TIMEOUT] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 }, + [RPC_L_SERVER] = { .name = "server", .type = BLOBMSG_TYPE_STRING }, + [RPC_L_PORT] = { .name = "port", .type = BLOBMSG_TYPE_INT16 }, + [RPC_L_LIMIT] = { .name = "limit", .type = BLOBMSG_TYPE_INT32 }, +}; + + +static int +rrdns_cmp_id(const void *k1, const void *k2, void *ptr) +{ + const uint16_t *id1 = k1, *id2 = k2; + return (*id1 - *id2); +} + +static int +rrdns_cmp_addr(const void *k1, const void *k2, void *ptr) +{ + const struct in6_addr *a1 = k1, *a2 = k2; + return memcmp(a1, a2, sizeof(*a1)); +} + +static int +rrdns_parse_response(struct rrdns_context *rctx) +{ + int n, len; + uint16_t id; + struct rrdns_request *req; + unsigned char res[512]; + char buf[INET6_ADDRSTRLEN], dname[MAXDNAME]; + HEADER *hdr; + ns_msg handle; + ns_rr rr; + + len = recv(rctx->socket.fd, res, sizeof(res), 0); + + if (len < sizeof(*hdr)) + return -ENODATA; + + hdr = (HEADER *)res; + id = hdr->id; + req = avl_find_element(&rctx->request_ids, &id, req, by_id); + + if (!req) + return -ENOENT; + + avl_delete(&rctx->request_ids, &req->by_id); + + if (ns_initparse(res, len, &handle)) + return -EINVAL; + + for (n = 0; n < ns_msg_count(handle, ns_s_an); n++) { + if (ns_parserr(&handle, ns_s_an, n, &rr)) + return -EINVAL; + + if (ns_rr_type(rr) != ns_t_ptr) + continue; + + if (ns_name_uncompress(ns_msg_base(handle), ns_msg_end(handle), + ns_rr_rdata(rr), dname, sizeof(dname)) < 0) + return -EINVAL; + + inet_ntop(req->family, &req->addr, buf, sizeof(buf)); + blobmsg_add_string(&rctx->blob, buf, dname); + } + + return 0; +} + +static int +rrdns_next_query(struct rrdns_context *rctx) +{ + const char *addr = NULL, *hex = "0123456789abcdef"; + struct rrdns_request *req; + int i, alen, family; + char *p, dname[73]; + + union { + unsigned char uchar[4]; + struct in6_addr in6; + struct in_addr in; + } a = { }; + + union { + unsigned char buf[512]; + HEADER hdr; + } msg; + + if (rctx->addr_rem > 0 && + blob_pad_len(rctx->addr_cur) <= rctx->addr_rem && + blob_pad_len(rctx->addr_cur) >= sizeof(struct blob_attr)) { + + addr = blobmsg_get_string(rctx->addr_cur); + rctx->addr_rem -= blob_pad_len(rctx->addr_cur); + rctx->addr_cur = blob_next(rctx->addr_cur); + } + + if (!addr) + return 0; + + if (inet_pton(AF_INET6, addr, &a.in6)) { + memset(dname, 0, sizeof(dname)); + + for (i = 0, p = dname; i < 16; i++) { + *p++ = hex[a.in6.s6_addr[15-i] % 16]; + *p++ = '.'; + *p++ = hex[a.in6.s6_addr[15-i] / 16]; + *p++ = '.'; + } + + p += snprintf(p, p - dname - 1, "ip6.arpa"); + + family = AF_INET6; + alen = p - dname; + } + else if (inet_pton(AF_INET, addr, &a.in)) { + family = AF_INET; + alen = snprintf(dname, sizeof(dname), "%u.%u.%u.%u.in-addr.arpa", + a.uchar[3], a.uchar[2], a.uchar[1], a.uchar[0]); + } + else { + return -EINVAL; + } + + alen = res_mkquery(QUERY, dname, C_IN, T_PTR, NULL, 0, NULL, + msg.buf, sizeof(msg.buf)); + + if (alen < 0) + return alen; + + if (avl_find(&rctx->request_addrs, &a.in6)) + return -ENOTUNIQ; + + if (send(rctx->socket.fd, msg.buf, alen, 0) != alen) + return -errno; + + req = calloc(1, sizeof(*req)); + + if (!req) + return -ENOMEM; + + req->id = msg.hdr.id; + req->by_id.key = &req->id; + avl_insert(&rctx->request_ids, &req->by_id); + + req->family = family; + req->addr.in6 = a.in6; + req->by_addr.key = &req->addr.in6; + avl_insert(&rctx->request_addrs, &req->by_addr); + + return 0; +} + +static void +rdns_shutdown(struct rrdns_context *rctx) +{ + struct rrdns_request *req, *tmp; + + uloop_timeout_cancel(&rctx->timeout); + uloop_fd_delete(&rctx->socket); + + close(rctx->socket.fd); + + ubus_send_reply(rctx->context, &rctx->request, rctx->blob.head); + ubus_complete_deferred_request(rctx->context, &rctx->request, + UBUS_STATUS_OK); + + avl_remove_all_elements(&rctx->request_addrs, req, by_addr, tmp) + free(req); + + blob_buf_free(&rctx->blob); + free(rctx); +} + +static void +rrdns_handle_timeout(struct uloop_timeout *utm) +{ + struct rrdns_context *rctx = + container_of(utm, struct rrdns_context, timeout); + + rdns_shutdown(rctx); +} + +static void +rrdns_handle_response(struct uloop_fd *ufd, unsigned int ev) +{ + struct rrdns_context *rctx = + container_of(ufd, struct rrdns_context, socket); + + int err = rrdns_parse_response(rctx); + + if (err != -ENODATA && err != -ENOENT) + rrdns_next_query(rctx); + + if (avl_is_empty(&rctx->request_ids)) + rdns_shutdown(rctx); +} + +static char * +rrdns_find_nameserver(void) +{ + static char line[2*INET6_ADDRSTRLEN]; + struct in6_addr in6; + FILE *resolvconf; + char *p; + + resolvconf = fopen("/etc/resolv.conf", "r"); + + if (!resolvconf) + return NULL; + + while (fgets(line, sizeof(line), resolvconf)) { + p = strtok(line, " \t"); + + if (!p || strcmp(p, "nameserver")) + continue; + + p = strtok(NULL, " \t\r\n"); + + if (!p) + continue; + + if (!inet_pton(AF_INET6, p, &in6) && !inet_pton(AF_INET, p, &in6)) + continue; + + fclose(resolvconf); + return p; + } + + fclose(resolvconf); + return NULL; +} + +static int +rpc_rrdns_lookup(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + int port = 53, limit = RRDNS_DEF_LIMIT, timeout = RRDNS_DEF_TIMEOUT; + struct blob_attr *tb[__RPC_L_MAX]; + struct rrdns_context *rctx; + const char *server = NULL; + + blobmsg_parse(rpc_lookup_policy, __RPC_L_MAX, tb, + blob_data(msg), blob_len(msg)); + + if (tb[RPC_L_PORT]) + port = blobmsg_get_u16(tb[RPC_L_PORT]); + + if (tb[RPC_L_LIMIT]) + limit = blobmsg_get_u32(tb[RPC_L_LIMIT]); + + if (tb[RPC_L_TIMEOUT]) + timeout = blobmsg_get_u32(tb[RPC_L_TIMEOUT]); + + if (tb[RPC_L_SERVER]) + server = blobmsg_get_string(tb[RPC_L_SERVER]); + + + if (!tb[RPC_L_ADDRS]) + return UBUS_STATUS_INVALID_ARGUMENT; + + if (port <= 0) + return UBUS_STATUS_INVALID_ARGUMENT; + + if (limit <= 0 || limit > RRDNS_MAX_LIMIT) + return UBUS_STATUS_INVALID_ARGUMENT; + + if (timeout <= 0 || timeout > RRDNS_MAX_TIMEOUT) + return UBUS_STATUS_INVALID_ARGUMENT; + + + if (!server || !*server) + server = rrdns_find_nameserver(); + + if (!server) + return UBUS_STATUS_NOT_FOUND; + + rctx = calloc(1, sizeof(*rctx)); + + if (!rctx) + return UBUS_STATUS_UNKNOWN_ERROR; + + rctx->socket.fd = usock(USOCK_UDP, server, usock_port(port)); + + if (rctx->socket.fd < 0) { + free(rctx); + return UBUS_STATUS_UNKNOWN_ERROR; + } + + rctx->context = ctx; + rctx->addr_cur = blobmsg_data(tb[RPC_L_ADDRS]); + rctx->addr_rem = blobmsg_data_len(tb[RPC_L_ADDRS]); + + avl_init(&rctx->request_ids, rrdns_cmp_id, false, NULL); + avl_init(&rctx->request_addrs, rrdns_cmp_addr, false, NULL); + + rctx->timeout.cb = rrdns_handle_timeout; + uloop_timeout_set(&rctx->timeout, timeout); + + rctx->socket.cb = rrdns_handle_response; + uloop_fd_add(&rctx->socket, ULOOP_READ); + + blob_buf_init(&rctx->blob, 0); + + while (limit--) + rrdns_next_query(rctx); + + ubus_defer_request(ctx, req, &rctx->request); + + return UBUS_STATUS_OK; +} + + +static int +rpc_rrdns_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx) +{ + static const struct ubus_method rrdns_methods[] = { + UBUS_METHOD("lookup", rpc_rrdns_lookup, rpc_lookup_policy), + }; + + static struct ubus_object_type rrdns_type = + UBUS_OBJECT_TYPE("rpcd-rrdns", rrdns_methods); + + static struct ubus_object obj = { + .name = "network.rrdns", + .type = &rrdns_type, + .methods = rrdns_methods, + .n_methods = ARRAY_SIZE(rrdns_methods), + }; + + return ubus_add_object(ctx, &obj); +} + +struct rpc_plugin rpc_plugin = { + .init = rpc_rrdns_api_init +}; diff --git a/libs/rpcd-mod-rrdns/src/rrdns.h b/libs/rpcd-mod-rrdns/src/rrdns.h new file mode 100644 index 0000000000..3f95116f08 --- /dev/null +++ b/libs/rpcd-mod-rrdns/src/rrdns.h @@ -0,0 +1,51 @@ +/* + * rrdns - Rapid Reverse DNS lookup plugin for the UBUS RPC server + * + * Copyright (C) 2016-2017 Jo-Philipp Wich <jo@mein.io> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <libubus.h> +#include <libubox/avl.h> +#include <libubox/uloop.h> + +#define RRDNS_MAX_TIMEOUT 5000 +#define RRDNS_DEF_TIMEOUT 250 + +#define RRDNS_MAX_LIMIT 1000 +#define RRDNS_DEF_LIMIT 10 + + +struct rrdns_request { + struct avl_node by_id; + struct avl_node by_addr; + uint16_t id; + uint16_t family; + union { + struct in_addr in; + struct in6_addr in6; + } addr; +}; + +struct rrdns_context { + struct ubus_context *context; + struct ubus_request_data request; + struct uloop_timeout timeout; + struct blob_attr *addr_cur; + int addr_rem; + struct uloop_fd socket; + struct blob_buf blob; + struct avl_tree request_ids; + struct avl_tree request_addrs; +}; |