Jump to content
Wikipedia The Free Encyclopedia

Module:IP/sandbox

From Wikipedia, the free encyclopedia
This is the module sandbox page for Module:IP (diff).
See also the companion subpage for test cases (run).
Module documentation[view] [edit] [history] [purge]
This module is rated as ready for general use. It has reached a mature state, is considered relatively stable and bug-free, and may be used wherever appropriate. It can be mentioned on help pages and other Wikipedia resources as an option for new users. To minimise server load and avoid disruptive output, improvements should be developed through sandbox testing rather than repeated trial-and-error editing.
This module can only be edited by administrators because it is transcluded onto one or more cascade-protected pages.
[画像:Warning] This Lua module is used in system messages, and on approximately 145,000 pages.
Changes to it can cause immediate changes to the Wikipedia user interface.
To avoid major disruption and server load, any changes should be tested in the module's /sandbox or /testcases subpages, or in your own module sandbox. The tested changes can be added to this page in a single edit. Please discuss changes on the talk page before implementing them.

Module:IP is a library for working with IP addresses and subnets. It can handle both IPv4 and IPv6. The library exports four classes, IPAddress, Subnet, IPv4Collection, and IPv6Collection.

Loading the library

[edit ]
localIP=require('Module:IP')
localIPAddress=IP.IPAddress
localSubnet=IP.Subnet

IPAddress

[edit ]

The IPAddress class is used to work with single IP addresses. To create a new IPAddress object:

localipAddress=IPAddress.new(ipString)

The ipString variable can be a valid IPv4 or IPv6 address.

Examples:

localipv4Address=IPAddress.new('1.2.3.4')
localipv6Address=IPAddress.new('2001:db8::ff00:12:3456')

If a non-IP string or an invalid IP address is passed to the function, that returns an error. If you want to check whether a given string is an IP address and continue the procedure in the caller module, use pcall.

localisIp,ip=pcall(IPAddress.new,'1.2.3.4')-- isIp: true, ip: IPAddress object
localisIp,ip=pcall(IPAddress.new,'Example')-- isIp: false, ip: nil
localisIp,ip=pcall(IPAddress.new,'1.2.3.256')-- isIp: false, ip: nil

IPAddress objects can be compared with relational operators:

-- Equality
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
-- Less than / greater than
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

