Package dns :: Module ipv6
[hide private]
[frames] | no frames]

Source Code for Module dns.ipv6

  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: 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 # Finally we can go to binary. 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
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)
182