1 # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
3 # Permission to use, copy, modify, and distribute this software and its
4 # documentation for any purpose with or without fee is hereby granted,
5 # provided that the above copyright notice and this permission notice
6 # appear in all copies.
8 # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
9 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
11 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
14 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 """IPv6 helper functions."""
23 _leading_zero
= re
.compile(r
'0+([0-9a-f]+)')
25 def inet_ntoa(address
):
26 """Convert a network format IPv6 address into text.
28 @param address: the binary address
31 @raises ValueError: the address isn't 16 bytes long
34 if len(address
) != 16:
35 raise ValueError("IPv6 addresses are 16 bytes long")
36 hex = address
.encode('hex_codec')
41 chunk
= hex[i
: i
+ 4]
42 # strip leading zeros. we do this with an re instead of
43 # with lstrip() because lstrip() didn't support chars until
45 m
= _leading_zero
.match(chunk
)
51 # Compress the longest subsequence of 0-value chunks to ::
61 current_len
= end
- start
62 if current_len
> best_len
:
64 best_len
= current_len
66 elif not last_was_zero
:
71 current_len
= end
- start
72 if current_len
> best_len
:
74 best_len
= current_len
76 if best_start
== 0 and \
78 best_len
== 5 and chunks
[5] == 'ffff'):
79 # We have an embedded IPv4 address
84 hex = prefix
+ dns
.ipv4
.inet_ntoa(address
[12:])
86 hex = ':'.join(chunks
[:best_start
]) + '::' + \
87 ':'.join(chunks
[best_start
+ best_len
:])
89 hex = ':'.join(chunks
)
92 _v4_ending
= re
.compile(r
'(.*):(\d+\.\d+\.\d+\.\d+)$')
93 _colon_colon_start
= re
.compile(r
'::.*')
94 _colon_colon_end
= re
.compile(r
'.*::$')
97 """Convert a text format IPv6 address into network format.
99 @param text: the textual address
102 @raises dns.exception.SyntaxError: the text was not properly formatted
106 # Our aim here is not something fast; we just want something that works.
112 # Get rid of the icky dot-quad syntax if we have it.
114 m
= _v4_ending
.match(text
)
116 b
= dns
.ipv4
.inet_aton(m
.group(2))
117 text
= "%s:%02x%02x:%02x%02x" % (m
.group(1), ord(b
[0]), ord(b
[1]),
118 ord(b
[2]), ord(b
[3]))
120 # Try to turn '::<whatever>' into ':<whatever>'; if no match try to
121 # turn '<whatever>::' into '<whatever>:'
123 m
= _colon_colon_start
.match(text
)
127 m
= _colon_colon_end
.match(text
)
131 # Now canonicalize into 8 chunks of 4 hex digits each
133 chunks
= text
.split(':')
136 raise dns
.exception
.SyntaxError
142 raise dns
.exception
.SyntaxError
144 for i
in xrange(0, 8 - l
+ 1):
145 canonical
.append('0000')
149 raise dns
.exception
.SyntaxError
151 c
= ('0' * (4 - lc
)) + c
153 if l
< 8 and not seen_empty
:
154 raise dns
.exception
.SyntaxError
155 text
= ''.join(canonical
)
158 # Finally we can go to binary.
161 return text
.decode('hex_codec')
163 raise dns
.exception
.SyntaxError