Package netaddr :: Module address
[frames] | no frames]

Source Code for Module netaddr.address

   1  #!/usr/bin/env python 
   2  #----------------------------------------------------------------------------- 
   3  #   Copyright (c) 2008, David P. D. Moss. All rights reserved. 
   4  # 
   5  #   Released under the BSD license. See the LICENSE file for details. 
   6  #----------------------------------------------------------------------------- 
   7  """ 
   8  network address classes (IP, EUI) and associated aggregate classes (CIDR, 
   9  Wildcard and IPRange). 
  10  """ 
  11  import math as _math 
  12  import socket as _socket 
  13   
  14  from netaddr import AddrFormatError, AddrConversionError, AT_UNSPEC, \ 
  15      AT_INET, AT_INET6, AT_LINK, AT_EUI64, AT_NAMES 
  16   
  17  from netaddr.strategy import ST_IPV4, ST_IPV6, ST_EUI48, ST_EUI64, \ 
  18      AddrStrategy 
  19   
  20  from netaddr.eui import OUI, IAB 
  21   
  22  #: Address type to strategy object lookup dict. 
  23  AT_STRATEGIES = { 
  24      AT_UNSPEC   : None, 
  25      AT_INET     : ST_IPV4, 
  26      AT_INET6    : ST_IPV6, 
  27      AT_LINK     : ST_EUI48, 
  28      AT_EUI64    : ST_EUI64, 
  29  } 
  30   
  31  #----------------------------------------------------------------------------- 
