Package PyFoam :: Package ThirdParty :: Module IPy
[hide private]
[frames] | no frames]

Source Code for Module PyFoam.ThirdParty.IPy

   1  """ IPy - class and tools for handling of IPv4 and IPv6 Addresses and Networks. 
   2   
   3  Copyright (c) 2006, INL 
   4  Copyright (c) 2001-2005, Maximillian Dornseif 
   5  All rights reserved. 
   6  Redistribution and use in source and binary forms, with or without 
   7  modification, are permitted provided that the following conditions are met: 
   8   
   9      * Redistributions of source code must retain the above copyright 
  10        notice, this list of conditions and the following disclaimer. 
  11      * Redistributions in binary form must reproduce the above copyright 
  12        notice, this list of conditions and the following disclaimer in the 
  13        documentation and/or other materials provided with the distribution. 
  14      * Neither the name of IPy nor the names of its contributors may be used 
  15        to endorse or promote products derived from this software without 
  16        specific prior written permission. 
  17   
  18  THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY 
  19  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
  20  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
  21  DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY 
  22  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
  23  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
  24  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
  25  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
  26  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
  27  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  28   
  29   
  30  Presentation of the API 
  31  ======================= 
  32   
  33  The IP class allows a comfortable parsing and handling for most 
  34  notations in use for IPv4 and IPv6 Addresses and Networks. It was 
  35  greatly inspired bei RIPE's Perl module NET::IP's interface but 
  36  doesn't share the Implementation. It doesn't share non-CIDR netmasks, 
  37  so funky stuff lixe a netmask 0xffffff0f can't be done here. 
  38   
  39      >>> from IPy import IP 
  40      >>> ip = IP('127.0.0.0/30') 
  41      >>> for x in ip: 
  42      ...  print x 
  43      ... 
  44      127.0.0.0 
  45      127.0.0.1 
  46      127.0.0.2 
  47      127.0.0.3 
  48      >>> ip2 = IP('0x7f000000/30') 
  49      >>> ip == ip2 
  50      1 
  51      >>> ip.reverseNames() 
  52      ['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.'] 
  53      >>> ip.reverseName() 
  54      '0-3.0.0.127.in-addr.arpa.' 
  55      >>> ip.iptype() 
  56      'PRIVATE' 
  57   
  58   
  59  Support all IP addresses 
  60  ======================== 
  61   
  62  It can detect about a dozen different ways of expressing IP addresses 
  63  and networks, parse them and distinguish between IPv4 and IPv6 addresses: 
  64   
  65      >>> IP('10.0.0.0/8').version() 
  66      4 
  67      >>> IP('::1').version() 
  68      6 
  69   
  70  IPv4 addresses 
  71  -------------- 
  72   
  73      >>> print IP(0x7f000001) 
  74      127.0.0.1 
  75      >>> print IP('0x7f000001') 
  76      127.0.0.1 
  77      >>> print IP('127.0.0.1') 
  78      127.0.0.1 
  79      >>> print IP('10') 
  80      10.0.0.0 
  81   
  82  IPv6 addresses 
  83  -------------- 
  84   
  85      >>> print IP('1080:0:0:0:8:800:200C:417A') 
  86      1080:0000:0000:0000:0008:0800:200c:417a 
  87      >>> print IP('1080::8:800:200C:417A') 
  88      1080:0000:0000:0000:0008:0800:200c:417a 
  89      >>> print IP('::1') 
  90      0000:0000:0000:0000:0000:0000:0000:0001 
  91      >>> print IP('::13.1.68.3') 
  92      0000:0000:0000:0000:0000:0000:0d01:4403 
  93   
  94  Network mask 
  95  ------------ 
  96   
  97      >>> print IP('127.0.0.0/8') 
  98      127.0.0.0/8 
  99      >>> print IP('127.0.0.0/255.0.0.0') 
 100      127.0.0.0/8 
 101      >>> print IP('127.0.0.0-127.255.255.255') 
 102      127.0.0.0/8 
 103   
 104   
 105  Option check_addr_prefixlen 
 106  =========================== 
 107   
 108  By default, IPy rejects uncommon netmask like 172.30.1.0/22: 
 109   
 110      >>> import IPy 
 111      >>> IPy.check_addr_prefixlen = True    # default value 
 112      >>> ips = IP('172.30.1.0/22') 
 113      Traceback (most recent call last): 
 114        ... 
 115      ValueError: IP('172.30.1.0/22') has invalid prefix length (22) 
 116   
 117  You can change this behaviour with global option check_addr_prefixlen: 
 118   
 119      >>> IPy.check_addr_prefixlen = False   # disable 
 120      >>> ips = IP('172.30.1.0/22') 
 121      >>> len(ips) 
 122      1024 
 123   
 124   
 125  Convert address to string 
 126  ========================= 
 127   
 128  Nearly all class methods which return a string have an optional 
 129  parameter 'wantprefixlen' which controlles if the prefixlen or netmask 
 130  is printed. Per default the prefilen is always shown if the net 
 131  contains more than one address:: 
 132   
 133      wantprefixlen == 0 / None        don't return anything    1.2.3.0 
 134      wantprefixlen == 1               /prefix                  1.2.3.0/24 
 135      wantprefixlen == 2               /netmask                 1.2.3.0/255.255.255.0 
 136      wantprefixlen == 3               -lastip                  1.2.3.0-1.2.3.255 
 137   
 138  You can also change the defaults on an per-object basis by fiddeling with the class members: 
 139   
 140   * NoPrefixForSingleIp 
 141   * WantPrefixLen 
 142   
 143  Examples of string conversions: 
 144   
 145      >>> IP('10.0.0.0/32').strNormal() 
 146      '10.0.0.0' 
 147      >>> IP('10.0.0.0/24').strNormal() 
 148      '10.0.0.0/24' 
 149      >>> IP('10.0.0.0/24').strNormal(0) 
 150      '10.0.0.0' 
 151      >>> IP('10.0.0.0/24').strNormal(1) 
 152      '10.0.0.0/24' 
 153      >>> IP('10.0.0.0/24').strNormal(2) 
 154      '10.0.0.0/255.255.255.0' 
 155      >>> IP('10.0.0.0/24').strNormal(3) 
 156      '10.0.0.0-10.0.0.255' 
 157      >>> ip = IP('10.0.0.0') 
 158      >>> print ip 
 159      10.0.0.0 
 160      >>> ip.NoPrefixForSingleIp = None 
 161      >>> print ip 
 162      10.0.0.0/32 
 163      >>> ip.WantPrefixLen = 3 
 164      >>> print ip 
 165      10.0.0.0-10.0.0.0 
 166   
 167   
 168  What's new? 
 169  =========== 
 170   
 171  Changes between version 0.51 and 0.52: 
 172   
 173   * Fix strCompressed() for IPv6 "ffff:ffff:ffff:ffff:ffff:f:f:fffc/127" 
 174   
 175  Changes between version 0.5 and 0.51: 
 176   
 177   * Use real name of IPy author 
 178   * Use version "0.51" to help packaging since 0.5 was smaller than 0.42 
 179   * Fix unit test for Python 2.3 (don't use doctest.testfile) 
 180   * Fix unit test for Python 2.5 (problem of hex() lower case) 
 181   * IPy now works on Python 2.2 to 2.5 
 182   
 183  Changes between version 0.42 and 0.5: Fix all known bugs: 
 184   
 185   * Apply Jean Gillaux's patch for netmask "/0.0.0.0" bug 
 186   * Apply William McVey's patch for __nonzero__() bug 
 187   * Apply Victor Stinner patch: setup.py can use setuptools and fix URLs 
 188   * Allow "172.30.1.0/22" with new option IPy.check_addr_prefixlen=False 
 189   
 190  Other changes: 
 191   
 192   * Add regression tests 
 193   * Create AUTHORS file 
 194   
 195   
 196  Compatibility and links 
 197  ======================= 
 198   
 199  IPy works on Python version 2.2 to 2.5. 
 200   
 201  This Python module is under BSD license: see COPYING file. 
 202   
 203  Further Information might be available at: http://software.inl.fr/trac/trac.cgi/wiki/IPy 
 204   
 205  TODO 
 206  ==== 
 207   
 208   * better comparison (__cmp__ and friends) 
 209   * tests for __cmp__ 
 210   * always write hex values lowercase 
 211   * interpret 2001:1234:5678:1234/64 as 2001:1234:5678:1234::/64 
 212   * move size in bits into class variables to get rid of some "if self._ipversion ..." 
 213   * support for base85 encoding 
 214   * support for output of IPv6 encoded IPv4 Addresses 
 215   * update address type tables 
 216   * first-last notation should be allowed for IPv6 
 217   * add IPv6 docstring examples 
 218   * check better for negative parameters 
 219   * add addition / aggregation 
 220   * move reverse name stuff out of the classes and refactor it 
 221   * support for aggregation of more than two nets at once 
 222   * support for aggregation with "holes" 
 223   * support for finding common prefix 
 224   * '>>' and '<<' for prefix manipulation 
 225   * add our own exceptions instead ValueError all the time 
 226   * rename checkPrefix to checkPrefixOk 
 227   * add more documentation and doctests 
 228   * refactor 
 229  """ 
 230   
 231  # $HeadURL$ 
 232  # $Id$ 
 233   
 234  __rcsid__ = '$Id$' 
 235  __version__ = '0.51' 
 236   
 237  import types 
 238   
 239  # New in API 0.5: if true, it rejects uncommon net mask like "172.30.1.0/22". 
 240  # Default is enable, ie. raise ValueError on such netmask. 
 241  check_addr_prefixlen = 1 
 242   
 243  # Definition of the Ranges for IPv4 IPs 
 244  # this should include www.iana.org/assignments/ipv4-address-space 
 245  # and www.iana.org/assignments/multicast-addresses 
 246  IPv4ranges = { 
 247      '0':                'PUBLIC',   # fall back 
 248      '00000000':         'PRIVATE',  # 0/8 
 249      '00001010':         'PRIVATE',  # 10/8 
 250      '01111111':         'PRIVATE',  # 127.0/8 
 251      '1':                'PUBLIC',   # fall back 
 252      '1010100111111110': 'PRIVATE',  # 169.254/16 
 253      '101011000001':     'PRIVATE',  # 172.16/12 
 254      '1100000010101000': 'PRIVATE',  # 192.168/16 
 255      '11011111':         'RESERVED', # 223/8 
 256      '111':              'RESERVED'  # 224/3 
 257      } 
 258   
 259  # Definition of the Ranges for IPv6 IPs 
 260  # see also www.iana.org/assignments/ipv6-address-space, 
 261  # www.iana.org/assignments/ipv6-tla-assignments, 
 262  # www.iana.org/assignments/ipv6-multicast-addresses, 
 263  # www.iana.org/assignments/ipv6-anycast-addresses 
 264  IPv6ranges = { 
 265      '00000000'              : 'RESERVED',       # ::/8 
 266      '00000001'              : 'UNASSIGNED',     # 100::/8 
 267      '0000001'               : 'NSAP',           # 200::/7 
 268      '0000010'               : 'IPX',            # 400::/7 
 269      '0000011'               : 'UNASSIGNED',     # 600::/7 
 270      '00001'                 : 'UNASSIGNED',     # 800::/5 
 271      '0001'                  : 'UNASSIGNED',     # 1000::/4 
 272      '0010000000000000'      : 'RESERVED',       # 2000::/16 Reserved 
 273      '0010000000000001'      : 'ASSIGNABLE',     # 2001::/16 Sub-TLA Assignments [RFC2450] 
 274      '00100000000000010000000': 'ASSIGNABLE IANA',  # 2001:0000::/29 - 2001:01F8::/29 IANA 
 275      '00100000000000010000001': 'ASSIGNABLE APNIC', # 2001:0200::/29 - 2001:03F8::/29 APNIC 
 276      '00100000000000010000010': 'ASSIGNABLE ARIN',  # 2001:0400::/29 - 2001:05F8::/29 ARIN 
 277      '00100000000000010000011': 'ASSIGNABLE RIPE',  # 2001:0600::/29 - 2001:07F8::/29 RIPE NCC 
 278      '0010000000000010'      : '6TO4',           # 2002::/16 "6to4" [RFC3056] 
 279      '0011111111111110'      : '6BONE',          # 3FFE::/16 6bone Testing [RFC2471] 
 280      '0011111111111111'      : 'RESERVED',       # 3FFF::/16 Reserved 
 281      '010'                   : 'GLOBAL-UNICAST', # 4000::/3 
 282      '011'                   : 'UNASSIGNED',     # 6000::/3 
 283      '100'                   : 'GEO-UNICAST',    # 8000::/3 
 284      '101'                   : 'UNASSIGNED',     # A000::/3 
 285      '110'                   : 'UNASSIGNED',     # C000::/3 
 286      '1110'                  : 'UNASSIGNED',     # E000::/4 
 287      '11110'                 : 'UNASSIGNED',     # F000::/5 
 288      '111110'                : 'UNASSIGNED',     # F800::/6 
 289      '1111110'               : 'UNASSIGNED',     # FC00::/7 
 290      '111111100'             : 'UNASSIGNED',     # FE00::/9 
 291      '1111111010'            : 'LINKLOCAL',      # FE80::/10 
 292      '1111111011'            : 'SITELOCAL',      # FEC0::/10 
 293      '11111111'              : 'MULTICAST',      # FF00::/8 
 294      '0' * 96                : 'IPV4COMP',       # ::/96 
 295      '0' * 80 + '1' * 16     : 'IPV4MAP',        # ::FFFF:0:0/96 
 296      '0' * 128               : 'UNSPECIFIED',    # ::/128 
 297      '0' * 127 + '1'         : 'LOOPBACK'        # ::1/128 
 298      } 
 299   
 300   
