最近,看了一下Socks5的RFC,在网上找了一下Scoks5的Python实现,有几个地方自己看得不是很明白,就多查看了一下,Wireshark抓包也看了一下,稍微记录一下。
网上找的源码如下:其中我不是很明白的地方在如下:
1: #!/usr/bin/python 2: # Filename s5.py 3: # Python Dynamic Socks5 Proxy 4: # Usage: python s5.py 1080 5: # Background Run: nohup python s5.py 1080 & 6: 7: import socket, sys, select, SocketServer, struct, time 8: 9: class ThreadingTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): pass 10: class Socks5Server(SocketServer.StreamRequestHandler): 11: def handle_tcp(self, sock, remote): 12: fdset = [sock, remote] 13: while True: 14: r, w, e = select.select(fdset, [], []) 15: if sock in r: 16: if remote.send(sock.recv(4096)) <= 0: break 17: if remote in r: 18: if sock.send(remote.recv(4096)) <= 0: break 19: def handle(self): 20: try: 21: pass # print 'from ', self.client_address nothing to do. 22: sock = self.connection 23: # 1. Version 24: sock.recv(262) 25: sock.send("\x05\x00"); 26: # 2. Request 27: data = self.rfile.read(4) 28: mode = ord(data[1]) 29: addrtype = ord(data[3]) 30: if addrtype == 1: # IPv4 31: addr = socket.inet_ntoa(self.rfile.read(4)) 32: elif addrtype == 3: # Domain name 33: addr = self.rfile.read(ord(sock.recv(1)[0])) 34: port = struct.unpack('>H', self.rfile.read(2)) 35: reply = "\x05\x00\x00\x01" 36: try: 37: if mode == 1: # 1. Tcp connect 38: remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 39: remote.connect((addr, port[0])) 40: pass # print 'To', addr, port[0] nothing do to. 41: else: 42: reply = "\x05\x07\x00\x01" # Command not supported 43: local = remote.getsockname() 44: reply += socket.inet_aton(local[0]) + struct.pack(">H", local[1]) 45: except socket.error: 46: # Connection refused 47: reply = '\x05\x05\x00\x01\x00\x00\x00\x00\x00\x00' 48: sock.send(reply) 49: # 3. Transfering 50: if reply[1] == '\x00': # Success 51: if mode == 1: # 1. Tcp connect 52: self.handle_tcp(sock, remote) 53: except socket.error: 54: pass #print 'error' nothing to do . 55: except IndexError: 56: pass 57: def main(): 58: filename = sys.argv[0]; 59: if len(sys.argv)<2: 60: print 'usage: ' + filename + ' port' 61: sys.exit() 62: socks_port = int(sys.argv[1]); 63: server = ThreadingTCPServer(('', socks_port), Socks5Server) 64: print 'bind port: %d' % socks_port + ' ok!' 65: server.serve_forever() 66: if __name__ == '__main__': 67: main() 68:为啥要接收262个字节的数据,抓包发现其实没有这么多,可以看下抓包的结果。
首先是TCP三次握手建立连接:
客户端给服务器发送05 01 00共三个字节,要求匿名代理:
服务器端发送05 00两个字节,表示允许匿名代理。
客户端如果发送05 01 00 03 表示后面跟的是域名而不是IP地址,由Socks5服务器进行DNS解析。
RFC中有这样一句。
The programming interface for a SOCKS-aware UDP MUST report an available buffer space for UDP datagrams that is smaller than the actual space provided by the operating system: o if ATYP is X'01' - 10+method_dependent octets smaller o if ATYP is X'03' - 262+method_dependent octets smaller o if ATYP is X'04' - 20+method_dependent octets smaller我琢磨是不是这个的原因,recv那里写的是262字节了。我把262改的很小也是可以的,但是改的太小,比如10就报错了。如果有知道原因的朋友,请留言告诉我。
还有几个是Python语法相关的问题,在源码中看到了第39行的port[0],就是因为struct.unpack返回的是一个元组,即使返回值只要一个元素。recv(1)的意思是接收<=1个byte的数据,[0]取第一个byte也就是后续域名的长度。
上面源码实现的只是Socks5的TCP连接方式。
参考链接:http://blog.csdn.net/liujiayu2/article/details/51691778