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

Source Code for Module dns.ipv6

  1  # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc. 
  2  # 
  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. 
  7  # 
  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. 
 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   
27 -def inet_ntoa(address):
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 # strip leading zeros. we do this with an re instead of 45 # with lstrip() because lstrip() didn't support chars until 46 # python 2.2.2 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 # Compress the longest subsequence of 0-value chunks to :: 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 # We have an embedded IPv4 address 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
98 -def inet_aton(text):
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 # Our aim here is not something fast; we just want something that works. 109 # 110 if not isinstance(text, binary_type): 111 text = text.encode() 112 113 if text == b'::': 114 text = b'0::' 115 # 116 # Get rid of the icky dot-quad syntax if we have it. 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 # Try to turn '::<whatever>' into ':<whatever>'; if no match try to 125 # turn '<whatever>::' into '<whatever>:' 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 # Now canonicalize into 8 chunks of 4 hex digits each 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 # Finally we can go to binary. 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
171 -def is_mapped(address):
172 return address.startswith(_mapped_prefix)
173