dnspython  1.16.0
About: dnspython is a DNS toolkit (for Python 2.x) that supports almost all record types.
  Fossies Dox: dnspython-1.16.0.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

ipv6.py
Go to the documentation of this file.
1 # Copyright (C) Dnspython Contributors, see LICENSE for text of ISC license
2 
3 # Copyright (C) 2003-2017 Nominum, Inc.
4 #
5 # Permission to use, copy, modify, and distribute this software and its
6 # documentation for any purpose with or without fee is hereby granted,
7 # provided that the above copyright notice and this permission notice
8 # appear in all copies.
9 #
10 # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
11 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
13 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
16 # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
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 
29 def inet_ntoa(address):
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  # strip leading zeros. we do this with an re instead of
47  # with lstrip() because lstrip() didn't support chars until
48  # python 2.2.2
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  # Compress the longest subsequence of 0-value chunks to ::
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  # We have an embedded IPv4 address
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 
100 def inet_aton(text):
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  # Our aim here is not something fast; we just want something that works.
110  #
111  if not isinstance(text, binary_type):
112  text = text.encode()
113 
114  if text == b'::':
115  text = b'0::'
116  #
117  # Get rid of the icky dot-quad syntax if we have it.
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  # Try to turn '::<whatever>' into ':<whatever>'; if no match try to
127  # turn '<whatever>::' into '<whatever>:'
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  # Now canonicalize into 8 chunks of 4 hex digits each
138  #
139  chunks = text.split(b':')
140  l = len(chunks)
141  if l > 8:
143  seen_empty = False
144  canonical = []
145  for c in chunks:
146  if c == b'':
147  if seen_empty:
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:
156  if lc != 4:
157  c = (b'0' * (4 - lc)) + c
158  canonical.append(c)
159  if l < 8 and not seen_empty:
161  text = b''.join(canonical)
162 
163  #
164  # Finally we can go to binary.
165  #
166  try:
167  return binascii.unhexlify(text)
168  except (binascii.Error, TypeError):
170 
171 _mapped_prefix = b'\x00' * 10 + b'\xff\xff'
172 
173 def is_mapped(address):
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)
dns.ipv6.inet_ntoa
def inet_ntoa(address)
Definition: ipv6.py:29
dns.ipv4
Definition: ipv4.py:1
dns.exception.SyntaxError
Definition: exception.py:113
dns.ipv6.inet_aton
def inet_aton(text)
Definition: ipv6.py:100
dns._compat.maybe_decode
def maybe_decode(x)
Definition: _compat.py:22
dns.ipv4.inet_ntoa
def inet_ntoa(address)
Definition: ipv4.py:25
dns.ipv4.inet_aton
def inet_aton(text)
Definition: ipv4.py:40
dns._compat.xrange
xrange
Definition: _compat.py:11
dns.ipv6.is_mapped
def is_mapped(address)
Definition: ipv6.py:173
dns.exception
Definition: exception.py:1