1# Copyright (c) 2013 The Chromium OS Authors and the python-socks5 authors. 2# 3# This program is free software: you can redistribute it and/or modify 4# it under the terms of the GNU General Public License version 3, 5# as published by the Free Software Foundation. 6# 7# This program is distributed in the hope that it will be useful, 8# but WITHOUT ANY WARRANTY; without even the implied warranty of 9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10# GNU General Public License for more details. 11# 12# You should have received a copy of the GNU General Public License 13# along with this program. If not, see <http://www.gnu.org/licenses/>. 14 15import subprocess 16import test 17 18# Taken and hacked from https://code.google.com/p/python-socks5/ 19 20import socket 21from threading import Thread 22 23from autotest_lib.client.common_lib import error 24 25SOCKTIMEOUT=5 26RESENDTIMEOUT=300 27 28class Forwarder(Thread): 29 def __init__(self,src,dest): 30 Thread.__init__(self) 31 self.src=src 32 self.dest=dest 33 34 35 def __str__(self): 36 return '<Forwarder from %s to %s>' % (self.src, self.dest) 37 38 39 def run(self): 40 print '%s: starting' % self 41 try: 42 self.forward() 43 except socket.error as e: 44 print '%s: exception %s' % (self, e) 45 self.src.close() 46 self.dest.close() 47 finally: 48 print '%s: exiting' % self 49 50 51 def forward(self): 52 BUFSIZE = 1024 53 data = self.src.recv(BUFSIZE) 54 while data: 55 self.dest.sendall(data) 56 data = self.src.recv(BUFSIZE) 57 self.src.close() 58 self.dest.close() 59 print '%s: client quit normally' % self 60 61 62class ProxyForwarder(Forwarder): 63 def __init__(self, src, dest_addr): 64 Forwarder.__init__(self, src, None) 65 self.dest_addr = dest_addr 66 self.src = src 67 self.dest = None 68 69 70 def __str__(self): 71 return '<ProxyForwarder between %s and %s (%s:%d)' % ( 72 self.src, self.dest, self.dest_addr[0], self.dest_addr[1]) 73 74 75 def forward(self): 76 self.dest = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 77 self.dest.connect(self.dest_addr) 78 self.src.settimeout(RESENDTIMEOUT) 79 self.dest.settimeout(RESENDTIMEOUT) 80 Forwarder(self.src,self.dest).start() 81 Forwarder(self.dest,self.src).start() 82 83 84def recvbytes(sock, n): 85 bs = sock.recv(n) 86 return [ ord(x) for x in bs ] 87 88 89def recvshort(sock): 90 x = recvbytes(sock, 2) 91 return x[0] * 256 + x[1] 92 93 94def create_server(ip,port): 95 SOCKS5_VER = "\x05" 96 AUTH_NONE = "\x00" 97 98 ATYP_DOMAIN = 0x03 99 100 CMD_CONNECT = 0x01 101 102 ERR_SUCCESS = "\x00" 103 ERR_UNSUPP = "\x07" 104 105 transformer = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 106 transformer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 107 transformer.bind((ip, port)) 108 transformer.listen(1000) 109 110 network_port = chr(port >> 8) + chr(port & 0xff) 111 # Turn the textual IP address we were supplied with into a 112 # network-byte-order IP address for SOCKS5 wire protocol 113 network_ip = "".join(chr(int(i)) for i in ip.split(".")) 114 while True: 115 sock = transformer.accept()[0] 116 sock.settimeout(SOCKTIMEOUT) 117 print "Got one client connection" 118 (_, nmethods) = recvbytes(sock, 2) 119 _ = recvbytes(sock, nmethods) 120 sock.sendall(SOCKS5_VER + AUTH_NONE) 121 (_, cmd, _, atyp) = recvbytes(sock, 4) 122 dst_addr = None 123 dst_port = None 124 if atyp == ATYP_DOMAIN: 125 addr_len = recvbytes(sock, 1)[0] 126 dst_addr = "".join([unichr(x) for x in recvbytes(sock, addr_len)]) 127 dst_port = recvshort(sock) 128 else: 129 socket.sendall(SOCKS5_VER + ERR_UNSUPP + network_ip + network_port) 130 print "Proxying to %s:%d" %(dst_addr,dst_port) 131 132 if cmd == CMD_CONNECT: 133 sock.sendall(SOCKS5_VER + ERR_SUCCESS + "\x00" + "\x01" + 134 network_ip + network_port) 135 print "Starting forwarding thread" 136 ProxyForwarder(sock, (dst_addr, dst_port)).start() 137 else: 138 sock.sendall(SOCKS5_VER + ERR_UNSUPP + network_ip + network_port) 139 sock.close() 140 141 142class ServingThread(Thread): 143 def __init__(self, ip, port): 144 Thread.__init__(self) 145 self.ip = ip 146 self.port = port 147 148 149 def run(self): 150 create_server(self.ip, self.port) 151 152 153class platform_TLSDateActual(test.test): 154 version = 1 155 156 157 def tlsdate(self, host, proxy): 158 args = ['/usr/bin/tlsdate', '-v', '-l', '-H', host] 159 if proxy: 160 args += ['-x', proxy] 161 p = subprocess.Popen(args, stderr=subprocess.PIPE) 162 out = p.communicate()[1] 163 print out 164 return p.returncode 165 166 167 def run_once(self): 168 t = ServingThread("127.0.0.1", 8083) 169 t.start() 170 r = self.tlsdate('clients3.google.com', None) 171 if r != 0: 172 raise error.TestFail('tlsdate with no proxy to good host failed: %d' % r) 173 r = self.tlsdate('clients3.google.com', 'socks5://127.0.0.1:8083') 174 if r != 0: 175 raise error.TestFail('tlsdate with proxy to good host failed: %d' % r) 176 r = self.tlsdate('invalid-host.example.com', None) 177 if r == 0: 178 raise error.TestFail('tlsdate with no proxy to bad host succeeded') 179 r = self.tlsdate('invalid-host.example.com', 'socks5://127.0.0.1:8083') 180 if r == 0: 181 raise error.TestFail('tlsdate with proxy to bad host succeeded') 182