Package netaddr :: Module strategy |
[hide private]
[frames] | no frames]

# Source Code for Module netaddr.strategy

      1  #!/usr/bin/env python
      2  #-----------------------------------------------------------------------------
      3  #   Copyright (c) 2008-2009, 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 conversion logic, constants and shared strategy objects.
      9  """
     10  import struct as _struct
     11  import re as _re
     12  from netaddr.util import BYTES_TO_BITS as _BYTES_TO_BITS
     13
     14  #   Check whether we need to use fallback code or not.
     15  try:
     16      import socket as _socket
     17      #   Check for a common bug on Windows and some other socket modules.
     18      _socket.inet_aton('255.255.255.255')
     19      from socket import inet_aton as _inet_aton, \
     20                         inet_ntoa as _inet_ntoa, \
     21                         AF_INET as _AF_INET
     22  except:
     23      from netaddr.fallback import inet_aton as _inet_aton, \
     24                                   inet_ntoa as _inet_ntoa, \
     25                                   AF_INET as _AF_INET
     26  try:
     27      import socket as _socket
     28      #   These might all generate exceptions on different platforms.
     29      if not _socket.has_ipv6:
     30          raise Exception('IPv6 disabled')
     31      _socket.inet_pton
     32      _socket.AF_INET6
     33      from socket import inet_pton as _inet_pton, \
     34                         inet_ntop as _inet_ntop, \
     35                         AF_INET6 as _AF_INET6
     36  except:
     37      from netaddr.fallback import inet_pton as _inet_pton, \
     38                                   inet_ntop as _inet_ntop, \
     39                                   AF_INET6 as _AF_INET6
     40
     41  from netaddr import BIG_ENDIAN_PLATFORM, AT_UNSPEC, AT_INET, AT_INET6, \
     42                      AT_LINK, AT_EUI64, AT_NAMES, AddrFormatError
     43
     44  #-----------------------------------------------------------------------------
     45 -class AddrStrategy(object):
     46      """Basic support for common operations performed on each address type"""
     47
     48      #: Lookup table for struct module format strings.
     49      STRUCT_FORMATS = {
     50           8 : 'B',   # unsigned char
     51          16 : 'H',   # unsigned short
     52          32 : 'I',   # unsigned int
     53      }
     54
     55 -    def __init__(self, width, word_size, word_sep, word_fmt='%x',
     56                   addr_type=AT_UNSPEC, word_base=16):
     57          """
     58          Constructor.
     59
     60          @param width: size of address in bits.
     61              (e.g. 32 - IPv4, 48 - MAC, 128 - IPv6)
     62
     63          @param word_size: size of each word.
     64              (e.g. 8 - octets, 16 - hextets)
     65
     66          @param word_sep: separator between each word.
     67              (e.g. '.' - IPv4, ':' - IPv6, '-' - EUI-48)
     68
     69          @param word_fmt: format string for each word.
     70              (Default: '%x')
     71
     72          @param addr_type: address type.
     73              (Default: AT_UNSPEC)
     74
     75          @param word_base: number base used to convert each word using int().
     76              (Default: 16)
     77          """
     78
     79          self.width = width
     80          self.max_int = 2 ** width - 1
     81          self.word_size = word_size
     82          self.num_words = width / word_size
     83          self.max_word = 2 ** word_size - 1
     84          self.word_sep = word_sep
     85          self.word_fmt  = word_fmt
     86          self.word_base = word_base
     87          self.addr_type = addr_type
     88
     89          try:
     90              self.name = AT_NAMES[addr_type]
     91          except KeyError:
     92              self.name = AT_NAMES[AT_UNSPEC]
     93
     94 -    def __repr__(self):
     95          """@return: executable Python string to recreate equivalent object"""
     96          return "%s(%r, %r, %r, %r, %r)" %  (self.__class__.__name__,
     97              self.width, self.word_size, self.word_sep, self.addr_type,
     98              self.word_base)
     99
    100      #-------------------------------------------------------------------------
    101      #   Binary methods.
    102      #-------------------------------------------------------------------------
    103
    104 -    def valid_bits(self, bits):
    105          """
    106          @param bits: A network address in readable binary form.
    107
    108          @return: C{True} if network address is valid for this address type,
    109              C{False} otherwise.
    110          """
    111          if not isinstance(bits, (str, unicode)):
    112              return False
    113
    114          bits = bits.replace(self.word_sep, '')
    115
    116          if len(bits) != self.width:
    117              return False
    118
    119          try:
    120              if 0 <= int(bits, 2) <= self.max_int:
    121                  return True
    122          except ValueError:
    123              return False
    124          return False
    125
    126 -    def bits_to_int(self, bits):
    127          """
    128          @param bits: A network address in readable binary form.
    129
    130          @return: An unsigned integer that is equivalent to value represented
    131              by network address in readable binary form.
    132          """
    133          if not self.valid_bits(bits):
    134              raise ValueError('%r is not a valid binary form string for ' \
    135                  'address type!' % bits)
    136
    137          return int(bits.replace(self.word_sep, ''), 2)
    138
    139      #-------------------------------------------------------------------------
    140      #   Integer methods.
    141      #-------------------------------------------------------------------------
    142
    143 -    def valid_int(self, int_val):
    144          """
    145          @param int_val: An unsigned integer.
    146
    147          @return: C{True} if integer falls within the boundaries of this
    148              address type, C{False} otherwise.
    149          """
    150          if not isinstance(int_val, (int, long)):
    151              return False
    152
    153          return 0 <= int_val <= self.max_int
    154
    155 -    def int_to_str(self, int_val):
    156          """
    157          @param int_val: An unsigned integer.
    158
    159          @return: A network address in string form that is equivalent to value
    160              represented by an unsigned integer.
    161          """
    162          words = self.int_to_words(int_val)
    163          tokens = [self.word_fmt % i for i in words]
    164          addr = self.word_sep.join(tokens)
    165
    166          return addr
    167
    168 -    def int_to_bits(self, int_val, word_sep=None):
    169          """
    170          @param int_val: An unsigned integer.
    171
    172          @param word_sep: (optional) the separator to insert between words.
    173              Default: None - use default separator for address type.
    174
    175          @return: A network address in readable binary form that is equivalent
    176              to value represented by an unsigned integer.
    177          """
    178          bit_words = []
    179
    180          for word in self.int_to_words(int_val):
    181              bits = []
    182              while word:
    183                  bits.append(_BYTES_TO_BITS[word&255])
    184                  word >>= 8
    185              bits.reverse()
    186              bit_str = ''.join(bits) or '0'*self.word_size
    187              bits = ('0'*self.word_size+bit_str)[-self.word_size:]
    188              bit_words.append(bits)
    189
    190          if word_sep is not None:
    191              #   Custom separator.
    192              if not hasattr(word_sep, 'join'):
    193                  raise ValueError('Word separator must be a string!')
    194              return word_sep.join(bit_words)
    195
    196          #   Default separator.
    197          return self.word_sep.join(bit_words)
    198
    199 -    def int_to_bin(self, int_val):
    200          """
    201          @param int_val: An unsigned integer.
    202
    203          @return: A network address in standard binary representation format
    204              that is equivalent to integer address value. Essentially a back
    205              port of the bin() builtin in Python 2.6.x and higher.
    206          """
    207          bit_words = []
    208
    209          for word in self.int_to_words(int_val):
    210              bits = []
    211              while word:
    212                  bits.append(_BYTES_TO_BITS[word&255])
    213                  word >>= 8
    214              bits.reverse()
    215              bit_str = ''.join(bits) or '0'*self.word_size
    216              bits = ('0'*self.word_size+bit_str)[-self.word_size:]
    217              bit_words.append(bits)
    218
    219          return '0b' + _re.sub(r'^[0]+([01]+)$', r'\1', ''.join(bit_words))
    220
    221 -    def int_to_words(self, int_val, num_words=None, word_size=None):
    222          """
    223          @param int_val: An unsigned integer to be divided up into words.
    224
    225          @param num_words: (optional) number of words expected in return value
    226              tuple. Uses address type default if not specified.
    227
    228          @param word_size: (optional) size/width of individual words (in bits).
    229               Uses address type default if not specified.
    230          """
    231          if not self.valid_int(int_val):
    232              raise IndexError('integer %r is out of bounds!' % hex(int_val))
    233
    234          #   Set defaults for optional args.
    235          if num_words is None:
    236              num_words = self.num_words
    237          if word_size is None:
    238              word_size = self.word_size
    239
    240          max_word_size = 2 ** word_size - 1
    241
    242          words = []
    243          for _ in range(num_words):
    244              word = int_val & max_word_size
    245              words.append(int(word))
    246              int_val >>= word_size
    247
    248          return tuple(reversed(words))
    249
    250 -    def int_to_packed(self, int_val):
    251          """
    252          @param int_val: the integer to be packed.
    253
    254          @return: a packed string that is equivalent to value represented by an
    255          unsigned integer.
    256          """
    257
    258          words = self.int_to_words(int_val, self.num_words, self.word_size)
    259
    260          try:
    261              fmt = '>%d%s' % (self.num_words, AddrStrategy.STRUCT_FORMATS[
    262                  self.word_size])
    263          except KeyError:
    264              raise ValueError('unsupported word size: %d!' % self.word_size)
    265
    266          return _struct.pack(fmt, *words)
    267
    268
    269      #-------------------------------------------------------------------------
    270      #   Packed string methods.
    271      #-------------------------------------------------------------------------
    272
    273 -    def packed_to_int(self, packed_int):
    274          """
    275          @param packed_int: a packed string containing an unsigned integer.
    276              It is assumed that the string is packed in network byte order.
    277
    278          @return: An unsigned integer equivalent to value of network address
    279              represented by packed binary string.
    280          """
    281          try:
    282              fmt = '>%d%s' % (self.num_words, AddrStrategy.STRUCT_FORMATS[
    283                  self.word_size])
    284          except KeyError:
    285              raise ValueError('unsupported word size: %d!' % self.word_size)
    286
    287          words = list(_struct.unpack(fmt, packed_int))
    288
    289          int_val = 0
    290          for i, num in enumerate(reversed(words)):
    291              word = num
    292              word = word << self.word_size * i
    293              int_val = int_val | word
    294
    295          return int_val
    296
    297      #-------------------------------------------------------------------------
    298      #   String methods.
    299      #-------------------------------------------------------------------------
    300
    301 -    def valid_str(self, addr):
    302          """
    303          @param addr: A network address in string form.
    304
    305          @return: C{True} if network address in string form is valid for this
    306              address type, C{False} otherwise.
    307          """
    308          if not isinstance(addr, (str, unicode)):
    309              return False
    310
    311          tokens = addr.split(self.word_sep)
    312          if len(tokens) != self.num_words:
    313              return False
    314
    315          try:
    316              for token in tokens:
    317                  if not 0 <= int(token, self.word_base) <= \
    318                         self.max_word:
    319                      return False
    320          except TypeError:
    321              return False
    322          except ValueError:
    323              return False
    324          return True
    325
    326 -    def str_to_int(self, addr):
    327          """
    328          @param addr: A network address in string form.
    329
    330          @return: An unsigned integer equivalent to value represented by network
    331              address in string form.
    332          """
    333          if not self.valid_str(addr):
    334              raise ValueError('%r is not a recognised string representation' \
    335                  ' of this address type!' % addr)
    336
    337          tokens = addr.split(self.word_sep)
    338          words = [ int(token, self.word_base) for token in tokens ]
    339
    340          return self.words_to_int(words)
    341
    342      #-------------------------------------------------------------------------
    343      #   Word list methods.
    344      #-------------------------------------------------------------------------
    345
    346 -    def valid_words(self, words):
    347          """
    348          @param words: A sequence containing integer word values.
    349
    350          @return: C{True} if word sequence is valid for this address type,
    351              C{False} otherwise.
    352          """
    353          if not hasattr(words, '__iter__'):
    354              return False
    355
    356          if len(words) != self.num_words:
    357              return False
    358
    359          for i in words:
    360              if not isinstance(i, (int, long)):
    361                  return False
    362
    363              if not 0 <= i <= self.max_word:
    364                  return False
    365          return True
    366
    367 -    def words_to_int(self, words):
    368          """
    369          @param words: A list or tuple containing integer word values.
    370
    371          @return: An unsigned integer that is equivalent to value represented
    372              by word sequence.
    373          """
    374          if not self.valid_words(words):
    375              raise ValueError('%r is not a valid word list!' % words)
    376
    377          int_val = 0
    378          for i, num in enumerate(reversed(words)):
    379              word = num
    380              word = word << self.word_size * i
    381              int_val = int_val | word
    382
    383          return int_val
    384
    385  #-----------------------------------------------------------------------------
    386 -class IPv4Strategy(AddrStrategy):
    387      """An L{AddrStrategy} for IPv4 address processing."""
    388 -    def __init__(self):
    389          """Constructor."""
    390          super(IPv4Strategy, self).__init__(width=32, word_size=8,
    391                word_fmt='%d', word_sep='.', addr_type=AT_INET, word_base=10)
    392
    393 -    def valid_str(self, addr):
    394          """
    395          @param addr: An IP address in presentation (string) format.
    396
    397          @return: C{True} if network address in string form is valid for this
    398              address type, C{False} otherwise.
    399          """
    400          if addr == '':
    401              raise AddrFormatError('Empty strings are not supported!')
    402
    403          try:
    404              _inet_aton(addr)
    405          except:
    406              return False
    407          return True
    408
    409 -    def str_to_int(self, addr):
    410          """
    411          @param addr: An IPv4 dotted decimal address in string form.
    412
    413          @return: An unsigned integer that is equivalent to value represented
    414              by the IPv4 dotted decimal address string.
    415          """
    416          if addr == '':
    417              raise AddrFormatError('Empty strings are not supported!')
    418          try:
    419              return _struct.unpack('>I', _inet_aton(addr))[0]
    420          except:
    421              raise AddrFormatError('%r is not a valid IPv4 address string!' \
    422                  % addr)
    423
    424 -    def int_to_str(self, int_val):
    425          """
    426          @param int_val: An unsigned integer.
    427
    428          @return: An IPv4 dotted decimal address string that is equivalent to
    429              value represented by a 32 bit unsigned integer.
    430          """
    431          if self.valid_int(int_val):
    432              return _inet_ntoa(_struct.pack('>I', int_val))
    433          else:
    434              raise ValueError('%r is not a valid 32-bit unsigned integer!' \
    435                  % int_val)
    436
    437 -    def int_to_words(self, int_val, num_words=None, word_size=None):
    438          """
    439          @param int_val: An unsigned integer.
    440
    441          @param num_words: (unused) *** interface compatibility only ***
    442
    443          @param word_size: (unused) *** interface compatibility only ***
    444
    445          @return: An integer word (octet) sequence that is equivalent to value
    446              represented by an unsigned integer.
    447          """
    448          if not self.valid_int(int_val):
    449              raise ValueError('%r is not a valid integer value supported ' \
    450                  'by this address type!' % int_val)
    451          return _struct.unpack('4B', _struct.pack('>I', int_val))
    452
    453 -    def words_to_int(self, octets):
    454          """
    455          @param octets: A list or tuple containing integer octets.
    456
    457          @return: An unsigned integer that is equivalent to value represented
    458              by word (octet) sequence.
    459          """
    460          if not self.valid_words(octets):
    461              raise ValueError('%r is not a valid octet list for an IPv4 ' \
    462                  'address!' % octets)
    463          return _struct.unpack('>I', _struct.pack('4B', *octets))[0]
    464
    465 -    def int_to_arpa(self, int_val):
    466          """
    467          @param int_val: An unsigned integer.
    468
    469          @return: The reverse DNS lookup for an IPv4 address in network byte
    470              order integer form.
    471          """
    472          words = ["%d" % i for i in self.int_to_words(int_val)]
    473          words.reverse()
    474          words.extend(['in-addr', 'arpa', ''])
    475          return '.'.join(words)
    476
    477  #-----------------------------------------------------------------------------
    478 -class IPv6Strategy(AddrStrategy):
    479      """
    480      An L{AddrStrategy} for IPv6 address processing.
    481
    482      Implements the operations that can be performed on an IPv6 network address
    483      in accordance with RFC 4291.
    484      """
    485 -    def __init__(self):
    486          """Constructor."""
    487          super(IPv6Strategy, self).__init__(addr_type=AT_INET6,
    488              width=128, word_size=16, word_fmt='%x', word_sep=':')
    489
    490 -    def valid_str(self, addr):
    491          """
    492          @param addr: An IPv6 address in string form.
    493
    494          @return: C{True} if IPv6 network address string is valid, C{False}
    495              otherwise.
    496          """
    497          if addr == '':
    498              raise AddrFormatError('Empty strings are not supported!')
    499
    500          try:
    501              _inet_pton(_AF_INET6, addr)
    502          except _socket.error:
    503              return False
    504          except TypeError:
    505              return False
    506          except ValueError:
    507              return False
    508          return True
    509
    510 -    def str_to_int(self, addr):
    511          """
    512          @param addr: An IPv6 address in string form.
    513
    514          @return: The equivalent unsigned integer for a given IPv6 address.
    515          """
    516          if addr == '':
    517              raise AddrFormatError('Empty strings are not supported!')
    518          try:
    519              packed_int = _inet_pton(_AF_INET6, addr)
    520              return self.packed_to_int(packed_int)
    521          except Exception, e:
    522              raise AddrFormatError('%r is not a valid IPv6 address string!' \
    523                  % addr)
    524
    525 -    def int_to_str(self, int_val, compact=True, word_fmt=None):
    526          """
    527          @param int_val: An unsigned integer.
    528
    529          @param compact: (optional) A boolean flag indicating if compact
    530              formatting should be used. If True, this method uses the '::'
    531              string to represent the first adjacent group of words with a value
    532              of zero. Default: True
    533
    534          @param word_fmt: (optional) The Python format string used to override
    535              formatting for each word. Only applies when compact is False.
    536
    537          @return: The IPv6 string form equal to the unsigned integer provided.
    538          """
    539          try:
    540              packed_int = self.int_to_packed(int_val)
    541              if compact:
    542                  #   Default return value.
    543                  return _inet_ntop(_AF_INET6, packed_int)
    544              else:
    545                  #   Custom return value.
    546                  if word_fmt is None:
    547                      word_fmt = self.word_fmt
    548                  words = list(_struct.unpack('>8H', packed_int))
    549                  tokens = [word_fmt % word for word in words]
    550                  return self.word_sep.join(tokens)
    551          except Exception, e:
    552              raise ValueError('%r is not a valid 128-bit unsigned integer!' \
    553                  % int_val)
    554
    555 -    def int_to_packed(self, int_val):
    556          """
    557          @param int_val: the integer to be packed.
    558
    559          @return: a packed string that is equivalent to value represented by an
    560          unsigned integer (in network byte order).
    561          """
    562          #   Here we've over-ridden the normal values to get the fastest
    563          #   possible conversion speeds. Still quite slow versus IPv4 speed ups.
    564          num_words = 4
    565          word_size = 32
    566
    567          words = self.int_to_words(int_val, num_words, word_size)
    568
    569          try:
    570              fmt = '>%d%s'% (num_words, AddrStrategy.STRUCT_FORMATS[word_size])
    571          except KeyError:
    572              raise ValueError('unsupported word size: %d!' % word_size)
    573
    574          return _struct.pack(fmt, *words)
    575
    576 -    def packed_to_int(self, packed_int):
    577          """
    578          @param packed_int: a packed string containing an unsigned integer.
    579              It is assumed that string is packed in network byte order.
    580
    581          @return: An unsigned integer that is equivalent to value of network
    582              address represented by packed binary string.
    583          """
    584          #   Here we've over-ridden the normal values to get the fastest
    585          #   conversion speeds. Still quite slow versus IPv4 speed ups.
    586          num_words = 4
    587          word_size = 32
    588
    589          try:
    590              fmt = '>%d%s'% (num_words, AddrStrategy.STRUCT_FORMATS[word_size])
    591          except KeyError:
    592              raise ValueError('unsupported word size: %d!' % word_size)
    593
    594          words = list(_struct.unpack(fmt, packed_int))
    595
    596          int_val = 0
    597          for i, num in enumerate(reversed(words)):
    598              word = num
    599              word = word << word_size * i
    600              int_val = int_val | word
    601
    602          return int_val
    603
    604 -    def int_to_arpa(self, int_val):
    605          """
    606          @param int_val: An unsigned integer.
    607
    608          @return: The reverse DNS lookup for an IPv6 address in network byte
    609              order integer form.
    610          """
    611          addr = self.int_to_str(int_val, compact=False, word_fmt='%.4x')
    612          tokens = list(addr.replace(':', ''))
    613          tokens.reverse()
    614          #   We won't support ip6.int here - see RFC 3152 for details.
    615          tokens = tokens + ['ip6', 'arpa', '']
    616          return '.'.join(tokens)
    617
    618  #-----------------------------------------------------------------------------
    619 -class EUI48Strategy(AddrStrategy):
    620      """
    621      Implements the operations that can be performed on an IEEE 48-bit EUI
    622      (Extended Unique Identifer) a.k.a. a MAC (Media Access Control) layer 2
    623      address.
    624
    625      Supports all common (and some less common MAC string formats including
    626      Cisco's 'triple hextet' format and also bare MACs that contain no
    627      delimiters.
    628      """
    629      #   Regular expression to match the numerous different MAC formats.
    630      RE_MAC_FORMATS = (
    631          #   2 bytes x 6 (UNIX, Windows, EUI-48)
    632          '^' + ':'.join(['([0-9A-F]{1,2})'] * 6) + '$',
    633          '^' + '-'.join(['([0-9A-F]{1,2})'] * 6) + '$',
    634
    635          #   4 bytes x 3 (Cisco)
    636          '^' + ':'.join(['([0-9A-F]{1,4})'] * 3) + '$',
    637          '^' + '-'.join(['([0-9A-F]{1,4})'] * 3) + '$',
    638
    639          #   6 bytes x 2 (PostgreSQL)
    640          '^' + '-'.join(['([0-9A-F]{5,6})'] * 2) + '$',
    641          '^' + ':'.join(['([0-9A-F]{5,6})'] * 2) + '$',
    642
    643          #   12 bytes (bare, no delimiters)
    644          '^(' + ''.join(['[0-9A-F]'] * 12) + ')$',
    645          '^(' + ''.join(['[0-9A-F]'] * 11) + ')$',
    646      )
    647      #   For efficiency, replace each string regexp with its compiled
    648      #   equivalent.
    649      RE_MAC_FORMATS = [_re.compile(_, _re.IGNORECASE) for _ in RE_MAC_FORMATS]
    650
    651 -    def __init__(self, word_fmt='%.2X', word_sep='-'):
    652          """
    653          Constructor.
    654
    655          @param word_sep: separator between each word.
    656              (Default: '-')
    657
    658          @param word_fmt: format string for each hextet.
    659              (Default: '%02x')
    660          """
    661          super(self.__class__, self).__init__(addr_type=AT_LINK, width=48,
    662                word_size=8, word_fmt=word_fmt, word_sep=word_sep)
    663
    664
    665 -    def reset(self):
    666          """
    667          Resets the internal state of this strategy to safe default values.
    668          """
    669          #   These are the settings for EUI-48 specific formatting.
    670          self.width = 48
    671          self.max_int = 2 ** self.width - 1
    672          self.word_size = 8
    673          self.num_words = self.width / self.word_size
    674          self.max_word = 2 ** self.word_size - 1
    675          self.word_sep = '-'
    676          self.word_fmt  = '%.2X'
    677          self.word_base = 16
    678          self.addr_type = AT_LINK
    679
    680 -    def valid_str(self, addr):
    681          """
    682          @param addr: An EUI-48 or MAC address in string form.
    683
    684          @return: C{True} if MAC address string is valid, C{False} otherwise.
    685          """
    686          if not isinstance(addr, (str, unicode)):
    687              return False
    688
    689          for regexp in EUI48Strategy.RE_MAC_FORMATS:
    690              match_result = regexp.findall(addr)
    691              if len(match_result) != 0:
    692                  return True
    693          return False
    694
    695 -    def str_to_int(self, addr):
    696          """
    697          @param addr: An EUI-48 or MAC address in string form.
    698
    699          @return: An unsigned integer that is equivalent to value represented
    700              by EUI-48/MAC string address.
    701          """
    702          words = []
    703          if isinstance(addr, (str, unicode)):
    704              found_match = False
    705              for regexp in EUI48Strategy.RE_MAC_FORMATS:
    706                  match_result = regexp.findall(addr)
    707                  if len(match_result) != 0:
    708                      found_match = True
    709                      if isinstance(match_result[0], tuple):
    710                          words = match_result[0]
    711                      else:
    712                          words = (match_result[0],)
    713                      break
    714              if not found_match:
    715                  raise AddrFormatError('%r is not a supported MAC format!' \
    716                      % addr)
    717          else:
    718              raise TypeError('%r is not str() or unicode()!' % addr)
    719
    720          int_val = None
    721
    722          if len(words) == 6:
    723              #   2 bytes x 6 (UNIX, Windows, EUI-48)
    724              int_val = int(''.join(['%.2x' % int(w, 16) for w in words]), 16)
    725          elif len(words) == 3:
    726              #   4 bytes x 3 (Cisco)
    727              int_val = int(''.join(['%.4x' % int(w, 16) for w in words]), 16)
    728          elif len(words) == 2:
    729              #   6 bytes x 2 (PostgreSQL)
    730              int_val = int(''.join(['%.6x' % int(w, 16) for w in words]), 16)
    731          elif len(words) == 1:
    732              #   12 bytes (bare, no delimiters)
    733              int_val = int('%012x' % int(words[0], 16), 16)
    734          else:
    735              raise AddrFormatError('unexpected word count in MAC address %r!' \
    736                  % addr)
    737
    738          return int_val
    739
    740 -    def int_to_str(self, int_val, word_sep=None, word_fmt=None):
    741          """
    742          @param int_val: An unsigned integer.
    743
    744          @param word_sep: (optional) The separator used between words in an
    745              address string.
    746
    747          @param word_fmt: (optional) A Python format string used to format
    748              each word of address.
    749
    750          @return: A MAC address in string form that is equivalent to value
    751              represented by an unsigned integer.
    752          """
    753          _word_sep = self.word_sep
    754          if word_sep is not None:
    755              _word_sep = word_sep
    756
    757          _word_fmt = self.word_fmt
    758          if word_fmt is not None:
    759              _word_fmt = word_fmt
    760
    761          words = self.int_to_words(int_val)
    762          tokens = [_word_fmt % i for i in words]
    763          addr = _word_sep.join(tokens)
    764
    765          return addr
    766
    767  #-----------------------------------------------------------------------------
    768  #   Shared strategy objects for supported address types.
    769  #-----------------------------------------------------------------------------
    770
    771  #: A shared strategy object supporting all operations on IPv4 addresses.
    772  ST_IPV4 = IPv4Strategy()
    773
    774  #: A shared strategy object supporting all operations on IPv6 addresses.
    775  ST_IPV6 = IPv6Strategy()
    776
    777  #: A shared strategy object supporting all operations on EUI-48/MAC addresses.
    778  ST_EUI48 = EUI48Strategy()
    779
    780  #: A shared strategy object supporting all operations on EUI-64 addresses.
    781  ST_EUI64 = AddrStrategy(addr_type=AT_EUI64, width=64, word_size=8, \
    782                           word_fmt='%.2X', word_sep='-')
    783
    784  #-----------------------------------------------------------------------------
    785  if __name__ == '__main__':
    786      pass
    787