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