133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck#
333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# Permission to use, copy, modify, and distribute this software and its
433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# documentation for any purpose with or without fee is hereby granted,
533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# provided that the above copyright notice and this permission notice
633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# appear in all copies.
733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck#
833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
1133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
1433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck# OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
1633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck"""IPv6 helper functions."""
1733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
1833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport re
1933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport dns.exception
2133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckimport dns.ipv4
2233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck_leading_zero = re.compile(r'0+([0-9a-f]+)')
2433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckdef inet_ntoa(address):
2633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """Convert a network format IPv6 address into text.
2733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
2833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @param address: the binary address
2933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @type address: string
3033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @rtype: string
3133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @raises ValueError: the address isn't 16 bytes long
3233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """
3333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
3433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if len(address) != 16:
3533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        raise ValueError("IPv6 addresses are 16 bytes long")
3633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    hex = address.encode('hex_codec')
3733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    chunks = []
3833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    i = 0
3933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    l = len(hex)
4033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    while i < l:
4133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        chunk = hex[i : i + 4]
4233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        # strip leading zeros.  we do this with an re instead of
4333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        # with lstrip() because lstrip() didn't support chars until
4433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        # python 2.2.2
4533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        m = _leading_zero.match(chunk)
4633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if not m is None:
4733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            chunk = m.group(1)
4833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        chunks.append(chunk)
4933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        i += 4
5033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    #
5133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    # Compress the longest subsequence of 0-value chunks to ::
5233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    #
5333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    best_start = 0
5433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    best_len = 0
5533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    start = -1
5633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    last_was_zero = False
5733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    for i in xrange(8):
5833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if chunks[i] != '0':
5933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            if last_was_zero:
6033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                end = i
6133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                current_len = end - start
6233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                if current_len > best_len:
6333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                    best_start = start
6433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                    best_len = current_len
6533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                last_was_zero = False
6633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        elif not last_was_zero:
6733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            start = i
6833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            last_was_zero = True
6933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if last_was_zero:
7033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        end = 8
7133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        current_len = end - start
7233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if current_len > best_len:
7333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            best_start = start
7433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            best_len = current_len
7533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if best_len > 0:
7633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if best_start == 0 and \
7733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck           (best_len == 6 or
7833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            best_len == 5 and chunks[5] == 'ffff'):
7933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            # We have an embedded IPv4 address
8033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            if best_len == 6:
8133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                prefix = '::'
8233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            else:
8333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                prefix = '::ffff:'
8433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            hex = prefix + dns.ipv4.inet_ntoa(address[12:])
8533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        else:
8633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            hex = ':'.join(chunks[:best_start]) + '::' + \
8733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                  ':'.join(chunks[best_start + best_len:])
8833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    else:
8933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        hex = ':'.join(chunks)
9033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    return hex
9133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
9233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck_v4_ending = re.compile(r'(.*):(\d+)\.(\d+)\.(\d+)\.(\d+)$')
9333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck_colon_colon_start = re.compile(r'::.*')
9433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck_colon_colon_end = re.compile(r'.*::$')
9533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
9633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reckdef inet_aton(text):
9733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """Convert a text format IPv6 address into network format.
9833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
9933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @param text: the textual address
10033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @type text: string
10133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @rtype: string
10233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    @raises dns.exception.SyntaxError: the text was not properly formatted
10333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    """
10433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
10533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    #
10633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    # Our aim here is not something fast; we just want something that works.
10733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    #
10833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
10933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if text == '::':
11033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        text = '0::'
11133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    #
11233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    # Get rid of the icky dot-quad syntax if we have it.
11333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    #
11433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    m = _v4_ending.match(text)
11533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if not m is None:
11633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        text = "%s:%04x:%04x" % (m.group(1),
11733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                                 int(m.group(2)) * 256 + int(m.group(3)),
11833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                                 int(m.group(4)) * 256 + int(m.group(5)))
11933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    #
12033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    # Try to turn '::<whatever>' into ':<whatever>'; if no match try to
12133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    # turn '<whatever>::' into '<whatever>:'
12233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    #
12333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    m = _colon_colon_start.match(text)
12433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if not m is None:
12533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        text = text[1:]
12633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    else:
12733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        m = _colon_colon_end.match(text)
12833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if not m is None:
12933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            text = text[:-1]
13033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    #
13133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    # Now canonicalize into 8 chunks of 4 hex digits each
13233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    #
13333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    chunks = text.split(':')
13433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    l = len(chunks)
13533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if l > 8:
13633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        raise dns.exception.SyntaxError
13733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    seen_empty = False
13833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    canonical = []
13933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    for c in chunks:
14033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        if c == '':
14133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            if seen_empty:
14233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                raise dns.exception.SyntaxError
14333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            seen_empty = True
14433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            for i in xrange(0, 8 - l + 1):
14533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                canonical.append('0000')
14633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        else:
14733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            lc = len(c)
14833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            if lc > 4:
14933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                raise dns.exception.SyntaxError
15033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            if lc != 4:
15133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck                c = ('0' * (4 - lc)) + c
15233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck            canonical.append(c)
15333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    if l < 8 and not seen_empty:
15433259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        raise dns.exception.SyntaxError
15533259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    text = ''.join(canonical)
15633259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck
15733259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    #
15833259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    # Finally we can go to binary.
15933259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    #
16033259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    try:
16133259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        return text.decode('hex_codec')
16233259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck    except TypeError:
16333259e44c8229f70ffe0cf3bb5ca9375c4feb2f9John Reck        raise dns.exception.SyntaxError
164