1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
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
45
46
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
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
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
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
109
110 if not isinstance(text, binary_type):
111 text = text.encode()
112
113 if text == b'::':
114 text = b'0::'
115
116
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
125
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
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
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
173