1#-----------------------------------------------------------------------------
2#   Copyright (c) 2008 by David P. D. Moss. All rights reserved.
3#
4#   Released under the BSD license. See the LICENSE file for details.
5#-----------------------------------------------------------------------------
6"""
7IPv6 address logic.
8"""
9import struct as _struct
10
11OPT_IMPORTS = False
12
13#   Check whether we need to use fallback code or not.
14try:
15    import socket as _socket
16    #   These might all generate exceptions on different platforms.
17    if not _socket.has_ipv6:
18        raise Exception('IPv6 disabled')
19    _socket.inet_pton
20    _socket.AF_INET6
21    from _socket import (inet_pton as _inet_pton, inet_ntop as _inet_ntop,
22                         AF_INET6)
23    OPT_IMPORTS = True
24except Exception:
25    from netaddr.fbsocket import (inet_pton as _inet_pton, inet_ntop as _inet_ntop,
26                                 AF_INET6)
27
28from netaddr.core import AddrFormatError
29from netaddr.strategy import (
30    valid_words as _valid_words, int_to_words as _int_to_words,
31    words_to_int as _words_to_int, valid_bits as _valid_bits,
32    bits_to_int as _bits_to_int, int_to_bits as _int_to_bits,
33    valid_bin as _valid_bin, int_to_bin as _int_to_bin,
34    bin_to_int as _bin_to_int)
35
36#: The width (in bits) of this address type.
37width = 128
38
39#: The individual word size (in bits) of this address type.
40word_size = 16
41
42#: The separator character used between each word.
43word_sep = ':'
44
45#: The AF_* constant value of this address type.
46family = AF_INET6
47
48#: A friendly string name address type.
49family_name = 'IPv6'
50
51#: The version of this address type.
52version = 6
53
54#: The number base to be used when interpreting word values as integers.
55word_base = 16
56
57#: The maximum integer value that can be represented by this address type.
58max_int = 2 ** width - 1
59
60#: The number of words in this address type.
61num_words = width // word_size
62
63#: The maximum integer value for an individual word in this address type.
64max_word = 2 ** word_size - 1
65
66#: A dictionary mapping IPv6 CIDR prefixes to the equivalent netmasks.
67prefix_to_netmask = dict(
68    [(i, max_int ^ (2 ** (width - i) - 1)) for i in range(0, width+1)])
69
70#: A dictionary mapping IPv6 netmasks to their equivalent CIDR prefixes.
71netmask_to_prefix = dict(
72    [(max_int ^ (2 ** (width - i) - 1), i) for i in range(0, width+1)])
73
74#: A dictionary mapping IPv6 CIDR prefixes to the equivalent hostmasks.
75prefix_to_hostmask = dict(
76    [(i, (2 ** (width - i) - 1)) for i in range(0, width+1)])
77
78#: A dictionary mapping IPv6 hostmasks to their equivalent CIDR prefixes.
79hostmask_to_prefix = dict(
80    [((2 ** (width - i) - 1), i) for i in range(0, width+1)])
81
82#-----------------------------------------------------------------------------
83#   Dialect classes.
84#-----------------------------------------------------------------------------
85
86class ipv6_compact(object):
87    """An IPv6 dialect class - compact form."""
88    #: The format string used to converting words into string values.
89    word_fmt = '%x'
90
91    #: Boolean flag indicating if IPv6 compaction algorithm should be used.
92    compact = True
93
94class ipv6_full(ipv6_compact):
95    """An IPv6 dialect class - 'all zeroes' form."""
96
97    #: Boolean flag indicating if IPv6 compaction algorithm should be used.
98    compact = False
99
100class ipv6_verbose(ipv6_compact):
101    """An IPv6 dialect class - extra wide 'all zeroes' form."""
102
103    #: The format string used to converting words into string values.
104    word_fmt = '%.4x'
105
106    #: Boolean flag indicating if IPv6 compaction algorithm should be used.
107    compact = False
108
109
110def valid_str(addr, flags=0):
111    """
112    :param addr: An IPv6 address in presentation (string) format.
113
114    :param flags: decides which rules are applied to the interpretation of the
115        addr value. Future use - currently has no effect.
116
117    :return: ``True`` if IPv6 address is valid, ``False`` otherwise.
118    """
119    if addr == '':
120        raise AddrFormatError('Empty strings are not supported!')
121
122    try:
123        _inet_pton(AF_INET6, addr)
124    except:
125        return False
126    return True
127
128
129def str_to_int(addr, flags=0):
130    """
131    :param addr: An IPv6 address in string form.
132
133    :param flags: decides which rules are applied to the interpretation of the
134        addr value. Future use - currently has no effect.
135
136    :return: The equivalent unsigned integer for a given IPv6 address.
137    """
138    try:
139        packed_int = _inet_pton(AF_INET6, addr)
140        return packed_to_int(packed_int)
141    except Exception:
142        raise AddrFormatError('%r is not a valid IPv6 address string!' % (addr,))
143
144
145def int_to_str(int_val, dialect=None):
146    """
147    :param int_val: An unsigned integer.
148
149    :param dialect: (optional) a Python class defining formatting options.
150
151    :return: The IPv6 presentation (string) format address equivalent to the
152        unsigned integer provided.
153    """
154    if dialect is None:
155        dialect = ipv6_compact
156
157    addr = None
158
159    try:
160        packed_int = int_to_packed(int_val)
161        if dialect.compact:
162            #   Default return value.
163            addr = _inet_ntop(AF_INET6, packed_int)
164        else:
165            #   Custom return value.
166            words = list(_struct.unpack('>8H', packed_int))
167            tokens = [dialect.word_fmt % word for word in words]
168            addr = word_sep.join(tokens)
169    except Exception:
170        raise ValueError('%r is not a valid 128-bit unsigned integer!' % (int_val,))
171
172    return addr
173
174
175def int_to_arpa(int_val):
176    """
177    :param int_val: An unsigned integer.
178
179    :return: The reverse DNS lookup for an IPv6 address in network byte
180        order integer form.
181    """
182    addr = int_to_str(int_val, ipv6_verbose)
183    tokens = list(addr.replace(':', ''))
184    tokens.reverse()
185    #   We won't support ip6.int here - see RFC 3152 for details.
186    tokens = tokens + ['ip6', 'arpa', '']
187    return '.'.join(tokens)
188
189
190def int_to_packed(int_val):
191    """
192    :param int_val: the integer to be packed.
193
194    :return: a packed string that is equivalent to value represented by an
195    unsigned integer.
196    """
197    words = int_to_words(int_val, 4, 32)
198    return _struct.pack('>4I', *words)
199
200
201def packed_to_int(packed_int):
202    """
203    :param packed_int: a packed string containing an unsigned integer.
204        It is assumed that string is packed in network byte order.
205
206    :return: An unsigned integer equivalent to value of network address
207        represented by packed binary string.
208    """
209    words = list(_struct.unpack('>4I', packed_int))
210
211    int_val = 0
212    for i, num in enumerate(reversed(words)):
213        word = num
214        word = word << 32 * i
215        int_val = int_val | word
216
217    return int_val
218
219
220def valid_words(words):
221    return _valid_words(words, word_size, num_words)
222
223
224def int_to_words(int_val, num_words=None, word_size=None):
225    if num_words is None:
226        num_words = globals()['num_words']
227    if word_size is None:
228        word_size = globals()['word_size']
229    return _int_to_words(int_val, word_size, num_words)
230
231
232def words_to_int(words):
233    return _words_to_int(words, word_size, num_words)
234
235
236def valid_bits(bits):
237    return _valid_bits(bits, width, word_sep)
238
239
240def bits_to_int(bits):
241    return _bits_to_int(bits, width, word_sep)
242
243
244def int_to_bits(int_val, word_sep=None):
245    if word_sep is None:
246        word_sep = globals()['word_sep']
247    return _int_to_bits(int_val, word_size, num_words, word_sep)
248
249
250def valid_bin(bin_val):
251    return _valid_bin(bin_val, width)
252
253
254def int_to_bin(int_val):
255    return _int_to_bin(int_val, width)
256
257
258def bin_to_int(bin_val):
259    return _bin_to_int(bin_val, width)
260