32 -class AddrTypeDescriptor(object):
33 """ 34 A descriptor that checks addr_type property assignments for validity and 35 also keeps the strategy property in sync with any changes made. 36 """
37 - def __init__(self, addr_types):
38 """ 39 Constructor. 40 41 @param addr_types: a list of address types constants that are 42 acceptable for assignment to the addr_type property. 43 """ 44 self.addr_types = addr_types
45
46 - def __set__(self, instance, value):
47 if value not in self.addr_types: 48 raise ValueError('addr_type %r is invalid for objects of ' \ 49 'the %s() class!' % (value, instance.__class__.__name__)) 50 instance.__dict__['addr_type'] = value 51 instance.__dict__['strategy'] = AT_STRATEGIES[value]
52 53 #-----------------------------------------------------------------------------
54 -class AddrValueDescriptor(object):
55 """ 56 A descriptor that checks assignments to the named parameter passed to the 57 constructor. It accepts network addresses in either string format or as 58 network byte order integers. String based addresses are converted to their 59 integer equivalents before assignment to the named parameter. Also ensures 60 that addr_type and strategy are set correctly when parsing string based 61 addresses. 62 """
63 - def __init__(self, name):
64 """ 65 Descriptor constructor. 66 67 @param name: the name of attribute which will be assigned the value. 68 """ 69 self.name = name
70
71 - def __set__(self, instance, value):
72 if issubclass(value.__class__, Addr): 73 if instance.strategy is None: 74 instance.strategy = value.strategy 75 value = int(value) 76 else: 77 if instance.addr_type == AT_UNSPEC: 78 # Select a strategy object for this address. 79 for strategy in instance.__class__.STRATEGIES: 80 if strategy.valid_str(value): 81 instance.strategy = strategy 82 break 83 84 # Make sure we picked up a strategy object. 85 if instance.strategy is None: 86 raise AddrFormatError('%r is not a supported address ' \ 87 'format!' % value) 88 89 if isinstance(value, (str, unicode)): 90 # Calculate the integer value for this address. 91 value = instance.strategy.str_to_int(value) 92 elif isinstance(value, (int, long)): 93 if not instance.strategy.valid_int(value): 94 raise OverflowError('value %r cannot be represented ' \ 95 'in %d bit(s)!' % (value, instance.strategy.width)) 96 else: 97 raise TypeError('%r is an unsupported type!' % value) 98 99 instance.__dict__[self.name] = value
100 101 #-----------------------------------------------------------------------------
102 -class StrategyDescriptor(object):
103 """ 104 A descriptor that checks strategy property assignments for validity and 105 also keeps the addr_type property in sync with any changes made. 106 """
107 - def __init__(self, strategies):
108 """ 109 Constructor. 110 111 @param strategies: a list of strategy objects that are acceptable for 112 assignment to the strategy property. 113 """ 114 self.strategies = strategies
115
116 - def __set__(self, instance, value):
117 if value not in self.strategies: 118 raise Exception('%r is not a supported strategy!' % value) 119 instance.__dict__['strategy'] = value 120 instance.__dict__['addr_type'] = instance.strategy.addr_type
121 122 #-----------------------------------------------------------------------------
123 -class PrefixLenDescriptor(object):
124 """ 125 A descriptor that checks prefixlen property assignments for validity based 126 on address type. Also accepts netmasks and hostmasks which can easily be 127 converted to the equivalent prefixlen integer. 128 """
129 - def __init__(self, class_id=None):
130 """ 131 Constructor. 132 133 @param class_id: (optional) the name of the class that uses this 134 descriptor. 135 """ 136 self.class_id = class_id
137
138 - def __set__(self, instance, value):
139 try: 140 # Basic integer subnet prefix. 141 prefixlen = int(value) 142 except ValueError: 143 # Convert possible subnet mask to integer subnet prefix. 144 ip = IP(value) 145 146 if instance.addr_type != ip.addr_type: 147 raise ValueError('address and netmask type mismatch!') 148 149 if ip.is_netmask(): 150 # prefixlen is a netmask address. 151 prefixlen = ip.netmask_bits() 152 elif ip.is_hostmask(): 153 # prefixlen is an ACL (hostmask) address. 154 netmask = IP(ip.strategy.max_int ^ int(ip), ip.addr_type) 155 prefixlen = netmask.netmask_bits() 156 else: 157 raise ValueError('%s is not a valid netmask/hostmask!' % ip) 158 159 # Validate subnet prefix. 160 if not 0 <= prefixlen <= instance.strategy.width: 161 raise ValueError('%d is an invalid prefix for an %s CIDR!' \ 162 % (prefixlen, AT_NAMES[instance.addr_type])) 163 164 # Make sure instance is not a subnet mask trying to set a prefix! 165 if isinstance(instance, IP): 166 if instance.is_netmask() and instance.addr_type == AT_INET \ 167 and prefixlen != 32: 168 raise ValueError('IPv4 netmasks must have a prefix of /32!') 169 170 instance.__dict__['prefixlen'] = prefixlen 171 172 # Don't run this on a CIDR that is initialising itself. 173 if self.class_id == 'CIDR' and 'first' in instance.__dict__: 174 first = instance.__dict__['first'] 175 strategy = instance.__dict__['strategy'] 176 hostmask = (1 << (strategy.width - prefixlen)) - 1 177 instance.__dict__['first'] = (first | hostmask) - hostmask 178 instance.__dict__['last'] = first | hostmask
179 180 #-----------------------------------------------------------------------------
181 -class FormatDescriptor(object):
182 """ 183 A descriptor that checks formatter property assignments for validity. 184 """
185 - def __init__(self, default):
186 """ 187 Constructor. 188 189 @param default: the default callable to use if the formatter 190 property is None. 191 """ 192 self.default = default
193
194 - def __set__(self, instance, value):
195 if callable(value) and \ 196 value in (str, int, Addr, IP, long, unicode, hex): 197 pass 198 elif value is None: 199 # Use default. 200 value = self.default 201 else: 202 raise TypeError("unsupported formatter callable: %r!" % value) 203 204 instance.__dict__['fmt'] = value
205 206 #-----------------------------------------------------------------------------
207 -class Addr(object):
208 """ 209 The base class containing common functionality for all subclasses 210 representing various network address types. 211 212 It is a fully functioning class (as opposed to a virtual class) with a 213 heuristic constructor that detects the type of address via the first 214 argument if it is a string and sets itself up accordingly. If the first 215 argument is an integer, then a constant must be provided via the second 216 argument indicating the address type explicitly. 217 218 Objects of this class behave differently dependent upon the type of address 219 they represent. 220 """ 221 STRATEGIES = (ST_IPV4, ST_IPV6, ST_EUI48, ST_EUI64) 222 ADDR_TYPES = (AT_UNSPEC, AT_INET, AT_INET6, AT_LINK, AT_EUI64) 223 224 # Descriptor registrations. 225 value = AddrValueDescriptor('value') 226 strategy = StrategyDescriptor(STRATEGIES) 227 addr_type = AddrTypeDescriptor(ADDR_TYPES) 228
229 - def __init__(self, addr, addr_type=AT_UNSPEC):
230 """ 231 Constructor. 232 233 @param addr: the string form of a network address, or a network byte 234 order integer within the supported range for the address type. 235 236 @param addr_type: (optional) the network address type. If addr is an 237 integer, this argument becomes mandatory. 238 """ 239 self.addr_type = addr_type 240 self.value = addr
241
242 - def __hash__(self):
243 """@return: hash of this address suitable for dict keys, sets etc""" 244 return hash((self.value, self.addr_type))
245
246 - def __int__(self):
247 """@return: value of this address as a network byte order integer""" 248 return self.value
249
250 - def __long__(self):
251 """@return: value of this address as a network byte order integer""" 252 return self.value
253
254 - def __str__(self):
255 """@return: common string representation of this address""" 256 return self.strategy.int_to_str(self.value)
257
258 - def __repr__(self):
259 """@return: executable Python string to recreate equivalent object""" 260 return "%s(%r)" % (self.__class__.__name__, str(self))
261
262 - def bits(self):
263 """@return: human-readable binary digit string of this address""" 264 return self.strategy.int_to_bits(self.value)
265
266 - def packed(self):
267 """@return: binary packed string of this address""" 268 return self.strategy.int_to_packed(self.value)
269
270 - def bin(self):
271 """ 272 @return: standard Python binary representation of this address. A back 273 port of the format provided by the builtin bin() type available in 274 Python 2.6.x and higher.""" 275 return self.strategy.int_to_bin(self.value)
276
277 - def __len__(self):
278 """@return: The size (width) of this address in bits""" 279 return self.strategy.width
280
281 - def __iter__(self):
282 """@return: An iterator over individual words in this address""" 283 return iter(self.strategy.int_to_words(self.value))
284
285 - def __getitem__(self, index):
286 """ 287 @return: The integer value of the word referenced by index (both 288 positive and negative). Raises C{IndexError} if index is out 289 of bounds. Also supports Python list slices for accessing 290 word groups. 291 """ 292 if isinstance(index, (int, long)): 293 # Indexing, including negative indexing goodness. 294 num_words = self.strategy.num_words 295 if not (-num_words) <= index <= (num_words - 1): 296 raise IndexError('index out range for address type!') 297 return self.strategy.int_to_words(self.value)[index] 298 elif isinstance(index, slice): 299 # Slicing baby! 300 words = self.strategy.int_to_words(self.value) 301 return [words[i] for i in range(*index.indices(len(words)))] 302 else: 303 raise TypeError('unsupported type %r!' % index)
304
305 - def __setitem__(self, index, value):
306 """Sets the value of the word referenced by index in this address""" 307 if isinstance(index, slice): 308 # TODO - settable slices. 309 raise NotImplementedError('settable slices not yet supported!') 310 311 if not isinstance(index, (int, long)): 312 raise TypeError('index not an integer!') 313 314 if not 0 <= index <= (self.strategy.num_words - 1): 315 raise IndexError('index %d outside address type boundary!' % index) 316 317 if not isinstance(value, (int, long)): 318 raise TypeError('value not an integer!') 319 320 if not 0 <= value <= self.strategy.max_word: 321 raise IndexError('value %d outside word size maximum of %d bits!' 322 % (value, self.strategy.word_size)) 323 324 words = list(self.strategy.int_to_words(self.value)) 325 words[index] = value 326 self.value = self.strategy.words_to_int(words)
327
328 - def __hex__(self):
329 """@return: network byte order hexadecimal string of this address""" 330 return hex(self.value).rstrip('L').lower()
331
332 - def __iadd__(self, num):
333 """ 334 Increment value of network address by specified amount. Behaves like 335 an unsigned integer, rolling over to zero when it reaches the maximum 336 value threshold. 337 338 @param num: size of increment 339 """ 340 try: 341 new_value = self.value + num 342 if new_value > self.strategy.max_int: 343 self.value = new_value - (self.strategy.max_int + 1) 344 else: 345 self.value = new_value 346 except TypeError: 347 raise TypeError('Increment value must be an integer!') 348 return self
349
350 - def __isub__(self, num):
351 """ 352 Decrement value of network address by specified amount. Behaves like 353 an unsigned integer, rolling over to maximum value when it goes below 354 zero. 355 356 @param num: size of decrement 357 """ 358 try: 359 new_value = self.value - num 360 if new_value < 0: 361 self.value = new_value + (self.strategy.max_int + 1) 362 else: 363 self.value = new_value 364 except TypeError: 365 raise TypeError('Decrement value must be an integer!') 366 return self
367
368 - def __add__(self, other):
369 """ 370 @param other: an integer or int-like object. 371 372 @return: A new (potentially larger) Addr class/subclass instance. 373 """ 374 return self.__class__(self.value + int(other), self.addr_type)
375
376 - def __sub__(self, other):
377 """ 378 @param other: an integer or int-like object. 379 380 @return: A new (potentially smaller) Addr class/subclass instance. 381 """ 382 return self.__class__(self.value - int(other), self.addr_type)
383
384 - def __eq__(self, other):
385 """ 386 @return: C{True} if this address is numerically the same as other, 387 C{False} otherwise. 388 """ 389 try: 390 return (self.addr_type, self.value) == (other.addr_type, other.value) 391 except AttributeError: 392 return False
393
394 - def __ne__(self, other):
395 """ 396 @return: C{True} if this address is not numerically the same other, 397 C{False} otherwise. 398 """ 399 try: 400 return (self.addr_type, self.value) != (other.addr_type, other.value) 401 except AttributeError: 402 return False
403
404 - def __lt__(self, other):
405 """ 406 @return: C{True} if this address is numerically lower in value than 407 other, C{False} otherwise. 408 """ 409 try: 410 return (self.addr_type, self.value) < (other.addr_type, other.value) 411 except AttributeError: 412 return False
413
414 - def __le__(self, other):
415 """ 416 @return: C{True} if this address is numerically lower or equal in 417 value to other, C{False} otherwise. 418 """ 419 try: 420 return (self.addr_type, self.value) <= (other.addr_type, other.value) 421 except AttributeError: 422 return False
423
424 - def __gt__(self, other):
425 """ 426 @return: C{True} if this address is numerically greater in value than 427 other, C{False} otherwise. 428 """ 429 try: 430 return (self.addr_type, self.value) > (other.addr_type, other.value) 431 except AttributeError: 432 return False
433
434 - def __ge__(self, other):
435 """ 436 @return: C{True} if this address is numerically greater or equal in 437 value to other, C{False} otherwise. 438 """ 439 try: 440 return (self.addr_type, self.value) >= (other.addr_type, other.value) 441 except AttributeError: 442 return False
443 - def __or__(self, other):
444 """ 445 @param other: an integer or int-like object. 446 447 @return: bitwise OR (x | y) of self.value with other.value. 448 """ 449 return self.__class__(self.value | other.value, self.addr_type)
450
451 - def __and__(self, other):
452 """ 453 @param other: an integer or int-like object. 454 455 @return: bitwise AND (x & y) of self.value with other.value. 456 """ 457 return self.__class__(self.value | other.value, self.addr_type)
458
459 - def __xor__(self, other):
460 """ 461 @param other: an integer or int-like object. 462 463 @return: bitwise exclusive OR (x ^ y) of self.value with other.value. 464 """ 465 return self.__class__(self.value ^ other.value, self.addr_type)
466
467 - def __lshift__(self, numbits):
468 """ 469 @param numbits: size of shift (in bits). 470 471 @return: integer value of this IP address shifted left by x bits. 472 """ 473 return self.__class__(self.value << numbits, self.addr_type)
474
475 - def __rshift__(self, numbits):
476 """ 477 @param numbits: size of shift (in bits). 478 479 @return: integer value of this IP address right shifted by x bits. 480 """ 481 return self.__class__(self.value >> numbits, self.addr_type)
482
483 - def __invert__(self, other):
484 """ 485 @param other: an integer or int-like object. 486 487 @return: inversion (~x) of self.value. 488 """ 489 return self.__class__(~self.value)
490 491 #-----------------------------------------------------------------------------
492 -class EUI(Addr):
493 """ 494 Represents an IEEE EUI (Extended Unique Identifier) indentifier. 495 496 Input parser is flexible, supporting EUI-48 (including the many Media 497 Access Control variants) and EUI-64. 498 """ 499 STRATEGIES = (ST_EUI48, ST_EUI64) 500 ADDR_TYPES = (AT_UNSPEC, AT_LINK, AT_EUI64) 501 502 # Descriptor registrations. 503 strategy = StrategyDescriptor(STRATEGIES) 504 addr_type = AddrTypeDescriptor(ADDR_TYPES) 505
506 - def __init__(self, addr, addr_type=AT_UNSPEC):
507 """ 508 Constructor. 509 510 @param addr: an EUI-48 (MAC) or EUI-64 address string or a network 511 byte order integer. 512 513 @param addr_type: (optional) the specific EUI address type (C{AT_LINK} 514 or C{AT_EUI64}). If addr is an integer, this argument is mandatory. 515 """ 516 super(EUI, self).__init__(addr, addr_type)
517
518 - def oui(self, fmt=OUI):
519 """ 520 @param fmt: callable used on return values. Default: L{OUI} object. 521 Also Supports str(), unicode(), int() and long(). 522 523 @return: The OUI (Organisationally Unique Identifier) for this EUI. 524 """ 525 if callable(fmt) and fmt in (OUI, int, long): 526 return fmt(self.value >> 24) 527 elif callable(fmt) and fmt in (str, unicode, None): 528 return '-'.join(["%02x" % i for i in self[0:3]]).upper() 529 else: 530 raise TypeError("unsupported formatter callable: %r!" % fmt)
531
532 - def ei(self):
533 """@return: The EI (Extension Identifier) for this EUI""" 534 if self.strategy == ST_EUI48: 535 return '-'.join(["%02x" % i for i in self[3:6]]).upper() 536 elif self.strategy == ST_EUI64: 537 return '-'.join(["%02x" % i for i in self[3:8]]).upper()
538
539 - def isiab(self):
540 """@return: True if this EUI is an IAB address, False otherwise""" 541 return 0x50c2000 <= (self.value >> 12) <= 0x50c2fff
542
543 - def iab(self, fmt=IAB):
544 """ 545 @param fmt: callable used on return values. Default: L{IAB} object. 546 Also Supports str(), unicode(), int() and long(). 547 548 @return: If isiab() is True, the IAB (Individual Address Block) 549 is returned, None otherwise. 550 """ 551 if self.isiab(): 552 if callable(fmt) and fmt in (IAB, int, long): 553 return fmt(self.value >> 12) 554 elif callable(fmt) and fmt in (str, unicode, None): 555 usermask = (1 << (self.strategy.width - 36)) - 1 556 last_eui = self.value | usermask 557 first_eui = last_eui - usermask 558 iab_words = self.strategy.int_to_words(first_eui) 559 return '-'.join(["%02x" % i for i in iab_words]).upper() 560 else: 561 raise TypeError("unsupported formatter callable: %r!" % fmt)
562
563 - def eui64(self):
564 """ 565 @return: The value of this EUI object as a new 64-bit EUI object. 566 - If this object represents an EUI-48 it is converted to EUI-64 567 as per the standard. 568 - If this object is already and EUI-64, it just returns a new, 569 numerically equivalent object is returned instead. 570 """ 571 if self.addr_type == AT_LINK: 572 eui64_words = ["%02x" % i for i in self[0:3]] + ['ff', 'fe'] + \ 573 ["%02x" % i for i in self[3:6]] 574 575 return self.__class__('-'.join(eui64_words)) 576 else: 577 return EUI(str(self))
578 607
608 - def info(self):
609 """ 610 @return: A record dict containing IEEE registration details for this 611 EUI (MAC-48) if available, None otherwise. 612 """ 613 data = {'OUI': self.oui().registration()} 614 if self.isiab(): 615 data['IAB'] = self.iab().registration() 616 return data
617 618 #-----------------------------------------------------------------------------
619 -class IP(Addr):
620 """ 621 Represents B{individual} IPv4 and IPv6 addresses. 622 623 B{Please Note:} this class is intended to provide low-level functionality 624 to individual IP addresses such as octet/hextet access, integer/hex/binary 625 conversions, etc. If you are coming from other libraries you may expect to 626 find much higher level networking operations here. While the inclusion of 627 a bitmask prefix or netmask to indicate subnet membership is permitted by 628 the class constructor they are provided only as a convenience to the user. 629 630 All higher level subnet and network operations can be found in objects of 631 classes L{CIDR}, L{IPRange} and L{Wildcard}. There are handy helper methods 632 here, (C{.cidr()}, C{.iprange()} and C{.wildcard()}) that return pre-initialised 633 objects of those classes without you having to call them explicitly. 634 635 Example usage :: 636 637 >>> ip = IP('10.0.0.1') 638 >>> list(ip) == [10, 0, 0, 1] 639 True 640 >>> ip += 1 641 >>> str(ip) == '10.0.0.2' 642 True 643 644 >>> IP('10.0.0.0/28').iprange() 645 IPRange('10.0.0.0', '10.0.0.15') 646 647 >>> IP('10.0.0.64/24').cidr() 648 CIDR('10.0.0.0/24') 649 650 >>> IP('192.168.0.1/255.255.253.0').wildcard() 651 Wildcard('192.168.0-1.*') 652 653 >>> ipv6 = IP('fe80::20f:1fff:fe12:e733') 654 >>> ipv6[0:4] 655 [65152, 0, 0, 0] 656 657 >>> IP('fe80::20f:1fff:fe12:e733/64').cidr() 658 CIDR('fe80::/64') 659 660 See those classes for details on the functionality they provide. 661 """ 662 STRATEGIES = (ST_IPV4, ST_IPV6) 663 ADDR_TYPES = (AT_UNSPEC, AT_INET, AT_INET6) 664 TRANSLATE_STR = ''.join([chr(_) for _ in range(256)]) #FIXME: replace this 665 666 # Descriptor registrations. 667 strategy = StrategyDescriptor(STRATEGIES) 668 addr_type = AddrTypeDescriptor(ADDR_TYPES) 669 prefixlen = PrefixLenDescriptor() 670
671 - def __init__(self, addr, addr_type=AT_UNSPEC):
672 """ 673 Constructor. 674 675 @param addr: an IPv4 or IPv6 address string with an optional subnet 676 prefix or a network byte order integer. 677 678 @param addr_type: (optional) the IP address type (C{AT_INET} or 679 C{AT_INET6}). If L{addr} is an integer, this argument is mandatory. 680 """ 681 prefixlen = None 682 # Check for prefix in address and split it out. 683 try: 684 if '/' in addr: 685 (addr, prefixlen) = addr.split('/', 1) 686 except TypeError: 687 # addr is an int - let it pass through. 688 pass 689 690 # Call superclass constructor before processing subnet prefix to 691 # assign the strategyn object. 692 super(IP, self).__init__(addr, addr_type) 693 694 # Set the subnet prefix. 695 if prefixlen is None: 696 self.__dict__['prefixlen'] = self.strategy.width 697 else: 698 self.prefixlen = prefixlen
699
700 - def is_netmask(self):
701 """ 702 @return: C{True} if this addr is a mask that would return a host id, 703 C{False} otherwise. 704 """ 705 int_val = (self.value ^ self.strategy.max_int) + 1 706 return (int_val & (int_val - 1) == 0)
707
708 - def netmask_bits(self): #FIXME: replace this
709 """ 710 @return: If this address is a valid netmask, the number of non-zero 711 bits are returned, otherwise it returns the width in bits for 712 based on the version, 32 for IPv4 and 128 for IPv6. 713 """ 714 if not self.is_netmask(): 715 return self.strategy.width 716 717 bits = self.strategy.int_to_bits(self.value) 718 mask_bits = bits.translate(IP.TRANSLATE_STR, ':.0') 719 mask_length = len(mask_bits) 720 721 if not 1 <= mask_length <= self.strategy.width: 722 raise ValueError('Unexpected mask length %d for address type!' \ 723 % mask_length) 724 725 return mask_length
726
727 - def reverse_dns(self):
728 """@return: The reverse DNS lookup string for this IP address""" 729 return self.strategy.int_to_arpa(self.value)
730
731 - def is_hostmask(self):
732 """ 733 @return: C{True} if this address is a mask that would return a host 734 id, C{False} otherwise. 735 """ 736 int_val = self.value + 1 737 return (int_val & (int_val-1) == 0)
738
739 - def hostname(self):
740 """ 741 @return: Returns the FQDN for this IP address via a DNS query 742 using gethostbyaddr() Python's socket module. 743 """ 744 try: 745 return _socket.gethostbyaddr(str(self))[0] 746 except: 747 return
748
749 - def cidr(self):
750 """@return: A L{CIDR} object based on this IP address""" 751 hostmask = (1 << (self.strategy.width - self.prefixlen)) - 1 752 start = (self.value | hostmask) - hostmask 753 network = self.strategy.int_to_str(start) 754 return CIDR("%s/%d" % (network, self.prefixlen))
755
756 - def wildcard(self):
757 """@return: A L{Wildcard} object based on this IP address""" 758 if self.addr_type == AT_INET6: 759 raise AddrConversionError('wildcards not support by IPv6!') 760 return self.iprange().wildcard()
761
762 - def iprange(self):
763 """@return: A L{CIDR} object based on this IP address""" 764 hostmask = (1 << (self.strategy.width - self.prefixlen)) - 1 765 netmask = self.strategy.max_int ^ hostmask 766 first = (self.value | hostmask) - hostmask 767 last = first | hostmask 768 return IPRange(self.strategy.int_to_str(first), 769 self.strategy.int_to_str(last))
770
771 - def ipv4(self):
772 """ 773 @return: A new version 4 L{IP} object numerically equivalent this 774 address. If this object is already IPv4 then a copy is returned. If 775 this object is IPv6 and its value is compatible with IPv4, a new IPv4 776 L{IP} object is returned. 777 778 Raises an L{AddrConversionError} is IPv6 address cannot be converted. 779 """ 780 ip_addr = None 781 if self.addr_type == AT_INET: 782 ip_addr = IP(self.value, AT_INET) 783 ip_addr.prefixlen = self.prefixlen 784 elif self.addr_type == AT_INET6: 785 words = self.strategy.int_to_words(self.value) 786 #TODO: Replace these with bit shifts. 787 if words[0:6] == (0, 0, 0, 0, 0, 0): 788 ip_addr = IP(self.value, AT_INET) 789 ip_addr.prefixlen = self.prefixlen - 96 790 #TODO: Replace these with bit shifts. 791 elif words[0:6] == (0, 0, 0, 0, 0, 0xffff): 792 ip_addr = IP(self.value - 0xffff00000000, AT_INET) 793 ip_addr.prefixlen = self.prefixlen - 96 794 else: 795 raise AddrConversionError('IPv6 address %s not suitable for' \ 796 'IPv4 conversion!' % self) 797 return ip_addr
798
799 - def ipv6(self, ipv4_compatible=False):
800 """ 801 B{Please Note:} the IPv4-Mapped IPv6 address format is now considered 802 deprecated. Reference: RFC 4291 803 804 @param ipv4_compatible: If C{True} returns an IPv4-Mapped address 805 (::ffff:x.x.x.x), an IPv4-Compatible (::x.x.x.x) address 806 otherwise. Default: False (IPv4-Mapped). 807 808 @return: A new L{IP} version 6 object that is numerically equivalent 809 this address. If this object is already IPv6 then a copy of this 810 object is returned. If this object is IPv4, a new version 6 L{IP} 811 object is returned. 812 """ 813 ip_addr = None 814 if self.addr_type == AT_INET6: 815 ip_addr = IP(self.value, AT_INET6) 816 ip_addr.prefixlen = self.prefixlen - 96 817 elif self.addr_type == AT_INET: 818 ip_addr = IP(self.value, AT_INET6) 819 if ipv4_compatible: 820 # IPv4-Compatible IPv6 address 821 ip_addr[5] = 0 822 else: 823 # IPv4-Mapped IPv6 address 824 ip_addr[5] = 0xffff 825 ip_addr.prefixlen = self.prefixlen + 96 826 return ip_addr
827
828 - def is_unicast(self):
829 """@return: C{True} if this IP is unicast, C{False} otherwise""" 830 return not self.is_multicast()
831
832 - def is_loopback(self):
833 """ 834 @return: C{True} if this IP is loopback address (not for network 835 transmission), C{False} otherwise. 836 References: RFC 3330 and 4291. 837 """ 838 if self.addr_type == AT_INET: 839 return self in CIDR('127/8') 840 elif self.addr_type == AT_INET6: 841 return self == IP('::1')
842
843 - def is_multicast(self):
844 """@return: C{True} if this IP is multicast, C{False} otherwise""" 845 if self.addr_type == AT_INET: 846 return self in CIDR('224/4') 847 elif self.addr_type == AT_INET6: 848 return self in CIDR('ff00::/8')
849
850 - def is_private(self):
851 """ 852 @return: C{True} if this IP is for internal/private use only 853 (i.e. non-public), C{False} otherwise. Reference: RFCs 1918, 854 3330, 4193, 3879 and 2365. 855 """ 856 if self.addr_type == AT_INET: 857 for cidr in (CIDR('192.168/16'), CIDR('10/8'),CIDR('172.16/12'), 858 CIDR('192.0.2.0/24'), CIDR('239.192/14')): 859 if self in cidr: 860 return True 861 elif self.addr_type == AT_INET6: 862 # Please Note: FEC0::/10 has been deprecated! See RFC 3879. 863 return self in CIDR('fc00::/7') # ULAs - Unique Local Addresses 864 865 if self.is_link_local(): 866 return True 867 868 return False
869 879
880 - def is_reserved(self):
881 """ 882 @return: C{True} if this IP is in IANA reserved range, C{False} 883 otherwise. Reference: RFCs 3330 and 3171. 884 """ 885 if self.addr_type == AT_INET: 886 # Use of wildcards here much more concise than CIDR... 887 for cidr in (CIDR('240/4'), CIDR('234/7'), CIDR('236/7'), 888 Wildcard('225-231.*.*.*'), Wildcard('234-238.*.*.*')): 889 if self in cidr: 890 return True 891 if self.addr_type == AT_INET6: 892 for cidr in (CIDR('ff00::/12'),CIDR('::/8'), CIDR('0100::/8'), 893 CIDR('0200::/7'), CIDR('0400::/6'), CIDR('0800::/5'), 894 CIDR('1000::/4'), CIDR('4000::/3'), CIDR('6000::/3'), 895 CIDR('8000::/3'), CIDR('A000::/3'), CIDR('C000::/3'), 896 CIDR('E000::/4'), CIDR('F000::/5'), CIDR('F800::/6'), 897 CIDR('FE00::/9')): 898 if self in cidr: 899 return True 900 return False
901
902 - def is_ipv4_mapped(self):
903 """ 904 @return: C{True} if this IP is IPv4-compatible IPv6 address, C{False} 905 otherwise. 906 """ 907 return self.addr_type == AT_INET6 and (self.value >> 32) == 0xffff
908
909 - def is_ipv4_compat(self):
910 """ 911 @return: C{True} if this IP is IPv4-mapped IPv6 address, C{False} 912 otherwise. 913 """ 914 return self.addr_type == AT_INET6 and (self.value >> 32) == 0
915
916 - def info(self):
917 """ 918 @return: A record dict containing IANA registration details for this 919 IP address if available, None otherwise. 920 """ 921 # This import is placed here for efficiency. If you don't call this 922 # method, you don't take the (small), one time, import start up 923 # penalty. Also gets around a nasty module dependency issue. 924 # Two birds, one stone ... 925 import netaddr.ip 926 return netaddr.ip.query(self)
927
928 - def __str__(self):
929 """@return: common string representation for this IP address""" 930 return self.strategy.int_to_str(self.value)
931
932 - def __repr__(self):
933 """@return: executable Python string to recreate equivalent object.""" 934 if self.prefixlen == self.strategy.width: 935 return "%s('%s')" % (self.__class__.__name__, str(self)) 936 937 return "%s('%s/%d')" % (self.__class__.__name__, str(self), 938 self.prefixlen)
939 940 #-----------------------------------------------------------------------------
941 -def nrange(start, stop, step=1, fmt=None):
942 """ 943 An xrange work alike generator that produces sequences of IP addresses 944 based on start and stop addresses, in intervals of step size. 945 946 @param start: first IP address string or L{IP} object in range. 947 948 @param stop: last IP address string or L{IP} object in range 949 950 @param step: (optional) size of step between address in range. 951 (Default: 1) 952 953 @param fmt: (optional) callable used on addresses returned. 954 (Default: None - L{IP} objects). Supported options :- 955 - C{str} - IP address strings 956 - C{int}, C{long} - IP address integer (network byte order) 957 - C{hex} - IP address as a hexadecimal number 958 - L{IP} class/subclass or callable that accepts C{addr_value} and 959 C{addr_type} arguments. 960 """ 961 if not isinstance(start, IP): 962 if isinstance(start, (str, unicode)): 963 start = IP(start) 964 else: 965 raise TypeError('start is not recognised address in string ' \ 966 'format or IP class/subclass instance!') 967 else: 968 # Use start object's constructor as formatter. 969 if fmt is None: 970 fmt = start.__class__ 971 972 if not isinstance(stop, IP): 973 if isinstance(stop, (str, unicode)): 974 stop = IP(stop) 975 else: 976 raise TypeError('stop is not recognised address string ' \ 977 'or IP class/subclass instance!') 978 979 if not isinstance(step, (int, long)): 980 raise TypeError('step must be type int|long, not %s!' % type(step)) 981 982 if start.addr_type != stop.addr_type: 983 raise TypeError('start and stop are not the same address type!') 984 985 if step == 0: 986 raise ValueError('step argument cannot be zero') 987 988 negative_step = False 989 addr_type = start.addr_type 990 991 # We don't need objects from here onwards. Basic integer values will do 992 # just fine. 993 start_fmt = start.__class__ 994 start = int(start) 995 stop = int(stop) 996 997 if step < 0: 998 negative_step = True 999 1000 index = start - step 1001 1002 # Default formatter. 1003 if fmt is None: 1004 fmt = IP 1005 1006 if fmt in (int, long, hex): 1007 # Yield network address integer values. 1008 while True: 1009 index += step 1010 if negative_step: 1011 if not index >= stop: 1012 return 1013 else: 1014 if not index <= stop: 1015 return 1016 yield fmt(index) 1017 elif fmt in (str, unicode): 1018 # Yield address string values. 1019 while True: 1020 index += step 1021 if negative_step: 1022 if not index >= stop: 1023 return 1024 else: 1025 if not index <= stop: 1026 return 1027 yield str(start_fmt(index, addr_type)) 1028 else: 1029 # Yield network address objects. 1030 while True: 1031 index += step 1032 if negative_step: 1033 if not index >= stop: 1034 return 1035 else: 1036 if not index <= stop: 1037 return 1038 1039 yield fmt(index, addr_type)
1040 1041 #-----------------------------------------------------------------------------
1042 -class IPRange(object):
1043 """ 1044 Represents arbitrary contiguous blocks of IPv4 and IPv6 addresses using 1045 only a lower and upper bound IP address. 1046 1047 It is the base class for more specialised block types such as L{CIDR()} 1048 and L{Wildcard()}. There is no requirement that the boundary IP addresses 1049 fall on strict bitmask boundaries. 1050 1051 The sort order for sequence of mixed version L{IPRange} objects is IPv4 1052 followed by IPv6, based on the range's magnitude (size). 1053 """ 1054 STRATEGIES = (ST_IPV4, ST_IPV6) 1055 ADDR_TYPES = (AT_UNSPEC, AT_INET, AT_INET6) 1056 1057 # Descriptor registrations. 1058 strategy = StrategyDescriptor(STRATEGIES) 1059 addr_type = AddrTypeDescriptor(ADDR_TYPES) 1060 first = AddrValueDescriptor('first') 1061 last = AddrValueDescriptor('last') 1062 fmt = FormatDescriptor(IP) 1063
1064 - def __init__(self, first, last, fmt=IP):
1065 """ 1066 Constructor. 1067 1068 @param first: start address for this IP address range. 1069 1070 @param last: stop address for this IP address range. 1071 1072 @param fmt: (optional) callable used to create return values. 1073 Default: L{IP()} objects. See L{nrange()} documentations for 1074 more details on the various options. 1075 """ 1076 #TODO: this can be optimised, consider accepting addr_type via the 1077 #TODO: constructor. 1078 self.addr_type = AT_UNSPEC 1079 self.first = first 1080 self.last = last 1081 if self.last < self.first: 1082 raise IndexError('start address is greater than stop address!') 1083 self.fmt = fmt
1084
1085 - def __hash__(self):
1086 """ 1087 @return: The hash of this address range. Allow them to be used in sets 1088 and as keys in dictionaries. 1089 """ 1090 return hash((self.first, self.last, self.addr_type))
1091
1092 - def __len__(self):
1093 """ 1094 @return: The total number of network addresses in this range. 1095 - Use this method only for ranges that contain less than 1096 C{2^31} addresses or try the L{size()} method. Raises an 1097 C{IndexError} if size is exceeded. 1098 """ 1099 size = self.size() 1100 if size > (2 ** 31): 1101 # Use size() method in this class instead as len() will b0rk! 1102 raise IndexError("range contains greater than 2^31 addresses! " \ 1103 "Use obj.size() instead.") 1104 return size
1105
1106 - def size(self):
1107 """ 1108 @return: The total number of network addresses in this range. 1109 - Use this method in preference to L{__len__()} when size of 1110 ranges potentially exceeds C{2^31} addresses. 1111 """ 1112 return self.last - self.first + 1
1113
1114 - def format(self, int_addr, fmt=None):
1115 """ 1116 @param int_addr: a network address integer (network byte order). 1117 1118 @param fmt: (optional) a callable used on return values. 1119 Default: None. If set to None, this method uses the self.fmt 1120 setting instead. The argument is provided as an override option. 1121 1122 @return: a network address in the format returned after passing it 1123 through this object's fmt property callable. 1124 """ 1125 if fmt is None: 1126 fmt = self.fmt 1127 1128 if fmt in (str, unicode): 1129 return self.strategy.int_to_str(int_addr) 1130 elif fmt in (int, long, hex): 1131 return fmt(int_addr) 1132 else: 1133 return fmt(int_addr, self.addr_type)
1134
1135 - def __getitem__(self, index):
1136 """ 1137 @return: The IP address(es) in this address range referenced by 1138 index/slice. Slicing objects can produce large sequences so 1139 generator objects are returned instead of a list. Wrapping a slice 1140 with C{list()} or C{tuple()} may be required dependent on context 1141 in which it is called. 1142 """ 1143 1144 if isinstance(index, (int, long)): 1145 if (- self.size()) <= index < 0: 1146 # negative index. 1147 return self.format(self.last + index + 1) 1148 elif 0 <= index <= (self.size() - 1): 1149 # Positive index or zero index. 1150 return self.format(self.first + index) 1151 else: 1152 raise IndexError('index out range for address range size!') 1153 elif isinstance(index, slice): 1154 # slices 1155 #FIXME: IPv6 breaks the .indices() method on the slice object 1156 #FIXME: spectacularly. We'll have to work out the start, stop and 1157 #FIXME: step ourselves :-( 1158 # 1159 #FIXME: see PySlice_GetIndicesEx function in Python SVN 1160 #FIXME: repository for implementation details :- 1161 #FIXME: http://svn.python.org/view/python/trunk/Objects/sliceobject.c 1162 (start, stop, step) = index.indices(self.size()) 1163 1164 start_addr = IP(self.first + start, self.addr_type) 1165 end_addr = IP(self.first + stop - step, self.addr_type) 1166 return nrange(start_addr, end_addr, step, fmt=self.fmt) 1167 else: 1168 raise TypeError('unsupported type %r!' % index)
1169
1170 - def __iter__(self):
1171 """ 1172 @return: An iterator object providing access to all network addresses 1173 within this range. 1174 """ 1175 start_addr = IP(self.first, self.addr_type) 1176 end_addr = IP(self.last, self.addr_type) 1177 return nrange(start_addr, end_addr, fmt=self.fmt)
1178
1179 - def __contains__(self, addr):
1180 """ 1181 @param addr: and IP/IPRange class/subclass instance or IP string value 1182 to be compared. 1183 1184 @return: C{True} if given address or range falls within this range, 1185 C{False} otherwise. 1186 """ 1187 if isinstance(addr, (str, unicode)): 1188 # string address or address range. 1189 c_addr = IP(addr) 1190 if c_addr.addr_type == self.addr_type: 1191 if self.first <= int(c_addr) <= self.last: 1192 return True 1193 elif isinstance(addr, IP): 1194 # Single value check. 1195 if self.first <= int(addr) <= self.last: 1196 return True 1197 elif issubclass(addr.__class__, IPRange): 1198 # Range value check. 1199 if addr.first >= self.first and addr.last <= self.last: 1200 return True 1201 else: 1202 raise TypeError('%r is an unsupported type or class!' % addr) 1203 1204 return False
1205
1206 - def __eq__(self, other):
1207 """ 1208 @param other: an address object of the same address type as C{self}. 1209 1210 @return: C{True} if the boundaries of this range are the same as 1211 other, C{False} otherwise. 1212 """ 1213 try: 1214 return (self.addr_type, self.first, self.last) == \ 1215 (other.addr_type, other.first, other.last) 1216 except AttributeError: 1217 return False
1218
1219 - def __ne__(self, other):
1220 """ 1221 @param other: an address object of the same address type as C{self}. 1222 1223 @return: C{True} if the boundaries of this range are not the same as 1224 other, C{False} otherwise. 1225 """ 1226 try: 1227 return (self.addr_type, self.first, self.last) != \ 1228 (other.addr_type, other.first, other.last) 1229 except AttributeError: 1230 return False
1231
1232 - def __lt__(self, other):
1233 """ 1234 @param other: an address object of the same address type as C{self}. 1235 1236 @return: C{True} if the boundaries of this range are less than other, 1237 C{False} otherwise. 1238 """ 1239 try: 1240 return (self.addr_type, self.first, self.last) < \ 1241 (other.addr_type, other.first, other.last) 1242 except AttributeError: 1243 return False
1244
1245 - def __le__(self, other):
1246 """ 1247 @param other: an address object of the same address type as C{self}. 1248 1249 @return: C{True} if the boundaries of this range are less or equal to 1250 other, C{False} otherwise. 1251 """ 1252 try: 1253 return (self.addr_type, self.first, self.last) <= \ 1254 (other.addr_type, other.first, other.last) 1255 except AttributeError: 1256 return False
1257
1258 - def __gt__(self, other):
1259 """ 1260 @param other: an address object of the same address type as C{self}. 1261 1262 @return: C{True} if the boundaries of this range are greater than 1263 other, C{False} otherwise. 1264 """ 1265 try: 1266 return (self.addr_type, self.first, self.last) > \ 1267 (other.addr_type, other.first, other.last) 1268 except AttributeError: 1269 return False
1270
1271 - def __ge__(self, other):
1272 """ 1273 @param other: an address object of the same address type as C{self}. 1274 1275 @return: C{True} if the boundaries of this range are greater or equal 1276 to other, C{False} otherwise. 1277 """ 1278 try: 1279 return (self.addr_type, self.first, self.last) >= \ 1280 (other.addr_type, other.first, other.last) 1281 except AttributeError: 1282 return False
1283
1284 - def __iadd__(self, i):
1285 """ 1286 Increments start and end addresses of this range by the current size. 1287 1288 Raises IndexError if the result exceeds address range maximum. 1289 """ 1290 try: 1291 new_first = self.first + (self.size() * i) 1292 new_last = self.last + (self.size() * i) 1293 except TypeError: 1294 raise TypeError('Increment value must be an integer!') 1295 1296 if new_last > self.strategy.max_int: 1297 raise IndexError('Invalid increment is outside address boundary!') 1298 1299 self.first = new_first 1300 self.last = new_last 1301 1302 return self
1303
1304 - def __isub__(self, i):
1305 """ 1306 Decrements start and end addresses of this range by the current size. 1307 1308 Raises IndexError if the result is less than zero. 1309 """ 1310 try: 1311 new_first = self.first - (self.size() * i) 1312 new_last = self.last - (self.size() * i) 1313 except TypeError: 1314 raise TypeError('Decrement value must be an integer!') 1315 1316 if new_last < 0: 1317 raise IndexError('Invalid decrement is outside address boundary!') 1318 1319 self.first = new_first 1320 self.last = new_last 1321 1322 return self
1323
1324 - def iprange(self):
1325 """ 1326 @return: A valid L{IPRange} object for this address range. 1327 """ 1328 #TODO: this can be optimised. 1329 ip_range = IPRange(self.strategy.int_to_str(self.first), 1330 self.strategy.int_to_str(self.last)) 1331 if self.fmt == str: 1332 return str(ip_range) 1333 return ip_range
1334
1335 - def cidrs(self):
1336 """ 1337 @return: A list of one or more L{CIDR} objects covering this address 1338 range. B{Please Note:} a list is returned even if this range maps 1339 to a single CIDR because arbitrary ranges may not be aligned with 1340 base 2 subnet sizes and will therefore return multiple CIDRs. 1341 """ 1342 # This can probably be tidied up a bit but I'm really proud of this 1343 # method. It is one seriously sweet piece of code!!! 1344 cidr_list = [] 1345 1346 # Get spanning CIDR covering both addresses. 1347 start = IP(self.first, self.addr_type) 1348 end = IP(self.last, self.addr_type) 1349 1350 cidr_span = CIDR.span([start, end]) 1351 1352 if cidr_span.first == self.first and cidr_span.last == self.last: 1353 # Spanning CIDR matches start and end exactly. 1354 cidr_list = [cidr_span] 1355 elif cidr_span.last == self.last: 1356 # Spanning CIDR matches end exactly. 1357 ip = IP(start) 1358 first_int_val = int(ip) 1359 ip -= 1 1360 cidr_remainder = cidr_span - ip 1361 1362 first_found = False 1363 for cidr in cidr_remainder: 1364 if cidr.first == first_int_val: 1365 first_found = True 1366 if first_found: 1367 cidr_list.append(cidr) 1368 elif cidr_span.first == self.first: 1369 # Spanning CIDR matches start exactly. 1370 ip = IP(end) 1371 last_int_val = int(ip) 1372 ip += 1 1373 cidr_remainder = cidr_span - ip 1374 1375 last_found = False 1376 for cidr in cidr_remainder: 1377 cidr_list.append(cidr) 1378 if cidr.last == last_int_val: 1379 break 1380 elif cidr_span.first <= self.first and cidr_span.last >= self.last: 1381 # Spanning CIDR overlaps start and end. 1382 ip = IP(start) 1383 first_int_val = int(ip) 1384 ip -= 1 1385 cidr_remainder = cidr_span - ip 1386 1387 # Fix start. 1388 first_found = False 1389 for cidr in cidr_remainder: 1390 if cidr.first == first_int_val: 1391 first_found = True 1392 if first_found: 1393 cidr_list.append(cidr) 1394 1395 # Fix end. 1396 ip = IP(end) 1397 last_int_val = int(ip) 1398 ip += 1 1399 cidr_remainder = cidr_list.pop() - ip 1400 1401 last_found = False 1402 for cidr in cidr_remainder: 1403 cidr_list.append(cidr) 1404 if cidr.last == last_int_val: 1405 break 1406 1407 # Return address list in requested format. 1408 if self.fmt in (str, unicode): 1409 cidr_list = [self.fmt(c) for c in cidr_list] 1410 1411 return cidr_list
1412
1413 - def wildcard(self):
1414 """ 1415 @return: A L{Wildcard} object equivalent to this CIDR. 1416 - If CIDR was initialised with C{fmt=str}, a wildcard string 1417 is returned, in all other cases a L{Wildcard} object is 1418 returned. 1419 - Only supports IPv4 CIDR addresses. 1420 """ 1421 t1 = self.strategy.int_to_words(self.first) 1422 t2 = self.strategy.int_to_words(self.last) 1423 1424 if self.addr_type != AT_INET: 1425 raise AddrConversionError('wildcards only suitable for IPv4 ' \ 1426 'ranges!') 1427 1428 tokens = [] 1429 1430 seen_hyphen = False 1431 seen_asterisk = False 1432 1433 for i in range(4): 1434 if t1[i] == t2[i]: 1435 # A normal octet. 1436 tokens.append(str(t1[i])) 1437 elif (t1[i] == 0) and (t2[i] == 255): 1438 # An asterisk octet. 1439 tokens.append('*') 1440 seen_asterisk = True 1441 else: 1442 # Create a hyphenated octet - only one allowed per wildcard. 1443 if not seen_asterisk: 1444 if not seen_hyphen: 1445 tokens.append('%s-%s' % (t1[i], t2[i])) 1446 seen_hyphen = True 1447 else: 1448 raise SyntaxError('only one hyphenated octet per ' \ 1449 'wildcard permitted!') 1450 else: 1451 raise SyntaxError("* chars aren't permitted before ' \ 1452 'hyphenated octets!") 1453 1454 wildcard = '.'.join(tokens) 1455 1456 if self.fmt == str: 1457 return wildcard 1458 1459 return Wildcard(wildcard)
1460
1461 - def issubnet(self, other):
1462 """ 1463 @return: True if other's boundary is equal to or within this range. 1464 False otherwise. 1465 """ 1466 if isinstance(other, (str, unicode)): 1467 other = CIDR(other) 1468 1469 if not hasattr(other, 'addr_type'): 1470 raise TypeError('%r is an unsupported argument type!' % other) 1471 1472 if self.addr_type != other.addr_type: 1473 raise TypeError('Ranges must be the same address type!') 1474 1475 return self.first >= other.first and self.last <= other.last
1476
1477 - def issupernet(self, other):
1478 """ 1479 @return: True if other's boundary is equal to or contains this range. 1480 False otherwise. 1481 """ 1482 if isinstance(other, (str, unicode)): 1483 other = CIDR(other) 1484 1485 if not hasattr(other, 'addr_type'): 1486 raise TypeError('%r is an unsupported argument type!' % other) 1487 1488 if self.addr_type != other.addr_type: 1489 raise TypeError('Ranges must be the same address type!') 1490 1491 return self.first <= other.first and self.last >= other.last
1492
1493 - def adjacent(self, other):
1494 """ 1495 @return: True if other's boundary touches the boundary of this 1496 address range, False otherwise. 1497 """ 1498 if isinstance(other, (str, unicode)): 1499 other = CIDR(other) 1500 1501 if not hasattr(other, 'addr_type'): 1502 raise TypeError('%r is an unsupported argument type!' % other) 1503 1504 if self.addr_type != other.addr_type: 1505 raise TypeError('addresses must be of the same type!') 1506 1507 if isinstance(other, IPRange): 1508 # Left hand side of this address range. 1509 if self.first == (other.last + 1): 1510 return True 1511 1512 # Right hand side of this address range. 1513 if self.last == (other.first - 1): 1514 return True 1515 elif isinstance(other, IP): 1516 # Left hand side of this address range. 1517 if self.first == (other.value + 1): 1518 return True 1519 1520 # Right hand side of this address range. 1521 if self.last == (other.value - 1): 1522 return True 1523 else: 1524 raise TypeError('unexpected error for argument: %r!') 1525 1526 return False
1527
1528 - def overlaps(self, other):
1529 """ 1530 @return: True if other's boundary crosses the boundary of this address 1531 range, False otherwise. 1532 """ 1533 if isinstance(other, (str, unicode)): 1534 other = CIDR(other) 1535 1536 if not hasattr(other, 'addr_type'): 1537 raise TypeError('%r is an unsupported argument type!' % other) 1538 1539 if self.addr_type != other.addr_type: 1540 raise TypeError('Ranges must be the same address type!') 1541 1542 # Left hand side of this address range. 1543 if self.first <= other.last <= self.last: 1544 return True 1545 1546 # Right hand side of this address range. 1547 if self.first <= other.first <= self.last: 1548 return True 1549 1550 return False
1551
1552 - def __str__(self):
1553 return "%s-%s" % (self.strategy.int_to_str(self.first), 1554 self.strategy.int_to_str(self.last))
1555
1556 - def __repr__(self):
1557 """@return: executable Python string to recreate equivalent object.""" 1558 return "%s(%r, %r)" % (self.__class__.__name__, 1559 self.strategy.int_to_str(self.first), 1560 self.strategy.int_to_str(self.last))
1561 1562 #-----------------------------------------------------------------------------
1563 -class CIDR(IPRange):
1564 """ 1565 Represents blocks of IPv4 and IPv6 addresses using CIDR (Classless 1566 Inter-Domain Routing) notation. 1567 1568 CIDR is a method of categorising contiguous blocks of both IPv4 and IPv6 1569 addresses. It is very scalable allowing for the optimal usage of the IP 1570 address space. It permits the aggregation of networks via route 1571 summarisation (supernetting) where adjacent routes can be combined into a 1572 single route easily. This greatly assists in the reduction of routing 1573 table sizes and improves network router efficiency. 1574 1575 CIDR blocks are represented by a base network address and a prefix 1576 indicating the size of the (variable length) subnet mask. These are 1577 separated by a single '/' character. Subnet sizes increase in powers of 1578 base 2 aligning to bit boundaries. 1579 1580 It is technically invalid to have non-zero bits in a CIDR address to the 1581 right of the implied netmask. For user convenience this is however 1582 configurable and can be disabled using a constructor argument. 1583 1584 The constructor accepts CIDRs expressed in one of 4 different ways :- 1585 1586 A) Standard CIDR format :- 1587 1588 IPv4:: 1589 1590 x.x.x.x/y -> 192.0.2.0/24 1591 1592 where the x's represent the network address and y is the netmask 1593 prefix between 0 and 32. 1594 1595 IPv6:: 1596 1597 x::/y -> fe80::/10 1598 1599 where the x's represent the network address and y is the netmask 1600 prefix between 0 and 128. 1601 1602 B) Abbreviated CIDR format (IPv4 only):: 1603 1604 x -> 192 1605 x/y -> 10/8 1606 x.x/y -> 192.168/16 1607 x.x.x/y -> 192.168.0/24 1608 1609 which are equivalent to:: 1610 1611 x.0.0.0/y -> 192.0.0.0/24 1612 x.0.0.0/y -> 10.0.0.0/8 1613 x.x.0.0/y -> 192.168.0.0/16 1614 x.x.x.0/y -> 192.168.0.0/24 1615 1616 - The trailing zeros are implicit. 1617 - Old classful IP address rules apply if y is omitted. 1618 1619 C) Hybrid CIDR format (prefix replaced by netmask) :- 1620 1621 IPv4:: 1622 1623 x.x.x.x/y.y.y.y -> 192.0.2.0/255.255.255.0 1624 1625 IPv6:: 1626 1627 x::/y:: -> fe80::/ffc0:: 1628 1629 where the y's represent a valid netmask. 1630 1631 D) ACL-style CIDR format (prefix is replaced by a hostmask) :- 1632 1633 Akin to Cisco's ACL (Access Control List) bitmasking (reverse 1634 netmasks). 1635 1636 IPv4:: 1637 1638 x.x.x.x/y.y.y.y -> 192.0.2.0/0.0.0.255 1639 1640 IPv6:: 1641 1642 x::/y:: -> fe80::/3f:ffff:ffff:ffff:ffff:ffff:ffff:ffff 1643 1644 where the y's represent a valid hostmask. 1645 1646 Reference: RFCs 1338 and 4632. 1647 """ 1648 STRATEGIES = (ST_IPV4, ST_IPV6) 1649 ADDR_TYPES = (AT_UNSPEC, AT_INET, AT_INET6) 1650 1651 # Descriptor registrations. 1652 strategy = StrategyDescriptor(STRATEGIES) 1653 addr_type = AddrTypeDescriptor(ADDR_TYPES) 1654 prefixlen = PrefixLenDescriptor('CIDR') 1655 fmt = FormatDescriptor(IP) 1656
1657 - def abbrev_to_verbose(abbrev_cidr):
1658 """ 1659 A static method that converts abbreviated IPv4 CIDRs to their more 1660 verbose equivalent. 1661 1662 @param abbrev_cidr: an abbreviated CIDR. 1663 1664 Uses the old-style classful IP address rules to decide on a default 1665 subnet prefix if one is not explicitly provided. 1666 1667 Only supports IPv4 addresses. 1668 1669 Examples :: 1670 1671 10 - 10.0.0.0/8 1672 10/16 - 10.0.0.0/16 1673 128 - 128.0.0.0/16 1674 128/8 - 128.0.0.0/8 1675 192.168 - 192.168.0.0/16 1676 1677 @return: A verbose CIDR from an abbreviated CIDR or old-style classful 1678 network address, C{None} if format provided was not recognised or 1679 supported. 1680 """ 1681 # Internal function that returns a prefix value based on the old IPv4 1682 # classful network scheme that has been superseded (almost) by CIDR. 1683 def classful_prefix(octet): 1684 octet = int(octet) 1685 if not 0 <= octet <= 255: 1686 raise IndexError('Invalid octet: %r!' % octet) 1687 if 0 <= octet <= 127: # Legacy class 'A' classification. 1688 return 8 1689 elif 128 <= octet <= 191: # Legacy class 'B' classification. 1690 return 16 1691 elif 192 <= octet <= 223: # Legacy class 'C' classification. 1692 return 24 1693 elif octet == 224: # Multicast address range. 1694 return 4 1695 elif 225 <= octet <= 239: # Reserved. 1696 return 8 1697 return 32 # Default.
1698 1699 start = '' 1700 tokens = [] 1701 prefix = None 1702 1703 if isinstance(abbrev_cidr, (str, unicode)): 1704 # Don't support IPv6 for now... 1705 if ':' in abbrev_cidr: 1706 return None 1707 try: 1708 # Single octet partial integer or string address. 1709 i = int(abbrev_cidr) 1710 tokens = [str(i), '0', '0', '0'] 1711 return "%s%s/%s" % (start, '.'.join(tokens), classful_prefix(i)) 1712 1713 except ValueError: 1714 # Multi octet partial string address with optional prefix. 1715 part_addr = abbrev_cidr 1716 tokens = [] 1717 1718 if part_addr == '': 1719 # Not a recognisable format. 1720 return None 1721 1722 if '/' in part_addr: 1723 (part_addr, prefix) = part_addr.split('/', 1) 1724 1725 # Check prefix for validity. 1726 if prefix is not None: 1727 try: 1728 if not 0 <= int(prefix) <= 32: 1729 return None 1730 except ValueError: 1731 return None 1732 1733 if '.' in part_addr: 1734 tokens = part_addr.split('.') 1735 else: 1736 tokens = [part_addr] 1737 1738 if 1 <= len(tokens) <= 4: 1739 for i in range(4 - len(tokens)): 1740 tokens.append('0') 1741 else: 1742 # Not a recognisable format. 1743 return None 1744 1745 if prefix is None: 1746 try: 1747 prefix = classful_prefix(tokens[0]) 1748 except ValueError: 1749 return None 1750 1751 return "%s%s/%s" % (start, '.'.join(tokens), prefix) 1752 1753 except TypeError: 1754 pass 1755 except IndexError: 1756 pass 1757 1758 # Not a recognisable format. 1759 return None
1760
1761 - def span(addrs, fmt=None):
1762 """ 1763 Static method that accepts a sequence of IP addresses and/or CIDRs, 1764 Wildcards and IPRanges returning a single CIDR that is large enough 1765 to span the lowest and highest IP addresses in the sequence (with 1766 a possible overlap on either end). 1767 1768 @param addrs: a sequence of IP, CIDR, Wildcard or IPRange objects 1769 and/or their string representations. 1770 1771 @param fmt: (optional) callable used on return values. 1772 (Default: None - L{CIDR} object) Also accepts str() and unicode(). 1773 1774 @return: a single CIDR object spanning all addresses. 1775 """ 1776 if not isinstance(addrs, (list, tuple)): # Required - DO NOT CHANGE! 1777 raise TypeError('expected address sequence is not iterable!') 1778 1779 if not len(addrs) > 1: 1780 raise ValueError('sequence must contain 2 or more elements!') 1781 1782 if fmt not in (None, str, unicode): 1783 raise ValueError('unsupported formatter %r!' % fmt) 1784 1785 # List is required. 1786 if not isinstance(addrs, list): 1787 addrs = list(addrs) 1788 1789 # Detect type of string address or address range and create the 1790 # equivalent instance. 1791 for (i, addr) in enumerate(addrs): 1792 if isinstance(addr, (str, unicode)): 1793 try: 1794 obj = IP(addr) 1795 addrs[i] = obj 1796 continue 1797 except: 1798 pass 1799 try: 1800 obj = CIDR(addr) 1801 addrs[i] = obj 1802 continue 1803 except: 1804 pass 1805 try: 1806 obj = Wildcard(addr) 1807 addrs[i] = obj 1808 continue 1809 except: 1810 pass 1811 1812 # Find lowest and highest IP objects in address list. 1813 addrs.sort() 1814 lowest = addrs[0] 1815 highest = addrs[-1] 1816 1817 if isinstance(lowest, IPRange): 1818 # Create new IP as user may be returning address strings. 1819 lowest = IP(lowest.first, lowest.addr_type) 1820 1821 if isinstance(highest, IPRange): 1822 # Create new IP as user may be returning address strings. 1823 highest = IP(highest.last, highest.addr_type) 1824 1825 if lowest.addr_type != highest.addr_type: 1826 raise TypeError('address types are not the same!') 1827 1828 cidr = highest.cidr() 1829 1830 while cidr.prefixlen > 0: 1831 if highest in cidr and lowest not in cidr: 1832 cidr.prefixlen -= 1 1833 else: 1834 break 1835 1836 # Return address in string format. 1837 if fmt is not None: 1838 return fmt(cidr) 1839 1840 return cidr
1841 1842 abbrev_to_verbose = staticmethod(abbrev_to_verbose) 1843 span = staticmethod(span) 1844
1845 - def __init__(self, cidr, fmt=IP, strict=True):
1846 """ 1847 Constructor. 1848 1849 @param cidr: a valid IPv4/IPv6 CIDR address or abbreviated IPv4 1850 network address. 1851 1852 @param fmt: (optional) callable used on return values. 1853 Default: L{IP} class. See L{nrange()} documentations for 1854 more details on the various options. 1855 1856 @param strict: (optional) performs a test to ensure there are no 1857 non-zero bits to the right of the subnet mask or prefix when it is 1858 applied to the base address. (default: True) 1859 """ 1860 cidr_arg = cidr # Keep a copy of original argument. 1861 1862 # Replace an abbreviation with a verbose CIDR. 1863 verbose_cidr = CIDR.abbrev_to_verbose(cidr) 1864 if verbose_cidr is not None: 1865 cidr = verbose_cidr 1866 1867 if not isinstance(cidr, (str, unicode)): 1868 raise TypeError('%r is not a valid CIDR!' % cidr) 1869 1870 # Check for prefix in address and extract it. 1871 try: 1872 (network, mask) = cidr.split('/', 1) 1873 except ValueError: 1874 raise AddrFormatError('CIDR objects %r require a subnet prefix!' \ 1875 % cidr_arg) 1876 1877 #FIXME: Are IP objects for first and last really necessary? 1878 #FIXME: Should surely just be integer values. 1879 first = IP(network) 1880 self.strategy = first.strategy 1881 self.prefixlen = mask 1882 1883 strategy = first.strategy 1884 addr_type = strategy.addr_type 1885 1886 hostmask = (1 << (strategy.width - self.prefixlen)) - 1 1887 1888 last = IP(first.value | hostmask, addr_type) 1889 1890 if strict: 1891 # Strict CIDRs enabled. 1892 netmask = strategy.max_int ^ hostmask 1893 host = (first.value | netmask) - netmask 1894 if host != 0: 1895 raise ValueError('%s contains non-zero bits right of the ' \ 1896 '%d-bit mask! Did you mean %s instead?' \ 1897 % (first, self.prefixlen, 1898 strategy.int_to_str(int(last) - hostmask))) 1899 else: 1900 # Strict CIDRs disabled. 1901 first.value = strategy.int_to_str(int(last) - hostmask) 1902 1903 super(CIDR, self).__init__(first, last, fmt)
1904
1905 - def __sub__(self, other):
1906 """ 1907 Subtract another CIDR from this one. 1908 1909 @param other: a CIDR object that is greater than or equal to C{self}. 1910 1911 @return: A list of CIDR objects than remain after subtracting C{other} 1912 from C{self}. 1913 """ 1914 cidrs = [] 1915 1916 if self.prefixlen == self.strategy.width: 1917 # Fail fast. Nothing to do in this case. 1918 return cidrs 1919 1920 new_prefixlen = self.prefixlen + 1 1921 i_lower = self.first 1922 i_upper = self.first + (2 ** (self.strategy.width - new_prefixlen)) 1923 1924 lower = CIDR('%s/%d' % (self.strategy.int_to_str(i_lower), 1925 new_prefixlen)) 1926 upper = CIDR('%s/%d' % (self.strategy.int_to_str(i_upper), 1927 new_prefixlen)) 1928 1929 while other.prefixlen >= new_prefixlen: 1930 if other in lower: 1931 matched = i_lower 1932 unmatched = i_upper 1933 elif other in upper: 1934 matched = i_upper 1935 unmatched = i_lower 1936 1937 cidr = CIDR('%s/%d' % (self.strategy.int_to_str(unmatched), 1938 new_prefixlen)) 1939 1940 cidrs.append(cidr) 1941 1942 new_prefixlen += 1 1943 1944 if new_prefixlen > self.strategy.width: 1945 break 1946 1947 i_lower = matched 1948 i_upper = matched + (2 ** (self.strategy.width - new_prefixlen)) 1949 1950 lower = CIDR('%s/%d' % (self.strategy.int_to_str(i_lower), 1951 new_prefixlen)) 1952 upper = CIDR('%s/%d' % (self.strategy.int_to_str(i_upper), 1953 new_prefixlen)) 1954 1955 cidrs.sort() 1956 1957 # Return string based CIDR address values at user's request. 1958 if self.fmt is str: 1959 return [str(cidr) for cidr in cidrs] 1960 1961 return cidrs
1962
1963 - def __add__(self, other):
1964 """ 1965 Add another CIDR to this one returning a CIDR supernet that will 1966 contain both in the smallest possible sized range. 1967 1968 @param other: a CIDR object. 1969 1970 @return: A new (potentially larger) CIDR object. 1971 """ 1972 cidr = CIDR.span([self, other]) 1973 if self.fmt is str: 1974 return str(cidr) 1975 return cidr
1976
1977 - def network(self):
1978 """@return: The network (first) address in this CIDR block.""" 1979 return self[0]
1980
1981 - def broadcast(self):
1982 """ 1983 B{Please Note:} although IPv6 doesn't actually recognise the concept of 1984 a 'broadcast' address as the last address in a subnet (as in IPv4) so 1985 many other libraries do this that it isn't worth trying to resist. 1986 This issue raises so many user queries that it is easier to dispense 1987 with the theory and be pragmatic instead. 1988 1989 @return: The broadcast (last) address in this CIDR block. 1990 """ 1991 return self[-1]
1992
1993 - def netmask(self):
1994 """@return: The subnet mask address of this CIDR block.""" 1995 hostmask = (1 << (self.strategy.width - self.prefixlen)) - 1 1996 netmask = self.strategy.max_int ^ hostmask 1997 return self.format(netmask)
1998
1999 - def hostmask(self):
2000 """@return: The host mask address of this CIDR block.""" 2001 hostmask = (1 << (self.strategy.width - self.prefixlen)) - 1 2002 return self.format(hostmask)
2003 2004 network = property(network) 2005 broadcast = property(broadcast) 2006 netmask = property(netmask) 2007 hostmask = property(hostmask) 2008
2009 - def iter_host_addrs(self):
2010 """ 2011 @return: An iterator object providing access to all valid host IP 2012 addresses within the specified CIDR block. 2013 - with IPv4 the network and broadcast addresses are always 2014 excluded. Any smaller than 4 hosts yields an emtpy list. 2015 - with IPv6 only the unspecified address '::' is excluded from 2016 the yielded list. 2017 """ 2018 if self.addr_type == AT_INET: 2019 # IPv4 2020 if self.size() >= 4: 2021 return nrange( IP(self.first+1, self.addr_type), 2022 IP(self.last-1, self.addr_type), fmt=self.fmt) 2023 else: 2024 return iter([]) 2025 elif self.addr_type == AT_INET6: 2026 # IPv6 2027 if self.first == 0: 2028 # Don't return '::'. 2029 return nrange(IP(self.first+1, self.addr_type), 2030 IP(self.last, self.addr_type), fmt=self.fmt) 2031 else: 2032 return iter(self)
2033
2034 - def subnet(self, prefixlen, count=None, fmt=None):
2035 """ 2036 A generator that returns CIDR subnets based on the current network 2037 base address and provided CIDR prefix and count. 2038 2039 @param prefixlen: a CIDR prefix. 2040 2041 @param count: number of consecutive CIDRs to be returned. 2042 2043 @param fmt: callable used on return values. 2044 Default: None - L{CIDR} objects. str() and unicode() supported. 2045 2046 @return: an iterator (as lists could potentially be very large) 2047 containing CIDR subnets below this CIDR's base address. 2048 """ 2049 if not 0 <= self.prefixlen <= self.strategy.width: 2050 raise ValueError('prefixlen %d out of bounds for address type!' \ 2051 % prefixlen) 2052 2053 if not self.prefixlen <= prefixlen: 2054 raise ValueError('prefixlen less than current CIDR prefixlen!') 2055 2056 # Calculate number of subnets to be returned. 2057 width = self.strategy.width 2058 max_count = 2 ** (width - self.prefixlen) / 2 ** (width - prefixlen) 2059 2060 if count is None: 2061 count = max_count 2062 2063 if 1 < count < max_count: 2064 raise ValueError('count not within current CIDR boundaries!') 2065 2066 base_address = self.strategy.int_to_str(self.first) 2067 2068 if fmt is None: 2069 # Create new CIDR instances for each subnet returned. 2070 for i in xrange(count): 2071 cidr = CIDR('%s/%d' % (base_address, prefixlen)) 2072 cidr.first += cidr.size() * i 2073 cidr.prefixlen = prefixlen 2074 yield cidr 2075 elif fmt in (str, unicode): 2076 # Keep the same CIDR and just modify it. 2077 cidr = CIDR('%s/%d' % (base_address, prefixlen)) 2078 for i in xrange(count): 2079 cidr.first += cidr.size() * i 2080 cidr.prefixlen = prefixlen 2081 yield fmt(cidr) 2082 else: 2083 raise TypeError('unsupported fmt callable %r' % fmt)
2084
2085 - def cidrs(self):
2086 """ 2087 @return: A list of a copy of this L{CIDR} object. This method is here 2088 mainly for compatibility with IPRange interface. 2089 """ 2090 cidr_copy = CIDR('%s/%d' % (self.strategy.int_to_str(self.first), 2091 self.prefixlen)) 2092 2093 # Respect formatting. 2094 if self.fmt in (str, unicode): 2095 return [self.fmt(cidr_copy)] 2096 2097 return [cidr_copy]
2098
2099 - def __str__(self):
2100 return "%s/%s" % (self.strategy.int_to_str(self.first), self.prefixlen)
2101
2102 - def __repr__(self):
2103 """@return: executable Python string to recreate equivalent object.""" 2104 return "%s('%s/%d')" % (self.__class__.__name__, 2105 self.strategy.int_to_str(self.first), self.prefixlen)
2106 2107 #-----------------------------------------------------------------------------
2108 -class Wildcard(IPRange):
2109 """ 2110 Represents blocks of IPv4 addresses using a wildcard or glob style syntax. 2111 2112 Individual octets can be represented using the following shortcuts : 2113 2114 1. C{*} - the asterisk octet (represents values 0 through 255) 2115 2. C{'x-y'} - the hyphenated octet (represents values x through y) 2116 2117 A few basic rules also apply : 2118 2119 1. x must always be greater than y, therefore : 2120 2121 - x can only be 0 through 254 2122 - y can only be 1 through 255 2123 2124 2. only one hyphenated octet per wildcard is allowed 2125 3. only asterisks are permitted after a hyphenated octet 2126 2127 Example wildcards :: 2128 2129 '192.168.0.1' # a single address 2130 '192.168.0.0-31' # 32 addresses 2131 '192.168.0.*' # 256 addresses 2132 '192.168.0-1.*' # 512 addresses 2133 '192.168-169.*.*' # 131,072 addresses 2134 '*.*.*.*' # the whole IPv4 address space 2135 2136 Aside 2137 ===== 2138 I{Wildcard ranges are not directly equivalent to CIDR blocks as they 2139 can represent address ranges that do not fall on strict bit mask 2140 boundaries. They are very suitable in configuration files being more 2141 obvious and readable than their CIDR equivalents, especially for admins 2142 and users without much networking knowledge or experience.} 2143 2144 I{All CIDR blocks can always be represented as wildcard ranges but the 2145 reverse is not true. Wildcards are almost but not quite as flexible 2146 as IPRange objects.} 2147 """ 2148 STRATEGIES = (ST_IPV4,) 2149 ADDR_TYPES = (AT_UNSPEC, AT_INET) 2150 2151 # Descriptor registrations. 2152 strategy = StrategyDescriptor(STRATEGIES) 2153 addr_type = AddrTypeDescriptor(ADDR_TYPES) 2154 fmt = FormatDescriptor(IP) 2155
2156 - def is_valid(wildcard):
2157 """ 2158 A static method that validates wildcard address ranges. 2159 2160 @param wildcard: an IPv4 wildcard address. 2161 2162 @return: True if wildcard address is valid, False otherwise. 2163 """ 2164 #TODO: Add support for abbreviated wildcards 2165 #TODO: e.g. 192.168.*.* == 192.168.* 2166 #TODO: *.*.*.* == * 2167 #TODO: Add strict flag to enable verbose wildcard checking. 2168 seen_hyphen = False 2169 seen_asterisk = False 2170 try: 2171 octets = wildcard.split('.') 2172 if len(octets) != 4: 2173 return False 2174 for o in octets: 2175 if '-' in o: 2176 if seen_hyphen: 2177 return False 2178 seen_hyphen = True 2179 if seen_asterisk: 2180 # Asterisks cannot precede hyphenated octets. 2181 return False 2182 (o1, o2) = [int(i) for i in o.split('-')] 2183 if o1 >= o2: 2184 return False 2185 if not 0 <= o1 <= 254: 2186 return False 2187 if not 1 <= o2 <= 255: 2188 return False 2189 elif o == '*': 2190 seen_asterisk = True 2191 else: 2192 if seen_hyphen is True: 2193 return False 2194 if seen_asterisk is True: 2195 return False 2196 if not 0 <= int(o) <= 255: 2197 return False 2198 except AttributeError: 2199 return False 2200 except ValueError: 2201 return False 2202 return True
2203 2204 is_valid = staticmethod(is_valid) 2205
2206 - def __init__(self, wildcard, fmt=IP):
2207 """ 2208 Constructor. 2209 2210 @param wildcard: a valid IPv4 wildcard address 2211 2212 @param fmt: (optional) callable used on return values. 2213 Default: L{IP} objects. See L{nrange()} documentations for 2214 more details on the various options.. 2215 """ 2216 if not Wildcard.is_valid(wildcard): 2217 raise AddrFormatError('%r is not a recognised wildcard address!' \ 2218 % wildcard) 2219 t1 = [] 2220 t2 = [] 2221 2222 for octet in wildcard.split('.'): 2223 if '-' in octet: 2224 oct_tokens = octet.split('-') 2225 t1 += [oct_tokens[0]] 2226 t2 += [oct_tokens[1]] 2227 elif octet == '*': 2228 t1 += ['0'] 2229 t2 += ['255'] 2230 else: 2231 t1 += [octet] 2232 t2 += [octet] 2233 2234 first = '.'.join(t1) 2235 last = '.'.join(t2) 2236 super(Wildcard, self).__init__(first, last, fmt=fmt) 2237 2238 if self.addr_type != AT_INET: 2239 raise AddrFormatError('Wildcard syntax only supports IPv4!')
2240
2241 - def __str__(self):
2242 t1 = self.strategy.int_to_words(self.first) 2243 t2 = self.strategy.int_to_words(self.last) 2244 2245 tokens = [] 2246 2247 seen_hyphen = False 2248 seen_asterisk = False 2249 2250 for i in range(4): 2251 if t1[i] == t2[i]: 2252 # A normal octet. 2253 tokens.append(str(t1[i])) 2254 elif (t1[i] == 0) and (t2[i] == 255): 2255 # An asterisk octet. 2256 tokens.append('*') 2257 seen_asterisk = True 2258 else: 2259 # Create a hyphenated octet - only one allowed per wildcard. 2260 if not seen_asterisk: 2261 if not seen_hyphen: 2262 tokens.append('%s-%s' % (t1[i], t2[i])) 2263 seen_hyphen = True 2264 else: 2265 raise AddrFormatError('only one hyphenated octet ' \ 2266 ' per wildcard allowed!') 2267 else: 2268 raise AddrFormatError('asterisks not permitted before ' \ 2269 'hyphenated octets!') 2270 2271 return '.'.join(tokens)
2272
2273 - def __repr__(self):
2274 """@return: executable Python string to recreate equivalent object.""" 2275 return "%s(%r)" % (self.__class__.__name__, str(self))
2276 2277 2278 #-----------------------------------------------------------------------------
2279 -class IPRangeSet(set):
2280 """ 2281 B{*EXPERIMENTAL*} A customised Python set class that deals with collections 2282 of IPRange class and subclass instances. 2283 """
2284 - def __init__(self, addrs):
2285 """ 2286 Constructor. 2287 2288 @param addrs: A sequence of IPRange class/subclass instances used to 2289 pre-populate the set. Individual CIDR objects can be added and 2290 removed after instantiation with the usual set methods, add() and 2291 remove(). 2292 """ 2293 for addr in addrs: 2294 if isinstance(addr, IP): 2295 self.add(addr.cidr()) 2296 if isinstance(addr, str): 2297 try: 2298 self.add(CIDR(addr)) 2299 except: 2300 pass 2301 try: 2302 ip = IP(addr) 2303 self.add(ip.cidr()) 2304 except: 2305 pass 2306 try: 2307 wildcard = Wildcard(addr) 2308 try: 2309 self.add(wildcard.cidr()) 2310 except: 2311 self.add(wildcard) 2312 except: 2313 pass 2314 else: 2315 self.add(addr)
2316
2317 - def __contains__(self, other):
2318 """ 2319 @return: True if C{other} IP or IPRange class/subclass instance 2320 matches any of the members in this IPRangeSet, False otherwise. 2321 """ 2322 for addr in self: 2323 if other in addr: 2324 return True
2325
2326 - def any_match(self, other):
2327 """ 2328 @param other: An IP or IPRange class/subclass instance. 2329 2330 @return: The first IP or IPRange class/subclass instance object that 2331 matches C{other} from any of the members in this IPRangeSet, None 2332 otherwise. 2333 """ 2334 for addr in self: 2335 if other in addr: 2336 return addr
2337
2338 - def all_matches(self, other):
2339 """ 2340 @param other: An IP or IPRange class/subclass instance. 2341 2342 @return: All IP or IPRange class/subclass instances matching C{other} 2343 from this IPRangeSet, an empty list otherwise. 2344 """ 2345 addrs = [] 2346 for addr in self: 2347 if other in addr: 2348 addrs.append(addr) 2349 return addrs
2350
2351 - def min_match(self, other):
2352 """ 2353 @param other: An IP or IPRange class/subclass instance. 2354 2355 @return: The smallest IP or IPRange class/subclass instance matching 2356 C{other} from this IPRangeSet, None otherwise. 2357 """ 2358 addrs = self.all_matches(other) 2359 addrs.sort() 2360 return addrs[0]
2361 2362
2363 - def max_match(self, other):
2364 """ 2365 @param other: An IP or IPRange class/subclass instance. 2366 2367 @return: The largest IP or IPRange class/subclass instance matching 2368 C{other} from this IPRangeSet, None otherwise. 2369 """ 2370 addrs = self.all_matches(other) 2371 addrs.sort() 2372 return addrs[-1]
2373