1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 """IPv6 helper functions."""
19
20 import re
21 import binascii
22
23 import dns.exception
24 import dns.ipv4
25 from ._compat import xrange, binary_type, maybe_decode
26
27 _leading_zero = re.compile(r'0+([0-9a-f]+)')
28
30 """Convert an IPv6 address in binary form to text form.
31
32 *address*, a ``binary``, the IPv6 address in binary form.
33
34 Raises ``ValueError`` if the address isn't 16 bytes long.
35 Returns a ``text``.
36 """
37
38 if len(address) != 16:
39 raise ValueError("IPv6 addresses are 16 bytes long")
40 hex = binascii.hexlify(address)
41 chunks = []
42 i = 0
43 l = len(hex)
44 while i < l:
45 chunk = maybe_decode(hex[i : i + 4])
46
47
48
49 m = _leading_zero.match(chunk)
50 if not m is None:
51 chunk = m.group(1)
52 chunks.append(chunk)
53 i += 4
54
55
56
57 best_start = 0
58 best_len = 0
59 start = -1
60 last_was_zero = False
61 for i in xrange(8):
62 if chunks[i] != '0':
63 if last_was_zero:
64 end = i
65 current_len = end - start
66 if current_len > best_len:
67 best_start = start
68 best_len = current_len
69 last_was_zero = False
70 elif not last_was_zero:
71 start = i
72 last_was_zero = True
73 if last_was_zero:
74 end = 8
75 current_len = end - start
76 if current_len > best_len:
77 best_start = start
78 best_len = current_len
79 if best_len > 1:
80 if best_start == 0 and \
81 (best_len == 6 or
82 best_len == 5 and chunks[5] == 'ffff'):
83
84 if best_len == 6:
85 prefix = '::'
86 else:
87 prefix = '::ffff:'
88 hex = prefix + dns.ipv4.inet_ntoa(address[12:])
89 else:
90 hex = ':'.join(chunks[:best_start]) + '::' + \
91 ':'.join(chunks[best_start + best_len:])
92 else:
93 hex = ':'.join(chunks)
94 return hex
95
96 _v4_ending = re.compile(br'(.*):(\d+\.\d+\.\d+\.\d+)$')
97 _colon_colon_start = re.compile(br'::.*')
98 _colon_colon_end = re.compile(br'.*::$')
99
101 """Convert an IPv6 address in text form to binary form.
102
103 *text*, a ``text``, the IPv6 address in textual form.
104
105 Returns a ``binary``.
106 """
107
108
109
110
111 if not isinstance(text, binary_type):
112 text = text.encode()
113
114 if text == b'::':
115 text = b'0::'
116
117
118
119 m = _v4_ending.match(text)
120 if not m is None:
121 b = bytearray(dns.ipv4.inet_aton(m.group(2)))
122 text = (u"{}:{:02x}{:02x}:{:02x}{:02x}".format(m.group(1).decode(),
123 b[0], b[1], b[2],
124 b[3])).encode()
125
126
127
128
129 m = _colon_colon_start.match(text)
130 if not m is None:
131 text = text[1:]
132 else:
133 m = _colon_colon_end.match(text)
134 if not m is None:
135 text = text[:-1]
136
137
138
139 chunks = text.split(b':')
140 l = len(chunks)
141 if l > 8:
142 raise dns.exception.SyntaxError
143 seen_empty = False
144 canonical = []
145 for c in chunks:
146 if c == b'':
147 if seen_empty:
148 raise dns.exception.SyntaxError
149 seen_empty = True
150 for i in xrange(0, 8 - l + 1):
151 canonical.append(b'0000')
152 else:
153 lc = len(c)
154 if lc > 4:
155 raise dns.exception.SyntaxError
156 if lc != 4:
157 c = (b'0' * (4 - lc)) + c
158 canonical.append(c)
159 if l < 8 and not seen_empty:
160 raise dns.exception.SyntaxError
161 text = b''.join(canonical)
162
163
164
165
166 try:
167 return binascii.unhexlify(text)
168 except (binascii.Error, TypeError):
169 raise dns.exception.SyntaxError
170
171 _mapped_prefix = b'\x00' * 10 + b'\xff\xff'
172
174 """Is the specified address a mapped IPv4 address?
175
176 *address*, a ``binary`` is an IPv6 address in binary form.
177
178 Returns a ``bool``.
179 """
180
181 return address.startswith(_mapped_prefix)
182