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  import binascii
     20
     21  import dns.exception
     22  import dns.ipv4
     23  from ._compat import xrange, binary_type, maybe_decode
     24
     25  _leading_zero = re.compile(b'0+([0-9a-f]+)')
     26
     27 -def inet_ntoa(address):
     28      """Convert a network format IPv6 address into text.
     29
     30      @param address: the binary address
     31      @type address: string
     32      @rtype: string
     33      @raises ValueError: the address isn't 16 bytes long
     34      """
     35
     36      if len(address) != 16:
     37          raise ValueError("IPv6 addresses are 16 bytes long")
     38      hex = binascii.hexlify(address)
     39      chunks = []
     40      i = 0
     41      l = len(hex)
     42      while i < l:
     43          chunk = hex[i : i + 4]
     44          # strip leading zeros.  we do this with an re instead of
     45          # with lstrip() because lstrip() didn't support chars until
     46          # python 2.2.2
     47          m = _leading_zero.match(chunk)
     48          if not m is None:
     49              chunk = m.group(1)
     50          chunks.append(chunk)
     51          i += 4
     52      #
     53      # Compress the longest subsequence of 0-value chunks to ::
     54      #
     55      best_start = 0
     56      best_len = 0
     57      start = -1
     58      last_was_zero = False
     59      for i in xrange(8):
     60          if chunks[i] != b'0':
     61              if last_was_zero:
     62                  end = i
     63                  current_len = end - start
     64                  if current_len > best_len:
     65                      best_start = start
     66                      best_len = current_len
     67                  last_was_zero = False
     68          elif not last_was_zero:
     69              start = i
     70              last_was_zero = True
     71      if last_was_zero:
     72          end = 8
     73          current_len = end - start
     74          if current_len > best_len:
     75              best_start = start
     76              best_len = current_len
     77      if best_len > 1:
     78          if best_start == 0 and \
     79             (best_len == 6 or
     80              best_len == 5 and chunks[5] == b'ffff'):
     81              # We have an embedded IPv4 address
     82              if best_len == 6:
     83                  prefix = b'::'
     84              else:
     85                  prefix = b'::ffff:'
     86              hex = prefix + dns.ipv4.inet_ntoa(address[12:])
     87          else:
     88              hex = b':'.join(chunks[:best_start]) + b'::' + \
     89                    b':'.join(chunks[best_start + best_len:])
     90      else:
     91          hex = b':'.join(chunks)
     92      return maybe_decode(hex)
     93
     94  _v4_ending = re.compile(b'(.*):(\d+\.\d+\.\d+\.\d+)$')
     95  _colon_colon_start = re.compile(b'::.*')
     96  _colon_colon_end = re.compile(b'.*::$')
     97
     98 -def inet_aton(text):
     99      """Convert a text format IPv6 address into network format.
    100
    101      @param text: the textual address
    102      @type text: string
    103      @rtype: string
    104      @raises dns.exception.SyntaxError: the text was not properly formatted
    105      """
    106
    107      #
    108      # Our aim here is not something fast; we just want something that works.
    109      #
    110      if not isinstance(text, binary_type):
    111          text = text.encode()
    112
    113      if text == b'::':
    114          text = b'0::'
    115      #
    116      # Get rid of the icky dot-quad syntax if we have it.
    117      #
    118      m = _v4_ending.match(text)
    119      if not m is None:
    120          b = bytearray(dns.ipv4.inet_aton(m.group(2)))
    121          text = (u"%s:%02x%02x:%02x%02x" % (m.group(1).decode(), b[0], b[1],
    122                                             b[2], b[3])).encode()
    123      #
    124      # Try to turn '::<whatever>' into ':<whatever>'; if no match try to
    125      # turn '<whatever>::' into '<whatever>:'
    126      #
    127      m = _colon_colon_start.match(text)
    128      if not m is None:
    129          text = text[1:]
    130      else:
    131          m = _colon_colon_end.match(text)
    132          if not m is None:
    133              text = text[:-1]
    134      #
    135      # Now canonicalize into 8 chunks of 4 hex digits each
    136      #
    137      chunks = text.split(b':')
    138      l = len(chunks)
    139      if l > 8:
    140          raise dns.exception.SyntaxError
    141      seen_empty = False
    142      canonical = []
    143      for c in chunks:
    144          if c == b'':
    145              if seen_empty:
    146                  raise dns.exception.SyntaxError
    147              seen_empty = True
    148              for i in xrange(0, 8 - l + 1):
    149                  canonical.append(b'0000')
    150          else:
    151              lc = len(c)
    152              if lc > 4:
    153                  raise dns.exception.SyntaxError
    154              if lc != 4:
    155                  c = (b'0' * (4 - lc)) + c
    156              canonical.append(c)
    157      if l < 8 and not seen_empty:
    158          raise dns.exception.SyntaxError
    159      text = b''.join(canonical)
    160
    161      #
    162      # Finally we can go to binary.
    163      #
    164      try:
    165          return binascii.unhexlify(text)
    166      except (binascii.Error, TypeError):
    167          raise dns.exception.SyntaxError
    168
    169  _mapped_prefix = b'\x00' * 10 + b'\xff\xff'
    170
    171 -def is_mapped(address):
    172      return address.startswith(_mapped_prefix)
    173