SS(socks5)代理服務端的實驗版實現
阿新 • • 發佈:2019-02-19
花了一下午,其實也就是改了點程式碼,複用客戶端的實現。
不過在buffer加密解密這方面似乎有問題,例如淘寶這種複雜的網站就打不開了....
日誌顯得太亂了,需要修改
其實socks5在瀏覽器應用的這種場景下,只跟瀏覽器執行的服務端有關係。
協議只是為了把本地ip、埠、目的ip、埠給封裝在一個報文頭裡,使得接下來的tcp連線可以在服務端得到複製。
因為決定一個tcp連線的就是上述的埠和ip。
程式碼的handle_socks5()就是在第一次建立連線時,通過包頭的地址與埠,複製一個local所需要的tcp連線。
這樣,對於tcp所請求的目標伺服器來說,它並不知道你使用了代理,只會知道你的ip是所使用的伺服器的ip(通常就是所使用的VPS的ip啦)
需要注意的是,由於不確定建立連線時所需要獲取的連線的報文頭長度(因為網址的域名長度不一,而如果先獲取addr_len的話,所使用的加密不一定支援一個位元組一個位元組地解密,如果可以的話那這個加密就有點low了),所以先獲取固定長度的buffer,再統一解密。那麼除去報文頭的部分,就是正兒八經的tcp報文了,需要把這部分報文傳送到建立好的連線。
對於加密解密相關需要加深學習啊
客戶端的實現在這裡
突然感覺select不是這麼用的...會開好多個select,應該是加入set中
#!/usr/bin/python # -*- coding: utf-8 -*- from __future__ import absolute_import,division, \ with_statement import os, sys reload(sys) sys.setdefaultencoding('utf-8') import json import SocketServer import socket import struct import select sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) from shadowsocks import encrypt def send_all(sock, data): bytes_sent = 0while True: r = sock.sendall(data[bytes_sent:]) print "r:" + str(r) return len(data) class ThreadingTCPServer(SocketServer.ThreadingMixIn,SocketServer.TCPServer): #class ThreadingTCPServer(SocketServer.TCPServer): # 單執行緒除錯時使用 # 繼承了這兩個類第一個類放前面表示優先查詢他的方法,而他有多執行緒的handler # init的方法,在第二個類中找到,於是入參就是埠加RequestHandlerClass # process_request Overridden by ThreadingMixIn. allow_reuse_address = True class Socks5Server(SocketServer.StreamRequestHandler): # 繼承自Base class for request handler classes,自定義handle方法即可 def encrypt(self, data): return self._encryptor.encrypt(data) def decrypt(self, data): return self._encryptor.decrypt(data) def send_encrypt(self, sock, data): sock.send(self.encrypt(data)) def handle_socks5(self): try: self._encryptor = encrypt.Encryptor(str(KEY), str(METHOD)) sock = self.connection buff = sock.recv(1024) #print "buff len:",str(len(buff)) data = self._encryptor.decrypt(buff) #print "data:",data[1:]," end data",str(len(data)) addrtype = ord(data[0]) print "addrtype :", str(addrtype),"^" if addrtype == 1: addr = socket.inet_ntoa(data[1:5]) #4bytes for 1 ip addrport = data[5:7] port = struct.unpack('>H', addrport) print "addr:%s addrpot:%d" %(addr,port[0]) addrlen = 3 #not real len, -1 for addrlen elif addrtype == 3: addrlen = ord(data[1]) addr = data[2:2 + addrlen] addrport = data[2 + addrlen:4 + addrlen] port = struct.unpack('>H', addrport) print "addrlen:", addrlen, print " addr %s" % (addr) #print " addrport %s %d" %(addrport,port[0]) else: print "\n\n\n\n warning :addr_type not support"+str(addrtype)+"++\n\n\n" print "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" print " warning :addr_type not support\n" print "connect to %s:%d" % (addr, port[0]) remote = socket.socket(socket.AF_INET, socket.SOCK_STREAM) remote.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) remote.connect((addr, port[0])) send_all(remote,data[4 + addrlen:]) self.handle_tcp(sock,remote) except Exception,e: print e def handle_tcp(self,sock, remote): try: fdset = [sock, remote] while True: #print "handle tcp loop\n" r, w, e = select.select(fdset, [], []) # use select I/O multiplexing model if sock in r: # if local socket is ready for reading data = sock.recv(1024) #print "sock data:",str(self.decrypt(data)),"sock data end" if len(data) <= 0: # received all data print 'sock len' + str(len(data)) break #print 'sock sendall' result = send_all(remote, self.decrypt(data)) # send data after encrypting if result < len(data): print 'len' + str(result) + 'failed to send all data' #raise Exception('failed to send all data sock') if remote in r: # remote socket(proxy) ready for reading data = remote.recv(1024) #print "remote data:", str(data), "remote data end" if len(data) <= 0: print 'remote len' + str(len(data)) break #print 'remote sendall' result = send_all(sock, self.encrypt(data)) # send to local socket(application) if result < len(data): print 'failed to send all data remote' #raise Exception('failed to send all data remote') except Exception,e: sock.close() remote.close() print e def handle(self): self.handle_socks5() if __name__ == '__main__': os.chdir(os.path.dirname(__file__) or './') with open('config.json', 'rb') as f: config = json.load(f) SERVER = config['server'] REMOTE_PORT = config['server_port'] PORT = config['local_port'] KEY = config['password'] METHOD = config['method'] server = ThreadingTCPServer(('', REMOTE_PORT), Socks5Server) print "server start prot: %d" % PORT server.serve_forever() """serve_forever -> _handle_request_noblock -> process_request -> finish_request -> -> self.RequestHandlerClass(request, client_address, self) -> handle() 所以重寫了requestHandlerClass"""