├── README.md ├── win_reset_gp.py ├── win32runas.py ├── socket_inet_p.py └── pteredor.py /README.md: -------------------------------------------------------------------------------- 1 | # pteredor 2 | A tool to help evaluate the teredo servers. 3 | 4 | # Compatible 5 | Tested with Python 2.7 and above. 6 | 7 | # Notice 8 | Ensure DNS resolve and firewall setting is correct. 9 | 10 | # Usage 11 | Run use default teredo server list. 12 | ``` 13 | python pteredor.py -h -p 54301 14 | 15 | pteredor [-p ] [-P ] [-h] [ [ [...]]] 16 | -p Set the local port num. (client) 17 | -P Set the remote port num. (server) 18 | -h Show this help. 19 | 20 | The teredo server is a host name (domain or IP). 21 | ``` 22 | 23 | or 24 | 25 | ``` 26 | >>> import pteredor 27 | >>> pteredor.main(local_port=256, remote_port=3544) 28 | ``` 29 | 30 | Run append custom teredo server list. 31 | ``` 32 | python pteredor.py server1 server2 ... 33 | ``` 34 | 35 | or 36 | 37 | ``` 38 | >>> pteredor.main(server1, server2 ...) 39 | ``` 40 | 41 | or 42 | 43 | ``` 44 | >>> server_list = [server1, server2 ...] 45 | >>> pteredor.main(server_list) 46 | ``` 47 | 48 | Output 49 | ``` 50 | Stop teredo tunnel for run prober, Y/N? n 51 | try bind local port: 2694 52 | Starting probe NAT type... 53 | The NAT type is cone. 54 | Starting evaluate servers... 55 | 65.55.158.118 ['win10.ipv6.microsoft.com', 'win1710.ipv6.microsoft.com'] 202ms 56 | 157.56.144.215 ['win1711.ipv6.microsoft.com'] 242ms 57 | 83.170.6.76 ['teredo.remlab.net', 'teredo-debian.remlab.net'] 312ms 58 | 217.17.192.217 ['teredo.iks-jena.de'] 327ms 59 | 195.140.195.140 ['teredo.trex.fi'] 374ms 60 | 61 | The recommend server is ['win10.ipv6.microsoft.com', 'win1710.ipv6.microsoft.com']. 62 | Do you want to set recommend teredo server, Y/N? n 63 | Press enter to over... 64 | ``` 65 | -------------------------------------------------------------------------------- /win_reset_gp.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | 3 | import os 4 | import platform 5 | import locale 6 | import ctypes 7 | import win32runas 8 | 9 | 10 | def win32_notify( msg='msg', title='Title'): 11 | res = ctypes.windll.user32.MessageBoxW(None, msg, title, 1) 12 | # Yes:1 No:2 13 | return res == 1 14 | 15 | def reset_teredo(): 16 | gp_split = b'[\x00' 17 | gp_teredo = '\x00'.join(list('v6Transition\x00;Teredo')).encode() 18 | 19 | with open(gp_regpol_file, 'rb') as f: 20 | gp_regpol_old = f.read().split(gp_split) 21 | 22 | gp_regpol_new = [gp for gp in gp_regpol_old if gp_teredo not in gp] 23 | 24 | if len(gp_regpol_new) != len(gp_regpol_old) and \ 25 | win32_notify(notice_text, notice_title): 26 | with open(gp_regpol_file, 'wb') as f: 27 | f.write(gp_split.join(gp_regpol_new)) 28 | os.system(sysnative + '\\gpupdate /target:computer /force') 29 | 30 | def main(): 31 | if os.name != 'nt': 32 | return 33 | 34 | sysver = platform.version() 35 | if sysver < '6': 36 | # Teredo item was added to Group Policy starting with Windows Vista 37 | return 38 | 39 | windir = os.environ.get('windir') 40 | if not windir: 41 | return 42 | 43 | try: 44 | zh_locale = locale.getdefaultlocale()[0] == 'zh_CN' 45 | except: 46 | zh_locale = None 47 | 48 | if zh_locale: 49 | notice_title = u'\u63d0\u9192' 50 | notice_text = u'\u53d1\u73b0\u7ec4\u7b56\u7565 Teredo \u8bbe\u7f6e\uff0c\u662f\u5426\u91cd\u7f6e\uff1f' 51 | else: 52 | notice_title = 'Notice' 53 | notice_text = 'Found Teredo settings in Group Policy, do you want to reset it?' 54 | 55 | sys64 = os.path.exists(windir + '\\SysWOW64') 56 | pe32 = platform.architecture()[0] == '32bit' 57 | sysalias = 'Sysnative' if sys64 and pe32 else 'System32' 58 | sysnative = '%s\\%s' % (windir, sysalias) 59 | gp_regpol_file = sysnative + '\\GroupPolicy\\Machine\\Registry.pol' 60 | 61 | if os.path.exists(gp_regpol_file): 62 | if not win32runas.is_admin(): 63 | win32runas.runas(os.path.abspath(__file__)) 64 | return 65 | reset_teredo() 66 | 67 | if '__main__' == __name__: 68 | main() 69 | -------------------------------------------------------------------------------- /win32runas.py: -------------------------------------------------------------------------------- 1 | # coding:utf-8 2 | # run as admin in Windows 3 | 4 | import os 5 | import sys 6 | import ctypes 7 | import subprocess 8 | from ctypes import c_ulong, c_char_p, c_int, c_void_p 9 | from ctypes.wintypes import HANDLE, DWORD, HWND, HINSTANCE, HKEY 10 | 11 | 12 | def is_admin(): 13 | try: 14 | return ctypes.windll.shell32.IsUserAnAdmin() 15 | except: 16 | return False 17 | 18 | def encode_for_locale(s): 19 | if s is None: 20 | return 21 | try: 22 | return s.encode('mbcs') 23 | except Exception as e: 24 | if isinstance(s, bytes): 25 | try: 26 | return s.decode('utf8').encode('mbcs') 27 | except: 28 | return s 29 | else: 30 | raise e 31 | 32 | if os.name == 'nt': 33 | class ShellExecuteInfo(ctypes.Structure): 34 | _fields_ = [('cbSize', DWORD), 35 | ('fMask', c_ulong), 36 | ('hwnd', HWND), 37 | ('lpVerb', c_char_p), 38 | ('lpFile', c_char_p), 39 | ('lpParameters', c_char_p), 40 | ('lpDirectory', c_char_p), 41 | ('nShow', c_int), 42 | ('hInstApp', HINSTANCE), 43 | ('lpIDList', c_void_p), 44 | ('lpClass', c_char_p), 45 | ('hKeyClass', HKEY), 46 | ('dwHotKey', DWORD), 47 | ('hIcon', HANDLE), 48 | ('hProcess', HANDLE)] 49 | 50 | SEE_MASK_NOCLOSEPROCESS = 0x00000040 51 | ShellExecuteEx = ctypes.windll.Shell32.ShellExecuteEx 52 | ShellExecuteEx.argtypes = ctypes.POINTER(ShellExecuteInfo), 53 | WaitForSingleObject = ctypes.windll.kernel32.WaitForSingleObject 54 | SE_ERR_CODES = { 55 | 0: 'Out of memory or resources', 56 | 2: 'File not found', 57 | 3: 'Path not found', 58 | 5: 'Access denied', 59 | 8: 'Out of memory', 60 | 26: 'Cannot share an open file', 61 | 27: 'File association information not complete', 62 | 28: 'DDE operation timed out', 63 | 29: 'DDE operation failed', 64 | 30: 'DDE operation is busy', 65 | 31: 'File association not available', 66 | 32: 'Dynamic-link library not found', 67 | } 68 | sys.argv[0] = os.path.abspath(sys.argv[0]) 69 | 70 | def runas(args=sys.argv, executable=sys.executable, cwd=None, 71 | nShow=1, waitClose=True, waitTimeout=-1): 72 | if not 0 <= nShow <= 10: 73 | nShow = 1 74 | try: 75 | if args is not None and not isinstance(args, str): 76 | args = subprocess.list2cmdline(args) 77 | pExecInfo = ShellExecuteInfo() 78 | pExecInfo.cbSize = ctypes.sizeof(pExecInfo) 79 | pExecInfo.fMask |= SEE_MASK_NOCLOSEPROCESS 80 | pExecInfo.lpVerb = b'open' if is_admin() else b'runas' 81 | pExecInfo.lpFile = encode_for_locale(executable) 82 | pExecInfo.lpParameters = encode_for_locale(args) 83 | pExecInfo.lpDirectory = encode_for_locale(cwd) 84 | pExecInfo.nShow = nShow 85 | if ShellExecuteEx(pExecInfo): 86 | if waitClose: 87 | WaitForSingleObject(pExecInfo.hProcess, waitTimeout) 88 | return True 89 | else: 90 | return pExecInfo.hProcess 91 | else: 92 | se_err = SE_ERR_CODES.get(pExecInfo.hInstApp) 93 | if se_err: 94 | err = ctypes.WinError() 95 | err.strerror = '%s: %s' % (se_err, err.strerror) 96 | raise err 97 | else: 98 | raise ctypes.WinError() 99 | except Exception as e: 100 | print('runas failed! error: %s' % e) 101 | 102 | if __name__ == '__main__': 103 | if len(sys.argv) > 1: 104 | runas(sys.argv[2:], sys.argv[1]) 105 | -------------------------------------------------------------------------------- /socket_inet_p.py: -------------------------------------------------------------------------------- 1 | """Implement inet_pton and inet_ntop in python.""" 2 | 3 | import socket 4 | from socket import error, AF_INET, AF_INET6, inet_aton, inet_ntoa 5 | from struct import pack, unpack 6 | 7 | 8 | try: 9 | _compat_str_types = (str, unicode) 10 | except NameError: 11 | _compat_str_types = (str, ) 12 | 13 | def inet_pton(family, ip_string): 14 | if family == AF_INET: 15 | # inet_aton() also accepts strings like '1', '127.1', some also trailing 16 | # data like '127.0.0.1 whatever', but inet_pton() does not. 17 | ip_packed = inet_aton(ip_string) 18 | if inet_ntoa(ip_packed) == ip_string: 19 | # Only accept injective ip strings 20 | return ip_packed 21 | raise error("illegal IP address string passed to inet_pton") 22 | 23 | if family == AF_INET6: 24 | if not isinstance(ip_string, _compat_str_types): 25 | raise TypeError("inet_pton() argument 2 must be string, not %s" 26 | % type(ip_string).__name__) 27 | try: 28 | parts = _explode_ip_string(ip_string).split(":") 29 | if len(parts) == 7: 30 | return pack("!8H", *[int(i, 16) for i in parts]) 31 | else: 32 | ip4 = inet_aton(parts.pop()) 33 | ip6 = pack("!6H", *[int(i, 16) for i in parts]) 34 | return ip6 + ip4 35 | except Exception: 36 | pass 37 | raise error("illegal IP address string passed to inet_pton") 38 | 39 | raise error("unknown address family %r" % family) 40 | 41 | def inet_ntop(family, ip_packed): 42 | if family == AF_INET: 43 | return inet_ntoa(ip_packed) 44 | 45 | if family == AF_INET6: 46 | if not isinstance(ip_packed, (bytes, bytearray)): 47 | raise TypeError("inet_ntop() argument 2 must be %s, not %s" 48 | % (bytes.__name__, type(ip_string).__name__)) 49 | try: 50 | hextets = ["%x" % i for i in unpack("!8H", ip_packed)] 51 | return ":".join(_compress_hextets(hextets)) 52 | except Exception: 53 | pass 54 | raise error("illegal IP address string passed to inet_ntop") 55 | 56 | raise error("unknown address family %r" % family) 57 | 58 | 59 | def _explode_ip_string(ip_string): 60 | assert 1 < len(ip_string) < 40, 0 61 | if ip_string[:1] == ":": 62 | assert ip_string[:2] == "::", 0 63 | ip_string = "0" + ip_string 64 | if ip_string[-1:] == ":": 65 | assert ip_string[-2:] == "::", 0 66 | ip_string = ip_string + "0" 67 | 68 | d_clns = ip_string.count("::") 69 | assert d_clns == 0 or d_clns == 1 and ip_string.count(":::") == 0, 0 70 | 71 | clns = ip_string.count(":") 72 | m_clns = 6 if "." in ip_string[-4:] else 7 73 | if d_clns: 74 | assert 1 < clns <= m_clns, 0 75 | exploded = "0".join([":"] * (2 + m_clns - clns)) 76 | ip_string = ip_string.replace("::", exploded, 1) 77 | else: 78 | assert clns == m_clns, 0 79 | 80 | return ip_string 81 | 82 | # Copy from ipaddress module 83 | def _compress_hextets(hextets): 84 | best_doublecolon_start = -1 85 | best_doublecolon_len = 0 86 | doublecolon_start = -1 87 | doublecolon_len = 0 88 | for index, hextet in enumerate(hextets): 89 | if hextet == "0": 90 | doublecolon_len += 1 91 | if doublecolon_start == -1: 92 | # Start of a sequence of zeros. 93 | doublecolon_start = index 94 | if doublecolon_len > best_doublecolon_len: 95 | # This is the longest sequence of zeros so far. 96 | best_doublecolon_len = doublecolon_len 97 | best_doublecolon_start = doublecolon_start 98 | else: 99 | doublecolon_len = 0 100 | doublecolon_start = -1 101 | 102 | if best_doublecolon_len > 1: 103 | best_doublecolon_end = (best_doublecolon_start + best_doublecolon_len) 104 | # For zeros at the end of the address. 105 | if best_doublecolon_end == len(hextets): 106 | hextets += [""] 107 | hextets[best_doublecolon_start:best_doublecolon_end] = [""] 108 | # For zeros at the beginning of the address. 109 | if best_doublecolon_start == 0: 110 | hextets = [""] + hextets 111 | 112 | return hextets 113 | 114 | if not hasattr(socket, "inet_pton"): 115 | socket.inet_pton = inet_pton 116 | 117 | if not hasattr(socket, "inet_ntop"): 118 | socket.inet_ntop = inet_ntop 119 | -------------------------------------------------------------------------------- /pteredor.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding:utf-8 3 | 4 | # A tool to help evaluate the teredo servers. 5 | # Author: SeaHOH 6 | # Compatible: Python 2.7 and above 7 | # Thanks XndroidDev 8 | # References: 9 | # https://tools.ietf.org/html/rfc4380 5.1 5.2 10 | # https://tools.ietf.org/html/rfc4861 4.1 4.2 11 | # https://tools.ietf.org/html/rfc2460 8.1 12 | # https://github.com/XndroidDev/Xndroid/blob/master/fqrouter/manager/teredo.py 13 | 14 | __version__ = '0.1.0' 15 | 16 | import sys 17 | import os 18 | import socket 19 | import random 20 | import struct 21 | import collections 22 | import time 23 | import logging 24 | import select 25 | import errno 26 | 27 | import socket_inet_p 28 | 29 | try: 30 | import queue 31 | except: 32 | import Queue as queue 33 | 34 | try: 35 | import thread 36 | except: 37 | import _thread as thread 38 | 39 | try: 40 | _real_raw_input = raw_input 41 | def raw_input(s='', file=sys.stdout): 42 | if isinstance(s, unicode): 43 | file.write(s.encode(sys.getfilesystemencoding(), 'replace')) 44 | return _real_raw_input() 45 | else: 46 | return _real_raw_input(s) 47 | except: 48 | raw_input = input 49 | 50 | logger = logging.getLogger('pteredor') 51 | 52 | 53 | teredo_timeout = 4 54 | teredo_port = 3544 55 | link_local_addr = 'fe80::ffff:ffff:ffff' 56 | all_router_multicast = 'ff02::2' 57 | teredo_server_list = [ 58 | # limited 59 | #'teredo.ginzado.ne.jp', 60 | 61 | # disuse 62 | #'debian-miredo.progsoc.org', 63 | #'teredo.autotrans.consulintel.com', 64 | #'teredo.ngix.ne.kr', 65 | #'teredo.managemydedi.com', 66 | #'teredo.ipv6.microsoft.com', 67 | #'win8.ipv6.microsoft.com', 68 | 69 | # inuse 70 | 'teredo.remlab.net', 71 | 'teredo2.remlab.net', 72 | 'teredo-debian.remlab.net', 73 | 'teredo.trex.fi', 74 | 'teredo.nic.cz', 75 | 'teredo.iks-jena.de', 76 | 'win10.ipv6.microsoft.com', 77 | 'win1710.ipv6.microsoft.com', 78 | 'win1711.ipv6.microsoft.com' 79 | ] 80 | 81 | def creat_rs_nonce(): 82 | return struct.pack('d', random.randint(0, 1 << 62)) 83 | 84 | def creat_ipv6_rs_msg(checksum=None): 85 | return struct.pack('!2BH4x', 133, 0, checksum or 0) 86 | 87 | def in_checksum(data): 88 | n = len(data) 89 | f = '%dH' % (n // 2) 90 | if n % 2: 91 | f += 'B' 92 | s = sum(struct.unpack(f, data)) 93 | while (s >> 16): 94 | s = (s & 0xffff) + (s >> 16) 95 | s = ~s & 0xffff 96 | return socket.ntohs(s) 97 | 98 | class teredo_rs_packet(object): 99 | 100 | rs_src = socket.inet_pton(socket.AF_INET6, link_local_addr) 101 | rs_dst = socket.inet_pton(socket.AF_INET6, all_router_multicast) 102 | _icmpv6_rs_msg = creat_ipv6_rs_msg() 103 | 104 | def __init__(self, nonce=None): 105 | self.nonce = nonce or creat_rs_nonce() 106 | self.rs_src = bytearray(self.rs_src) 107 | self.teredo_header = self.creat_teredo_header() 108 | 109 | def creat_teredo_header(self): 110 | return struct.pack('!H2x8sx', 1, self.nonce) 111 | 112 | def creat_ipv6_pseudo_header(self): 113 | return struct.pack('!16s16sI3xB', 114 | bytes(self.rs_src), 115 | self.rs_dst, 116 | 58, 117 | len(self._icmpv6_rs_msg) 118 | ) 119 | 120 | def creat_rs_packet(self, cone=None): 121 | self.rs_src[8] = 0x80 if cone else 0 122 | pseudo_header = self.creat_ipv6_pseudo_header() 123 | checksum = in_checksum(self._icmpv6_rs_msg + pseudo_header) 124 | rs_msg = creat_ipv6_rs_msg(checksum) 125 | return self.teredo_header + struct.pack('!B4x3B16s16s', 126 | 0x60, 127 | len(rs_msg), 128 | 58, 129 | 255, 130 | bytes(self.rs_src), 131 | self.rs_dst 132 | ) + rs_msg 133 | 134 | @property 135 | def type_cone(self): 136 | if not hasattr(self, '_type_cone'): 137 | self._type_cone = self.creat_rs_packet(True) 138 | return self._type_cone 139 | 140 | @property 141 | def type_restricted(self): 142 | if not hasattr(self, '_type_restricted'): 143 | self._type_restricted = self.creat_rs_packet() 144 | return self._type_restricted 145 | 146 | def get_sock(port): 147 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 148 | while True: 149 | try: 150 | _port = port or random.randint(1025, 5000) 151 | print('try bind local port:', _port) 152 | sock.bind(('0.0.0.0', _port)) 153 | return sock 154 | except socket.error as e: 155 | if port: 156 | print('bind local port %d fail: %r' % (_port, e)) 157 | return 158 | if e.args[0] == errno.EADDRINUSE: 159 | pass 160 | 161 | def is_ipv4(ip): 162 | try: 163 | socket.inet_aton(ip) 164 | except: 165 | return False 166 | else: 167 | return True 168 | 169 | def resolve(host): 170 | try: 171 | return socket.gethostbyname_ex(host)[-1] 172 | except: 173 | return [] 174 | 175 | def ip2int(ip): 176 | return struct.unpack('>I', socket.inet_aton(ip))[0] 177 | 178 | def int2ip(int): 179 | return socket.inet_ntoa(struct.pack('>I', int)) 180 | 181 | def get_second_server_ip(ip): 182 | return int2ip(ip2int(ip) + 1) 183 | 184 | def remove_same_server(server_ip_list): 185 | logger.debug('input ip: %s' % server_ip_list) 186 | cleared_list = set() 187 | for ip1 in server_ip_list: 188 | _ip1 = ip2int(ip1) 189 | for ip2 in server_ip_list: 190 | b = _ip1 - ip2int(ip2) 191 | if b in (1, -1): 192 | cleared_list.add(ip1 if b < 0 else ip2) 193 | break 194 | if b not in (1, -1): 195 | cleared_list.add(ip1) 196 | logger.debug('cleared ip: %s' % cleared_list) 197 | return cleared_list 198 | 199 | def str2hex(str): 200 | str = bytearray(str) 201 | h = [''] 202 | for c in str: 203 | if c > 0xf: 204 | h.append(hex(c)[2:]) 205 | else: 206 | h.append('0' + hex(c)[2:]) 207 | return '\\x'.join(h) 208 | 209 | class deque(collections.deque): 210 | 211 | def put(self, v): 212 | self.append(v) 213 | 214 | def get(self): 215 | try: 216 | return self.popleft() 217 | except: 218 | return None 219 | 220 | class default_prober_dict(dict): 221 | 222 | def __init__(self): 223 | self['nonce'] = None 224 | self['rs_packet'] = None 225 | self['ra_packets'] = deque() 226 | 227 | class teredo_prober(object): 228 | 229 | _stoped = None 230 | nat_type = 'null' 231 | qualified = False 232 | rs_cone_flag = 1 233 | timeout = teredo_timeout 234 | teredo_port = teredo_port 235 | 236 | def __init__(self, server_list=teredo_server_list, local_port=None, 237 | remote_port=None, probe_nat=True): 238 | self.teredo_sock = get_sock(local_port) 239 | if remote_port: 240 | self.teredo_port = remote_port 241 | self.prober_dict = collections.defaultdict(default_prober_dict) 242 | self.ip2server = collections.defaultdict(list) 243 | server_ip_list = [] 244 | if isinstance(server_list, str): 245 | server_list = [server_list] 246 | for server in server_list: 247 | if is_ipv4(server): 248 | server_ip_list.append(server) 249 | else: 250 | ip_list = resolve(server) 251 | for ip in ip_list: 252 | self.ip2server[ip].append(server) 253 | server_ip_list += ip_list 254 | self.server_ip_list = remove_same_server(server_ip_list) 255 | if len(self.server_ip_list) < 1: 256 | msg = 'Servers could not be resolved, %r.' % server_list 257 | print(msg) 258 | raise Exception(msg) 259 | elif len(self.server_ip_list) < 2: 260 | print('Need input more teredo servers, now is %d.' 261 | % len(self.server_ip_list)) 262 | thread.start_new_thread(self.receive_loop, ()) 263 | if probe_nat: 264 | self.nat_type = self.nat_type_probe() 265 | 266 | def unpack_indication(self, data): 267 | return struct.unpack('!2s4s', data[2:8]) 268 | 269 | def handle_ra_packet(self, ipv6_pkt): 270 | server_ip = socket.inet_ntoa(ipv6_pkt[76:80]) 271 | cone_flag = bytearray(ipv6_pkt)[32] >> 7 & 1 272 | logger.debug('ipv6_pkt ; RA_cone = %s\nsrc:%s\ndst:%s' % ( 273 | cone_flag, 274 | str2hex(ipv6_pkt[8:24]), 275 | str2hex(ipv6_pkt[24:40]))) 276 | return server_ip, cone_flag 277 | 278 | def receive_ra_packet(self): 279 | data, addr = self.teredo_sock.recvfrom(10240) 280 | received_ip, port = addr 281 | if port != self.teredo_port or len(data) < 40: 282 | logger.debug('ipv6_pkt ;1 drop:\n%s' % str2hex(data)) 283 | return 284 | auth_pkt = indicate_pkt = ipv6_pkt = None 285 | if data[0:2] == b'\x00\x01': 286 | auth_len = 13 + sum(struct.unpack('2B', data[2:4])) 287 | auth_pkt = data[0:auth_len] 288 | if data[auth_len:auth_len + 2] == b'\x00\x00': 289 | indicate_pkt = data[auth_len:auth_len + 8] 290 | ipv6_pkt = data[auth_len + 8:] 291 | if (auth_pkt is None or 292 | indicate_pkt is None or 293 | ipv6_pkt is None or 294 | bytearray(ipv6_pkt)[0] & 0xf0 != 0x60 or 295 | bytearray(ipv6_pkt)[40] != 134 or 296 | struct.unpack('!H', ipv6_pkt[4:6])[0] + 40 != len(ipv6_pkt) 297 | ): 298 | logger.debug('ipv6_pkt ;2 drop:\n%s' % str2hex(data)) 299 | return 300 | server_ip, ra_cone_flag = self.handle_ra_packet(ipv6_pkt) 301 | logger.debug('server ip: %s ; received ip: %s' % (server_ip, received_ip)) 302 | if (received_ip != server_ip and 303 | received_ip != get_second_server_ip(server_ip) or 304 | auth_pkt[4:12] != self.prober_dict[server_ip]['rs_packet'].nonce 305 | ): 306 | logger.debug('ipv6_pkt ;3 drop:\n%s' % str2hex(data)) 307 | return 308 | qualified = ra_cone_flag, indicate_pkt 309 | self.prober_dict[received_ip]['ra_packets'].put(qualified) 310 | 311 | def receive_loop(self): 312 | while not self._stoped: 313 | try: 314 | rd, _, _ = select.select([self.teredo_sock], [], [], 0.5) 315 | if rd and not self._stoped: 316 | self.receive_ra_packet() 317 | except Exception as e: 318 | logger.exception('receive procedure fail once: %r', e) 319 | pass 320 | 321 | def send_rs_packet(self, rs_packet, dst_ip): 322 | rs_packet = rs_packet.type_cone if self.rs_cone_flag else rs_packet.type_restricted 323 | logger.debug('send ; RS_cone = %s\n%s' % (self.rs_cone_flag, str2hex(rs_packet))) 324 | self.teredo_sock.sendto(rs_packet, (dst_ip, self.teredo_port)) 325 | 326 | def qualify(self, server_ip, second_server_ip=None): 327 | rs_packet = self.prober_dict[server_ip]['rs_packet'] 328 | if rs_packet is None: 329 | self.prober_dict[server_ip]['rs_packet'] = rs_packet = teredo_rs_packet() 330 | dst_ip = second_server_ip or server_ip 331 | self.send_rs_packet(rs_packet, dst_ip) 332 | 333 | begin_recv = time.time() 334 | while time.time() < self.timeout + begin_recv: 335 | qualified = self.prober_dict[dst_ip]['ra_packets'].get() 336 | if qualified: 337 | return qualified 338 | time.sleep(0.01) 339 | 340 | def qualify_loop(self, server_ip, second_server=None): 341 | if second_server: 342 | self.rs_cone_flag = 0 343 | second_server_ip = get_second_server_ip(server_ip) 344 | else: 345 | second_server_ip = None 346 | for i in range(3): 347 | try: 348 | return self.qualify(server_ip, second_server_ip) 349 | except Exception as e: 350 | logger.exception('qualify procedure fail once: %r', e) 351 | 352 | def nat_type_probe(self): 353 | print('Starting probe NAT type...') 354 | self.nat_type = 'probing' 355 | server_ip_list = self.server_ip_list.copy() 356 | self.rs_cone_flag = 1 357 | for server_ip in server_ip_list: 358 | qualified = self.qualify_loop(server_ip) 359 | if qualified: 360 | break 361 | if qualified is None: 362 | self.rs_cone_flag = 0 363 | while server_ip_list: 364 | server_ip = server_ip_list.pop() 365 | qualified = self.qualify_loop(server_ip) 366 | if qualified: 367 | break 368 | if qualified is None: 369 | self.qualified = False 370 | return 'offline' 371 | ra_cone_flag, first_indicate = qualified 372 | if ra_cone_flag: 373 | self.qualified = True 374 | return 'cone' 375 | qualified = None 376 | qualified = self.qualify_loop(server_ip, second_server=True) 377 | if qualified is None: 378 | self.last_server_ip = server_ip 379 | self.qualified = True 380 | return 'unknown' 381 | ra_cone_flag, second_indicate = qualified 382 | if first_indicate == second_indicate: 383 | self.qualified = True 384 | return 'restricted' 385 | else: 386 | self.qualified = False 387 | return 'symmetric' 388 | 389 | def _eval_servers(self, server_ip, queue_obj): 390 | start = time.time() 391 | qualified = self.qualify_loop(server_ip) 392 | cost = int((time.time() - start) * 1000) 393 | queue_obj.put((bool(qualified), self.ip2server[server_ip], server_ip, cost)) 394 | 395 | def eval_servers(self): 396 | if self.nat_type is 'null': 397 | self.nat_type = self.nat_type_probe() 398 | elif self.nat_type is 'probing': 399 | print('Is probing NAT type now, pleace wait...') 400 | while self.nat_type is 'probing': 401 | time.sleep(0.1) 402 | if not self.qualified: 403 | print('This device can not use teredo tunnel, the NAT type is %s!' 404 | % prober.nat_type) 405 | return [] 406 | print('Starting evaluate servers...') 407 | self.clear() 408 | eval_list = [] 409 | queue_obj = queue.Queue() 410 | for server_ip in self.server_ip_list: 411 | thread.start_new_thread(self._eval_servers, (server_ip, queue_obj)) 412 | for _ in self.server_ip_list: 413 | eval_list.append(queue_obj.get()) 414 | return eval_list 415 | 416 | def close(self): 417 | self._stoped = True 418 | self.clear() 419 | if self.teredo_sock: 420 | self.teredo_sock.close() 421 | self.teredo_sock = None 422 | 423 | def clear(self): 424 | for server_ip in self.server_ip_list: 425 | second_server_ip = get_second_server_ip(server_ip) 426 | self.prober_dict.pop(server_ip, None) 427 | self.prober_dict.pop(second_server_ip, None) 428 | 429 | local_ip_startswith = tuple( 430 | ['192.168', '10.'] + 431 | ['100.%d.' % (64 + n) for n in range(1 << 6)] + 432 | ['172.%d.' % (16 + n) for n in range(1 << 4)] 433 | ) 434 | 435 | import locale 436 | 437 | zh_locale = None 438 | try: 439 | zh_locale = locale.getdefaultlocale()[0] == 'zh_CN' 440 | except: 441 | if sys.platform == "darwin": 442 | try: 443 | oot = os.pipe() 444 | p = subprocess.Popen(['/usr/bin/defaults', 445 | 'read', 446 | 'NSGlobalDomain', 447 | 'AppleLanguages'], stdout=oot[1]) 448 | p.communicate() 449 | zh_locale = b'zh' in os.read(oot[0], 10000) 450 | except: 451 | pass 452 | 453 | if zh_locale: 454 | help_info = u''' 455 | pteredor [-p ] [-P ] [-h] [ [ [...]]] 456 | -p \u8bbe\u7f6e\u672c\u5730\u5ba2\u6237\u7aef\u7aef\u53e3\u3002 457 | -P \u8bbe\u7f6e\u8fdc\u7a0b\u670d\u52a1\u7aef\u7aef\u53e3\u3002 458 | -h \u663e\u793a\u672c\u5e2e\u52a9\u4fe1\u606f\u3002 459 | 460 | Teredo server \u662f\u4e00\u4e2a\u4e3b\u673a\u540d\uff0c\u53ef\u4ee5\u4f7f\u7528\u57df\u540d\u6216 IP\u3002 461 | 462 | ''' 463 | result_info = u'\n\u7ecf\u68c0\u6d4b\uff0c\u63a8\u8350\u670d\u52a1\u5668\u662f %r.' 464 | wait_info = u'\u8bf7\u7b49\u5f85 10 \u79d2\u949f\u2026\u2026' 465 | resume_info = u'Teredo \u5ba2\u6237\u7aef\u5df2\u6062\u590d\u8fd0\u884c\u3002' 466 | warn_1 = u'\u53c2\u6570 "-p" \u9519\u8bef\uff1a\u7aef\u53e3\u5fc5\u987b\u662f\u4e00\u4e2a\u6570\u5b57\u3002' 467 | warn_2 = u'\u53c2\u6570 "-P" \u9519\u8bef\uff1a\u7aef\u53e3\u5fc5\u987b\u662f\u4e00\u4e2a\u6570\u5b57\u3002' 468 | warn_3 = u'\u5f53\u524d\u8bbe\u5907\u53ef\u80fd\u65e0\u6cd5\u6b63\u5e38\u4f7f\u7528 teredo \u96a7\u9053\uff0cNAT \u7c7b\u578b\u662f %s\uff01' 469 | warn_4 = u'\u65e0\u6cd5\u5224\u65ad NAT \u7c7b\u578b\u3002' 470 | confirm_stop = u'\u662f\u5426\u5148\u6682\u65f6\u5173\u95ed teredo \u5ba2\u6237\u7aef\uff08IPv6\uff09\u518d\u8fdb\u884c\u6d4b\u8bd5\uff1f\uff08Y/N\uff09' 471 | confirm_set = u'\u4f60\u60f3\u8981\u5c06 teredo \u670d\u52a1\u5668\u8bbe\u7f6e\u4e3a\u672c\u6d4b\u8bd5\u7684\u63a8\u8350\u503c\u5417\uff1f\uff08Y/N\uff09' 472 | confirm_reset = u'\u4f60\u60f3\u8981\u91cd\u7f6e teredo \u5ba2\u6237\u7aef\u7684\u5237\u65b0\u95f4\u9694\u5417\uff1f\uff08Y/N\uff09' 473 | confirm_over = u'\u6309\u56de\u8f66\u952e\u7ed3\u675f\u2026\u2026' 474 | confirm_force = u'\u4f60\u60f3\u8981\u7ee7\u7eed\u8fdb\u884c\u6d4b\u8bd5\u5417\uff1f\uff08Y/N\uff09' 475 | nat_type_result = u'NAT \u7c7b\u578b\u662f %s\u3002' 476 | else: 477 | help_info = ''' 478 | pteredor [-p ] [-P ] [-h] [ [ [...]]] 479 | -p Set the local port num. (client) 480 | -P Set the remote port num. (server) 481 | -h Show this help. 482 | 483 | The teredo server is a host name (domain or IP). 484 | 485 | ''' 486 | result_info = '\nThe recommend server is %r.' 487 | wait_info = 'Please wait 10 seconds...' 488 | resume_info = 'The teredo cilent has resumed.' 489 | warn_1 = 'The value of parameter "-p" error: local port must be a number.' 490 | warn_2 = 'The value of parameter "-P" error: remote port must be a number.' 491 | warn_3 = 'This device may not be able to use teredo tunnel, the NAT type is %s!' 492 | warn_4 = 'We can not judge the NAT type.' 493 | confirm_stop = 'Stop teredo cilent for run prober, Y/N? ' 494 | confirm_set = 'Do you want to set recommend teredo server, Y/N? ' 495 | confirm_reset = 'Do you want to reset refreshinterval to the default value, Y/N? ' 496 | confirm_over = 'Press enter to over...' 497 | confirm_force = 'Do you want to force probe and set the teredo servers, Y/N? ' 498 | nat_type_result = 'The NAT type is %s.' 499 | 500 | if os.name == 'nt': 501 | import win32runas 502 | if win32runas.is_admin(): 503 | runas = os.system 504 | else: 505 | def runas(cmd): 506 | cmd = tuple(cmd.split(None, 1)) 507 | if len(cmd) == 1: 508 | cmd += None, 509 | win32runas.runas(cmd[1], cmd[0]) 510 | 511 | def main(local_port=None, remote_port=None, *args): 512 | server_list = [] + teredo_server_list 513 | for arg in args: 514 | if isinstance(arg, str): 515 | server_list.append(arg) 516 | elif isinstance(arg, list): 517 | server_list += arg 518 | elif isinstance(arg, tuple): 519 | server_list += list(arg) 520 | prober = teredo_prober(server_list, local_port=local_port, remote_port=remote_port) 521 | need_probe = recommend = None 522 | if not prober.qualified: 523 | print(warn_3 % prober.nat_type) 524 | if (prober.nat_type is 'symmetric' and 525 | raw_input(confirm_force).lower() == 'y'): 526 | need_probe = True 527 | prober.qualified = True 528 | elif prober.nat_type is 'unknown': 529 | print(warn_4) 530 | recommend = prober.ip2server[prober.last_server_ip] 531 | else: 532 | print(nat_type_result % prober.nat_type) 533 | need_probe = True 534 | if need_probe: 535 | qualified_list = prober.eval_servers() 536 | for qualified, server, server_ip, cost in qualified_list: 537 | print('%s %s %s' % (server_ip, server, '%sms' % cost if qualified else 'timedout')) 538 | recommend = qualified_list[0][1] 539 | prober.close() 540 | return recommend, prober.nat_type 541 | 542 | def test(): 543 | logging.basicConfig(level=logging.DEBUG) 544 | blank_rs_packet = bytearray( 545 | b'\x00\x01\x00\x00\x8a\xde\xb0\xd0\x2e\xea\x0b\xfc\x00' 546 | b'\x60\x00\x00\x00\x00\x08\x3a\xff\xfe\x80\x00\x00\x00\x00\x00\x00' 547 | b'\x00\x00\xff\xff\xff\xff\xff\xff\xff\x02\x00\x00\x00\x00\x00\x00' 548 | b'\x00\x00\x00\x00\x00\x00\x00\x02\x85\x00\x7d\x37\x00\x00\x00\x00') 549 | nonce = creat_rs_nonce() 550 | blank_rs_packet[4:12] = nonce 551 | assert(teredo_rs_packet(nonce).type_restricted == bytes(blank_rs_packet)) 552 | 553 | server_list = ['teredo.remlab.net','win1711.ipv6.microsoft.com'] 554 | prober = teredo_prober(server_list, probe_nat=False) 555 | prober.timeout = 4 556 | server_ip_list = prober.server_ip_list.copy() 557 | server_ip = server_ip_list.pop() 558 | for _ in range(2): 559 | print(prober.qualify_loop(server_ip)) 560 | prober.rs_cone_flag = prober.rs_cone_flag ^ 1 561 | server_ip = server_ip_list.pop() 562 | for _ in range(2): 563 | print(prober.qualify_loop(server_ip)) 564 | prober.rs_cone_flag = prober.rs_cone_flag ^ 1 565 | #prober.close() 566 | 567 | print(main()) 568 | raw_input(confirm_over) 569 | sys.exit(0) 570 | 571 | if '__main__' == __name__: 572 | #test() 573 | args = sys.argv[1:] 574 | if '-h' in args: 575 | args.remove('-h') 576 | print(help_info) 577 | if not args: 578 | raw_input(confirm_over) 579 | sys.exit(0) 580 | try: 581 | local_port = args[args.index('-p') + 1] 582 | args.remove('-p') 583 | args.remove(local_port) 584 | try: 585 | local_port = int(local_port) 586 | except: 587 | local_port = None 588 | print(warn_1) 589 | except: 590 | local_port = None 591 | try: 592 | remote_port = args[args.index('-P') + 1] 593 | args.remove('-P') 594 | args.remove(remote_port) 595 | try: 596 | remote_port = int(remote_port) 597 | except: 598 | remote_port = None 599 | print(warn_2) 600 | except: 601 | remote_port = None 602 | done_disabled = False 603 | if os.name == 'nt': 604 | if raw_input(confirm_stop).lower() == 'y': 605 | if runas('netsh interface teredo set state disable'): 606 | done_disabled = True 607 | win32runas.runas('win_reset_gp.py') 608 | print(os.system('netsh interface teredo show state')) 609 | recommend, nat_type = main(*args, local_port=local_port, remote_port=remote_port) 610 | print(result_info % recommend) 611 | if os.name == 'nt': 612 | ip = [a for a in os.popen('route print').readlines() if ' 0.0.0.0 ' in a][0].split()[-2] 613 | if nat_type == 'cone': 614 | client = 'client' 615 | else: 616 | import platform 617 | client_ext = 'natawareclient' if platform.version()[0] > '6' else 'enterpriseclient' 618 | client = client_ext if ip.startswith(local_ip_startswith) else 'client' 619 | if recommend: 620 | if os.name == 'nt' and \ 621 | raw_input(confirm_set).lower() == 'y': 622 | cmd = 'netsh interface teredo set state type=%s servername=%s.' 623 | if raw_input(confirm_reset).lower() == 'y': 624 | cmd += ' refreshinterval=default' 625 | if not remote_port: 626 | cmd += ' clientport=default' 627 | runas(cmd % (client, recommend[0])) 628 | print(wait_info) 629 | time.sleep(10) 630 | print(os.system('netsh interface teredo show state')) 631 | done_disabled = False 632 | if done_disabled: 633 | if runas('netsh interface teredo set state type=%s' % client): 634 | print(resume_info) 635 | raw_input(confirm_over) 636 | --------------------------------------------------------------------------------