[Python-modules-commits] r371 - in /packages/ipy: ./ branches/
branches/upstream/
branches/upstream/current/ branches/upstream/current/build/
branches/upstream/current/build/lib/ branches/upstream/current/example/
branches/upstream/current/test/ tags/
werner at users.alioth.debian.org
werner at users.alioth.debian.org
Mon May 1 12:04:15 UTC 2006
Author: werner
Date: Mon May 1 12:03:34 2006
New Revision: 371
URL: http://svn.debian.org/wsvn/python-modules/?sc=1&rev=371
Log:
[svn-inject] Installing original source of ipy
Added:
packages/ipy/
packages/ipy/branches/
packages/ipy/branches/upstream/
packages/ipy/branches/upstream/current/
packages/ipy/branches/upstream/current/CHANGES
packages/ipy/branches/upstream/current/IPy.py
packages/ipy/branches/upstream/current/MANIFEST.in
packages/ipy/branches/upstream/current/README
packages/ipy/branches/upstream/current/THANKS
packages/ipy/branches/upstream/current/build/
packages/ipy/branches/upstream/current/build/lib/
packages/ipy/branches/upstream/current/build/lib/IPy.py
packages/ipy/branches/upstream/current/example/
packages/ipy/branches/upstream/current/example/confbuilder
packages/ipy/branches/upstream/current/example/confbuilder.py
packages/ipy/branches/upstream/current/setup.py
packages/ipy/branches/upstream/current/test/
packages/ipy/branches/upstream/current/test/test_IPy.py
packages/ipy/tags/
Added: packages/ipy/branches/upstream/current/CHANGES
URL: http://svn.debian.org/wsvn/python-modules/packages/ipy/branches/upstream/current/CHANGES?rev=371&op=file
==============================================================================
--- packages/ipy/branches/upstream/current/CHANGES (added)
+++ packages/ipy/branches/upstream/current/CHANGES Mon May 1 12:03:34 2006
@@ -1,0 +1,5 @@
+IPy 0.42 works on Python 2.3 without warnings
+
+IPy 0.41 has Python < 2.2 compatible unit tests and a README file
+
+IPy 0.4 was the first public relase
Added: packages/ipy/branches/upstream/current/IPy.py
URL: http://svn.debian.org/wsvn/python-modules/packages/ipy/branches/upstream/current/IPy.py?rev=371&op=file
==============================================================================
--- packages/ipy/branches/upstream/current/IPy.py (added)
+++ packages/ipy/branches/upstream/current/IPy.py Mon May 1 12:03:34 2006
@@ -1,0 +1,1267 @@
+""" IPy - class and tools for handling of IPv4 and IPv6 Addresses and Networks.
+
+$HeadURL: http://svn.23.nu/svn/repos/IPy/trunk/IPy.py $
+
+$Id: IPy.py 671 2004-08-22 21:02:29Z md $
+
+The IP class allows a comfortable parsing and handling for most
+notations in use for IPv4 and IPv6 Addresses and Networks. It was
+greatly inspired bei RIPE's Perl module NET::IP's interface but
+doesn't share the Implementation. It doesn't share non-CIDR netmasks,
+so funky stuff lixe a netmask 0xffffff0f can't be done here.
+
+ >>> ip = IP('127.0.0.0/30')
+ >>> for x in ip:
+ ... print x
+ ...
+ 127.0.0.0
+ 127.0.0.1
+ 127.0.0.2
+ 127.0.0.3
+ >>> ip2 = IP('0x7f000000/30')
+ >>> ip == ip2
+ 1
+ >>> ip.reverseNames()
+ ['0.0.0.127.in-addr.arpa.', '1.0.0.127.in-addr.arpa.', '2.0.0.127.in-addr.arpa.', '3.0.0.127.in-addr.arpa.']
+ >>> ip.reverseName()
+ '0-3.0.0.127.in-addr.arpa.'
+ >>> ip.iptype()
+ 'PRIVATE'
+
+It can detect about a dozen different ways of expressing IP addresses
+and networks, parse them and distinguish between IPv4 and IPv6 addresses.
+
+ >>> IP('10.0.0.0/8').version()
+ 4
+ >>> IP('::1').version()
+ 6
+ >>> print IP(0x7f000001)
+ 127.0.0.1
+ >>> print IP('0x7f000001')
+ 127.0.0.1
+ >>> print IP('127.0.0.1')
+ 127.0.0.1
+ >>> print IP('10')
+ 10.0.0.0
+ >>> print IP('1080:0:0:0:8:800:200C:417A')
+ 1080:0000:0000:0000:0008:0800:200c:417a
+ >>> print IP('1080::8:800:200C:417A')
+ 1080:0000:0000:0000:0008:0800:200c:417a
+ >>> print IP('::1')
+ 0000:0000:0000:0000:0000:0000:0000:0001
+ >>> print IP('::13.1.68.3')
+ 0000:0000:0000:0000:0000:0000:0d01:4403
+ >>> print IP('127.0.0.0/8')
+ 127.0.0.0/8
+ >>> print IP('127.0.0.0/255.0.0.0')
+ 127.0.0.0/8
+ >>> print IP('127.0.0.0-127.255.255.255')
+ 127.0.0.0/8
+
+Nearly all class methods which return a string have an optional
+parameter 'wantprefixlen' which controlles if the prefixlen or netmask
+is printed. Per default the prefilen is always shown if the net
+contains more than one address.
+
+wantprefixlen == 0 / None don't return anything 1.2.3.0
+wantprefixlen == 1 /prefix 1.2.3.0/24
+wantprefixlen == 2 /netmask 1.2.3.0/255.255.255.0
+wantprefixlen == 3 -lastip 1.2.3.0-1.2.3.255
+
+You can also change the defaults on an per-object basis by fiddeling with the class members
+
+NoPrefixForSingleIp
+WantPrefixLen
+
+ >>> IP('10.0.0.0/32').strNormal()
+ '10.0.0.0'
+ >>> IP('10.0.0.0/24').strNormal()
+ '10.0.0.0/24'
+ >>> IP('10.0.0.0/24').strNormal(0)
+ '10.0.0.0'
+ >>> IP('10.0.0.0/24').strNormal(1)
+ '10.0.0.0/24'
+ >>> IP('10.0.0.0/24').strNormal(2)
+ '10.0.0.0/255.255.255.0'
+ >>> IP('10.0.0.0/24').strNormal(3)
+ '10.0.0.0-10.0.0.255'
+ >>> ip = IP('10.0.0.0')
+ >>> print ip
+ 10.0.0.0
+ >>> ip.NoPrefixForSingleIp = None
+ >>> print ip
+ 10.0.0.0/32
+ >>> ip.WantPrefixLen = 3
+ >>> print ip
+ 10.0.0.0-10.0.0.0
+
+
+Further Information might be available at http://c0re.jp/c0de/IPy/
+
+Hacked 2001 by drt at un.bewaff.net
+
+TODO:
+ * better comparison (__cmp__ and friends)
+ * tests for __cmp__
+ * always write hex values lowercase
+ * interpret 2001:1234:5678:1234/64 as 2001:1234:5678:1234::/64
+ * move size in bits into class variables to get rid of some "if self._ipversion ..."
+ * support for base85 encoding
+ * support for output of IPv6 encoded IPv4 Addresses
+ * update address type tables
+ * first-last notation should be allowed for IPv6
+ * add IPv6 docstring examples
+ * check better for negative parameters
+ * add addition / aggregation
+ * move reverse name stuff out of the classes and refactor it
+ * support for aggregation of more than two nets at once
+ * support for aggregation with "holes"
+ * support for finding common prefix
+ * '>>' and '<<' for prefix manipulation
+ * add our own exceptions instead ValueError all the time
+ * rename checkPrefix to checkPrefixOk
+ * add more documentation and doctests
+ * refactor
+"""
+
+__rcsid__ = '$Id: IPy.py 671 2004-08-22 21:02:29Z md $'
+__version__ = '0.42'
+
+import types
+
+# Definition of the Ranges for IPv4 IPs
+# this should include www.iana.org/assignments/ipv4-address-space
+# and www.iana.org/assignments/multicast-addresses
+IPv4ranges = {
+ '0' : 'PUBLIC', # fall back
+ '00000000' : 'PRIVATE', # 0/8
+ '00001010' : 'PRIVATE', # 10/8
+ '01111111' : 'PRIVATE', # 127.0/8
+ '1' : 'PUBLIC', # fall back
+ '101011000001' : 'PRIVATE', # 172.16/12
+ '1100000010101000' : 'PRIVATE', # 192.168/16
+ '11011111' : 'RESERVED', # 223/8
+ '111' : 'RESERVED' # 224/3
+ }
+
+# Definition of the Ranges for IPv6 IPs
+# see also www.iana.org/assignments/ipv6-address-space,
+# www.iana.org/assignments/ipv6-tla-assignments,
+# www.iana.org/assignments/ipv6-multicast-addresses,
+# www.iana.org/assignments/ipv6-anycast-addresses
+IPv6ranges = {
+ '00000000' : 'RESERVED', # ::/8
+ '00000001' : 'UNASSIGNED', # 100::/8
+ '0000001' : 'NSAP', # 200::/7
+ '0000010' : 'IPX', # 400::/7
+ '0000011' : 'UNASSIGNED', # 600::/7
+ '00001' : 'UNASSIGNED', # 800::/5
+ '0001' : 'UNASSIGNED', # 1000::/4
+ '0010000000000000' : 'RESERVED', # 2000::/16 Reserved
+ '0010000000000001' : 'ASSIGNABLE', # 2001::/16 Sub-TLA Assignments [RFC2450]
+ '00100000000000010000000': 'ASSIGNABLE IANA', # 2001:0000::/29 - 2001:01F8::/29 IANA
+ '00100000000000010000001': 'ASSIGNABLE APNIC', # 2001:0200::/29 - 2001:03F8::/29 APNIC
+ '00100000000000010000010': 'ASSIGNABLE ARIN', # 2001:0400::/29 - 2001:05F8::/29 ARIN
+ '00100000000000010000011': 'ASSIGNABLE RIPE', # 2001:0600::/29 - 2001:07F8::/29 RIPE NCC
+ '0010000000000010' : '6TO4', # 2002::/16 "6to4" [RFC3056]
+ '0011111111111110' : '6BONE', # 3FFE::/16 6bone Testing [RFC2471]
+ '0011111111111111' : 'RESERVED', # 3FFF::/16 Reserved
+ '010' : 'GLOBAL-UNICAST', # 4000::/3
+ '011' : 'UNASSIGNED', # 6000::/3
+ '100' : 'GEO-UNICAST', # 8000::/3
+ '101' : 'UNASSIGNED', # A000::/3
+ '110' : 'UNASSIGNED', # C000::/3
+ '1110' : 'UNASSIGNED', # E000::/4
+ '11110' : 'UNASSIGNED', # F000::/5
+ '111110' : 'UNASSIGNED', # F800::/6
+ '1111110' : 'UNASSIGNED', # FC00::/7
+ '111111100' : 'UNASSIGNED', # FE00::/9
+ '1111111010' : 'LINKLOCAL', # FE80::/10
+ '1111111011' : 'SITELOCAL', # FEC0::/10
+ '11111111' : 'MULTICAST', # FF00::/8
+ '0' * 96 : 'IPV4COMP', # ::/96
+ '0' * 80 + '1' * 16 : 'IPV4MAP', # ::FFFF:0:0/96
+ '0' * 128 : 'UNSPECIFIED', # ::/128
+ '0' * 127 + '1' : 'LOOPBACK' # ::1/128
+ }
+
+
+class IPint:
+ """Handling of IP addresses returning integers.
+
+ Use class IP instead because some features are not implemented for
+ IPint."""
+
+ def __init__(self, data, ipversion = 0):
+ """Create an instance of an IP object.
+
+ Data can be a network specification or a single IP. IP
+ Addresses can be specified in all forms understood by
+ parseAddress.() the size of a network can be specified as
+
+ /prefixlen a.b.c.0/24 2001:658:22a:cafe::/64
+ -lastIP a.b.c.0-a.b.c.255 2001:658:22a:cafe::-2001:658:22a:cafe:ffff:ffff:ffff:ffff
+ /decimal netmask a.b.c.d/255.255.255.0 not supported for IPv6
+
+ If no size specification is given a size of 1 address (/32 for
+ IPv4 and /128 for IPv6) is assumed.
+
+ >>> print IP('127.0.0.0/8')
+ 127.0.0.0/8
+ >>> print IP('127.0.0.0/255.0.0.0')
+ 127.0.0.0/8
+ >>> print IP('127.0.0.0-127.255.255.255')
+ 127.0.0.0/8
+
+ See module documentation for more examples.
+ """
+
+ self.NoPrefixForSingleIp = 1 # Print no Prefixlen for /32 and /128
+ self.WantPrefixLen = None # Do we want prefix printed by default? see _printPrefix()
+
+ netbits = 0
+ prefixlen = -1
+
+ # handling of non string values in constructor
+ if type(data) == types.IntType or type(data) == types.LongType:
+ self.ip = long(data)
+ if ipversion == 0:
+ if self.ip < 0x100000000L:
+ ipversion = 4
+ else:
+ ipversion = 6
+ if ipversion == 4:
+ prefixlen = 32
+ elif ipversion == 6:
+ prefixlen = 128
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+ self._ipversion = ipversion
+ self._prefixlen = prefixlen
+ # handle IP instance as an parameter
+ elif isinstance(data, IPint):
+ self._ipversion = data._ipversion
+ self._prefixlen = data._prefixlen
+ self.ip = data.ip
+ else:
+ # TODO: refactor me!
+ # splitting of a string into IP and prefixlen et. al.
+ x = data.split('-')
+ if len(x) == 2:
+ # a.b.c.0-a.b.c.255 specification ?
+ (ip, last) = x
+ (self.ip, parsedVersion) = parseAddress(ip)
+ if parsedVersion != 4:
+ raise ValueError, "first-last notation only allowed for IPv4"
+ (last, lastversion) = parseAddress(last)
+ if lastversion != 4:
+ raise ValueError, "last address should be IPv4, too"
+ if last < self.ip:
+ raise ValueError, "last address should be larger than first"
+ size = last - self.ip
+ netbits = _count1Bits(size)
+ elif len(x) == 1:
+ x = data.split('/')
+ # if no prefix is given use defaults
+ if len(x) == 1:
+ ip = x[0]
+ prefixlen = -1
+ elif len(x) > 2:
+ raise ValueError, "only one '/' allowed in IP Address"
+ else:
+ (ip, prefixlen) = x
+ if prefixlen.find('.') != -1:
+ # check if the user might have used a netmask like
+ # a.b.c.d/255.255.255.0
+ (netmask, vers) = parseAddress(prefixlen)
+ if vers != 4:
+ raise ValueError, "netmask must be IPv4"
+ prefixlen = _netmaskToPrefixlen(netmask)
+ elif len(x) > 2:
+ raise ValueError, "only one '-' allowed in IP Address"
+ else:
+ raise ValueError, "can't parse"
+
+ (self.ip, parsedVersion) = parseAddress(ip)
+ if ipversion == 0:
+ ipversion = parsedVersion
+ if prefixlen == -1:
+ if ipversion == 4:
+ prefixlen = 32 - netbits
+ elif ipversion == 6:
+ prefixlen = 128 - netbits
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+ self._ipversion = ipversion
+ self._prefixlen = int(prefixlen)
+
+ if not _checkNetaddrWorksWithPrefixlen(self.ip, self._prefixlen, self._ipversion):
+ raise ValueError, "%s goes not well with prefixlen %d" % (hex(self.ip), self._prefixlen)
+
+
+ def int(self):
+ """Return the first / base / network addess as an (long) integer.
+
+ The same as IP[0].
+
+ >>> hex(IP('10.0.0.0/8').int())
+ '0xA000000L'
+ """
+ return self.ip
+
+ def version(self):
+ """Return the IP version of this Object.
+
+ >>> IP('10.0.0.0/8').version()
+ 4
+ >>> IP('::1').version()
+ 6
+ """
+ return self._ipversion
+
+ def prefixlen(self):
+ """Returns Network Prefixlen.
+
+ >>> IP('10.0.0.0/8').prefixlen()
+ 8
+ """
+ return self._prefixlen
+
+ def net(self):
+ """Return the base (first) address of a network as an (long) integer."""
+
+ return self.int()
+
+ def broadcast(self):
+ """Return the broadcast (last) address of a network as an (long) integer.
+
+ The same as IP[-1]."""
+ return self.int() + self.len() - 1
+
+ def _printPrefix(self, want):
+ """Prints Prefixlen/Netmask.
+
+ Not really. In fact it is our universal Netmask/Prefixlen printer.
+ This is considered an internel function.
+
+ want == 0 / None don't return anything 1.2.3.0
+ want == 1 /prefix 1.2.3.0/24
+ want == 2 /netmask 1.2.3.0/255.255.255.0
+ want == 3 -lastip 1.2.3.0-1.2.3.255
+ """
+
+ if (self._ipversion == 4 and self._prefixlen == 32) or \
+ (self._ipversion == 6 and self._prefixlen == 128):
+ if self.NoPrefixForSingleIp:
+ want = 0
+ if want == None:
+ want = self.WantPrefixLen
+ if want == None:
+ want = 1
+ if want:
+ if want == 2:
+ # this should work wit IP and IPint
+ netmask = self.netmask()
+ if type(netmask) != types.IntType and type(netmask) != types.LongType:
+ netmask = netmask.int()
+ return "/%s" % (intToIp(netmask, self._ipversion))
+ elif want == 3:
+ return "-%s" % (intToIp(self.ip + self.len() - 1, self._ipversion))
+ else:
+ # default
+ return "/%d" % (self._prefixlen)
+ else:
+ return ''
+
+ # We have different Favours to convert to:
+ # strFullsize 127.0.0.1 2001:0658:022a:cafe:0200:c0ff:fe8d:08fa
+ # strNormal 127.0.0.1 2001:658:22a:cafe:200:c0ff:fe8d:08fa
+ # strCompressed 127.0.0.1 2001:658:22a:cafe::1
+ # strHex 0x7F000001L 0x20010658022ACAFE0200C0FFFE8D08FA
+ # strDec 2130706433 42540616829182469433547974687817795834
+
+ def strBin(self, wantprefixlen = None):
+ """Return a string representation as a binary value.
+
+ >>> print IP('127.0.0.1').strBin()
+ 01111111000000000000000000000001
+ """
+
+
+ if self._ipversion == 4:
+ bits = 32
+ elif self._ipversion == 6:
+ bits = 128
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+ if self.WantPrefixLen == None and wantprefixlen == None:
+ wantprefixlen = 0
+ ret = _intToBin(self.ip)
+ return '0' * (bits - len(ret)) + ret + self._printPrefix(wantprefixlen)
+
+ def strCompressed(self, wantprefixlen = None):
+ """Return a string representation in compressed format using '::' Notation.
+
+ >>> print IP('127.0.0.1').strCompressed()
+ 127.0.0.1
+ >>> print IP('2001:0658:022a:cafe:0200::1').strCompressed()
+ 2001:658:22a:cafe:200::1
+ """
+
+ if self.WantPrefixLen == None and wantprefixlen == None:
+ wantprefixlen = 1
+
+ if self._ipversion == 4:
+ return self.strFullsize(wantprefixlen)
+ else:
+ # find the longest sequence of '0'
+ hextets = [int(x, 16) for x in self.strFullsize(0).split(':')]
+ # every element of followingzeros will contain the number of zeros
+ # following the corrospondending element of hextetes
+ followingzeros = [0] * 8
+ for i in range(len(hextets)):
+ followingzeros[i] = _countFollowingZeros(hextets[i:])
+ # compressionpos is the position where we can start removing zeros
+ compressionpos = followingzeros.index(max(followingzeros))
+ if max(followingzeros) > 1:
+ # genererate string with the longest number of zeros cut out
+ # now we need hextets as strings
+ hextets = [x for x in self.strNormal(0).split(':')]
+ while compressionpos < len(hextets) and hextets[compressionpos] == '0':
+ del(hextets[compressionpos])
+ hextets.insert(compressionpos, '')
+ if compressionpos + 1 >= len(hextets):
+ hextets.append('')
+ if compressionpos == 0:
+ hextets = [''] + hextets
+ return ':'.join(hextets) + self._printPrefix(wantprefixlen)
+ else:
+ return self.strNormal() + self._printPrefix(wantprefixlen)
+
+ def strNormal(self, wantprefixlen = None):
+ """Return a string representation in the usual format.
+
+ >>> print IP('127.0.0.1').strNormal()
+ 127.0.0.1
+ >>> print IP('2001:0658:022a:cafe:0200::1').strNormal()
+ 2001:658:22a:cafe:200:0:0:1
+ """
+
+ if self.WantPrefixLen == None and wantprefixlen == None:
+ wantprefixlen = 1
+
+ if self._ipversion == 4:
+ ret = self.strFullsize(0)
+ elif self._ipversion == 6:
+ ret = ':'.join([hex(x)[2:] for x in [int(x, 16) for x in self.strFullsize(0).split(':')]])
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+
+
+ return ret + self._printPrefix(wantprefixlen)
+
+ def strFullsize(self, wantprefixlen = None):
+ """Return a string representation in the non mangled format.
+
+ >>> print IP('127.0.0.1').strFullsize()
+ 127.0.0.1
+ >>> print IP('2001:0658:022a:cafe:0200::1').strFullsize()
+ 2001:0658:022a:cafe:0200:0000:0000:0001
+ """
+
+ if self.WantPrefixLen == None and wantprefixlen == None:
+ wantprefixlen = 1
+
+ return intToIp(self.ip, self._ipversion).lower() + self._printPrefix(wantprefixlen)
+
+ def strHex(self, wantprefixlen = None):
+ """Return a string representation in hex format.
+
+ >>> print IP('127.0.0.1').strHex()
+ 0x7F000001
+ >>> print IP('2001:0658:022a:cafe:0200::1').strHex()
+ 0x20010658022ACAFE0200000000000001
+ """
+
+ if self.WantPrefixLen == None and wantprefixlen == None:
+ wantprefixlen = 0
+
+ x = hex(self.ip)
+ if x[-1] == 'L':
+ x = x[:-1]
+ return x + self._printPrefix(wantprefixlen)
+
+ def strDec(self, wantprefixlen = None):
+ """Return a string representation in decimal format.
+
+ >>> print IP('127.0.0.1').strDec()
+ 2130706433
+ >>> print IP('2001:0658:022a:cafe:0200::1').strDec()
+ 42540616829182469433547762482097946625
+ """
+
+ if self.WantPrefixLen == None and wantprefixlen == None:
+ wantprefixlen = 0
+
+ x = str(self.ip)
+ if x[-1] == 'L':
+ x = x[:-1]
+ return x + self._printPrefix(wantprefixlen)
+
+ def iptype(self):
+ """Return a description of the IP type ('PRIVATE', 'RESERVERD', etc).
+
+ >>> print IP('127.0.0.1').iptype()
+ PRIVATE
+ >>> print IP('192.168.1.1').iptype()
+ PRIVATE
+ >>> print IP('195.185.1.2').iptype()
+ PUBLIC
+ >>> print IP('::1').iptype()
+ LOOPBACK
+ >>> print IP('2001:0658:022a:cafe:0200::1').iptype()
+ ASSIGNABLE RIPE
+
+ The type information for IPv6 is out of sync with reality.
+ """
+
+ # this could be greatly improved
+
+ if self._ipversion == 4:
+ iprange = IPv4ranges
+ elif self._ipversion == 6:
+ iprange = IPv6ranges
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+ bits = self.strBin()
+ for i in range(len(bits), 0, -1):
+ if iprange.has_key(bits[:i]):
+ return iprange[bits[:i]]
+ return "unknown"
+
+
+ def netmask(self):
+ """Return netmask as an integer.
+
+ >>> print hex(IP('195.185.0.0/16').netmask().int())
+ 0xFFFF0000L
+ """
+
+ # TODO: unify with prefixlenToNetmask?
+ if self._ipversion == 4:
+ locallen = 32 - self._prefixlen
+ elif self._ipversion == 6:
+ locallen = 128 - self._prefixlen
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+ return ((2L ** self._prefixlen) - 1) << locallen
+
+
+ def strNetmask(self):
+ """Return netmask as an string. Mostly useful for IPv6.
+
+ >>> print IP('195.185.0.0/16').strNetmask()
+ 255.255.0.0
+ >>> print IP('2001:0658:022a:cafe::0/64').strNetmask()
+ /64
+ """
+
+ # TODO: unify with prefixlenToNetmask?
+ if self._ipversion == 4:
+ locallen = 32 - self._prefixlen
+ return intToIp(((2L ** self._prefixlen) - 1) << locallen, 4)
+ elif self._ipversion == 6:
+ locallen = 128 - self._prefixlen
+ return "/%d" % self._prefixlen
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+ def len(self):
+ """Return the length of an subnet.
+
+ >>> print IP('195.185.1.0/28').len()
+ 16
+ >>> print IP('195.185.1.0/24').len()
+ 256
+ """
+
+ if self._ipversion == 4:
+ locallen = 32 - self._prefixlen
+ elif self._ipversion == 6:
+ locallen = 128 - self._prefixlen
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+ return 2L ** locallen
+
+
+ def __len__(self):
+ """Return the length of an subnet.
+
+ Called to implement the built-in function len().
+ It breaks with IPv6 Networks. Anybody knows how to fix this."""
+
+ # Python < 2.2 has this silly restriction which breaks IPv6
+ # how about Python >= 2.2 ... ouch - it presists!
+
+ return int(self.len())
+
+
+ def __getitem__(self, key):
+ """Called to implement evaluation of self[key].
+
+ >>> ip=IP('127.0.0.0/30')
+ >>> for x in ip:
+ ... print hex(x.int())
+ ...
+ 0x7F000000L
+ 0x7F000001L
+ 0x7F000002L
+ 0x7F000003L
+ >>> hex(ip[2].int())
+ '0x7F000002L'
+ >>> hex(ip[-1].int())
+ '0x7F000003L'
+ """
+
+ if type(key) != types.IntType and type(key) != types.LongType:
+ raise TypeError
+ if abs(key) >= self.len():
+ raise IndexError
+ if key < 0:
+ key = self.len() - abs(key)
+
+ return self.ip + long(key)
+
+
+
+ def __contains__(self, item):
+ """Called to implement membership test operators.
+
+ Should return true if item is in self, false otherwise. Item
+ can be other IP-objects, strings or ints.
+
+ >>> print IP('195.185.1.1').strHex()
+ 0xC3B90101
+ >>> 0xC3B90101L in IP('195.185.1.0/24')
+ 1
+ >>> '127.0.0.1' in IP('127.0.0.0/24')
+ 1
+ >>> IP('127.0.0.0/24') in IP('127.0.0.0/25')
+ 0
+ """
+
+ item = IP(item)
+ if item.ip >= self.ip and item.ip < self.ip + self.len() - item.len() + 1:
+ return 1
+ else:
+ return 0
+
+
+ def overlaps(self, item):
+ """Check if two IP address ranges overlap.
+
+ Returns 0 if the two ranged don't overlap, 1 if the given
+ range overlaps at the end and -1 if it does at the beginning.
+
+ >>> IP('192.168.0.0/23').overlaps('192.168.1.0/24')
+ 1
+ >>> IP('192.168.0.0/23').overlaps('192.168.1.255')
+ 1
+ >>> IP('192.168.0.0/23').overlaps('192.168.2.0')
+ 0
+ >>> IP('192.168.1.0/24').overlaps('192.168.0.0/23')
+ -1
+ """
+
+ item = IP(item)
+ if item.ip >= self.ip and item.ip < self.ip + self.len():
+ return 1
+ elif self.ip >= item.ip and self.ip < item.ip + item.len():
+ return -1
+ else:
+ return 0
+
+
+ def __str__(self):
+ """Dispatch to the prefered String Representation.
+
+ Used to implement str(IP)."""
+
+ return self.strFullsize()
+
+
+ def __repr__(self):
+ """Print a representation of the Object.
+
+ Used to implement repr(IP). Returns a string which evaluates
+ to an identical Object (without the wnatprefixlen stuff - see
+ module docstring.
+
+ >>> print repr(IP('10.0.0.0/24'))
+ IP('10.0.0.0/24')
+ """
+
+ return("IPint('%s')" % (self.strCompressed(1)))
+
+
+ def __cmp__(self, other):
+ """Called by comparison operations.
+
+ Should return a negative integer if self < other, zero if self
+ == other, a positive integer if self > other.
+
+ Networks with different prefixlen are considered non-equal.
+ Networks with the same prefixlen and differing addresses are
+ considered non equal but are compared by thair base address
+ integer value to aid sorting of IP objects.
+
+ The Version of Objects is not put into consideration.
+
+ >>> IP('10.0.0.0/24') > IP('10.0.0.0')
+ 1
+ >>> IP('10.0.0.0/24') < IP('10.0.0.0')
+ 0
+ >>> IP('10.0.0.0/24') < IP('12.0.0.0/24')
+ 1
+ >>> IP('10.0.0.0/24') > IP('12.0.0.0/24')
+ 0
+
+ """
+
+ # Im not really sure if this is "the right thing to do"
+ if self._prefixlen < other.prefixlen():
+ return (other.prefixlen() - self._prefixlen)
+ elif self._prefixlen > other.prefixlen():
+
+ # Fixed bySamuel Krempp <krempp at crans.ens-cachan.fr>:
+
+ # The bug is quite obvious really (as 99% bugs are once
+ # spotted, isn't it ? ;-) Because of precedence of
+ # multiplication by -1 over the substraction, prefixlen
+ # differences were causing the __cmp__ function to always
+ # return positive numbers, thus the function was failing
+ # the basic assumptions for a __cmp__ function.
+
+ # Namely we could have (a > b AND b > a), when the
+ # prefixlen of a and b are different. (eg let
+ # a=IP("1.0.0.0/24"); b=IP("2.0.0.0/16");) thus, anything
+ # could happen when launching a sort algorithm..
+ # everything's in order with the trivial, attached patch.
+
+ return (self._prefixlen - other.prefixlen()) * -1
+ else:
+ if self.ip < other.ip:
+ return -1
+ elif self.ip > other.ip:
+ return 1
+ else:
+ return 0
+
+
+ def __hash__(self):
+ """Called for the key object for dictionary operations, and by
+ the built-in function hash() Should return a 32-bit integer
+ usable as a hash value for dictionary operations. The only
+ required property is that objects which compare equal have the
+ same hash value
+
+ >>> hex(IP('10.0.0.0/24').__hash__())
+ '0xf5ffffe7'
+ """
+
+ thehash = int(-1)
+ ip = self.ip
+ while ip > 0:
+ thehash = thehash ^ (ip & 0x7fffffff)
+ ip = ip >> 32
+ thehash = thehash ^ self._prefixlen
+ return int(thehash)
+
+
+class IP(IPint):
+ """Class for handling IP Addresses and Networks."""
+
+ def net(self):
+ """Return the base (first) address of a network as an IP object.
+
+ The same as IP[0].
+
+ >>> IP('10.0.0.0/8').net()
+ IP('10.0.0.0')
+ """
+ return IP(IPint.net(self))
+
+ def broadcast(self):
+ """Return the broadcast (last) address of a network as an IP object.
+
+ The same as IP[-1].
+
+ >>> IP('10.0.0.0/8').broadcast()
+ IP('10.255.255.255')
+ """
+ return IP(IPint.broadcast(self))
+
+ def netmask(self):
+ """Return netmask as an IP object.
+
+ >>> IP('10.0.0.0/8').netmask()
+ IP('255.0.0.0')
+ """
+ return IP(IPint.netmask(self))
+
+
+ def reverseNames(self):
+ """Return a list with values forming the reverse lookup.
+
+ >>> IP('213.221.113.87/32').reverseNames()
+ ['87.113.221.213.in-addr.arpa.']
+ >>> IP('213.221.112.224/30').reverseNames()
+ ['224.112.221.213.in-addr.arpa.', '225.112.221.213.in-addr.arpa.', '226.112.221.213.in-addr.arpa.', '227.112.221.213.in-addr.arpa.']
+ >>> IP('127.0.0.0/24').reverseNames()
+ ['0.0.127.in-addr.arpa.']
+ >>> IP('127.0.0.0/23').reverseNames()
+ ['0.0.127.in-addr.arpa.', '1.0.127.in-addr.arpa.']
+ >>> IP('127.0.0.0/16').reverseNames()
+ ['0.127.in-addr.arpa.']
+ >>> IP('127.0.0.0/15').reverseNames()
+ ['0.127.in-addr.arpa.', '1.127.in-addr.arpa.']
+ >>> IP('128.0.0.0/8').reverseNames()
+ ['128.in-addr.arpa.']
+ >>> IP('128.0.0.0/7').reverseNames()
+ ['128.in-addr.arpa.', '129.in-addr.arpa.']
+
+ """
+
+ if self._ipversion == 4:
+ ret =[]
+ # TODO: Refactor. Add support for IPint objects
+ if self.len() < 2**8:
+ for x in self:
+ ret.append(x.reverseName())
+ elif self.len() < 2**16L:
+ for i in range(0, self.len(), 2**8):
+ ret.append(self[i].reverseName()[2:])
+ elif self.len() < 2**24L:
+ for i in range(0, self.len(), 2**16):
+ ret.append(self[i].reverseName()[4:])
+ else:
+ for i in range(0, self.len(), 2**24):
+ ret.append(self[i].reverseName()[6:])
+ return ret
+ elif self._ipversion == 6:
+ s = hex(self.ip)[2:].lower()
+ if s[-1] == 'l':
+ s = s[:-1]
+ if self._prefixlen % 4 != 0:
+ raise NotImplementedError, "can't create IPv6 reverse names at sub nibble level"
+ s = list(s)
+ s.reverse()
+ s = '.'.join(s)
+ first_nibble_index = int(32 - (self._prefixlen / 4)) * 2
+ return ["%s.ip6.int." % s[first_nibble_index:]]
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+
+
+ def reverseName(self):
+ """Return the value for reverse lookup/PTR records as RfC 2317 look alike.
+
+ RfC 2317 is an ugly hack which only works for sub-/24 e.g. not
+ for /23. Do not use it. Better set up a Zone for every
+ address. See reverseName for a way to arcive that.
+
+ >>> print IP('195.185.1.1').reverseName()
+ 1.1.185.195.in-addr.arpa.
+ >>> print IP('195.185.1.0/28').reverseName()
+ 0-15.1.185.195.in-addr.arpa.
+ """
+
+ if self._ipversion == 4:
+ s = self.strFullsize(0)
+ s = s.split('.')
+ s.reverse()
+ first_byte_index = int(4 - (self._prefixlen / 8))
+ if self._prefixlen % 8 != 0:
+ nibblepart = "%s-%s" % (s[3-(self._prefixlen / 8)], intToIp(self.ip + self.len() - 1, 4).split('.')[-1])
+ if nibblepart[-1] == 'l':
+ nibblepart = nibblepart[:-1]
+ nibblepart += '.'
+ else:
+ nibblepart = ""
+
+ s = '.'.join(s[first_byte_index:])
+ return "%s%s.in-addr.arpa." % (nibblepart, s)
+
+ elif self._ipversion == 6:
+ s = hex(self.ip)[2:].lower()
+ if s[-1] == 'l':
+ s = s[:-1]
+ if self._prefixlen % 4 != 0:
+ nibblepart = "%s-%s" % (s[self._prefixlen:], hex(self.ip + self.len() - 1)[2:].lower())
+ if nibblepart[-1] == 'l':
+ nibblepart = nibblepart[:-1]
+ nibblepart += '.'
+ else:
+ nibblepart = ""
+ s = list(s)
+ s.reverse()
+ s = '.'.join(s)
+ first_nibble_index = int(32 - (self._prefixlen / 4)) * 2
+ return "%s%s.ip6.int." % (nibblepart, s[first_nibble_index:])
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+ def __getitem__(self, key):
+ """Called to implement evaluation of self[key].
+
+ >>> ip=IP('127.0.0.0/30')
+ >>> for x in ip:
+ ... print str(x)
+ ...
+ 127.0.0.0
+ 127.0.0.1
+ 127.0.0.2
+ 127.0.0.3
+ >>> print str(ip[2])
+ 127.0.0.2
+ >>> print str(ip[-1])
+ 127.0.0.3
+ """
+ return IP(IPint.__getitem__(self, key))
+
+ def __repr__(self):
+ """Print a representation of the Object.
+
+ >>> IP('10.0.0.0/8')
+ IP('10.0.0.0/8')
+ """
+
+ return("IP('%s')" % (self.strCompressed(1)))
+
+ def __add__(self, other):
+ """Emulate numeric objects through network aggregation"""
+ if self.prefixlen() != other.prefixlen():
+ raise ValueError, "Only networks with the same prefixlen can be added."
+ if self.prefixlen < 1:
+ raise ValueError, "Networks with a prefixlen longer than /1 can't be added."
+ if self.version() != other.version():
+ raise ValueError, "Only networks with the same IP version can be added."
+ if self > other:
+ # fixed by Skinny Puppy <skin_pup-IPy at happypoo.com>
+ return other.__add__(self)
+ else:
+ ret = IP(self.int())
+ ret._prefixlen = self.prefixlen() - 1
+ return ret
+
+def parseAddress(ipstr):
+ """Parse a string and return the corrospondending IPaddress and the a guess of the IP version.
+
+ Following Forms ar recorgnized:
+ 0x0123456789abcdef # IPv4 if <= 0xffffffff else IPv6
+ 123.123.123.123 # IPv4
+ 123.123 # 0-padded IPv4
+ 1080:0000:0000:0000:0008:0800:200C:417A
+ 1080:0:0:0:8:800:200C:417A
+ 1080:0::8:800:200C:417A
+ ::1
+ ::
+ 0:0:0:0:0:FFFF:129.144.52.38
+ ::13.1.68.3
+ ::FFFF:129.144.52.38
+ """
+
+ # TODO: refactor me!
+ if ipstr.startswith('0x'):
+ ret = long(ipstr[2:], 16)
+ if ret > 0xffffffffffffffffffffffffffffffffL:
+ raise ValueError, "%r: IP Address can't be bigger than 2^128" % (ipstr)
+ if ret < 0x100000000L:
+ return (ret, 4)
+ else:
+ return (ret, 6)
+
+ if ipstr.find(':') != -1:
+ # assume IPv6
+ if ipstr.find(':::') != -1:
+ raise ValueError, "%r: IPv6 Address can't contain ':::'" % (ipstr)
+ hextets = ipstr.split(':')
+ if ipstr.find('.') != -1:
+ # this might be a mixed address like '0:0:0:0:0:0:13.1.68.3'
+ (v4, foo) = parseAddress(hextets[-1])
+ assert foo == 4
+ del(hextets[-1])
+ hextets.append(hex(v4 >> 16)[2:-1])
+ hextets.append(hex(v4 & 0xffff)[2:-1])
+ if len(hextets) > 8:
+ raise ValueError, "%r: IPv6 Address with more than 8 hexletts" % (ipstr)
+ if len(hextets) < 8:
+ if '' not in hextets:
+ raise ValueError, "%r IPv6 Address with less than 8 hexletts and without '::'" % (ipstr)
+ # catch :: at the beginning or end
+ if hextets.index('') < len(hextets) - 1 and hextets[hextets.index('')+1] == '':
+ hextets.remove('')
+ # catch '::'
+ if hextets.index('') < len(hextets) - 1 and hextets[hextets.index('')+1] == '':
+ hextets.remove('')
+
+ for foo in range(9-len(hextets)):
+ hextets.insert(hextets.index(''), '0')
+ hextets.remove('')
+ if '' in hextets:
+ raise ValueError, "%r IPv6 Address may contain '::' only once" % (ipstr)
+ if '' in hextets:
+ raise ValueError, "%r IPv6 Address may contain '::' only if it has less than 8 hextets" % (ipstr)
+ num = ''
+ for x in hextets:
+ if len(x) < 4:
+ x = ((4 - len(x)) * '0') + x
+ if int(x, 16) < 0 or int(x, 16) > 0xffff:
+ raise ValueError, "%r: single hextet must be 0 <= hextet <= 0xffff which isn't true for %s" % (ipstr, x)
+ num += x
+ return (long(num, 16), 6)
+
+ elif len(ipstr) == 32:
+ # assume IPv6 in pure hexadecimal notation
+ return (long(ipstr, 16), 6)
+
+ elif ipstr.find('.') != -1 or (len(ipstr) < 4 and int(ipstr) < 256):
+ # assume IPv4 ('127' gets interpreted as '127.0.0.0')
+ bytes = ipstr.split('.')
+ if len(bytes) > 4:
+ raise ValueError, "IPv4 Address with more than 4 bytes"
+ bytes += ['0'] * (4 - len(bytes))
+ bytes = [long(x) for x in bytes]
+ for x in bytes:
+ if x > 255 or x < 0:
+ raise ValueError, "%r: single byte must be 0 <= byte < 256" % (ipstr)
+ return ((bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3], 4)
+
+ else:
+ # we try to interprete it as a decimal digit -
+ # this ony works for numbers > 255 ... others
+ # will be interpreted as IPv4 first byte
+ ret = long(ipstr)
+ if ret > 0xffffffffffffffffffffffffffffffffL:
+ raise ValueError, "IP Address cant be bigger than 2^128"
+ if ret <= 0xffffffffL:
+ return (ret, 4)
+ else:
+ return (ret, 6)
+
+
+def intToIp(ip, version):
+ """Transform an integer string into an IP address."""
+
+ # just to be sure and hoping for Python 2.22
+ ip = long(ip)
+
+ if ip < 0:
+ raise ValueError, "IPs can't be negative: %d" % (ip)
+
+ ret = ''
+ if version == 4:
+ if ip > 0xffffffffL:
+ raise ValueError, "IPv4 Addresses can't be larger than 0xffffffff: %s" % (hex(ip))
+ for l in range(4):
+ ret = str(ip & 0xffL) + '.' + ret
+ ip = ip >> 8;
+ ret = ret[:-1]
+ elif version == 6:
+ if ip > 0xffffffffffffffffffffffffffffffffL:
+ raise ValueError, "IPv6 Addresses can't be larger than 0xffffffffffffffffffffffffffffffff: %s" % (hex(ip))
+ l = '0' * 32 + hex(ip)[2:-1]
+ for x in range(1,33):
+ ret = l[-x] + ret
+ if x % 4 == 0:
+ ret = ':' + ret
+ ret = ret[1:]
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+ return ret;
+
+def _ipVersionToLen(version):
+ """Return number of bits in address for a certain IP version.
+
+ >>> _ipVersionToLen(4)
+ 32
+ >>> _ipVersionToLen(6)
+ 128
+ >>> _ipVersionToLen(5)
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "IPy.py", line 1076, in _ipVersionToLen
+ raise ValueError, "only IPv4 and IPv6 supported"
+ ValueError: only IPv4 and IPv6 supported
+ """
+
+ if version == 4:
+ return 32
+ elif version == 6:
+ return 128
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+
+def _countFollowingZeros(l):
+ """Return Nr. of elements containing 0 at the beginning th the list."""
+ if len(l) == 0:
+ return 0
+ elif l[0] != 0:
+ return 0
+ else:
+ return 1 + _countFollowingZeros(l[1:])
+
+
+_BitTable = {'0': '0000', '1': '0001', '2': '0010', '3': '0011',
+ '4': '0100', '5': '0101', '6': '0110', '7': '0111',
+ '8': '1000', '9': '1001', 'a': '1010', 'b': '1011',
+ 'c': '1100', 'd': '1101', 'e': '1110', 'f': '1111'}
+
+def _intToBin(val):
+ """Return the binary representation of an integer as string."""
+
+ if val < 0:
+ raise ValueError, "Only positive Values allowed"
+ s = hex(val).lower()
+ ret = ''
+ if s[-1] == 'l':
+ s = s[:-1]
+ for x in s[2:]:
+ if __debug__:
+ if not _BitTable.has_key(x):
+ raise AssertionError, "hex() returned strange result"
+ ret += _BitTable[x]
+ # remove leading zeros
+ while ret[0] == '0' and len(ret) > 1:
+ ret = ret[1:]
+ return ret
+
+def _count1Bits(num):
+ """Find the highest bit set to 1 in an integer."""
+ ret = 0
+ while num > 0:
+ num = num >> 1
+ ret += 1
+ return ret
+
+def _count0Bits(num):
+ """Find the highest bit set to 0 in an integer."""
+
+ # this could be so easy if _count1Bits(~long(num)) would work as excepted
+ num = long(num)
+ if num < 0:
+ raise ValueError, "Only positive Numbers please: %s" % (num)
+ ret = 0
+ while num > 0:
+ if num & 1 == 1:
+ break
+ num = num >> 1
+ ret += 1
+ return ret
+
+
+def _checkPrefix(ip, prefixlen, version):
+ """Check the validity of a prefix
+
+ Checks if the variant part of a prefix only has 0s, and the length is
+ correct.
+
+ >>> _checkPrefix(0x7f000000L, 24, 4)
+ 1
+ >>> _checkPrefix(0x7f000001L, 24, 4)
+ 0
+ >>> repr(_checkPrefix(0x7f000001L, -1, 4))
+ 'None'
+ >>> repr(_checkPrefix(0x7f000001L, 33, 4))
+ 'None'
+ """
+
+ # TODO: unify this v4/v6/invalid code in a function
+ bits = _ipVersionToLen(version)
+
+ if prefixlen < 0 or prefixlen > bits:
+ return None
+
+ if ip == 0:
+ zbits = bits + 1
+ else:
+ zbits = _count0Bits(ip)
+ if zbits < bits - prefixlen:
+ return 0
+ else:
+ return 1
+
+
+def _checkNetmask(netmask, masklen):
+ """Checks if a netmask is expressable as e prefixlen."""
+
+ num = long(netmask)
+ bits = masklen
+
+ # remove zero bits at the end
+ while (num & 1) == 0:
+ num = num >> 1
+ bits -= 1
+ if bits == 0:
+ break
+ # now check if the rest consists only of ones
+ while bits > 0:
+ if (num & 1) == 0:
+ raise ValueError, "Netmask %s can't be expressed as an prefix." % (hex(netmask))
+ num = num >> 1
+ bits -= 1
+
+
+def _checkNetaddrWorksWithPrefixlen(net, prefixlen, version):
+ """Check if a base addess of e network is compatible with a prefixlen"""
+ if net & _prefixlenToNetmask(prefixlen, version) == net:
+ return 1
+ else:
+ return 0
+
+
+def _netmaskToPrefixlen(netmask):
+ """Convert an Integer reprsenting a Netmask to an prefixlen.
+
+ E.g. 0xffffff00 (255.255.255.0) returns 24
+ """
+
+ netlen = _count0Bits(netmask)
+ masklen = _count1Bits(netmask)
+ _checkNetmask(netmask, masklen)
+ return masklen - netlen
+
+
+def _prefixlenToNetmask(prefixlen, version):
+ """Return a mask of n bits as a long integer.
+
+ From 'IP address conversion functions with the builtin socket module' by Alex Martelli
+ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66517
+ """
+ if prefixlen == 0:
+ return 0
+ elif prefixlen < 0:
+ raise ValueError, "Prefixlen must be > 0"
+ return ((2L<<prefixlen-1)-1) << (_ipVersionToLen(version) - prefixlen)
+
+
+def _test():
+ import doctest, IPy
+ return doctest.testmod(IPy)
+
+if __name__ == "__main__":
+ _test()
+
+ t = [0xf0, 0xf00, 0xff00, 0xffff00, 0xffffff00L]
+ o = []
+ for x in t:
+ pass
+ x = 0L
Added: packages/ipy/branches/upstream/current/MANIFEST.in
URL: http://svn.debian.org/wsvn/python-modules/packages/ipy/branches/upstream/current/MANIFEST.in?rev=371&op=file
==============================================================================
--- packages/ipy/branches/upstream/current/MANIFEST.in (added)
+++ packages/ipy/branches/upstream/current/MANIFEST.in Mon May 1 12:03:34 2006
@@ -1,0 +1,1 @@
+recursive-include example
Added: packages/ipy/branches/upstream/current/README
URL: http://svn.debian.org/wsvn/python-modules/packages/ipy/branches/upstream/current/README?rev=371&op=file
==============================================================================
--- packages/ipy/branches/upstream/current/README (added)
+++ packages/ipy/branches/upstream/current/README Mon May 1 12:03:34 2006
@@ -1,0 +1,75 @@
+IPy is a Python module for handling IPv4 and IPv6 Addresses and Networks in
+a fashion similar to perl's Net::IP and friends. The IP class allows a
+comfortable parsing and handling for most notations in use for IPv4 and IPv6
+Addresses and Networks.
+
+It enables code like this:
+
+ >>> ip = IP('127.0.0.0/30')
+ >>> for x in ip:
+ ... print x
+ ...
+ 127.0.0.0
+ 127.0.0.1
+ 127.0.0.2
+ 127.0.0.3
+ >>> ip2 = IP('0x7f000000/30')
+ >>> ip == ip2
+ 1
+ >>> ip.reverseNames()
+ ['0.0.0.127.in-addr.arpa.', '1.0.0.127.in-addr.arpa.',
+ '2.0.0.127.in-addr.arpa.', '3.0.0.127.in-addr.arpa.']
+ >>> ip.reverseName()
+ '0-3.0.0.127.in-addr.arpa.'
+ >>> ip.iptype()
+ 'RESERVED'
+
+It can detect about a dozen different ways of expressing IP addresses and
+networks, parse them and distinguish between IPv4 and IPv6 addresses.
+
+ >>> IP('10.0.0.0/8').version()
+ 4
+ >>> IP('::1').version()
+ 6
+ >>> print IP(0x7f000001)
+ 127.0.0.1
+ >>> print IP('0x7f000001')
+ 127.0.0.1
+ >>> print IP('127.0.0.1')
+ 127.0.0.1
+ >>> print IP('10')
+ 10.0.0.0
+ >>> print IP('1080:0:0:0:8:800:200C:417A')
+ 1080:0000:0000:0000:0008:0800:200c:417a
+ >>> print IP('1080::8:800:200C:417A')
+ 1080:0000:0000:0000:0008:0800:200c:417a
+ >>> print IP('::1')
+ 0000:0000:0000:0000:0000:0000:0000:0001
+ >>> print IP('::13.1.68.3')
+ 0000:0000:0000:0000:0000:0000:0d01:4403
+ >>> print IP('127.0.0.0/8')
+ 127.0.0.0/8
+ >>> print IP('127.0.0.0/255.0.0.0')
+ 127.0.0.0/8
+ >>> print IP('127.0.0.0-127.255.255.255')
+ 127.0.0.0/8
+ >>> IP('10.0.0.0/24').strNormal(q)
+ '10.0.0.0/24'
+ >>> IP('10.0.0.0/24').strNormal(2)
+ '10.0.0.0/255.255.255.0'
+ >>> IP('10.0.0.0/24').strNormal(3)
+ '10.0.0.0-10.0.0.255'
+
+To install type
+
+$ python setup.py install
+$ pydoc -w IPy
+$ cd test
+$ python test_IPy.py
+
+Read IPy.html for usage Instructions.
+
+IPy has been tested with python 2.2 and pyton 2.1.
+
+Further Information can be found at http://c0re.jp/c0de/IPy/
+ --drt at un.bewaff.net
Added: packages/ipy/branches/upstream/current/THANKS
URL: http://svn.debian.org/wsvn/python-modules/packages/ipy/branches/upstream/current/THANKS?rev=371&op=file
==============================================================================
--- packages/ipy/branches/upstream/current/THANKS (added)
+++ packages/ipy/branches/upstream/current/THANKS Mon May 1 12:03:34 2006
@@ -1,0 +1,4 @@
+Mark Johnston - bringing IPy to Python 2.3
+Shell, Hin-lik Hung - bring IPy to OpenBSD ports
+Samuel Krempp - fix __cmp__() bug
+Skinny Puppy - __add__() now always returns a value
Added: packages/ipy/branches/upstream/current/build/lib/IPy.py
URL: http://svn.debian.org/wsvn/python-modules/packages/ipy/branches/upstream/current/build/lib/IPy.py?rev=371&op=file
==============================================================================
--- packages/ipy/branches/upstream/current/build/lib/IPy.py (added)
+++ packages/ipy/branches/upstream/current/build/lib/IPy.py Mon May 1 12:03:34 2006
@@ -1,0 +1,1262 @@
+""" IPy - class and tools for handling of IPv4 and IPv6 Addresses and Networks.
+
+$Id: IPy.py,v 1.10 2002/05/16 05:22:26 drt Exp $
+
+The IP class allows a comfortable parsing and handling for most
+notations in use for IPv4 and IPv6 Addresses and Networks. It was
+greatly inspired bei RIPE's Perl module NET::IP's interface but
+doesn't share the Implementation. It doesn't share non-CIDR netmasks,
+so funky stuff lixe a netmask 0xffffff0f can't be done here.
+
+ >>> ip = IP('127.0.0.0/30')
+ >>> for x in ip:
+ ... print x
+ ...
+ 127.0.0.0
+ 127.0.0.1
+ 127.0.0.2
+ 127.0.0.3
+ >>> ip2 = IP('0x7f000000/30')
+ >>> ip == ip2
+ 1
+ >>> ip.reverseNames()
+ ['0.0.0.127.in-addr.arpa.', '1.0.0.127.in-addr.arpa.', '2.0.0.127.in-addr.arpa.', '3.0.0.127.in-addr.arpa.']
+ >>> ip.reverseName()
+ '0-3.0.0.127.in-addr.arpa.'
+ >>> ip.iptype()
+ 'PRIVATE'
+
+It can detect about a dozen different ways of expressing IP addresses
+and networks, parse them and distinguish between IPv4 and IPv6 addresses.
+
+ >>> IP('10.0.0.0/8').version()
+ 4
+ >>> IP('::1').version()
+ 6
+ >>> print IP(0x7f000001)
+ 127.0.0.1
+ >>> print IP('0x7f000001')
+ 127.0.0.1
+ >>> print IP('127.0.0.1')
+ 127.0.0.1
+ >>> print IP('10')
+ 10.0.0.0
+ >>> print IP('1080:0:0:0:8:800:200C:417A')
+ 1080:0000:0000:0000:0008:0800:200c:417a
+ >>> print IP('1080::8:800:200C:417A')
+ 1080:0000:0000:0000:0008:0800:200c:417a
+ >>> print IP('::1')
+ 0000:0000:0000:0000:0000:0000:0000:0001
+ >>> print IP('::13.1.68.3')
+ 0000:0000:0000:0000:0000:0000:0d01:4403
+ >>> print IP('127.0.0.0/8')
+ 127.0.0.0/8
+ >>> print IP('127.0.0.0/255.0.0.0')
+ 127.0.0.0/8
+ >>> print IP('127.0.0.0-127.255.255.255')
+ 127.0.0.0/8
+
+Nearly all class methods which return a string have an optional
+parameter 'wantprefixlen' which controlles if the prefixlen or netmask
+is printed. Per default the prefilen is always shown if the net
+contains more than one address.
+
+wantprefixlen == 0 / None don't return anything 1.2.3.0
+wantprefixlen == 1 /prefix 1.2.3.0/24
+wantprefixlen == 2 /netmask 1.2.3.0/255.255.255.0
+wantprefixlen == 3 -lastip 1.2.3.0-1.2.3.255
+
+You can also change the defaults on an per-object basis by fiddeling with the class members
+
+NoPrefixForSingleIp
+WantPrefixLen
+
+ >>> IP('10.0.0.0/32').strNormal()
+ '10.0.0.0'
+ >>> IP('10.0.0.0/24').strNormal()
+ '10.0.0.0/24'
+ >>> IP('10.0.0.0/24').strNormal(0)
+ '10.0.0.0'
+ >>> IP('10.0.0.0/24').strNormal(1)
+ '10.0.0.0/24'
+ >>> IP('10.0.0.0/24').strNormal(2)
+ '10.0.0.0/255.255.255.0'
+ >>> IP('10.0.0.0/24').strNormal(3)
+ '10.0.0.0-10.0.0.255'
+ >>> ip = IP('10.0.0.0')
+ >>> print ip
+ 10.0.0.0
+ >>> ip.NoPrefixForSingleIp = None
+ >>> print ip
+ 10.0.0.0/32
+ >>> ip.WantPrefixLen = 3
+ >>> print ip
+ 10.0.0.0-10.0.0.0
+
+
+Further Information might be available at http://c0re.jp/c0de/IPy/
+
+Hacked 2001 by drt at un.bewaff.net
+
+TODO:
+ * better comparison (__cmp__ and friends)
+ * tests for __cmp__
+ * always write hex values lowercase
+ * interpret 2001:1234:5678:1234/64 as 2001:1234:5678:1234::/64
+ * move size in bits into class variables to get rid of some "if self._ipversion ..."
+ * support for base85 encoding
+ * support for output of IPv6 encoded IPv4 Addresses
+ * update address type tables
+ * first-last notation should be allowed for IPv6
+ * add IPv6 docstring examples
+ * check better for negative parameters
+ * add addition / aggregation
+ * move reverse name stuff out of the classes and refactor it
+ * support for aggregation of more than two nets at once
+ * support for aggregation with "holes"
+ * support for finding common prefix
+ * '>>' and '<<' for prefix manipulation
+ * add our own exceptions instead ValueError all the time
+ * rename checkPrefix to checkPrefixOk
+ * add more documentation and doctests
+ * refactor
+"""
+
+import types
+
+# Definition of the Ranges for IPv4 IPs
+# this should include www.iana.org/assignments/ipv4-address-space
+# and www.iana.org/assignments/multicast-addresses
+IPv4ranges = {
+ '0' : 'PUBLIC', # fall back
+ '00000000' : 'PRIVATE', # 0/8
+ '00001010' : 'PRIVATE', # 10/8
+ '01111111' : 'PRIVATE', # 127.0/8
+ '1' : 'PUBLIC', # fall back
+ '101011000001' : 'PRIVATE', # 172.16/12
+ '1100000010101000' : 'PRIVATE', # 192.168/16
+ '11011111' : 'RESERVED', # 223/8
+ '111' : 'RESERVED' # 224/3
+ }
+
+# Definition of the Ranges for IPv6 IPs
+# see also www.iana.org/assignments/ipv6-address-space,
+# www.iana.org/assignments/ipv6-tla-assignments,
+# www.iana.org/assignments/ipv6-multicast-addresses,
+# www.iana.org/assignments/ipv6-anycast-addresses
+IPv6ranges = {
+ '00000000' : 'RESERVED', # ::/8
+ '00000001' : 'UNASSIGNED', # 100::/8
+ '0000001' : 'NSAP', # 200::/7
+ '0000010' : 'IPX', # 400::/7
+ '0000011' : 'UNASSIGNED', # 600::/7
+ '00001' : 'UNASSIGNED', # 800::/5
+ '0001' : 'UNASSIGNED', # 1000::/4
+ '0010000000000000' : 'RESERVED', # 2000::/16 Reserved
+ '0010000000000001' : 'ASSIGNABLE', # 2001::/16 Sub-TLA Assignments [RFC2450]
+ '00100000000000010000000': 'ASSIGNABLE IANA', # 2001:0000::/29 - 2001:01F8::/29 IANA
+ '00100000000000010000001': 'ASSIGNABLE APNIC', # 2001:0200::/29 - 2001:03F8::/29 APNIC
+ '00100000000000010000010': 'ASSIGNABLE ARIN', # 2001:0400::/29 - 2001:05F8::/29 ARIN
+ '00100000000000010000011': 'ASSIGNABLE RIPE', # 2001:0600::/29 - 2001:07F8::/29 RIPE NCC
+ '0010000000000010' : '6TO4', # 2002::/16 "6to4" [RFC3056]
+ '0011111111111110' : '6BONE', # 3FFE::/16 6bone Testing [RFC2471]
+ '0011111111111111' : 'RESERVED', # 3FFF::/16 Reserved
+ '010' : 'GLOBAL-UNICAST', # 4000::/3
+ '011' : 'UNASSIGNED', # 6000::/3
+ '100' : 'GEO-UNICAST', # 8000::/3
+ '101' : 'UNASSIGNED', # A000::/3
+ '110' : 'UNASSIGNED', # C000::/3
+ '1110' : 'UNASSIGNED', # E000::/4
+ '11110' : 'UNASSIGNED', # F000::/5
+ '111110' : 'UNASSIGNED', # F800::/6
+ '1111110' : 'UNASSIGNED', # FC00::/7
+ '111111100' : 'UNASSIGNED', # FE00::/9
+ '1111111010' : 'LINKLOCAL', # FE80::/10
+ '1111111011' : 'SITELOCAL', # FEC0::/10
+ '11111111' : 'MULTICAST', # FF00::/8
+ '0' * 96 : 'IPV4COMP', # ::/96
+ '0' * 80 + '1' * 16 : 'IPV4MAP', # ::FFFF:0:0/96
+ '0' * 128 : 'UNSPECIFIED', # ::/128
+ '0' * 127 + '1' : 'LOOPBACK' # ::1/128
+ }
+
+
+class IPint:
+ """Handling of IP addresses returning integers.
+
+ Use class IP instead because some features are not implemented for
+ IPint."""
+
+ def __init__(self, data, ipversion = 0):
+ """Create an instance of an IP object.
+
+ Data can be a network specification or a single IP. IP
+ Addresses can be specified in all forms understood by
+ parseAddress.() the size of a network can be specified as
+
+ /prefixlen a.b.c.0/24 2001:658:22a:cafe::/64
+ -lastIP a.b.c.0-a.b.c.255 2001:658:22a:cafe::-2001:658:22a:cafe:ffff:ffff:ffff:ffff
+ /decimal netmask a.b.c.d/255.255.255.0 not supported for IPv6
+
+ If no size specification is given a size of 1 address (/32 for
+ IPv4 and /128 for IPv6) is assumed.
+
+ >>> print IP('127.0.0.0/8')
+ 127.0.0.0/8
+ >>> print IP('127.0.0.0/255.0.0.0')
+ 127.0.0.0/8
+ >>> print IP('127.0.0.0-127.255.255.255')
+ 127.0.0.0/8
+
+ See module documentation for more examples.
+ """
+
+ self.NoPrefixForSingleIp = 1 # Print no Prefixlen for /32 and /128
+ self.WantPrefixLen = None # Do we want prefix printed by default? see _printPrefix()
+
+ netbits = 0
+ prefixlen = -1
+
+ # handling of non string values in constructor
+ if type(data) == types.IntType or type(data) == types.LongType:
+ self.ip = long(data)
+ if ipversion == 0:
+ if self.ip < 0x100000000L:
+ ipversion = 4
+ else:
+ ipversion = 6
+ if ipversion == 4:
+ prefixlen = 32
+ elif ipversion == 6:
+ prefixlen = 128
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+ self._ipversion = ipversion
+ self._prefixlen = prefixlen
+ # handle IP instance as an parameter
+ elif isinstance(data, IPint):
+ self._ipversion = data._ipversion
+ self._prefixlen = data._prefixlen
+ self.ip = data.ip
+ else:
+ # TODO: refactor me!
+ # splitting of a string into IP and prefixlen et. al.
+ x = data.split('-')
+ if len(x) == 2:
+ # a.b.c.0-a.b.c.255 specification ?
+ (ip, last) = x
+ (self.ip, parsedVersion) = parseAddress(ip)
+ if parsedVersion != 4:
+ raise ValueError, "first-last notation only allowed for IPv4"
+ (last, lastversion) = parseAddress(last)
+ if lastversion != 4:
+ raise ValueError, "last address should be IPv4, too"
+ if last < self.ip:
+ raise ValueError, "last address should be larger than first"
+ size = last - self.ip
+ netbits = _count1Bits(size)
+ elif len(x) == 1:
+ x = data.split('/')
+ # if no prefix is given use defaults
+ if len(x) == 1:
+ ip = x[0]
+ prefixlen = -1
+ elif len(x) > 2:
+ raise ValueError, "only one '/' allowed in IP Address"
+ else:
+ (ip, prefixlen) = x
+ if prefixlen.find('.') != -1:
+ # check if the user might have used a netmask like
+ # a.b.c.d/255.255.255.0
+ (netmask, vers) = parseAddress(prefixlen)
+ if vers != 4:
+ raise ValueError, "netmask must be IPv4"
+ prefixlen = _netmaskToPrefixlen(netmask)
+ elif len(x) > 2:
+ raise ValueError, "only one '-' allowed in IP Address"
+ else:
+ raise ValueError, "can't parse"
+
+ (self.ip, parsedVersion) = parseAddress(ip)
+ if ipversion == 0:
+ ipversion = parsedVersion
+ if prefixlen == -1:
+ if ipversion == 4:
+ prefixlen = 32 - netbits
+ elif ipversion == 6:
+ prefixlen = 128 - netbits
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+ self._ipversion = ipversion
+ self._prefixlen = int(prefixlen)
+
+ if not _checkNetaddrWorksWithPrefixlen(self.ip, self._prefixlen, self._ipversion):
+ raise ValueError, "%s goes not well with prefixlen %d" % (hex(self.ip), self._prefixlen)
+
+
+ def int(self):
+ """Return the first / base / network addess as an (long) integer.
+
+ The same as IP[0].
+
+ >>> hex(IP('10.0.0.0/8').int())
+ '0xA000000L'
+ """
+ return self.ip
+
+ def version(self):
+ """Return the IP version of this Object.
+
+ >>> IP('10.0.0.0/8').version()
+ 4
+ >>> IP('::1').version()
+ 6
+ """
+ return self._ipversion
+
+ def prefixlen(self):
+ """Returns Network Prefixlen.
+
+ >>> IP('10.0.0.0/8').prefixlen()
+ 8
+ """
+ return self._prefixlen
+
+ def net(self):
+ """Return the base (first) address of a network as an (long) integer."""
+
+ return self.int()
+
+ def broadcast(self):
+ """Return the broadcast (last) address of a network as an (long) integer.
+
+ The same as IP[-1]."""
+ return self.int() + self.len() - 1
+
+ def _printPrefix(self, want):
+ """Prints Prefixlen/Netmask.
+
+ Not really. In fact it is our universal Netmask/Prefixlen printer.
+ This is considered an internel function.
+
+ want == 0 / None don't return anything 1.2.3.0
+ want == 1 /prefix 1.2.3.0/24
+ want == 2 /netmask 1.2.3.0/255.255.255.0
+ want == 3 -lastip 1.2.3.0-1.2.3.255
+ """
+
+ if (self._ipversion == 4 and self._prefixlen == 32) or \
+ (self._ipversion == 6 and self._prefixlen == 128):
+ if self.NoPrefixForSingleIp:
+ want = 0
+ if want == None:
+ want = self.WantPrefixLen
+ if want == None:
+ want = 1
+ if want:
+ if want == 2:
+ # this should work wit IP and IPint
+ netmask = self.netmask()
+ if type(netmask) != types.IntType and type(netmask) != types.LongType:
+ netmask = netmask.int()
+ return "/%s" % (intToIp(netmask, self._ipversion))
+ elif want == 3:
+ return "-%s" % (intToIp(self.ip + self.len() - 1, self._ipversion))
+ else:
+ # default
+ return "/%d" % (self._prefixlen)
+ else:
+ return ''
+
+ # We have different Favours to convert to:
+ # strFullsize 127.0.0.1 2001:0658:022a:cafe:0200:c0ff:fe8d:08fa
+ # strNormal 127.0.0.1 2001:658:22a:cafe:200:c0ff:fe8d:08fa
+ # strCompressed 127.0.0.1 2001:658:22a:cafe::1
+ # strHex 0x7F000001L 0x20010658022ACAFE0200C0FFFE8D08FA
+ # strDec 2130706433 42540616829182469433547974687817795834
+
+ def strBin(self, wantprefixlen = None):
+ """Return a string representation as a binary value.
+
+ >>> print IP('127.0.0.1').strBin()
+ 01111111000000000000000000000001
+ """
+
+
+ if self._ipversion == 4:
+ bits = 32
+ elif self._ipversion == 6:
+ bits = 128
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+ if self.WantPrefixLen == None and wantprefixlen == None:
+ wantprefixlen = 0
+ ret = _intToBin(self.ip)
+ return '0' * (bits - len(ret)) + ret + self._printPrefix(wantprefixlen)
+
+ def strCompressed(self, wantprefixlen = None):
+ """Return a string representation in compressed format using '::' Notation.
+
+ >>> print IP('127.0.0.1').strCompressed()
+ 127.0.0.1
+ >>> print IP('2001:0658:022a:cafe:0200::1').strCompressed()
+ 2001:658:22a:cafe:200::1
+ """
+
+ if self.WantPrefixLen == None and wantprefixlen == None:
+ wantprefixlen = 1
+
+ if self._ipversion == 4:
+ return self.strFullsize(wantprefixlen)
+ else:
+ # find the longest sequence of '0'
+ hextets = [int(x, 16) for x in self.strFullsize(0).split(':')]
+ # every element of followingzeros will contain the number of zeros
+ # following the corrospondending element of hextetes
+ followingzeros = [0] * 8
+ for i in range(len(hextets)):
+ followingzeros[i] = _countFollowingZeros(hextets[i:])
+ # compressionpos is the position where we can start removing zeros
+ compressionpos = followingzeros.index(max(followingzeros))
+ if max(followingzeros) > 1:
+ # genererate string with the longest number of zeros cut out
+ # now we need hextets as strings
+ hextets = [x for x in self.strNormal(0).split(':')]
+ while compressionpos < len(hextets) and hextets[compressionpos] == '0':
+ del(hextets[compressionpos])
+ hextets.insert(compressionpos, '')
+ if compressionpos + 1 >= len(hextets):
+ hextets.append('')
+ if compressionpos == 0:
+ hextets = [''] + hextets
+ return ':'.join(hextets) + self._printPrefix(wantprefixlen)
+ else:
+ return self.strNormal() + self._printPrefix(wantprefixlen)
+
+ def strNormal(self, wantprefixlen = None):
+ """Return a string representation in the usual format.
+
+ >>> print IP('127.0.0.1').strNormal()
+ 127.0.0.1
+ >>> print IP('2001:0658:022a:cafe:0200::1').strNormal()
+ 2001:658:22a:cafe:200:0:0:1
+ """
+
+ if self.WantPrefixLen == None and wantprefixlen == None:
+ wantprefixlen = 1
+
+ if self._ipversion == 4:
+ ret = self.strFullsize(0)
+ elif self._ipversion == 6:
+ ret = ':'.join([hex(x)[2:] for x in [int(x, 16) for x in self.strFullsize(0).split(':')]])
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+
+
+ return ret + self._printPrefix(wantprefixlen)
+
+ def strFullsize(self, wantprefixlen = None):
+ """Return a string representation in the non mangled format.
+
+ >>> print IP('127.0.0.1').strFullsize()
+ 127.0.0.1
+ >>> print IP('2001:0658:022a:cafe:0200::1').strFullsize()
+ 2001:0658:022a:cafe:0200:0000:0000:0001
+ """
+
+ if self.WantPrefixLen == None and wantprefixlen == None:
+ wantprefixlen = 1
+
+ return intToIp(self.ip, self._ipversion).lower() + self._printPrefix(wantprefixlen)
+
+ def strHex(self, wantprefixlen = None):
+ """Return a string representation in hex format.
+
+ >>> print IP('127.0.0.1').strHex()
+ 0x7F000001
+ >>> print IP('2001:0658:022a:cafe:0200::1').strHex()
+ 0x20010658022ACAFE0200000000000001
+ """
+
+ if self.WantPrefixLen == None and wantprefixlen == None:
+ wantprefixlen = 0
+
+ x = hex(self.ip)
+ if x[-1] == 'L':
+ x = x[:-1]
+ return x + self._printPrefix(wantprefixlen)
+
+ def strDec(self, wantprefixlen = None):
+ """Return a string representation in decimal format.
+
+ >>> print IP('127.0.0.1').strDec()
+ 2130706433
+ >>> print IP('2001:0658:022a:cafe:0200::1').strDec()
+ 42540616829182469433547762482097946625
+ """
+
+ if self.WantPrefixLen == None and wantprefixlen == None:
+ wantprefixlen = 0
+
+ x = str(self.ip)
+ if x[-1] == 'L':
+ x = x[:-1]
+ return x + self._printPrefix(wantprefixlen)
+
+ def iptype(self):
+ """Return a description of the IP type ('PRIVATE', 'RESERVERD', etc).
+
+ >>> print IP('127.0.0.1').iptype()
+ PRIVATE
+ >>> print IP('192.168.1.1').iptype()
+ PRIVATE
+ >>> print IP('195.185.1.2').iptype()
+ PUBLIC
+ >>> print IP('::1').iptype()
+ LOOPBACK
+ >>> print IP('2001:0658:022a:cafe:0200::1').iptype()
+ ASSIGNABLE RIPE
+
+ The type information for IPv6 is out of sync with reality.
+ """
+
+ # this could be greatly improved
+
+ if self._ipversion == 4:
+ iprange = IPv4ranges
+ elif self._ipversion == 6:
+ iprange = IPv6ranges
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+ bits = self.strBin()
+ for i in range(len(bits), 0, -1):
+ if iprange.has_key(bits[:i]):
+ return iprange[bits[:i]]
+ return "unknown"
+
+
+ def netmask(self):
+ """Return netmask as an integer.
+
+ >>> print hex(IP('195.185.0.0/16').netmask().int())
+ 0xFFFF0000L
+ """
+
+ # TODO: unify with prefixlenToNetmask?
+ if self._ipversion == 4:
+ locallen = 32 - self._prefixlen
+ elif self._ipversion == 6:
+ locallen = 128 - self._prefixlen
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+ return ((2L ** self._prefixlen) - 1) << locallen
+
+
+ def strNetmask(self):
+ """Return netmask as an string. Mostly useful for IPv6.
+
+ >>> print IP('195.185.0.0/16').strNetmask()
+ 255.255.0.0
+ >>> print IP('2001:0658:022a:cafe::0/64').strNetmask()
+ /64
+ """
+
+ # TODO: unify with prefixlenToNetmask?
+ if self._ipversion == 4:
+ locallen = 32 - self._prefixlen
+ return intToIp(((2L ** self._prefixlen) - 1) << locallen, 4)
+ elif self._ipversion == 6:
+ locallen = 128 - self._prefixlen
+ return "/%d" % self._prefixlen
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+ def len(self):
+ """Return the length of an subnet.
+
+ >>> print IP('195.185.1.0/28').len()
+ 16
+ >>> print IP('195.185.1.0/24').len()
+ 256
+ """
+
+ if self._ipversion == 4:
+ locallen = 32 - self._prefixlen
+ elif self._ipversion == 6:
+ locallen = 128 - self._prefixlen
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+ return 2L ** locallen
+
+
+ def __len__(self):
+ """Return the length of an subnet.
+
+ Called to implement the built-in function len().
+ It breaks with IPv6 Networks. Anybody knows how to fix this."""
+
+ # Python < 2.2 has this silly restriction which breaks IPv6
+ # how about Python >= 2.2 ... ouch - it presists!
+
+ return int(self.len())
+
+
+ def __getitem__(self, key):
+ """Called to implement evaluation of self[key].
+
+ >>> ip=IP('127.0.0.0/30')
+ >>> for x in ip:
+ ... print hex(x.int())
+ ...
+ 0x7F000000L
+ 0x7F000001L
+ 0x7F000002L
+ 0x7F000003L
+ >>> hex(ip[2].int())
+ '0x7F000002L'
+ >>> hex(ip[-1].int())
+ '0x7F000003L'
+ """
+
+ if type(key) != types.IntType and type(key) != types.LongType:
+ raise TypeError
+ if abs(key) >= self.len():
+ raise IndexError
+ if key < 0:
+ key = self.len() - abs(key)
+
+ return self.ip + long(key)
+
+
+
+ def __contains__(self, item):
+ """Called to implement membership test operators.
+
+ Should return true if item is in self, false otherwise. Item
+ can be other IP-objects, strings or ints.
+
+ >>> print IP('195.185.1.1').strHex()
+ 0xC3B90101
+ >>> 0xC3B90101L in IP('195.185.1.0/24')
+ 1
+ >>> '127.0.0.1' in IP('127.0.0.0/24')
+ 1
+ >>> IP('127.0.0.0/24') in IP('127.0.0.0/25')
+ 0
+ """
+
+ item = IP(item)
+ if item.ip >= self.ip and item.ip < self.ip + self.len() - item.len() + 1:
+ return 1
+ else:
+ return 0
+
+
+ def overlaps(self, item):
+ """Check if two IP address ranges overlap.
+
+ Returns 0 if the two ranged don't overlap, 1 if the given
+ range overlaps at the end and -1 if it does at the beginning.
+
+ >>> IP('192.168.0.0/23').overlaps('192.168.1.0/24')
+ 1
+ >>> IP('192.168.0.0/23').overlaps('192.168.1.255')
+ 1
+ >>> IP('192.168.0.0/23').overlaps('192.168.2.0')
+ 0
+ >>> IP('192.168.1.0/24').overlaps('192.168.0.0/23')
+ -1
+ """
+
+ item = IP(item)
+ if item.ip >= self.ip and item.ip < self.ip + self.len():
+ return 1
+ elif self.ip >= item.ip and self.ip < item.ip + item.len():
+ return -1
+ else:
+ return 0
+
+
+ def __str__(self):
+ """Dispatch to the prefered String Representation.
+
+ Used to implement str(IP)."""
+
+ return self.strFullsize()
+
+
+ def __repr__(self):
+ """Print a representation of the Object.
+
+ Used to implement repr(IP). Returns a string which evaluates
+ to an identical Object (without the wnatprefixlen stuff - see
+ module docstring.
+
+ >>> print repr(IP('10.0.0.0/24'))
+ IP('10.0.0.0/24')
+ """
+
+ return("IPint('%s')" % (self.strCompressed(1)))
+
+
+ def __cmp__(self, other):
+ """Called by comparison operations.
+
+ Should return a negative integer if self < other, zero if self
+ == other, a positive integer if self > other.
+
+ Networks with different prefixlen are considered non-equal.
+ Networks with the same prefixlen and differing addresses are
+ considered non equal but are compared by thair base address
+ integer value to aid sorting of IP objects.
+
+ The Version of Objects is not put into consideration.
+
+ >>> IP('10.0.0.0/24') > IP('10.0.0.0')
+ 1
+ >>> IP('10.0.0.0/24') < IP('10.0.0.0')
+ 0
+ >>> IP('10.0.0.0/24') < IP('12.0.0.0/24')
+ 1
+ >>> IP('10.0.0.0/24') > IP('12.0.0.0/24')
+ 0
+
+ """
+
+ # Im not really sure if this is "the right thing to do"
+ if self._prefixlen < other.prefixlen():
+ return (other.prefixlen() - self._prefixlen)
+ elif self._prefixlen > other.prefixlen():
+
+ # Fixed bySamuel Krempp <krempp at crans.ens-cachan.fr>:
+
+ # The bug is quite obvious really (as 99% bugs are once
+ # spotted, isn't it ? ;-) Because of precedence of
+ # multiplication by -1 over the substraction, prefixlen
+ # differences were causing the __cmp__ function to always
+ # return positive numbers, thus the function was failing
+ # the basic assumptions for a __cmp__ function.
+
+ # Namely we could have (a > b AND b > a), when the
+ # prefixlen of a and b are different. (eg let
+ # a=IP("1.0.0.0/24"); b=IP("2.0.0.0/16");) thus, anything
+ # could happen when launching a sort algorithm..
+ # everything's in order with the trivial, attached patch.
+
+ return (self._prefixlen - other.prefixlen()) * -1
+ else:
+ if self.ip < other.ip:
+ return -1
+ elif self.ip > other.ip:
+ return 1
+ else:
+ return 0
+
+
+ def __hash__(self):
+ """Called for the key object for dictionary operations, and by
+ the built-in function hash() Should return a 32-bit integer
+ usable as a hash value for dictionary operations. The only
+ required property is that objects which compare equal have the
+ same hash value
+
+ >>> hex(IP('10.0.0.0/24').__hash__())
+ '0xf5ffffe7'
+ """
+
+ hash = int(-1)
+ ip = self.ip
+ while ip > 0:
+ hash = hash ^ (ip & 0x7fffffff)
+ ip = ip >> 32
+ hash = hash ^ self._prefixlen
+ return int(hash)
+
+
+class IP(IPint):
+ """Class for handling IP Addresses and Networks."""
+
+ def net(self):
+ """Return the base (first) address of a network as an IP object.
+
+ The same as IP[0].
+
+ >>> IP('10.0.0.0/8').net()
+ IP('10.0.0.0')
+ """
+ return IP(IPint.net(self))
+
+ def broadcast(self):
+ """Return the broadcast (last) address of a network as an IP object.
+
+ The same as IP[-1].
+
+ >>> IP('10.0.0.0/8').broadcast()
+ IP('10.255.255.255')
+ """
+ return IP(IPint.broadcast(self))
+
+ def netmask(self):
+ """Return netmask as an IP object.
+
+ >>> IP('10.0.0.0/8').netmask()
+ IP('255.0.0.0')
+ """
+ return IP(IPint.netmask(self))
+
+
+ def reverseNames(self):
+ """Return a list with values forming the reverse lookup.
+
+ >>> IP('213.221.113.87/32').reverseNames()
+ ['87.113.221.213.in-addr.arpa.']
+ >>> IP('213.221.112.224/30').reverseNames()
+ ['224.112.221.213.in-addr.arpa.', '225.112.221.213.in-addr.arpa.', '226.112.221.213.in-addr.arpa.', '227.112.221.213.in-addr.arpa.']
+ >>> IP('127.0.0.0/24').reverseNames()
+ ['0.0.127.in-addr.arpa.']
+ >>> IP('127.0.0.0/23').reverseNames()
+ ['0.0.127.in-addr.arpa.', '1.0.127.in-addr.arpa.']
+ >>> IP('127.0.0.0/16').reverseNames()
+ ['0.127.in-addr.arpa.']
+ >>> IP('127.0.0.0/15').reverseNames()
+ ['0.127.in-addr.arpa.', '1.127.in-addr.arpa.']
+ >>> IP('128.0.0.0/8').reverseNames()
+ ['128.in-addr.arpa.']
+ >>> IP('128.0.0.0/7').reverseNames()
+ ['128.in-addr.arpa.', '129.in-addr.arpa.']
+
+ """
+
+ if self._ipversion == 4:
+ ret =[]
+ # TODO: Refactor. Add support for IPint objects
+ if self.len() < 2**8:
+ for x in self:
+ ret.append(x.reverseName())
+ elif self.len() < 2**16L:
+ for i in range(0, self.len(), 2**8):
+ ret.append(self[i].reverseName()[2:])
+ elif self.len() < 2**24L:
+ for i in range(0, self.len(), 2**16):
+ ret.append(self[i].reverseName()[4:])
+ else:
+ for i in range(0, self.len(), 2**24):
+ ret.append(self[i].reverseName()[6:])
+ return ret
+ elif self._ipversion == 6:
+ s = hex(self.ip)[2:].lower()
+ if s[-1] == 'l':
+ s = s[:-1]
+ if self._prefixlen % 4 != 0:
+ raise NotImplementedError, "can't create IPv6 reverse names at sub nibble level"
+ s = list(s)
+ s.reverse()
+ s = '.'.join(s)
+ first_nibble_index = int(32 - (self._prefixlen / 4)) * 2
+ return ["%s.ip6.int." % s[first_nibble_index:]]
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+
+
+ def reverseName(self):
+ """Return the value for reverse lookup/PTR records as RfC 2317 look alike.
+
+ RfC 2317 is an ugly hack which only works for sub-/24 e.g. not
+ for /23. Do not use it. Better set up a Zone for every
+ address. See reverseName for a way to arcive that.
+
+ >>> print IP('195.185.1.1').reverseName()
+ 1.1.185.195.in-addr.arpa.
+ >>> print IP('195.185.1.0/28').reverseName()
+ 0-15.1.185.195.in-addr.arpa.
+ """
+
+ if self._ipversion == 4:
+ s = self.strFullsize(0)
+ s = s.split('.')
+ s.reverse()
+ first_byte_index = int(4 - (self._prefixlen / 8))
+ if self._prefixlen % 8 != 0:
+ nibblepart = "%s-%s" % (s[3-(self._prefixlen / 8)], intToIp(self.ip + self.len() - 1, 4).split('.')[-1])
+ if nibblepart[-1] == 'l':
+ nibblepart = nibblepart[:-1]
+ nibblepart += '.'
+ else:
+ nibblepart = ""
+
+ s = '.'.join(s[first_byte_index:])
+ return "%s%s.in-addr.arpa." % (nibblepart, s)
+
+ elif self._ipversion == 6:
+ s = hex(self.ip)[2:].lower()
+ if s[-1] == 'l':
+ s = s[:-1]
+ if self._prefixlen % 4 != 0:
+ nibblepart = "%s-%s" % (s[self._prefixlen:], hex(self.ip + self.len() - 1)[2:].lower())
+ if nibblepart[-1] == 'l':
+ nibblepart = nibblepart[:-1]
+ nibblepart += '.'
+ else:
+ nibblepart = ""
+ s = list(s)
+ s.reverse()
+ s = '.'.join(s)
+ first_nibble_index = int(32 - (self._prefixlen / 4)) * 2
+ return "%s%s.ip6.int." % (nibblepart, s[first_nibble_index:])
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+ def __getitem__(self, key):
+ """Called to implement evaluation of self[key].
+
+ >>> ip=IP('127.0.0.0/30')
+ >>> for x in ip:
+ ... print str(x)
+ ...
+ 127.0.0.0
+ 127.0.0.1
+ 127.0.0.2
+ 127.0.0.3
+ >>> print str(ip[2])
+ 127.0.0.2
+ >>> print str(ip[-1])
+ 127.0.0.3
+ """
+ return IP(IPint.__getitem__(self, key))
+
+ def __repr__(self):
+ """Print a representation of the Object.
+
+ >>> IP('10.0.0.0/8')
+ IP('10.0.0.0/8')
+ """
+
+ return("IP('%s')" % (self.strCompressed(1)))
+
+ def __add__(self, other):
+ """Emulate numeric objects through network aggregation"""
+ if self.prefixlen() != other.prefixlen():
+ raise ValueError, "Only networks with the same prefixlen can be added."
+ if self.prefixlen < 1:
+ raise ValueError, "Networks with a prefixlen longer than /1 can't be added."
+ if self.version() != other.version():
+ raise ValueError, "Only networks with the same IP version can be added."
+ if self > other:
+ # fixed by Skinny Puppy <skin_pup-IPy at happypoo.com>
+ return other.__add__(self)
+ else:
+ ret = IP(self.int())
+ ret._prefixlen = self.prefixlen() - 1
+ return ret
+
+def parseAddress(ipstr):
+ """Parse a string and return the corrospondending IPaddress and the a guess of the IP version.
+
+ Following Forms ar recorgnized:
+ 0x0123456789abcdef # IPv4 if <= 0xffffffff else IPv6
+ 123.123.123.123 # IPv4
+ 123.123 # 0-padded IPv4
+ 1080:0000:0000:0000:0008:0800:200C:417A
+ 1080:0:0:0:8:800:200C:417A
+ 1080:0::8:800:200C:417A
+ ::1
+ ::
+ 0:0:0:0:0:FFFF:129.144.52.38
+ ::13.1.68.3
+ ::FFFF:129.144.52.38
+ """
+
+ # TODO: refactor me!
+ if ipstr.startswith('0x'):
+ ret = long(ipstr[2:], 16)
+ if ret > 0xffffffffffffffffffffffffffffffffL:
+ raise ValueError, "%r: IP Address can't be bigger than 2^128" % (ipstr)
+ if ret < 0x100000000L:
+ return (ret, 4)
+ else:
+ return (ret, 6)
+
+ if ipstr.find(':') != -1:
+ # assume IPv6
+ if ipstr.find(':::') != -1:
+ raise ValueError, "%r: IPv6 Address can't contain ':::'" % (ipstr)
+ hextets = ipstr.split(':')
+ if ipstr.find('.') != -1:
+ # this might be a mixed address like '0:0:0:0:0:0:13.1.68.3'
+ (v4, foo) = parseAddress(hextets[-1])
+ assert foo == 4
+ del(hextets[-1])
+ hextets.append(hex(v4 >> 16)[2:-1])
+ hextets.append(hex(v4 & 0xffff)[2:-1])
+ if len(hextets) > 8:
+ raise ValueError, "%r: IPv6 Address with more than 8 hexletts" % (ipstr)
+ if len(hextets) < 8:
+ if '' not in hextets:
+ raise ValueError, "%r IPv6 Address with less than 8 hexletts and without '::'" % (ipstr)
+ # catch :: at the beginning or end
+ if hextets.index('') < len(hextets) - 1 and hextets[hextets.index('')+1] == '':
+ hextets.remove('')
+ # catch '::'
+ if hextets.index('') < len(hextets) - 1 and hextets[hextets.index('')+1] == '':
+ hextets.remove('')
+
+ for i in range(9-len(hextets)):
+ hextets.insert(hextets.index(''), '0')
+ hextets.remove('')
+ if '' in hextets:
+ raise ValueError, "%r IPv6 Address may contain '::' only once" % (ipstr)
+ if '' in hextets:
+ raise ValueError, "%r IPv6 Address may contain '::' only if it has less than 8 hextets" % (ipstr)
+ num = ''
+ for x in hextets:
+ if len(x) < 4:
+ x = ((4 - len(x)) * '0') + x
+ if int(x, 16) < 0 or int(x, 16) > 0xffff:
+ raise ValueError, "%r: single hextet must be 0 <= hextet <= 0xffff which isn't true for %s" % (ipstr, x)
+ num += x
+ return (long(num, 16), 6)
+
+ elif len(ipstr) == 32:
+ # assume IPv6 in pure hexadecimal notation
+ return (long(ipstr, 16), 6)
+
+ elif ipstr.find('.') != -1 or (len(ipstr) < 4 and int(ipstr) < 256):
+ # assume IPv4 ('127' gets interpreted as '127.0.0.0')
+ bytes = ipstr.split('.')
+ if len(bytes) > 4:
+ raise ValueError, "IPv4 Address with more than 4 bytes"
+ bytes += ['0'] * (4 - len(bytes))
+ bytes = [long(x) for x in bytes]
+ for x in bytes:
+ if x > 255 or x < 0:
+ raise ValueError, "%r: single byte must be 0 <= byte < 256" % (ipstr)
+ return ((bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3], 4)
+
+ else:
+ # we try to interprete it as a decimal digit -
+ # this ony works for numbers > 255 ... others
+ # will be interpreted as IPv4 first byte
+ ret = long(ipstr)
+ if ret > 0xffffffffffffffffffffffffffffffffL:
+ raise ValueError, "IP Address cant be bigger than 2^128"
+ if ret <= 0xffffffff:
+ return (ret, 4)
+ else:
+ return (ret, 6)
+
+
+def intToIp(ip, version):
+ """Transform an integer string into an IP address."""
+
+ # just to be sure and hoping for Python 2.22
+ ip = long(ip)
+
+ if ip < 0:
+ raise ValueError, "IPs can't be negative: %d" % (ip)
+
+ ret = ''
+ if version == 4:
+ if ip > 0xffffffffL:
+ raise ValueError, "IPv4 Addresses can't be larger than 0xffffffff: %s" % (hex(ip))
+ for l in range(4):
+ ret = str(ip & 0xffL) + '.' + ret
+ ip = ip >> 8;
+ ret = ret[:-1]
+ elif version == 6:
+ if ip > 0xffffffffffffffffffffffffffffffffL:
+ raise ValueError, "IPv6 Addresses can't be larger than 0xffffffffffffffffffffffffffffffff: %s" % (hex(ip))
+ l = '0' * 32 + hex(ip)[2:-1]
+ for x in range(1,33):
+ ret = l[-x] + ret
+ if x % 4 == 0:
+ ret = ':' + ret
+ ret = ret[1:]
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+ return ret;
+
+def _ipVersionToLen(version):
+ """Return number of bits in address for a certain IP version.
+
+ >>> _ipVersionToLen(4)
+ 32
+ >>> _ipVersionToLen(6)
+ 128
+ >>> _ipVersionToLen(5)
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in ?
+ File "IPy.py", line 1076, in _ipVersionToLen
+ raise ValueError, "only IPv4 and IPv6 supported"
+ ValueError: only IPv4 and IPv6 supported
+ """
+
+ if version == 4:
+ return 32
+ elif version == 6:
+ return 128
+ else:
+ raise ValueError, "only IPv4 and IPv6 supported"
+
+
+def _countFollowingZeros(l):
+ """Return Nr. of elements containing 0 at the beginning th the list."""
+ if len(l) == 0:
+ return 0
+ elif l[0] != 0:
+ return 0
+ else:
+ return 1 + _countFollowingZeros(l[1:])
+
+
+_BitTable = {'0': '0000', '1': '0001', '2': '0010', '3': '0011',
+ '4': '0100', '5': '0101', '6': '0110', '7': '0111',
+ '8': '1000', '9': '1001', 'a': '1010', 'b': '1011',
+ 'c': '1100', 'd': '1101', 'e': '1110', 'f': '1111'}
+
+def _intToBin(val):
+ """Return the binary representation of an integer as string."""
+
+ if val < 0:
+ raise ValueError, "Only positive Values allowed"
+ s = hex(val).lower()
+ ret = ''
+ if s[-1] == 'l':
+ s = s[:-1]
+ for x in s[2:]:
+ if __debug__:
+ if not _BitTable.has_key(x):
+ raise AssertionError, "hex() returned strange result"
+ ret += _BitTable[x]
+ # remove leading zeros
+ while ret[0] == '0' and len(ret) > 1:
+ ret = ret[1:]
+ return ret
+
+def _count1Bits(num):
+ """Find the highest bit set to 1 in an integer."""
+ ret = 0
+ while num > 0:
+ num = num >> 1
+ ret += 1
+ return ret
+
+def _count0Bits(num):
+ """Find the highest bit set to 0 in an integer."""
+
+ # this could be so easy if _count1Bits(~long(num)) would work as excepted
+ num = long(num)
+ if num < 0:
+ raise ValueError, "Only positive Numbers please: %s" % (num)
+ ret = 0
+ while num > 0:
+ if num & 1 == 1:
+ break
+ num = num >> 1
+ ret += 1
+ return ret
+
+
+def _checkPrefix(ip, prefixlen, version):
+ """Check the validity of a prefix
+
+ Checks if the variant part of a prefix only has 0s, and the length is
+ correct.
+
+ >>> _checkPrefix(0x7f000000L, 24, 4)
+ 1
+ >>> _checkPrefix(0x7f000001L, 24, 4)
+ 0
+ >>> repr(_checkPrefix(0x7f000001L, -1, 4))
+ 'None'
+ >>> repr(_checkPrefix(0x7f000001L, 33, 4))
+ 'None'
+ """
+
+ # TODO: unify this v4/v6/invalid code in a function
+ bits = _ipVersionToLen(version)
+
+ if prefixlen < 0 or prefixlen > bits:
+ return None
+
+ if ip == 0:
+ zbits = bits + 1
+ else:
+ zbits = _count0Bits(ip)
+ if zbits < bits - prefixlen:
+ return 0
+ else:
+ return 1
+
+
+def _checkNetmask(netmask, masklen):
+ """Checks if a netmask is expressable as e prefixlen."""
+
+ num = long(netmask)
+ bits = masklen
+
+ # remove zero bits at the end
+ while (num & 1) == 0:
+ num = num >> 1
+ bits -= 1
+ if bits == 0:
+ break
+ # now check if the rest consists only of ones
+ while bits > 0:
+ if (num & 1) == 0:
+ raise ValueError, "Netmask %s can't be expressed as an prefix." % (hex(netmask))
+ num = num >> 1
+ bits -= 1
+
+
+def _checkNetaddrWorksWithPrefixlen(net, prefixlen, version):
+ """Check if a base addess of e network is compatible with a prefixlen"""
+ if net & _prefixlenToNetmask(prefixlen, version) == net:
+ return 1
+ else:
+ return 0
+
+
+def _netmaskToPrefixlen(netmask):
+ """Convert an Integer reprsenting a Netmask to an prefixlen.
+
+ E.g. 0xffffff00 (255.255.255.0) returns 24
+ """
+
+ netlen = _count0Bits(netmask)
+ masklen = _count1Bits(netmask)
+ _checkNetmask(netmask, masklen)
+ return masklen - netlen
+
+
+def _prefixlenToNetmask(prefixlen, version):
+ """Return a mask of n bits as a long integer.
+
+ From 'IP address conversion functions with the builtin socket module' by Alex Martelli
+ http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66517
+ """
+ if prefixlen == 0:
+ return 0
+ elif prefixlen < 0:
+ raise ValueError, "Prefixlen must be > 0"
+ return ((2L<<prefixlen-1)-1) << (_ipVersionToLen(version) - prefixlen)
+
+
+def _test():
+ import doctest, IPy
+ return doctest.testmod(IPy)
+
+if __name__ == "__main__":
+ _test()
+
+ t = [0xf0, 0xf00, 0xff00, 0xffff00, 0xffffff00L]
+ o = []
+ for x in t:
+ pass
+ x = 0L
Added: packages/ipy/branches/upstream/current/example/confbuilder
URL: http://svn.debian.org/wsvn/python-modules/packages/ipy/branches/upstream/current/example/confbuilder?rev=371&op=file
==============================================================================
--- packages/ipy/branches/upstream/current/example/confbuilder (added)
+++ packages/ipy/branches/upstream/current/example/confbuilder Mon May 1 12:03:34 2006
@@ -1,0 +1,169 @@
+use Net::IP;
+
+ at ns=(':ns.nerxs.com',':ns.dorsch.org',':ns.c0re.jp');
+
+print "\n# domains\n";
+
+open(IN, '<domains');
+
+while(<IN>)
+{
+ if(!/^#/)
+ {
+ s/\n//;
+ ($domain, $owner) = split(/:/);
+ print ("'$domain:Contact for this domain is $owner\n");
+ foreach (@ns)
+ {
+ print (".".$domain.":$_\n");
+ }
+ }
+ }
+
+
+print "\n# networks\n";
+
+open(IN, '<networks');
+
+while(<IN>)
+ {
+
+ if(!/^#/)
+ {
+ s/\n//;
+ print "# $_\n";
+ @nets = split(/,/);
+ $name = shift @nets;
+ foreach(@nets)
+ {
+ my $ip = new Net::IP($_) or die (Net::IP::Error());
+ $net=$ip->ip();
+ $last=$ip->last_ip();
+ $net =~ s/://g;
+ $last =~ s/://g;
+
+ if($ip->size() > 32)
+ {
+ foreach (@ns)
+ {
+ print (".".$ip->reverse_ip().":$_\n");
+ }
+ $ip6map{$ip->ip()} = $name;
+ }
+ else
+ {
+ for($i = int($ip->intip()) + 1; $i < int($ip->intip()) + int($ip->size()); $i++)
+ {
+ $nmap{sprintf("%d.%d.%d.%d", ($i >> 24) & 0xff, ($i >> 16) & 0xff, ($i>>8) & 0xff, $i & 0xff)} = "$name";
+ $rmap{sprintf("%d.%d.%d.%d", ($i >> 24) & 0xff, ($i >> 16) & 0xff, ($i>>8) & 0xff, $i & 0xff)} = $ip->binip(). ".$name";
+ }
+ }
+ print ("=net.$name:".$net."\n");
+ print ("=broadcast.$name:".$last."\n");
+ #print ("Bin : ".$ip->binip()."\n");
+ #print ("Mask: ".$ip->mask()."\n");
+ #print ("Size: ".$ip->size()."\n");
+ #print ("Type: ".$ip->iptype()."\n");
+ }
+ }
+ }
+
+close(IN);
+
+print "\n# hosts\n";
+
+open(IN, '<hosts');
+
+while(<IN>)
+ {
+ s/\n//;
+ if(!/^#/)
+ {
+ if(!/^-/)
+ {
+ if(/^=/ || /^\+/)
+ {
+ @i = split(':');
+ $rmap{$i[1]} = '';
+ }
+ print "$_\n";
+ }
+ else
+ {
+ @fields = split(/\|/);
+ $name = shift(@fields);
+ $name =~ s/^.(.)/$1/;
+ $_ = shift(@fields);
+ @ips = split(/,/);
+ $_ = shift(@fields);
+ @aliases = split(/,/);
+ $admin = shift(@fields);
+ if(!$admin)
+ {
+ $admin = 'technik at c0re.23.nu';
+ }
+ $_ = shift(@fields);
+ @mxes = split(/,/);
+ foreach(@ips)
+ {
+ my $ip = new Net::IP($_) or die (Net::IP::Error());
+ if(length($ip->binip()) == 32)
+ {
+ # IPv4 is easy
+ if(!$nmap{$_})
+ {
+ print STDERR "*** warning: no network for $_ ($name) - ignoring\n";
+ print "# no network for $_ ($name) - ignoring\n";
+ }
+ else
+ {
+ print "=$name." . $nmap{$_} . ":$_\n";
+ $rmap{$_} = '';
+ print "'$name." . $nmap{$_} . ":Host contact is $admin\n";
+ }
+ foreach(@aliases)
+ {
+ print "+$_:".$ip->ip()."\n";
+ print "'$_:Host contact is $admin\n";
+ }
+ }
+ else
+ {
+ #IPv6 here
+ $net = $ip->ip();
+ $net =~ s/^(....:....:....:....).*/$1:0000:0000:0000:0000/;
+ if(!$ip6map{$net})
+ {
+ print STDERR "*** warning: no network for $_ ($name) - ignoring\n";
+ print "# no network for $_ ($name) - ignoring\n";
+ }
+ else
+ {
+ $rip = $ip->ip();
+ $rip =~ s/://g;
+ print "6$name." . $ip6map{$net} . ":".$rip."\n";
+ }
+ foreach(@aliases)
+ {
+ $rip = $ip->ip();
+ $rip =~ s/://g;
+ print "3$_:".$rip."\n";
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+close(IN);
+
+
+print "\n# reverse lookup\n";
+foreach(sort(keys(%nmap)))
+{
+ if($rmap{$_})
+ {
+ print "=".$rmap{$_}.":$_\n";
+ }
+}
Added: packages/ipy/branches/upstream/current/example/confbuilder.py
URL: http://svn.debian.org/wsvn/python-modules/packages/ipy/branches/upstream/current/example/confbuilder.py?rev=371&op=file
==============================================================================
--- packages/ipy/branches/upstream/current/example/confbuilder.py (added)
+++ packages/ipy/branches/upstream/current/example/confbuilder.py Mon May 1 12:03:34 2006
@@ -1,0 +1,141 @@
+# This is a hack I use to generate my tinydns configuration
+# It serves as e test for converting from Perl Net::IP to
+# Python and IPy
+
+# Further Information might be available at http://c0re.jp/c0de/IPy/
+# Hacked 2001 by drt at un.bewaff.net
+
+import sys
+sys.path.append('..')
+
+import IPy
+
+
+ns = {'ns.nerxs.com': '213.221.113.70',
+ 'ns.dorsch.org': '195.143.234.25',
+ 'ns.c0re.jp': '217.6.214.130'}
+
+print "# *** nameservers ***"
+for x in ns.keys():
+ print "=%s:%s" % (x, ns[x])
+
+print "\n# *** domains ***"
+
+fd = open('domains')
+
+for x in fd.readlines():
+ if x[0] != '#':
+ if x[-1] == '\n':
+ x = x[:-1]
+ (domain, owner) = x.split(':')
+ print "'%s:Contact for this domain is %s" % (domain, owner)
+ for y in ns.keys():
+ print ".%s::%s" % (domain, y)
+
+fd.close()
+
+print "\n# *** Networks ***"
+
+fd = open('networks')
+ip6map = {}
+rmap = {}
+nmap = {}
+
+for x in fd.readlines():
+ if x[-1] == '\n':
+ x = x[:-1]
+ if len(x) > 0 and x[0] != '#':
+ nets = x.split(',')
+ name = nets.pop(0)
+ print "# Network: %s" % name
+ for y in nets:
+ ip = IPy.IP(y)
+ print "# Address range: %s (%s), %d addresses" % (ip.strCompressed(), ip.iptype(), ip.len())
+ print "=net.%s:%s" % (name, ip.net())
+ print "=broadcast.%s:%s" % (name, ip.broadcast())
+
+ if ip.version() == 4:
+ for z in ip:
+ # TODO reverse?
+ nmap[z.int()] = name
+ rmap[z.int()] = z.strBin() + "." + name
+ else:
+ # IPv6
+ for z in ns.keys():
+ for v in ip.reverseName():
+ print ".%s::%s" % (v, z)
+ ip6map[ip.strFullsize(0)] = name
+
+fd.close()
+
+print "\n# *** hosts ***"
+
+fd = open('hosts')
+
+for x in fd.readlines():
+ if x[-1] == '\n':
+ x = x[:-1]
+ if x != '' and x[0] != '#':
+ if "@Z'.".find(x[0]) >= 0:
+ print x
+ else:
+ if "=+'".find(x[0]) >= 0:
+ i = x.split(':')
+ rmap[IPy.IP(i[1]).int()] = ''
+ print x
+ else:
+ x = x[1:]
+ x += '||||'
+ fields = x.split('|')
+ name = fields.pop(0)
+ if name[0] == '.':
+ name = name[1:]
+ v = fields.pop(0)
+ ips = v.split(',')
+ v = fields.pop(0)
+ aliases = v.split(',')
+ if aliases == ['']:
+ aliases = []
+ admin = fields.pop()
+ if admin == '':
+ admin = 'technik at c0re.23.nu'
+ v = fields.pop()
+ mxes = v.split(',')
+ if mxes == ['']:
+ mxes = []
+ for y in ips:
+ ip = IPy.IP(y)
+ if ip.version() == 4:
+ # IPv4 is easy
+ if not nmap.has_key(ip.int()):
+ print >>sys.stderr, "*** warning: no network for %s (%s) - ignoring" % (y, name)
+ print "# no network for %s (%s)" % (y, name)
+ else:
+ print "=%s.%s:%s" % (name, nmap[ip.int()], y)
+ print "'%s.%s:Host contact is %s" % (name, nmap[ip.int()], admin)
+ rmap[ip.int()] = ''
+ for z in aliases:
+ print "+%s:%s" % (z, ip)
+ print "'%s:Host contact is %s" % (z, admin)
+ else:
+ #IPv6 here
+ net = ip.strFullsize(0)
+ net = net[:19] + ':0000:0000:0000:0000'
+ if ip6map.has_key(net):
+ print >>sys.stderr, "*** warning: no network for %s (%s) - ignoring" % (ip, name)
+ print "# no network for %s (%s) - ignoring" % (ip, name)
+ else:
+ print "6%s.%s:%s"; (name, ip6map[net], ip.strHex()[2:])
+ for z in aliases:
+ print "3%s:%s" % (name, ip.strHex()[2:])
+ print "'%s:Host contact is %s" % (name, admin)
+
+fd.close()
+
+print "\n# *** reverse lookup ***"
+k = nmap.keys()
+k.sort()
+for x in k:
+ if rmap.has_key(x) and rmap[x] != '':
+ print "=%s:%s" % (rmap[x], str(IPy.IP(x)))
+
Added: packages/ipy/branches/upstream/current/setup.py
URL: http://svn.debian.org/wsvn/python-modules/packages/ipy/branches/upstream/current/setup.py?rev=371&op=file
==============================================================================
--- packages/ipy/branches/upstream/current/setup.py (added)
+++ packages/ipy/branches/upstream/current/setup.py Mon May 1 12:03:34 2006
@@ -1,0 +1,10 @@
+# $Id: setup.py 671 2004-08-22 21:02:29Z md $
+from distutils.core import setup
+setup(name="IPy",
+ version="0.41",
+ description="IPv4 and IPv6 parsing and handling class",
+ author="drt",
+ author_email="drt at un.bewaff.net",
+ url="http://c0re.jp/c0de/Ipy/",
+ py_modules=["IPy"])
+
Added: packages/ipy/branches/upstream/current/test/test_IPy.py
URL: http://svn.debian.org/wsvn/python-modules/packages/ipy/branches/upstream/current/test/test_IPy.py?rev=371&op=file
==============================================================================
--- packages/ipy/branches/upstream/current/test/test_IPy.py (added)
+++ packages/ipy/branches/upstream/current/test/test_IPy.py Mon May 1 12:03:34 2006
@@ -1,0 +1,771 @@
+"""Unit test for IPy.py
+
+Further Information might be available at http://c0re.jp/c0de/IPy/
+
+Hacked 2001 by drt at un.bewaff.net
+"""
+
+# TODO: unify assert / FilIf usage
+
+import sys
+sys.path.append('..')
+sys.path.append('.')
+
+import IPy
+import unittest
+import random
+
+testloops = 250
+
+class parseAddress(unittest.TestCase):
+ okValues = [('FEDC:BA98:7654:3210:FEDC:BA98:7654:3210', 338770000845734292534325025077361652240L),
+ ('FEDCBA9876543210FEDCBA9876543210', 338770000845734292534325025077361652240L),
+ ('0xFEDCBA9876543210FEDCBA9876543210', 338770000845734292534325025077361652240L),
+ ('1080:0000:0000:0000:0008:0800:200C:417A', 21932261930451111902915077091070067066L),
+ ('1080:0:0:0:8:800:200C:417A', 21932261930451111902915077091070067066L),
+ ('1080:0::8:800:200C:417A', 21932261930451111902915077091070067066L),
+ ('1080::8:800:200C:417A', 21932261930451111902915077091070067066L),
+ ('FF01:0:0:0:0:0:0:43', 338958331222012082418099330867817087043L),
+ ('FF01:0:0::0:0:43', 338958331222012082418099330867817087043L),
+ ('FF01::43', 338958331222012082418099330867817087043L),
+ ('0:0:0:0:0:0:0:1', 1L),
+ ('0:0:0::0:0:1', 1L),
+ ('::1', 1L),
+ ('0:0:0:0:0:0:0:0', 0L),
+ ('0:0:0::0:0:0', 0L),
+ ('::', 0L),
+ ('0:0:0:0:0:0:13.1.68.3', 218186755L),
+ ('::13.1.68.3', 218186755L),
+ ('0:0:0:0:0:FFFF:129.144.52.38', 281472855454758L),
+ ('::FFFF:129.144.52.38', 281472855454758L),
+ ('1080:0:0:0:8:800:200C:417A', 21932261930451111902915077091070067066L),
+ ('1080::8:800:200C:417A', 21932261930451111902915077091070067066L),
+ ('0.0.0.0', 0L),
+ ('0', 0L),
+ ('127.0.0.1', 2130706433L),
+ ('255.255.255.255', 4294967295L),
+ ('0.0.0.1', 1L),
+ ('1', 16777216L),
+ ('213.221.113.87', 3588059479L),
+ ('0000', 0L),
+ ('127001', 127001L),
+ ('1234576', 1234576L),
+ ('1', 16777216L),
+ ('232111387', 232111387L),
+ ('255', 4278190080L),
+ ('256', 256L),
+ ('0xffffffff', 4294967295L),
+ ('0x100000000', 4294967296L),
+ ('0xffffffffffffffffffffffffffffffff', 0xffffffffffffffffffffffffffffffffL),
+ ('0xdeadbeef', 0xdeadbeefL),
+ ('0xdeadbabe', 0xdeadbabeL),
+ ('0xdeadc0de', 0xdeadc0deL),
+ ('0xc0decafe', 0xc0decafeL),
+ ('0xc0debabe', 0xc0debabeL),
+ ('0xbabec0de', 0xbabec0deL),
+ ('0xcafebabe', 0xcafebabeL),
+ ('0x1', 1L),
+ ('0xabcdef', 11259375L)]
+
+ # TODO: check for more invalid input
+
+ def testKnownValues(self):
+ """parsing of known values should give known results"""
+ for x in self.okValues:
+ (question, answer) = x
+ (result, version) = IPy.parseAddress(question)
+ self.assertEqual(answer, result, "%r, %r, %r" % (question, answer, result))
+
+ def testVersionDistinction(self):
+ """problems destinguishing IPv4 and IPv6"""
+ (result, version) = IPy.parseAddress('0xffffffff')
+ self.assertEqual(version, 4)
+ (result, version) = IPy.parseAddress('0x100000000')
+ self.assertEqual(version, 6)
+
+ def testEmpty(self):
+ """'' should raise an exception"""
+ self.assertRaises(ValueError, IPy.parseAddress, '')
+
+ def testTooBig(self):
+ """'' should raise an exception"""
+ self.assertRaises(ValueError, IPy.parseAddress, '0x100000000000000000000000000000000')
+
+ def testLongIPv4(self):
+ """'1.2.3.4.5' should raise an exception"""
+ self.assertRaises(ValueError, IPy.parseAddress, '1.2.3.4.5')
+
+ def testNonByteIPv4(self):
+ """'1.2.3.256' should raise an exception"""
+ self.assertRaises(ValueError, IPy.parseAddress, '1.2.3.256')
+
+ def testNegativeByteIPv4(self):
+ """'-1.2.3.4' and '1.2.3.-4' should raise an exception"""
+ self.assertRaises(ValueError, IPy.parseAddress, '-1.2.3.4')
+ self.assertRaises(ValueError, IPy.parseAddress, '1.2.3.-4')
+
+ def testTripleColonIPv6(self):
+ """'2001:::1' should raise an exception"""
+ self.assertRaises(ValueError, IPy.parseAddress, '2001:::1')
+
+ def testRepeatDoubleColonIPv6(self):
+ """'2001::ABCD::1' should raise an exception"""
+ self.assertRaises(ValueError, IPy.parseAddress, '2001::ABCD::1')
+
+ def testDoubleColonWithEightHextetsIPv6(self):
+ """'1111::2222:3333:4444:5555:6666:7777:8888' should raise an exception"""
+ self.assertRaises(ValueError, IPy.parseAddress, '1111::2222:3333:4444:5555:6666:7777:8888')
+
+ def testBeginningColonWithEightHextetsIPv6(self):
+ """':1111:2222:3333:4444:5555:6666:7777:8888' should raise an exception"""
+ self.assertRaises(ValueError, IPy.parseAddress, ':1111:2222:3333:4444:5555:6666:7777:8888')
+
+ def testEndingColonWithEightHextetsIPv6(self):
+ """'1111:2222:3333:4444:5555:6666:7777:8888:' should raise an exception"""
+ self.assertRaises(ValueError, IPy.parseAddress, '1111:2222:3333:4444:5555:6666:7777:8888:')
+
+ def testNegativeHexletIPv6(self):
+ """'2001:-ABCD::1' should raise an exception"""
+ self.assertRaises(ValueError, IPy.parseAddress, '2001:-ABCD::1')
+
+ def testTooBigHexletIPv6(self):
+ """'2001:10000::1' should raise an exception"""
+ self.assertRaises(ValueError, IPy.parseAddress, '2001:10000::1')
+
+ def testShortAddressIPv6(self):
+ """'1111:2222:3333:4444:5555:6666:7777' should raise an exception"""
+ self.assertRaises(ValueError, IPy.parseAddress, '1111:2222:3333:4444:5555:6666:7777')
+
+ def testLongAddressIPv6(self):
+ """'1111:2222:3333:4444:5555:6666:7777:8888:9999' should raise an exception"""
+ self.assertRaises(ValueError, IPy.parseAddress, '1111:2222:3333:4444:5555:6666:7777:8888:9999')
+
+class _intToIP(unittest.TestCase):
+ v4values = [(0x7f000001, '127.0.0.1'),
+ (0x0, '0.0.0.0'),
+ (0x1, '0.0.0.1'),
+ (0xf, '0.0.0.15'),
+ (0xff, '0.0.0.255'),
+ (0xFFFFFFFFL, '255.255.255.255')]
+ v6values = [(0x7f000001, '0000:0000:0000:0000:0000:0000:7F00:0001'),
+ (0x0, '0000:0000:0000:0000:0000:0000:0000:0000'),
+ (0x1, '0000:0000:0000:0000:0000:0000:0000:0001'),
+ (0xf, '0000:0000:0000:0000:0000:0000:0000:000F'),
+ (0xff, '0000:0000:0000:0000:0000:0000:0000:00FF'),
+ (0xFFFFFFFFL, '0000:0000:0000:0000:0000:0000:FFFF:FFFF'),
+ (0x100000000L, '0000:0000:0000:0000:0000:0001:0000:0000'),
+ (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFL, 'FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF')]
+
+ def testKnownValuesv4(self):
+ """printing of known IPv4 values should give known results"""
+ for x in self.v4values:
+ (question, answer) = x
+ result = IPy.intToIp(question, 4)
+ self.assertEqual(answer, result, "%r, %r, %r" % (question, answer, result))
+
+ def testKnownValuesv6(self):
+ """printing of known IPv6 values should give known results"""
+ for x in self.v6values:
+ (question, answer) = x
+ result = IPy.intToIp(question, 6)
+ self.assertEqual(answer, result, "%r, %r, %r" % (question, answer, result))
+
+ def testNegativeIPv4(self):
+ """negative IPv4 Values should raise an exception"""
+ self.assertRaises(ValueError, IPy.intToIp, -1, 4)
+
+ def testNegativeIPv6(self):
+ """negative IPv6 Values should raise an exception"""
+ self.assertRaises(ValueError, IPy.intToIp, -1, 6)
+
+ def testLargeIPv4(self):
+ """IPv4: Values > 0xffffffff should raise an exception"""
+ self.assertRaises(ValueError, IPy.intToIp, 0x100000000L, 4)
+
+ def testLargeIPv6(self):
+ """IPv6: Values > 0xffffffffffffffffffffffffffffffff should raise an exception"""
+ self.assertRaises(ValueError, IPy.intToIp, 0x100000000000000000000000000000000L, 6)
+
+ def testIllegalVersion(self):
+ """IPVersion other than 4 and 6 should raise an exception"""
+ self.assertRaises(ValueError, IPy.intToIp, 1, 0)
+ self.assertRaises(ValueError, IPy.intToIp, 1, 1)
+ self.assertRaises(ValueError, IPy.intToIp, 1, 2)
+ self.assertRaises(ValueError, IPy.intToIp, 1, 3)
+ self.assertRaises(ValueError, IPy.intToIp, 1, 5)
+ self.assertRaises(ValueError, IPy.intToIp, 1, 7)
+ self.assertRaises(ValueError, IPy.intToIp, 1, 8)
+
+class ParseAndBack(unittest.TestCase):
+ def testRandomValuesv4(self):
+ for i in range(testloops):
+ question = long(random.randrange(0x7fffffff)) + long(random.randrange(0x7fffffff))
+ self.assertEqual(IPy.parseAddress(IPy.intToIp(question, 4)), (question, 4), hex(question))
+
+ def testRandomValuesv6(self):
+ for i in range(testloops):
+ question = ((long(random.randrange(0x7fffffff)) + long(random.randrange(0x7fffffff))) +
+ ((long(random.randrange(0x7fffffff)) + long(random.randrange(0x7fffffff))) << 32) +
+ ((long(random.randrange(0x7fffffff)) + long(random.randrange(0x7fffffff))) << 64) +
+ ((long(random.randrange(0x7fffffff)) + long(random.randrange(0x7fffffff))) << 96))
+ self.assertEqual(IPy.parseAddress(IPy.intToIp(question, 6)), (question, 6), hex(question))
+
+
+class _countXBits(unittest.TestCase):
+ def testCount1Bits(self):
+ self.assertEqual(IPy._count1Bits(0), 0)
+ self.assertEqual(IPy._count1Bits(0xf), 4)
+ self.assertEqual(IPy._count1Bits(0x10), 5)
+ self.assertEqual(IPy._count1Bits(0xff), 8)
+ self.assertEqual(IPy._count1Bits(0xffff), 16)
+ self.assertEqual(IPy._count1Bits(0xffffffffL), 32)
+ self.assertEqual(IPy._count1Bits(0xffffffffffffffffffffffffffffffffL), 128)
+
+ def testCount1Bits(self):
+ self.assertEqual(IPy._count0Bits(0), 0)
+ self.assertEqual(IPy._count0Bits(0xf0L), 4)
+ self.assertEqual(IPy._count0Bits(0xf00L), 8)
+ self.assertEqual(IPy._count0Bits(0xf000L), 12)
+ self.assertEqual(IPy._count0Bits(0xf0000L), 16)
+ self.assertEqual(IPy._count0Bits(0xf00000L), 20)
+ self.assertEqual(IPy._count0Bits(0xf000000L), 24)
+ self.assertEqual(IPy._count0Bits(0xf0000000L), 28)
+ self.assertEqual(IPy._count0Bits(0xff000000L), 24)
+ self.assertEqual(IPy._count0Bits(0xfff00000L), 20)
+ self.assertEqual(IPy._count0Bits(0x80000000L), 31)
+ self.assertEqual(IPy._count0Bits(0xf0000000000000000000000000000000L), 124)
+ self.assertEqual(IPy._count0Bits(0x80000000000000000000000000000000L), 127)
+
+
+class _intToBin(unittest.TestCase):
+ knownValues = [(0, '0'), (1, '1'), (2, '10'), (3, '11'), (4, '100'), (5, '101'),
+ (6, '110'), (7, '111'), (8, '1000'), (9, '1001'),
+ (0xf, '1111'), (0xff, '11111111'),
+ (0xFFFFFFFFL, '11111111111111111111111111111111'),
+ (0x100000000L, '100000000000000000000000000000000'),
+ (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFL, '11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'),
+ (0x100000000000000000000000000000000L, '100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')]
+
+ def testKnownValues(self):
+ """conversion of known values values should give known results"""
+ for x in self.knownValues:
+ (question, answer) = x
+ result = IPy._intToBin(question)
+ self.assertEqual(answer, result, str(question))
+
+ def testNegativeIPv4(self):
+ """negative Values should raise an exception"""
+ self.assertRaises(ValueError, IPy._intToBin, -1)
+
+class netmaskPrefixlenConv(unittest.TestCase):
+ known4Values = [(0xFFFFFFFFL, 32), (0xFFFFFFFEL, 31), (0xFFFFFFFCL, 30), (0xFFFFFFF8L, 29),
+ (0xFFFFFFF0L, 28), (0xFFFFFFE0L, 27), (0xFFFFFFC0L, 26), (0xFFFFFF80L, 25),
+ (0xFFFFFF00L, 24), (0xFFFFFE00L, 23), (0xFFFFFC00L, 22), (0xFFFFF800L, 21),
+ (0xFFFFF000L, 20), (0xFFFFE000L, 19), (0xFFFFC000L, 18), (0xFFFF8000L, 17),
+ (0xFFFF0000L, 16), (0xFFFE0000L, 15), (0xFFFC0000L, 14), (0xFFF80000L, 13),
+ (0xFFF00000L, 12), (0xFFE00000L, 11), (0xFFC00000L, 10), (0xFF800000L, 9),
+ (0xFF000000L, 8), (0xFE000000L, 7), (0xFC000000L, 6), (0xF8000000L, 5),
+ (0xF0000000L, 4), (0xE0000000L, 3), (0xC0000000L, 2), (0x80000000L, 1)]
+ known6Values = [(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFL, 128),
+ (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEL, 127),
+ (0xFFFFFFFFFFFFFFFFFFFFFFFF80000000L, 97),
+ (0xFFFFFFFFFFFFFFFFFFFFFFFF00000000L, 96),
+ (0xFFFFFFFFFFFFFFFFFFFFFFFE00000000L, 95),
+ (0xFFFFFFFFFFFFFFFF8000000000000000L, 65),
+ (0xFFFFFFFFFFFFFFFF0000000000000000L, 64),
+ (0xFFFFFFFFFFFFFFFE0000000000000000L, 63),
+ (0xFFFFFFFF800000000000000000000000L, 33),
+ (0xFFFFFFFF000000000000000000000000L, 32),
+ (0xFFFFFFFE000000000000000000000000L, 31),
+ (0xC0000000000000000000000000000000L, 2),
+ (0x80000000000000000000000000000000L, 1)]
+
+ def testKnownValuesv4n2p(self):
+ """conversion of known values values should give known results"""
+ for x in self.known4Values:
+ (question, answer) = x
+ result = IPy._netmaskToPrefixlen(question)
+ self.assertEqual(answer, result, hex(question))
+
+ def testKnownValuesv6n2p(self):
+ """conversion of known values values should give known results"""
+ for x in self.known6Values:
+ (question, answer) = x
+ result = IPy._netmaskToPrefixlen(question)
+ self.assertEqual(answer, result, hex(question))
+
+ def testKnownValuesv4p2n(self):
+ """conversion of known values values should give known results"""
+ for x in self.known4Values:
+ (answer, question) = x
+ result = IPy._prefixlenToNetmask(question, 4)
+ self.assertEqual(answer, result, hex(question))
+
+ def testKnownValuesv6p2n(self):
+ """conversion of known values values should give known results"""
+ for x in self.known6Values:
+ (answer, question) = x
+ result = IPy._prefixlenToNetmask(question, 6)
+ self.assertEqual(answer, result, "%d: %s != %s" % (question, hex(answer), result))
+
+ def testInvalidv4n2p(self):
+ """Netmasks should be all ones in the first part and all zeros in the second part"""
+ self.failUnlessRaises(ValueError, IPy._netmaskToPrefixlen, 0xff00ff00L)
+
+ def testInvalidv6n2p(self):
+ """Netmasks should be all ones in the first part and all zeros in the second part"""
+ self.failUnlessRaises(ValueError, IPy._netmaskToPrefixlen, 0xff00ff00ff00ff00ff00ff00ff00ff00L)
+
+
+class checkChecks(unittest.TestCase):
+
+ def testCheckNetmaskOk(self):
+ """Legal Netmasks should be allowed."""
+ self.failIf(IPy._checkNetmask(0xffffffffL, 32))
+ self.failIf(IPy._checkNetmask(0xffffff00L, 32))
+ self.failIf(IPy._checkNetmask(0xffff0000L, 32))
+ self.failIf(IPy._checkNetmask(0xff000000L, 32))
+ self.failIf(IPy._checkNetmask(0, 32))
+
+ def testCheckNetmaskFail(self):
+ """Illegal Netmasks should be rejected."""
+ self.failUnlessRaises(ValueError, IPy._checkNetmask, 0xf0ffffffL, 32)
+ self.failUnlessRaises(ValueError, IPy._checkNetmask, 0xf0f0f0f0L, 32)
+ self.failUnlessRaises(ValueError, IPy._checkNetmask, 0xff00ff00L, 32)
+ self.failUnlessRaises(ValueError, IPy._checkNetmask, 0x70000001L, 32)
+ self.failUnlessRaises(ValueError, IPy._checkNetmask, 0xfffffffL, 32)
+
+ def testCheckPrefixOk(self):
+ """Legal IP/prefix combinations should check ok."""
+ self.failUnless(IPy._checkPrefix(0x0, 32, 4))
+ self.failUnless(IPy._checkPrefix(0xffffffffL, 32, 4))
+ self.failUnless(IPy._checkPrefix(0x7f000001L, 32, 4))
+ self.failUnless(IPy._checkPrefix(0x80000000L, 1, 4))
+ self.failUnless(IPy._checkPrefix(0x40000000L, 2, 4))
+ self.failUnless(IPy._checkPrefix(0x80000000L, 3, 4))
+ self.failUnless(IPy._checkPrefix(0x80000000L, 4, 4))
+ self.failUnless(IPy._checkPrefix(0xffffff00L, 24, 4))
+ self.failUnless(IPy._checkPrefix(0xffffff00L, 24, 4))
+ self.failUnless(IPy._checkPrefix(0xfffffff0L, 28, 4))
+ self.failUnless(IPy._checkPrefix(0x0, 32, 4))
+ self.failUnless(IPy._checkPrefix(0x0, 1, 4))
+ self.failUnless(IPy._checkPrefix(0x0, 0, 4))
+ self.failUnless(IPy._checkPrefix(0xffffffffffffffff0000000000000000L, 64, 6))
+ self.failUnless(IPy._checkPrefix(0x0L, 64, 6))
+ self.failUnless(IPy._checkPrefix(0x0L, 0, 6))
+ self.failUnless(IPy._checkPrefix(0x0L, 128, 6))
+ self.failUnless(IPy._checkPrefix(0xffffffffffffffffffffffffffffffffL, 128, 6))
+
+
+ def testCheckPrefixFail(self):
+ """Illegal Prefixes should be catched."""
+ self.failIf(IPy._checkPrefix(0x7f000001L, -1, 4))
+ self.failIf(IPy._checkPrefix(0x7f000001L, 33, 4))
+ self.failIf(IPy._checkPrefix(0x7f000001L, 24, 4))
+ self.failIf(IPy._checkPrefix(0x7f000001L, 31, 4))
+ self.failIf(IPy._checkPrefix(0x7f000080L, 24, 4))
+ self.failIf(IPy._checkPrefix(0x7f000100L, 23, 4))
+ self.failIf(IPy._checkPrefix(0x7f000000L, 1, 4))
+ self.failIf(IPy._checkPrefix(0x7f000000L, 0, 4))
+ self.failIf(IPy._checkPrefix(0x1L, -1, 6))
+ self.failIf(IPy._checkPrefix(0x1L, 129, 6))
+ self.failIf(IPy._checkPrefix(0xffffffffffffffff0000000000000001L, 64, 6))
+ self.failIf(IPy._checkPrefix(0xffffffffffffffff1000000000000000L, 64, 6))
+
+
+ # TODO: _checkNetaddrWorksWithPrefixlen(net, prefixlen, version):
+
+class PythonObjectBehaviour(unittest.TestCase):
+ def testIfUsuableAsDictionaryKey(self):
+ """IP Object should be usable as dictionary key"""
+ d = {}
+ d[IPy.IP('127.0.0.1')] = 1
+ d[IPy.IP('2001::1')] = 1
+ d[IPy.IP('127.0.0.0/24')] = 1
+ d[IPy.IP('2001::/64')] = 1
+
+ def testIfCanBeInteratedOver(self):
+ """It should be possible to iterate over an IP Object."""
+ i = 0
+ for x in IPy.IP('127.0.0.0/24'):
+ i += 1
+ self.assertEqual(i, 256, "iteration over a /24 should yiels 256 values")
+ i = 0
+ for x in IPy.IP('2001::/124'):
+ i += 1
+ self.assertEqual(i, 16, "iteration over a /124 should yiels 16 values")
+
+ def testIfComparesEqual(self):
+ """nets of the same base and size should be considered equal, others not"""
+ a = IPy.IP('127.0.0.0/24')
+ a2 = a
+ b = IPy.IP('127.0.0.0/24')
+ c = IPy.IP('127.0.0.0/23')
+ d = IPy.IP('127.0.0.0/22')
+ e = IPy.IP('64.0.0.0/24')
+ self.assertEqual(a2, a)
+ self.assertEqual(a2, b)
+ self.assertEqual(a, a)
+ self.assertEqual(a, b)
+ self.assertNotEqual(a, c)
+ self.assertNotEqual(a, d)
+ self.assertNotEqual(a, e)
+ self.assertNotEqual(b, c)
+ self.assertNotEqual(b, d)
+ self.assertNotEqual(b, e)
+ self.assertNotEqual(c, d)
+ self.assertNotEqual(c, e)
+ self.assertNotEqual(d, e)
+
+ def testIfContainsInt(self):
+ """__contains__() should work somewhat with ints"""
+ ip = IPy.IP('127.0.0.0/28')
+ for x in ip:
+ self.failUnless(x.int() in ip)
+ ip = IPy.IP('2001::/124')
+ for x in ip:
+ self.failUnless(x.int() in ip)
+
+ def testIfContainsStr(self):
+ """__contains__() should work somewhat with strings"""
+ ip = IPy.IP('127.0.0.0/28')
+ for x in ip:
+ self.failUnless(x.strNormal() in ip, "%r not in %r" % (x.strNormal(), ip))
+ ip = IPy.IP('2001::/124')
+ for x in ip:
+ self.failUnless(x.strNormal() in ip, "%r not in %r" % (x.strNormal(), ip))
+
+ def testIfContainsIPobj(self):
+ """__contains__() should work somewhat with IP instances"""
+ ip = IPy.IP('127.0.0.0/28')
+ for x in ip:
+ self.failUnless(x in ip)
+ ip = IPy.IP('2001::/124')
+ for x in ip:
+ self.failUnless(x in ip)
+
+ def testActingAsArray(self):
+ """An IP-object should handle indices."""
+ ip = IPy.IP('127.0.0.0/24')
+ self.assertEqual(ip[0], ip.net())
+ self.assertEqual(ip[-1], ip.broadcast())
+ self.failUnless(ip[255])
+ self.failUnlessRaises(IndexError, ip.__getitem__, 256)
+
+ def testStr(self):
+ """string() should work somewhat with IP instances"""
+ ip = IPy.IP('127.0.0.0/28')
+ for x in ip:
+ self.failUnless(str(x))
+ ip = IPy.IP('2001::/124')
+ for x in ip:
+ self.failUnless(str(x))
+
+ def testRepr(self):
+ """repr() should work somewhat with IP instances"""
+ ip = IPy.IP('127.0.0.0/28')
+ for x in ip:
+ self.failUnless(repr(x))
+ ip = IPy.IP('2001::/124')
+ for x in ip:
+ self.failUnless(repr(x))
+
+ def testLen(self):
+ """object should have an working __len__() interface."""
+ self.failUnlessEqual(len(IPy.IP('127.0.0.0/28')), 16)
+ self.failUnlessEqual(len(IPy.IP('127.0.0.0/30')), 4)
+ self.failUnlessEqual(len(IPy.IP('127.0.0.0/26')), 64)
+ self.failUnlessEqual(len(IPy.IP('127.0.0.0/16')), 2**16)
+
+ # cmp
+ # IP[0xffffffff]
+ # IP + IP
+ # reverse
+ # netmsk
+ # ip
+
+class IPobject(unittest.TestCase):
+ def testStrCompressed(self):
+ """Compressed string Output."""
+ testValues = ['127.0.0.1',
+ 'dead::beef',
+ 'dead:beef::',
+ 'dead:beef::/48',
+ 'ff00:1::',
+ 'ff00:0:f000::',
+ '0:0:1000::',
+ '::e000:0/112',
+ '::e001:0/112',
+ 'dead:beef::/48',
+ 'ff00:1::/64',
+ 'ff00:0:f000::/64',
+ '0:0:1000::/64',
+ '::e000:0/112',
+ '::e001:0/112',
+ '::1:0:0:0:2',
+ '0:1:2:3:4:5:6:7',
+ '1:2:3:4:0:5:6:7',
+ '1:2:3:4:5:6:7:0',
+ '1:0:0:2::',
+ '1:0:0:2::3',
+ '1::2:0:0:3']
+ for question in testValues:
+ result = IPy.IP(question).strCompressed()
+ self.failUnlessEqual(question, result, (question, result))
+
+ def testStrBin(self):
+ """Binary string Output."""
+
+ testValues = [('0.0.0.0', '00000000000000000000000000000000'),
+ ('0.0.0.1', '00000000000000000000000000000001'),
+ ('255.255.255.255', '11111111111111111111111111111111'),
+ ('128.0.0.0', '10000000000000000000000000000000'),
+ ('::0', '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'),
+ ('::1', '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'),
+ ('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', '11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'),
+ ('5555:5555:5555:5555:5555:5555:5555:5555', '01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101'),
+ ('aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa', '10101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010'),
+ ('85.85.85.85', '01010101010101010101010101010101'),
+ ('170.170.170.170', '10101010101010101010101010101010'),
+ ('127.0.0.1', '01111111000000000000000000000001'),
+ ('1::2:0:0:3', '00000000000000010000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000011')]
+ for (question, answer) in testValues:
+ result = IPy.IP(question).strBin()
+ self.failUnlessEqual(answer, result, (question, answer, result))
+
+ def testStrNormal(self):
+ """Normal string Output."""
+ testValues = [(338770000845734292534325025077361652240L, 'fedc:ba98:7654:3210:fedc:ba98:7654:3210'),
+ (21932261930451111902915077091070067066L, '1080:0:0:0:8:800:200c:417a'),
+ (338958331222012082418099330867817087043L, 'ff01:0:0:0:0:0:0:43'),
+ (0L, '0.0.0.0'),
+ (2130706433L, '127.0.0.1'),
+ (4294967295L, '255.255.255.255'),
+ (1L, '0.0.0.1'),
+ (3588059479L, '213.221.113.87')]
+ for (question, answer) in testValues:
+ result = IPy.IP(question).strNormal(question)
+ self.failUnlessEqual(answer, result, (question, result, answer))
+
+ def testStrFullsize(self):
+ """Normal / 0-padded string Output."""
+ testValues = [(338770000845734292534325025077361652240L, 'fedc:ba98:7654:3210:fedc:ba98:7654:3210'),
+ (21932261930451111902915077091070067066L, '1080:0000:0000:0000:0008:0800:200c:417a'),
+ (338958331222012082418099330867817087043L, 'ff01:0000:0000:0000:0000:0000:0000:0043'),
+ (0L, '0.0.0.0'),
+ (2130706433L, '127.0.0.1'),
+ (4294967295L, '255.255.255.255'),
+ (1L, '0.0.0.1'),
+ (3588059479L, '213.221.113.87')]
+ for (question, answer) in testValues:
+ result = IPy.IP(question).strFullsize(question)
+ self.failUnlessEqual(answer, result, (question, result, answer))
+
+ def testStrHex(self):
+ """Hex string Output."""
+ testValues = [(338770000845734292534325025077361652240L, '0xFEDCBA9876543210FEDCBA9876543210'),
+ (21932261930451111902915077091070067066L, '0x108000000000000000080800200C417A'),
+ (338958331222012082418099330867817087043L, '0xFF010000000000000000000000000043'),
+ (0L, '0x0'),
+ (1L, '0x1'),
+ (4294967295l, '0xFFFFFFFF'),
+ (3588059479L, '0xD5DD7157'),
+ (0x12345678, '0x12345678')]
+ for (question, answer) in testValues:
+ result = IPy.IP(question).strHex(question)
+ self.failUnlessEqual(answer, result, (question, result, answer))
+
+ def testStrDec(self):
+ """Decimal string Output."""
+ testValues = [(338770000845734292534325025077361652240L, '338770000845734292534325025077361652240'),
+ (21932261930451111902915077091070067066L, '21932261930451111902915077091070067066'),
+ (338958331222012082418099330867817087043L, '338958331222012082418099330867817087043'),
+ (0L, '0'),
+ (1L, '1'),
+ (0xFFFFFFFFL, '4294967295'),
+ (0xD5DD7157L, '3588059479')]
+ for (question, answer) in testValues:
+ result = IPy.IP(question).strDec(question)
+ self.failUnlessEqual(answer, result, (question, result, answer))
+
+ def testNet(self):
+ """Returning of the Network Address"""
+ self.failUnlessEqual(str(IPy.IP("127.0.0.1").net()), "127.0.0.1")
+ self.failUnlessEqual(str(IPy.IP("0.0.0.0/0").net()), "0.0.0.0")
+ self.failUnlessEqual(str(IPy.IP("2001:1234:5678:1234::/64").net()), "2001:1234:5678:1234:0000:0000:0000:0000")
+
+
+ def testBroadcast(self):
+ """Returning of broadcast address."""
+ self.failUnlessEqual(str(IPy.IP("127.0.0.1").broadcast()), "127.0.0.1")
+ self.failUnlessEqual(str(IPy.IP("0.0.0.0/0").broadcast()), "255.255.255.255")
+ self.failUnlessEqual(str(IPy.IP("2001:1234:5678:1234::/64").broadcast()), "2001:1234:5678:1234:ffff:ffff:ffff:ffff")
+
+
+ def testStrNetmask(self):
+ """StrNetmask should return netmasks"""
+ self.failUnlessEqual(IPy.IP("0.0.0.0/0").strNetmask(), "0.0.0.0")
+ self.failUnlessEqual(IPy.IP("0.0.0.0/32").strNetmask(), "255.255.255.255")
+ self.failUnlessEqual(IPy.IP("127.0.0.0/24").strNetmask(), "255.255.255.0")
+ self.failUnlessEqual(IPy.IP("2001:1234:5678:1234::/64").strNetmask(), "/64")
+
+
+ def testNetmask(self):
+ """Netmask should return netmasks"""
+ self.failUnlessEqual(str(IPy.IP("0.0.0.0/0").netmask()), "0.0.0.0")
+ self.failUnlessEqual(str(IPy.IP("0.0.0.0/32").netmask()), "255.255.255.255")
+ self.failUnlessEqual(str(IPy.IP("127.0.0.0/24").netmask()), "255.255.255.0")
+ self.failUnlessEqual(str(IPy.IP("2001:1234:5678:1234::/64").netmask()), "ffff:ffff:ffff:ffff:0000:0000:0000:0000")
+
+ def testInt(self):
+ """Prefixlen"""
+ self.failUnlessEqual(IPy.IP("127.0.0.1").int(), 2130706433)
+ self.failUnlessEqual(IPy.IP("0.0.0.0").int(), 0)
+ self.failUnlessEqual(IPy.IP("255.255.255.255").int(), 0xffffffffL)
+ self.failUnlessEqual(IPy.IP("0000:0000:0000:0000:0000:0000:0000:0000").int(), 0)
+ self.failUnlessEqual(IPy.IP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").int(), 0xffffffffffffffffffffffffffffffffL)
+ self.failUnlessEqual(IPy.IP("2001:1234:5678:9abc:de00:0000:0000:0000").int(), 42540857391974671903776007410583339008L)
+
+
+ def testPrefixlen(self):
+ """Prefixlen"""
+ self.failUnlessEqual(IPy.IP("127.0.0.1").prefixlen(), 32)
+ self.failUnlessEqual(IPy.IP("::1").prefixlen(), 128)
+ self.failUnlessEqual(IPy.IP("10.0.0.0/24").prefixlen(), 24)
+ self.failUnlessEqual(IPy.IP("10.0.0.0-10.0.0.255").prefixlen(), 24)
+ self.failUnlessEqual(IPy.IP("10.0.0.0/255.255.255.0").prefixlen(), 24)
+ self.failUnlessEqual(IPy.IP("2001::/64").prefixlen(), 64)
+
+
+ def testVersion(self):
+ """IP-version detection should work"""
+ self.failUnlessEqual(IPy.IP("0.0.0.0/0").version(), 4)
+ self.failUnlessEqual(IPy.IP("::1").version(), 6)
+
+ # TODO:
+ #def reverseNames(self):
+ #def reverseName(self):
+ #def __cmp__(self, other):
+ #def __add__(self, other):
+ #def _printPrefix(self, want):
+
+ def testOverlaps(self):
+ """Overlapping Address Ranges."""
+ testValues = [('192.168.0.0/23', '192.168.1.0/24', 1),
+ ('192.168.0.0/23', '192.168.0.0/20', 1),
+ ('192.168.0.0/23', '192.168.2.0', 0),
+ ('192.168.0.0/23', '192.167.255.255', 0),
+ ('192.168.0.0/23', '192.168.0.0', 1),
+ ('192.168.0.0/23', '192.168.1.255', 1),
+ ('192.168.1.0/24', '192.168.0.0/23', -1),
+ ('127.0.0.1', '127.0.0.1', 1),
+ ('127.0.0.1', '127.0.0.2', 0)]
+ for (a, b, answer) in testValues:
+ result = IPy.IP(a).overlaps(b)
+ self.failUnlessEqual(answer, result, (a, b, result, answer))
+
+ def testNetmask(self):
+ """Normal string Output."""
+ testValues = [(338770000845734292534325025077361652240L, '0xFEDCBA9876543210FEDCBA9876543210'),
+ (21932261930451111902915077091070067066L, '0x108000000000000000080800200C417A'),
+ (338958331222012082418099330867817087043L, '0xFF010000000000000000000000000043'),
+ (0L, '0x0'),
+ (1L, '0x1'),
+ (4294967295l, '0xFFFFFFFF'),
+ (3588059479L, '0xD5DD7157')]
+ for (question, answer) in testValues:
+ result = IPy.IP(question).strHex(question)
+ self.failUnlessEqual(answer, result, (question, result, answer))
+
+# TODO
+#eval(repr(IPy))
+# differences between IP and IPint
+
+
+# I ported this checks to be sure that I don't have errors in my own checks.
+class NetIPChecks(unittest.TestCase):
+ """Checks taken from perls Net::IP"""
+ def testMisc(self):
+ ip = IPy.IP('195.114.80/24')
+ self.assertEqual(ip.int(), 3279048704L)
+ self.assertEqual(ip.reverseName(),'80.114.195.in-addr.arpa.')
+ self.assertEqual(ip.strBin(),'11000011011100100101000000000000')
+ self.assertEqual(str(ip.net()),'195.114.80.0')
+ self.assertEqual(str(ip),'195.114.80.0/24')
+ self.assertEqual(ip.prefixlen(),24)
+ self.assertEqual(ip.version(),4)
+ self.assertEqual(ip.len(),256)
+ self.assertEqual(IPy._intToBin(ip.netmask().int()),'11111111111111111111111100000000')
+ self.assertEqual(ip.strNetmask(),'255.255.255.0')
+ self.assertEqual(ip.iptype(), 'PUBLIC')
+ self.assertEqual(ip.broadcast().strBin(),'11000011011100100101000011111111')
+ self.assertEqual(str(ip.broadcast()),'195.114.80.255')
+
+ ip = IPy.IP('202.31.4/24')
+ self.assertEqual(str(ip.net()),'202.31.4.0')
+
+ self.failUnlessRaises(ValueError, IPy.IP, '234.245.252.253/2')
+
+ # because we ar using integer representation we don't need a special "binadd"
+ ip = IPy.IP('62.33.41.9')
+ ip2 = IPy.IP('0.1.0.5')
+ self.assertEqual(str(IPy.IP(ip.int() + ip2.int())),'62.34.41.14')
+ #$T->ok_eq ($ip->binadd($ip2)->ip(),'62.34.41.14',$ip->error());
+
+ ip = IPy.IP('133.45.0/24')
+ ip2 = IPy.IP('133.45.1/24')
+ self.assertEqual((ip + ip2).prefixlen(),23)
+
+ ip2 = IPy.IP('133.44.255.255');
+ #$T->ok_eqnum ($ip->bincomp('gt',$ip2),1,$ip->error());
+
+ # this is something we can't do with IPy
+ #ip = IPy.IP('133.44.255.255-133.45.0.42');
+ #$T->ok_eq (($ip->find_prefixes())[3],'133.45.0.40/31',$ip->error());
+
+ ip = IPy.IP('201.33.128.0/22');
+ ip2 = IPy.IP('201.33.129.0/24');
+ #$T->ok_eqnum ($ip->overlaps($ip2),$IP_B_IN_A_OVERLAP,$ip->error());
+
+ ip = IPy.IP('dead:beef:0::/48')
+ self.assertEqual(str(ip.net()),'dead:beef:0000:0000:0000:0000:0000:0000')
+ self.assertEqual(ip.int(), 295990755014133383690938178081940045824L)
+ self.assertEqual(ip.strBin(),'11011110101011011011111011101111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
+ self.assertEqual(ip.strCompressed(),'dead:beef::/48')
+ self.assertEqual(ip.prefixlen(), 48)
+ self.assertEqual(ip.version(), 6)
+ self.assertEqual(ip.strNetmask(),'/48')
+ self.assertEqual(str(ip.netmask()),'ffff:ffff:ffff:0000:0000:0000:0000:0000')
+ self.assertEqual(ip.iptype(),'UNASSIGNED')
+ self.assertEqual(ip.reverseName(),'0.0.0.0.f.e.e.b.d.a.e.d.ip6.int.')
+ self.assertEqual(str(ip.broadcast()),'dead:beef:0000:ffff:ffff:ffff:ffff:ffff')
+
+ ip = IPy.IP('202.31.4/24')
+ self.assertEqual(str(ip.net()),'202.31.4.0')
+
+ # TODO: fix this in IPy ... after rereading the RfC
+ # ip = IPy.IP(':1/128');
+ #$T->ok_eq ($ip->error(),'Invalid address :1 (starts with :)',$ip->error());
+ #$T->ok_eqnum ($ip->errno(),109,$ip->error());
+
+ ip = IPy.IP('ff00:0:f000::')
+ ip2 = IPy.IP('0:0:1000::')
+ self.assertEqual(IPy.IP(ip.int() + ip2.int()).strCompressed(), 'ff00:1::')
+
+ ip = IPy.IP('::e000:0/112')
+ ip2 = IPy.IP('::e001:0/112')
+ self.assertEqual(ip.__add__(ip2).prefixlen(),111)
+
+ ip2 = IPy.IP('::dfff:ffff')
+ #$T->ok_eqnum ($ip->bincomp('gt',$ip2),1,$ip->error());
+
+ #ip = IPy.IP('::e000:0 - ::e002:42')
+ #$T->ok_eq (($ip->find_prefixes())[2],'0000:0000:0000:0000:0000:0000:e002:0040/127',$ip->error());
+
+ ip = IPy.IP('ffff::/16')
+ ip2 = IPy.IP('8000::/16')
+ #$T->ok_eqnum ($ip->overlaps($ip2),$IP_NO_OVERLAP,$ip->error());
+
+if __name__ == "__main__":
+ unittest.main()
+
More information about the Python-modules-commits
mailing list