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

# Source Code for Module netaddr.fallback

      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  A module containing pure-Python implementations of certain socket module

      9  constants and functions. Useful on some platforms or Python interpreters where

     10  they may be missing for various reasons.

     11  """
     12  from struct import unpack as _unpack, pack as _pack
     13

     14  AF_UNSPEC = 0
     15  AF_INET = 2
     16  AF_INET6 = 10
     17

     18  #-----------------------------------------------------------------------------

     19 -def inet_ntoa(packed_ip):
     20      """

     21      Convert an IP address from 32-bit packed binary format to string format.

     22      """
     23      if len(packed_ip) != 4 or not hasattr(packed_ip, 'split'):
     24          raise ValueError('invalid length of packed IP address string')
     25      return '%d.%d.%d.%d' % _unpack('4B', packed_ip)
     26

     27  #-----------------------------------------------------------------------------

     28 -def inet_aton(ip_string):
     29      """

     30      Convert an IP address in string format (123.45.67.89) to the 32-bit packed

     31      binary format used in low-level network functions.

     32      """
     33      if hasattr(ip_string, 'split'):
     34          invalid_addr = ValueError('illegal IP address string %r' % ip_string)
     35          #   Support for hexadecimal and octal octets.

     36          tokens = []
     37          base = 10
     38          for token in ip_string.split('.'):
     39              if token.startswith('0x'):
     40                  base = 16
     41              elif token.startswith('0') and len(token) > 1:
     42                  base = 8
     43              elif token == '':
     44                  continue
     45              try:
     46                  tokens.append(int(token, base))
     47              except ValueError:
     48                  raise invalid_addr
     49

     50          #   Zero fill missing octets.

     51          num_tokens = len(tokens)
     52          if num_tokens < 4:
     53              fill_tokens = [0] * (4 - num_tokens)
     54              if num_tokens > 1:
     55                  end_token = tokens.pop()
     56                  tokens = tokens + fill_tokens + [end_token]
     57              else:
     58                  tokens = tokens + fill_tokens
     59

     60          #   Pack octets.

     61          if len(tokens) == 4:
     62              words = []
     63              for token in tokens:
     64                  if (token >> 8) != 0:
     65                      raise invalid_addr
     66                  words.append(chr(token))
     67              return ''.join(words)
     68          else:
     69              raise invalid_addr
     70

     71      raise ValueError('argument should be a string, not %s' % type(ip_string))
     72

     73  #-----------------------------------------------------------------------------

     74 -def _compact_ipv6_tokens(tokens):
     75      new_tokens = []
     76

     77      positions = []
     78      within_run = False
     79      start_index = None
     80      num_tokens = 0
     81

     82      #   Discover all runs of zeros.

     83      for idx, token in enumerate(tokens):
     84          if token == '0':
     85              within_run = True
     86              if start_index is None:
     87                  start_index = idx
     88              num_tokens += 1
     89          else:
     90              if num_tokens > 1:
     91                  positions.append((num_tokens, start_index))
     92              within_run = False
     93              start_index = None
     94              num_tokens = 0
     95

     96          new_tokens.append(token)
     97

     98      #   Store any position not saved before loop exit.

     99      if num_tokens > 1:
    100          positions.append((num_tokens, start_index))
    101

    102      #   Replace first longest run with an empty string.

    103      if len(positions) != 0:
    104          #   Locate longest, left-most run of zeros.

    105          positions.sort(lambda x, y: cmp(x[1], y[1]))
    106          best_position = positions[0]
    107          for position in positions:
    108              if position[0] > best_position[0]:
    109                  best_position = position
    110          #   Replace chosen zero run.

    111          (length, start_idx) = best_position
    112          new_tokens = new_tokens[0:start_idx] + [''] + \

    113                       new_tokens[start_idx+length:]
    114

    115          #   Add start and end blanks so join creates '::'.

    116          if new_tokens[0] == '':
    117              new_tokens.insert(0, '')
    118

    119          if new_tokens[-1] == '':
    120              new_tokens.append('')
    121

    122      return new_tokens
    123

    124  #-----------------------------------------------------------------------------

    125 -def inet_ntop(af, packed_ip):
    126      """Convert an packed IP address of the given family to string format."""
    127      if af == AF_INET:
    128          #   IPv4.

    129          return inet_ntoa(packed_ip)
    130      elif af == AF_INET6:
    131          #   IPv6.

    132          if len(packed_ip) != 16 or not hasattr(packed_ip, 'split'):
    133              raise ValueError('invalid length of packed IP address string')
    134

    135          tokens = ['%x' % i for i in _unpack('>8H', packed_ip)]
    136

    137          #   Convert packed address to an integer value.

    138          words = list(_unpack('>8H', packed_ip))
    139          int_val = 0
    140          for i, num in enumerate(reversed(words)):
    141              word = num
    142              word = word << 16 * i
    143              int_val = int_val | word
    144

    145          if 0xffff < int_val <= 0xffffffff or int_val >> 32 == 0xffff:
    146              #   IPv4 compatible / mapped IPv6.

    147              packed_ipv4 = _pack('>2H', *[int(i, 16) for i in tokens[-2:]])
    148              ipv4_str = inet_ntoa(packed_ipv4)
    149              tokens = tokens[0:-2] + [ipv4_str]
    150

    151          return ':'.join(_compact_ipv6_tokens(tokens))
    152      else:
    153          raise ValueError('unknown address family %d' % af)
    154

    155  #-----------------------------------------------------------------------------

    156 -def inet_pton(af, ip_string):
    157      """

    158      Convert an IP address from string format to a packed string suitable for

    159      use with low-level network functions.

    160      """
    161      if af == AF_INET:
    162          #   IPv4.

    163          return inet_aton(ip_string)
    164      elif af == AF_INET6:
    165          invalid_addr = ValueError('illegal IP address string %r' % ip_string)
    166          #   IPv6.

    167          values = []
    168

    169          if not hasattr(ip_string, 'split'):
    170              raise invalid_addr
    171

    172          if '::' in ip_string:
    173              if ip_string == '::':
    174                  #   Unspecified address.

    175                  return '\x00'*16
    176              #   IPv6 compact mode.

    177              try:
    178                  prefix, suffix = ip_string.split('::')
    179              except ValueError:
    180                  raise invalid_addr
    181

    182              l_prefix = []
    183              l_suffix = []
    184

    185              if prefix != '':
    186                  l_prefix = prefix.split(':')
    187

    188              if suffix != '':
    189                  l_suffix = suffix.split(':')
    190

    191              #   IPv6 compact IPv4 compatibility mode.

    192              if len(l_suffix) and '.' in l_suffix[-1]:
    193                  ipv4_str = inet_aton(l_suffix.pop())
    194                  l_suffix.append('%x' % _unpack('>H', ipv4_str[0:2])[0])
    195                  l_suffix.append('%x' % _unpack('>H', ipv4_str[2:4])[0])
    196

    197              token_count = len(l_prefix) + len(l_suffix)
    198

    199              if not 0 <= token_count <= 8 - 1:
    200                  raise invalid_addr
    201

    202              gap_size = 8 - ( len(l_prefix) + len(l_suffix) )
    203

    204              values = [_pack('>H', int(i, 16)) for i in l_prefix] \

    205                     + ['\x00\x00' for i in range(gap_size)] \

    206                     + [_pack('>H', int(i, 16)) for i in l_suffix]
    207              try:
    208                  for token in l_prefix + l_suffix:
    209                      word = int(token, 16)
    210                      if not 0 <= word <= 0xffff:
    211                          raise invalid_addr
    212              except ValueError:
    213                  raise invalid_addr
    214          else:
    215              #   IPv6 verbose mode.

    216              if ':' in ip_string:
    217                  tokens = ip_string.split(':')
    218

    219                  if '.' in ip_string:
    220                      ipv6_prefix = tokens[:-1]
    221                      if ipv6_prefix[:-1] != ['0', '0', '0', '0', '0']:
    222                          raise invalid_addr
    223

    224                      if ipv6_prefix[-1].lower() not in ('0', 'ffff'):
    225                          raise invalid_addr
    226

    227                      #   IPv6 verbose IPv4 compatibility mode.

    228                      if len(tokens) != 7:
    229                          raise invalid_addr
    230

    231                      ipv4_str = inet_aton(tokens.pop())
    232                      tokens.append('%x' % _unpack('>H', ipv4_str[0:2])[0])
    233                      tokens.append('%x' % _unpack('>H', ipv4_str[2:4])[0])
    234

    235                      values = [_pack('>H', int(i, 16)) for i in tokens]
    236                  else:
    237                      #   IPv6 verbose mode.

    238                      if len(tokens) != 8:
    239                          raise invalid_addr
    240                  try:
    241                      tokens = [int(token, 16) for token in tokens]
    242                      for token in tokens:
    243                          if not 0 <= token <= 0xffff:
    244                              raise invalid_addr
    245

    246                  except ValueError:
    247                      raise invalid_addr
    248

    249                  values = [_pack('>H', i) for i in tokens]
    250              else:
    251                  raise invalid_addr
    252

    253          return ''.join(values)
    254      else:
    255          raise ValueError('Unknown address family %d' % af)
    256