├── IPy-0.60 ├── AUTHORS ├── COPYING ├── ChangeLog ├── IPy.egg-info │ ├── PKG-INFO │ ├── SOURCES.txt │ ├── dependency_links.txt │ └── top_level.txt ├── IPy.py ├── IPy.pyc ├── Makefile ├── PKG-INFO ├── README ├── build │ └── lib │ │ └── IPy.py ├── example │ ├── confbuilder │ └── confbuilder.py ├── rest.css ├── setup.app │ └── Contents │ │ ├── Info.plist │ │ ├── MacOS │ │ ├── Python │ │ └── setup │ │ ├── PkgInfo │ │ └── Resources │ │ ├── PythonApplet.icns │ │ ├── __argvemulator_setup.py │ │ └── setup.py ├── setup.cfg ├── setup.py ├── test │ ├── test.rst │ └── test_IPy.py └── test_doc.py └── modscan.py /IPy-0.60/AUTHORS: -------------------------------------------------------------------------------- 1 | Bernhard Frauendienst - fixed IPv6 parser 2 | James Teh - make_net option patch 3 | Jean Gillaux - Fix netmask "/0.0.0.0" 4 | Mark Johnston - bringing IPy to Python 2.3 5 | Matthew Flanagan - Fix strCompressed() 6 | Maximillian Dornseif - IPy author and maintainer until version 0.42 7 | Samuel Krempp - fix __cmp__() bug 8 | Shell, Hin-lik Hung - bring IPy to OpenBSD ports 9 | Skinny Puppy - __add__() now always returns a value 10 | Victor Stinner - Patch setup.py for setuptools, maintainer since version 0.5 11 | William McVey - Fix IP.__nonzero__() 12 | -------------------------------------------------------------------------------- /IPy-0.60/COPYING: -------------------------------------------------------------------------------- 1 | Copyright (c) 2006, INL 2 | Copyright (c) 2001-2005, Maximillian Dornseif 3 | All rights reserved. 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of IPy nor the names of its contributors may be used 13 | to endorse or promote products derived from this software without 14 | specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY 17 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | -------------------------------------------------------------------------------- /IPy-0.60/ChangeLog: -------------------------------------------------------------------------------- 1 | Version 0.60 2 | * strCompressed() formats '::ffff:a.b.c.d' correctly 3 | * Use strCompressed() instead of strFullsize() to format IP addresses, 4 | ouput is smarter with IPv6 address 5 | * Remove check_addr_prefixlen because it generates invalid IP address 6 | 7 | 2008-02-05 8 | * Release IPy 0.56 9 | * Fix IPv6 parser for unit tests: reject 10 | '1111::2222:3333:4444:5555:6666:7777:8888' address since '::' is 11 | useless 12 | 13 | 2007-08-16 14 | * Release IPy 0.55 15 | * Rewrite IPv6 parser to allow address "1:2:3:4:5:6::" 16 | 17 | 2007-06-22 18 | * Release IPy 0.54 19 | * make_net() match from James Teh: transform an IP address into a network 20 | address by applying the given netmask 21 | 22 | 2007-02-28 23 | * Release IPy 0.53 24 | * Reject '0.0.0.0-0.0.0.4' if check_addr_prefixlen is enable 25 | * Fix many english spelling mistakes 26 | 27 | 2006-11-06 28 | * Release IPy 0.52 29 | * Fix strCompressed() for IPv6 "ffff:ffff:ffff:ffff:ffff:f:f:fffc/127" 30 | 31 | 2006-11-02 32 | * Release IPy 0.51 33 | * Write real name of IPy author (Maximillian Dornseif) 34 | * Use version "0.51" to help packaging since 0.5 was smaller than 0.42 35 | * Fix unit test for Python 2.3 (don't use doctest.testfile) and 2.5 36 | (problem of hex() lower case) 37 | * "make test" also check IPy documentation 38 | * IPy now works on Python 2.2 to 2.5 39 | 40 | 2006-10-26 41 | * Release IPy 0.5 42 | * Apply Jean Gillaux patch for netmask "/0.0.0.0" bug 43 | * Apply William McVey patch for __nonzero__() bug 44 | * Apply Victor Stinner patch: setup.py can use setuptools and fix URLs 45 | * Allow "172.30.1.0/22" with new option IPy.check_addr_prefixlen=False 46 | * Add regression tests 47 | * Create AUTHORS file 48 | 49 | 2004-08-22 50 | * IPy 0.42 works on Python 2.3 without warnings 51 | 52 | 2002-01-16 53 | * IPy 0.41 has Python < 2.2 compatible unit tests and a README file 54 | 55 | 2001-12-22 56 | * IPy 0.4 was the first public relase 57 | -------------------------------------------------------------------------------- /IPy-0.60/IPy.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 1.0 2 | Name: IPy 3 | Version: 0.60 4 | Summary: Class and tools for handling of IPv4 and IPv6 addresses and networks 5 | Home-page: http://software.inl.fr/trac/trac.cgi/wiki/IPy 6 | Author: Victor Stinner 7 | Author-email: victor.stinner AT inl.fr 8 | License: BSD License 9 | Download-URL: http://software.inl.fr/trac/trac.cgi/wiki/IPy 10 | Description: IPy - class and tools for handling of IPv4 and IPv6 addresses and networks. 11 | 12 | Presentation of the API 13 | ======================= 14 | 15 | The IP class allows a comfortable parsing and handling for most 16 | notations in use for IPv4 and IPv6 addresses and networks. It was 17 | greatly inspired by RIPE's Perl module NET::IP's interface but 18 | doesn't share the implementation. It doesn't share non-CIDR netmasks, 19 | so funky stuff like a netmask of 0xffffff0f can't be done here. 20 | 21 | >>> from IPy import IP 22 | >>> ip = IP('127.0.0.0/30') 23 | >>> for x in ip: 24 | ... print x 25 | ... 26 | 127.0.0.0 27 | 127.0.0.1 28 | 127.0.0.2 29 | 127.0.0.3 30 | >>> ip2 = IP('0x7f000000/30') 31 | >>> ip == ip2 32 | 1 33 | >>> ip.reverseNames() 34 | ['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.'] 35 | >>> ip.reverseName() 36 | '0-3.0.0.127.in-addr.arpa.' 37 | >>> ip.iptype() 38 | 'PRIVATE' 39 | 40 | 41 | Supports most IP address formats 42 | ================================ 43 | 44 | It can detect about a dozen different ways of expressing IP addresses 45 | and networks, parse them and distinguish between IPv4 and IPv6 addresses: 46 | 47 | >>> IP('10.0.0.0/8').version() 48 | 4 49 | >>> IP('::1').version() 50 | 6 51 | 52 | IPv4 addresses 53 | -------------- 54 | 55 | >>> print IP(0x7f000001) 56 | 127.0.0.1 57 | >>> print IP('0x7f000001') 58 | 127.0.0.1 59 | >>> print IP('127.0.0.1') 60 | 127.0.0.1 61 | >>> print IP('10') 62 | 10.0.0.0 63 | 64 | IPv6 addresses 65 | -------------- 66 | 67 | >>> print IP('1080:0:0:0:8:800:200C:417A') 68 | 1080::8:800:200c:417a 69 | >>> print IP('1080::8:800:200C:417A') 70 | 1080::8:800:200c:417a 71 | >>> print IP('::1') 72 | ::1 73 | >>> print IP('::13.1.68.3') 74 | ::d01:4403 75 | 76 | Network mask and prefixes 77 | ------------------------- 78 | 79 | >>> print IP('127.0.0.0/8') 80 | 127.0.0.0/8 81 | >>> print IP('127.0.0.0/255.0.0.0') 82 | 127.0.0.0/8 83 | >>> print IP('127.0.0.0-127.255.255.255') 84 | 127.0.0.0/8 85 | 86 | 87 | Derive network address 88 | =========================== 89 | 90 | IPy can transform an IP address into a network address by applying the given 91 | netmask: 92 | >>> print IP('127.0.0.1/255.0.0.0', make_net=True) 93 | 127.0.0.0/8 94 | 95 | This can also be done for existing IP instances: 96 | >>> print IP('127.0.0.1').make_net('255.0.0.0') 97 | 127.0.0.0/8 98 | 99 | 100 | Convert address to string 101 | ========================= 102 | 103 | Nearly all class methods which return a string have an optional 104 | parameter 'wantprefixlen' which controls if the prefixlen or netmask 105 | is printed. Per default the prefilen is always shown if the network 106 | contains more than one address:: 107 | 108 | wantprefixlen == 0 / None don't return anything 1.2.3.0 109 | wantprefixlen == 1 /prefix 1.2.3.0/24 110 | wantprefixlen == 2 /netmask 1.2.3.0/255.255.255.0 111 | wantprefixlen == 3 -lastip 1.2.3.0-1.2.3.255 112 | 113 | You can also change the defaults on an per-object basis by fiddling with 114 | the class members: 115 | 116 | * NoPrefixForSingleIp 117 | * WantPrefixLen 118 | 119 | Examples of string conversions: 120 | 121 | >>> IP('10.0.0.0/32').strNormal() 122 | '10.0.0.0' 123 | >>> IP('10.0.0.0/24').strNormal() 124 | '10.0.0.0/24' 125 | >>> IP('10.0.0.0/24').strNormal(0) 126 | '10.0.0.0' 127 | >>> IP('10.0.0.0/24').strNormal(1) 128 | '10.0.0.0/24' 129 | >>> IP('10.0.0.0/24').strNormal(2) 130 | '10.0.0.0/255.255.255.0' 131 | >>> IP('10.0.0.0/24').strNormal(3) 132 | '10.0.0.0-10.0.0.255' 133 | >>> ip = IP('10.0.0.0') 134 | >>> print ip 135 | 10.0.0.0 136 | >>> ip.NoPrefixForSingleIp = None 137 | >>> print ip 138 | 10.0.0.0/32 139 | >>> ip.WantPrefixLen = 3 140 | >>> print ip 141 | 10.0.0.0-10.0.0.0 142 | 143 | 144 | Compatibility and links 145 | ======================= 146 | 147 | IPy 0.60 works on Python version 2.4 and 2.5. 148 | 149 | This Python module is under BSD license: see COPYING file. 150 | 151 | Further Information might be available at: 152 | http://software.inl.fr/trac/trac.cgi/wiki/IPy 153 | 154 | 155 | TODO 156 | ==== 157 | 158 | * better comparison (__cmp__ and friends) 159 | * tests for __cmp__ 160 | * always write hex values lowercase 161 | * interpret 2001:1234:5678:1234/64 as 2001:1234:5678:1234::/64 162 | * move size in bits into class variables to get rid of 163 | some "if self._ipversion ..." 164 | * support for base85 encoding 165 | * support for output of IPv6 encoded IPv4 Addresses 166 | * update address type tables 167 | * first-last notation should be allowed for IPv6 168 | * add IPv6 docstring examples 169 | * check better for negative parameters 170 | * add addition / aggregation 171 | * move reverse name stuff out of the classes and refactor it 172 | * support for aggregation of more than two nets at once 173 | * support for aggregation with "holes" 174 | * support for finding common prefix 175 | * '>>' and '<<' for prefix manipulation 176 | * add our own exceptions instead ValueError all the time 177 | * rename checkPrefix to checkPrefixOk 178 | * add more documentation and doctests 179 | * refactor 180 | 181 | What's new 182 | ========== 183 | 184 | Version 0.60 185 | * strCompressed() formats '::ffff:a.b.c.d' correctly 186 | * Use strCompressed() instead of strFullsize() to format IP addresses, 187 | ouput is smarter with IPv6 address 188 | * Remove check_addr_prefixlen because it generates invalid IP address 189 | 190 | 2008-02-05 191 | * Release IPy 0.56 192 | * Fix IPv6 parser for unit tests: reject 193 | '1111::2222:3333:4444:5555:6666:7777:8888' address since '::' is 194 | useless 195 | 196 | 2007-08-16 197 | * Release IPy 0.55 198 | * Rewrite IPv6 parser to allow address "1:2:3:4:5:6::" 199 | 200 | 2007-06-22 201 | * Release IPy 0.54 202 | * make_net() match from James Teh: transform an IP address into a network 203 | address by applying the given netmask 204 | 205 | 2007-02-28 206 | * Release IPy 0.53 207 | * Reject '0.0.0.0-0.0.0.4' if check_addr_prefixlen is enable 208 | * Fix many english spelling mistakes 209 | 210 | 2006-11-06 211 | * Release IPy 0.52 212 | * Fix strCompressed() for IPv6 "ffff:ffff:ffff:ffff:ffff:f:f:fffc/127" 213 | 214 | 2006-11-02 215 | * Release IPy 0.51 216 | * Write real name of IPy author (Maximillian Dornseif) 217 | * Use version "0.51" to help packaging since 0.5 was smaller than 0.42 218 | * Fix unit test for Python 2.3 (don't use doctest.testfile) and 2.5 219 | (problem of hex() lower case) 220 | * "make test" also check IPy documentation 221 | * IPy now works on Python 2.2 to 2.5 222 | 223 | 2006-10-26 224 | * Release IPy 0.5 225 | * Apply Jean Gillaux patch for netmask "/0.0.0.0" bug 226 | * Apply William McVey patch for __nonzero__() bug 227 | * Apply Victor Stinner patch: setup.py can use setuptools and fix URLs 228 | * Allow "172.30.1.0/22" with new option IPy.check_addr_prefixlen=False 229 | * Add regression tests 230 | * Create AUTHORS file 231 | 232 | 2004-08-22 233 | * IPy 0.42 works on Python 2.3 without warnings 234 | 235 | 2002-01-16 236 | * IPy 0.41 has Python < 2.2 compatible unit tests and a README file 237 | 238 | 2001-12-22 239 | * IPy 0.4 was the first public relase 240 | Keywords: ipv4 ipv6 netmask 241 | Platform: UNKNOWN 242 | Classifier: Development Status :: 5 - Production/Stable 243 | Classifier: Intended Audience :: Developers 244 | Classifier: Intended Audience :: System Administrators 245 | Classifier: Environment :: Plugins 246 | Classifier: Topic :: Software Development :: Libraries :: Python Modules 247 | Classifier: Topic :: Communications 248 | Classifier: Topic :: Internet 249 | Classifier: Topic :: System :: Networking 250 | Classifier: License :: OSI Approved :: BSD License 251 | Classifier: Operating System :: OS Independent 252 | Classifier: Natural Language :: English 253 | Classifier: Programming Language :: Python 254 | -------------------------------------------------------------------------------- /IPy-0.60/IPy.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | AUTHORS 2 | COPYING 3 | ChangeLog 4 | IPy.py 5 | Makefile 6 | README 7 | rest.css 8 | setup.py 9 | test_doc.py 10 | IPy.egg-info/PKG-INFO 11 | IPy.egg-info/SOURCES.txt 12 | IPy.egg-info/dependency_links.txt 13 | IPy.egg-info/top_level.txt 14 | example/confbuilder 15 | example/confbuilder.py 16 | test/test.rst 17 | test/test_IPy.py 18 | -------------------------------------------------------------------------------- /IPy-0.60/IPy.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /IPy-0.60/IPy.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | IPy 2 | -------------------------------------------------------------------------------- /IPy-0.60/IPy.py: -------------------------------------------------------------------------------- 1 | """ 2 | IPy - class and tools for handling of IPv4 and IPv6 addresses and networks. 3 | See README file for learn how to use IPy. 4 | 5 | Further Information might be available at: 6 | http://software.inl.fr/trac/trac.cgi/wiki/IPy 7 | """ 8 | 9 | # $HeadURL: https://svn.inl.fr/inl-svn/src/tools/ipy/trunk/IPy.py $ 10 | # $Id: IPy.py 14624 2008-05-16 09:53:05Z haypo $ 11 | 12 | __rcsid__ = '$Id: IPy.py 14624 2008-05-16 09:53:05Z haypo $' 13 | __version__ = '0.60' 14 | 15 | import types 16 | 17 | # Definition of the Ranges for IPv4 IPs 18 | # this should include www.iana.org/assignments/ipv4-address-space 19 | # and www.iana.org/assignments/multicast-addresses 20 | IPv4ranges = { 21 | '0': 'PUBLIC', # fall back 22 | '00000000': 'PRIVATE', # 0/8 23 | '00001010': 'PRIVATE', # 10/8 24 | '01111111': 'PRIVATE', # 127.0/8 25 | '1': 'PUBLIC', # fall back 26 | '1010100111111110': 'PRIVATE', # 169.254/16 27 | '101011000001': 'PRIVATE', # 172.16/12 28 | '1100000010101000': 'PRIVATE', # 192.168/16 29 | '11011111': 'RESERVED', # 223/8 30 | '111': 'RESERVED' # 224/3 31 | } 32 | 33 | # Definition of the Ranges for IPv6 IPs 34 | # see also www.iana.org/assignments/ipv6-address-space, 35 | # www.iana.org/assignments/ipv6-tla-assignments, 36 | # www.iana.org/assignments/ipv6-multicast-addresses, 37 | # www.iana.org/assignments/ipv6-anycast-addresses 38 | IPv6ranges = { 39 | '00000000' : 'RESERVED', # ::/8 40 | '00000001' : 'UNASSIGNED', # 100::/8 41 | '0000001' : 'NSAP', # 200::/7 42 | '0000010' : 'IPX', # 400::/7 43 | '0000011' : 'UNASSIGNED', # 600::/7 44 | '00001' : 'UNASSIGNED', # 800::/5 45 | '0001' : 'UNASSIGNED', # 1000::/4 46 | '0010000000000000' : 'RESERVED', # 2000::/16 Reserved 47 | '0010000000000001' : 'ASSIGNABLE', # 2001::/16 Sub-TLA Assignments [RFC2450] 48 | '00100000000000010000000': 'ASSIGNABLE IANA', # 2001:0000::/29 - 2001:01F8::/29 IANA 49 | '00100000000000010000001': 'ASSIGNABLE APNIC', # 2001:0200::/29 - 2001:03F8::/29 APNIC 50 | '00100000000000010000010': 'ASSIGNABLE ARIN', # 2001:0400::/29 - 2001:05F8::/29 ARIN 51 | '00100000000000010000011': 'ASSIGNABLE RIPE', # 2001:0600::/29 - 2001:07F8::/29 RIPE NCC 52 | '0010000000000010' : '6TO4', # 2002::/16 "6to4" [RFC3056] 53 | '0011111111111110' : '6BONE', # 3FFE::/16 6bone Testing [RFC2471] 54 | '0011111111111111' : 'RESERVED', # 3FFF::/16 Reserved 55 | '010' : 'GLOBAL-UNICAST', # 4000::/3 56 | '011' : 'UNASSIGNED', # 6000::/3 57 | '100' : 'GEO-UNICAST', # 8000::/3 58 | '101' : 'UNASSIGNED', # A000::/3 59 | '110' : 'UNASSIGNED', # C000::/3 60 | '1110' : 'UNASSIGNED', # E000::/4 61 | '11110' : 'UNASSIGNED', # F000::/5 62 | '111110' : 'UNASSIGNED', # F800::/6 63 | '1111110' : 'UNASSIGNED', # FC00::/7 64 | '111111100' : 'UNASSIGNED', # FE00::/9 65 | '1111111010' : 'LINKLOCAL', # FE80::/10 66 | '1111111011' : 'SITELOCAL', # FEC0::/10 67 | '11111111' : 'MULTICAST', # FF00::/8 68 | '0' * 96 : 'IPV4COMP', # ::/96 69 | '0' * 80 + '1' * 16 : 'IPV4MAP', # ::FFFF:0:0/96 70 | '0' * 128 : 'UNSPECIFIED', # ::/128 71 | '0' * 127 + '1' : 'LOOPBACK' # ::1/128 72 | } 73 | 74 | 75 | class IPint: 76 | """Handling of IP addresses returning integers. 77 | 78 | Use class IP instead because some features are not implemented for 79 | IPint.""" 80 | 81 | def __init__(self, data, ipversion = 0, make_net = 0): 82 | """Create an instance of an IP object. 83 | 84 | Data can be a network specification or a single IP. IP 85 | addresses can be specified in all forms understood by 86 | parseAddress(). The size of a network can be specified as 87 | 88 | /prefixlen a.b.c.0/24 2001:658:22a:cafe::/64 89 | -lastIP a.b.c.0-a.b.c.255 2001:658:22a:cafe::-2001:658:22a:cafe:ffff:ffff:ffff:ffff 90 | /decimal netmask a.b.c.d/255.255.255.0 not supported for IPv6 91 | 92 | If no size specification is given a size of 1 address (/32 for 93 | IPv4 and /128 for IPv6) is assumed. 94 | 95 | If make_net is True, an IP address will be transformed into the network 96 | address by applying the specified netmask. 97 | 98 | >>> print IP('127.0.0.0/8') 99 | 127.0.0.0/8 100 | >>> print IP('127.0.0.0/255.0.0.0') 101 | 127.0.0.0/8 102 | >>> print IP('127.0.0.0-127.255.255.255') 103 | 127.0.0.0/8 104 | >>> print IP('127.0.0.1/255.0.0.0', make_net=True) 105 | 127.0.0.0/8 106 | 107 | See module documentation for more examples. 108 | """ 109 | 110 | # Print no Prefixlen for /32 and /128 111 | self.NoPrefixForSingleIp = 1 112 | 113 | # Do we want prefix printed by default? see _printPrefix() 114 | self.WantPrefixLen = None 115 | 116 | netbits = 0 117 | prefixlen = -1 118 | 119 | # handling of non string values in constructor 120 | if type(data) == types.IntType or type(data) == types.LongType: 121 | self.ip = long(data) 122 | if ipversion == 0: 123 | if self.ip < 0x100000000L: 124 | ipversion = 4 125 | else: 126 | ipversion = 6 127 | if ipversion == 4: 128 | prefixlen = 32 129 | elif ipversion == 6: 130 | prefixlen = 128 131 | else: 132 | raise ValueError, "only IPv4 and IPv6 supported" 133 | self._ipversion = ipversion 134 | self._prefixlen = prefixlen 135 | # handle IP instance as an parameter 136 | elif isinstance(data, IPint): 137 | self._ipversion = data._ipversion 138 | self._prefixlen = data._prefixlen 139 | self.ip = data.ip 140 | else: 141 | # TODO: refactor me! 142 | # splitting of a string into IP and prefixlen et. al. 143 | x = data.split('-') 144 | if len(x) == 2: 145 | # a.b.c.0-a.b.c.255 specification ? 146 | (ip, last) = x 147 | (self.ip, parsedVersion) = parseAddress(ip) 148 | if parsedVersion != 4: 149 | raise ValueError, "first-last notation only allowed for IPv4" 150 | (last, lastversion) = parseAddress(last) 151 | if lastversion != 4: 152 | raise ValueError, "last address should be IPv4, too" 153 | if last < self.ip: 154 | raise ValueError, "last address should be larger than first" 155 | size = last - self.ip 156 | netbits = _count1Bits(size) 157 | # make sure the broadcast is the same as the last ip 158 | # otherwise it will return /16 for something like: 159 | # 192.168.0.0-192.168.191.255 160 | if IP('%s/%s' % (ip, 32-netbits)).broadcast().int() != last: 161 | raise ValueError, \ 162 | "the range %s is not on a network boundary." % data 163 | elif len(x) == 1: 164 | x = data.split('/') 165 | # if no prefix is given use defaults 166 | if len(x) == 1: 167 | ip = x[0] 168 | prefixlen = -1 169 | elif len(x) > 2: 170 | raise ValueError, "only one '/' allowed in IP Address" 171 | else: 172 | (ip, prefixlen) = x 173 | if prefixlen.find('.') != -1: 174 | # check if the user might have used a netmask like 175 | # a.b.c.d/255.255.255.0 176 | (netmask, vers) = parseAddress(prefixlen) 177 | if vers != 4: 178 | raise ValueError, "netmask must be IPv4" 179 | prefixlen = _netmaskToPrefixlen(netmask) 180 | elif len(x) > 2: 181 | raise ValueError, "only one '-' allowed in IP Address" 182 | else: 183 | raise ValueError, "can't parse" 184 | 185 | (self.ip, parsedVersion) = parseAddress(ip) 186 | if ipversion == 0: 187 | ipversion = parsedVersion 188 | if prefixlen == -1: 189 | if ipversion == 4: 190 | prefixlen = 32 - netbits 191 | elif ipversion == 6: 192 | prefixlen = 128 - netbits 193 | else: 194 | raise ValueError, "only IPv4 and IPv6 supported" 195 | self._ipversion = ipversion 196 | self._prefixlen = int(prefixlen) 197 | 198 | if make_net: 199 | self.ip = self.ip & _prefixlenToNetmask(self._prefixlen, self._ipversion) 200 | 201 | if not _checkNetaddrWorksWithPrefixlen(self.ip, 202 | self._prefixlen, self._ipversion): 203 | raise ValueError, "%s has invalid prefix length (%s)" % (repr(self), self._prefixlen) 204 | 205 | def int(self): 206 | """Return the first / base / network addess as an (long) integer. 207 | 208 | The same as IP[0]. 209 | 210 | >>> "%X" % IP('10.0.0.0/8').int() 211 | 'A000000' 212 | """ 213 | return self.ip 214 | 215 | def version(self): 216 | """Return the IP version of this Object. 217 | 218 | >>> IP('10.0.0.0/8').version() 219 | 4 220 | >>> IP('::1').version() 221 | 6 222 | """ 223 | return self._ipversion 224 | 225 | def prefixlen(self): 226 | """Returns Network Prefixlen. 227 | 228 | >>> IP('10.0.0.0/8').prefixlen() 229 | 8 230 | """ 231 | return self._prefixlen 232 | 233 | def net(self): 234 | """ 235 | Return the base (first) address of a network as an (long) integer. 236 | """ 237 | return self.int() 238 | 239 | def broadcast(self): 240 | """ 241 | Return the broadcast (last) address of a network as an (long) integer. 242 | 243 | The same as IP[-1].""" 244 | return self.int() + self.len() - 1 245 | 246 | def _printPrefix(self, want): 247 | """Prints Prefixlen/Netmask. 248 | 249 | Not really. In fact it is our universal Netmask/Prefixlen printer. 250 | This is considered an internal function. 251 | 252 | want == 0 / None don't return anything 1.2.3.0 253 | want == 1 /prefix 1.2.3.0/24 254 | want == 2 /netmask 1.2.3.0/255.255.255.0 255 | want == 3 -lastip 1.2.3.0-1.2.3.255 256 | """ 257 | 258 | if (self._ipversion == 4 and self._prefixlen == 32) or \ 259 | (self._ipversion == 6 and self._prefixlen == 128): 260 | if self.NoPrefixForSingleIp: 261 | want = 0 262 | if want == None: 263 | want = self.WantPrefixLen 264 | if want == None: 265 | want = 1 266 | if want: 267 | if want == 2: 268 | # this should work with IP and IPint 269 | netmask = self.netmask() 270 | if type(netmask) != types.IntType \ 271 | and type(netmask) != types.LongType: 272 | netmask = netmask.int() 273 | return "/%s" % (intToIp(netmask, self._ipversion)) 274 | elif want == 3: 275 | return "-%s" % (intToIp(self.ip + self.len() - 1, self._ipversion)) 276 | else: 277 | # default 278 | return "/%d" % (self._prefixlen) 279 | else: 280 | return '' 281 | 282 | # We have different flavours to convert to: 283 | # strFullsize 127.0.0.1 2001:0658:022a:cafe:0200:c0ff:fe8d:08fa 284 | # strNormal 127.0.0.1 2001:658:22a:cafe:200:c0ff:fe8d:08fa 285 | # strCompressed 127.0.0.1 2001:658:22a:cafe::1 286 | # strHex 0x7F000001L 0x20010658022ACAFE0200C0FFFE8D08FA 287 | # strDec 2130706433 42540616829182469433547974687817795834 288 | 289 | def strBin(self, wantprefixlen = None): 290 | """Return a string representation as a binary value. 291 | 292 | >>> print IP('127.0.0.1').strBin() 293 | 01111111000000000000000000000001 294 | """ 295 | 296 | 297 | if self._ipversion == 4: 298 | bits = 32 299 | elif self._ipversion == 6: 300 | bits = 128 301 | else: 302 | raise ValueError, "only IPv4 and IPv6 supported" 303 | 304 | if self.WantPrefixLen == None and wantprefixlen == None: 305 | wantprefixlen = 0 306 | ret = _intToBin(self.ip) 307 | return '0' * (bits - len(ret)) + ret + self._printPrefix(wantprefixlen) 308 | 309 | def strCompressed(self, wantprefixlen = None): 310 | """Return a string representation in compressed format using '::' Notation. 311 | 312 | >>> IP('127.0.0.1').strCompressed() 313 | '127.0.0.1' 314 | >>> IP('2001:0658:022a:cafe:0200::1').strCompressed() 315 | '2001:658:22a:cafe:200::1' 316 | >>> IP('ffff:ffff:ffff:ffff:ffff:f:f:fffc/127').strCompressed() 317 | 'ffff:ffff:ffff:ffff:ffff:f:f:fffc/127' 318 | """ 319 | 320 | if self.WantPrefixLen == None and wantprefixlen == None: 321 | wantprefixlen = 1 322 | 323 | if self._ipversion == 4: 324 | return self.strFullsize(wantprefixlen) 325 | else: 326 | if self.ip >> 32 == 0xffff: 327 | return "::ffff:%s" % intToIp(self.ip & 0xffffffff, 4) 328 | # find the longest sequence of '0' 329 | hextets = [int(x, 16) for x in self.strFullsize(0).split(':')] 330 | # every element of followingzeros will contain the number of zeros 331 | # following the corresponding element of hextets 332 | followingzeros = [0] * 8 333 | for i in range(len(hextets)): 334 | followingzeros[i] = _countFollowingZeros(hextets[i:]) 335 | # compressionpos is the position where we can start removing zeros 336 | compressionpos = followingzeros.index(max(followingzeros)) 337 | if max(followingzeros) > 1: 338 | # genererate string with the longest number of zeros cut out 339 | # now we need hextets as strings 340 | hextets = [x for x in self.strNormal(0).split(':')] 341 | while compressionpos < len(hextets) and hextets[compressionpos] == '0': 342 | del(hextets[compressionpos]) 343 | hextets.insert(compressionpos, '') 344 | if compressionpos + 1 >= len(hextets): 345 | hextets.append('') 346 | if compressionpos == 0: 347 | hextets = [''] + hextets 348 | return ':'.join(hextets) + self._printPrefix(wantprefixlen) 349 | else: 350 | return self.strNormal(0) + self._printPrefix(wantprefixlen) 351 | 352 | def strNormal(self, wantprefixlen = None): 353 | """Return a string representation in the usual format. 354 | 355 | >>> print IP('127.0.0.1').strNormal() 356 | 127.0.0.1 357 | >>> print IP('2001:0658:022a:cafe:0200::1').strNormal() 358 | 2001:658:22a:cafe:200:0:0:1 359 | """ 360 | 361 | if self.WantPrefixLen == None and wantprefixlen == None: 362 | wantprefixlen = 1 363 | 364 | if self._ipversion == 4: 365 | ret = self.strFullsize(0) 366 | elif self._ipversion == 6: 367 | ret = ':'.join([hex(x)[2:] for x in [int(x, 16) for x in self.strFullsize(0).split(':')]]) 368 | else: 369 | raise ValueError, "only IPv4 and IPv6 supported" 370 | 371 | 372 | 373 | return ret + self._printPrefix(wantprefixlen) 374 | 375 | def strFullsize(self, wantprefixlen = None): 376 | """Return a string representation in the non-mangled format. 377 | 378 | >>> print IP('127.0.0.1').strFullsize() 379 | 127.0.0.1 380 | >>> print IP('2001:0658:022a:cafe:0200::1').strFullsize() 381 | 2001:0658:022a:cafe:0200:0000:0000:0001 382 | """ 383 | 384 | if self.WantPrefixLen == None and wantprefixlen == None: 385 | wantprefixlen = 1 386 | 387 | return intToIp(self.ip, self._ipversion).lower() + self._printPrefix(wantprefixlen) 388 | 389 | def strHex(self, wantprefixlen = None): 390 | """Return a string representation in hex format in lower case. 391 | 392 | >>> IP('127.0.0.1').strHex() 393 | '0x7f000001' 394 | >>> IP('2001:0658:022a:cafe:0200::1').strHex() 395 | '0x20010658022acafe0200000000000001' 396 | """ 397 | 398 | if self.WantPrefixLen == None and wantprefixlen == None: 399 | wantprefixlen = 0 400 | 401 | x = hex(self.ip) 402 | if x[-1] == 'L': 403 | x = x[:-1] 404 | return x.lower() + self._printPrefix(wantprefixlen) 405 | 406 | def strDec(self, wantprefixlen = None): 407 | """Return a string representation in decimal format. 408 | 409 | >>> print IP('127.0.0.1').strDec() 410 | 2130706433 411 | >>> print IP('2001:0658:022a:cafe:0200::1').strDec() 412 | 42540616829182469433547762482097946625 413 | """ 414 | 415 | if self.WantPrefixLen == None and wantprefixlen == None: 416 | wantprefixlen = 0 417 | 418 | x = str(self.ip) 419 | if x[-1] == 'L': 420 | x = x[:-1] 421 | return x + self._printPrefix(wantprefixlen) 422 | 423 | def iptype(self): 424 | """Return a description of the IP type ('PRIVATE', 'RESERVERD', etc). 425 | 426 | >>> print IP('127.0.0.1').iptype() 427 | PRIVATE 428 | >>> print IP('192.168.1.1').iptype() 429 | PRIVATE 430 | >>> print IP('195.185.1.2').iptype() 431 | PUBLIC 432 | >>> print IP('::1').iptype() 433 | LOOPBACK 434 | >>> print IP('2001:0658:022a:cafe:0200::1').iptype() 435 | ASSIGNABLE RIPE 436 | 437 | The type information for IPv6 is out of sync with reality. 438 | """ 439 | 440 | # this could be greatly improved 441 | 442 | if self._ipversion == 4: 443 | iprange = IPv4ranges 444 | elif self._ipversion == 6: 445 | iprange = IPv6ranges 446 | else: 447 | raise ValueError, "only IPv4 and IPv6 supported" 448 | 449 | bits = self.strBin() 450 | for i in range(len(bits), 0, -1): 451 | if iprange.has_key(bits[:i]): 452 | return iprange[bits[:i]] 453 | return "unknown" 454 | 455 | 456 | def netmask(self): 457 | """Return netmask as an integer. 458 | 459 | >>> "%X" % IP('195.185.0.0/16').netmask().int() 460 | 'FFFF0000' 461 | """ 462 | 463 | # TODO: unify with prefixlenToNetmask? 464 | if self._ipversion == 4: 465 | locallen = 32 - self._prefixlen 466 | elif self._ipversion == 6: 467 | locallen = 128 - self._prefixlen 468 | else: 469 | raise ValueError, "only IPv4 and IPv6 supported" 470 | 471 | return ((2L ** self._prefixlen) - 1) << locallen 472 | 473 | 474 | def strNetmask(self): 475 | """Return netmask as an string. Mostly useful for IPv6. 476 | 477 | >>> print IP('195.185.0.0/16').strNetmask() 478 | 255.255.0.0 479 | >>> print IP('2001:0658:022a:cafe::0/64').strNetmask() 480 | /64 481 | """ 482 | 483 | # TODO: unify with prefixlenToNetmask? 484 | if self._ipversion == 4: 485 | locallen = 32 - self._prefixlen 486 | return intToIp(((2L ** self._prefixlen) - 1) << locallen, 4) 487 | elif self._ipversion == 6: 488 | locallen = 128 - self._prefixlen 489 | return "/%d" % self._prefixlen 490 | else: 491 | raise ValueError, "only IPv4 and IPv6 supported" 492 | 493 | def len(self): 494 | """Return the length of a subnet. 495 | 496 | >>> print IP('195.185.1.0/28').len() 497 | 16 498 | >>> print IP('195.185.1.0/24').len() 499 | 256 500 | """ 501 | 502 | if self._ipversion == 4: 503 | locallen = 32 - self._prefixlen 504 | elif self._ipversion == 6: 505 | locallen = 128 - self._prefixlen 506 | else: 507 | raise ValueError, "only IPv4 and IPv6 supported" 508 | 509 | return 2L ** locallen 510 | 511 | 512 | def __nonzero__(self): 513 | """All IPy objects should evaluate to true in boolean context. 514 | Ordinarily they do, but if handling a default route expressed as 515 | 0.0.0.0/0, the __len__() of the object becomes 0, which is used 516 | as the boolean value of the object. 517 | """ 518 | return 1 519 | 520 | 521 | def __len__(self): 522 | """Return the length of a subnet. 523 | 524 | Called to implement the built-in function len(). 525 | It breaks with IPv6 Networks. Anybody knows how to fix this.""" 526 | 527 | # Python < 2.2 has this silly restriction which breaks IPv6 528 | # how about Python >= 2.2 ... ouch - it persists! 529 | 530 | return int(self.len()) 531 | 532 | 533 | def __getitem__(self, key): 534 | """Called to implement evaluation of self[key]. 535 | 536 | >>> ip=IP('127.0.0.0/30') 537 | >>> for x in ip: 538 | ... print repr(x) 539 | ... 540 | IP('127.0.0.0') 541 | IP('127.0.0.1') 542 | IP('127.0.0.2') 543 | IP('127.0.0.3') 544 | >>> ip[2] 545 | IP('127.0.0.2') 546 | >>> ip[-1] 547 | IP('127.0.0.3') 548 | """ 549 | 550 | if type(key) != types.IntType and type(key) != types.LongType: 551 | raise TypeError 552 | if abs(key) >= self.len(): 553 | raise IndexError 554 | if key < 0: 555 | key = self.len() - abs(key) 556 | 557 | return self.ip + long(key) 558 | 559 | 560 | 561 | def __contains__(self, item): 562 | """Called to implement membership test operators. 563 | 564 | Should return true if item is in self, false otherwise. Item 565 | can be other IP-objects, strings or ints. 566 | 567 | >>> IP('195.185.1.1').strHex() 568 | '0xc3b90101' 569 | >>> 0xC3B90101L in IP('195.185.1.0/24') 570 | 1 571 | >>> '127.0.0.1' in IP('127.0.0.0/24') 572 | 1 573 | >>> IP('127.0.0.0/24') in IP('127.0.0.0/25') 574 | 0 575 | """ 576 | 577 | item = IP(item) 578 | if item.ip >= self.ip and item.ip < self.ip + self.len() - item.len() + 1: 579 | return 1 580 | else: 581 | return 0 582 | 583 | 584 | def overlaps(self, item): 585 | """Check if two IP address ranges overlap. 586 | 587 | Returns 0 if the two ranges don't overlap, 1 if the given 588 | range overlaps at the end and -1 if it does at the beginning. 589 | 590 | >>> IP('192.168.0.0/23').overlaps('192.168.1.0/24') 591 | 1 592 | >>> IP('192.168.0.0/23').overlaps('192.168.1.255') 593 | 1 594 | >>> IP('192.168.0.0/23').overlaps('192.168.2.0') 595 | 0 596 | >>> IP('192.168.1.0/24').overlaps('192.168.0.0/23') 597 | -1 598 | """ 599 | 600 | item = IP(item) 601 | if item.ip >= self.ip and item.ip < self.ip + self.len(): 602 | return 1 603 | elif self.ip >= item.ip and self.ip < item.ip + item.len(): 604 | return -1 605 | else: 606 | return 0 607 | 608 | 609 | def __str__(self): 610 | """Dispatch to the prefered String Representation. 611 | 612 | Used to implement str(IP).""" 613 | 614 | return self.strCompressed() 615 | 616 | 617 | def __repr__(self): 618 | """Print a representation of the Object. 619 | 620 | Used to implement repr(IP). Returns a string which evaluates 621 | to an identical Object (without the wantprefixlen stuff - see 622 | module docstring. 623 | 624 | >>> print repr(IP('10.0.0.0/24')) 625 | IP('10.0.0.0/24') 626 | """ 627 | 628 | return("IPint('%s')" % (self.strCompressed(1))) 629 | 630 | 631 | def __cmp__(self, other): 632 | """Called by comparison operations. 633 | 634 | Should return a negative integer if self < other, zero if self 635 | == other, a positive integer if self > other. 636 | 637 | Networks with different prefixlen are considered non-equal. 638 | Networks with the same prefixlen and differing addresses are 639 | considered non equal but are compared by their base address 640 | integer value to aid sorting of IP objects. 641 | 642 | The version of Objects is not put into consideration. 643 | 644 | >>> IP('10.0.0.0/24') > IP('10.0.0.0') 645 | 1 646 | >>> IP('10.0.0.0/24') < IP('10.0.0.0') 647 | 0 648 | >>> IP('10.0.0.0/24') < IP('12.0.0.0/24') 649 | 1 650 | >>> IP('10.0.0.0/24') > IP('12.0.0.0/24') 651 | 0 652 | 653 | """ 654 | 655 | # Im not really sure if this is "the right thing to do" 656 | if self._prefixlen < other.prefixlen(): 657 | return (other.prefixlen() - self._prefixlen) 658 | elif self._prefixlen > other.prefixlen(): 659 | 660 | # Fixed bySamuel Krempp : 661 | 662 | # The bug is quite obvious really (as 99% bugs are once 663 | # spotted, isn't it ? ;-) Because of precedence of 664 | # multiplication by -1 over the substraction, prefixlen 665 | # differences were causing the __cmp__ function to always 666 | # return positive numbers, thus the function was failing 667 | # the basic assumptions for a __cmp__ function. 668 | 669 | # Namely we could have (a > b AND b > a), when the 670 | # prefixlen of a and b are different. (eg let 671 | # a=IP("1.0.0.0/24"); b=IP("2.0.0.0/16");) thus, anything 672 | # could happen when launching a sort algorithm.. 673 | # everything's in order with the trivial, attached patch. 674 | 675 | return (self._prefixlen - other.prefixlen()) * -1 676 | else: 677 | if self.ip < other.ip: 678 | return -1 679 | elif self.ip > other.ip: 680 | return 1 681 | else: 682 | return 0 683 | 684 | 685 | def __hash__(self): 686 | """Called for the key object for dictionary operations, and by 687 | the built-in function hash(). Should return a 32-bit integer 688 | usable as a hash value for dictionary operations. The only 689 | required property is that objects which compare equal have the 690 | same hash value 691 | 692 | >>> IP('10.0.0.0/24').__hash__() 693 | -167772185 694 | """ 695 | 696 | thehash = int(-1) 697 | ip = self.ip 698 | while ip > 0: 699 | thehash = thehash ^ (ip & 0x7fffffff) 700 | ip = ip >> 32 701 | thehash = thehash ^ self._prefixlen 702 | return int(thehash) 703 | 704 | 705 | class IP(IPint): 706 | """Class for handling IP addresses and networks.""" 707 | 708 | def net(self): 709 | """Return the base (first) address of a network as an IP object. 710 | 711 | The same as IP[0]. 712 | 713 | >>> IP('10.0.0.0/8').net() 714 | IP('10.0.0.0') 715 | """ 716 | return IP(IPint.net(self)) 717 | 718 | def broadcast(self): 719 | """Return the broadcast (last) address of a network as an IP object. 720 | 721 | The same as IP[-1]. 722 | 723 | >>> IP('10.0.0.0/8').broadcast() 724 | IP('10.255.255.255') 725 | """ 726 | return IP(IPint.broadcast(self)) 727 | 728 | def netmask(self): 729 | """Return netmask as an IP object. 730 | 731 | >>> IP('10.0.0.0/8').netmask() 732 | IP('255.0.0.0') 733 | """ 734 | return IP(IPint.netmask(self)) 735 | 736 | 737 | def reverseNames(self): 738 | """Return a list with values forming the reverse lookup. 739 | 740 | >>> IP('213.221.113.87/32').reverseNames() 741 | ['87.113.221.213.in-addr.arpa.'] 742 | >>> IP('213.221.112.224/30').reverseNames() 743 | ['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.'] 744 | >>> IP('127.0.0.0/24').reverseNames() 745 | ['0.0.127.in-addr.arpa.'] 746 | >>> IP('127.0.0.0/23').reverseNames() 747 | ['0.0.127.in-addr.arpa.', '1.0.127.in-addr.arpa.'] 748 | >>> IP('127.0.0.0/16').reverseNames() 749 | ['0.127.in-addr.arpa.'] 750 | >>> IP('127.0.0.0/15').reverseNames() 751 | ['0.127.in-addr.arpa.', '1.127.in-addr.arpa.'] 752 | >>> IP('128.0.0.0/8').reverseNames() 753 | ['128.in-addr.arpa.'] 754 | >>> IP('128.0.0.0/7').reverseNames() 755 | ['128.in-addr.arpa.', '129.in-addr.arpa.'] 756 | 757 | """ 758 | 759 | if self._ipversion == 4: 760 | ret = [] 761 | # TODO: Refactor. Add support for IPint objects 762 | if self.len() < 2**8: 763 | for x in self: 764 | ret.append(x.reverseName()) 765 | elif self.len() < 2**16L: 766 | for i in range(0, self.len(), 2**8): 767 | ret.append(self[i].reverseName()[2:]) 768 | elif self.len() < 2**24L: 769 | for i in range(0, self.len(), 2**16): 770 | ret.append(self[i].reverseName()[4:]) 771 | else: 772 | for i in range(0, self.len(), 2**24): 773 | ret.append(self[i].reverseName()[6:]) 774 | return ret 775 | elif self._ipversion == 6: 776 | s = hex(self.ip)[2:].lower() 777 | if s[-1] == 'l': 778 | s = s[:-1] 779 | if self._prefixlen % 4 != 0: 780 | raise NotImplementedError, "can't create IPv6 reverse names at sub nibble level" 781 | s = list(s) 782 | s.reverse() 783 | s = '.'.join(s) 784 | first_nibble_index = int(32 - (self._prefixlen / 4)) * 2 785 | return ["%s.ip6.int." % s[first_nibble_index:]] 786 | else: 787 | raise ValueError, "only IPv4 and IPv6 supported" 788 | 789 | 790 | 791 | def reverseName(self): 792 | """Return the value for reverse lookup/PTR records as RFC 2317 look alike. 793 | 794 | RFC 2317 is an ugly hack which only works for sub-/24 e.g. not 795 | for /23. Do not use it. Better set up a zone for every 796 | address. See reverseName for a way to achieve that. 797 | 798 | >>> print IP('195.185.1.1').reverseName() 799 | 1.1.185.195.in-addr.arpa. 800 | >>> print IP('195.185.1.0/28').reverseName() 801 | 0-15.1.185.195.in-addr.arpa. 802 | """ 803 | 804 | if self._ipversion == 4: 805 | s = self.strFullsize(0) 806 | s = s.split('.') 807 | s.reverse() 808 | first_byte_index = int(4 - (self._prefixlen / 8)) 809 | if self._prefixlen % 8 != 0: 810 | nibblepart = "%s-%s" % (s[3-(self._prefixlen / 8)], intToIp(self.ip + self.len() - 1, 4).split('.')[-1]) 811 | if nibblepart[-1] == 'l': 812 | nibblepart = nibblepart[:-1] 813 | nibblepart += '.' 814 | else: 815 | nibblepart = "" 816 | 817 | s = '.'.join(s[first_byte_index:]) 818 | return "%s%s.in-addr.arpa." % (nibblepart, s) 819 | 820 | elif self._ipversion == 6: 821 | s = hex(self.ip)[2:].lower() 822 | if s[-1] == 'l': 823 | s = s[:-1] 824 | if self._prefixlen % 4 != 0: 825 | nibblepart = "%s-%s" % (s[self._prefixlen:], hex(self.ip + self.len() - 1)[2:].lower()) 826 | if nibblepart[-1] == 'l': 827 | nibblepart = nibblepart[:-1] 828 | nibblepart += '.' 829 | else: 830 | nibblepart = "" 831 | s = list(s) 832 | s.reverse() 833 | s = '.'.join(s) 834 | first_nibble_index = int(32 - (self._prefixlen / 4)) * 2 835 | return "%s%s.ip6.int." % (nibblepart, s[first_nibble_index:]) 836 | else: 837 | raise ValueError, "only IPv4 and IPv6 supported" 838 | 839 | def make_net(self, netmask): 840 | """Transform a single IP address into a network specification by 841 | applying the given netmask. 842 | 843 | Returns a new IP instance. 844 | 845 | >>> print IP('127.0.0.1').make_net('255.0.0.0') 846 | 127.0.0.0/8 847 | """ 848 | if '/' in str(netmask): 849 | raise ValueError, "invalid netmask (%s)" % netmask 850 | return IP('%s/%s' % (self, netmask), make_net=True) 851 | 852 | def __getitem__(self, key): 853 | """Called to implement evaluation of self[key]. 854 | 855 | >>> ip=IP('127.0.0.0/30') 856 | >>> for x in ip: 857 | ... print str(x) 858 | ... 859 | 127.0.0.0 860 | 127.0.0.1 861 | 127.0.0.2 862 | 127.0.0.3 863 | >>> print str(ip[2]) 864 | 127.0.0.2 865 | >>> print str(ip[-1]) 866 | 127.0.0.3 867 | """ 868 | return IP(IPint.__getitem__(self, key)) 869 | 870 | def __repr__(self): 871 | """Print a representation of the Object. 872 | 873 | >>> IP('10.0.0.0/8') 874 | IP('10.0.0.0/8') 875 | """ 876 | 877 | return("IP('%s')" % (self.strCompressed(1))) 878 | 879 | def __add__(self, other): 880 | """Emulate numeric objects through network aggregation""" 881 | if self.prefixlen() != other.prefixlen(): 882 | raise ValueError, "Only networks with the same prefixlen can be added." 883 | if self.prefixlen < 1: 884 | raise ValueError, "Networks with a prefixlen longer than /1 can't be added." 885 | if self.version() != other.version(): 886 | raise ValueError, "Only networks with the same IP version can be added." 887 | if self > other: 888 | # fixed by Skinny Puppy 889 | return other.__add__(self) 890 | else: 891 | ret = IP(self.int()) 892 | ret._prefixlen = self.prefixlen() - 1 893 | return ret 894 | 895 | 896 | def _parseAddressIPv6(ipstr): 897 | """ 898 | Internal function used by parseAddress() to parse IPv6 address with ':'. 899 | 900 | >>> _parseAddressIPv6('::') 901 | 0L 902 | >>> _parseAddressIPv6('::1') 903 | 1L 904 | >>> _parseAddressIPv6('0:0:0:0:0:0:0:1') 905 | 1L 906 | >>> _parseAddressIPv6('0:0:0::0:0:1') 907 | 1L 908 | >>> _parseAddressIPv6('0:0:0:0:0:0:0:0') 909 | 0L 910 | >>> _parseAddressIPv6('0:0:0::0:0:0') 911 | 0L 912 | 913 | >>> _parseAddressIPv6('FEDC:BA98:7654:3210:FEDC:BA98:7654:3210') 914 | 338770000845734292534325025077361652240L 915 | >>> _parseAddressIPv6('1080:0000:0000:0000:0008:0800:200C:417A') 916 | 21932261930451111902915077091070067066L 917 | >>> _parseAddressIPv6('1080:0:0:0:8:800:200C:417A') 918 | 21932261930451111902915077091070067066L 919 | >>> _parseAddressIPv6('1080:0::8:800:200C:417A') 920 | 21932261930451111902915077091070067066L 921 | >>> _parseAddressIPv6('1080::8:800:200C:417A') 922 | 21932261930451111902915077091070067066L 923 | >>> _parseAddressIPv6('FF01:0:0:0:0:0:0:43') 924 | 338958331222012082418099330867817087043L 925 | >>> _parseAddressIPv6('FF01:0:0::0:0:43') 926 | 338958331222012082418099330867817087043L 927 | >>> _parseAddressIPv6('FF01::43') 928 | 338958331222012082418099330867817087043L 929 | >>> _parseAddressIPv6('0:0:0:0:0:0:13.1.68.3') 930 | 218186755L 931 | >>> _parseAddressIPv6('::13.1.68.3') 932 | 218186755L 933 | >>> _parseAddressIPv6('0:0:0:0:0:FFFF:129.144.52.38') 934 | 281472855454758L 935 | >>> _parseAddressIPv6('::FFFF:129.144.52.38') 936 | 281472855454758L 937 | >>> _parseAddressIPv6('1080:0:0:0:8:800:200C:417A') 938 | 21932261930451111902915077091070067066L 939 | >>> _parseAddressIPv6('1080::8:800:200C:417A') 940 | 21932261930451111902915077091070067066L 941 | >>> _parseAddressIPv6('::1:2:3:4:5:6') 942 | 1208962713947218704138246L 943 | >>> _parseAddressIPv6('1:2:3:4:5:6::') 944 | 5192455318486707404433266432802816L 945 | """ 946 | 947 | # Split string into a list, example: 948 | # '1080:200C::417A' => ['1080', '200C', '417A'] and fill_pos=2 949 | # and fill_pos is the position of '::' in the list 950 | items = [] 951 | index = 0 952 | fill_pos = None 953 | while index < len(ipstr): 954 | text = ipstr[index:] 955 | if text.startswith("::"): 956 | if fill_pos is not None: 957 | # Invalid IPv6, eg. '1::2::' 958 | raise ValueError("%r: Invalid IPv6 address: more than one '::'" % ipstr) 959 | fill_pos = len(items) 960 | index += 2 961 | continue 962 | pos = text.find(':') 963 | if pos == 0: 964 | # Invalid IPv6, eg. '1::2:' 965 | raise ValueError("%r: Invalid IPv6 address" % ipstr) 966 | if pos != -1: 967 | items.append(text[:pos]) 968 | if text[pos:pos+2] == "::": 969 | index += pos 970 | else: 971 | index += pos+1 972 | 973 | if index == len(ipstr): 974 | # Invalid IPv6, eg. '1::2:' 975 | raise ValueError("%r: Invalid IPv6 address" % ipstr) 976 | else: 977 | items.append(text) 978 | break 979 | 980 | if items and '.' in items[-1]: 981 | # IPv6 ending with IPv4 like '::ffff:192.168.0.1' 982 | if not (fill_pos <= len(items)-1): 983 | # Invalid IPv6: 'ffff:192.168.0.1::' 984 | raise ValueError("%r: Invalid IPv6 address: '::' after IPv4" % ipstr) 985 | value = parseAddress(items[-1])[0] 986 | items = items[:-1] + ["%04x" % (value >> 16), "%04x" % (value & 0xffff)] 987 | 988 | # Expand fill_pos to fill with '0' 989 | # ['1','2'] with fill_pos=1 => ['1', '0', '0', '0', '0', '0', '0', '2'] 990 | if fill_pos is not None: 991 | diff = 8 - len(items) 992 | if diff <= 0: 993 | raise ValueError("%r: Invalid IPv6 address: '::' is not needed" % ipstr) 994 | items = items[:fill_pos] + ['0']*diff + items[fill_pos:] 995 | 996 | # Here we have a list of 8 strings 997 | if len(items) != 8: 998 | # Invalid IPv6, eg. '1:2:3' 999 | raise ValueError("%r: Invalid IPv6 address: should have 8 hextets" % ipstr) 1000 | 1001 | # Convert strings to long integer 1002 | value = 0L 1003 | index = 0 1004 | for item in items: 1005 | try: 1006 | item = int(item, 16) 1007 | error = not(0 <= item <= 0xFFFF) 1008 | except ValueError: 1009 | error = True 1010 | if error: 1011 | raise ValueError("%r: Invalid IPv6 address: invalid hexlet %r" % (ipstr, item)) 1012 | value = (value << 16) + item 1013 | index += 1 1014 | return value 1015 | 1016 | def parseAddress(ipstr): 1017 | """ 1018 | Parse a string and return the corresponding IP address (as integer) 1019 | and a guess of the IP version. 1020 | 1021 | Following address formats are recognized: 1022 | 1023 | >>> parseAddress('0x0123456789abcdef') # IPv4 if <= 0xffffffff else IPv6 1024 | (81985529216486895L, 6) 1025 | >>> parseAddress('123.123.123.123') # IPv4 1026 | (2071690107L, 4) 1027 | >>> parseAddress('123.123') # 0-padded IPv4 1028 | (2071658496L, 4) 1029 | >>> parseAddress('1080:0000:0000:0000:0008:0800:200C:417A') 1030 | (21932261930451111902915077091070067066L, 6) 1031 | >>> parseAddress('1080:0:0:0:8:800:200C:417A') 1032 | (21932261930451111902915077091070067066L, 6) 1033 | >>> parseAddress('1080:0::8:800:200C:417A') 1034 | (21932261930451111902915077091070067066L, 6) 1035 | >>> parseAddress('::1') 1036 | (1L, 6) 1037 | >>> parseAddress('::') 1038 | (0L, 6) 1039 | >>> parseAddress('0:0:0:0:0:FFFF:129.144.52.38') 1040 | (281472855454758L, 6) 1041 | >>> parseAddress('::13.1.68.3') 1042 | (218186755L, 6) 1043 | >>> parseAddress('::FFFF:129.144.52.38') 1044 | (281472855454758L, 6) 1045 | """ 1046 | 1047 | if ipstr.startswith('0x'): 1048 | ret = long(ipstr[2:], 16) 1049 | if ret > 0xffffffffffffffffffffffffffffffffL: 1050 | raise ValueError, "%r: IP Address can't be bigger than 2^128" % (ipstr) 1051 | if ret < 0x100000000L: 1052 | return (ret, 4) 1053 | else: 1054 | return (ret, 6) 1055 | 1056 | if ipstr.find(':') != -1: 1057 | return (_parseAddressIPv6(ipstr), 6) 1058 | 1059 | elif len(ipstr) == 32: 1060 | # assume IPv6 in pure hexadecimal notation 1061 | return (long(ipstr, 16), 6) 1062 | 1063 | elif ipstr.find('.') != -1 or (len(ipstr) < 4 and int(ipstr) < 256): 1064 | # assume IPv4 ('127' gets interpreted as '127.0.0.0') 1065 | bytes = ipstr.split('.') 1066 | if len(bytes) > 4: 1067 | raise ValueError, "IPv4 Address with more than 4 bytes" 1068 | bytes += ['0'] * (4 - len(bytes)) 1069 | bytes = [long(x) for x in bytes] 1070 | for x in bytes: 1071 | if x > 255 or x < 0: 1072 | raise ValueError, "%r: single byte must be 0 <= byte < 256" % (ipstr) 1073 | return ((bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3], 4) 1074 | 1075 | else: 1076 | # we try to interprete it as a decimal digit - 1077 | # this ony works for numbers > 255 ... others 1078 | # will be interpreted as IPv4 first byte 1079 | ret = long(ipstr, 10) 1080 | if ret > 0xffffffffffffffffffffffffffffffffL: 1081 | raise ValueError, "IP Address can't be bigger than 2^128" 1082 | if ret <= 0xffffffffL: 1083 | return (ret, 4) 1084 | else: 1085 | return (ret, 6) 1086 | 1087 | 1088 | def intToIp(ip, version): 1089 | """Transform an integer string into an IP address.""" 1090 | 1091 | # just to be sure and hoping for Python 2.22 1092 | ip = long(ip) 1093 | 1094 | if ip < 0: 1095 | raise ValueError, "IPs can't be negative: %d" % (ip) 1096 | 1097 | ret = '' 1098 | if version == 4: 1099 | if ip > 0xffffffffL: 1100 | raise ValueError, "IPv4 Addresses can't be larger than 0xffffffff: %s" % (hex(ip)) 1101 | for l in range(4): 1102 | ret = str(ip & 0xffL) + '.' + ret 1103 | ip = ip >> 8 1104 | ret = ret[:-1] 1105 | elif version == 6: 1106 | if ip > 0xffffffffffffffffffffffffffffffffL: 1107 | raise ValueError, "IPv6 Addresses can't be larger than 0xffffffffffffffffffffffffffffffff: %s" % (hex(ip)) 1108 | l = '0' * 32 + hex(ip)[2:-1] 1109 | for x in range(1, 33): 1110 | ret = l[-x] + ret 1111 | if x % 4 == 0: 1112 | ret = ':' + ret 1113 | ret = ret[1:] 1114 | else: 1115 | raise ValueError, "only IPv4 and IPv6 supported" 1116 | 1117 | return ret 1118 | 1119 | def _ipVersionToLen(version): 1120 | """Return number of bits in address for a certain IP version. 1121 | 1122 | >>> _ipVersionToLen(4) 1123 | 32 1124 | >>> _ipVersionToLen(6) 1125 | 128 1126 | >>> _ipVersionToLen(5) 1127 | Traceback (most recent call last): 1128 | File "", line 1, in ? 1129 | File "IPy.py", line 1076, in _ipVersionToLen 1130 | raise ValueError, "only IPv4 and IPv6 supported" 1131 | ValueError: only IPv4 and IPv6 supported 1132 | """ 1133 | 1134 | if version == 4: 1135 | return 32 1136 | elif version == 6: 1137 | return 128 1138 | else: 1139 | raise ValueError, "only IPv4 and IPv6 supported" 1140 | 1141 | 1142 | def _countFollowingZeros(l): 1143 | """Return number of elements containing 0 at the beginning of the list.""" 1144 | if len(l) == 0: 1145 | return 0 1146 | elif l[0] != 0: 1147 | return 0 1148 | else: 1149 | return 1 + _countFollowingZeros(l[1:]) 1150 | 1151 | 1152 | _BitTable = {'0': '0000', '1': '0001', '2': '0010', '3': '0011', 1153 | '4': '0100', '5': '0101', '6': '0110', '7': '0111', 1154 | '8': '1000', '9': '1001', 'a': '1010', 'b': '1011', 1155 | 'c': '1100', 'd': '1101', 'e': '1110', 'f': '1111'} 1156 | 1157 | def _intToBin(val): 1158 | """Return the binary representation of an integer as string.""" 1159 | 1160 | if val < 0: 1161 | raise ValueError, "Only positive values allowed" 1162 | s = hex(val).lower() 1163 | ret = '' 1164 | if s[-1] == 'l': 1165 | s = s[:-1] 1166 | for x in s[2:]: 1167 | if __debug__: 1168 | if not _BitTable.has_key(x): 1169 | raise AssertionError, "hex() returned strange result" 1170 | ret += _BitTable[x] 1171 | # remove leading zeros 1172 | while ret[0] == '0' and len(ret) > 1: 1173 | ret = ret[1:] 1174 | return ret 1175 | 1176 | def _count1Bits(num): 1177 | """Find the highest bit set to 1 in an integer.""" 1178 | ret = 0 1179 | while num > 0: 1180 | num = num >> 1 1181 | ret += 1 1182 | return ret 1183 | 1184 | def _count0Bits(num): 1185 | """Find the highest bit set to 0 in an integer.""" 1186 | 1187 | # this could be so easy if _count1Bits(~long(num)) would work as excepted 1188 | num = long(num) 1189 | if num < 0: 1190 | raise ValueError, "Only positive Numbers please: %s" % (num) 1191 | ret = 0 1192 | while num > 0: 1193 | if num & 1 == 1: 1194 | break 1195 | num = num >> 1 1196 | ret += 1 1197 | return ret 1198 | 1199 | 1200 | def _checkPrefix(ip, prefixlen, version): 1201 | """Check the validity of a prefix 1202 | 1203 | Checks if the variant part of a prefix only has 0s, and the length is 1204 | correct. 1205 | 1206 | >>> _checkPrefix(0x7f000000L, 24, 4) 1207 | 1 1208 | >>> _checkPrefix(0x7f000001L, 24, 4) 1209 | 0 1210 | >>> repr(_checkPrefix(0x7f000001L, -1, 4)) 1211 | 'None' 1212 | >>> repr(_checkPrefix(0x7f000001L, 33, 4)) 1213 | 'None' 1214 | """ 1215 | 1216 | # TODO: unify this v4/v6/invalid code in a function 1217 | bits = _ipVersionToLen(version) 1218 | 1219 | if prefixlen < 0 or prefixlen > bits: 1220 | return None 1221 | 1222 | if ip == 0: 1223 | zbits = bits + 1 1224 | else: 1225 | zbits = _count0Bits(ip) 1226 | if zbits < bits - prefixlen: 1227 | return 0 1228 | else: 1229 | return 1 1230 | 1231 | 1232 | def _checkNetmask(netmask, masklen): 1233 | """Checks if a netmask is expressable as a prefixlen.""" 1234 | 1235 | num = long(netmask) 1236 | bits = masklen 1237 | 1238 | # remove zero bits at the end 1239 | while (num & 1) == 0 and bits != 0: 1240 | num = num >> 1 1241 | bits -= 1 1242 | if bits == 0: 1243 | break 1244 | # now check if the rest consists only of ones 1245 | while bits > 0: 1246 | if (num & 1) == 0: 1247 | raise ValueError, "Netmask %s can't be expressed as an prefix." % (hex(netmask)) 1248 | num = num >> 1 1249 | bits -= 1 1250 | 1251 | 1252 | def _checkNetaddrWorksWithPrefixlen(net, prefixlen, version): 1253 | """Check if a base addess of a network is compatible with a prefixlen""" 1254 | if net & _prefixlenToNetmask(prefixlen, version) == net: 1255 | return 1 1256 | else: 1257 | return 0 1258 | 1259 | 1260 | def _netmaskToPrefixlen(netmask): 1261 | """Convert an Integer representing a netmask to a prefixlen. 1262 | 1263 | E.g. 0xffffff00 (255.255.255.0) returns 24 1264 | """ 1265 | 1266 | netlen = _count0Bits(netmask) 1267 | masklen = _count1Bits(netmask) 1268 | _checkNetmask(netmask, masklen) 1269 | return masklen - netlen 1270 | 1271 | 1272 | def _prefixlenToNetmask(prefixlen, version): 1273 | """Return a mask of n bits as a long integer. 1274 | 1275 | From 'IP address conversion functions with the builtin socket module' 1276 | by Alex Martelli 1277 | http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66517 1278 | """ 1279 | if prefixlen == 0: 1280 | return 0 1281 | elif prefixlen < 0: 1282 | raise ValueError, "Prefixlen must be > 0" 1283 | return ((2L<>> from IPy import IP 22 | >>> ip = IP('127.0.0.0/30') 23 | >>> for x in ip: 24 | ... print x 25 | ... 26 | 127.0.0.0 27 | 127.0.0.1 28 | 127.0.0.2 29 | 127.0.0.3 30 | >>> ip2 = IP('0x7f000000/30') 31 | >>> ip == ip2 32 | 1 33 | >>> ip.reverseNames() 34 | ['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.'] 35 | >>> ip.reverseName() 36 | '0-3.0.0.127.in-addr.arpa.' 37 | >>> ip.iptype() 38 | 'PRIVATE' 39 | 40 | 41 | Supports most IP address formats 42 | ================================ 43 | 44 | It can detect about a dozen different ways of expressing IP addresses 45 | and networks, parse them and distinguish between IPv4 and IPv6 addresses: 46 | 47 | >>> IP('10.0.0.0/8').version() 48 | 4 49 | >>> IP('::1').version() 50 | 6 51 | 52 | IPv4 addresses 53 | -------------- 54 | 55 | >>> print IP(0x7f000001) 56 | 127.0.0.1 57 | >>> print IP('0x7f000001') 58 | 127.0.0.1 59 | >>> print IP('127.0.0.1') 60 | 127.0.0.1 61 | >>> print IP('10') 62 | 10.0.0.0 63 | 64 | IPv6 addresses 65 | -------------- 66 | 67 | >>> print IP('1080:0:0:0:8:800:200C:417A') 68 | 1080::8:800:200c:417a 69 | >>> print IP('1080::8:800:200C:417A') 70 | 1080::8:800:200c:417a 71 | >>> print IP('::1') 72 | ::1 73 | >>> print IP('::13.1.68.3') 74 | ::d01:4403 75 | 76 | Network mask and prefixes 77 | ------------------------- 78 | 79 | >>> print IP('127.0.0.0/8') 80 | 127.0.0.0/8 81 | >>> print IP('127.0.0.0/255.0.0.0') 82 | 127.0.0.0/8 83 | >>> print IP('127.0.0.0-127.255.255.255') 84 | 127.0.0.0/8 85 | 86 | 87 | Derive network address 88 | =========================== 89 | 90 | IPy can transform an IP address into a network address by applying the given 91 | netmask: 92 | >>> print IP('127.0.0.1/255.0.0.0', make_net=True) 93 | 127.0.0.0/8 94 | 95 | This can also be done for existing IP instances: 96 | >>> print IP('127.0.0.1').make_net('255.0.0.0') 97 | 127.0.0.0/8 98 | 99 | 100 | Convert address to string 101 | ========================= 102 | 103 | Nearly all class methods which return a string have an optional 104 | parameter 'wantprefixlen' which controls if the prefixlen or netmask 105 | is printed. Per default the prefilen is always shown if the network 106 | contains more than one address:: 107 | 108 | wantprefixlen == 0 / None don't return anything 1.2.3.0 109 | wantprefixlen == 1 /prefix 1.2.3.0/24 110 | wantprefixlen == 2 /netmask 1.2.3.0/255.255.255.0 111 | wantprefixlen == 3 -lastip 1.2.3.0-1.2.3.255 112 | 113 | You can also change the defaults on an per-object basis by fiddling with 114 | the class members: 115 | 116 | * NoPrefixForSingleIp 117 | * WantPrefixLen 118 | 119 | Examples of string conversions: 120 | 121 | >>> IP('10.0.0.0/32').strNormal() 122 | '10.0.0.0' 123 | >>> IP('10.0.0.0/24').strNormal() 124 | '10.0.0.0/24' 125 | >>> IP('10.0.0.0/24').strNormal(0) 126 | '10.0.0.0' 127 | >>> IP('10.0.0.0/24').strNormal(1) 128 | '10.0.0.0/24' 129 | >>> IP('10.0.0.0/24').strNormal(2) 130 | '10.0.0.0/255.255.255.0' 131 | >>> IP('10.0.0.0/24').strNormal(3) 132 | '10.0.0.0-10.0.0.255' 133 | >>> ip = IP('10.0.0.0') 134 | >>> print ip 135 | 10.0.0.0 136 | >>> ip.NoPrefixForSingleIp = None 137 | >>> print ip 138 | 10.0.0.0/32 139 | >>> ip.WantPrefixLen = 3 140 | >>> print ip 141 | 10.0.0.0-10.0.0.0 142 | 143 | 144 | Compatibility and links 145 | ======================= 146 | 147 | IPy 0.60 works on Python version 2.4 and 2.5. 148 | 149 | This Python module is under BSD license: see COPYING file. 150 | 151 | Further Information might be available at: 152 | http://software.inl.fr/trac/trac.cgi/wiki/IPy 153 | 154 | 155 | TODO 156 | ==== 157 | 158 | * better comparison (__cmp__ and friends) 159 | * tests for __cmp__ 160 | * always write hex values lowercase 161 | * interpret 2001:1234:5678:1234/64 as 2001:1234:5678:1234::/64 162 | * move size in bits into class variables to get rid of 163 | some "if self._ipversion ..." 164 | * support for base85 encoding 165 | * support for output of IPv6 encoded IPv4 Addresses 166 | * update address type tables 167 | * first-last notation should be allowed for IPv6 168 | * add IPv6 docstring examples 169 | * check better for negative parameters 170 | * add addition / aggregation 171 | * move reverse name stuff out of the classes and refactor it 172 | * support for aggregation of more than two nets at once 173 | * support for aggregation with "holes" 174 | * support for finding common prefix 175 | * '>>' and '<<' for prefix manipulation 176 | * add our own exceptions instead ValueError all the time 177 | * rename checkPrefix to checkPrefixOk 178 | * add more documentation and doctests 179 | * refactor 180 | 181 | What's new 182 | ========== 183 | 184 | Version 0.60 185 | * strCompressed() formats '::ffff:a.b.c.d' correctly 186 | * Use strCompressed() instead of strFullsize() to format IP addresses, 187 | ouput is smarter with IPv6 address 188 | * Remove check_addr_prefixlen because it generates invalid IP address 189 | 190 | 2008-02-05 191 | * Release IPy 0.56 192 | * Fix IPv6 parser for unit tests: reject 193 | '1111::2222:3333:4444:5555:6666:7777:8888' address since '::' is 194 | useless 195 | 196 | 2007-08-16 197 | * Release IPy 0.55 198 | * Rewrite IPv6 parser to allow address "1:2:3:4:5:6::" 199 | 200 | 2007-06-22 201 | * Release IPy 0.54 202 | * make_net() match from James Teh: transform an IP address into a network 203 | address by applying the given netmask 204 | 205 | 2007-02-28 206 | * Release IPy 0.53 207 | * Reject '0.0.0.0-0.0.0.4' if check_addr_prefixlen is enable 208 | * Fix many english spelling mistakes 209 | 210 | 2006-11-06 211 | * Release IPy 0.52 212 | * Fix strCompressed() for IPv6 "ffff:ffff:ffff:ffff:ffff:f:f:fffc/127" 213 | 214 | 2006-11-02 215 | * Release IPy 0.51 216 | * Write real name of IPy author (Maximillian Dornseif) 217 | * Use version "0.51" to help packaging since 0.5 was smaller than 0.42 218 | * Fix unit test for Python 2.3 (don't use doctest.testfile) and 2.5 219 | (problem of hex() lower case) 220 | * "make test" also check IPy documentation 221 | * IPy now works on Python 2.2 to 2.5 222 | 223 | 2006-10-26 224 | * Release IPy 0.5 225 | * Apply Jean Gillaux patch for netmask "/0.0.0.0" bug 226 | * Apply William McVey patch for __nonzero__() bug 227 | * Apply Victor Stinner patch: setup.py can use setuptools and fix URLs 228 | * Allow "172.30.1.0/22" with new option IPy.check_addr_prefixlen=False 229 | * Add regression tests 230 | * Create AUTHORS file 231 | 232 | 2004-08-22 233 | * IPy 0.42 works on Python 2.3 without warnings 234 | 235 | 2002-01-16 236 | * IPy 0.41 has Python < 2.2 compatible unit tests and a README file 237 | 238 | 2001-12-22 239 | * IPy 0.4 was the first public relase 240 | Keywords: ipv4 ipv6 netmask 241 | Platform: UNKNOWN 242 | Classifier: Development Status :: 5 - Production/Stable 243 | Classifier: Intended Audience :: Developers 244 | Classifier: Intended Audience :: System Administrators 245 | Classifier: Environment :: Plugins 246 | Classifier: Topic :: Software Development :: Libraries :: Python Modules 247 | Classifier: Topic :: Communications 248 | Classifier: Topic :: Internet 249 | Classifier: Topic :: System :: Networking 250 | Classifier: License :: OSI Approved :: BSD License 251 | Classifier: Operating System :: OS Independent 252 | Classifier: Natural Language :: English 253 | Classifier: Programming Language :: Python 254 | -------------------------------------------------------------------------------- /IPy-0.60/README: -------------------------------------------------------------------------------- 1 | IPy - class and tools for handling of IPv4 and IPv6 addresses and networks. 2 | 3 | Presentation of the API 4 | ======================= 5 | 6 | The IP class allows a comfortable parsing and handling for most 7 | notations in use for IPv4 and IPv6 addresses and networks. It was 8 | greatly inspired by RIPE's Perl module NET::IP's interface but 9 | doesn't share the implementation. It doesn't share non-CIDR netmasks, 10 | so funky stuff like a netmask of 0xffffff0f can't be done here. 11 | 12 | >>> from IPy import IP 13 | >>> ip = IP('127.0.0.0/30') 14 | >>> for x in ip: 15 | ... print x 16 | ... 17 | 127.0.0.0 18 | 127.0.0.1 19 | 127.0.0.2 20 | 127.0.0.3 21 | >>> ip2 = IP('0x7f000000/30') 22 | >>> ip == ip2 23 | 1 24 | >>> ip.reverseNames() 25 | ['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.'] 26 | >>> ip.reverseName() 27 | '0-3.0.0.127.in-addr.arpa.' 28 | >>> ip.iptype() 29 | 'PRIVATE' 30 | 31 | 32 | Supports most IP address formats 33 | ================================ 34 | 35 | It can detect about a dozen different ways of expressing IP addresses 36 | and networks, parse them and distinguish between IPv4 and IPv6 addresses: 37 | 38 | >>> IP('10.0.0.0/8').version() 39 | 4 40 | >>> IP('::1').version() 41 | 6 42 | 43 | IPv4 addresses 44 | -------------- 45 | 46 | >>> print IP(0x7f000001) 47 | 127.0.0.1 48 | >>> print IP('0x7f000001') 49 | 127.0.0.1 50 | >>> print IP('127.0.0.1') 51 | 127.0.0.1 52 | >>> print IP('10') 53 | 10.0.0.0 54 | 55 | IPv6 addresses 56 | -------------- 57 | 58 | >>> print IP('1080:0:0:0:8:800:200C:417A') 59 | 1080::8:800:200c:417a 60 | >>> print IP('1080::8:800:200C:417A') 61 | 1080::8:800:200c:417a 62 | >>> print IP('::1') 63 | ::1 64 | >>> print IP('::13.1.68.3') 65 | ::d01:4403 66 | 67 | Network mask and prefixes 68 | ------------------------- 69 | 70 | >>> print IP('127.0.0.0/8') 71 | 127.0.0.0/8 72 | >>> print IP('127.0.0.0/255.0.0.0') 73 | 127.0.0.0/8 74 | >>> print IP('127.0.0.0-127.255.255.255') 75 | 127.0.0.0/8 76 | 77 | 78 | Derive network address 79 | =========================== 80 | 81 | IPy can transform an IP address into a network address by applying the given 82 | netmask: 83 | >>> print IP('127.0.0.1/255.0.0.0', make_net=True) 84 | 127.0.0.0/8 85 | 86 | This can also be done for existing IP instances: 87 | >>> print IP('127.0.0.1').make_net('255.0.0.0') 88 | 127.0.0.0/8 89 | 90 | 91 | Convert address to string 92 | ========================= 93 | 94 | Nearly all class methods which return a string have an optional 95 | parameter 'wantprefixlen' which controls if the prefixlen or netmask 96 | is printed. Per default the prefilen is always shown if the network 97 | contains more than one address:: 98 | 99 | wantprefixlen == 0 / None don't return anything 1.2.3.0 100 | wantprefixlen == 1 /prefix 1.2.3.0/24 101 | wantprefixlen == 2 /netmask 1.2.3.0/255.255.255.0 102 | wantprefixlen == 3 -lastip 1.2.3.0-1.2.3.255 103 | 104 | You can also change the defaults on an per-object basis by fiddling with 105 | the class members: 106 | 107 | * NoPrefixForSingleIp 108 | * WantPrefixLen 109 | 110 | Examples of string conversions: 111 | 112 | >>> IP('10.0.0.0/32').strNormal() 113 | '10.0.0.0' 114 | >>> IP('10.0.0.0/24').strNormal() 115 | '10.0.0.0/24' 116 | >>> IP('10.0.0.0/24').strNormal(0) 117 | '10.0.0.0' 118 | >>> IP('10.0.0.0/24').strNormal(1) 119 | '10.0.0.0/24' 120 | >>> IP('10.0.0.0/24').strNormal(2) 121 | '10.0.0.0/255.255.255.0' 122 | >>> IP('10.0.0.0/24').strNormal(3) 123 | '10.0.0.0-10.0.0.255' 124 | >>> ip = IP('10.0.0.0') 125 | >>> print ip 126 | 10.0.0.0 127 | >>> ip.NoPrefixForSingleIp = None 128 | >>> print ip 129 | 10.0.0.0/32 130 | >>> ip.WantPrefixLen = 3 131 | >>> print ip 132 | 10.0.0.0-10.0.0.0 133 | 134 | 135 | Compatibility and links 136 | ======================= 137 | 138 | IPy 0.60 works on Python version 2.4 and 2.5. 139 | 140 | This Python module is under BSD license: see COPYING file. 141 | 142 | Further Information might be available at: 143 | http://software.inl.fr/trac/trac.cgi/wiki/IPy 144 | 145 | 146 | TODO 147 | ==== 148 | 149 | * better comparison (__cmp__ and friends) 150 | * tests for __cmp__ 151 | * always write hex values lowercase 152 | * interpret 2001:1234:5678:1234/64 as 2001:1234:5678:1234::/64 153 | * move size in bits into class variables to get rid of 154 | some "if self._ipversion ..." 155 | * support for base85 encoding 156 | * support for output of IPv6 encoded IPv4 Addresses 157 | * update address type tables 158 | * first-last notation should be allowed for IPv6 159 | * add IPv6 docstring examples 160 | * check better for negative parameters 161 | * add addition / aggregation 162 | * move reverse name stuff out of the classes and refactor it 163 | * support for aggregation of more than two nets at once 164 | * support for aggregation with "holes" 165 | * support for finding common prefix 166 | * '>>' and '<<' for prefix manipulation 167 | * add our own exceptions instead ValueError all the time 168 | * rename checkPrefix to checkPrefixOk 169 | * add more documentation and doctests 170 | * refactor 171 | 172 | -------------------------------------------------------------------------------- /IPy-0.60/build/lib/IPy.py: -------------------------------------------------------------------------------- 1 | """ 2 | IPy - class and tools for handling of IPv4 and IPv6 addresses and networks. 3 | See README file for learn how to use IPy. 4 | 5 | Further Information might be available at: 6 | http://software.inl.fr/trac/trac.cgi/wiki/IPy 7 | """ 8 | 9 | # $HeadURL: https://svn.inl.fr/inl-svn/src/tools/ipy/trunk/IPy.py $ 10 | # $Id: IPy.py 14624 2008-05-16 09:53:05Z haypo $ 11 | 12 | __rcsid__ = '$Id: IPy.py 14624 2008-05-16 09:53:05Z haypo $' 13 | __version__ = '0.60' 14 | 15 | import types 16 | 17 | # Definition of the Ranges for IPv4 IPs 18 | # this should include www.iana.org/assignments/ipv4-address-space 19 | # and www.iana.org/assignments/multicast-addresses 20 | IPv4ranges = { 21 | '0': 'PUBLIC', # fall back 22 | '00000000': 'PRIVATE', # 0/8 23 | '00001010': 'PRIVATE', # 10/8 24 | '01111111': 'PRIVATE', # 127.0/8 25 | '1': 'PUBLIC', # fall back 26 | '1010100111111110': 'PRIVATE', # 169.254/16 27 | '101011000001': 'PRIVATE', # 172.16/12 28 | '1100000010101000': 'PRIVATE', # 192.168/16 29 | '11011111': 'RESERVED', # 223/8 30 | '111': 'RESERVED' # 224/3 31 | } 32 | 33 | # Definition of the Ranges for IPv6 IPs 34 | # see also www.iana.org/assignments/ipv6-address-space, 35 | # www.iana.org/assignments/ipv6-tla-assignments, 36 | # www.iana.org/assignments/ipv6-multicast-addresses, 37 | # www.iana.org/assignments/ipv6-anycast-addresses 38 | IPv6ranges = { 39 | '00000000' : 'RESERVED', # ::/8 40 | '00000001' : 'UNASSIGNED', # 100::/8 41 | '0000001' : 'NSAP', # 200::/7 42 | '0000010' : 'IPX', # 400::/7 43 | '0000011' : 'UNASSIGNED', # 600::/7 44 | '00001' : 'UNASSIGNED', # 800::/5 45 | '0001' : 'UNASSIGNED', # 1000::/4 46 | '0010000000000000' : 'RESERVED', # 2000::/16 Reserved 47 | '0010000000000001' : 'ASSIGNABLE', # 2001::/16 Sub-TLA Assignments [RFC2450] 48 | '00100000000000010000000': 'ASSIGNABLE IANA', # 2001:0000::/29 - 2001:01F8::/29 IANA 49 | '00100000000000010000001': 'ASSIGNABLE APNIC', # 2001:0200::/29 - 2001:03F8::/29 APNIC 50 | '00100000000000010000010': 'ASSIGNABLE ARIN', # 2001:0400::/29 - 2001:05F8::/29 ARIN 51 | '00100000000000010000011': 'ASSIGNABLE RIPE', # 2001:0600::/29 - 2001:07F8::/29 RIPE NCC 52 | '0010000000000010' : '6TO4', # 2002::/16 "6to4" [RFC3056] 53 | '0011111111111110' : '6BONE', # 3FFE::/16 6bone Testing [RFC2471] 54 | '0011111111111111' : 'RESERVED', # 3FFF::/16 Reserved 55 | '010' : 'GLOBAL-UNICAST', # 4000::/3 56 | '011' : 'UNASSIGNED', # 6000::/3 57 | '100' : 'GEO-UNICAST', # 8000::/3 58 | '101' : 'UNASSIGNED', # A000::/3 59 | '110' : 'UNASSIGNED', # C000::/3 60 | '1110' : 'UNASSIGNED', # E000::/4 61 | '11110' : 'UNASSIGNED', # F000::/5 62 | '111110' : 'UNASSIGNED', # F800::/6 63 | '1111110' : 'UNASSIGNED', # FC00::/7 64 | '111111100' : 'UNASSIGNED', # FE00::/9 65 | '1111111010' : 'LINKLOCAL', # FE80::/10 66 | '1111111011' : 'SITELOCAL', # FEC0::/10 67 | '11111111' : 'MULTICAST', # FF00::/8 68 | '0' * 96 : 'IPV4COMP', # ::/96 69 | '0' * 80 + '1' * 16 : 'IPV4MAP', # ::FFFF:0:0/96 70 | '0' * 128 : 'UNSPECIFIED', # ::/128 71 | '0' * 127 + '1' : 'LOOPBACK' # ::1/128 72 | } 73 | 74 | 75 | class IPint: 76 | """Handling of IP addresses returning integers. 77 | 78 | Use class IP instead because some features are not implemented for 79 | IPint.""" 80 | 81 | def __init__(self, data, ipversion = 0, make_net = 0): 82 | """Create an instance of an IP object. 83 | 84 | Data can be a network specification or a single IP. IP 85 | addresses can be specified in all forms understood by 86 | parseAddress(). The size of a network can be specified as 87 | 88 | /prefixlen a.b.c.0/24 2001:658:22a:cafe::/64 89 | -lastIP a.b.c.0-a.b.c.255 2001:658:22a:cafe::-2001:658:22a:cafe:ffff:ffff:ffff:ffff 90 | /decimal netmask a.b.c.d/255.255.255.0 not supported for IPv6 91 | 92 | If no size specification is given a size of 1 address (/32 for 93 | IPv4 and /128 for IPv6) is assumed. 94 | 95 | If make_net is True, an IP address will be transformed into the network 96 | address by applying the specified netmask. 97 | 98 | >>> print IP('127.0.0.0/8') 99 | 127.0.0.0/8 100 | >>> print IP('127.0.0.0/255.0.0.0') 101 | 127.0.0.0/8 102 | >>> print IP('127.0.0.0-127.255.255.255') 103 | 127.0.0.0/8 104 | >>> print IP('127.0.0.1/255.0.0.0', make_net=True) 105 | 127.0.0.0/8 106 | 107 | See module documentation for more examples. 108 | """ 109 | 110 | # Print no Prefixlen for /32 and /128 111 | self.NoPrefixForSingleIp = 1 112 | 113 | # Do we want prefix printed by default? see _printPrefix() 114 | self.WantPrefixLen = None 115 | 116 | netbits = 0 117 | prefixlen = -1 118 | 119 | # handling of non string values in constructor 120 | if type(data) == types.IntType or type(data) == types.LongType: 121 | self.ip = long(data) 122 | if ipversion == 0: 123 | if self.ip < 0x100000000L: 124 | ipversion = 4 125 | else: 126 | ipversion = 6 127 | if ipversion == 4: 128 | prefixlen = 32 129 | elif ipversion == 6: 130 | prefixlen = 128 131 | else: 132 | raise ValueError, "only IPv4 and IPv6 supported" 133 | self._ipversion = ipversion 134 | self._prefixlen = prefixlen 135 | # handle IP instance as an parameter 136 | elif isinstance(data, IPint): 137 | self._ipversion = data._ipversion 138 | self._prefixlen = data._prefixlen 139 | self.ip = data.ip 140 | else: 141 | # TODO: refactor me! 142 | # splitting of a string into IP and prefixlen et. al. 143 | x = data.split('-') 144 | if len(x) == 2: 145 | # a.b.c.0-a.b.c.255 specification ? 146 | (ip, last) = x 147 | (self.ip, parsedVersion) = parseAddress(ip) 148 | if parsedVersion != 4: 149 | raise ValueError, "first-last notation only allowed for IPv4" 150 | (last, lastversion) = parseAddress(last) 151 | if lastversion != 4: 152 | raise ValueError, "last address should be IPv4, too" 153 | if last < self.ip: 154 | raise ValueError, "last address should be larger than first" 155 | size = last - self.ip 156 | netbits = _count1Bits(size) 157 | # make sure the broadcast is the same as the last ip 158 | # otherwise it will return /16 for something like: 159 | # 192.168.0.0-192.168.191.255 160 | if IP('%s/%s' % (ip, 32-netbits)).broadcast().int() != last: 161 | raise ValueError, \ 162 | "the range %s is not on a network boundary." % data 163 | elif len(x) == 1: 164 | x = data.split('/') 165 | # if no prefix is given use defaults 166 | if len(x) == 1: 167 | ip = x[0] 168 | prefixlen = -1 169 | elif len(x) > 2: 170 | raise ValueError, "only one '/' allowed in IP Address" 171 | else: 172 | (ip, prefixlen) = x 173 | if prefixlen.find('.') != -1: 174 | # check if the user might have used a netmask like 175 | # a.b.c.d/255.255.255.0 176 | (netmask, vers) = parseAddress(prefixlen) 177 | if vers != 4: 178 | raise ValueError, "netmask must be IPv4" 179 | prefixlen = _netmaskToPrefixlen(netmask) 180 | elif len(x) > 2: 181 | raise ValueError, "only one '-' allowed in IP Address" 182 | else: 183 | raise ValueError, "can't parse" 184 | 185 | (self.ip, parsedVersion) = parseAddress(ip) 186 | if ipversion == 0: 187 | ipversion = parsedVersion 188 | if prefixlen == -1: 189 | if ipversion == 4: 190 | prefixlen = 32 - netbits 191 | elif ipversion == 6: 192 | prefixlen = 128 - netbits 193 | else: 194 | raise ValueError, "only IPv4 and IPv6 supported" 195 | self._ipversion = ipversion 196 | self._prefixlen = int(prefixlen) 197 | 198 | if make_net: 199 | self.ip = self.ip & _prefixlenToNetmask(self._prefixlen, self._ipversion) 200 | 201 | if not _checkNetaddrWorksWithPrefixlen(self.ip, 202 | self._prefixlen, self._ipversion): 203 | raise ValueError, "%s has invalid prefix length (%s)" % (repr(self), self._prefixlen) 204 | 205 | def int(self): 206 | """Return the first / base / network addess as an (long) integer. 207 | 208 | The same as IP[0]. 209 | 210 | >>> "%X" % IP('10.0.0.0/8').int() 211 | 'A000000' 212 | """ 213 | return self.ip 214 | 215 | def version(self): 216 | """Return the IP version of this Object. 217 | 218 | >>> IP('10.0.0.0/8').version() 219 | 4 220 | >>> IP('::1').version() 221 | 6 222 | """ 223 | return self._ipversion 224 | 225 | def prefixlen(self): 226 | """Returns Network Prefixlen. 227 | 228 | >>> IP('10.0.0.0/8').prefixlen() 229 | 8 230 | """ 231 | return self._prefixlen 232 | 233 | def net(self): 234 | """ 235 | Return the base (first) address of a network as an (long) integer. 236 | """ 237 | return self.int() 238 | 239 | def broadcast(self): 240 | """ 241 | Return the broadcast (last) address of a network as an (long) integer. 242 | 243 | The same as IP[-1].""" 244 | return self.int() + self.len() - 1 245 | 246 | def _printPrefix(self, want): 247 | """Prints Prefixlen/Netmask. 248 | 249 | Not really. In fact it is our universal Netmask/Prefixlen printer. 250 | This is considered an internal function. 251 | 252 | want == 0 / None don't return anything 1.2.3.0 253 | want == 1 /prefix 1.2.3.0/24 254 | want == 2 /netmask 1.2.3.0/255.255.255.0 255 | want == 3 -lastip 1.2.3.0-1.2.3.255 256 | """ 257 | 258 | if (self._ipversion == 4 and self._prefixlen == 32) or \ 259 | (self._ipversion == 6 and self._prefixlen == 128): 260 | if self.NoPrefixForSingleIp: 261 | want = 0 262 | if want == None: 263 | want = self.WantPrefixLen 264 | if want == None: 265 | want = 1 266 | if want: 267 | if want == 2: 268 | # this should work with IP and IPint 269 | netmask = self.netmask() 270 | if type(netmask) != types.IntType \ 271 | and type(netmask) != types.LongType: 272 | netmask = netmask.int() 273 | return "/%s" % (intToIp(netmask, self._ipversion)) 274 | elif want == 3: 275 | return "-%s" % (intToIp(self.ip + self.len() - 1, self._ipversion)) 276 | else: 277 | # default 278 | return "/%d" % (self._prefixlen) 279 | else: 280 | return '' 281 | 282 | # We have different flavours to convert to: 283 | # strFullsize 127.0.0.1 2001:0658:022a:cafe:0200:c0ff:fe8d:08fa 284 | # strNormal 127.0.0.1 2001:658:22a:cafe:200:c0ff:fe8d:08fa 285 | # strCompressed 127.0.0.1 2001:658:22a:cafe::1 286 | # strHex 0x7F000001L 0x20010658022ACAFE0200C0FFFE8D08FA 287 | # strDec 2130706433 42540616829182469433547974687817795834 288 | 289 | def strBin(self, wantprefixlen = None): 290 | """Return a string representation as a binary value. 291 | 292 | >>> print IP('127.0.0.1').strBin() 293 | 01111111000000000000000000000001 294 | """ 295 | 296 | 297 | if self._ipversion == 4: 298 | bits = 32 299 | elif self._ipversion == 6: 300 | bits = 128 301 | else: 302 | raise ValueError, "only IPv4 and IPv6 supported" 303 | 304 | if self.WantPrefixLen == None and wantprefixlen == None: 305 | wantprefixlen = 0 306 | ret = _intToBin(self.ip) 307 | return '0' * (bits - len(ret)) + ret + self._printPrefix(wantprefixlen) 308 | 309 | def strCompressed(self, wantprefixlen = None): 310 | """Return a string representation in compressed format using '::' Notation. 311 | 312 | >>> IP('127.0.0.1').strCompressed() 313 | '127.0.0.1' 314 | >>> IP('2001:0658:022a:cafe:0200::1').strCompressed() 315 | '2001:658:22a:cafe:200::1' 316 | >>> IP('ffff:ffff:ffff:ffff:ffff:f:f:fffc/127').strCompressed() 317 | 'ffff:ffff:ffff:ffff:ffff:f:f:fffc/127' 318 | """ 319 | 320 | if self.WantPrefixLen == None and wantprefixlen == None: 321 | wantprefixlen = 1 322 | 323 | if self._ipversion == 4: 324 | return self.strFullsize(wantprefixlen) 325 | else: 326 | if self.ip >> 32 == 0xffff: 327 | return "::ffff:%s" % intToIp(self.ip & 0xffffffff, 4) 328 | # find the longest sequence of '0' 329 | hextets = [int(x, 16) for x in self.strFullsize(0).split(':')] 330 | # every element of followingzeros will contain the number of zeros 331 | # following the corresponding element of hextets 332 | followingzeros = [0] * 8 333 | for i in range(len(hextets)): 334 | followingzeros[i] = _countFollowingZeros(hextets[i:]) 335 | # compressionpos is the position where we can start removing zeros 336 | compressionpos = followingzeros.index(max(followingzeros)) 337 | if max(followingzeros) > 1: 338 | # genererate string with the longest number of zeros cut out 339 | # now we need hextets as strings 340 | hextets = [x for x in self.strNormal(0).split(':')] 341 | while compressionpos < len(hextets) and hextets[compressionpos] == '0': 342 | del(hextets[compressionpos]) 343 | hextets.insert(compressionpos, '') 344 | if compressionpos + 1 >= len(hextets): 345 | hextets.append('') 346 | if compressionpos == 0: 347 | hextets = [''] + hextets 348 | return ':'.join(hextets) + self._printPrefix(wantprefixlen) 349 | else: 350 | return self.strNormal(0) + self._printPrefix(wantprefixlen) 351 | 352 | def strNormal(self, wantprefixlen = None): 353 | """Return a string representation in the usual format. 354 | 355 | >>> print IP('127.0.0.1').strNormal() 356 | 127.0.0.1 357 | >>> print IP('2001:0658:022a:cafe:0200::1').strNormal() 358 | 2001:658:22a:cafe:200:0:0:1 359 | """ 360 | 361 | if self.WantPrefixLen == None and wantprefixlen == None: 362 | wantprefixlen = 1 363 | 364 | if self._ipversion == 4: 365 | ret = self.strFullsize(0) 366 | elif self._ipversion == 6: 367 | ret = ':'.join([hex(x)[2:] for x in [int(x, 16) for x in self.strFullsize(0).split(':')]]) 368 | else: 369 | raise ValueError, "only IPv4 and IPv6 supported" 370 | 371 | 372 | 373 | return ret + self._printPrefix(wantprefixlen) 374 | 375 | def strFullsize(self, wantprefixlen = None): 376 | """Return a string representation in the non-mangled format. 377 | 378 | >>> print IP('127.0.0.1').strFullsize() 379 | 127.0.0.1 380 | >>> print IP('2001:0658:022a:cafe:0200::1').strFullsize() 381 | 2001:0658:022a:cafe:0200:0000:0000:0001 382 | """ 383 | 384 | if self.WantPrefixLen == None and wantprefixlen == None: 385 | wantprefixlen = 1 386 | 387 | return intToIp(self.ip, self._ipversion).lower() + self._printPrefix(wantprefixlen) 388 | 389 | def strHex(self, wantprefixlen = None): 390 | """Return a string representation in hex format in lower case. 391 | 392 | >>> IP('127.0.0.1').strHex() 393 | '0x7f000001' 394 | >>> IP('2001:0658:022a:cafe:0200::1').strHex() 395 | '0x20010658022acafe0200000000000001' 396 | """ 397 | 398 | if self.WantPrefixLen == None and wantprefixlen == None: 399 | wantprefixlen = 0 400 | 401 | x = hex(self.ip) 402 | if x[-1] == 'L': 403 | x = x[:-1] 404 | return x.lower() + self._printPrefix(wantprefixlen) 405 | 406 | def strDec(self, wantprefixlen = None): 407 | """Return a string representation in decimal format. 408 | 409 | >>> print IP('127.0.0.1').strDec() 410 | 2130706433 411 | >>> print IP('2001:0658:022a:cafe:0200::1').strDec() 412 | 42540616829182469433547762482097946625 413 | """ 414 | 415 | if self.WantPrefixLen == None and wantprefixlen == None: 416 | wantprefixlen = 0 417 | 418 | x = str(self.ip) 419 | if x[-1] == 'L': 420 | x = x[:-1] 421 | return x + self._printPrefix(wantprefixlen) 422 | 423 | def iptype(self): 424 | """Return a description of the IP type ('PRIVATE', 'RESERVERD', etc). 425 | 426 | >>> print IP('127.0.0.1').iptype() 427 | PRIVATE 428 | >>> print IP('192.168.1.1').iptype() 429 | PRIVATE 430 | >>> print IP('195.185.1.2').iptype() 431 | PUBLIC 432 | >>> print IP('::1').iptype() 433 | LOOPBACK 434 | >>> print IP('2001:0658:022a:cafe:0200::1').iptype() 435 | ASSIGNABLE RIPE 436 | 437 | The type information for IPv6 is out of sync with reality. 438 | """ 439 | 440 | # this could be greatly improved 441 | 442 | if self._ipversion == 4: 443 | iprange = IPv4ranges 444 | elif self._ipversion == 6: 445 | iprange = IPv6ranges 446 | else: 447 | raise ValueError, "only IPv4 and IPv6 supported" 448 | 449 | bits = self.strBin() 450 | for i in range(len(bits), 0, -1): 451 | if iprange.has_key(bits[:i]): 452 | return iprange[bits[:i]] 453 | return "unknown" 454 | 455 | 456 | def netmask(self): 457 | """Return netmask as an integer. 458 | 459 | >>> "%X" % IP('195.185.0.0/16').netmask().int() 460 | 'FFFF0000' 461 | """ 462 | 463 | # TODO: unify with prefixlenToNetmask? 464 | if self._ipversion == 4: 465 | locallen = 32 - self._prefixlen 466 | elif self._ipversion == 6: 467 | locallen = 128 - self._prefixlen 468 | else: 469 | raise ValueError, "only IPv4 and IPv6 supported" 470 | 471 | return ((2L ** self._prefixlen) - 1) << locallen 472 | 473 | 474 | def strNetmask(self): 475 | """Return netmask as an string. Mostly useful for IPv6. 476 | 477 | >>> print IP('195.185.0.0/16').strNetmask() 478 | 255.255.0.0 479 | >>> print IP('2001:0658:022a:cafe::0/64').strNetmask() 480 | /64 481 | """ 482 | 483 | # TODO: unify with prefixlenToNetmask? 484 | if self._ipversion == 4: 485 | locallen = 32 - self._prefixlen 486 | return intToIp(((2L ** self._prefixlen) - 1) << locallen, 4) 487 | elif self._ipversion == 6: 488 | locallen = 128 - self._prefixlen 489 | return "/%d" % self._prefixlen 490 | else: 491 | raise ValueError, "only IPv4 and IPv6 supported" 492 | 493 | def len(self): 494 | """Return the length of a subnet. 495 | 496 | >>> print IP('195.185.1.0/28').len() 497 | 16 498 | >>> print IP('195.185.1.0/24').len() 499 | 256 500 | """ 501 | 502 | if self._ipversion == 4: 503 | locallen = 32 - self._prefixlen 504 | elif self._ipversion == 6: 505 | locallen = 128 - self._prefixlen 506 | else: 507 | raise ValueError, "only IPv4 and IPv6 supported" 508 | 509 | return 2L ** locallen 510 | 511 | 512 | def __nonzero__(self): 513 | """All IPy objects should evaluate to true in boolean context. 514 | Ordinarily they do, but if handling a default route expressed as 515 | 0.0.0.0/0, the __len__() of the object becomes 0, which is used 516 | as the boolean value of the object. 517 | """ 518 | return 1 519 | 520 | 521 | def __len__(self): 522 | """Return the length of a subnet. 523 | 524 | Called to implement the built-in function len(). 525 | It breaks with IPv6 Networks. Anybody knows how to fix this.""" 526 | 527 | # Python < 2.2 has this silly restriction which breaks IPv6 528 | # how about Python >= 2.2 ... ouch - it persists! 529 | 530 | return int(self.len()) 531 | 532 | 533 | def __getitem__(self, key): 534 | """Called to implement evaluation of self[key]. 535 | 536 | >>> ip=IP('127.0.0.0/30') 537 | >>> for x in ip: 538 | ... print repr(x) 539 | ... 540 | IP('127.0.0.0') 541 | IP('127.0.0.1') 542 | IP('127.0.0.2') 543 | IP('127.0.0.3') 544 | >>> ip[2] 545 | IP('127.0.0.2') 546 | >>> ip[-1] 547 | IP('127.0.0.3') 548 | """ 549 | 550 | if type(key) != types.IntType and type(key) != types.LongType: 551 | raise TypeError 552 | if abs(key) >= self.len(): 553 | raise IndexError 554 | if key < 0: 555 | key = self.len() - abs(key) 556 | 557 | return self.ip + long(key) 558 | 559 | 560 | 561 | def __contains__(self, item): 562 | """Called to implement membership test operators. 563 | 564 | Should return true if item is in self, false otherwise. Item 565 | can be other IP-objects, strings or ints. 566 | 567 | >>> IP('195.185.1.1').strHex() 568 | '0xc3b90101' 569 | >>> 0xC3B90101L in IP('195.185.1.0/24') 570 | 1 571 | >>> '127.0.0.1' in IP('127.0.0.0/24') 572 | 1 573 | >>> IP('127.0.0.0/24') in IP('127.0.0.0/25') 574 | 0 575 | """ 576 | 577 | item = IP(item) 578 | if item.ip >= self.ip and item.ip < self.ip + self.len() - item.len() + 1: 579 | return 1 580 | else: 581 | return 0 582 | 583 | 584 | def overlaps(self, item): 585 | """Check if two IP address ranges overlap. 586 | 587 | Returns 0 if the two ranges don't overlap, 1 if the given 588 | range overlaps at the end and -1 if it does at the beginning. 589 | 590 | >>> IP('192.168.0.0/23').overlaps('192.168.1.0/24') 591 | 1 592 | >>> IP('192.168.0.0/23').overlaps('192.168.1.255') 593 | 1 594 | >>> IP('192.168.0.0/23').overlaps('192.168.2.0') 595 | 0 596 | >>> IP('192.168.1.0/24').overlaps('192.168.0.0/23') 597 | -1 598 | """ 599 | 600 | item = IP(item) 601 | if item.ip >= self.ip and item.ip < self.ip + self.len(): 602 | return 1 603 | elif self.ip >= item.ip and self.ip < item.ip + item.len(): 604 | return -1 605 | else: 606 | return 0 607 | 608 | 609 | def __str__(self): 610 | """Dispatch to the prefered String Representation. 611 | 612 | Used to implement str(IP).""" 613 | 614 | return self.strCompressed() 615 | 616 | 617 | def __repr__(self): 618 | """Print a representation of the Object. 619 | 620 | Used to implement repr(IP). Returns a string which evaluates 621 | to an identical Object (without the wantprefixlen stuff - see 622 | module docstring. 623 | 624 | >>> print repr(IP('10.0.0.0/24')) 625 | IP('10.0.0.0/24') 626 | """ 627 | 628 | return("IPint('%s')" % (self.strCompressed(1))) 629 | 630 | 631 | def __cmp__(self, other): 632 | """Called by comparison operations. 633 | 634 | Should return a negative integer if self < other, zero if self 635 | == other, a positive integer if self > other. 636 | 637 | Networks with different prefixlen are considered non-equal. 638 | Networks with the same prefixlen and differing addresses are 639 | considered non equal but are compared by their base address 640 | integer value to aid sorting of IP objects. 641 | 642 | The version of Objects is not put into consideration. 643 | 644 | >>> IP('10.0.0.0/24') > IP('10.0.0.0') 645 | 1 646 | >>> IP('10.0.0.0/24') < IP('10.0.0.0') 647 | 0 648 | >>> IP('10.0.0.0/24') < IP('12.0.0.0/24') 649 | 1 650 | >>> IP('10.0.0.0/24') > IP('12.0.0.0/24') 651 | 0 652 | 653 | """ 654 | 655 | # Im not really sure if this is "the right thing to do" 656 | if self._prefixlen < other.prefixlen(): 657 | return (other.prefixlen() - self._prefixlen) 658 | elif self._prefixlen > other.prefixlen(): 659 | 660 | # Fixed bySamuel Krempp : 661 | 662 | # The bug is quite obvious really (as 99% bugs are once 663 | # spotted, isn't it ? ;-) Because of precedence of 664 | # multiplication by -1 over the substraction, prefixlen 665 | # differences were causing the __cmp__ function to always 666 | # return positive numbers, thus the function was failing 667 | # the basic assumptions for a __cmp__ function. 668 | 669 | # Namely we could have (a > b AND b > a), when the 670 | # prefixlen of a and b are different. (eg let 671 | # a=IP("1.0.0.0/24"); b=IP("2.0.0.0/16");) thus, anything 672 | # could happen when launching a sort algorithm.. 673 | # everything's in order with the trivial, attached patch. 674 | 675 | return (self._prefixlen - other.prefixlen()) * -1 676 | else: 677 | if self.ip < other.ip: 678 | return -1 679 | elif self.ip > other.ip: 680 | return 1 681 | else: 682 | return 0 683 | 684 | 685 | def __hash__(self): 686 | """Called for the key object for dictionary operations, and by 687 | the built-in function hash(). Should return a 32-bit integer 688 | usable as a hash value for dictionary operations. The only 689 | required property is that objects which compare equal have the 690 | same hash value 691 | 692 | >>> IP('10.0.0.0/24').__hash__() 693 | -167772185 694 | """ 695 | 696 | thehash = int(-1) 697 | ip = self.ip 698 | while ip > 0: 699 | thehash = thehash ^ (ip & 0x7fffffff) 700 | ip = ip >> 32 701 | thehash = thehash ^ self._prefixlen 702 | return int(thehash) 703 | 704 | 705 | class IP(IPint): 706 | """Class for handling IP addresses and networks.""" 707 | 708 | def net(self): 709 | """Return the base (first) address of a network as an IP object. 710 | 711 | The same as IP[0]. 712 | 713 | >>> IP('10.0.0.0/8').net() 714 | IP('10.0.0.0') 715 | """ 716 | return IP(IPint.net(self)) 717 | 718 | def broadcast(self): 719 | """Return the broadcast (last) address of a network as an IP object. 720 | 721 | The same as IP[-1]. 722 | 723 | >>> IP('10.0.0.0/8').broadcast() 724 | IP('10.255.255.255') 725 | """ 726 | return IP(IPint.broadcast(self)) 727 | 728 | def netmask(self): 729 | """Return netmask as an IP object. 730 | 731 | >>> IP('10.0.0.0/8').netmask() 732 | IP('255.0.0.0') 733 | """ 734 | return IP(IPint.netmask(self)) 735 | 736 | 737 | def reverseNames(self): 738 | """Return a list with values forming the reverse lookup. 739 | 740 | >>> IP('213.221.113.87/32').reverseNames() 741 | ['87.113.221.213.in-addr.arpa.'] 742 | >>> IP('213.221.112.224/30').reverseNames() 743 | ['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.'] 744 | >>> IP('127.0.0.0/24').reverseNames() 745 | ['0.0.127.in-addr.arpa.'] 746 | >>> IP('127.0.0.0/23').reverseNames() 747 | ['0.0.127.in-addr.arpa.', '1.0.127.in-addr.arpa.'] 748 | >>> IP('127.0.0.0/16').reverseNames() 749 | ['0.127.in-addr.arpa.'] 750 | >>> IP('127.0.0.0/15').reverseNames() 751 | ['0.127.in-addr.arpa.', '1.127.in-addr.arpa.'] 752 | >>> IP('128.0.0.0/8').reverseNames() 753 | ['128.in-addr.arpa.'] 754 | >>> IP('128.0.0.0/7').reverseNames() 755 | ['128.in-addr.arpa.', '129.in-addr.arpa.'] 756 | 757 | """ 758 | 759 | if self._ipversion == 4: 760 | ret = [] 761 | # TODO: Refactor. Add support for IPint objects 762 | if self.len() < 2**8: 763 | for x in self: 764 | ret.append(x.reverseName()) 765 | elif self.len() < 2**16L: 766 | for i in range(0, self.len(), 2**8): 767 | ret.append(self[i].reverseName()[2:]) 768 | elif self.len() < 2**24L: 769 | for i in range(0, self.len(), 2**16): 770 | ret.append(self[i].reverseName()[4:]) 771 | else: 772 | for i in range(0, self.len(), 2**24): 773 | ret.append(self[i].reverseName()[6:]) 774 | return ret 775 | elif self._ipversion == 6: 776 | s = hex(self.ip)[2:].lower() 777 | if s[-1] == 'l': 778 | s = s[:-1] 779 | if self._prefixlen % 4 != 0: 780 | raise NotImplementedError, "can't create IPv6 reverse names at sub nibble level" 781 | s = list(s) 782 | s.reverse() 783 | s = '.'.join(s) 784 | first_nibble_index = int(32 - (self._prefixlen / 4)) * 2 785 | return ["%s.ip6.int." % s[first_nibble_index:]] 786 | else: 787 | raise ValueError, "only IPv4 and IPv6 supported" 788 | 789 | 790 | 791 | def reverseName(self): 792 | """Return the value for reverse lookup/PTR records as RFC 2317 look alike. 793 | 794 | RFC 2317 is an ugly hack which only works for sub-/24 e.g. not 795 | for /23. Do not use it. Better set up a zone for every 796 | address. See reverseName for a way to achieve that. 797 | 798 | >>> print IP('195.185.1.1').reverseName() 799 | 1.1.185.195.in-addr.arpa. 800 | >>> print IP('195.185.1.0/28').reverseName() 801 | 0-15.1.185.195.in-addr.arpa. 802 | """ 803 | 804 | if self._ipversion == 4: 805 | s = self.strFullsize(0) 806 | s = s.split('.') 807 | s.reverse() 808 | first_byte_index = int(4 - (self._prefixlen / 8)) 809 | if self._prefixlen % 8 != 0: 810 | nibblepart = "%s-%s" % (s[3-(self._prefixlen / 8)], intToIp(self.ip + self.len() - 1, 4).split('.')[-1]) 811 | if nibblepart[-1] == 'l': 812 | nibblepart = nibblepart[:-1] 813 | nibblepart += '.' 814 | else: 815 | nibblepart = "" 816 | 817 | s = '.'.join(s[first_byte_index:]) 818 | return "%s%s.in-addr.arpa." % (nibblepart, s) 819 | 820 | elif self._ipversion == 6: 821 | s = hex(self.ip)[2:].lower() 822 | if s[-1] == 'l': 823 | s = s[:-1] 824 | if self._prefixlen % 4 != 0: 825 | nibblepart = "%s-%s" % (s[self._prefixlen:], hex(self.ip + self.len() - 1)[2:].lower()) 826 | if nibblepart[-1] == 'l': 827 | nibblepart = nibblepart[:-1] 828 | nibblepart += '.' 829 | else: 830 | nibblepart = "" 831 | s = list(s) 832 | s.reverse() 833 | s = '.'.join(s) 834 | first_nibble_index = int(32 - (self._prefixlen / 4)) * 2 835 | return "%s%s.ip6.int." % (nibblepart, s[first_nibble_index:]) 836 | else: 837 | raise ValueError, "only IPv4 and IPv6 supported" 838 | 839 | def make_net(self, netmask): 840 | """Transform a single IP address into a network specification by 841 | applying the given netmask. 842 | 843 | Returns a new IP instance. 844 | 845 | >>> print IP('127.0.0.1').make_net('255.0.0.0') 846 | 127.0.0.0/8 847 | """ 848 | if '/' in str(netmask): 849 | raise ValueError, "invalid netmask (%s)" % netmask 850 | return IP('%s/%s' % (self, netmask), make_net=True) 851 | 852 | def __getitem__(self, key): 853 | """Called to implement evaluation of self[key]. 854 | 855 | >>> ip=IP('127.0.0.0/30') 856 | >>> for x in ip: 857 | ... print str(x) 858 | ... 859 | 127.0.0.0 860 | 127.0.0.1 861 | 127.0.0.2 862 | 127.0.0.3 863 | >>> print str(ip[2]) 864 | 127.0.0.2 865 | >>> print str(ip[-1]) 866 | 127.0.0.3 867 | """ 868 | return IP(IPint.__getitem__(self, key)) 869 | 870 | def __repr__(self): 871 | """Print a representation of the Object. 872 | 873 | >>> IP('10.0.0.0/8') 874 | IP('10.0.0.0/8') 875 | """ 876 | 877 | return("IP('%s')" % (self.strCompressed(1))) 878 | 879 | def __add__(self, other): 880 | """Emulate numeric objects through network aggregation""" 881 | if self.prefixlen() != other.prefixlen(): 882 | raise ValueError, "Only networks with the same prefixlen can be added." 883 | if self.prefixlen < 1: 884 | raise ValueError, "Networks with a prefixlen longer than /1 can't be added." 885 | if self.version() != other.version(): 886 | raise ValueError, "Only networks with the same IP version can be added." 887 | if self > other: 888 | # fixed by Skinny Puppy 889 | return other.__add__(self) 890 | else: 891 | ret = IP(self.int()) 892 | ret._prefixlen = self.prefixlen() - 1 893 | return ret 894 | 895 | 896 | def _parseAddressIPv6(ipstr): 897 | """ 898 | Internal function used by parseAddress() to parse IPv6 address with ':'. 899 | 900 | >>> _parseAddressIPv6('::') 901 | 0L 902 | >>> _parseAddressIPv6('::1') 903 | 1L 904 | >>> _parseAddressIPv6('0:0:0:0:0:0:0:1') 905 | 1L 906 | >>> _parseAddressIPv6('0:0:0::0:0:1') 907 | 1L 908 | >>> _parseAddressIPv6('0:0:0:0:0:0:0:0') 909 | 0L 910 | >>> _parseAddressIPv6('0:0:0::0:0:0') 911 | 0L 912 | 913 | >>> _parseAddressIPv6('FEDC:BA98:7654:3210:FEDC:BA98:7654:3210') 914 | 338770000845734292534325025077361652240L 915 | >>> _parseAddressIPv6('1080:0000:0000:0000:0008:0800:200C:417A') 916 | 21932261930451111902915077091070067066L 917 | >>> _parseAddressIPv6('1080:0:0:0:8:800:200C:417A') 918 | 21932261930451111902915077091070067066L 919 | >>> _parseAddressIPv6('1080:0::8:800:200C:417A') 920 | 21932261930451111902915077091070067066L 921 | >>> _parseAddressIPv6('1080::8:800:200C:417A') 922 | 21932261930451111902915077091070067066L 923 | >>> _parseAddressIPv6('FF01:0:0:0:0:0:0:43') 924 | 338958331222012082418099330867817087043L 925 | >>> _parseAddressIPv6('FF01:0:0::0:0:43') 926 | 338958331222012082418099330867817087043L 927 | >>> _parseAddressIPv6('FF01::43') 928 | 338958331222012082418099330867817087043L 929 | >>> _parseAddressIPv6('0:0:0:0:0:0:13.1.68.3') 930 | 218186755L 931 | >>> _parseAddressIPv6('::13.1.68.3') 932 | 218186755L 933 | >>> _parseAddressIPv6('0:0:0:0:0:FFFF:129.144.52.38') 934 | 281472855454758L 935 | >>> _parseAddressIPv6('::FFFF:129.144.52.38') 936 | 281472855454758L 937 | >>> _parseAddressIPv6('1080:0:0:0:8:800:200C:417A') 938 | 21932261930451111902915077091070067066L 939 | >>> _parseAddressIPv6('1080::8:800:200C:417A') 940 | 21932261930451111902915077091070067066L 941 | >>> _parseAddressIPv6('::1:2:3:4:5:6') 942 | 1208962713947218704138246L 943 | >>> _parseAddressIPv6('1:2:3:4:5:6::') 944 | 5192455318486707404433266432802816L 945 | """ 946 | 947 | # Split string into a list, example: 948 | # '1080:200C::417A' => ['1080', '200C', '417A'] and fill_pos=2 949 | # and fill_pos is the position of '::' in the list 950 | items = [] 951 | index = 0 952 | fill_pos = None 953 | while index < len(ipstr): 954 | text = ipstr[index:] 955 | if text.startswith("::"): 956 | if fill_pos is not None: 957 | # Invalid IPv6, eg. '1::2::' 958 | raise ValueError("%r: Invalid IPv6 address: more than one '::'" % ipstr) 959 | fill_pos = len(items) 960 | index += 2 961 | continue 962 | pos = text.find(':') 963 | if pos == 0: 964 | # Invalid IPv6, eg. '1::2:' 965 | raise ValueError("%r: Invalid IPv6 address" % ipstr) 966 | if pos != -1: 967 | items.append(text[:pos]) 968 | if text[pos:pos+2] == "::": 969 | index += pos 970 | else: 971 | index += pos+1 972 | 973 | if index == len(ipstr): 974 | # Invalid IPv6, eg. '1::2:' 975 | raise ValueError("%r: Invalid IPv6 address" % ipstr) 976 | else: 977 | items.append(text) 978 | break 979 | 980 | if items and '.' in items[-1]: 981 | # IPv6 ending with IPv4 like '::ffff:192.168.0.1' 982 | if not (fill_pos <= len(items)-1): 983 | # Invalid IPv6: 'ffff:192.168.0.1::' 984 | raise ValueError("%r: Invalid IPv6 address: '::' after IPv4" % ipstr) 985 | value = parseAddress(items[-1])[0] 986 | items = items[:-1] + ["%04x" % (value >> 16), "%04x" % (value & 0xffff)] 987 | 988 | # Expand fill_pos to fill with '0' 989 | # ['1','2'] with fill_pos=1 => ['1', '0', '0', '0', '0', '0', '0', '2'] 990 | if fill_pos is not None: 991 | diff = 8 - len(items) 992 | if diff <= 0: 993 | raise ValueError("%r: Invalid IPv6 address: '::' is not needed" % ipstr) 994 | items = items[:fill_pos] + ['0']*diff + items[fill_pos:] 995 | 996 | # Here we have a list of 8 strings 997 | if len(items) != 8: 998 | # Invalid IPv6, eg. '1:2:3' 999 | raise ValueError("%r: Invalid IPv6 address: should have 8 hextets" % ipstr) 1000 | 1001 | # Convert strings to long integer 1002 | value = 0L 1003 | index = 0 1004 | for item in items: 1005 | try: 1006 | item = int(item, 16) 1007 | error = not(0 <= item <= 0xFFFF) 1008 | except ValueError: 1009 | error = True 1010 | if error: 1011 | raise ValueError("%r: Invalid IPv6 address: invalid hexlet %r" % (ipstr, item)) 1012 | value = (value << 16) + item 1013 | index += 1 1014 | return value 1015 | 1016 | def parseAddress(ipstr): 1017 | """ 1018 | Parse a string and return the corresponding IP address (as integer) 1019 | and a guess of the IP version. 1020 | 1021 | Following address formats are recognized: 1022 | 1023 | >>> parseAddress('0x0123456789abcdef') # IPv4 if <= 0xffffffff else IPv6 1024 | (81985529216486895L, 6) 1025 | >>> parseAddress('123.123.123.123') # IPv4 1026 | (2071690107L, 4) 1027 | >>> parseAddress('123.123') # 0-padded IPv4 1028 | (2071658496L, 4) 1029 | >>> parseAddress('1080:0000:0000:0000:0008:0800:200C:417A') 1030 | (21932261930451111902915077091070067066L, 6) 1031 | >>> parseAddress('1080:0:0:0:8:800:200C:417A') 1032 | (21932261930451111902915077091070067066L, 6) 1033 | >>> parseAddress('1080:0::8:800:200C:417A') 1034 | (21932261930451111902915077091070067066L, 6) 1035 | >>> parseAddress('::1') 1036 | (1L, 6) 1037 | >>> parseAddress('::') 1038 | (0L, 6) 1039 | >>> parseAddress('0:0:0:0:0:FFFF:129.144.52.38') 1040 | (281472855454758L, 6) 1041 | >>> parseAddress('::13.1.68.3') 1042 | (218186755L, 6) 1043 | >>> parseAddress('::FFFF:129.144.52.38') 1044 | (281472855454758L, 6) 1045 | """ 1046 | 1047 | if ipstr.startswith('0x'): 1048 | ret = long(ipstr[2:], 16) 1049 | if ret > 0xffffffffffffffffffffffffffffffffL: 1050 | raise ValueError, "%r: IP Address can't be bigger than 2^128" % (ipstr) 1051 | if ret < 0x100000000L: 1052 | return (ret, 4) 1053 | else: 1054 | return (ret, 6) 1055 | 1056 | if ipstr.find(':') != -1: 1057 | return (_parseAddressIPv6(ipstr), 6) 1058 | 1059 | elif len(ipstr) == 32: 1060 | # assume IPv6 in pure hexadecimal notation 1061 | return (long(ipstr, 16), 6) 1062 | 1063 | elif ipstr.find('.') != -1 or (len(ipstr) < 4 and int(ipstr) < 256): 1064 | # assume IPv4 ('127' gets interpreted as '127.0.0.0') 1065 | bytes = ipstr.split('.') 1066 | if len(bytes) > 4: 1067 | raise ValueError, "IPv4 Address with more than 4 bytes" 1068 | bytes += ['0'] * (4 - len(bytes)) 1069 | bytes = [long(x) for x in bytes] 1070 | for x in bytes: 1071 | if x > 255 or x < 0: 1072 | raise ValueError, "%r: single byte must be 0 <= byte < 256" % (ipstr) 1073 | return ((bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3], 4) 1074 | 1075 | else: 1076 | # we try to interprete it as a decimal digit - 1077 | # this ony works for numbers > 255 ... others 1078 | # will be interpreted as IPv4 first byte 1079 | ret = long(ipstr, 10) 1080 | if ret > 0xffffffffffffffffffffffffffffffffL: 1081 | raise ValueError, "IP Address can't be bigger than 2^128" 1082 | if ret <= 0xffffffffL: 1083 | return (ret, 4) 1084 | else: 1085 | return (ret, 6) 1086 | 1087 | 1088 | def intToIp(ip, version): 1089 | """Transform an integer string into an IP address.""" 1090 | 1091 | # just to be sure and hoping for Python 2.22 1092 | ip = long(ip) 1093 | 1094 | if ip < 0: 1095 | raise ValueError, "IPs can't be negative: %d" % (ip) 1096 | 1097 | ret = '' 1098 | if version == 4: 1099 | if ip > 0xffffffffL: 1100 | raise ValueError, "IPv4 Addresses can't be larger than 0xffffffff: %s" % (hex(ip)) 1101 | for l in range(4): 1102 | ret = str(ip & 0xffL) + '.' + ret 1103 | ip = ip >> 8 1104 | ret = ret[:-1] 1105 | elif version == 6: 1106 | if ip > 0xffffffffffffffffffffffffffffffffL: 1107 | raise ValueError, "IPv6 Addresses can't be larger than 0xffffffffffffffffffffffffffffffff: %s" % (hex(ip)) 1108 | l = '0' * 32 + hex(ip)[2:-1] 1109 | for x in range(1, 33): 1110 | ret = l[-x] + ret 1111 | if x % 4 == 0: 1112 | ret = ':' + ret 1113 | ret = ret[1:] 1114 | else: 1115 | raise ValueError, "only IPv4 and IPv6 supported" 1116 | 1117 | return ret 1118 | 1119 | def _ipVersionToLen(version): 1120 | """Return number of bits in address for a certain IP version. 1121 | 1122 | >>> _ipVersionToLen(4) 1123 | 32 1124 | >>> _ipVersionToLen(6) 1125 | 128 1126 | >>> _ipVersionToLen(5) 1127 | Traceback (most recent call last): 1128 | File "", line 1, in ? 1129 | File "IPy.py", line 1076, in _ipVersionToLen 1130 | raise ValueError, "only IPv4 and IPv6 supported" 1131 | ValueError: only IPv4 and IPv6 supported 1132 | """ 1133 | 1134 | if version == 4: 1135 | return 32 1136 | elif version == 6: 1137 | return 128 1138 | else: 1139 | raise ValueError, "only IPv4 and IPv6 supported" 1140 | 1141 | 1142 | def _countFollowingZeros(l): 1143 | """Return number of elements containing 0 at the beginning of the list.""" 1144 | if len(l) == 0: 1145 | return 0 1146 | elif l[0] != 0: 1147 | return 0 1148 | else: 1149 | return 1 + _countFollowingZeros(l[1:]) 1150 | 1151 | 1152 | _BitTable = {'0': '0000', '1': '0001', '2': '0010', '3': '0011', 1153 | '4': '0100', '5': '0101', '6': '0110', '7': '0111', 1154 | '8': '1000', '9': '1001', 'a': '1010', 'b': '1011', 1155 | 'c': '1100', 'd': '1101', 'e': '1110', 'f': '1111'} 1156 | 1157 | def _intToBin(val): 1158 | """Return the binary representation of an integer as string.""" 1159 | 1160 | if val < 0: 1161 | raise ValueError, "Only positive values allowed" 1162 | s = hex(val).lower() 1163 | ret = '' 1164 | if s[-1] == 'l': 1165 | s = s[:-1] 1166 | for x in s[2:]: 1167 | if __debug__: 1168 | if not _BitTable.has_key(x): 1169 | raise AssertionError, "hex() returned strange result" 1170 | ret += _BitTable[x] 1171 | # remove leading zeros 1172 | while ret[0] == '0' and len(ret) > 1: 1173 | ret = ret[1:] 1174 | return ret 1175 | 1176 | def _count1Bits(num): 1177 | """Find the highest bit set to 1 in an integer.""" 1178 | ret = 0 1179 | while num > 0: 1180 | num = num >> 1 1181 | ret += 1 1182 | return ret 1183 | 1184 | def _count0Bits(num): 1185 | """Find the highest bit set to 0 in an integer.""" 1186 | 1187 | # this could be so easy if _count1Bits(~long(num)) would work as excepted 1188 | num = long(num) 1189 | if num < 0: 1190 | raise ValueError, "Only positive Numbers please: %s" % (num) 1191 | ret = 0 1192 | while num > 0: 1193 | if num & 1 == 1: 1194 | break 1195 | num = num >> 1 1196 | ret += 1 1197 | return ret 1198 | 1199 | 1200 | def _checkPrefix(ip, prefixlen, version): 1201 | """Check the validity of a prefix 1202 | 1203 | Checks if the variant part of a prefix only has 0s, and the length is 1204 | correct. 1205 | 1206 | >>> _checkPrefix(0x7f000000L, 24, 4) 1207 | 1 1208 | >>> _checkPrefix(0x7f000001L, 24, 4) 1209 | 0 1210 | >>> repr(_checkPrefix(0x7f000001L, -1, 4)) 1211 | 'None' 1212 | >>> repr(_checkPrefix(0x7f000001L, 33, 4)) 1213 | 'None' 1214 | """ 1215 | 1216 | # TODO: unify this v4/v6/invalid code in a function 1217 | bits = _ipVersionToLen(version) 1218 | 1219 | if prefixlen < 0 or prefixlen > bits: 1220 | return None 1221 | 1222 | if ip == 0: 1223 | zbits = bits + 1 1224 | else: 1225 | zbits = _count0Bits(ip) 1226 | if zbits < bits - prefixlen: 1227 | return 0 1228 | else: 1229 | return 1 1230 | 1231 | 1232 | def _checkNetmask(netmask, masklen): 1233 | """Checks if a netmask is expressable as a prefixlen.""" 1234 | 1235 | num = long(netmask) 1236 | bits = masklen 1237 | 1238 | # remove zero bits at the end 1239 | while (num & 1) == 0 and bits != 0: 1240 | num = num >> 1 1241 | bits -= 1 1242 | if bits == 0: 1243 | break 1244 | # now check if the rest consists only of ones 1245 | while bits > 0: 1246 | if (num & 1) == 0: 1247 | raise ValueError, "Netmask %s can't be expressed as an prefix." % (hex(netmask)) 1248 | num = num >> 1 1249 | bits -= 1 1250 | 1251 | 1252 | def _checkNetaddrWorksWithPrefixlen(net, prefixlen, version): 1253 | """Check if a base addess of a network is compatible with a prefixlen""" 1254 | if net & _prefixlenToNetmask(prefixlen, version) == net: 1255 | return 1 1256 | else: 1257 | return 0 1258 | 1259 | 1260 | def _netmaskToPrefixlen(netmask): 1261 | """Convert an Integer representing a netmask to a prefixlen. 1262 | 1263 | E.g. 0xffffff00 (255.255.255.0) returns 24 1264 | """ 1265 | 1266 | netlen = _count0Bits(netmask) 1267 | masklen = _count1Bits(netmask) 1268 | _checkNetmask(netmask, masklen) 1269 | return masklen - netlen 1270 | 1271 | 1272 | def _prefixlenToNetmask(prefixlen, version): 1273 | """Return a mask of n bits as a long integer. 1274 | 1275 | From 'IP address conversion functions with the builtin socket module' 1276 | by Alex Martelli 1277 | http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66517 1278 | """ 1279 | if prefixlen == 0: 1280 | return 0 1281 | elif prefixlen < 0: 1282 | raise ValueError, "Prefixlen must be > 0" 1283 | return ((2L<) 10 | { 11 | if(!/^#/) 12 | { 13 | s/\n//; 14 | ($domain, $owner) = split(/:/); 15 | print ("'$domain:Contact for this domain is $owner\n"); 16 | foreach (@ns) 17 | { 18 | print (".".$domain.":$_\n"); 19 | } 20 | } 21 | } 22 | 23 | 24 | print "\n# networks\n"; 25 | 26 | open(IN, ') 29 | { 30 | 31 | if(!/^#/) 32 | { 33 | s/\n//; 34 | print "# $_\n"; 35 | @nets = split(/,/); 36 | $name = shift @nets; 37 | foreach(@nets) 38 | { 39 | my $ip = new Net::IP($_) or die (Net::IP::Error()); 40 | $net=$ip->ip(); 41 | $last=$ip->last_ip(); 42 | $net =~ s/://g; 43 | $last =~ s/://g; 44 | 45 | if($ip->size() > 32) 46 | { 47 | foreach (@ns) 48 | { 49 | print (".".$ip->reverse_ip().":$_\n"); 50 | } 51 | $ip6map{$ip->ip()} = $name; 52 | } 53 | else 54 | { 55 | for($i = int($ip->intip()) + 1; $i < int($ip->intip()) + int($ip->size()); $i++) 56 | { 57 | $nmap{sprintf("%d.%d.%d.%d", ($i >> 24) & 0xff, ($i >> 16) & 0xff, ($i>>8) & 0xff, $i & 0xff)} = "$name"; 58 | $rmap{sprintf("%d.%d.%d.%d", ($i >> 24) & 0xff, ($i >> 16) & 0xff, ($i>>8) & 0xff, $i & 0xff)} = $ip->binip(). ".$name"; 59 | } 60 | } 61 | print ("=net.$name:".$net."\n"); 62 | print ("=broadcast.$name:".$last."\n"); 63 | #print ("Bin : ".$ip->binip()."\n"); 64 | #print ("Mask: ".$ip->mask()."\n"); 65 | #print ("Size: ".$ip->size()."\n"); 66 | #print ("Type: ".$ip->iptype()."\n"); 67 | } 68 | } 69 | } 70 | 71 | close(IN); 72 | 73 | print "\n# hosts\n"; 74 | 75 | open(IN, ') 78 | { 79 | s/\n//; 80 | if(!/^#/) 81 | { 82 | if(!/^-/) 83 | { 84 | if(/^=/ || /^\+/) 85 | { 86 | @i = split(':'); 87 | $rmap{$i[1]} = ''; 88 | } 89 | print "$_\n"; 90 | } 91 | else 92 | { 93 | @fields = split(/\|/); 94 | $name = shift(@fields); 95 | $name =~ s/^.(.)/$1/; 96 | $_ = shift(@fields); 97 | @ips = split(/,/); 98 | $_ = shift(@fields); 99 | @aliases = split(/,/); 100 | $admin = shift(@fields); 101 | if(!$admin) 102 | { 103 | $admin = 'technik@c0re.23.nu'; 104 | } 105 | $_ = shift(@fields); 106 | @mxes = split(/,/); 107 | foreach(@ips) 108 | { 109 | my $ip = new Net::IP($_) or die (Net::IP::Error()); 110 | if(length($ip->binip()) == 32) 111 | { 112 | # IPv4 is easy 113 | if(!$nmap{$_}) 114 | { 115 | print STDERR "*** warning: no network for $_ ($name) - ignoring\n"; 116 | print "# no network for $_ ($name) - ignoring\n"; 117 | } 118 | else 119 | { 120 | print "=$name." . $nmap{$_} . ":$_\n"; 121 | $rmap{$_} = ''; 122 | print "'$name." . $nmap{$_} . ":Host contact is $admin\n"; 123 | } 124 | foreach(@aliases) 125 | { 126 | print "+$_:".$ip->ip()."\n"; 127 | print "'$_:Host contact is $admin\n"; 128 | } 129 | } 130 | else 131 | { 132 | #IPv6 here 133 | $net = $ip->ip(); 134 | $net =~ s/^(....:....:....:....).*/$1:0000:0000:0000:0000/; 135 | if(!$ip6map{$net}) 136 | { 137 | print STDERR "*** warning: no network for $_ ($name) - ignoring\n"; 138 | print "# no network for $_ ($name) - ignoring\n"; 139 | } 140 | else 141 | { 142 | $rip = $ip->ip(); 143 | $rip =~ s/://g; 144 | print "6$name." . $ip6map{$net} . ":".$rip."\n"; 145 | } 146 | foreach(@aliases) 147 | { 148 | $rip = $ip->ip(); 149 | $rip =~ s/://g; 150 | print "3$_:".$rip."\n"; 151 | } 152 | } 153 | 154 | } 155 | } 156 | } 157 | } 158 | 159 | close(IN); 160 | 161 | 162 | print "\n# reverse lookup\n"; 163 | foreach(sort(keys(%nmap))) 164 | { 165 | if($rmap{$_}) 166 | { 167 | print "=".$rmap{$_}.":$_\n"; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /IPy-0.60/example/confbuilder.py: -------------------------------------------------------------------------------- 1 | # This is a hack I use to generate my tinydns configuration 2 | # It serves as e test for converting from Perl Net::IP to 3 | # Python and IPy 4 | 5 | # Further Information might be available at http://c0re.jp/c0de/IPy/ 6 | # Hacked 2001 by drt@un.bewaff.net 7 | 8 | import sys 9 | sys.path.append('..') 10 | 11 | import IPy 12 | 13 | 14 | ns = {'ns.nerxs.com': '213.221.113.70', 15 | 'ns.dorsch.org': '195.143.234.25', 16 | 'ns.c0re.jp': '217.6.214.130'} 17 | 18 | print "# *** nameservers ***" 19 | for x in ns.keys(): 20 | print "=%s:%s" % (x, ns[x]) 21 | 22 | print "\n# *** domains ***" 23 | 24 | fd = open('domains') 25 | 26 | for x in fd.readlines(): 27 | if x[0] != '#': 28 | if x[-1] == '\n': 29 | x = x[:-1] 30 | (domain, owner) = x.split(':') 31 | print "'%s:Contact for this domain is %s" % (domain, owner) 32 | for y in ns.keys(): 33 | print ".%s::%s" % (domain, y) 34 | 35 | fd.close() 36 | 37 | print "\n# *** Networks ***" 38 | 39 | fd = open('networks') 40 | ip6map = {} 41 | rmap = {} 42 | nmap = {} 43 | 44 | for x in fd.readlines(): 45 | if x[-1] == '\n': 46 | x = x[:-1] 47 | if len(x) > 0 and x[0] != '#': 48 | nets = x.split(',') 49 | name = nets.pop(0) 50 | print "# Network: %s" % name 51 | for y in nets: 52 | ip = IPy.IP(y) 53 | print "# Address range: %s (%s), %d addresses" % (ip.strCompressed(), ip.iptype(), ip.len()) 54 | print "=net.%s:%s" % (name, ip.net()) 55 | print "=broadcast.%s:%s" % (name, ip.broadcast()) 56 | 57 | if ip.version() == 4: 58 | for z in ip: 59 | # TODO reverse? 60 | nmap[z.int()] = name 61 | rmap[z.int()] = z.strBin() + "." + name 62 | else: 63 | # IPv6 64 | for z in ns.keys(): 65 | for v in ip.reverseName(): 66 | print ".%s::%s" % (v, z) 67 | ip6map[ip.strFullsize(0)] = name 68 | 69 | fd.close() 70 | 71 | print "\n# *** hosts ***" 72 | 73 | fd = open('hosts') 74 | 75 | for x in fd.readlines(): 76 | if x[-1] == '\n': 77 | x = x[:-1] 78 | if x != '' and x[0] != '#': 79 | if "@Z'.".find(x[0]) >= 0: 80 | print x 81 | else: 82 | if "=+'".find(x[0]) >= 0: 83 | i = x.split(':') 84 | rmap[IPy.IP(i[1]).int()] = '' 85 | print x 86 | else: 87 | x = x[1:] 88 | x += '||||' 89 | fields = x.split('|') 90 | name = fields.pop(0) 91 | if name[0] == '.': 92 | name = name[1:] 93 | v = fields.pop(0) 94 | ips = v.split(',') 95 | v = fields.pop(0) 96 | aliases = v.split(',') 97 | if aliases == ['']: 98 | aliases = [] 99 | admin = fields.pop() 100 | if admin == '': 101 | admin = 'technik@c0re.23.nu' 102 | v = fields.pop() 103 | mxes = v.split(',') 104 | if mxes == ['']: 105 | mxes = [] 106 | for y in ips: 107 | ip = IPy.IP(y) 108 | if ip.version() == 4: 109 | # IPv4 is easy 110 | if not nmap.has_key(ip.int()): 111 | print >>sys.stderr, "*** warning: no network for %s (%s) - ignoring" % (y, name) 112 | print "# no network for %s (%s)" % (y, name) 113 | else: 114 | print "=%s.%s:%s" % (name, nmap[ip.int()], y) 115 | print "'%s.%s:Host contact is %s" % (name, nmap[ip.int()], admin) 116 | rmap[ip.int()] = '' 117 | for z in aliases: 118 | print "+%s:%s" % (z, ip) 119 | print "'%s:Host contact is %s" % (z, admin) 120 | else: 121 | #IPv6 here 122 | net = ip.strFullsize(0) 123 | net = net[:19] + ':0000:0000:0000:0000' 124 | if ip6map.has_key(net): 125 | print >>sys.stderr, "*** warning: no network for %s (%s) - ignoring" % (ip, name) 126 | print "# no network for %s (%s) - ignoring" % (ip, name) 127 | else: 128 | print "6%s.%s:%s"; (name, ip6map[net], ip.strHex()[2:]) 129 | for z in aliases: 130 | print "3%s:%s" % (name, ip.strHex()[2:]) 131 | print "'%s:Host contact is %s" % (name, admin) 132 | 133 | fd.close() 134 | 135 | print "\n# *** reverse lookup ***" 136 | k = nmap.keys() 137 | k.sort() 138 | for x in k: 139 | if rmap.has_key(x) and rmap[x] != '': 140 | print "=%s:%s" % (rmap[x], str(IPy.IP(x))) 141 | 142 | -------------------------------------------------------------------------------- /IPy-0.60/rest.css: -------------------------------------------------------------------------------- 1 | /* 2 | :Author: David Goodger 3 | :Contact: goodger@users.sourceforge.net 4 | :date: $Date: 2003/08/28 22:51:30 $ 5 | :version: $Revision: 1.1 $ 6 | :copyright: This stylesheet has been placed in the public domain. 7 | 8 | Default cascading style sheet for the HTML output of Docutils. 9 | */ 10 | 11 | .first { 12 | margin-top: 0 } 13 | 14 | .last { 15 | margin-bottom: 0 } 16 | 17 | a.toc-backref { 18 | text-decoration: none ; 19 | color: black } 20 | 21 | dd { 22 | margin-bottom: 0.5em } 23 | 24 | div.abstract { 25 | margin: 2em 5em } 26 | 27 | div.abstract p.topic-title { 28 | font-weight: bold ; 29 | text-align: center } 30 | 31 | div.attention, div.caution, div.danger, div.error, div.hint, 32 | div.important, div.note, div.tip, div.warning, div.admonition { 33 | margin: 2em ; 34 | border: medium outset ; 35 | padding: 1em } 36 | 37 | div.attention p.admonition-title, div.caution p.admonition-title, 38 | div.danger p.admonition-title, div.error p.admonition-title, 39 | div.warning p.admonition-title { 40 | color: red ; 41 | font-weight: bold ; 42 | font-family: sans-serif } 43 | 44 | div.hint p.admonition-title, div.important p.admonition-title, 45 | div.note p.admonition-title, div.tip p.admonition-title, 46 | div.admonition p.admonition-title { 47 | font-weight: bold ; 48 | font-family: sans-serif } 49 | 50 | div.dedication { 51 | margin: 2em 5em ; 52 | text-align: center ; 53 | font-style: italic } 54 | 55 | div.dedication p.topic-title { 56 | font-weight: bold ; 57 | font-style: normal } 58 | 59 | div.figure { 60 | margin-left: 2em } 61 | 62 | div.footer, div.header { 63 | font-size: smaller } 64 | 65 | div.sidebar { 66 | margin-left: 1em ; 67 | border: medium outset ; 68 | padding: 0em 1em ; 69 | background-color: #ffffee ; 70 | width: 40% ; 71 | float: right ; 72 | clear: right } 73 | 74 | div.sidebar p.rubric { 75 | font-family: sans-serif ; 76 | font-size: medium } 77 | 78 | div.system-messages { 79 | margin: 5em } 80 | 81 | div.system-messages h1 { 82 | color: red } 83 | 84 | div.system-message { 85 | border: medium outset ; 86 | padding: 1em } 87 | 88 | div.system-message p.system-message-title { 89 | color: red ; 90 | font-weight: bold } 91 | 92 | div.topic { 93 | margin: 2em } 94 | 95 | h1.title { 96 | text-align: center } 97 | 98 | h2.subtitle { 99 | text-align: center } 100 | 101 | hr { 102 | width: 75% } 103 | 104 | ol.simple, ul.simple { 105 | margin-bottom: 1em } 106 | 107 | ol.arabic { 108 | list-style: decimal } 109 | 110 | ol.loweralpha { 111 | list-style: lower-alpha } 112 | 113 | ol.upperalpha { 114 | list-style: upper-alpha } 115 | 116 | ol.lowerroman { 117 | list-style: lower-roman } 118 | 119 | ol.upperroman { 120 | list-style: upper-roman } 121 | 122 | p.attribution { 123 | text-align: right ; 124 | margin-left: 50% } 125 | 126 | p.caption { 127 | font-style: italic } 128 | 129 | p.credits { 130 | font-style: italic ; 131 | font-size: smaller } 132 | 133 | p.label { 134 | white-space: nowrap } 135 | 136 | p.rubric { 137 | font-weight: bold ; 138 | font-size: larger ; 139 | color: darkred ; 140 | text-align: center } 141 | 142 | p.sidebar-title { 143 | font-family: sans-serif ; 144 | font-weight: bold ; 145 | font-size: larger } 146 | 147 | p.sidebar-subtitle { 148 | font-family: sans-serif ; 149 | font-weight: bold } 150 | 151 | p.topic-title { 152 | font-weight: bold } 153 | 154 | pre.address { 155 | margin-bottom: 0 ; 156 | margin-top: 0 ; 157 | font-family: serif ; 158 | font-size: 100% } 159 | 160 | pre.line-block { 161 | font-family: serif ; 162 | font-size: 100% } 163 | 164 | pre.literal-block, pre.doctest-block { 165 | margin-left: 2em ; 166 | margin-right: 2em ; 167 | background-color: #eeeeee } 168 | 169 | span.classifier { 170 | font-family: sans-serif ; 171 | font-style: oblique } 172 | 173 | span.classifier-delimiter { 174 | font-family: sans-serif ; 175 | font-weight: bold } 176 | 177 | span.interpreted { 178 | font-family: sans-serif } 179 | 180 | span.option { 181 | white-space: nowrap } 182 | 183 | span.option-argument { 184 | font-style: italic } 185 | 186 | span.pre { 187 | white-space: pre } 188 | 189 | span.problematic { 190 | color: red } 191 | 192 | table { 193 | margin-top: 0.5em ; 194 | margin-bottom: 0.5em } 195 | 196 | table.citation { 197 | border-left: solid thin gray ; 198 | padding-left: 0.5ex } 199 | 200 | table.docinfo { 201 | margin: 2em 4em } 202 | 203 | table.footnote { 204 | border-left: solid thin black ; 205 | padding-left: 0.5ex } 206 | 207 | td, th { 208 | padding-left: 0.5em ; 209 | padding-right: 0.5em ; 210 | vertical-align: top } 211 | 212 | th.docinfo-name, th.field-name { 213 | font-weight: bold ; 214 | text-align: left ; 215 | white-space: nowrap } 216 | 217 | h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt { 218 | font-size: 100% } 219 | 220 | tt { 221 | background-color: #eeeeee } 222 | 223 | ul.auto-toc { 224 | list-style-type: none } 225 | /* 226 | :Author: Fred L. Drake, Jr. 227 | :Author: Olivier Grisel 228 | :date: $Date: 2004/03/31 22:31:05 $ 229 | :version: $Revision: 1.7 $ 230 | */ 231 | 232 | @import url(default.css); 233 | 234 | body { 235 | margin: 0; 236 | padding: 0; 237 | background-color: #333; 238 | margin: 2% 5% 2% 5%; 239 | } 240 | 241 | 242 | div.document { 243 | font-size: 13px; 244 | padding: 3em 5em 3em 5em; 245 | background-color: white; 246 | border: 1px outset black; 247 | /* background-image: url(strands.png); 248 | background-position: 2em 0; 249 | background-repeat: repeat-y; */ 250 | } 251 | 252 | h1.title { 253 | font-size: 170%; 254 | } 255 | 256 | div.section { 257 | margin: 0px 0px 1.5em 0px; 258 | } 259 | 260 | div.section h1 { 261 | padding: 0.3em; 262 | margin-top: 1em; 263 | background-color: #333; 264 | border: 1px solid; 265 | border-color: #666 #222 #222 #666; 266 | } 267 | 268 | div.section h1 a { 269 | color: white; 270 | } 271 | 272 | h1 { 273 | font-family: sans-serif; 274 | font-size: 135%; 275 | margin-bottom: 4ex; } 276 | 277 | h2 { 278 | font-family: sans-serif; 279 | font-size: 120%; } 280 | 281 | h3 { 282 | font-family: sans-serif; 283 | font-size: 105%; } 284 | 285 | h4 { 286 | font-family: sans-serif; 287 | font-size: 100%; } 288 | 289 | h5 { 290 | font-family: sans-serif; 291 | font-size: 100%; } 292 | 293 | h6 { 294 | font-family: sans-serif; 295 | font-style: italic; 296 | font-size: 100%; } 297 | 298 | a, a:visited { 299 | color: #333; 300 | text-decoration: none; 301 | font-weight: bold; 302 | } 303 | 304 | a:hover { 305 | text-decoration: underline; 306 | } 307 | 308 | a:active { 309 | color: #666; 310 | } 311 | 312 | hr { 313 | width: 75%; 314 | } 315 | 316 | p { 317 | text-align: justify; 318 | } 319 | 320 | .literal .pre { 321 | background-color: white; 322 | font-family: lucidatypewriter, "lucida typewriter", sans-serif; } 323 | 324 | .literal-block { 325 | border: thin solid rgb(180,180,180); 326 | font-family: lucidatypewriter, "lucida typewriter", monospace; 327 | font-size: 90%; 328 | color: #111; 329 | line-height: 140%; 330 | background-color: #eee; 331 | padding: 0.5em; 332 | margin-bottom: 1.5em; 333 | } 334 | 335 | table.table { 336 | margin-left: 2em; 337 | margin-right: 2em; } 338 | 339 | table.table thead { 340 | background-color: rgb(230,230,230); } 341 | 342 | dt { 343 | font-weight: bold; } 344 | 345 | /* docutils uses the "option" class with both "col" and "span" 346 | elements, so we have to be explicit here */ 347 | .option-list span.option { 348 | font-weight: bold; } 349 | 350 | .option-list kbd { 351 | font-family: inherit; } 352 | -------------------------------------------------------------------------------- /IPy-0.60/setup.app/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleDocumentTypes 8 | 9 | 10 | CFBundleTypeOSTypes 11 | 12 | **** 13 | fold 14 | disk 15 | 16 | CFBundleTypeRole 17 | Viewer 18 | 19 | 20 | CFBundleExecutable 21 | setup 22 | CFBundleIconFile 23 | PythonApplet.icns 24 | CFBundleIdentifier 25 | setup 26 | CFBundleInfoDictionaryVersion 27 | 6.0 28 | CFBundleName 29 | setup 30 | CFBundlePackageType 31 | APPL 32 | CFBundleSignature 33 | ???? 34 | 35 | 36 | -------------------------------------------------------------------------------- /IPy-0.60/setup.app/Contents/MacOS/Python: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moki-ics/modscan/4e92d7f94b1a76cc1c74b55cf1b557cf075ba154/IPy-0.60/setup.app/Contents/MacOS/Python -------------------------------------------------------------------------------- /IPy-0.60/setup.app/Contents/MacOS/setup: -------------------------------------------------------------------------------- 1 | #!/System/Library/Frameworks/Python.framework/Versions/2.5/Resources/Python.app/Contents/MacOS/Python 2 | 3 | import sys, os 4 | execdir = os.path.dirname(sys.argv[0]) 5 | executable = os.path.join(execdir, "Python") 6 | resdir = os.path.join(os.path.dirname(execdir), "Resources") 7 | libdir = os.path.join(os.path.dirname(execdir), "Frameworks") 8 | mainprogram = os.path.join(resdir, "__argvemulator_setup.py") 9 | 10 | sys.argv.insert(1, mainprogram) 11 | if 0 or 0: 12 | os.environ["PYTHONPATH"] = resdir 13 | if 0: 14 | os.environ["PYTHONHOME"] = resdir 15 | else: 16 | pypath = os.getenv("PYTHONPATH", "") 17 | if pypath: 18 | pypath = ":" + pypath 19 | os.environ["PYTHONPATH"] = resdir + pypath 20 | os.environ["PYTHONEXECUTABLE"] = executable 21 | os.environ["DYLD_LIBRARY_PATH"] = libdir 22 | os.environ["DYLD_FRAMEWORK_PATH"] = libdir 23 | os.execve(executable, sys.argv, os.environ) 24 | -------------------------------------------------------------------------------- /IPy-0.60/setup.app/Contents/PkgInfo: -------------------------------------------------------------------------------- 1 | APPL???? -------------------------------------------------------------------------------- /IPy-0.60/setup.app/Contents/Resources/PythonApplet.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/moki-ics/modscan/4e92d7f94b1a76cc1c74b55cf1b557cf075ba154/IPy-0.60/setup.app/Contents/Resources/PythonApplet.icns -------------------------------------------------------------------------------- /IPy-0.60/setup.app/Contents/Resources/__argvemulator_setup.py: -------------------------------------------------------------------------------- 1 | import argvemulator, os 2 | 3 | argvemulator.ArgvCollector().mainloop() 4 | execfile(os.path.join(os.path.split(__file__)[0], "setup.py")) 5 | -------------------------------------------------------------------------------- /IPy-0.60/setup.app/Contents/Resources/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # $Id: setup.py 671 2004-08-22 21:02:29Z md $ 3 | 4 | import sys 5 | if "--setuptools" in sys.argv: 6 | sys.argv.remove("--setuptools") 7 | from setuptools import setup 8 | else: 9 | from distutils.core import setup 10 | 11 | # Open IPy.py to read version 12 | from imp import load_source 13 | IPy = load_source("IPy", "IPy.py") 14 | 15 | README = open('README').read().strip() + "\n\n" 16 | ChangeLog = \ 17 | "What's new\n" + \ 18 | "==========\n" + \ 19 | "\n" + \ 20 | open('ChangeLog').read().strip() 21 | 22 | LONG_DESCRIPTION = README + ChangeLog 23 | CLASSIFIERS = [ 24 | 'Development Status :: 5 - Production/Stable', 25 | 'Intended Audience :: Developers', 26 | 'Intended Audience :: System Administrators', 27 | 'Environment :: Plugins', 28 | 'Topic :: Software Development :: Libraries :: Python Modules', 29 | 'Topic :: Communications', 30 | 'Topic :: Internet', 31 | 'Topic :: System :: Networking', 32 | 'License :: OSI Approved :: BSD License', 33 | 'Operating System :: OS Independent', 34 | 'Natural Language :: English', 35 | 'Programming Language :: Python'] 36 | URL = "http://software.inl.fr/trac/trac.cgi/wiki/IPy" 37 | 38 | setup(name="IPy", 39 | version=IPy.__version__, 40 | description="Class and tools for handling of IPv4 and IPv6 addresses and networks", 41 | long_description=LONG_DESCRIPTION, 42 | author="Maximillian Dornseif", 43 | maintainer="Victor Stinner", 44 | maintainer_email="victor.stinner AT inl.fr", 45 | license="BSD License", 46 | keywords="ipv4 ipv6 netmask", 47 | url=URL, 48 | download_url=URL, 49 | classifiers= CLASSIFIERS, 50 | py_modules=["IPy"]) 51 | 52 | -------------------------------------------------------------------------------- /IPy-0.60/setup.cfg: -------------------------------------------------------------------------------- 1 | [egg_info] 2 | tag_build = 3 | tag_date = 0 4 | tag_svn_revision = 0 5 | 6 | -------------------------------------------------------------------------------- /IPy-0.60/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # $Id: setup.py 671 2004-08-22 21:02:29Z md $ 3 | 4 | import sys 5 | if "--setuptools" in sys.argv: 6 | sys.argv.remove("--setuptools") 7 | from setuptools import setup 8 | else: 9 | from distutils.core import setup 10 | 11 | # Open IPy.py to read version 12 | from imp import load_source 13 | IPy = load_source("IPy", "IPy.py") 14 | 15 | README = open('README').read().strip() + "\n\n" 16 | ChangeLog = \ 17 | "What's new\n" + \ 18 | "==========\n" + \ 19 | "\n" + \ 20 | open('ChangeLog').read().strip() 21 | 22 | LONG_DESCRIPTION = README + ChangeLog 23 | CLASSIFIERS = [ 24 | 'Development Status :: 5 - Production/Stable', 25 | 'Intended Audience :: Developers', 26 | 'Intended Audience :: System Administrators', 27 | 'Environment :: Plugins', 28 | 'Topic :: Software Development :: Libraries :: Python Modules', 29 | 'Topic :: Communications', 30 | 'Topic :: Internet', 31 | 'Topic :: System :: Networking', 32 | 'License :: OSI Approved :: BSD License', 33 | 'Operating System :: OS Independent', 34 | 'Natural Language :: English', 35 | 'Programming Language :: Python'] 36 | URL = "http://software.inl.fr/trac/trac.cgi/wiki/IPy" 37 | 38 | setup(name="IPy", 39 | version=IPy.__version__, 40 | description="Class and tools for handling of IPv4 and IPv6 addresses and networks", 41 | long_description=LONG_DESCRIPTION, 42 | author="Maximillian Dornseif", 43 | maintainer="Victor Stinner", 44 | maintainer_email="victor.stinner AT inl.fr", 45 | license="BSD License", 46 | keywords="ipv4 ipv6 netmask", 47 | url=URL, 48 | download_url=URL, 49 | classifiers= CLASSIFIERS, 50 | py_modules=["IPy"]) 51 | 52 | -------------------------------------------------------------------------------- /IPy-0.60/test/test.rst: -------------------------------------------------------------------------------- 1 | >>> from IPy import IP 2 | >>> IP('::ffff:1.2.3.4').strCompressed() 3 | '::ffff:1.2.3.4' 4 | 5 | -------------------------------------------------------------------------------- /IPy-0.60/test/test_IPy.py: -------------------------------------------------------------------------------- 1 | """Unit test for IPy.py 2 | 3 | Further Information might be available at http://c0re.jp/c0de/IPy/ 4 | 5 | Hacked 2001 by drt@un.bewaff.net 6 | """ 7 | 8 | # TODO: unify assert / FilIf usage 9 | 10 | import sys 11 | import threading 12 | sys.path.append('.') 13 | sys.path.append('..') 14 | 15 | import IPy 16 | import unittest 17 | import random 18 | 19 | testloops = 250 20 | 21 | class parseAddress(unittest.TestCase): 22 | okValues = [('FEDC:BA98:7654:3210:FEDC:BA98:7654:3210', 338770000845734292534325025077361652240L), 23 | ('FEDCBA9876543210FEDCBA9876543210', 338770000845734292534325025077361652240L), 24 | ('0xFEDCBA9876543210FEDCBA9876543210', 338770000845734292534325025077361652240L), 25 | ('1080:0000:0000:0000:0008:0800:200C:417A', 21932261930451111902915077091070067066L), 26 | ('1080:0:0:0:8:800:200C:417A', 21932261930451111902915077091070067066L), 27 | ('1080:0::8:800:200C:417A', 21932261930451111902915077091070067066L), 28 | ('1080::8:800:200C:417A', 21932261930451111902915077091070067066L), 29 | ('FF01:0:0:0:0:0:0:43', 338958331222012082418099330867817087043L), 30 | ('FF01:0:0::0:0:43', 338958331222012082418099330867817087043L), 31 | ('FF01::43', 338958331222012082418099330867817087043L), 32 | ('0:0:0:0:0:0:0:1', 1L), 33 | ('0:0:0::0:0:1', 1L), 34 | ('::1', 1L), 35 | ('0:0:0:0:0:0:0:0', 0L), 36 | ('0:0:0::0:0:0', 0L), 37 | ('::', 0L), 38 | ('0:0:0:0:0:0:13.1.68.3', 218186755L), 39 | ('::13.1.68.3', 218186755L), 40 | ('0:0:0:0:0:FFFF:129.144.52.38', 281472855454758L), 41 | ('::FFFF:129.144.52.38', 281472855454758L), 42 | ('1080:0:0:0:8:800:200C:417A', 21932261930451111902915077091070067066L), 43 | ('1080::8:800:200C:417A', 21932261930451111902915077091070067066L), 44 | ('0.0.0.0', 0L), 45 | ('0', 0L), 46 | ('127.0.0.1', 2130706433L), 47 | ('255.255.255.255', 4294967295L), 48 | ('0.0.0.1', 1L), 49 | ('1', 16777216L), 50 | ('213.221.113.87', 3588059479L), 51 | ('0000', 0L), 52 | ('127001', 127001L), 53 | ('1234576', 1234576L), 54 | ('1', 16777216L), 55 | ('232111387', 232111387L), 56 | ('255', 4278190080L), 57 | ('256', 256L), 58 | ('0xffffffff', 4294967295L), 59 | ('0x100000000', 4294967296L), 60 | ('0xffffffffffffffffffffffffffffffff', 0xffffffffffffffffffffffffffffffffL), 61 | ('0xdeadbeef', 0xdeadbeefL), 62 | ('0xdeadbabe', 0xdeadbabeL), 63 | ('0xdeadc0de', 0xdeadc0deL), 64 | ('0xc0decafe', 0xc0decafeL), 65 | ('0xc0debabe', 0xc0debabeL), 66 | ('0xbabec0de', 0xbabec0deL), 67 | ('0xcafebabe', 0xcafebabeL), 68 | ('0x1', 1L), 69 | ('0xabcdef', 11259375L)] 70 | 71 | # TODO: check for more invalid input 72 | 73 | def testKnownValues(self): 74 | """parsing of known values should give known results""" 75 | for x in self.okValues: 76 | (question, answer) = x 77 | (result, version) = IPy.parseAddress(question) 78 | self.assertEqual(answer, result, "%r, %r, %r" % (question, answer, result)) 79 | 80 | def testVersionDistinction(self): 81 | """problems destinguishing IPv4 and IPv6""" 82 | (result, version) = IPy.parseAddress('0xffffffff') 83 | self.assertEqual(version, 4) 84 | (result, version) = IPy.parseAddress('0x100000000') 85 | self.assertEqual(version, 6) 86 | 87 | def testEmpty(self): 88 | """'' should raise an exception""" 89 | self.assertRaises(ValueError, IPy.parseAddress, '') 90 | 91 | def testTooBig(self): 92 | """'' should raise an exception""" 93 | self.assertRaises(ValueError, IPy.parseAddress, '0x100000000000000000000000000000000') 94 | 95 | def testLongIPv4(self): 96 | """'1.2.3.4.5' should raise an exception""" 97 | self.assertRaises(ValueError, IPy.parseAddress, '1.2.3.4.5') 98 | 99 | def testNonByteIPv4(self): 100 | """'1.2.3.256' should raise an exception""" 101 | self.assertRaises(ValueError, IPy.parseAddress, '1.2.3.256') 102 | 103 | def testNegativeByteIPv4(self): 104 | """'-1.2.3.4' and '1.2.3.-4' should raise an exception""" 105 | self.assertRaises(ValueError, IPy.parseAddress, '-1.2.3.4') 106 | self.assertRaises(ValueError, IPy.parseAddress, '1.2.3.-4') 107 | 108 | def testTripleColonIPv6(self): 109 | """'2001:::1' should raise an exception""" 110 | self.assertRaises(ValueError, IPy.parseAddress, '2001:::1') 111 | 112 | def testRepeatDoubleColonIPv6(self): 113 | """'2001::ABCD::1' should raise an exception""" 114 | self.assertRaises(ValueError, IPy.parseAddress, '2001::ABCD::1') 115 | 116 | def testDoubleColonWithEightHextetsIPv6(self): 117 | """'1111::2222:3333:4444:5555:6666:7777:8888' should raise an exception""" 118 | self.assertRaises(ValueError, IPy.parseAddress, '1111::2222:3333:4444:5555:6666:7777:8888') 119 | 120 | def testBeginningColonWithEightHextetsIPv6(self): 121 | """':1111:2222:3333:4444:5555:6666:7777:8888' should raise an exception""" 122 | self.assertRaises(ValueError, IPy.parseAddress, ':1111:2222:3333:4444:5555:6666:7777:8888') 123 | 124 | def testEndingColonWithEightHextetsIPv6(self): 125 | """'1111:2222:3333:4444:5555:6666:7777:8888:' should raise an exception""" 126 | self.assertRaises(ValueError, IPy.parseAddress, '1111:2222:3333:4444:5555:6666:7777:8888:') 127 | 128 | def testNegativeHexletIPv6(self): 129 | """'2001:-ABCD::1' should raise an exception""" 130 | self.assertRaises(ValueError, IPy.parseAddress, '2001:-ABCD::1') 131 | 132 | def testTooBigHexletIPv6(self): 133 | """'2001:10000::1' should raise an exception""" 134 | self.assertRaises(ValueError, IPy.parseAddress, '2001:10000::1') 135 | 136 | def testShortAddressIPv6(self): 137 | """'1111:2222:3333:4444:5555:6666:7777' should raise an exception""" 138 | self.assertRaises(ValueError, IPy.parseAddress, '1111:2222:3333:4444:5555:6666:7777') 139 | 140 | def testLongAddressIPv6(self): 141 | """'1111:2222:3333:4444:5555:6666:7777:8888:9999' should raise an exception""" 142 | self.assertRaises(ValueError, IPy.parseAddress, '1111:2222:3333:4444:5555:6666:7777:8888:9999') 143 | 144 | class _intToIP(unittest.TestCase): 145 | v4values = [(0x7f000001, '127.0.0.1'), 146 | (0x0, '0.0.0.0'), 147 | (0x1, '0.0.0.1'), 148 | (0xf, '0.0.0.15'), 149 | (0xff, '0.0.0.255'), 150 | (0xFFFFFFFFL, '255.255.255.255')] 151 | v6values = [(0x7f000001, '0000:0000:0000:0000:0000:0000:7f00:0001'), 152 | (0x0, '0000:0000:0000:0000:0000:0000:0000:0000'), 153 | (0x1, '0000:0000:0000:0000:0000:0000:0000:0001'), 154 | (0xf, '0000:0000:0000:0000:0000:0000:0000:000f'), 155 | (0xff, '0000:0000:0000:0000:0000:0000:0000:00ff'), 156 | (0xFFFFFFFFL, '0000:0000:0000:0000:0000:0000:ffff:ffff'), 157 | (0x100000000L, '0000:0000:0000:0000:0000:0001:0000:0000'), 158 | (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFL, 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')] 159 | 160 | def testKnownValuesv4(self): 161 | """printing of known IPv4 values should give known results""" 162 | for x in self.v4values: 163 | (question, answer) = x 164 | result = IPy.intToIp(question, 4).lower() 165 | self.assertEqual(answer, result, "%r, %r, %r" % (question, answer, result)) 166 | 167 | def testKnownValuesv6(self): 168 | """printing of known IPv6 values should give known results""" 169 | for x in self.v6values: 170 | (question, answer) = x 171 | result = IPy.intToIp(question, 6).lower() 172 | self.assertEqual(answer, result, "%r, %r, %r" % (question, answer, result)) 173 | 174 | def testNegativeIPv4(self): 175 | """negative IPv4 Values should raise an exception""" 176 | self.assertRaises(ValueError, IPy.intToIp, -1, 4) 177 | 178 | def testNegativeIPv6(self): 179 | """negative IPv6 Values should raise an exception""" 180 | self.assertRaises(ValueError, IPy.intToIp, -1, 6) 181 | 182 | def testLargeIPv4(self): 183 | """IPv4: Values > 0xffffffff should raise an exception""" 184 | self.assertRaises(ValueError, IPy.intToIp, 0x100000000L, 4) 185 | 186 | def testLargeIPv6(self): 187 | """IPv6: Values > 0xffffffffffffffffffffffffffffffff should raise an exception""" 188 | self.assertRaises(ValueError, IPy.intToIp, 0x100000000000000000000000000000000L, 6) 189 | 190 | def testIllegalVersion(self): 191 | """IPVersion other than 4 and 6 should raise an exception""" 192 | self.assertRaises(ValueError, IPy.intToIp, 1, 0) 193 | self.assertRaises(ValueError, IPy.intToIp, 1, 1) 194 | self.assertRaises(ValueError, IPy.intToIp, 1, 2) 195 | self.assertRaises(ValueError, IPy.intToIp, 1, 3) 196 | self.assertRaises(ValueError, IPy.intToIp, 1, 5) 197 | self.assertRaises(ValueError, IPy.intToIp, 1, 7) 198 | self.assertRaises(ValueError, IPy.intToIp, 1, 8) 199 | 200 | class ParseAndBack(unittest.TestCase): 201 | def testRandomValuesv4(self): 202 | for i in range(testloops): 203 | question = long(random.randrange(0x7fffffff)) + long(random.randrange(0x7fffffff)) 204 | self.assertEqual(IPy.parseAddress(IPy.intToIp(question, 4)), (question, 4), hex(question)) 205 | 206 | def testRandomValuesv6(self): 207 | for i in range(testloops): 208 | question = ((long(random.randrange(0x7fffffff)) + long(random.randrange(0x7fffffff))) + 209 | ((long(random.randrange(0x7fffffff)) + long(random.randrange(0x7fffffff))) << 32) + 210 | ((long(random.randrange(0x7fffffff)) + long(random.randrange(0x7fffffff))) << 64) + 211 | ((long(random.randrange(0x7fffffff)) + long(random.randrange(0x7fffffff))) << 96)) 212 | self.assertEqual(IPy.parseAddress(IPy.intToIp(question, 6)), (question, 6), hex(question)) 213 | 214 | 215 | class _countXBits(unittest.TestCase): 216 | def testCount1Bits(self): 217 | self.assertEqual(IPy._count1Bits(0), 0) 218 | self.assertEqual(IPy._count1Bits(0xf), 4) 219 | self.assertEqual(IPy._count1Bits(0x10), 5) 220 | self.assertEqual(IPy._count1Bits(0xff), 8) 221 | self.assertEqual(IPy._count1Bits(0xffff), 16) 222 | self.assertEqual(IPy._count1Bits(0xffffffffL), 32) 223 | self.assertEqual(IPy._count1Bits(0xffffffffffffffffffffffffffffffffL), 128) 224 | 225 | def testCount1Bits(self): 226 | self.assertEqual(IPy._count0Bits(0), 0) 227 | self.assertEqual(IPy._count0Bits(0xf0L), 4) 228 | self.assertEqual(IPy._count0Bits(0xf00L), 8) 229 | self.assertEqual(IPy._count0Bits(0xf000L), 12) 230 | self.assertEqual(IPy._count0Bits(0xf0000L), 16) 231 | self.assertEqual(IPy._count0Bits(0xf00000L), 20) 232 | self.assertEqual(IPy._count0Bits(0xf000000L), 24) 233 | self.assertEqual(IPy._count0Bits(0xf0000000L), 28) 234 | self.assertEqual(IPy._count0Bits(0xff000000L), 24) 235 | self.assertEqual(IPy._count0Bits(0xfff00000L), 20) 236 | self.assertEqual(IPy._count0Bits(0x80000000L), 31) 237 | self.assertEqual(IPy._count0Bits(0xf0000000000000000000000000000000L), 124) 238 | self.assertEqual(IPy._count0Bits(0x80000000000000000000000000000000L), 127) 239 | 240 | 241 | class _intToBin(unittest.TestCase): 242 | knownValues = [(0, '0'), (1, '1'), (2, '10'), (3, '11'), (4, '100'), (5, '101'), 243 | (6, '110'), (7, '111'), (8, '1000'), (9, '1001'), 244 | (0xf, '1111'), (0xff, '11111111'), 245 | (0xFFFFFFFFL, '11111111111111111111111111111111'), 246 | (0x100000000L, '100000000000000000000000000000000'), 247 | (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFL, '11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'), 248 | (0x100000000000000000000000000000000L, '100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')] 249 | 250 | def testKnownValues(self): 251 | """conversion of known values values should give known results""" 252 | for x in self.knownValues: 253 | (question, answer) = x 254 | result = IPy._intToBin(question) 255 | self.assertEqual(answer, result, str(question)) 256 | 257 | def testNegativeIPv4(self): 258 | """negative Values should raise an exception""" 259 | self.assertRaises(ValueError, IPy._intToBin, -1) 260 | 261 | class netmaskPrefixlenConv(unittest.TestCase): 262 | known4Values = [(0xFFFFFFFFL, 32), (0xFFFFFFFEL, 31), (0xFFFFFFFCL, 30), (0xFFFFFFF8L, 29), 263 | (0xFFFFFFF0L, 28), (0xFFFFFFE0L, 27), (0xFFFFFFC0L, 26), (0xFFFFFF80L, 25), 264 | (0xFFFFFF00L, 24), (0xFFFFFE00L, 23), (0xFFFFFC00L, 22), (0xFFFFF800L, 21), 265 | (0xFFFFF000L, 20), (0xFFFFE000L, 19), (0xFFFFC000L, 18), (0xFFFF8000L, 17), 266 | (0xFFFF0000L, 16), (0xFFFE0000L, 15), (0xFFFC0000L, 14), (0xFFF80000L, 13), 267 | (0xFFF00000L, 12), (0xFFE00000L, 11), (0xFFC00000L, 10), (0xFF800000L, 9), 268 | (0xFF000000L, 8), (0xFE000000L, 7), (0xFC000000L, 6), (0xF8000000L, 5), 269 | (0xF0000000L, 4), (0xE0000000L, 3), (0xC0000000L, 2), (0x80000000L, 1)] 270 | known6Values = [(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFL, 128), 271 | (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEL, 127), 272 | (0xFFFFFFFFFFFFFFFFFFFFFFFF80000000L, 97), 273 | (0xFFFFFFFFFFFFFFFFFFFFFFFF00000000L, 96), 274 | (0xFFFFFFFFFFFFFFFFFFFFFFFE00000000L, 95), 275 | (0xFFFFFFFFFFFFFFFF8000000000000000L, 65), 276 | (0xFFFFFFFFFFFFFFFF0000000000000000L, 64), 277 | (0xFFFFFFFFFFFFFFFE0000000000000000L, 63), 278 | (0xFFFFFFFF800000000000000000000000L, 33), 279 | (0xFFFFFFFF000000000000000000000000L, 32), 280 | (0xFFFFFFFE000000000000000000000000L, 31), 281 | (0xC0000000000000000000000000000000L, 2), 282 | (0x80000000000000000000000000000000L, 1)] 283 | 284 | def testKnownValuesv4n2p(self): 285 | """conversion of known values values should give known results""" 286 | for x in self.known4Values: 287 | (question, answer) = x 288 | result = IPy._netmaskToPrefixlen(question) 289 | self.assertEqual(answer, result, hex(question)) 290 | 291 | def testKnownValuesv6n2p(self): 292 | """conversion of known values values should give known results""" 293 | for x in self.known6Values: 294 | (question, answer) = x 295 | result = IPy._netmaskToPrefixlen(question) 296 | self.assertEqual(answer, result, hex(question)) 297 | 298 | def testKnownValuesv4p2n(self): 299 | """conversion of known values values should give known results""" 300 | for x in self.known4Values: 301 | (answer, question) = x 302 | result = IPy._prefixlenToNetmask(question, 4) 303 | self.assertEqual(answer, result, hex(question)) 304 | 305 | def testKnownValuesv6p2n(self): 306 | """conversion of known values values should give known results""" 307 | for x in self.known6Values: 308 | (answer, question) = x 309 | result = IPy._prefixlenToNetmask(question, 6) 310 | self.assertEqual(answer, result, "%d: %s != %s" % (question, hex(answer), result)) 311 | 312 | def testInvalidv4n2p(self): 313 | """Netmasks should be all ones in the first part and all zeros in the second part""" 314 | self.failUnlessRaises(ValueError, IPy._netmaskToPrefixlen, 0xff00ff00L) 315 | 316 | def testInvalidv6n2p(self): 317 | """Netmasks should be all ones in the first part and all zeros in the second part""" 318 | self.failUnlessRaises(ValueError, IPy._netmaskToPrefixlen, 0xff00ff00ff00ff00ff00ff00ff00ff00L) 319 | 320 | 321 | class checkChecks(unittest.TestCase): 322 | 323 | def testCheckNetmaskOk(self): 324 | """Legal Netmasks should be allowed.""" 325 | self.failIf(IPy._checkNetmask(0xffffffffL, 32)) 326 | self.failIf(IPy._checkNetmask(0xffffff00L, 32)) 327 | self.failIf(IPy._checkNetmask(0xffff0000L, 32)) 328 | self.failIf(IPy._checkNetmask(0xff000000L, 32)) 329 | self.failIf(IPy._checkNetmask(0, 32)) 330 | 331 | def testCheckNetmaskFail(self): 332 | """Illegal Netmasks should be rejected.""" 333 | self.failUnlessRaises(ValueError, IPy._checkNetmask, 0xf0ffffffL, 32) 334 | self.failUnlessRaises(ValueError, IPy._checkNetmask, 0xf0f0f0f0L, 32) 335 | self.failUnlessRaises(ValueError, IPy._checkNetmask, 0xff00ff00L, 32) 336 | self.failUnlessRaises(ValueError, IPy._checkNetmask, 0x70000001L, 32) 337 | self.failUnlessRaises(ValueError, IPy._checkNetmask, 0xfffffffL, 32) 338 | 339 | def testCheckPrefixOk(self): 340 | """Legal IP/prefix combinations should check ok.""" 341 | self.failUnless(IPy._checkPrefix(0x0, 32, 4)) 342 | self.failUnless(IPy._checkPrefix(0xffffffffL, 32, 4)) 343 | self.failUnless(IPy._checkPrefix(0x7f000001L, 32, 4)) 344 | self.failUnless(IPy._checkPrefix(0x80000000L, 1, 4)) 345 | self.failUnless(IPy._checkPrefix(0x40000000L, 2, 4)) 346 | self.failUnless(IPy._checkPrefix(0x80000000L, 3, 4)) 347 | self.failUnless(IPy._checkPrefix(0x80000000L, 4, 4)) 348 | self.failUnless(IPy._checkPrefix(0xffffff00L, 24, 4)) 349 | self.failUnless(IPy._checkPrefix(0xffffff00L, 24, 4)) 350 | self.failUnless(IPy._checkPrefix(0xfffffff0L, 28, 4)) 351 | self.failUnless(IPy._checkPrefix(0x0, 32, 4)) 352 | self.failUnless(IPy._checkPrefix(0x0, 1, 4)) 353 | self.failUnless(IPy._checkPrefix(0x0, 0, 4)) 354 | self.failUnless(IPy._checkPrefix(0xffffffffffffffff0000000000000000L, 64, 6)) 355 | self.failUnless(IPy._checkPrefix(0x0L, 64, 6)) 356 | self.failUnless(IPy._checkPrefix(0x0L, 0, 6)) 357 | self.failUnless(IPy._checkPrefix(0x0L, 128, 6)) 358 | self.failUnless(IPy._checkPrefix(0xffffffffffffffffffffffffffffffffL, 128, 6)) 359 | 360 | 361 | def testCheckPrefixFail(self): 362 | """Illegal Prefixes should be catched.""" 363 | self.failIf(IPy._checkPrefix(0x7f000001L, -1, 4)) 364 | self.failIf(IPy._checkPrefix(0x7f000001L, 33, 4)) 365 | self.failIf(IPy._checkPrefix(0x7f000001L, 24, 4)) 366 | self.failIf(IPy._checkPrefix(0x7f000001L, 31, 4)) 367 | self.failIf(IPy._checkPrefix(0x7f000080L, 24, 4)) 368 | self.failIf(IPy._checkPrefix(0x7f000100L, 23, 4)) 369 | self.failIf(IPy._checkPrefix(0x7f000000L, 1, 4)) 370 | self.failIf(IPy._checkPrefix(0x7f000000L, 0, 4)) 371 | self.failIf(IPy._checkPrefix(0x1L, -1, 6)) 372 | self.failIf(IPy._checkPrefix(0x1L, 129, 6)) 373 | self.failIf(IPy._checkPrefix(0xffffffffffffffff0000000000000001L, 64, 6)) 374 | self.failIf(IPy._checkPrefix(0xffffffffffffffff1000000000000000L, 64, 6)) 375 | 376 | 377 | # TODO: _checkNetaddrWorksWithPrefixlen(net, prefixlen, version): 378 | 379 | class PythonObjectBehaviour(unittest.TestCase): 380 | def testIfUsuableAsDictionaryKey(self): 381 | """IP Object should be usable as dictionary key""" 382 | d = {} 383 | d[IPy.IP('127.0.0.1')] = 1 384 | d[IPy.IP('2001::1')] = 1 385 | d[IPy.IP('127.0.0.0/24')] = 1 386 | d[IPy.IP('2001::/64')] = 1 387 | 388 | def testIfCanBeInteratedOver(self): 389 | """It should be possible to iterate over an IP Object.""" 390 | i = 0 391 | for x in IPy.IP('127.0.0.0/24'): 392 | i += 1 393 | self.assertEqual(i, 256, "iteration over a /24 should yiels 256 values") 394 | i = 0 395 | for x in IPy.IP('2001::/124'): 396 | i += 1 397 | self.assertEqual(i, 16, "iteration over a /124 should yiels 16 values") 398 | 399 | def testIfComparesEqual(self): 400 | """nets of the same base and size should be considered equal, others not""" 401 | a = IPy.IP('127.0.0.0/24') 402 | a2 = a 403 | b = IPy.IP('127.0.0.0/24') 404 | c = IPy.IP('127.0.0.0/23') 405 | d = IPy.IP('127.0.0.0/22') 406 | e = IPy.IP('64.0.0.0/24') 407 | self.assertEqual(a2, a) 408 | self.assertEqual(a2, b) 409 | self.assertEqual(a, a) 410 | self.assertEqual(a, b) 411 | self.assertNotEqual(a, c) 412 | self.assertNotEqual(a, d) 413 | self.assertNotEqual(a, e) 414 | self.assertNotEqual(b, c) 415 | self.assertNotEqual(b, d) 416 | self.assertNotEqual(b, e) 417 | self.assertNotEqual(c, d) 418 | self.assertNotEqual(c, e) 419 | self.assertNotEqual(d, e) 420 | 421 | def testIfContainsInt(self): 422 | """__contains__() should work somewhat with ints""" 423 | ip = IPy.IP('127.0.0.0/28') 424 | for x in ip: 425 | self.failUnless(x.int() in ip) 426 | ip = IPy.IP('2001::/124') 427 | for x in ip: 428 | self.failUnless(x.int() in ip) 429 | 430 | def testIfContainsStr(self): 431 | """__contains__() should work somewhat with strings""" 432 | ip = IPy.IP('127.0.0.0/28') 433 | for x in ip: 434 | self.failUnless(x.strNormal() in ip, "%r not in %r" % (x.strNormal(), ip)) 435 | ip = IPy.IP('2001::/124') 436 | for x in ip: 437 | self.failUnless(x.strNormal() in ip, "%r not in %r" % (x.strNormal(), ip)) 438 | 439 | def testIfContainsIPobj(self): 440 | """__contains__() should work somewhat with IP instances""" 441 | ip = IPy.IP('127.0.0.0/28') 442 | for x in ip: 443 | self.failUnless(x in ip) 444 | ip = IPy.IP('2001::/124') 445 | for x in ip: 446 | self.failUnless(x in ip) 447 | 448 | def testActingAsArray(self): 449 | """An IP-object should handle indices.""" 450 | ip = IPy.IP('127.0.0.0/24') 451 | self.assertEqual(ip[0], ip.net()) 452 | self.assertEqual(ip[-1], ip.broadcast()) 453 | self.failUnless(ip[255]) 454 | self.failUnlessRaises(IndexError, ip.__getitem__, 256) 455 | 456 | def testStr(self): 457 | """string() should work somewhat with IP instances""" 458 | ip = IPy.IP('127.0.0.0/28') 459 | for x in ip: 460 | self.failUnless(str(x)) 461 | ip = IPy.IP('2001::/124') 462 | for x in ip: 463 | self.failUnless(str(x)) 464 | 465 | def testRepr(self): 466 | """repr() should work somewhat with IP instances""" 467 | ip = IPy.IP('127.0.0.0/28') 468 | for x in ip: 469 | self.failUnless(repr(x)) 470 | ip = IPy.IP('2001::/124') 471 | for x in ip: 472 | self.failUnless(repr(x)) 473 | 474 | def testLen(self): 475 | """object should have an working __len__() interface.""" 476 | self.failUnlessEqual(len(IPy.IP('127.0.0.0/28')), 16) 477 | self.failUnlessEqual(len(IPy.IP('127.0.0.0/30')), 4) 478 | self.failUnlessEqual(len(IPy.IP('127.0.0.0/26')), 64) 479 | self.failUnlessEqual(len(IPy.IP('127.0.0.0/16')), 2**16) 480 | 481 | # cmp 482 | # IP[0xffffffff] 483 | # IP + IP 484 | # reverse 485 | # netmsk 486 | # ip 487 | 488 | class IPobject(unittest.TestCase): 489 | def testStrCompressed(self): 490 | """Compressed string Output.""" 491 | testValues = ['127.0.0.1', 492 | 'dead::beef', 493 | 'dead:beef::', 494 | 'dead:beef::/48', 495 | 'ff00:1::', 496 | 'ff00:0:f000::', 497 | '0:0:1000::', 498 | '::e000:0/112', 499 | '::e001:0/112', 500 | 'dead:beef::/48', 501 | 'ff00:1::/64', 502 | 'ff00:0:f000::/64', 503 | '0:0:1000::/64', 504 | '::e000:0/112', 505 | '::e001:0/112', 506 | '::1:0:0:0:2', 507 | '0:1:2:3:4:5:6:7', 508 | '1:2:3:4:0:5:6:7', 509 | '1:2:3:4:5:6:7:0', 510 | '1:0:0:2::', 511 | '1:0:0:2::3', 512 | '1::2:0:0:3'] 513 | for question in testValues: 514 | result = IPy.IP(question).strCompressed() 515 | self.failUnlessEqual(question, result, (question, result)) 516 | 517 | def testStrBin(self): 518 | """Binary string Output.""" 519 | 520 | testValues = [('0.0.0.0', '00000000000000000000000000000000'), 521 | ('0.0.0.1', '00000000000000000000000000000001'), 522 | ('255.255.255.255', '11111111111111111111111111111111'), 523 | ('128.0.0.0', '10000000000000000000000000000000'), 524 | ('::0', '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'), 525 | ('::1', '00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001'), 526 | ('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', '11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111'), 527 | ('5555:5555:5555:5555:5555:5555:5555:5555', '01010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101'), 528 | ('aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa:aaaa', '10101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010'), 529 | ('85.85.85.85', '01010101010101010101010101010101'), 530 | ('170.170.170.170', '10101010101010101010101010101010'), 531 | ('127.0.0.1', '01111111000000000000000000000001'), 532 | ('1::2:0:0:3', '00000000000000010000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000011')] 533 | for (question, answer) in testValues: 534 | result = IPy.IP(question).strBin() 535 | self.failUnlessEqual(answer, result, (question, answer, result)) 536 | 537 | def testStrNormal(self): 538 | """Normal string Output.""" 539 | testValues = [(338770000845734292534325025077361652240L, 'fedc:ba98:7654:3210:fedc:ba98:7654:3210'), 540 | (21932261930451111902915077091070067066L, '1080:0:0:0:8:800:200c:417a'), 541 | (338958331222012082418099330867817087043L, 'ff01:0:0:0:0:0:0:43'), 542 | (0L, '0.0.0.0'), 543 | (2130706433L, '127.0.0.1'), 544 | (4294967295L, '255.255.255.255'), 545 | (1L, '0.0.0.1'), 546 | (3588059479L, '213.221.113.87')] 547 | for (question, answer) in testValues: 548 | result = IPy.IP(question).strNormal(question) 549 | self.failUnlessEqual(answer, result, (question, result, answer)) 550 | 551 | def testStrFullsize(self): 552 | """Normal / 0-padded string Output.""" 553 | testValues = [(338770000845734292534325025077361652240L, 'fedc:ba98:7654:3210:fedc:ba98:7654:3210'), 554 | (21932261930451111902915077091070067066L, '1080:0000:0000:0000:0008:0800:200c:417a'), 555 | (338958331222012082418099330867817087043L, 'ff01:0000:0000:0000:0000:0000:0000:0043'), 556 | (0L, '0.0.0.0'), 557 | (2130706433L, '127.0.0.1'), 558 | (4294967295L, '255.255.255.255'), 559 | (1L, '0.0.0.1'), 560 | (3588059479L, '213.221.113.87')] 561 | for (question, answer) in testValues: 562 | result = IPy.IP(question).strFullsize(question) 563 | self.failUnlessEqual(answer, result, (question, result, answer)) 564 | 565 | def testStrHex(self): 566 | """Hex string Output.""" 567 | testValues = [(338770000845734292534325025077361652240L, '0xfedcba9876543210fedcba9876543210'), 568 | (21932261930451111902915077091070067066L, '0x108000000000000000080800200c417a'), 569 | (338958331222012082418099330867817087043L, '0xff010000000000000000000000000043'), 570 | (0L, '0x0'), 571 | (1L, '0x1'), 572 | (4294967295l, '0xffffffff'), 573 | (3588059479L, '0xd5dd7157'), 574 | (0x12345678, '0x12345678')] 575 | for (question, answer) in testValues: 576 | result = IPy.IP(question).strHex(question).lower() 577 | self.failUnlessEqual(answer, result, (question, result, answer)) 578 | 579 | def testStrDec(self): 580 | """Decimal string Output.""" 581 | testValues = [(338770000845734292534325025077361652240L, '338770000845734292534325025077361652240'), 582 | (21932261930451111902915077091070067066L, '21932261930451111902915077091070067066'), 583 | (338958331222012082418099330867817087043L, '338958331222012082418099330867817087043'), 584 | (0L, '0'), 585 | (1L, '1'), 586 | (0xFFFFFFFFL, '4294967295'), 587 | (0xD5DD7157L, '3588059479')] 588 | for (question, answer) in testValues: 589 | result = IPy.IP(question).strDec(question) 590 | self.failUnlessEqual(answer, result, (question, result, answer)) 591 | 592 | def testNet(self): 593 | """Returning of the Network Address""" 594 | self.failUnlessEqual(str(IPy.IP("127.0.0.1").net()), "127.0.0.1") 595 | self.failUnlessEqual(str(IPy.IP("0.0.0.0/0").net()), "0.0.0.0") 596 | self.failUnlessEqual(str(IPy.IP("2001:1234:5678:1234::/64").net()), "2001:1234:5678:1234::") 597 | 598 | 599 | def testBroadcast(self): 600 | """Returning of broadcast address.""" 601 | self.failUnlessEqual(str(IPy.IP("127.0.0.1").broadcast()), "127.0.0.1") 602 | self.failUnlessEqual(str(IPy.IP("0.0.0.0/0").broadcast()), "255.255.255.255") 603 | self.failUnlessEqual(str(IPy.IP("2001:1234:5678:1234::/64").broadcast()), "2001:1234:5678:1234:ffff:ffff:ffff:ffff") 604 | 605 | 606 | def testStrNetmask(self): 607 | """StrNetmask should return netmasks""" 608 | self.failUnlessEqual(IPy.IP("0.0.0.0/0").strNetmask(), "0.0.0.0") 609 | self.failUnlessEqual(IPy.IP("0.0.0.0/32").strNetmask(), "255.255.255.255") 610 | self.failUnlessEqual(IPy.IP("127.0.0.0/24").strNetmask(), "255.255.255.0") 611 | self.failUnlessEqual(IPy.IP("2001:1234:5678:1234::/64").strNetmask(), "/64") 612 | 613 | 614 | def testNetmask(self): 615 | """Netmask should return netmasks""" 616 | self.failUnlessEqual(str(IPy.IP("0.0.0.0/0").netmask()), "0.0.0.0") 617 | self.failUnlessEqual(str(IPy.IP("0.0.0.0/32").netmask()), "255.255.255.255") 618 | self.failUnlessEqual(str(IPy.IP("127.0.0.0/24").netmask()), "255.255.255.0") 619 | self.failUnlessEqual(str(IPy.IP("2001:1234:5678:1234::/64").netmask()), "ffff:ffff:ffff:ffff:0000:0000:0000:0000") 620 | 621 | def testInt(self): 622 | """Prefixlen""" 623 | self.failUnlessEqual(IPy.IP("127.0.0.1").int(), 2130706433) 624 | self.failUnlessEqual(IPy.IP("0.0.0.0").int(), 0) 625 | self.failUnlessEqual(IPy.IP("255.255.255.255").int(), 0xffffffffL) 626 | self.failUnlessEqual(IPy.IP("0000:0000:0000:0000:0000:0000:0000:0000").int(), 0) 627 | self.failUnlessEqual(IPy.IP("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").int(), 0xffffffffffffffffffffffffffffffffL) 628 | self.failUnlessEqual(IPy.IP("2001:1234:5678:9abc:de00:0000:0000:0000").int(), 42540857391974671903776007410583339008L) 629 | 630 | 631 | def testPrefixlen(self): 632 | """Prefixlen""" 633 | self.failUnlessEqual(IPy.IP("127.0.0.1").prefixlen(), 32) 634 | self.failUnlessEqual(IPy.IP("::1").prefixlen(), 128) 635 | self.failUnlessEqual(IPy.IP("10.0.0.0/24").prefixlen(), 24) 636 | self.failUnlessEqual(IPy.IP("10.0.0.0-10.0.0.255").prefixlen(), 24) 637 | self.failUnlessEqual(IPy.IP("10.0.0.0/255.255.255.0").prefixlen(), 24) 638 | self.failUnlessEqual(IPy.IP("2001::/64").prefixlen(), 64) 639 | 640 | 641 | def testVersion(self): 642 | """IP-version detection should work""" 643 | self.failUnlessEqual(IPy.IP("0.0.0.0/0").version(), 4) 644 | self.failUnlessEqual(IPy.IP("::1").version(), 6) 645 | 646 | # TODO: 647 | #def reverseNames(self): 648 | #def reverseName(self): 649 | #def __cmp__(self, other): 650 | #def __add__(self, other): 651 | #def _printPrefix(self, want): 652 | 653 | def testOverlaps(self): 654 | """Overlapping Address Ranges.""" 655 | testValues = [('192.168.0.0/23', '192.168.1.0/24', 1), 656 | ('192.168.0.0/23', '192.168.0.0/20', 1), 657 | ('192.168.0.0/23', '192.168.2.0', 0), 658 | ('192.168.0.0/23', '192.167.255.255', 0), 659 | ('192.168.0.0/23', '192.168.0.0', 1), 660 | ('192.168.0.0/23', '192.168.1.255', 1), 661 | ('192.168.1.0/24', '192.168.0.0/23', -1), 662 | ('127.0.0.1', '127.0.0.1', 1), 663 | ('127.0.0.1', '127.0.0.2', 0)] 664 | for (a, b, answer) in testValues: 665 | result = IPy.IP(a).overlaps(b) 666 | self.failUnlessEqual(answer, result, (a, b, result, answer)) 667 | 668 | def testNetmask(self): 669 | """Normal string Output.""" 670 | testValues = [(338770000845734292534325025077361652240L, '0xfedcba9876543210fedcba9876543210'), 671 | (21932261930451111902915077091070067066L, '0x108000000000000000080800200c417a'), 672 | (338958331222012082418099330867817087043L, '0xff010000000000000000000000000043'), 673 | (0L, '0x0'), 674 | (1L, '0x1'), 675 | (4294967295l, '0xffffffff'), 676 | (3588059479L, '0xd5dd7157')] 677 | for (question, answer) in testValues: 678 | result = IPy.IP(question).strHex(question).lower() 679 | self.failUnlessEqual(answer, result, (question, result, answer)) 680 | 681 | # TODO 682 | #eval(repr(IPy)) 683 | # differences between IP and IPint 684 | 685 | 686 | # I ported this checks to be sure that I don't have errors in my own checks. 687 | class NetIPChecks(unittest.TestCase): 688 | """Checks taken from perls Net::IP""" 689 | def testMisc(self): 690 | ip = IPy.IP('195.114.80/24') 691 | self.assertEqual(ip.int(), 3279048704L) 692 | self.assertEqual(ip.reverseName(),'80.114.195.in-addr.arpa.') 693 | self.assertEqual(ip.strBin(),'11000011011100100101000000000000') 694 | self.assertEqual(str(ip.net()),'195.114.80.0') 695 | self.assertEqual(str(ip),'195.114.80.0/24') 696 | self.assertEqual(ip.prefixlen(),24) 697 | self.assertEqual(ip.version(),4) 698 | self.assertEqual(ip.len(),256) 699 | self.assertEqual(IPy._intToBin(ip.netmask().int()),'11111111111111111111111100000000') 700 | self.assertEqual(ip.strNetmask(),'255.255.255.0') 701 | self.assertEqual(ip.iptype(), 'PUBLIC') 702 | self.assertEqual(ip.broadcast().strBin(),'11000011011100100101000011111111') 703 | self.assertEqual(str(ip.broadcast()),'195.114.80.255') 704 | 705 | ip = IPy.IP('202.31.4/24') 706 | self.assertEqual(str(ip.net()),'202.31.4.0') 707 | 708 | self.failUnlessRaises(ValueError, IPy.IP, '234.245.252.253/2') 709 | 710 | # because we ar using integer representation we don't need a special "binadd" 711 | ip = IPy.IP('62.33.41.9') 712 | ip2 = IPy.IP('0.1.0.5') 713 | self.assertEqual(str(IPy.IP(ip.int() + ip2.int())),'62.34.41.14') 714 | #$T->ok_eq ($ip->binadd($ip2)->ip(),'62.34.41.14',$ip->error()); 715 | 716 | ip = IPy.IP('133.45.0/24') 717 | ip2 = IPy.IP('133.45.1/24') 718 | self.assertEqual((ip + ip2).prefixlen(),23) 719 | 720 | ip2 = IPy.IP('133.44.255.255'); 721 | #$T->ok_eqnum ($ip->bincomp('gt',$ip2),1,$ip->error()); 722 | 723 | # this is something we can't do with IPy 724 | #ip = IPy.IP('133.44.255.255-133.45.0.42'); 725 | #$T->ok_eq (($ip->find_prefixes())[3],'133.45.0.40/31',$ip->error()); 726 | 727 | ip = IPy.IP('201.33.128.0/22'); 728 | ip2 = IPy.IP('201.33.129.0/24'); 729 | #$T->ok_eqnum ($ip->overlaps($ip2),$IP_B_IN_A_OVERLAP,$ip->error()); 730 | 731 | ip = IPy.IP('dead:beef:0::/48') 732 | self.assertEqual(str(ip.net()),'dead:beef::') 733 | self.assertEqual(ip.int(), 295990755014133383690938178081940045824L) 734 | self.assertEqual(ip.strBin(),'11011110101011011011111011101111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000') 735 | self.assertEqual(ip.strCompressed(),'dead:beef::/48') 736 | self.assertEqual(ip.prefixlen(), 48) 737 | self.assertEqual(ip.version(), 6) 738 | self.assertEqual(ip.strNetmask(),'/48') 739 | self.assertEqual(str(ip.netmask()),'ffff:ffff:ffff::') 740 | self.assertEqual(ip.iptype(),'UNASSIGNED') 741 | self.assertEqual(ip.reverseName(),'0.0.0.0.f.e.e.b.d.a.e.d.ip6.int.') 742 | self.assertEqual(str(ip.broadcast()),'dead:beef:0:ffff:ffff:ffff:ffff:ffff') 743 | 744 | ip = IPy.IP('202.31.4/24') 745 | self.assertEqual(str(ip.net()),'202.31.4.0') 746 | 747 | # TODO: fix this in IPy ... after rereading the RfC 748 | # ip = IPy.IP(':1/128'); 749 | #$T->ok_eq ($ip->error(),'Invalid address :1 (starts with :)',$ip->error()); 750 | #$T->ok_eqnum ($ip->errno(),109,$ip->error()); 751 | 752 | ip = IPy.IP('ff00:0:f000::') 753 | ip2 = IPy.IP('0:0:1000::') 754 | self.assertEqual(IPy.IP(ip.int() + ip2.int()).strCompressed(), 'ff00:1::') 755 | 756 | ip = IPy.IP('::e000:0/112') 757 | ip2 = IPy.IP('::e001:0/112') 758 | self.assertEqual(ip.__add__(ip2).prefixlen(),111) 759 | 760 | ip2 = IPy.IP('::dfff:ffff') 761 | #$T->ok_eqnum ($ip->bincomp('gt',$ip2),1,$ip->error()); 762 | 763 | #ip = IPy.IP('::e000:0 - ::e002:42') 764 | #$T->ok_eq (($ip->find_prefixes())[2],'0000:0000:0000:0000:0000:0000:e002:0040/127',$ip->error()); 765 | 766 | ip = IPy.IP('ffff::/16') 767 | ip2 = IPy.IP('8000::/16') 768 | #$T->ok_eqnum ($ip->overlaps($ip2),$IP_NO_OVERLAP,$ip->error()); 769 | 770 | def timeout(func, args=(), kwargs={}, timeout_duration=1, default=None): 771 | """ 772 | ASPN receipe written by dustin lee to call a function with 773 | a timeout using threads: 774 | http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/473878 775 | 776 | Small patch: add setDaemon(True) to allow Python to leave whereas the 777 | thread is not done. 778 | """ 779 | class InterruptableThread(threading.Thread): 780 | def __init__(self): 781 | threading.Thread.__init__(self) 782 | self.result = None 783 | 784 | def run(self): 785 | try: 786 | self.result = func(*args, **kwargs) 787 | except: 788 | self.result = default 789 | 790 | it = InterruptableThread() 791 | it.setDaemon(True) 792 | it.start() 793 | it.join(timeout_duration) 794 | if it.isAlive(): 795 | return default 796 | else: 797 | return it.result 798 | 799 | class RegressionTest(unittest.TestCase): 800 | def testNulNetmask(self): 801 | ip = timeout(IPy.IP, ["0.0.0.0/0.0.0.0"], timeout_duration=0.250, default=None) 802 | if ip: 803 | text = str(ip) 804 | else: 805 | text = "*TIMEOUT*" 806 | self.assertEqual(text, "0.0.0.0/0") 807 | 808 | def testNonZeroType(self): 809 | self.assertEqual(bool(IPy.IP("0.0.0.0/0")), True) 810 | 811 | def testPrivate169(self): 812 | """ 813 | RFC 3330 indicates that 169.254.0.0/16 addresses are private. 814 | They are automatically configured for links in the absence of other 815 | information and should not be used on the internet 816 | """ 817 | self.assertEqual(IPy.IP("169.254.191.164").iptype(), "PRIVATE") 818 | 819 | def testCheckAddrPrefixlenOn(self): 820 | self.assertEqual(len(IPy.IP('192.168.0.0/24')), 256) 821 | self.assertRaises(ValueError, IPy.IP, '192.168.1.0/42') 822 | self.assertRaises(ValueError, IPy.IP, '172.30.1.0/22') 823 | 824 | def testCheckAddrPrefixlenOff(self): 825 | old = IPy.check_addr_prefixlen 826 | IPy.check_addr_prefixlen = False 827 | try: 828 | self.assertEqual(len(IPy.IP('192.168.0.0/24')), 256) 829 | self.assertRaises(ValueError, IPy.IP, '192.168.1.0/42') 830 | self.assertEqual(len(IPy.IP('172.30.1.0/22')), 1024) 831 | finally: 832 | IPy.check_addr_prefixlen = old 833 | 834 | if __name__ == "__main__": 835 | unittest.main() 836 | 837 | -------------------------------------------------------------------------------- /IPy-0.60/test_doc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import doctest 3 | import sys 4 | if hasattr(doctest, "testfile"): 5 | print "=== Test file: README ===" 6 | failure, tests = doctest.testfile('README', optionflags=doctest.ELLIPSIS) 7 | if failure: 8 | sys.exit(1) 9 | 10 | print "=== Test file: test.rst ===" 11 | failure, tests = doctest.testfile('test/test.rst', optionflags=doctest.ELLIPSIS) 12 | if failure: 13 | sys.exit(1) 14 | 15 | print "=== Test IPy module ===" 16 | import IPy 17 | failure, tests = doctest.testmod(IPy) 18 | if failure: 19 | sys.exit(1) 20 | else: 21 | sys.stderr.write("WARNING: doctest has no function testfile (before Python 2.4), unable to check README\n") 22 | 23 | -------------------------------------------------------------------------------- /modscan.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | """ 5 | 6 | File: modscan.py 7 | Desc: Modbus TCP Scanner 8 | Version: 0.1 9 | 10 | Copyright (c) 2008 Mark Bristow 11 | 12 | This program is free software: you can redistribute it and/or modify 13 | it under the terms of the GNU General Public License as published by 14 | the Free Software Foundation version either version 3 of the License, 15 | or (at your option) any later version. 16 | 17 | 18 | This program is distributed in the hope that it will be useful, 19 | but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | GNU General Public License for more details. 22 | 23 | You should have received a copy of the GNU General Public License 24 | along with this program. If not, see . 25 | 26 | """ 27 | 28 | import socket 29 | import array 30 | import optparse 31 | from IPy import IP 32 | import sys 33 | 34 | def main(): 35 | 36 | p = optparse.OptionParser( description=' Finds modbus devices in IP range and determines slave id.\nOutputs in ip:port sid format.', 37 | prog='modscan', 38 | version='modscan 0.1', 39 | usage = "usage: %prog [options] IPRange") 40 | p.add_option('--port', '-p', type='int', dest="port", default=502, help='modbus port DEFAULT:502') 41 | p.add_option('--timeout', '-t', type='int', dest="timeout", default=500, help='socket timeout (mills) DEFAULT:500') 42 | p.add_option('--aggressive', '-a', action ='store_true', help='continues checking past first found SID') 43 | p.add_option('--function', '-f', type='int', dest="function", default=17, help='MODBUS Function Code DEFAULT:17') 44 | p.add_option('--data', type='string', dest="fdata", help='MODBUS Function Data. Unicode escaped "\x00\x01"') 45 | p.add_option('-v', '--verbose', action ='store_true', help='returns verbose output') 46 | p.add_option('-d', '--debug', action ='store_true', help='returns extremely verbose output') 47 | 48 | options, arguments = p.parse_args() 49 | 50 | #make sure we have at least 1 argument (IP Addresses) 51 | if len(arguments) == 1: 52 | 53 | #build basic packet for this test 54 | 55 | """ 56 | Modbus Packet Structure 57 | \x00\x00 \x00\x00 \x00\x00 \x11 \x00 <=================> 58 | Trans ID ProtoID(0) Length UnitID FunctCode Data len(0-253byte) 59 | """ 60 | 61 | #this must be stored in a unsigned byte aray so we can make the assignment later... no string[] in python :( 62 | rsid = array.array('B') 63 | rsid.fromstring("\x00\x00\x00\x00\x00\x02\x01\x01") 64 | 65 | #set function 66 | rsid[7]=options.function 67 | 68 | #add function data 69 | if (options.fdata): 70 | aFData = array.array('B') 71 | 72 | #we must decode the escaped unicode before calling fromstring otherwise the literal \xXX will be interpreted 73 | aFData.fromstring(options.fdata.decode('unicode-escape') ) 74 | rsid += aFData 75 | 76 | #update length 77 | rsid[5]=len(aFData)+2 78 | 79 | #assign IP range 80 | iprange=IP(arguments[0]) 81 | 82 | #print friendly user message 83 | print "Starting Scan..." 84 | 85 | #primary loop over IP addresses 86 | for ip in iprange: 87 | 88 | #print str(ip)+" made it" 89 | #loop over possible sid values (1-247) 90 | for sid in range (1, 247): 91 | 92 | #error messaging 93 | fError=0 94 | msg = str(ip)+":"+str(options.port)+"\t"+str(sid) 95 | 96 | #print "msg="+msg 97 | 98 | #Wrap connect in a try box 99 | try: 100 | #socket object instantiation 101 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 102 | 103 | #set socket timeout, value from cmd is in mills 104 | s.settimeout(float(options.timeout) / float(1000)) 105 | 106 | #connect requires ip addresses in string format so it must be cast 107 | s.connect((str(ip), options.port)) 108 | 109 | except socket.error: 110 | #clean up 111 | fError=1 112 | msg += "\tFAILED TO CONNECT" 113 | s.close() 114 | break 115 | #end try 116 | 117 | 118 | #send query to device 119 | try: 120 | #set slave id 121 | rsid[6]=sid 122 | 123 | #send data to device 124 | s.send(rsid) 125 | 126 | except socket.error: 127 | #failed send close socket 128 | fError=1 129 | msg += "\tFAILED TO SEND" 130 | s.close() 131 | break 132 | #end try 133 | 134 | try: 135 | 136 | #recieve data 137 | data = s.recv(1024) 138 | 139 | except socket.timeout: 140 | fError=1 141 | msg += "\tFAILED TO RECV" 142 | break 143 | #end try 144 | 145 | #examine response 146 | if data: 147 | #parse response 148 | resp = array.array('B') 149 | resp.fromstring(data) 150 | 151 | if (options.debug): 152 | print "Recieved: "+str(resp) 153 | print (int(resp[7]) == int(options.function)) 154 | 155 | #if the function matches the one sent we are all good 156 | if (int(resp[7]) == int(options.function)): 157 | print msg 158 | 159 | #in aggressive mode we keep going 160 | if (not options.aggressive): 161 | break 162 | 163 | #If the function matches the one sent + 0x80 a positive response error code is detected 164 | elif int(resp[7]) == (int(options.function)+128): 165 | #if debug output message 166 | msg += "\tPositive Error Response" 167 | if (options.debug): 168 | print msg 169 | else: 170 | #if debug output message 171 | if (options.debug): 172 | print msg 173 | else: 174 | fError=1 175 | msg += "\tFAILED TO RECIEVE" 176 | s.close() 177 | break 178 | 179 | #end SID for 180 | 181 | 182 | #report based on verbosity 183 | if (options.verbose and fError): 184 | print msg 185 | elif (options.debug): 186 | print msg 187 | #end IP for 188 | 189 | #close socket, no longer needed 190 | #s.shutdown(socket.SHUT_RDWR) 191 | s.close() 192 | 193 | print "Scan Complete." 194 | 195 | #bad number of arguments. print help 196 | else: 197 | p.print_help() 198 | 199 | 200 | if __name__ == '__main__': 201 | try : main() 202 | except KeyboardInterrupt: 203 | print "Scan canceled by user." 204 | print "Thank you for using ModScan" 205 | except : 206 | sys.exit() 207 | 208 | --------------------------------------------------------------------------------