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
     579 -    def ipv6_link_local(self):
     580          """
     581          @return: new link local IPv6 L{IP} object based on this L{EUI} using
     582              technique described in RFC 4291. B{Please Note:} this technique
     583              poses security risks in certain scenarios. Please read RFC 4941 for
     584              details. Reference: RFCs 4291 and 4941.
     585          """
     586          prefix = 'fe80:0000:0000:0000:'
     587
     588          #   Add 2 to the first octet of this EUI address (temporarily).
     589          self[0] += 2
     590
     591          if self.addr_type == AT_LINK:
     592              #   Modify MAC to make it an EUI-64.
     593              suffix = ["%02x" % i for i in self[0:3]] + ['ff', 'fe'] + \
     594                       ["%02x" % i for i in self[3:6]]
     595          else:
     596              suffix = ["%02x" % i for i in list(self)]
     597
     598          suffix = ["%02x%02x" % (int(x[0], 16), int(x[1], 16)) for x in \
     599              zip(suffix[::2], suffix[1::2])]
     600
     601          #   Subtract 2 again to return EUI to its original value.
     602          self[0] -= 2
     603
     604          eui64 = ':'.join(suffix)
     605          addr = prefix + eui64
     606          return IP(addr)
     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
     870 -    def is_link_local(self):
     871          """
     872          @return: C{True} if this IP is link-local address C{False} otherwise.
     873              Reference: RFCs 3927 and 4291.
     874          """
     875          if self.addr_type == AT_INET:
     876              return self in CIDR('169.254/16')
     877          elif self.addr_type == AT_INET6:
     878              return self in CIDR('fe80::/10')
     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