You can use tostring on them (this is equivalent to using 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"
-- Expanded IPv6 addresses are abbreviated:
tostring(IPAddress.new('2001:db8:0:0:0:0:0:0'))-- "2001:db8::"

You can also concatenate them:

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"

IPAddress objects have several methods, outlined below.

getIP

[edit ]
ipAddress:getIP()

Returns a string representation of the IP address. IPv6 addresses are abbreviated if possible.

Examples:

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

[edit ]
ipAddress:getVersion()

Returns the version of the IP protocol being used. This is "IPv4" for IPv4 addresses, and "IPv6" for IPv6 addresses.

Examples:

IPAddress.new('1.2.3.4'):getVersion()-- "IPv4"
IPAddress.new('2001:db8::ff00:12:3456'):getVersion()-- "IPv6"

isIPv4

[edit ]
ipAddress:isIPv4()

Returns true if the IP address is an IPv4 address, and false otherwise.

Examples:

IPAddress.new('1.2.3.4'):isIPv4()-- true
IPAddress.new('2001:db8::ff00:12:3456'):isIPv4()-- false

isIPv6

[edit ]
ipAddress:isIPv6()

Returns true if the IP address is an IPv6 address, and false otherwise.

Examples:

IPAddress.new('1.2.3.4'):isIPv6()-- false
IPAddress.new('2001:db8::ff00:12:3456'):isIPv6()-- true

isInSubnet

[edit ]
ipAddress:isInSubnet(subnet)

Returns true if the IP address is in the subnet subnet, and false otherwise. subnet may be a Subnet object or a CIDR string.

Examples:

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

[edit ]
ipAddress:getSubnet(bitLength)

Returns a Subnet object for the subnet with a bit length of bitLength which contains the current IP. The bitLength parameter must be an integer between 0 and 32 for IPv4 addresses, or an integer between 0 and 128 for IPv6 addresses.

Examples:

IPAddress.new('1.2.3.4'):getSubnet(24)-- Equivalent to Subnet.new('1.2.3.0/24')

getNextIP

[edit ]
ipAddress:getNextIP()

Returns a new IPAddress object equivalent to the current IP address incremented by one. The IPv4 address "255.255.255.255" rolls around to "0.0.0.0", and the IPv6 address "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" rolls around to "::".

Examples:

IPAddress.new('1.2.3.4'):getNextIP()-- Equivalent to IPAddress.new('1.2.3.5')
IPAddress.new('2001:db8::ff00:12:3456'):getNextIP()-- Equivalent to IPAddress.new('2001:db8::ff00:12:3457')
IPAddress.new('255.255.255.255'):getNextIP()-- Equivalent to IPAddress.new('0.0.0.0')

getPreviousIP

[edit ]
ipAddress:getPreviousIP()

Returns a new IPAddress object equivalent to the current IP address decremented by one. The IPv4 address "0.0.0.0" rolls around to "255.255.255.255", and the IPv6 address "::" rolls around to "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff".

Examples:

IPAddress.new('1.2.3.4'):getPreviousIP()-- Equivalent to IPAddress.new('1.2.3.3')
IPAddress.new('2001:db8::ff00:12:3456'):getPreviousIP()-- Equivalent to IPAddress.new('2001:db8::ff00:12:3455')
IPAddress.new('0.0.0.0'):getPreviousIP()-- Equivalent to IPAddress.new('255.255.255.255')

Subnet

[edit ]

The Subnet class is used to work with subnetworks of IPv4 or IPv6 addresses. To create a new Subnet object:

localsubnet=Subnet.new(cidrString)

cidrString must be a valid IPv4 or IPv6 CIDR string.

localcidr=Subnet.new('255.255.255.0/24')-- Subnet object
localcidr=Subnet.new('255.255.255.1/24')-- error

Subnet objects can be compared for equality:

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

You can use tostring on them (this is equivalent to 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"

You can also concatenate them:

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"

Subnet objects have several methods, outlined below.

getPrefix

[edit ]
subnet:getPrefix()

Returns an IPAddress object for the lowest IP address in the subnet.

Examples:

Subnet.new('1.2.3.0/24'):getPrefix()-- Equivalent to IPAddress.new('1.2.3.0')
Subnet.new('2001:db8::ff00:12:0/112'):getPrefix()-- Equivalent to IPAddress.new('2001:db8::ff00:12:0')

getHighestIP

[edit ]
subnet:getHighestIP()

Returns an IPAddress object for the highest IP address in the subnet.

Examples:

Subnet.new('1.2.3.0/24'):getHighestIP()-- Equivalent to IPAddress.new('1.2.3.255')
Subnet.new('2001:db8::ff00:12:0/112'):getHighestIP()-- Equivalent to IPAddress.new('2001:db8::ff00:12:ffff')

getBitLength

[edit ]
subnet:getBitLength()

Returns the bit length of the subnet. This is an integer between 0 and 32 for IPv4 addresses, or an integer between 0 and 128 for IPv6 addresses.

Examples:

Subnet.new('1.2.3.0/24'):getBitLength()-- 24
Subnet.new('2001:db8::ff00:12:0/112'):getBitLength()-- 112

getCIDR

[edit ]
subnet:getCIDR()

Returns a CIDR string representation of the subnet.

Examples:

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

[edit ]
subnet:getVersion()

Returns the version of the IP protocol being used. This is "IPv4" for IPv4 addresses, and "IPv6" for IPv6 addresses.

Examples:

Subnet.new('1.2.3.0/24'):getVersion()-- "IPv4"
Subnet.new('2001:db8::ff00:12:0/112'):getVersion()-- "IPv6"

isIPv4

[edit ]
subnet:isIPv4()

Returns true if the subnet is using IPv4, and false otherwise.

Examples:

Subnet.new('1.2.3.0/24'):isIPv4()-- true
Subnet.new('2001:db8::ff00:12:0/112'):isIPv4()-- false

isIPv6

[edit ]
subnet:isIPv6()

Returns true if the subnet is using IPv6, and false otherwise.

Examples:

Subnet.new('1.2.3.0/24'):isIPv6()-- false
Subnet.new('2001:db8::ff00:12:0/112'):isIPv6()-- true

containsIP

[edit ]
subnet:containsIP(ip)

Returns true if the subnet contains the IP address ip, and false otherwise. ip can be an IP address string, or an IPAddress object.

Examples:

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

[edit ]
subnet:overlapsSubnet(subnet)

Returns true if the current subnet overlaps with subnet, and false otherwise. subnet can be a CIDR string or a subnet object.

Examples:

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

[edit ]
subnet:walk()

The walk method iterates over all of the IPAddress objects in the subnet.

Examples:

foripAddressinSubnet.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

[edit ]

The IPv4Collection class is used to work with several different IPv4 addresses and IPv4 subnets. To create a new IPv4Collection object:

localcollection=IPv4Collection.new()

IPv4Collection objects have several methods, outlined below.

getVersion

[edit ]
collection:getVersion()

Returns the string "IPv4".

addIP

[edit ]
collection:addIP(ip)

Adds an IP to the collection. The IP can be either a string or an IPAddress object.

Examples:

collection:addIP('1.2.3.4')
collection:addIP(IPAddress.new('1.2.3.4'))

This method is chainable:

collection:addIP('1.2.3.4'):addIP('5.6.7.8')

addSubnet

[edit ]
collection:addSubnet(subnet)

Adds a subnet to the collection. The subnet can be either a CIDR string or a Subnet object.

Examples:

collection:addSubnet('1.2.3.0/24')
collection:addSubnet(Subnet.new('1.2.3.0/24'))

This method is chainable:

collection:addSubnet('1.2.0.0/24'):addSubnet('1.2.1.0/24')

addFromString

[edit ]
collection:addFromString(str)

Extracts any IPv4 addresses and IPv4 CIDR subnets from str and adds them to the collection. Any text that is not an IPv4 address or CIDR subnet is ignored.

Examples:

collection:addFromString('Add some IPs and subnets: 1.2.3.4 1.2.3.5 2001:0::f foo 1.2.4.0/24')

This method is chainable:

collection:addFromString('foo 1.2.3.4'):addFromString('bar 5.6.7.8')

containsIP

[edit ]
collection:containsIP(ip)

Returns true if the collection contains the specified IP; otherwise returns false. The ip parameter can be a string or an IPAddress object.

Examples:

collection:containsIP('1.2.3.4')
collection:containsIP(IPAddress.new('1.2.3.4'))

getRanges

[edit ]
collection:getRanges()

Returns a sorted array of IP pairs equivalent to the collection. Each IP pair is an array representing a contiguous range of IP addresses from pair[1] to pair[2] inclusive. pair[1] and pair[2] are IPAddress objects.

Examples:

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())
-- Logs the following:
-- table#1 {
-- table#2 {
-- 1.2.0.0,
-- 1.2.1.255,
-- },
-- table#3 {
-- 1.2.10.0,
-- 1.2.10.255,
-- },
-- }

