From 7d6bae27cc46c2a587e8e8c3515b1322c8271ed2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Asbj=C3=B8rn=20Sloth=20T=C3=B8nnesen?= Date: Mon, 3 Apr 2023 17:30:14 +0000 Subject: expose LPeg patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Asbjørn Sloth Tønnesen --- README.rst | 24 +++++++++++ lua/inet/core.lua | 121 ++++++++++++++++++++++++++++++++++-------------------- lua/inet/init.lua | 2 + 3 files changed, 102 insertions(+), 45 deletions(-) diff --git a/README.rst b/README.rst index 207b1d9..b227b1a 100644 --- a/README.rst +++ b/README.rst @@ -76,6 +76,7 @@ API Description ``inet.is_set(foo)`` is ``set`` table? ``inet.set()`` get new empty ``set`` instance. ``inet.mixed_networks`` IPv6 mixed notation ``set`` +``inet.lpeg`` LPeg_ patterns ``inet.version`` API version (currently ``1``) ======================= ===================================================== @@ -188,6 +189,29 @@ There is a multitude of different ways to create ``inet*`` instances. inet('192.0.2.0', 33) -- returns nil, 'invalid mask' inet('2001:db8::', 129) -- returns nil, 'invalid mask' +Usable in LPeg patterns +~~~~~~~~~~~~~~~~~~~~~~~ + +Internally ``inet`` uses LPeg_ to parse IP adresses, but the +LPeg patterns are also available for embedding into your +own LPeg patterns. + +:: + + local lpeg = require 'lpeg' + local P = lpeg.P + local http_host_v6 = P('[') * inet.lpeg.ipv6 * P(']') + local http_host = http_host_v6 + inet.lpeg.ipv4 + local my_http_uri = P('https://') * http_host * P('/') * -1 + + my_http_uri:match('https://192.0.2.0/') -- returns inet('192.0.2.0') + my_http_uri:match('https://[2001:db8::1]/') -- returns inet('2001:db8::1') + + inet.lpeg.ipv6:match('2001:db8::/64') -- returns inet('2001:db8::/64') + inet.lpeg.ipv4:match('192.0.2.0/24') -- returns inet('192.0.2.0/24') + inet.lpeg.ip:match('2001:db8::/64') -- returns inet('2001:db8::/64') + inet.lpeg.ip:match('192.0.2.0/24') -- returns inet('192.0.2.0/24') + Mangling -------- diff --git a/lua/inet/core.lua b/lua/inet/core.lua index 2b36543..a12a036 100644 --- a/lua/inet/core.lua +++ b/lua/inet/core.lua @@ -79,13 +79,42 @@ function inet:full_family() return format('ipv%d', self:family()) end +local function build_bip(o1, o2, o3, o4) + return lshift(o1, 24) + lshift(o2, 16) + lshift(o3, 8) + o4 +end + +local function make_inet4(bip, mask) + return setmetatable({ + bip = bip, + mask = mask or 32, + }, inet4) +end + +local function make_inet6(pcs, mask) + local r = setmetatable({ + pcs = pcs, + mask = mask or 128, + }, inet6) + + -- ensure that the result is balanced + if not r:is_balanced() then + r:balance() + return nil, tostring(r)..' unbalanced' + end + + return r +end + +local ipv4_peg local ipv4_parser +local ipv6_peg local ipv6_parser do local lpeg = require 'lpeg' local C, Ct = lpeg.C, lpeg.Ct local S, R = lpeg.S, lpeg.R local B, Cc = lpeg.B, lpeg.Cc + local Cmt = lpeg.Cmt local digit = R('09') @@ -105,7 +134,13 @@ do local mask12 = R('12') * digit local mask3 = S('3') * R('02') local netmask = S('/') * C(mask12 + mask3 + digit) - ipv4_parser = ipv4addr * (netmask + Cc()) * -1 + local ipv4_base = ipv4addr * (netmask + Cc()) / + function (o1, o2, o3, o4, mask) + local bip = build_bip(o1, o2, o3, o4) + return bip, tonumber(mask) + end + ipv4_parser = ipv4_base * -1 + ipv4_peg = ipv4_base / make_inet4 end do @@ -124,20 +159,43 @@ do local partial = (piece * (colpi^-6))^-1 * colcol * ((picol^-6)*(ipv4embed+piece))^-1 local netmask = S('/') * C((digit^-3)) / tonumber local pieces = full + partial - ipv6_parser = Ct(pieces) * ((netmask + Cc())^-1) * -1 - end -end + local function check_pcs(_, _, pcs, mask) + if #pcs > 8 then return false end + if mask ~= nil and mask > 128 then return false end + return true, pcs, mask + end -local function build_bip(o1, o2, o3, o4) - return lshift(o1, 24) + lshift(o2, 16) + lshift(o3, 8) + o4 + local function expand_pieces(pcs) + local zero_pieces = 8 - #pcs + for i=1,#pcs do + if pcs[i] == '::' then + pcs[i] = 0 + for j=#pcs,i,-1 do + pcs[j+zero_pieces] = pcs[j] + end + for j=1,zero_pieces do + pcs[i+j] = 0 + end + end + end + assert(#pcs == 8, 'incorrect number of pieces') + return pcs + end + + local ipv6_base = Cmt(Ct(pieces) * ((netmask + Cc())^-1), check_pcs) / + function (pcs, mask) + expand_pieces(pcs) + return pcs, mask + end + ipv6_parser = ipv6_base * -1 + ipv6_peg = ipv6_base / make_inet6 + end end local function inet4_from_string(ipstr) - local o1, o2, o3, o4, mask = ipv4_parser:match(ipstr) - if not o1 then return nil, 'parse error' end - - local bip = build_bip(o1, o2, o3, o4) - return bip, tonumber(mask) + local bip, mask = ipv4_parser:match(ipstr) + if not bip then return nil, 'parse error' end + return bip, mask end local function inet4_from_number(bip) @@ -166,23 +224,6 @@ local inet4_constructors = { local function inet6_from_string(ipstr) local pcs, netmask = ipv6_parser:match(ipstr) if not pcs then return nil, 'parse error' end - if #pcs > 8 then return nil, 'too many pieces' end - local zero_pieces = 8 - #pcs - for i=1,#pcs do - if pcs[i] == '::' then - pcs[i] = 0 - for j=#pcs,i,-1 do - pcs[j+zero_pieces] = pcs[j] - end - for j=1,zero_pieces do - pcs[i+j] = 0 - end - end - end - if #pcs > 8 then return nil, 'too many pieces' end - if netmask ~= nil and netmask > 128 then - return nil, 'invalid netmask' - end return pcs, netmask end @@ -239,28 +280,13 @@ end local function new_inet4(ip, mask) local bip, outmask = generic_new(inet4_constructors, 32, ip, mask) if not bip then return nil, outmask end - return setmetatable({ - bip = bip, - mask = outmask, - }, inet4) + return make_inet4(bip, outmask) end local function new_inet6(ip, mask) local pcs, outmask = generic_new(inet6_constructors, 128, ip, mask) if not pcs then return nil, outmask end - - local r = setmetatable({ - pcs = pcs, - mask = outmask, - }, inet6) - - -- ensure that the result is balanced - if not r:is_balanced() then - r:balance() - return nil, tostring(r)..' unbalanced' - end - - return r + return make_inet6(pcs, outmask) end local function new_inet(ip, mask) @@ -877,5 +903,10 @@ M.is_inet4 = is_inet4 M.is_inet6 = is_inet6 M.is_inet = is_inet M.new_inet = new_inet +M.lpeg = { + ipv4 = ipv4_peg, + ipv6 = ipv6_peg, + ip = ipv4_peg + ipv6_peg, +} return M diff --git a/lua/inet/init.lua b/lua/inet/init.lua index 56c0eed..229fbd8 100644 --- a/lua/inet/init.lua +++ b/lua/inet/init.lua @@ -22,6 +22,8 @@ M.is4 = core.is_inet4 M.is6 = core.is_inet6 M.is = core.is_inet +M.lpeg = core.lpeg + M.is_set = set.is_set M.set = set.new -- cgit v1.2.1