Modulo:IP/pagipadasan
Daytoy ket isu ti panid a pagipadasan ti modulo para iti Modulo:IP (dip). |
Inus-usar daytoy a modulo kadagiti mensahe ti sistema. Dagiti panagbaliw iti daytoy ket gapuanan dagiti dagus a panagbaliw iti interface ti agar-aramat iti Wikipedia. Tapno maliklikan ti nakaro a pannakagulo, nasken nga umuna a masubokan dagiti ania man a panagbaliw kadagiti subpanid ti /pagipadasan wenno /pangsubok ti modulo, wenno iti bukodmo a pagipadasan. Mabalinton a mainayon dagiti nasubokan a panagbaliw iti maysa a panagurnos iti daytoy a modulo. Pangngaasi a pakitungtungan dagiti ania man a panagbaliw iti panid ti tungtungan sakbay nga isayangkat. |
Ti Modulo:IP iket maysa a libraria para iti panagobra kadagiti adres ti IP ken dagiti subnet. Makatengngel daytoy iti IPv4 ken IPv6. Mangieksport ti libraria kadagiti uppat a klase, IPAddress, Subnet, IPv4Collection, ken IPv6Collection.
Panangikarga ti libraria
urnosenlocal IP = require('Modulo:IP')
local IPAddress = IP.IPAddress
local Subnet = IP.Subnet
IPAddress
urnosenTi klase ti IPAddress ket inus-usar iti panagobra kadagiti bugbugtong nga adres ti IP. Ti agpartuat iti maysa a baro a banag ti IPAddress:
local ipAddress = IPAddress.new(ipString)
Ti agdumaduma nga ipString ket mabalin a maysa nga umiso nga adres ti IPv4 wenno IPv6.
Dagiti pagarigan:
local ipv4Address = IPAddress.new('1.2.3.4')
local ipv6Address = IPAddress.new('2001:db8::ff00:12:3456')
Dagiti banag ti IPAddress ket mabalin nga iyasping kadagiti mainaig nga operador:
-- Ekualidad
IPAddress.new('1.2.3.4') == IPAddress.new('1.2.3.4') -- true
IPAddress.new('1.2.3.4') == IPAddress.new('1.2.3.5') -- false
-- Basbassit ngem / ad-adu ngem
IPAddress.new('1.2.3.4') < IPAddress.new('1.2.3.5') -- true
IPAddress.new('1.2.3.4') > IPAddress.new('1.2.3.5') -- false
IPAddress.new('1.2.3.4') <= IPAddress.new('1.2.3.5') -- true
IPAddress.new('1.2.3.4') <= IPAddress.new('1.2.3.4') -- true
Mabalinmo nga usaren kaniada ti tostring (daytoy ket kapada ti panagusar ti getIP):
tostring(IPAddress.new('1.2.3.4')) -- "1.2.3.4"
tostring(IPAddress.new('2001:db8::ff00:12:3456')) -- "2001:db8::ff00:12:3456"
-- Naipangyababba dagiti napadakkel nga adres ti IPv6:
tostring(IPAddress.new('2001:db8:0:0:0:0:0:0')) -- "2001:db8::"
Mabalinmo pay nga i-concatenate kaniada:
IPAddress.new('1.2.3.4') .. ' foo' -- "1.2.3.4 foo"
IPAddress.new('1.2.3.4') .. IPAddress.new('5.6.7.8') -- "1.2.3.45.6.7.8"
Dagiti banag ti IPAddress ket addaan kadagiti nadumaduma a pamay-an, a naipalawag dita baba.
getIP
urnosenipAddress:getIP()
Mangisubli ti maysa a kuerdas a representasion ti adres ti IP. No mabalin maipangyababa dagiti adres ti IPv6.
Dagiti pagarigan:
IPAddress.new('1.2.3.4'):getIP() -- "1.2.3.4"
IPAddress.new('2001:db8::ff00:12:3456'):getIP() -- "2001:db8::ff00:12:3456"
IPAddress.new('2001:db8:0:0:0:0:0:0'):getIP() -- "2001:db8::"
getVersion
urnosenipAddress:getVersion()
Mangisubli ti bersion iti naus-usar a protokol ti IP. Daytoy ket "IPv4" para kadagiti adres ti IPv4, ken "IPv6" para kadagiti adres ti IPv6.
Dagiti pagarigan:
IPAddress.new('1.2.3.4'):getVersion() -- "IPv4"
IPAddress.new('2001:db8::ff00:12:3456'):getVersion() -- "IPv6"
isIPv4
urnosenipAddress:isIPv4()
Mangisubli ti "true" no ti adres ti IP ket maysa nga adres ti IPv4, ken "false" no saan.
Dagiti pagarigan:
IPAddress.new('1.2.3.4'):isIPv4() -- true
IPAddress.new('2001:db8::ff00:12:3456'):isIPv4() -- false
isIPv6
urnosenipAddress:isIPv6()
Mangisubli ti "true" no ti adres ti IP ket maysa nga adres ti IPv6, ken "false" no saan.
Dagiti pagarigan:
IPAddress.new('1.2.3.4'):isIPv6() -- false
IPAddress.new('2001:db8::ff00:12:3456'):isIPv6() -- true
isInSubnet
urnosenipAddress:isInSubnet(subnet)
Mangisubli ti "true" no ti adres ti IP ket adda iti subnet subnet, ken "false" no saan. subnet ken mabalin a maysa a banag ti Subnet wenno maysa a kuerdas ti CIDR].
Dagiti pagarigan:
IPAddress.new('1.2.3.4'):isInSubnet('1.2.3.0/24') -- true
IPAddress.new('1.2.3.4'):isInSubnet('1.2.4.0/24') -- false
IPAddress.new('1.2.3.4'):isInSubnet(Subnet.new('1.2.3.0/24')) -- true
IPAddress.new('2001:db8::ff00:12:3456'):isInSubnet('2001:db8::ff00:12:0/112') -- true
getSubnet
urnosenipAddress:getSubnet(bitLength)
Mangisubli ti mays a banag ti subnet para iti subnet iti maysa a kaatiddog ti bit iti bitLength nga aglaon iti agdama nga IP. Ti parametro ti bitLength ket nasken a maysa nga integer iti pagbaetan ti 0 ken 32 para kadagiti afres ti IPv4, wenno maysa nga integer iti pagbaetaan ti 0 ken 128 para kadagiti adres ti IPv6.
Dagiti pagarigan:
IPAddress.new('1.2.3.4'):getSubnet(24) -- Kapada iti Subnet.new('1.2.3.0/24')
getNextIP
urnosenipAddress:getNextIP()
Mangisubli iti baro a banag ti IPAddress a maipada iti agdama nga adres ti IP a maingkremento babaen ti maysa. Ti adres ti IPv4 ti "255.255.255.255" ket maipan iti agarup a "0.0.0.0", ken ti adres ti IPv6 iti "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" ket maipan iti agarup a "::".
Dagiti pagarigan:
IPAddress.new('1.2.3.4'):getNextIP() -- Kapada iti IPAddress.new('1.2.3.5')
IPAddress.new('2001:db8::ff00:12:3456'):getNextIP() -- Kapada iti IPAddress.new('2001:db8::ff00:12:3457')
IPAddress.new('255.255.255.255'):getNextIP() -- Kapada iti IPAddress.new('0.0.0.0')
getPreviousIP
urnosenipAddress:getPreviousIP()
Mangisubli iti baro a banag ti IPAddress a maipada iti agdama nga adres ti IP a maingkremento babaen ti maysa. Ti adres ti IPv4 iti "0.0.0.0" ket maipan iti agarup a "255.255.255.255", ken ti adres ti IPv6 iti "::" ket maipan iti agarup a "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff".
Dagiti pagarigan:
IPAddress.new('1.2.3.4'):getPreviousIP() -- Kapada iti IPAddress.new('1.2.3.3')
IPAddress.new('2001:db8::ff00:12:3456'):getPreviousIP() -- Kapada iti IPAddress.new('2001:db8::ff00:12:3455')
IPAddress.new('0.0.0.0'):getPreviousIP() -- Kapada iti IPAddress.new('255.255.255.255')
Subnet
urnosenTi klase ti subnet ket inus-usar iti panagobra kadagiti subnetwork dagiti adres ti IPv4 wenno IPv6. Ti agpartuat iti baro a banag ti Subnet:
local subnet = Subnet.new(cidrString)
Ti cidrString ket nasken nga umiso nga IPv4 wenno kuerdas ti IPv6 CIDR.
Dagiti banag ti subnet ket mabalinda nga iyasping para iti ekualidad:
Subnet.new('1.2.3.0/24') == Subnet.new('1.2.3.0/24') -- true
Subnet.new('1.2.3.0/24') == Subnet.new('1.2.3.0/25') -- false
Subnet.new('1.2.3.0/24') == Subnet.new('2001:db8::ff00:12:0/112') -- false
Subnet.new('2001:db8::ff00:12:0/112') == Subnet.new('2001:db8::ff00:12:0/112') -- true
Subnet.new('2001:db8:0:0:0:0:0:0/112') == Subnet.new('2001:db8::/112') -- true
Mabalinmo nga usaren kaniada ti tostring (daytoy ket kapada ti getCIDR):
tostring(Subnet.new('1.2.3.0/24')) -- "1.2.3.0/24"
tostring(Subnet.new('2001:db8::ff00:12:0/112')) -- "2001:db8::ff00:12:0/112"
tostring(Subnet.new('2001:db8:0:0:0:0:0:0/112')) -- "2001:db8::/112"
Mabalinmo pay nga i-concatenate kaniada:
Subnet.new('1.2.3.0/24') .. ' foo' -- "1.2.3.0/24 foo"
Subnet.new('1.2.3.0/24') .. Subnet.new('4.5.6.0/24') -- "1.2.3.0/244.5.6.0/24"
Dagiti banag ti subnet ket addaan kadagiti nadumaduma a pamay-an, a naipalawag dita baba.
getPrefix
urnosensubnet:getPrefix()
Mangisubli ti maysa a banag ti IPAddress para iti kababaan nga adres ti IP iti subnet.
Dagiti pagarigan:
Subnet.new('1.2.3.0/24'):getPrefix() -- Kapada iti IPAddress.new('1.2.3.0')
Subnet.new('2001:db8::ff00:12:0/112'):getPrefix() -- Kapada iti IPAddress.new('2001:db8::ff00:12:0')
getHighestIP
urnosensubnet:getHighestIP()
Mangisubli ti maysa a banag ti IPAddress para iti kangatuan nga adres ti IP iti subnet.
Dagiti pagarigan:
Subnet.new('1.2.3.0/24'):getHighestIP() -- Kapada iti IPAddress.new('1.2.3.255')
Subnet.new('2001:db8::ff00:12:0/112'):getHighestIP() -- Kapada iti IPAddress.new('2001:db8::ff00:12:ffff')
getBitLength
urnosensubnet:getBitLength()
Mnagisubli ti kaatiddog ti bit iti subnet. Daytoy ket maysa nga integer iti pagbaetan ti 0 ken 32 para kadagiti adres ti IPv4, wenno maysa nga integer iti pagbaetan ti 0 ken 128 para kadagiti adres ti IPv6.
Dagiti pagarigan:
Subnet.new('1.2.3.0/24'):getBitLength() -- 24
Subnet.new('2001:db8::ff00:12:0/112'):getBitLength() -- 112
getCIDR
urnosensubnet:getCIDR()
Mangisubli ti maysa a kuerdas ti representasion ti CIDR iti subnet.
Dagiti pagarigan:
Subnet.new('1.2.3.0/24'):getCIDR() -- "1.2.3.0/24"
Subnet.new('2001:db8::ff00:12:0/112'):getCIDR() -- "2001:db8::ff00:12:0/112"
Subnet.new('2001:db8:0:0:0:0:0:0/112'):getCIDR() -- "2001:db8::/112"
getVersion
urnosensubnet:getVersion()
Mangisubli ti bersion ti inus-usar a protokol ti IP. Daytoy ket ti "IPv4" para kadagiti adres ti IPv4s, ken "IPv6" para kadagiti adres ti IPv6.
Dagiti pagarigan:
Subnet.new('1.2.3.0/24'):getVersion() -- "IPv4"
Subnet.new('2001:db8::ff00:12:0/112'):getVersion() -- "IPv6"
isIPv4
urnosensubnet:isIPv4()
Mangisubli ti "true" no ti subnet ket agus-usar iti IPv4, ken "false" no saan.
Dagiti pagarigan:
Subnet.new('1.2.3.0/24'):isIPv4() -- true
Subnet.new('2001:db8::ff00:12:0/112'):isIPv4() -- false
isIPv6
urnosensubnet:isIPv6()
Mangisubli ti "true" no ti subnet ket agus-usar iti IPv6, ken "false" no saan.
Dagiti pagarigan:
Subnet.new('1.2.3.0/24'):isIPv6() -- false
Subnet.new('2001:db8::ff00:12:0/112'):isIPv6() -- true
containsIP
urnosensubnet:containsIP(ip)
Returns "true" no ti subnet ket aglaon ti adres ti IP ip, ken "false" no saan. ti ip ket mabalin a maysa a kuerdas ti adres ti IP, wenno maysa a banag ti IPAddress.
Dagiti pagarigan:
Subnet.new('1.2.3.0/24'):containsIP('1.2.3.4') -- true
Subnet.new('1.2.3.0/24'):containsIP('1.2.4.4') -- false
Subnet.new('1.2.3.0/24'):containsIP(IPAddress.new('1.2.3.4')) -- true
Subnet.new('2001:db8::ff00:12:0/112'):containsIP('2001:db8::ff00:12:3456') -- true
overlapsSubnet
urnosensubnet:overlapsSubnet(subnet)
Mangisubli ti "true" no ti agdama a subnet ket tuonanna iti subnet, ken "false" no saan. Ti subnet ket mabalin a kuerdas ti CIDR wenno maysa a banag ti subnet.
Dagiti pagarigan:
Subnet.new('1.2.3.0/24'):overlapsSubnet('1.2.0.0/16') -- true
Subnet.new('1.2.3.0/24'):overlapsSubnet('1.2.12.0/22') -- false
Subnet.new('1.2.3.0/24'):overlapsSubnet(Subnet.new('1.2.0.0/16')) -- true
Subnet.new('2001:db8::ff00:12:0/112'):overlapsSubnet('2001:db8::ff00:0:0/96') -- true
walk
urnosensubnet:walk()
Ti pamay-an ti "walk" ket iteradona kadagiti amin a banag ti IPAddress iti subnet.
Dagiti pagarigan:
for ipAddress in Subnet.new('192.168.0.0/30'):walk() do
mw.log(tostring(ipAddress))
end
-- 192.168.0.0
-- 192.168.0.1
-- 192.168.0.2
-- 192.168.0.3
IPv4Collection
urnosenTi klase ti IPv4Collection ke inus-usar iti panagobra kadagiti nadumaduma nga adres ti IPv4 ken dagiti subnet ti IPv4. Ti agpartuat iti baro a banag ti IPv4Collection:
local collection = IPv4Collection.new()
Adda dagiti nadumaduma a pamay-an ti IPv4Collection, kas ti naipalawag dita baba.
getVersion
urnosencollection:getVersion()
Mangisubli ti kuerdas ti "IPv4".
addIP
urnosencollection:addIP(ip)
Manginayon ti maysa nga IP iti koleksion. Ti IP ket mabalin a maysa a kuerdas wenno maysa a banag ti IPAddress.
Dagiti pagarigan:
collection:addIP('1.2.3.4')
collection:addIP(IPAddress.new('1.2.3.4'))
Mabalin a pagsisilpuen daytoy a pamay-an:
collection:addIP('1.2.3.4'):addIP('5.6.7.8')
addSubnet
urnosencollection:addSubnet(subnet)
Manginayon ti maysa a subnet iti koleksion. Ti subnet ket mabalin a maysa a kuerdas ti CIDR wenno maysa a banag ti Subnet.
Dagiti pagarigan:
collection:addSubnet('1.2.3.0/24')
collection:addSubnet(Subnet.new('1.2.3.0/24'))
Mabalin a pagsisilpuen daytoy a pamay-an:
collection:addSubnet('1.2.0.0/24'):addSubnet('1.2.1.0/24')
addFromString
urnosencollection:addFromString(str)
Mangiyaon kadagiti ania man nga adres ti IPv4 ken dagiti subnet ti IPv4 CIDR manipud iti str ken inayon dagitoy iti koleksion. Saan a maikaskaso ti ania man a teksto a saan nga adres ti IPv4 wenno subnet ti CIDR .
Dagiti pagarigan:
collection:addFromString('Add some IPs and subnets: 1.2.3.4 1.2.3.5 2001:0::f foo 1.2.4.0/24')
Mabalin a pagsisilpuen daytoy a pamay-an:
collection:addFromString('foo 1.2.3.4'):addFromString('bar 5.6.7.8')
containsIP
urnosencollection:containsIP(ip)
Mangisubli ti "true" no ti koleksion ket aglaon ti nainaganan nga IP; no saan mangisubli ti "false". Ti parametro ti ip ket mabalin a maysa a kuerdas wenno maysa a banag ti IPAddress.
Dagiti pagarigan:
collection:containsIP('1.2.3.4')
collection:containsIP(IPAddress.new('1.2.3.4'))
getRanges
urnosencollection:getRanges()
Mangisubli ti maysa nga urnos ti rimpuok dagiti paris ti adres ti IP a kapada iti koleksion. Ti tunggal maysa a paris ti IP ket maysa a rimpuok a mangipresenta iti maysa nga agtutuloy a sakup dagiti adres ti IP manipud iti paris[1] aginggana iti paris[2] nga ingklusibo. Ti paris[1] ken paris[2] ket dagiti banag ti IPAddress.
Dagiti pagarigan:
collection:addSubnet('1.2.0.0/24')
collection:addSubnet('1.2.1.0/24')
collection:addSubnet('1.2.10.0/24')
mw.logObject(collection:getRanges())
-- Ilistana dagiti sumaganad:
-- table#1 {
-- table#2 {
-- 1.2.0.0,
-- 1.2.1.255,
-- },
-- table#3 {
-- 1.2.10.0,
-- 1.2.10.255,
-- },
-- }
overlapsSubnet
urnosencollection:overlapsSubnet(subnet)
Mangisubli ti "true", "obj" no ti subnet ket tuonanna daytoy a koleksion, a ti "obj" ket ti umuna nga IPAddress wenno banag ti Subnet a mangtuoon iti subnet. No saan, mangisubli ti "false". Ti subnet ket mabalin a maysa a kuerdas ti CIDR wenno maysa a banag ti Subnet.
Dagiti pagarigan:
collection:addIP('1.2.3.4')
collection:overlapsSubnet('1.2.3.0/24') -- true, IPAddress.new('1.2.3.4')
collection:overlapsSubnet('1.2.4.0/24') -- false
IPv6Collection
urnosenTi klase ti IPv6Collection ket inus-usar iti panagobra kadagiti nadumaduma a sabali nga adres ti IPv6 ken dagiti subnet ti IPv6. Dagiti banag ti IPv6Collection ket dagus nga analogo kadagiti banag ti IPv4Collection: aglaonda kadagiti isu met laeng a pamay-an ken agpadada met laeng nga agobra, ngem amin dagiti adres ti IP ken dagiti subnet a mainayon iti daytoy ket nasken nga IPv6, ken saan nga IPv4.
Ti agpartuat iti baro a banag ti IPv6Collection:
local collection = IPv6Collection.new()
-- IP library
-- This library contains classes for working with IP addresses and IP ranges.
-- Load modules
require('strict')
local bit32 = require('bit32')
local libraryUtil = require('libraryUtil')
local checkType = libraryUtil.checkType
local checkTypeMulti = libraryUtil.checkTypeMulti
local makeCheckSelfFunction = libraryUtil.makeCheckSelfFunction
-- Constants
local V4 = 'IPv4'
local V6 = 'IPv6'
--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------
local function makeValidationFunction(className, isObjectFunc)
-- Make a function for validating a specific object.
return function (methodName, argIdx, arg)
if not isObjectFunc(arg) then
error(string.format(
"bad argument #%d to '%s' (not a valid %s object)",
argIdx, methodName, className
), 3)
end
end
end
--------------------------------------------------------------------------------
-- Collection class
-- This is a table used to hold items.
--------------------------------------------------------------------------------
local Collection = {}
Collection.__index = Collection
function Collection:add(item)
if item ~= nil then
self.n = self.n + 1
self[self.n] = item
end
end
function Collection:join(sep)
return table.concat(self, sep)
end
function Collection:remove(pos)
if self.n > 0 and (pos == nil or (0 < pos and pos <= self.n)) then
self.n = self.n - 1
return table.remove(self, pos)
end
end
function Collection:sort(comp)
table.sort(self, comp)
end
function Collection:deobjectify()
-- Turns the collection into a plain array without any special properties
-- or methods.
self.n = nil
setmetatable(self, nil)
end
function Collection.new()
return setmetatable({n = 0}, Collection)
end
--------------------------------------------------------------------------------
-- RawIP class
-- Numeric representation of an IPv4 or IPv6 address. Used internally.
-- A RawIP object is constructed by adding data to a Collection object and
-- then giving it a new metatable. This is to avoid the memory overhead of
-- copying the data to a new table.
--------------------------------------------------------------------------------
local RawIP = {}
RawIP.__index = RawIP
-- Constructors
function RawIP.newFromIPv4(ipStr)
-- Return a RawIP object if ipStr is a valid IPv4 string. Otherwise,
-- return nil.
-- This representation is for compatibility with IPv6 addresses.
local octets = Collection.new()
local s = ipStr:match('^%s*(.-)%s*$') .. '.'
for item in s:gmatch('(.-)%.') do
octets:add(item)
end
if octets.n == 4 then
for i, s in ipairs(octets) do
if s:match('^%d+$') then
local num = tonumber(s)
if 0 <= num and num <= 255 then
if num > 0 and s:match('^0') then
-- A redundant leading zero is for an IP in octal.
return nil
end
octets[i] = num
else
return nil
end
else
return nil
end
end
local parts = Collection.new()
for i = 1, 3, 2 do
parts:add(octets[i] * 256 + octets[i+1])
end
return setmetatable(parts, RawIP)
end
return nil
end
function RawIP.newFromIPv6(ipStr)
-- Return a RawIP object if ipStr is a valid IPv6 string. Otherwise,
-- return nil.
ipStr = ipStr:match('^%s*(.-)%s*$')
local _, n = ipStr:gsub(':', ':')
if n < 7 then
ipStr = ipStr:gsub('::', string.rep(':', 9 - n))
end
local parts = Collection.new()
for item in (ipStr .. ':'):gmatch('(.-):') do
parts:add(item)
end
if parts.n == 8 then
for i, s in ipairs(parts) do
if s == '' then
parts[i] = 0
else
if s:match('^%x+$') then
local num = tonumber(s, 16)
if num and 0 <= num and num <= 65535 then
parts[i] = num
else
return nil
end
else
return nil
end
end
end
return setmetatable(parts, RawIP)
end
return nil
end
function RawIP.newFromIP(ipStr)
-- Return a new RawIP object from either an IPv4 string or an IPv6
-- string. If ipStr is not a valid IPv4 or IPv6 string, then return
-- nil.
return RawIP.newFromIPv4(ipStr) or RawIP.newFromIPv6(ipStr)
end
-- Methods
function RawIP:getVersion()
-- Return a string with the version of the IP protocol we are using.
return self.n == 2 and V4 or V6
end
function RawIP:isIPv4()
-- Return true if this is an IPv4 representation, and false otherwise.
return self.n == 2
end
function RawIP:isIPv6()
-- Return true if this is an IPv6 representation, and false otherwise.
return self.n == 8
end
function RawIP:getBitLength()
-- Return the bit length of the IP address.
return self.n * 16
end
function RawIP:getAdjacent(previous)
-- Return a RawIP object for an adjacent IP address. If previous is true
-- then the previous IP is returned; otherwise the next IP is returned.
-- Will wraparound:
-- next 255.255.255.255 → 0.0.0.0
-- ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff → ::
-- previous 0.0.0.0 → 255.255.255.255
-- :: → ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff
local result = Collection.new()
result.n = self.n
local carry = previous and 0xffff or 1
for i = self.n, 1, -1 do
local sum = self[i] + carry
if sum >= 0x10000 then
carry = previous and 0x10000 or 1
sum = sum - 0x10000
else
carry = previous and 0xffff or 0
end
result[i] = sum
end
return setmetatable(result, RawIP)
end
function RawIP:getPrefix(bitLength)
-- Return a RawIP object for the prefix of the current IP Address with a
-- bit length of bitLength.
local result = Collection.new()
result.n = self.n
for i = 1, self.n do
if bitLength > 0 then
if bitLength >= 16 then
result[i] = self[i]
bitLength = bitLength - 16
else
result[i] = bit32.replace(self[i], 0, 0, 16 - bitLength)
bitLength = 0
end
else
result[i] = 0
end
end
return setmetatable(result, RawIP)
end
function RawIP:getHighestHost(bitLength)
-- Return a RawIP object for the highest IP with the prefix of length
-- bitLength. In other words, the network (the most-significant bits)
-- is the same as the current IP's, but the host bits (the
-- least-significant bits) are all set to 1.
local bits = self.n * 16
local width
if bitLength <= 0 then
width = bits
elseif bitLength >= bits then
width = 0
else
width = bits - bitLength
end
local result = Collection.new()
result.n = self.n
for i = self.n, 1, -1 do
if width > 0 then
if width >= 16 then
result[i] = 0xffff
width = width - 16
else
result[i] = bit32.replace(self[i], 0xffff, 0, width)
width = 0
end
else
result[i] = self[i]
end
end
return setmetatable(result, RawIP)
end
function RawIP:_makeIPv6String()
-- Return an IPv6 string representation of the object. Behavior is
-- undefined if the current object is IPv4.
local z1, z2 -- indices of run of zeroes to be displayed as "::"
local zstart, zcount
for i = 1, 9 do
-- Find left-most occurrence of longest run of two or more zeroes.
if i < 9 and self[i] == 0 then
if zstart then
zcount = zcount + 1
else
zstart = i
zcount = 1
end
else
if zcount and zcount > 1 then
if not z1 or zcount > z2 - z1 + 1 then
z1 = zstart
z2 = zstart + zcount - 1
end
end
zstart = nil
zcount = nil
end
end
local parts = Collection.new()
for i = 1, 8 do
if z1 and z1 <= i and i <= z2 then
if i == z1 then
if z1 == 1 or z2 == 8 then
if z1 == 1 and z2 == 8 then
return '::'
end
parts:add(':')
else
parts:add('')
end
end
else
parts:add(string.format('%x', self[i]))
end
end
return parts:join(':')
end
function RawIP:_makeIPv4String()
-- Return an IPv4 string representation of the object. Behavior is
-- undefined if the current object is IPv6.
local parts = Collection.new()
for i = 1, 2 do
local w = self[i]
parts:add(math.floor(w / 256))
parts:add(w % 256)
end
return parts:join('.')
end
function RawIP:__tostring()
-- Return a string equivalent to given IP address (IPv4 or IPv6).
if self.n == 2 then
return self:_makeIPv4String()
else
return self:_makeIPv6String()
end
end
function RawIP:__lt(obj)
if self.n == obj.n then
for i = 1, self.n do
if self[i] ~= obj[i] then
return self[i] < obj[i]
end
end
return false
end
return self.n < obj.n
end
function RawIP:__eq(obj)
if self.n == obj.n then
for i = 1, self.n do
if self[i] ~= obj[i] then
return false
end
end
return true
end
return false
end
--------------------------------------------------------------------------------
-- Initialize private methods available to IPAddress and Subnet
--------------------------------------------------------------------------------
-- Both IPAddress and Subnet need access to each others' private constructor
-- functions. IPAddress must be able to make Subnet objects from CIDR strings
-- and from RawIP objects, and Subnet must be able to make IPAddress objects
-- from IP strings and from RawIP objects. These constructors must all be
-- private to ensure correct error levels and to stop other modules from having
-- to worry about RawIP objects. Because they are private, they must be
-- initialized here.
local makeIPAddress, makeIPAddressFromRaw, makeSubnet, makeSubnetFromRaw
-- Objects need to be able to validate other objects that they are passed
-- as input, so initialize those functions here as well.
local validateCollection, validateIPAddress, validateSubnet
--------------------------------------------------------------------------------
-- IPAddress class
-- Represents a single IPv4 or IPv6 address.
--------------------------------------------------------------------------------
local IPAddress = {}
do
-- dataKey is a unique key to access objects' internal data. This is needed
-- to access the RawIP objects contained in other IPAddress objects so that
-- they can be compared with the current object's RawIP object. This data
-- is not available to other classes or other modules.
local dataKey = {}
-- Private static methods
local function isIPAddressObject(val)
return type(val) == 'table' and val[dataKey] ~= nil
end
validateIPAddress = makeValidationFunction('IPAddress', isIPAddressObject)
-- Metamethods that don't need upvalues
local function ipEquals(ip1, ip2)
return ip1[dataKey].rawIP == ip2[dataKey].rawIP
end
local function ipLessThan(ip1, ip2)
return ip1[dataKey].rawIP < ip2[dataKey].rawIP
end
local function concatIP(ip, val)
return tostring(ip) .. tostring(val)
end
local function ipToString(ip)
return ip:getIP()
end
-- Constructors
makeIPAddressFromRaw = function (rawIP)
-- Constructs a new IPAddress object from a rawIP object. This function
-- is for internal use; it is called by IPAddress.new and from other
-- IPAddress methods, and should be available to the Subnet class, but
-- should not be available to other modules.
assert(type(rawIP) == 'table', 'rawIP was type ' .. type(rawIP) .. '; expected type table')
-- Set up structure
local obj = {}
local data = {}
data.rawIP = rawIP
-- A function to check whether methods are called with a valid self
-- parameter.
local checkSelf = makeCheckSelfFunction(
'IP',
'ipAddress',
obj,
'IPAddress object'
)
-- Public methods
function obj:getIP()
checkSelf(self, 'getIP')
return tostring(data.rawIP)
end
function obj:getVersion()
checkSelf(self, 'getVersion')
return data.rawIP:getVersion()
end
function obj:isIPv4()
checkSelf(self, 'isIPv4')
return data.rawIP:isIPv4()
end
function obj:isIPv6()
checkSelf(self, 'isIPv6')
return data.rawIP:isIPv6()
end
function obj:isInCollection(collection)
checkSelf(self, 'isInCollection')
validateCollection('isInCollection', 1, collection)
return collection:containsIP(self)
end
function obj:isInSubnet(subnet)
checkSelf(self, 'isInSubnet')
local tp = type(subnet)
if tp == 'string' then
subnet = makeSubnet(subnet)
elseif tp == 'table' then
validateSubnet('isInSubnet', 1, subnet)
else
checkTypeMulti('isInSubnet', 1, subnet, {'string', 'table'})
end
return subnet:containsIP(self)
end
function obj:getSubnet(bitLength)
checkSelf(self, 'getSubnet')
checkType('getSubnet', 1, bitLength, 'number')
if bitLength < 0
or bitLength > data.rawIP:getBitLength()
or bitLength ~= math.floor(bitLength)
then
error(string.format(
"bad argument #1 to 'getSubnet' (must be an integer between 0 and %d)",
data.rawIP:getBitLength()
), 2)
end
return makeSubnetFromRaw(data.rawIP, bitLength)
end
function obj:getNextIP()
checkSelf(self, 'getNextIP')
return makeIPAddressFromRaw(data.rawIP:getAdjacent())
end
function obj:getPreviousIP()
checkSelf(self, 'getPreviousIP')
return makeIPAddressFromRaw(data.rawIP:getAdjacent(true))
end
-- Metamethods
return setmetatable(obj, {
__eq = ipEquals,
__lt = ipLessThan,
__concat = concatIP,
__tostring = ipToString,
__index = function (self, key)
-- If any code knows the unique data key, allow it to access
-- the data table.
if key == dataKey then
return data
end
end,
__metatable = false, -- don't allow access to the metatable
})
end
makeIPAddress = function (ip)
local rawIP = RawIP.newFromIP(ip)
if not rawIP then
error(string.format("'%s' is an invalid IP address", ip), 3)
end
return makeIPAddressFromRaw(rawIP)
end
function IPAddress.new(ip)
checkType('IPAddress.new', 1, ip, 'string')
return makeIPAddress(ip)
end
end
--------------------------------------------------------------------------------
-- Subnet class
-- Represents a block of IPv4 or IPv6 addresses.
--------------------------------------------------------------------------------
local Subnet = {}
do
-- uniqueKey is a unique, private key used to test whether a given object
-- is a Subnet object.
local uniqueKey = {}
-- Metatable
local mt = {
__index = function (self, key)
if key == uniqueKey then
return true
end
end,
__eq = function (self, obj)
return self:getCIDR() == obj:getCIDR()
end,
__concat = function (self, obj)
return tostring(self) .. tostring(obj)
end,
__tostring = function (self)
return self:getCIDR()
end,
__metatable = false
}
-- Private static methods
local function isSubnetObject(val)
-- Return true if val is a Subnet object, and false otherwise.
return type(val) == 'table' and val[uniqueKey] ~= nil
end
-- Function to validate subnet objects.
-- Params:
-- methodName (string) - the name of the method being validated
-- argIdx (number) - the position of the argument in the argument list
-- arg - the argument to be validated
validateSubnet = makeValidationFunction('Subnet', isSubnetObject)
-- Constructors
makeSubnetFromRaw = function (rawIP, bitLength)
-- Set up structure
local obj = setmetatable({}, mt)
local data = {
rawIP = rawIP,
bitLength = bitLength,
}
-- A function to check whether methods are called with a valid self
-- parameter.
local checkSelf = makeCheckSelfFunction(
'IP',
'subnet',
obj,
'Subnet object'
)
-- Public methods
function obj:getPrefix()
checkSelf(self, 'getPrefix')
if not data.prefix then
data.prefix = makeIPAddressFromRaw(
data.rawIP:getPrefix(data.bitLength)
)
end
return data.prefix
end
function obj:getHighestIP()
checkSelf(self, 'getHighestIP')
if not data.highestIP then
data.highestIP = makeIPAddressFromRaw(
data.rawIP:getHighestHost(data.bitLength)
)
end
return data.highestIP
end
function obj:getBitLength()
checkSelf(self, 'getBitLength')
return data.bitLength
end
function obj:getCIDR()
checkSelf(self, 'getCIDR')
return string.format(
'%s/%d',
tostring(self:getPrefix()), self:getBitLength()
)
end
function obj:getVersion()
checkSelf(self, 'getVersion')
return data.rawIP:getVersion()
end
function obj:isIPv4()
checkSelf(self, 'isIPv4')
return data.rawIP:isIPv4()
end
function obj:isIPv6()
checkSelf(self, 'isIPv6')
return data.rawIP:isIPv6()
end
function obj:containsIP(ip)
checkSelf(self, 'containsIP')
local tp = type(ip)
if tp == 'string' then
ip = makeIPAddress(ip)
elseif tp == 'table' then
validateIPAddress('containsIP', 1, ip)
else
checkTypeMulti('containsIP', 1, ip, {'string', 'table'})
end
if self:getVersion() == ip:getVersion() then
return self:getPrefix() <= ip and ip <= self:getHighestIP()
end
return false
end
function obj:overlapsCollection(collection)
checkSelf(self, 'overlapsCollection')
validateCollection('overlapsCollection', 1, collection)
return collection:overlapsSubnet(self)
end
function obj:overlapsSubnet(subnet)
checkSelf(self, 'overlapsSubnet')
local tp = type(subnet)
if tp == 'string' then
subnet = makeSubnet(subnet)
elseif tp == 'table' then
validateSubnet('overlapsSubnet', 1, subnet)
else
checkTypeMulti('overlapsSubnet', 1, subnet, {'string', 'table'})
end
if self:getVersion() == subnet:getVersion() then
return (
subnet:getHighestIP() >= self:getPrefix() and
subnet:getPrefix() <= self:getHighestIP()
)
end
return false
end
function obj:walk()
checkSelf(self, 'walk')
local started
local current = self:getPrefix()
local highest = self:getHighestIP()
return function ()
if not started then
started = true
return current
end
if current < highest then
current = current:getNextIP()
return current
end
end
end
return obj
end
makeSubnet = function (cidr)
-- Return a Subnet object from a CIDR string. If the CIDR string is
-- invalid, throw an error.
local lhs, rhs = cidr:match('^%s*(.-)/(%d+)%s*$')
if lhs then
local bits = lhs:find(':', 1, true) and 128 or 32
local n = tonumber(rhs)
if n and n <= bits and (n == 0 or not rhs:find('^0')) then
-- The right-hand side is a number between 0 and 32 (for IPv4)
-- or 0 and 128 (for IPv6) and doesn't have any leading zeroes.
local base = RawIP.newFromIP(lhs)
if base then
-- The left-hand side is a valid IP address.
local prefix = base:getPrefix(n)
if base == prefix then
-- The left-hand side is the lowest IP in the subnet.
return makeSubnetFromRaw(prefix, n)
end
end
end
end
error(string.format("'%s' is an invalid CIDR string", cidr), 3)
end
function Subnet.new(cidr)
checkType('Subnet.new', 1, cidr, 'string')
return makeSubnet(cidr)
end
end
--------------------------------------------------------------------------------
-- Ranges class
-- Holds a list of IPAdress pairs representing contiguous IP ranges.
--------------------------------------------------------------------------------
local Ranges = Collection.new()
Ranges.__index = Ranges
function Ranges.new()
return setmetatable({}, Ranges)
end
function Ranges:add(ip1, ip2)
validateIPAddress('add', 1, ip1)
if ip2 ~= nil then
validateIPAddress('add', 2, ip2)
if ip1 > ip2 then
error('The first IP must be less than or equal to the second', 2)
end
end
Collection.add(self, {ip1, ip2 or ip1})
end
function Ranges:merge()
self:sort(
function (lhs, rhs)
-- Sort by second value, then first.
if lhs[2] == rhs[2] then
return lhs[1] < rhs[1]
end
return lhs[2] < rhs[2]
end
)
local pos = self.n
while pos > 1 do
for i = pos - 1, 1, -1 do
local ip1 = self[i][2]
local ip2 = ip1:getNextIP()
if ip2 < ip1 then
ip2 = ip1 -- don't wrap around
end
if self[pos][1] > ip2 then
break
end
ip1 = self[i][1]
ip2 = self[pos][1]
self[i] = {ip1 > ip2 and ip2 or ip1, self[pos][2]}
self:remove(pos)
pos = pos - 1
if pos <= 1 then
break
end
end
pos = pos - 1
end
end
--------------------------------------------------------------------------------
-- IPCollection class
-- Holds a list of IP addresses/subnets. Used internally.
-- Each address/subnet has the same version (either IPv4 or IPv6).
--------------------------------------------------------------------------------
local IPCollection = {}
IPCollection.__index = IPCollection
function IPCollection.new(version)
assert(
version == V4 or version == V6,
'IPCollection.new called with an invalid version'
)
local obj = {
version = version, -- V4 or V6
addresses = Collection.new(), -- valid IP addresses
subnets = Collection.new(), -- valid subnets
omitted = Collection.new(), -- not-quite valid strings
}
return obj
end
function IPCollection:getVersion()
-- Return a string with the IP version of addresses in this collection.
return self.version
end
function IPCollection:_store(hit, stripColons)
local maker, location
if hit:find('/', 1, true) then
maker = Subnet.new
location = self.subnets
else
maker = IPAddress.new
location = self.addresses
end
local success, obj = pcall(maker, hit)
if success then
location:add(obj)
else
if stripColons then
local colons, hit = hit:match('^(:*)(.*)')
if colons ~= '' then
self:_store(hit)
return
end
end
self.omitted:add(hit)
end
end
function IPCollection:_assertVersion(version, msg)
if self.version ~= version then
error(msg, 3)
end
end
function IPCollection:addIP(ip)
local tp = type(ip)
if tp == 'string' then
ip = makeIPAddress(ip)
elseif tp == 'table' then
validateIPAddress('addIP', 1, ip)
else
checkTypeMulti('addIP', 1, ip, {'string', 'table'})
end
self:_assertVersion(ip:getVersion(), 'addIP called with incorrect IP version')
self.addresses:add(ip)
return self
end
function IPCollection:addSubnet(subnet)
local tp = type(subnet)
if tp == 'string' then
subnet = makeSubnet(subnet)
elseif tp == 'table' then
validateSubnet('addSubnet', 1, subnet)
else
checkTypeMulti('addSubnet', 1, subnet, {'string', 'table'})
end
self:_assertVersion(subnet:getVersion(), 'addSubnet called with incorrect subnet version')
self.subnets:add(subnet)
return self
end
function IPCollection:containsIP(ip)
-- Return true, obj if ip is in this collection,
-- where obj is the first IPAddress or Subnet with the ip.
-- Otherwise, return false.
local tp = type(ip)
if tp == 'string' then
ip = makeIPAddress(ip)
elseif tp == 'table' then
validateIPAddress('containsIP', 1, ip)
else
checkTypeMulti('containsIP', 1, ip, {'string', 'table'})
end
if self:getVersion() == ip:getVersion() then
for _, item in ipairs(self.addresses) do
if item == ip then
return true, item
end
end
for _, item in ipairs(self.subnets) do
if item:containsIP(ip) then
return true, item
end
end
end
return false
end
function IPCollection:getRanges()
-- Return a sorted table of IP pairs equivalent to the collection.
-- Each IP pair is a table representing a contiguous range of
-- IP addresses from pair[1] to pair[2] inclusive (IPAddress objects).
local ranges = Ranges.new()
for _, item in ipairs(self.addresses) do
ranges:add(item)
end
for _, item in ipairs(self.subnets) do
ranges:add(item:getPrefix(), item:getHighestIP())
end
ranges:merge()
ranges:deobjectify()
return ranges
end
function IPCollection:overlapsSubnet(subnet)
-- Return true, obj if subnet overlaps this collection,
-- where obj is the first IPAddress or Subnet overlapping the subnet.
-- Otherwise, return false.
local tp = type(subnet)
if tp == 'string' then
subnet = makeSubnet(subnet)
elseif tp == 'table' then
validateSubnet('overlapsSubnet', 1, subnet)
else
checkTypeMulti('overlapsSubnet', 1, subnet, {'string', 'table'})
end
if self:getVersion() == subnet:getVersion() then
for _, item in ipairs(self.addresses) do
if subnet:containsIP(item) then
return true, item
end
end
for _, item in ipairs(self.subnets) do
if subnet:overlapsSubnet(item) then
return true, item
end
end
end
return false
end
--------------------------------------------------------------------------------
-- IPv4Collection class
-- Holds a list of IPv4 addresses/subnets.
--------------------------------------------------------------------------------
local IPv4Collection = setmetatable({}, IPCollection)
IPv4Collection.__index = IPv4Collection
function IPv4Collection.new()
return setmetatable(IPCollection.new(V4), IPv4Collection)
end
function IPv4Collection:addFromString(text)
-- Extract any IPv4 addresses or CIDR subnets from given text.
checkType('addFromString', 1, text, 'string')
text = text:gsub('[:!"#&\'()+,%-;<=>?[%]_{|}]', ' ')
for hit in text:gmatch('%S+') do
if hit:match('^%d+%.%d+[%.%d/]+$') then
local _, n = hit:gsub('%.', '.')
if n >= 3 then
self:_store(hit)
end
end
end
return self
end
--------------------------------------------------------------------------------
-- IPv6Collection class
-- Holds a list of IPv6 addresses/subnets.
--------------------------------------------------------------------------------
local IPv6Collection = setmetatable({}, IPCollection)
IPv6Collection.__index = IPv6Collection
do
-- Private static methods
local function isCollectionObject(val)
-- Return true if val is probably derived from an IPCollection object,
-- otherwise return false.
if type(val) == 'table' then
local mt = getmetatable(val)
if mt == IPv4Collection or mt == IPv6Collection then
return true
end
end
return false
end
validateCollection = makeValidationFunction('IPCollection', isCollectionObject)
function IPv6Collection.new()
return setmetatable(IPCollection.new(V6), IPv6Collection)
end
function IPv6Collection:addFromString(text)
-- Extract any IPv6 addresses or CIDR subnets from given text.
-- Want to accept all valid IPv6 despite the fact that addresses used
-- are unlikely to start with ':'.
-- Also want to be able to parse arbitrary wikitext which might use
-- colons for indenting.
-- Therefore, if an address at the start of a line is valid, use it;
-- otherwise strip any leading colons and try again.
checkType('addFromString', 1, text, 'string')
for line in string.gmatch(text .. '\n', '[\t ]*(.-)[\t\r ]*\n') do
line = line:gsub('[!"#&\'()+,%-;<=>?[%]_{|}]', ' ')
for position, hit in line:gmatch('()(%S+)') do
local ip = hit:match('^([:%x]+)/?%d*$')
if ip then
local _, n = ip:gsub(':', ':')
if n >= 2 then
self:_store(hit, position == 1)
end
end
end
end
return self
end
end
return {
IPAddress = IPAddress,
Subnet = Subnet,
IPv4Collection = IPv4Collection,
IPv6Collection = IPv6Collection,
}