Package dns :: Module ipv6 |
[hide private]
[frames] | no frames]

# Source Code for Module dns.ipv6

      1  # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
      2  #
      3  # Permission to use, copy, modify, and distribute this software and its
      4  # documentation for any purpose with or without fee is hereby granted,
      5  # provided that the above copyright notice and this permission notice
      6  # appear in all copies.
      7  #
      8  # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
      9  # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10  # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
     11  # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12  # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13  # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
     14  # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15
     16  """IPv6 helper functions."""
     17
     18  import re
     19
     20  import dns.exception
     21  import dns.ipv4
     22
     23  _leading_zero = re.compile(r'0+([0-9a-f]+)')
     24
     25 -def inet_ntoa(address):
     26      """Convert a network format IPv6 address into text.
     27
     28      @param address: the binary address
     29      @type address: string
     30      @rtype: string
     31      @raises ValueError: the address isn't 16 bytes long
     32      """
     33
     34      if len(address) != 16:
     35          raise ValueError("IPv6 addresses are 16 bytes long")
     36      hex = address.encode('hex_codec')
     37      chunks = []
     38      i = 0
     39      l = len(hex)
     40      while i < l:
     41          chunk = hex[i : i + 4]
     42          # strip leading zeros.  we do this with an re instead of
     43          # with lstrip() because lstrip() didn't support chars until
     44          # python 2.2.2
     45          m = _leading_zero.match(chunk)
     46          if not m is None:
     47              chunk = m.group(1)
     48          chunks.append(chunk)
     49          i += 4
     50      #
     51      # Compress the longest subsequence of 0-value chunks to ::
     52      #
     53      best_start = 0
     54      best_len = 0
     55      start = -1
     56      last_was_zero = False
     57      for i in xrange(8):
     58          if chunks[i] != '0':
     59              if last_was_zero:
     60                  end = i
     61                  current_len = end - start
     62                  if current_len > best_len:
     63                      best_start = start
     64                      best_len = current_len
     65                  last_was_zero = False
     66          elif not last_was_zero:
     67              start = i
     68              last_was_zero = True
     69      if last_was_zero:
     70          end = 8
     71          current_len = end - start
     72          if current_len > best_len:
     73              best_start = start
     74              best_len = current_len
     75      if best_len > 1:
     76          if best_start == 0 and \
     77             (best_len == 6 or
     78              best_len == 5 and chunks[5] == 'ffff'):
     79              # We have an embedded IPv4 address
     80              if best_len == 6:
     81                  prefix = '::'
     82              else:
     83                  prefix = '::ffff:'
     84              hex = prefix + dns.ipv4.inet_ntoa(address[12:])
     85          else:
     86              hex = ':'.join(chunks[:best_start]) + '::' + \
     87                    ':'.join(chunks[best_start + best_len:])
     88      else:
     89          hex = ':'.join(chunks)
     90      return hex
     91
     92  _v4_ending = re.compile(r'(.*):(\d+\.\d+\.\d+\.\d+)$')
     93  _colon_colon_start = re.compile(r'::.*')
     94  _colon_colon_end = re.compile(r'.*::$')
     95
     96 -def inet_aton(text):
     97      """Convert a text format IPv6 address into network format.
     98
     99      @param text: the textual address
    100      @type text: string
    101      @rtype: string
    102      @raises dns.exception.SyntaxError: the text was not properly formatted
    103      """
    104
    105      #
    106      # Our aim here is not something fast; we just want something that works.
    107      #
    108
    109      if text == '::':
    110          text = '0::'
    111      #
    112      # Get rid of the icky dot-quad syntax if we have it.
    113      #
    114      m = _v4_ending.match(text)
    115      if not m is None:
    116          b = dns.ipv4.inet_aton(m.group(2))
    117          text = "%s:%02x%02x:%02x%02x" % (m.group(1), ord(b[0]), ord(b[1]),
    118                                           ord(b[2]), ord(b[3]))
    119      #
    120      # Try to turn '::<whatever>' into ':<whatever>'; if no match try to
    121      # turn '<whatever>::' into '<whatever>:'
    122      #
    123      m = _colon_colon_start.match(text)
    124      if not m is None:
    125          text = text[1:]
    126      else:
    127          m = _colon_colon_end.match(text)
    128          if not m is None:
    129              text = text[:-1]
    130      #
    131      # Now canonicalize into 8 chunks of 4 hex digits each
    132      #
    133      chunks = text.split(':')
    134      l = len(chunks)
    135      if l > 8:
    136          raise dns.exception.SyntaxError
    137      seen_empty = False
    138      canonical = []
    139      for c in chunks:
    140          if c == '':
    141              if seen_empty:
    142                  raise dns.exception.SyntaxError
    143              seen_empty = True
    144              for i in xrange(0, 8 - l + 1):
    145                  canonical.append('0000')
    146          else:
    147              lc = len(c)
    148              if lc > 4:
    149                  raise dns.exception.SyntaxError
    150              if lc != 4:
    151                  c = ('0' * (4 - lc)) + c
    152              canonical.append(c)
    153      if l < 8 and not seen_empty:
    154          raise dns.exception.SyntaxError
    155      text = ''.join(canonical)
    156
    157      #
    158      # Finally we can go to binary.
    159      #
    160      try:
    161          return text.decode('hex_codec')
    162      except TypeError:
    163          raise dns.exception.SyntaxError
    164
    165  _mapped_prefix = '\x00' * 10 + '\xff\xff'
    166
    167 -def is_mapped(address):
    168      return address.startswith(_mapped_prefix)
    169