overlapsSubnet

[edit ]
collection:overlapsSubnet(subnet)

Returns true, obj if subnet overlaps this collection, where obj is the first IPAddress or Subnet object overlapping the subnet. Otherwise, returns false. subnet can be a CIDR string or a Subnet object.

Examples:

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

[edit ]

The IPv6Collection class is used to work with several different IPv6 addresses and IPv6 subnets. IPv6Collection objects are directly analogous to IPv4Collection objects: they contain the same methods and work the same way, but all IP addresses and subnets added to it must be IPv6, not IPv4.

To create a new IPv6Collection object:

localcollection=IPv6Collection.new()
The above documentation is transcluded from Module:IP/doc. (edit | history)
Editors can experiment in this module's sandbox (edit | diff) and testcases (edit | run) pages.
Add categories to the /doc subpage. Subpages of this module.
 -- IP library
 -- This library contains classes for working with IP addresses and IP ranges.

 -- Load modules
 require('strict')
 localbit32=require('bit32')
 locallibraryUtil=require('libraryUtil')
 localcheckType=libraryUtil.checkType
 localcheckTypeMulti=libraryUtil.checkTypeMulti
 localmakeCheckSelfFunction=libraryUtil.makeCheckSelfFunction

 -- Constants
 localV4='IPv4'
 localV6='IPv6'

 --------------------------------------------------------------------------------
 -- Helper functions
 --------------------------------------------------------------------------------

 localfunctionmakeValidationFunction(className,isObjectFunc)
 -- Make a function for validating a specific object.
 returnfunction(methodName,argIdx,arg)
 ifnotisObjectFunc(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.
 --------------------------------------------------------------------------------

 localCollection={}
 Collection.__index=Collection

 functionCollection:add(item)
 ifitem~=nilthen
 self.n=self.n+1
 self[self.n]=item
 end
 end

 functionCollection:join(sep)
 returntable.concat(self,sep)
 end

 functionCollection:remove(pos)
 ifself.n>0and(pos==nilor(0<posandpos<=self.n))then
 self.n=self.n-1
 returntable.remove(self,pos)
 end
 end

 functionCollection:sort(comp)
 table.sort(self,comp)
 end

 functionCollection:deobjectify()
 -- Turns the collection into a plain array without any special properties
 -- or methods.
 self.n=nil
 setmetatable(self,nil)
 end

 functionCollection.new()
 returnsetmetatable({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.
 --------------------------------------------------------------------------------

 localRawIP={}
 RawIP.__index=RawIP

 -- Constructors
 functionRawIP.newFromIPv4(ipStr)
 -- Return a RawIP object if ipStr is a valid IPv4 string. Otherwise,
 -- return nil.
 -- This representation is for compatibility with IPv6 addresses.
 localoctets=Collection.new()
 locals=ipStr:match('^%s*(.-)%s*$')..'.'
 foritemins:gmatch('(.-)%.')do
 octets:add(item)
 end
 ifoctets.n==4then
 fori,sinipairs(octets)do
 ifs:match('^%d+$')then
 localnum=tonumber(s)
 if0<=numandnum<=255then
 ifnum>0ands:match('^0')then
 -- A redundant leading zero is for an IP in octal.
 returnnil
 end
 octets[i]=num
 else
 returnnil
 end
 else
 returnnil
 end
 end
 localparts=Collection.new()
 fori=1,3,2do
 parts:add(octets[i]*256+octets[i+1])
 end
 returnsetmetatable(parts,RawIP)
 end
 returnnil
 end

 functionRawIP.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(':',':')
 ifn<7then
 ipStr=ipStr:gsub('::',string.rep(':',9-n))
 end
 localparts=Collection.new()
 foritemin(ipStr..':'):gmatch('(.-):')do
 parts:add(item)
 end
 ifparts.n==8then
 fori,sinipairs(parts)do
 ifs==''then
 parts[i]=0
 else
 ifs:match('^%x+$')then
 localnum=tonumber(s,16)
 ifnumand0<=numandnum<=65535then
 parts[i]=num
 else
 returnnil
 end
 else
 returnnil
 end
 end
 end
 returnsetmetatable(parts,RawIP)
 end
 returnnil
 end

 functionRawIP.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.
 returnRawIP.newFromIPv4(ipStr)orRawIP.newFromIPv6(ipStr)
 end

 -- Methods
 functionRawIP:getVersion()
 -- Return a string with the version of the IP protocol we are using.
 returnself.n==2andV4orV6
 end

 functionRawIP:isIPv4()
 -- Return true if this is an IPv4 representation, and false otherwise.
 returnself.n==2
 end

 functionRawIP:isIPv6()
 -- Return true if this is an IPv6 representation, and false otherwise.
 returnself.n==8
 end

 functionRawIP:getBitLength()
 -- Return the bit length of the IP address.
 returnself.n*16
 end

 functionRawIP: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
 localresult=Collection.new()
 result.n=self.n
 localcarry=previousand0xffffor1
 fori=self.n,1,-1do
 localsum=self[i]+carry
 ifsum>=0x10000then
 carry=previousand0x10000or1
 sum=sum-0x10000
 else
 carry=previousand0xffffor0
 end
 result[i]=sum
 end
 returnsetmetatable(result,RawIP)
 end

 functionRawIP:getPrefix(bitLength)
 -- Return a RawIP object for the prefix of the current IP Address with a
 -- bit length of bitLength.
 localresult=Collection.new()
 result.n=self.n
 fori=1,self.ndo
 ifbitLength>0then
 ifbitLength>=16then
 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
 returnsetmetatable(result,RawIP)
 end

 functionRawIP: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.
 localbits=self.n*16
 localwidth
 ifbitLength<=0then
 width=bits
 elseifbitLength>=bitsthen
 width=0
 else
 width=bits-bitLength
 end
 localresult=Collection.new()
 result.n=self.n
 fori=self.n,1,-1do
 ifwidth>0then
 ifwidth>=16then
 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
 returnsetmetatable(result,RawIP)
 end

 functionRawIP:_makeIPv6String()
 -- Return an IPv6 string representation of the object. Behavior is
 -- undefined if the current object is IPv4.
 localz1,z2-- indices of run of zeroes to be displayed as "::"
 localzstart,zcount
 fori=1,9do
 -- Find left-most occurrence of longest run of two or more zeroes.
 ifi<9andself[i]==0then
 ifzstartthen
 zcount=zcount+1
 else
 zstart=i
 zcount=1
 end
 else
 ifzcountandzcount>1then
 ifnotz1orzcount>z2-z1+1then
 z1=zstart
 z2=zstart+zcount-1
 end
 end
 zstart=nil
 zcount=nil
 end
 end
 localparts=Collection.new()
 fori=1,8do
 ifz1andz1<=iandi<=z2then
 ifi==z1then
 ifz1==1orz2==8then
 ifz1==1andz2==8then
 return'::'
 end
 parts:add(':')
 else
 parts:add('')
 end
 end
 else
 parts:add(string.format('%x',self[i]))
 end
 end
 returnparts:join(':')
 end

 functionRawIP:_makeIPv4String()
 -- Return an IPv4 string representation of the object. Behavior is
 -- undefined if the current object is IPv6.
 localparts=Collection.new()
 fori=1,2do
 localw=self[i]
 parts:add(math.floor(w/256))
 parts:add(w%256)
 end
 returnparts:join('.')
 end

 functionRawIP:__tostring()
 -- Return a string equivalent to given IP address (IPv4 or IPv6).
 ifself.n==2then
 returnself:_makeIPv4String()
 else
 returnself:_makeIPv6String()
 end
 end

 functionRawIP:__lt(obj)
 ifself.n==obj.nthen
 fori=1,self.ndo
 ifself[i]~=obj[i]then
 returnself[i]<obj[i]
 end
 end
 returnfalse
 end
 returnself.n<obj.n
 end

 functionRawIP:__eq(obj)
 ifself.n==obj.nthen
 fori=1,self.ndo
 ifself[i]~=obj[i]then
 returnfalse
 end
 end
 returntrue
 end
 returnfalse
 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.
 localmakeIPAddress,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.
 localvalidateCollection,validateIPAddress,validateSubnet

 --------------------------------------------------------------------------------
 -- IPAddress class
 -- Represents a single IPv4 or IPv6 address.
 --------------------------------------------------------------------------------

 localIPAddress={}

 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.
 localdataKey={}

 -- Private static methods
 localfunctionisIPAddressObject(val)
 returntype(val)=='table'andval[dataKey]~=nil
 end

 validateIPAddress=makeValidationFunction('IPAddress',isIPAddressObject)

 -- Metamethods that don't need upvalues
 localfunctionipEquals(ip1,ip2)
 returnip1[dataKey].rawIP==ip2[dataKey].rawIP
 end

 localfunctionipLessThan(ip1,ip2)
 returnip1[dataKey].rawIP<ip2[dataKey].rawIP
 end

 localfunctionconcatIP(ip,val)
 returntostring(ip)..tostring(val)
 end

 localfunctionipToString(ip)
 returnip: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
 localobj={}
 localdata={}
 data.rawIP=rawIP

 -- A function to check whether methods are called with a valid self
 -- parameter.
 localcheckSelf=makeCheckSelfFunction(
 'IP',
 'ipAddress',
 obj,
 'IPAddress object'
 )

 -- Public methods
 functionobj:getIP()
 checkSelf(self,'getIP')
 returntostring(data.rawIP)
 end

 functionobj:getVersion()
 checkSelf(self,'getVersion')
 returndata.rawIP:getVersion()
 end

 functionobj:isIPv4()
 checkSelf(self,'isIPv4')
 returndata.rawIP:isIPv4()
 end

 functionobj:isIPv6()
 checkSelf(self,'isIPv6')
 returndata.rawIP:isIPv6()
 end

 functionobj:isInCollection(collection)
 checkSelf(self,'isInCollection')
 validateCollection('isInCollection',1,collection)
 returncollection:containsIP(self)
 end

 functionobj:isInSubnet(subnet)
 checkSelf(self,'isInSubnet')
 localtp=type(subnet)
 iftp=='string'then
 subnet=makeSubnet(subnet)
 elseiftp=='table'then
 validateSubnet('isInSubnet',1,subnet)
 else
 checkTypeMulti('isInSubnet',1,subnet,{'string','table'})
 end
 returnsubnet:containsIP(self)
 end

 functionobj:getSubnet(bitLength)
 checkSelf(self,'getSubnet')
 checkType('getSubnet',1,bitLength,'number')
 ifbitLength<0
 orbitLength>data.rawIP:getBitLength()
 orbitLength~=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
 returnmakeSubnetFromRaw(data.rawIP,bitLength)
 end

 functionobj:getNextIP()
 checkSelf(self,'getNextIP')
 returnmakeIPAddressFromRaw(data.rawIP:getAdjacent())
 end

 functionobj:getPreviousIP()
 checkSelf(self,'getPreviousIP')
 returnmakeIPAddressFromRaw(data.rawIP:getAdjacent(true))
 end

 -- Metamethods
 returnsetmetatable(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.
 ifkey==dataKeythen
 returndata
 end
 end,
 __metatable=false,-- don't allow access to the metatable
 })
 end

 makeIPAddress=function(ip)
 localrawIP=RawIP.newFromIP(ip)
 ifnotrawIPthen
 error(string.format("'%s' is an invalid IP address",ip),3)
 end
 returnmakeIPAddressFromRaw(rawIP)
 end

 functionIPAddress.new(ip)
 checkType('IPAddress.new',1,ip,'string')
 returnmakeIPAddress(ip)
 end
 end

 --------------------------------------------------------------------------------
 -- Subnet class
 -- Represents a block of IPv4 or IPv6 addresses.
 --------------------------------------------------------------------------------

 localSubnet={}

 do
 -- uniqueKey is a unique, private key used to test whether a given object
 -- is a Subnet object.
 localuniqueKey={}

 -- Metatable
 localmt={
 __index=function(self,key)
 ifkey==uniqueKeythen
 returntrue
 end
 end,
 __eq=function(self,obj)
 returnself:getCIDR()==obj:getCIDR()
 end,
 __concat=function(self,obj)
 returntostring(self)..tostring(obj)
 end,
 __tostring=function(self)
 returnself:getCIDR()
 end,
 __metatable=false
 }

 -- Private static methods
 localfunctionisSubnetObject(val)
 -- Return true if val is a Subnet object, and false otherwise.
 returntype(val)=='table'andval[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
 localobj=setmetatable({},mt)
 localdata={
 rawIP=rawIP,
 bitLength=bitLength,
 }

 -- A function to check whether methods are called with a valid self
 -- parameter.
 localcheckSelf=makeCheckSelfFunction(
 'IP',
 'subnet',
 obj,
 'Subnet object'
 )

 -- Public methods
 functionobj:getPrefix()
 checkSelf(self,'getPrefix')
 ifnotdata.prefixthen
 data.prefix=makeIPAddressFromRaw(
 data.rawIP:getPrefix(data.bitLength)
 )
 end
 returndata.prefix
 end

 functionobj:getHighestIP()
 checkSelf(self,'getHighestIP')
 ifnotdata.highestIPthen
 data.highestIP=makeIPAddressFromRaw(
 data.rawIP:getHighestHost(data.bitLength)
 )
 end
 returndata.highestIP
 end

 functionobj:getBitLength()
 checkSelf(self,'getBitLength')
 returndata.bitLength
 end

 functionobj:getCIDR()
 checkSelf(self,'getCIDR')
 returnstring.format(
 '%s/%d',
 tostring(self:getPrefix()),self:getBitLength()
 )
 end

 functionobj:getVersion()
 checkSelf(self,'getVersion')
 returndata.rawIP:getVersion()
 end

 functionobj:isIPv4()
 checkSelf(self,'isIPv4')
 returndata.rawIP:isIPv4()
 end

 functionobj:isIPv6()
 checkSelf(self,'isIPv6')
 returndata.rawIP:isIPv6()
 end

 functionobj:containsIP(ip)
 checkSelf(self,'containsIP')
 localtp=type(ip)
 iftp=='string'then
 ip=makeIPAddress(ip)
 elseiftp=='table'then
 validateIPAddress('containsIP',1,ip)
 else
 checkTypeMulti('containsIP',1,ip,{'string','table'})
 end
 ifself:getVersion()==ip:getVersion()then
 returnself:getPrefix()<=ipandip<=self:getHighestIP()
 end
 returnfalse
 end

 functionobj:overlapsCollection(collection)
 checkSelf(self,'overlapsCollection')
 validateCollection('overlapsCollection',1,collection)
 returncollection:overlapsSubnet(self)
 end

 functionobj:overlapsSubnet(subnet)
 checkSelf(self,'overlapsSubnet')
 localtp=type(subnet)
 iftp=='string'then
 subnet=makeSubnet(subnet)
 elseiftp=='table'then
 validateSubnet('overlapsSubnet',1,subnet)
 else
 checkTypeMulti('overlapsSubnet',1,subnet,{'string','table'})
 end
 ifself:getVersion()==subnet:getVersion()then
 return(
 subnet:getHighestIP()>=self:getPrefix()and
 subnet:getPrefix()<=self:getHighestIP()
 )
 end
 returnfalse
 end

 functionobj:walk()
 checkSelf(self,'walk')
 localstarted
 localcurrent=self:getPrefix()
 localhighest=self:getHighestIP()
 returnfunction()
 ifnotstartedthen
 started=true
 returncurrent
 end
 ifcurrent<highestthen
 current=current:getNextIP()
 returncurrent
 end
 end
 end

 returnobj
 end

 makeSubnet=function(cidr)
 -- Return a Subnet object from a CIDR string. If the CIDR string is
 -- invalid, throw an error.
 locallhs,rhs=cidr:match('^%s*(.-)/(%d+)%s*$')
 iflhsthen
 localbits=lhs:find(':',1,true)and128or32
 localn=tonumber(rhs)
 ifnandn<=bitsand(n==0ornotrhs: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.
 localbase=RawIP.newFromIP(lhs)
 ifbasethen
 -- The left-hand side is a valid IP address.
 localprefix=base:getPrefix(n)
 ifbase==prefixthen
 -- The left-hand side is the lowest IP in the subnet.
 returnmakeSubnetFromRaw(prefix,n)
 end
 end
 end
 end
 error(string.format("'%s' is an invalid CIDR string",cidr),3)
 end

 functionSubnet.new(cidr)
 checkType('Subnet.new',1,cidr,'string')
 returnmakeSubnet(cidr)
 end
 end

 --------------------------------------------------------------------------------
 -- Ranges class
 -- Holds a list of IPAdress pairs representing contiguous IP ranges.
 --------------------------------------------------------------------------------

 localRanges=Collection.new()
 Ranges.__index=Ranges

 functionRanges.new()
 returnsetmetatable({},Ranges)
 end

 functionRanges:add(ip1,ip2)
 validateIPAddress('add',1,ip1)
 ifip2~=nilthen
 validateIPAddress('add',2,ip2)
 ifip1>ip2then
 error('The first IP must be less than or equal to the second',2)
 end
 end
 Collection.add(self,{ip1,ip2orip1})
 end

 functionRanges:merge()
 self:sort(
 function(lhs,rhs)
 -- Sort by second value, then first.
 iflhs[2]==rhs[2]then
 returnlhs[1]<rhs[1]
 end
 returnlhs[2]<rhs[2]
 end
 )
 localpos=self.n
 whilepos>1do
 fori=pos-1,1,-1do
 localip1=self[i][2]
 localip2=ip1:getNextIP()
 ifip2<ip1then
 ip2=ip1-- don't wrap around
 end
 ifself[pos][1]>ip2then
 break
 end
 ip1=self[i][1]
 ip2=self[pos][1]
 self[i]={ip1>ip2andip2orip1,self[pos][2]}
 self:remove(pos)
 pos=pos-1
 ifpos<=1then
 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).
 --------------------------------------------------------------------------------

 localIPCollection={}
 IPCollection.__index=IPCollection

 functionIPCollection.new(version)
 assert(
 version==V4orversion==V6,
 'IPCollection.new called with an invalid version'
 )
 localobj={
 version=version,-- V4 or V6
 addresses=Collection.new(),-- valid IP addresses
 subnets=Collection.new(),-- valid subnets
 omitted=Collection.new(),-- not-quite valid strings
 }
 returnobj
 end

 functionIPCollection:getVersion()
 -- Return a string with the IP version of addresses in this collection.
 returnself.version
 end

 functionIPCollection:_store(hit,stripColons)
 localmaker,location
 ifhit:find('/',1,true)then
 maker=Subnet.new
 location=self.subnets
 else
 maker=IPAddress.new
 location=self.addresses
 end
 localsuccess,obj=pcall(maker,hit)
 ifsuccessthen
 location:add(obj)
 else
 ifstripColonsthen
 localcolons,hit=hit:match('^(:*)(.*)')
 ifcolons~=''then
 self:_store(hit)
 return
 end
 end
 self.omitted:add(hit)
 end
 end

 functionIPCollection:_assertVersion(version,msg)
 ifself.version~=versionthen
 error(msg,3)
 end
 end

 functionIPCollection:addIP(ip)
 localtp=type(ip)
 iftp=='string'then
 ip=makeIPAddress(ip)
 elseiftp=='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)
 returnself
 end

 functionIPCollection:addSubnet(subnet)
 localtp=type(subnet)
 iftp=='string'then
 subnet=makeSubnet(subnet)
 elseiftp=='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)
 returnself
 end

 functionIPCollection: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.
 localtp=type(ip)
 iftp=='string'then
 ip=makeIPAddress(ip)
 elseiftp=='table'then
 validateIPAddress('containsIP',1,ip)
 else
 checkTypeMulti('containsIP',1,ip,{'string','table'})
 end
 ifself:getVersion()==ip:getVersion()then
 for_,iteminipairs(self.addresses)do
 ifitem==ipthen
 returntrue,item
 end
 end
 for_,iteminipairs(self.subnets)do
 ifitem:containsIP(ip)then
 returntrue,item
 end
 end
 end
 returnfalse
 end

 functionIPCollection: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).
 localranges=Ranges.new()
 for_,iteminipairs(self.addresses)do
 ranges:add(item)
 end
 for_,iteminipairs(self.subnets)do
 ranges:add(item:getPrefix(),item:getHighestIP())
 end
 ranges:merge()
 ranges:deobjectify()
 returnranges
 end

 functionIPCollection:overlapsSubnet(subnet)
 -- Return true, obj if subnet overlaps this collection,
 -- where obj is the first IPAddress or Subnet overlapping the subnet.
 -- Otherwise, return false.
 localtp=type(subnet)
 iftp=='string'then
 subnet=makeSubnet(subnet)
 elseiftp=='table'then
 validateSubnet('overlapsSubnet',1,subnet)
 else
 checkTypeMulti('overlapsSubnet',1,subnet,{'string','table'})
 end
 ifself:getVersion()==subnet:getVersion()then
 for_,iteminipairs(self.addresses)do
 ifsubnet:containsIP(item)then
 returntrue,item
 end
 end
 for_,iteminipairs(self.subnets)do
 ifsubnet:overlapsSubnet(item)then
 returntrue,item
 end
 end
 end
 returnfalse
 end

 --------------------------------------------------------------------------------
 -- IPv4Collection class
 -- Holds a list of IPv4 addresses/subnets.
 --------------------------------------------------------------------------------

 localIPv4Collection=setmetatable({},IPCollection)
 IPv4Collection.__index=IPv4Collection

 functionIPv4Collection.new()
 returnsetmetatable(IPCollection.new(V4),IPv4Collection)
 end

 functionIPv4Collection:addFromString(text)
 -- Extract any IPv4 addresses or CIDR subnets from given text.
 checkType('addFromString',1,text,'string')
 text=text:gsub('[:!"#&\'()+,%-;<=>?[%]_{|}]',' ')
 forhitintext:gmatch('%S+')do
 ifhit:match('^%d+%.%d+[%.%d/]+$')then
 local_,n=hit:gsub('%.','.')
 ifn>=3then
 self:_store(hit)
 end
 end
 end
 returnself
 end

 --------------------------------------------------------------------------------
 -- IPv6Collection class
 -- Holds a list of IPv6 addresses/subnets.
 --------------------------------------------------------------------------------

 localIPv6Collection=setmetatable({},IPCollection)
 IPv6Collection.__index=IPv6Collection

 do
 -- Private static methods
 localfunctionisCollectionObject(val)
 -- Return true if val is probably derived from an IPCollection object,
 -- otherwise return false.
 iftype(val)=='table'then
 localmt=getmetatable(val)
 ifmt==IPv4Collectionormt==IPv6Collectionthen
 returntrue
 end
 end
 returnfalse
 end

 validateCollection=makeValidationFunction('IPCollection',isCollectionObject)

 functionIPv6Collection.new()
 returnsetmetatable(IPCollection.new(V6),IPv6Collection)
 end

 functionIPv6Collection: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')
 forlineinstring.gmatch(text..'\n','[\t ]*(.-)[\t\r ]*\n')do
 line=line:gsub('[!"#&\'()+,%-;<=>?[%]_{|}]',' ')
 forposition,hitinline:gmatch('()(%S+)')do
 localip=hit:match('^([:%x]+)/?%d*$')
 ifipthen
 local_,n=ip:gsub(':',':')
 ifn>=2then
 self:_store(hit,position==1)
 end
 end
 end
 end
 returnself
 end
 end

 --------------------------------------------------------------------------------
 -- Util class (static)
 -- Holds utility functions.
 --------------------------------------------------------------------------------

 localUtil={}

 functionUtil.removeDirMarkers(str)
 -- Remove any of following directional markers
 -- LRM : LEFT-TO-RIGHT MARK (U+200E) : hex e2 80 8e = 226 128 142
 -- LRE : LEFT-TO-RIGHT EMBEDDING (U+202A) : hex e2 80 aa = 226 128 170
 -- PDF : POP DIRECTIONAL FORMATTING (U+202C) : hex e2 80 ac = 226 128 172
 -- This is required for MediaWiki:Blockedtext message.
 returnstring.gsub(str,'226円128円[142円170円172円]','')
 end

 localfunctioncorrectCidr(cidrStr)
 -- Correct a well-formatted but invalid CIDR string to a valid one (e.g. 255.255.255.1/24 -> 255.255.255.0/24).
 -- Return a Subnet object only if correction takes place.
 localisCidr,cidr=pcall(Subnet.new,cidrStr)
 locali,_=string.find(cidrStr,'/%d+$');
 ifnotisCidrandi~=nilandi>1then
 localbitLen=tonumber(cidrStr:sub(i+1))
 localroot=cidrStr:sub(1,i-1)
 localisIp,ip=pcall(IPAddress.new,root)
 ifisIpthen
 localisValidSubnet=ip:isIPv4()and0<=bitLenandbitLen<=32orip:isIPv6()and0<=bitLenandbitLen<=128
 ifisValidSubnetthen
 returnip:getSubnet(bitLen)
 end
 end
 end
 returnnil
 end

 localfunctionisSpecifiedProtocol(obj,protocol)
 -- Check if a given IPAddress/Subnet object is an instance of IPv4, IPv6, or either, and return a boolean value.
 ifprotocol=='v4'then
 returnobj:isIPv4()
 elseifprotocol=='v6'then
 returnobj:isIPv6()
 else
 returnobj:isIPv4()orobj:isIPv6()
 end
 end

 localfunctionverifyIP(str,allowCidr,cidrOnly,protocol)
 -- Return 3 values: boolean, string, string/nil.
 -- v[1] is the result of whether the input string is an IP address or CIDR of the specified protocol (IPv4, IPv6, or either).
 -- v[2] is the input string.
 -- v[3] is a corrected CIDR string only if allowCidr or cidrOnly is true AND v[1] is true AND the input string is in a possible
 -- CIDR format but doesn't actually work as a CIDR and hence is corrected to a valid one (e.g. 1.2.3.4/24 -> 1.2.3.0/24).
 str=Util.removeDirMarkers(str)
 ifcidrOnly==truethenallowCidr=trueend-- Ignores the value of allowCidr if cidrOnly is true
 ifallowCidrthen
 localcorCidr=correctCidr(str)
 localcorrected=corCidr~=nil
 localisCidr,cidr
 ifcorrectedthen
 isCidr,cidr=true,corCidr
 else
 isCidr,cidr=pcall(Subnet.new,str)
 end
 ifisCidrthen-- The input (or corrected) string represents a valid CIDR
 isCidr=isSpecifiedProtocol(cidr,protocol)
 returnisCidr,str,(function()ifisCidrandcorrectedthenreturncidr:getCIDR()endend)()
 elseifcidrOnlythen-- Invalid as a CIDR
 returnfalse,str,nil
 end
 end
 localisIp,ip=pcall(IPAddress.new,str)
 ifisIpthen
 isIp=isSpecifiedProtocol(ip,protocol)
 end
 returnisIp,str,nil
 end

 functionUtil.isIPAddress(str,allowCidr,cidrOnly)
 returnverifyIP(str,allowCidr,cidrOnly,nil)
 end

 functionUtil.isIPv4Address(str,allowCidr,cidrOnly)
 returnverifyIP(str,allowCidr,cidrOnly,'v4')
 end

 functionUtil.isIPv6Address(str,allowCidr,cidrOnly)
 returnverifyIP(str,allowCidr,cidrOnly,'v6')
 end

 return{
 IPAddress=IPAddress,
 Subnet=Subnet,
 IPv4Collection=IPv4Collection,
 IPv6Collection=IPv6Collection,
 Util=Util
 }

AltStyle によって変換されたページ (->オリジナル) /