301 -class IPint:
302 """Handling of IP addresses returning integers. 303 304 Use class IP instead because some features are not implemented for 305 IPint.""" 306
307 - def __init__(self, data, ipversion = 0):
308 """Create an instance of an IP object. 309 310 Data can be a network specification or a single IP. IP 311 Addresses can be specified in all forms understood by 312 parseAddress.() the size of a network can be specified as 313 314 /prefixlen a.b.c.0/24 2001:658:22a:cafe::/64 315 -lastIP a.b.c.0-a.b.c.255 2001:658:22a:cafe::-2001:658:22a:cafe:ffff:ffff:ffff:ffff 316 /decimal netmask a.b.c.d/255.255.255.0 not supported for IPv6 317 318 If no size specification is given a size of 1 address (/32 for 319 IPv4 and /128 for IPv6) is assumed. 320 321 >>> print IP('127.0.0.0/8') 322 127.0.0.0/8 323 >>> print IP('127.0.0.0/255.0.0.0') 324 127.0.0.0/8 325 >>> print IP('127.0.0.0-127.255.255.255') 326 127.0.0.0/8 327 328 See module documentation for more examples. 329 """ 330 331 self.NoPrefixForSingleIp = 1 # Print no Prefixlen for /32 and /128 332 self.WantPrefixLen = None # Do we want prefix printed by default? see _printPrefix() 333 334 netbits = 0 335 prefixlen = -1 336 337 # handling of non string values in constructor 338 if type(data) == types.IntType or type(data) == types.LongType: 339 self.ip = long(data) 340 if ipversion == 0: 341 if self.ip < 0x100000000L: 342 ipversion = 4 343 else: 344 ipversion = 6 345 if ipversion == 4: 346 prefixlen = 32 347 elif ipversion == 6: 348 prefixlen = 128 349 else: 350 raise ValueError, "only IPv4 and IPv6 supported" 351 self._ipversion = ipversion 352 self._prefixlen = prefixlen 353 # handle IP instance as an parameter 354 elif isinstance(data, IPint): 355 self._ipversion = data._ipversion 356 self._prefixlen = data._prefixlen 357 self.ip = data.ip 358 else: 359 # TODO: refactor me! 360 # splitting of a string into IP and prefixlen et. al. 361 x = data.split('-') 362 if len(x) == 2: 363 # a.b.c.0-a.b.c.255 specification ? 364 (ip, last) = x 365 (self.ip, parsedVersion) = parseAddress(ip) 366 if parsedVersion != 4: 367 raise ValueError, "first-last notation only allowed for IPv4" 368 (last, lastversion) = parseAddress(last) 369 if lastversion != 4: 370 raise ValueError, "last address should be IPv4, too" 371 if last < self.ip: 372 raise ValueError, "last address should be larger than first" 373 size = last - self.ip 374 netbits = _count1Bits(size) 375 elif len(x) == 1: 376 x = data.split('/') 377 # if no prefix is given use defaults 378 if len(x) == 1: 379 ip = x[0] 380 prefixlen = -1 381 elif len(x) > 2: 382 raise ValueError, "only one '/' allowed in IP Address" 383 else: 384 (ip, prefixlen) = x 385 if prefixlen.find('.') != -1: 386 # check if the user might have used a netmask like 387 # a.b.c.d/255.255.255.0 388 (netmask, vers) = parseAddress(prefixlen) 389 if vers != 4: 390 raise ValueError, "netmask must be IPv4" 391 prefixlen = _netmaskToPrefixlen(netmask) 392 elif len(x) > 2: 393 raise ValueError, "only one '-' allowed in IP Address" 394 else: 395 raise ValueError, "can't parse" 396 397 (self.ip, parsedVersion) = parseAddress(ip) 398 if ipversion == 0: 399 ipversion = parsedVersion 400 if prefixlen == -1: 401 if ipversion == 4: 402 prefixlen = 32 - netbits 403 elif ipversion == 6: 404 prefixlen = 128 - netbits 405 else: 406 raise ValueError, "only IPv4 and IPv6 supported" 407 self._ipversion = ipversion 408 self._prefixlen = int(prefixlen) 409 410 if not _checkNetaddrWorksWithPrefixlen(self.ip, self._prefixlen, self._ipversion): 411 raise ValueError, "%s has invalid prefix length (%s)" % (repr(self), self._prefixlen)
412 413
414 - def int(self):
415 """Return the first / base / network addess as an (long) integer. 416 417 The same as IP[0]. 418 419 >>> "%X" % IP('10.0.0.0/8').int() 420 'A000000' 421 """ 422 return self.ip
423
424 - def version(self):
425 """Return the IP version of this Object. 426 427 >>> IP('10.0.0.0/8').version() 428 4 429 >>> IP('::1').version() 430 6 431 """ 432 return self._ipversion
433
434 - def prefixlen(self):
435 """Returns Network Prefixlen. 436 437 >>> IP('10.0.0.0/8').prefixlen() 438 8 439 """ 440 return self._prefixlen
441
442 - def net(self):
443 """Return the base (first) address of a network as an (long) integer.""" 444 445 return self.int()
446
447 - def broadcast(self):
448 """Return the broadcast (last) address of a network as an (long) integer. 449 450 The same as IP[-1].""" 451 return self.int() + self.len() - 1
452
453 - def _printPrefix(self, want):
454 """Prints Prefixlen/Netmask. 455 456 Not really. In fact it is our universal Netmask/Prefixlen printer. 457 This is considered an internel function. 458 459 want == 0 / None don't return anything 1.2.3.0 460 want == 1 /prefix 1.2.3.0/24 461 want == 2 /netmask 1.2.3.0/255.255.255.0 462 want == 3 -lastip 1.2.3.0-1.2.3.255 463 """ 464 465 if (self._ipversion == 4 and self._prefixlen == 32) or \ 466 (self._ipversion == 6 and self._prefixlen == 128): 467 if self.NoPrefixForSingleIp: 468 want = 0 469 if want == None: 470 want = self.WantPrefixLen 471 if want == None: 472 want = 1 473 if want: 474 if want == 2: 475 # this should work wit IP and IPint 476 netmask = self.netmask() 477 if type(netmask) != types.IntType and type(netmask) != types.LongType: 478 netmask = netmask.int() 479 return "/%s" % (intToIp(netmask, self._ipversion)) 480 elif want == 3: 481 return "-%s" % (intToIp(self.ip + self.len() - 1, self._ipversion)) 482 else: 483 # default 484 return "/%d" % (self._prefixlen) 485 else: 486 return ''
487 488 # We have different Favours to convert to: 489 # strFullsize 127.0.0.1 2001:0658:022a:cafe:0200:c0ff:fe8d:08fa 490 # strNormal 127.0.0.1 2001:658:22a:cafe:200:c0ff:fe8d:08fa 491 # strCompressed 127.0.0.1 2001:658:22a:cafe::1 492 # strHex 0x7F000001L 0x20010658022ACAFE0200C0FFFE8D08FA 493 # strDec 2130706433 42540616829182469433547974687817795834 494
495 - def strBin(self, wantprefixlen = None):
496 """Return a string representation as a binary value. 497 498 >>> print IP('127.0.0.1').strBin() 499 01111111000000000000000000000001 500 """ 501 502 503 if self._ipversion == 4: 504 bits = 32 505 elif self._ipversion == 6: 506 bits = 128 507 else: 508 raise ValueError, "only IPv4 and IPv6 supported" 509 510 if self.WantPrefixLen == None and wantprefixlen == None: 511 wantprefixlen = 0 512 ret = _intToBin(self.ip) 513 return '0' * (bits - len(ret)) + ret + self._printPrefix(wantprefixlen)
514
515 - def strCompressed(self, wantprefixlen = None):
516 """Return a string representation in compressed format using '::' Notation. 517 518 >>> IP('127.0.0.1').strCompressed() 519 '127.0.0.1' 520 >>> IP('2001:0658:022a:cafe:0200::1').strCompressed() 521 '2001:658:22a:cafe:200::1' 522 >>> IP('ffff:ffff:ffff:ffff:ffff:f:f:fffc/127').strCompressed() 523 'ffff:ffff:ffff:ffff:ffff:f:f:fffc/127' 524 """ 525 526 if self.WantPrefixLen == None and wantprefixlen == None: 527 wantprefixlen = 1 528 529 if self._ipversion == 4: 530 return self.strFullsize(wantprefixlen) 531 else: 532 # find the longest sequence of '0' 533 hextets = [int(x, 16) for x in self.strFullsize(0).split(':')] 534 # every element of followingzeros will contain the number of zeros 535 # following the corrospondending element of hextetes 536 followingzeros = [0] * 8 537 for i in range(len(hextets)): 538 followingzeros[i] = _countFollowingZeros(hextets[i:]) 539 # compressionpos is the position where we can start removing zeros 540 compressionpos = followingzeros.index(max(followingzeros)) 541 if max(followingzeros) > 1: 542 # genererate string with the longest number of zeros cut out 543 # now we need hextets as strings 544 hextets = [x for x in self.strNormal(0).split(':')] 545 while compressionpos < len(hextets) and hextets[compressionpos] == '0': 546 del(hextets[compressionpos]) 547 hextets.insert(compressionpos, '') 548 if compressionpos + 1 >= len(hextets): 549 hextets.append('') 550 if compressionpos == 0: 551 hextets = [''] + hextets 552 return ':'.join(hextets) + self._printPrefix(wantprefixlen) 553 else: 554 return self.strNormal(0) + self._printPrefix(wantprefixlen)
555
556 - def strNormal(self, wantprefixlen = None):
557 """Return a string representation in the usual format. 558 559 >>> print IP('127.0.0.1').strNormal() 560 127.0.0.1 561 >>> print IP('2001:0658:022a:cafe:0200::1').strNormal() 562 2001:658:22a:cafe:200:0:0:1 563 """ 564 565 if self.WantPrefixLen == None and wantprefixlen == None: 566 wantprefixlen = 1 567 568 if self._ipversion == 4: 569 ret = self.strFullsize(0) 570 elif self._ipversion == 6: 571 ret = ':'.join([hex(x)[2:] for x in [int(x, 16) for x in self.strFullsize(0).split(':')]]) 572 else: 573 raise ValueError, "only IPv4 and IPv6 supported" 574 575 576 577 return ret + self._printPrefix(wantprefixlen)
578
579 - def strFullsize(self, wantprefixlen = None):
580 """Return a string representation in the non mangled format. 581 582 >>> print IP('127.0.0.1').strFullsize() 583 127.0.0.1 584 >>> print IP('2001:0658:022a:cafe:0200::1').strFullsize() 585 2001:0658:022a:cafe:0200:0000:0000:0001 586 """ 587 588 if self.WantPrefixLen == None and wantprefixlen == None: 589 wantprefixlen = 1 590 591 return intToIp(self.ip, self._ipversion).lower() + self._printPrefix(wantprefixlen)
592
593 - def strHex(self, wantprefixlen = None):
594 """Return a string representation in hex format in lower case. 595 596 >>> IP('127.0.0.1').strHex() 597 '0x7f000001' 598 >>> IP('2001:0658:022a:cafe:0200::1').strHex() 599 '0x20010658022acafe0200000000000001' 600 """ 601 602 if self.WantPrefixLen == None and wantprefixlen == None: 603 wantprefixlen = 0 604 605 x = hex(self.ip) 606 if x[-1] == 'L': 607 x = x[:-1] 608 return x.lower() + self._printPrefix(wantprefixlen)
609
610 - def strDec(self, wantprefixlen = None):
611 """Return a string representation in decimal format. 612 613 >>> print IP('127.0.0.1').strDec() 614 2130706433 615 >>> print IP('2001:0658:022a:cafe:0200::1').strDec() 616 42540616829182469433547762482097946625 617 """ 618 619 if self.WantPrefixLen == None and wantprefixlen == None: 620 wantprefixlen = 0 621 622 x = str(self.ip) 623 if x[-1] == 'L': 624 x = x[:-1] 625 return x + self._printPrefix(wantprefixlen)
626
627 - def iptype(self):
628 """Return a description of the IP type ('PRIVATE', 'RESERVERD', etc). 629 630 >>> print IP('127.0.0.1').iptype() 631 PRIVATE 632 >>> print IP('192.168.1.1').iptype() 633 PRIVATE 634 >>> print IP('195.185.1.2').iptype() 635 PUBLIC 636 >>> print IP('::1').iptype() 637 LOOPBACK 638 >>> print IP('2001:0658:022a:cafe:0200::1').iptype() 639 ASSIGNABLE RIPE 640 641 The type information for IPv6 is out of sync with reality. 642 """ 643 644 # this could be greatly improved 645 646 if self._ipversion == 4: 647 iprange = IPv4ranges 648 elif self._ipversion == 6: 649 iprange = IPv6ranges 650 else: 651 raise ValueError, "only IPv4 and IPv6 supported" 652 653 bits = self.strBin() 654 for i in range(len(bits), 0, -1): 655 if iprange.has_key(bits[:i]): 656 return iprange[bits[:i]] 657 return "unknown"
658 659
660 - def netmask(self):
661 """Return netmask as an integer. 662 663 >>> "%X" % IP('195.185.0.0/16').netmask().int() 664 'FFFF0000' 665 """ 666 667 # TODO: unify with prefixlenToNetmask? 668 if self._ipversion == 4: 669 locallen = 32 - self._prefixlen 670 elif self._ipversion == 6: 671 locallen = 128 - self._prefixlen 672 else: 673 raise ValueError, "only IPv4 and IPv6 supported" 674 675 return ((2L ** self._prefixlen) - 1) << locallen
676 677
678 - def strNetmask(self):
679 """Return netmask as an string. Mostly useful for IPv6. 680 681 >>> print IP('195.185.0.0/16').strNetmask() 682 255.255.0.0 683 >>> print IP('2001:0658:022a:cafe::0/64').strNetmask() 684 /64 685 """ 686 687 # TODO: unify with prefixlenToNetmask? 688 if self._ipversion == 4: 689 locallen = 32 - self._prefixlen 690 return intToIp(((2L ** self._prefixlen) - 1) << locallen, 4) 691 elif self._ipversion == 6: 692 locallen = 128 - self._prefixlen 693 return "/%d" % self._prefixlen 694 else: 695 raise ValueError, "only IPv4 and IPv6 supported"
696
697 - def len(self):
698 """Return the length of an subnet. 699 700 >>> print IP('195.185.1.0/28').len() 701 16 702 >>> print IP('195.185.1.0/24').len() 703 256 704 """ 705 706 if self._ipversion == 4: 707 locallen = 32 - self._prefixlen 708 elif self._ipversion == 6: 709 locallen = 128 - self._prefixlen 710 else: 711 raise ValueError, "only IPv4 and IPv6 supported" 712 713 return 2L ** locallen
714 715
716 - def __nonzero__(self):
717 """All IPy objects should evaluate to true in boolean context. 718 Ordinarily, they do, but if handling a default route expressed as 719 0.0.0.0/0, the __len__() of the object becomes 0, which is used 720 as the boolean value of the object. 721 """ 722 return 1
723 724
725 - def __len__(self):
726 """Return the length of an subnet. 727 728 Called to implement the built-in function len(). 729 It breaks with IPv6 Networks. Anybody knows how to fix this.""" 730 731 # Python < 2.2 has this silly restriction which breaks IPv6 732 # how about Python >= 2.2 ... ouch - it presists! 733 734 return int(self.len())
735 736
737 - def __getitem__(self, key):
738 """Called to implement evaluation of self[key]. 739 740 >>> ip=IP('127.0.0.0/30') 741 >>> for x in ip: 742 ... print repr(x) 743 ... 744 IP('127.0.0.0') 745 IP('127.0.0.1') 746 IP('127.0.0.2') 747 IP('127.0.0.3') 748 >>> ip[2] 749 IP('127.0.0.2') 750 >>> ip[-1] 751 IP('127.0.0.3') 752 """ 753 754 if type(key) != types.IntType and type(key) != types.LongType: 755 raise TypeError 756 if abs(key) >= self.len(): 757 raise IndexError 758 if key < 0: 759 key = self.len() - abs(key) 760 761 return self.ip + long(key)
762 763 764
765 - def __contains__(self, item):
766 """Called to implement membership test operators. 767 768 Should return true if item is in self, false otherwise. Item 769 can be other IP-objects, strings or ints. 770 771 >>> IP('195.185.1.1').strHex() 772 '0xc3b90101' 773 >>> 0xC3B90101L in IP('195.185.1.0/24') 774 1 775 >>> '127.0.0.1' in IP('127.0.0.0/24') 776 1 777 >>> IP('127.0.0.0/24') in IP('127.0.0.0/25') 778 0 779 """ 780 781 item = IP(item) 782 if item.ip >= self.ip and item.ip < self.ip + self.len() - item.len() + 1: 783 return 1 784 else: 785 return 0
786 787
788 - def overlaps(self, item):
789 """Check if two IP address ranges overlap. 790 791 Returns 0 if the two ranged don't overlap, 1 if the given 792 range overlaps at the end and -1 if it does at the beginning. 793 794 >>> IP('192.168.0.0/23').overlaps('192.168.1.0/24') 795 1 796 >>> IP('192.168.0.0/23').overlaps('192.168.1.255') 797 1 798 >>> IP('192.168.0.0/23').overlaps('192.168.2.0') 799 0 800 >>> IP('192.168.1.0/24').overlaps('192.168.0.0/23') 801 -1 802 """ 803 804 item = IP(item) 805 if item.ip >= self.ip and item.ip < self.ip + self.len(): 806 return 1 807 elif self.ip >= item.ip and self.ip < item.ip + item.len(): 808 return -1 809 else: 810 return 0
811 812
813 - def __str__(self):
814 """Dispatch to the prefered String Representation. 815 816 Used to implement str(IP).""" 817 818 return self.strFullsize()
819 820
821 - def __repr__(self):
822 """Print a representation of the Object. 823 824 Used to implement repr(IP). Returns a string which evaluates 825 to an identical Object (without the wnatprefixlen stuff - see 826 module docstring. 827 828 >>> print repr(IP('10.0.0.0/24')) 829 IP('10.0.0.0/24') 830 """ 831 832 return("IPint('%s')" % (self.strCompressed(1)))
833 834
835 - def __cmp__(self, other):
836 """Called by comparison operations. 837 838 Should return a negative integer if self < other, zero if self 839 == other, a positive integer if self > other. 840 841 Networks with different prefixlen are considered non-equal. 842 Networks with the same prefixlen and differing addresses are 843 considered non equal but are compared by thair base address 844 integer value to aid sorting of IP objects. 845 846 The Version of Objects is not put into consideration. 847 848 >>> IP('10.0.0.0/24') > IP('10.0.0.0') 849 1 850 >>> IP('10.0.0.0/24') < IP('10.0.0.0') 851 0 852 >>> IP('10.0.0.0/24') < IP('12.0.0.0/24') 853 1 854 >>> IP('10.0.0.0/24') > IP('12.0.0.0/24') 855 0 856 857 """ 858 859 # Im not really sure if this is "the right thing to do" 860 if self._prefixlen < other.prefixlen(): 861 return (other.prefixlen() - self._prefixlen) 862 elif self._prefixlen > other.prefixlen(): 863 864 # Fixed bySamuel Krempp <krempp@crans.ens-cachan.fr>: 865 866 # The bug is quite obvious really (as 99% bugs are once 867 # spotted, isn't it ? ;-) Because of precedence of 868 # multiplication by -1 over the substraction, prefixlen 869 # differences were causing the __cmp__ function to always 870 # return positive numbers, thus the function was failing 871 # the basic assumptions for a __cmp__ function. 872 873 # Namely we could have (a > b AND b > a), when the 874 # prefixlen of a and b are different. (eg let 875 # a=IP("1.0.0.0/24"); b=IP("2.0.0.0/16");) thus, anything 876 # could happen when launching a sort algorithm.. 877 # everything's in order with the trivial, attached patch. 878 879 return (self._prefixlen - other.prefixlen()) * -1 880 else: 881 if self.ip < other.ip: 882 return -1 883 elif self.ip > other.ip: 884 return 1 885 else: 886 return 0
887 888
889 - def __hash__(self):
890 """Called for the key object for dictionary operations, and by 891 the built-in function hash() Should return a 32-bit integer 892 usable as a hash value for dictionary operations. The only 893 required property is that objects which compare equal have the 894 same hash value 895 896 >>> IP('10.0.0.0/24').__hash__() 897 -167772185 898 """ 899 900 thehash = int(-1) 901 ip = self.ip 902 while ip > 0: 903 thehash = thehash ^ (ip & 0x7fffffff) 904 ip = ip >> 32 905 thehash = thehash ^ self._prefixlen 906 return int(thehash)
907 908
909 -class IP(IPint):
910 """Class for handling IP Addresses and Networks.""" 911
912 - def net(self):
913 """Return the base (first) address of a network as an IP object. 914 915 The same as IP[0]. 916 917 >>> IP('10.0.0.0/8').net() 918 IP('10.0.0.0') 919 """ 920 return IP(IPint.net(self))
921
922 - def broadcast(self):
923 """Return the broadcast (last) address of a network as an IP object. 924 925 The same as IP[-1]. 926 927 >>> IP('10.0.0.0/8').broadcast() 928 IP('10.255.255.255') 929 """ 930 return IP(IPint.broadcast(self))
931
932 - def netmask(self):
933 """Return netmask as an IP object. 934 935 >>> IP('10.0.0.0/8').netmask() 936 IP('255.0.0.0') 937 """ 938 return IP(IPint.netmask(self))
939 940
941 - def reverseNames(self):
942 """Return a list with values forming the reverse lookup. 943 944 >>> IP('213.221.113.87/32').reverseNames() 945 ['87.113.221.213.in-addr.arpa.'] 946 >>> IP('213.221.112.224/30').reverseNames() 947 ['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.'] 948 >>> IP('127.0.0.0/24').reverseNames() 949 ['0.0.127.in-addr.arpa.'] 950 >>> IP('127.0.0.0/23').reverseNames() 951 ['0.0.127.in-addr.arpa.', '1.0.127.in-addr.arpa.'] 952 >>> IP('127.0.0.0/16').reverseNames() 953 ['0.127.in-addr.arpa.'] 954 >>> IP('127.0.0.0/15').reverseNames() 955 ['0.127.in-addr.arpa.', '1.127.in-addr.arpa.'] 956 >>> IP('128.0.0.0/8').reverseNames() 957 ['128.in-addr.arpa.'] 958 >>> IP('128.0.0.0/7').reverseNames() 959 ['128.in-addr.arpa.', '129.in-addr.arpa.'] 960 961 """ 962 963 if self._ipversion == 4: 964 ret =[] 965 # TODO: Refactor. Add support for IPint objects 966 if self.len() < 2**8: 967 for x in self: 968 ret.append(x.reverseName()) 969 elif self.len() < 2**16L: 970 for i in range(0, self.len(), 2**8): 971 ret.append(self[i].reverseName()[2:]) 972 elif self.len() < 2**24L: 973 for i in range(0, self.len(), 2**16): 974 ret.append(self[i].reverseName()[4:]) 975 else: 976 for i in range(0, self.len(), 2**24): 977 ret.append(self[i].reverseName()[6:]) 978 return ret 979 elif self._ipversion == 6: 980 s = hex(self.ip)[2:].lower() 981 if s[-1] == 'l': 982 s = s[:-1] 983 if self._prefixlen % 4 != 0: 984 raise NotImplementedError, "can't create IPv6 reverse names at sub nibble level" 985 s = list(s) 986 s.reverse() 987 s = '.'.join(s) 988 first_nibble_index = int(32 - (self._prefixlen / 4)) * 2 989 return ["%s.ip6.int." % s[first_nibble_index:]] 990 else: 991 raise ValueError, "only IPv4 and IPv6 supported"
992 993 994
995 - def reverseName(self):
996 """Return the value for reverse lookup/PTR records as RfC 2317 look alike. 997 998 RfC 2317 is an ugly hack which only works for sub-/24 e.g. not 999 for /23. Do not use it. Better set up a Zone for every 1000 address. See reverseName for a way to arcive that. 1001 1002 >>> print IP('195.185.1.1').reverseName() 1003 1.1.185.195.in-addr.arpa. 1004 >>> print IP('195.185.1.0/28').reverseName() 1005 0-15.1.185.195.in-addr.arpa. 1006 """ 1007 1008 if self._ipversion == 4: 1009 s = self.strFullsize(0) 1010 s = s.split('.') 1011 s.reverse() 1012 first_byte_index = int(4 - (self._prefixlen / 8)) 1013 if self._prefixlen % 8 != 0: 1014 nibblepart = "%s-%s" % (s[3-(self._prefixlen / 8)], intToIp(self.ip + self.len() - 1, 4).split('.')[-1]) 1015 if nibblepart[-1] == 'l': 1016 nibblepart = nibblepart[:-1] 1017 nibblepart += '.' 1018 else: 1019 nibblepart = "" 1020 1021 s = '.'.join(s[first_byte_index:]) 1022 return "%s%s.in-addr.arpa." % (nibblepart, s) 1023 1024 elif self._ipversion == 6: 1025 s = hex(self.ip)[2:].lower() 1026 if s[-1] == 'l': 1027 s = s[:-1] 1028 if self._prefixlen % 4 != 0: 1029 nibblepart = "%s-%s" % (s[self._prefixlen:], hex(self.ip + self.len() - 1)[2:].lower()) 1030 if nibblepart[-1] == 'l': 1031 nibblepart = nibblepart[:-1] 1032 nibblepart += '.' 1033 else: 1034 nibblepart = "" 1035 s = list(s) 1036 s.reverse() 1037 s = '.'.join(s) 1038 first_nibble_index = int(32 - (self._prefixlen / 4)) * 2 1039 return "%s%s.ip6.int." % (nibblepart, s[first_nibble_index:]) 1040 else: 1041 raise ValueError, "only IPv4 and IPv6 supported"
1042
1043 - def __getitem__(self, key):
1044 """Called to implement evaluation of self[key]. 1045 1046 >>> ip=IP('127.0.0.0/30') 1047 >>> for x in ip: 1048 ... print str(x) 1049 ... 1050 127.0.0.0 1051 127.0.0.1 1052 127.0.0.2 1053 127.0.0.3 1054 >>> print str(ip[2]) 1055 127.0.0.2 1056 >>> print str(ip[-1]) 1057 127.0.0.3 1058 """ 1059 return IP(IPint.__getitem__(self, key))
1060
1061 - def __repr__(self):
1062 """Print a representation of the Object. 1063 1064 >>> IP('10.0.0.0/8') 1065 IP('10.0.0.0/8') 1066 """ 1067 1068 return("IP('%s')" % (self.strCompressed(1)))
1069
1070 - def __add__(self, other):
1071 """Emulate numeric objects through network aggregation""" 1072 if self.prefixlen() != other.prefixlen(): 1073 raise ValueError, "Only networks with the same prefixlen can be added." 1074 if self.prefixlen < 1: 1075 raise ValueError, "Networks with a prefixlen longer than /1 can't be added." 1076 if self.version() != other.version(): 1077 raise ValueError, "Only networks with the same IP version can be added." 1078 if self > other: 1079 # fixed by Skinny Puppy <skin_pup-IPy@happypoo.com> 1080 return other.__add__(self) 1081 else: 1082 ret = IP(self.int()) 1083 ret._prefixlen = self.prefixlen() - 1 1084 return ret
1085
1086 -def parseAddress(ipstr):
1087 """Parse a string and return the corrospondending IPaddress and the a guess of the IP version. 1088 1089 Following Forms ar recorgnized: 1090 0x0123456789abcdef # IPv4 if <= 0xffffffff else IPv6 1091 123.123.123.123 # IPv4 1092 123.123 # 0-padded IPv4 1093 1080:0000:0000:0000:0008:0800:200C:417A 1094 1080:0:0:0:8:800:200C:417A 1095 1080:0::8:800:200C:417A 1096 ::1 1097 :: 1098 0:0:0:0:0:FFFF:129.144.52.38 1099 ::13.1.68.3 1100 ::FFFF:129.144.52.38 1101 """ 1102 1103 # TODO: refactor me! 1104 if ipstr.startswith('0x'): 1105 ret = long(ipstr[2:], 16) 1106 if ret > 0xffffffffffffffffffffffffffffffffL: 1107 raise ValueError, "%r: IP Address can't be bigger than 2^128" % (ipstr) 1108 if ret < 0x100000000L: 1109 return (ret, 4) 1110 else: 1111 return (ret, 6) 1112 1113 if ipstr.find(':') != -1: 1114 # assume IPv6 1115 if ipstr.find(':::') != -1: 1116 raise ValueError, "%r: IPv6 Address can't contain ':::'" % (ipstr) 1117 hextets = ipstr.split(':') 1118 if ipstr.find('.') != -1: 1119 # this might be a mixed address like '0:0:0:0:0:0:13.1.68.3' 1120 (v4, foo) = parseAddress(hextets[-1]) 1121 assert foo == 4 1122 del(hextets[-1]) 1123 hextets.append(hex(v4 >> 16)[2:-1]) 1124 hextets.append(hex(v4 & 0xffff)[2:-1]) 1125 if len(hextets) > 8: 1126 raise ValueError, "%r: IPv6 Address with more than 8 hexletts" % (ipstr) 1127 if len(hextets) < 8: 1128 if '' not in hextets: 1129 raise ValueError, "%r IPv6 Address with less than 8 hexletts and without '::'" % (ipstr) 1130 # catch :: at the beginning or end 1131 if hextets.index('') < len(hextets) - 1 and hextets[hextets.index('')+1] == '': 1132 hextets.remove('') 1133 # catch '::' 1134 if hextets.index('') < len(hextets) - 1 and hextets[hextets.index('')+1] == '': 1135 hextets.remove('') 1136 1137 for foo in range(9-len(hextets)): 1138 hextets.insert(hextets.index(''), '0') 1139 hextets.remove('') 1140 if '' in hextets: 1141 raise ValueError, "%r IPv6 Address may contain '::' only once" % (ipstr) 1142 if '' in hextets: 1143 raise ValueError, "%r IPv6 Address may contain '::' only if it has less than 8 hextets" % (ipstr) 1144 num = '' 1145 for x in hextets: 1146 if len(x) < 4: 1147 x = ((4 - len(x)) * '0') + x 1148 if int(x, 16) < 0 or int(x, 16) > 0xffff: 1149 raise ValueError, "%r: single hextet must be 0 <= hextet <= 0xffff which isn't true for %s" % (ipstr, x) 1150 num += x 1151 return (long(num, 16), 6) 1152 1153 elif len(ipstr) == 32: 1154 # assume IPv6 in pure hexadecimal notation 1155 return (long(ipstr, 16), 6) 1156 1157 elif ipstr.find('.') != -1 or (len(ipstr) < 4 and int(ipstr) < 256): 1158 # assume IPv4 ('127' gets interpreted as '127.0.0.0') 1159 bytes = ipstr.split('.') 1160 if len(bytes) > 4: 1161 raise ValueError, "IPv4 Address with more than 4 bytes" 1162 bytes += ['0'] * (4 - len(bytes)) 1163 bytes = [long(x) for x in bytes] 1164 for x in bytes: 1165 if x > 255 or x < 0: 1166 raise ValueError, "%r: single byte must be 0 <= byte < 256" % (ipstr) 1167 return ((bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3], 4) 1168 1169 else: 1170 # we try to interprete it as a decimal digit - 1171 # this ony works for numbers > 255 ... others 1172 # will be interpreted as IPv4 first byte 1173 ret = long(ipstr) 1174 if ret > 0xffffffffffffffffffffffffffffffffL: 1175 raise ValueError, "IP Address cant be bigger than 2^128" 1176 if ret <= 0xffffffffL: 1177 return (ret, 4) 1178 else: 1179 return (ret, 6)
1180 1181
1182 -def intToIp(ip, version):
1183 """Transform an integer string into an IP address.""" 1184 1185 # just to be sure and hoping for Python 2.22 1186 ip = long(ip) 1187 1188 if ip < 0: 1189 raise ValueError, "IPs can't be negative: %d" % (ip) 1190 1191 ret = '' 1192 if version == 4: 1193 if ip > 0xffffffffL: 1194 raise ValueError, "IPv4 Addresses can't be larger than 0xffffffff: %s" % (hex(ip)) 1195 for l in range(4): 1196 ret = str(ip & 0xffL) + '.' + ret 1197 ip = ip >> 8; 1198 ret = ret[:-1] 1199 elif version == 6: 1200 if ip > 0xffffffffffffffffffffffffffffffffL: 1201 raise ValueError, "IPv6 Addresses can't be larger than 0xffffffffffffffffffffffffffffffff: %s" % (hex(ip)) 1202 l = '0' * 32 + hex(ip)[2:-1] 1203 for x in range(1,33): 1204 ret = l[-x] + ret 1205 if x % 4 == 0: 1206 ret = ':' + ret 1207 ret = ret[1:] 1208 else: 1209 raise ValueError, "only IPv4 and IPv6 supported" 1210 1211 return ret;
1212
1213 -def _ipVersionToLen(version):
1214 """Return number of bits in address for a certain IP version. 1215 1216 >>> _ipVersionToLen(4) 1217 32 1218 >>> _ipVersionToLen(6) 1219 128 1220 >>> _ipVersionToLen(5) 1221 Traceback (most recent call last): 1222 File "<stdin>", line 1, in ? 1223 File "IPy.py", line 1076, in _ipVersionToLen 1224 raise ValueError, "only IPv4 and IPv6 supported" 1225 ValueError: only IPv4 and IPv6 supported 1226 """ 1227 1228 if version == 4: 1229 return 32 1230 elif version == 6: 1231 return 128 1232 else: 1233 raise ValueError, "only IPv4 and IPv6 supported"
1234 1235
1236 -def _countFollowingZeros(l):
1237 """Return Nr. of elements containing 0 at the beginning th the list.""" 1238 if len(l) == 0: 1239 return 0 1240 elif l[0] != 0: 1241 return 0 1242 else: 1243 return 1 + _countFollowingZeros(l[1:])
1244 1245 1246 _BitTable = {'0': '0000', '1': '0001', '2': '0010', '3': '0011', 1247 '4': '0100', '5': '0101', '6': '0110', '7': '0111', 1248 '8': '1000', '9': '1001', 'a': '1010', 'b': '1011', 1249 'c': '1100', 'd': '1101', 'e': '1110', 'f': '1111'} 1250
1251 -def _intToBin(val):
1252 """Return the binary representation of an integer as string.""" 1253 1254 if val < 0: 1255 raise ValueError, "Only positive Values allowed" 1256 s = hex(val).lower() 1257 ret = '' 1258 if s[-1] == 'l': 1259 s = s[:-1] 1260 for x in s[2:]: 1261 if __debug__: 1262 if not _BitTable.has_key(x): 1263 raise AssertionError, "hex() returned strange result" 1264 ret += _BitTable[x] 1265 # remove leading zeros 1266 while ret[0] == '0' and len(ret) > 1: 1267 ret = ret[1:] 1268 return ret
1269
1270 -def _count1Bits(num):
1271 """Find the highest bit set to 1 in an integer.""" 1272 ret = 0 1273 while num > 0: 1274 num = num >> 1 1275 ret += 1 1276 return ret
1277
1278 -def _count0Bits(num):
1279 """Find the highest bit set to 0 in an integer.""" 1280 1281 # this could be so easy if _count1Bits(~long(num)) would work as excepted 1282 num = long(num) 1283 if num < 0: 1284 raise ValueError, "Only positive Numbers please: %s" % (num) 1285 ret = 0 1286 while num > 0: 1287 if num & 1 == 1: 1288 break 1289 num = num >> 1 1290 ret += 1 1291 return ret
1292 1293
1294 -def _checkPrefix(ip, prefixlen, version):
1295 """Check the validity of a prefix 1296 1297 Checks if the variant part of a prefix only has 0s, and the length is 1298 correct. 1299 1300 >>> _checkPrefix(0x7f000000L, 24, 4) 1301 1 1302 >>> _checkPrefix(0x7f000001L, 24, 4) 1303 0 1304 >>> repr(_checkPrefix(0x7f000001L, -1, 4)) 1305 'None' 1306 >>> repr(_checkPrefix(0x7f000001L, 33, 4)) 1307 'None' 1308 """ 1309 1310 # TODO: unify this v4/v6/invalid code in a function 1311 bits = _ipVersionToLen(version) 1312 1313 if prefixlen < 0 or prefixlen > bits: 1314 return None 1315 1316 if ip == 0: 1317 zbits = bits + 1 1318 else: 1319 zbits = _count0Bits(ip) 1320 if zbits < bits - prefixlen: 1321 return 0 1322 else: 1323 return 1
1324 1325
1326 -def _checkNetmask(netmask, masklen):
1327 """Checks if a netmask is expressable as e prefixlen.""" 1328 1329 num = long(netmask) 1330 bits = masklen 1331 1332 # remove zero bits at the end 1333 while (num & 1) == 0 and bits != 0: 1334 num = num >> 1 1335 bits -= 1 1336 if bits == 0: 1337 break 1338 # now check if the rest consists only of ones 1339 while bits > 0: 1340 if (num & 1) == 0: 1341 raise ValueError, "Netmask %s can't be expressed as an prefix." % (hex(netmask)) 1342 num = num >> 1 1343 bits -= 1
1344 1345
1346 -def _checkNetaddrWorksWithPrefixlen(net, prefixlen, version):
1347 """Check if a base addess of e network is compatible with a prefixlen""" 1348 global check_addr_prefixlen 1349 if check_addr_prefixlen: 1350 if net & _prefixlenToNetmask(prefixlen, version) == net: 1351 return 1 1352 else: 1353 return 0 1354 else: 1355 if prefixlen < 0: 1356 return 0 1357 if _ipVersionToLen(version) < prefixlen: 1358 return 0 1359 return 1
1360 1361
1362 -def _netmaskToPrefixlen(netmask):
1363 """Convert an Integer reprsenting a Netmask to an prefixlen. 1364 1365 E.g. 0xffffff00 (255.255.255.0) returns 24 1366 """ 1367 1368 netlen = _count0Bits(netmask) 1369 masklen = _count1Bits(netmask) 1370 _checkNetmask(netmask, masklen) 1371 return masklen - netlen
1372 1373
1374 -def _prefixlenToNetmask(prefixlen, version):
1375 """Return a mask of n bits as a long integer. 1376 1377 From 'IP address conversion functions with the builtin socket module' by Alex Martelli 1378 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66517 1379 """ 1380 if prefixlen == 0: 1381 return 0 1382 elif prefixlen < 0: 1383 raise ValueError, "Prefixlen must be > 0" 1384 return ((2L<<prefixlen-1)-1) << (_ipVersionToLen(version) - prefixlen)
1385 1386
1387 -def _test():
1388 import doctest, IPy 1389 return doctest.testmod(IPy)
1390 1391 if __name__ == "__main__": 1392 _test() 1393 1394 t = [0xf0, 0xf00, 0xff00, 0xffff00, 0xffffff00L] 1395 o = [] 1396 for x in t: 1397 pass 1398 x = 0L 1399