由淺入深寫代理(4)-socks5-代理

接下來我們用 python 寫下 socks5 服務端的實現

0x03 socks5 實現

先看下整體代碼

import loggingnimport socketnimport structnimport selectnimport threadingnndef send_data(sock, data):n print(data)n bytes_sent = 0n while True:n r = sock.send(data[bytes_sent:])n if r < 0:n return rn bytes_sent += rn if bytes_sent == len(data):n return bytes_sentnndef handle_tcp(sock, remote):n # 處理 client socket 和 remote socket 的數據流n try:n fdset = [sock, remote]n while True:n # 用 IO 多路復用 select 監聽套接字是否有數據流n r, w, e = select.select(fdset, [], [])n if sock in r:n data = sock.recv(4096)n if len(data) <= 0:n breakn result = send_data(remote, data)n if result < len(data):n raise Exception(failed to send all data)nn if remote in r:n data = remote.recv(4096)n if len(data) <= 0:n breakn result = send_data(sock, data)n if result < len(data):n raise Exception(failed to send all data)n except Exception as e:n raise(e)n finally:n sock.close()n remote.close()nndef handle_con(sock, addr):n # 接受客戶端來的請求,socks5 的 認證和連接過程nn sock.recv(256)n # 無需進一步認證信息n sock.send(b"x05x00")n data = sock.recv(4) or x00 * 4n # CMD 為 0x01 也就是 CONNECT 繼續n mode = data[1]n if mode != 1:n returnn # DST.ADDR 有三種形式,分別做判斷n addr_type = data[3]n if addr_type == 1:n addr_ip = sock.recv(4)n remote_addr = socket.inet_ntoa(addr_ip)n elif addr_type == 3:n addr_len = int.from_bytes(sock.recv(1), byteorder=big)n remote_addr = sock.recv(addr_len)n elif addr_type == 4:n addr_ip = sock.recv(16)n remote_addr = socket.inet_ntop(socket.AF_INET6, addr_ip)n else:n returnn # DST.PORTn remote_addr_port = struct.unpack(>H, sock.recv(2))nn # 返回給客戶端 successn reply = b"x05x00x00x01"n reply += socket.inet_aton(0.0.0.0) + struct.pack(">H", 8888)n sock.send(reply)nn # 拿到 remote address 的信息後,建立連接n try:n remote = socket.create_connection((remote_addr, remote_addr_port[0]))n logging.info - 這個網站可出售 - 最佳的Logging 來源和相關信息(connecting %s:%d % (remote_addr, remote_addr_port[0]))n except socket.error as e:n logging.error(e)n returnnn handle_tcp(sock, remote)ndef main():n socketServer = socket.socket(socket.AF_INET, socket.SOCK_STREAM)n socketServer.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)nn socketServer.bind((, 8888))n socketServer.listen(5)nn try:n while True:n sock, addr = socketServer.accept()n t = threading.Thread(target=handle_con, args=(sock, addr))n t.start()n except socket.error as e:n logging.error(e)n except KeyboardInterrupt:n socketServer.close()nnnif __name__ == __main__:n main()n

首先,上面只是個 socks5 伺服器的簡單實現,處理 TCP 的轉發,無需密碼認證。

接下來我們分開來看,主要是三個部分 main(), handle_con(), handle_tcp()

  • main() 函數大家其實挺熟悉的,就是前面教程介紹的 socket 編程的線程方法,綁定套接字監聽,然後有客戶端請求後,調用 `handle_con()`
  • handle_con 實現了 socks5 的 認證和連接過程,大家對照下注釋和前面的 socks5 協議,應該挺容易看懂。
  • 認證完成後,就開始轉發請求了, handle_tcp() 實現了這個功能,這裡面用到了 select ,IO 多路復用模式。其實一開始怕大家不理解,也想用線程來實現,不過發現 IO 多路復用在一個線程中就能監聽多個套接字,代碼比多線程方式更加簡潔,索性就這麼用了。這裡面沒有根據平台去判斷用 select, poll 還是 epoll,感興趣的話可以直接看 ss 的實現 eventloop

代碼見 github: socks5_server.py

下篇我們看看 socks5 伺服器如何和 client 端交互。

參考鏈接:

* github.com/felix021/sso

* github.com/RicterZ/repr


推薦閱讀:

Python資料庫起航篇|零基礎起步
利用python完成大數據建模前期數據準備
用SVM看看技術指標有沒有用
Python新階段
Cython 基本用法

TAG:Python | 计算机网络 | socks代理 |