如何将http.server绑定到远程SOCKS5代理并监听端口?
如何让Python的http.server通过SOCKS5代理的BIND命令监听远程端口
我有一个Web服务器,需要通过远程SOCKS5代理响应请求。希望让Web服务器连接到代理,发送BIND命令并监听连接,请问该如何配置http.server来绑定并监听远程端口?
我尝试重写server_bind方法但未成功,代码如下:
class TestHTTPServer ( http.server.HTTPServer ): def __init__ ( self, server_address, RequestHandlerClass, proxy_host=None, proxy_port=None, bind_and_activate=True ): if ':' in (str)(server_address[0]): self.address_family = socket.AF_INET6 else: self.address_family = socket.AF_INET self.proxy_host = proxy_host self.proxy_port = proxy_port super().__init__ ( server_address, RequestHandlerClass, bind_and_activate ) def serve_forever ( self, poll_interval = 0.5 ): self.socket.settimeout ( poll_interval ) while not stop_unittest_event.is_set (): try: self.handle_request () except OSError: pass def server_bind ( self ): if self.proxy_host is None or self.proxy_port is None: super().server_bind () else: self.socket = socks.socksocket() self.socket.set_proxy(socks.SOCKS5, self.server_address[0], self.server_address[1]) self.socket.bind(self.server_address)
你当前的代码错误在于,socks.socksocket的set_proxy方法是配置客户端通过代理发起请求的逻辑,而非让代理主动监听端口并将请求转发到你的服务器。要实现类似FTP主动模式的效果,需要严格遵循SOCKS5的BIND命令流程:让代理在远程绑定端口,之后将发往该端口的所有连接转发到你的服务器。
修改后的实现代码
import socket import socks import http.server import threading # 自定义停止事件,根据实际业务逻辑调整 stop_unittest_event = threading.Event() class SOCKS5BoundHTTPServer(http.server.HTTPServer): def __init__(self, server_address, RequestHandlerClass, proxy_host, proxy_port, bind_and_activate=True): self.address_family = socket.AF_INET if ':' not in str(server_address[0]) else socket.AF_INET6 self.RequestHandlerClass = RequestHandlerClass self.proxy_host = proxy_host self.proxy_port = proxy_port # 传给代理的绑定地址:0.0.0.0:0表示让代理随机分配端口,也可以指定固定端口 self.target_bind_addr = server_address self.bound_port = None # 完成SOCKS5 BIND流程初始化 self._setup_socks5_bind() if bind_and_activate: self.server_activate() def _setup_socks5_bind(self): # 创建SOCKS5套接字并连接代理 self.socket = socks.socksocket(self.address_family, socket.SOCK_STREAM) self.socket.connect((self.proxy_host, self.proxy_port)) # SOCKS5协议握手 self.socket.sendall(b'\x05\x01\x00') resp = self.socket.recv(2) if resp != b'\x05\x00': raise Exception("SOCKS5代理握手失败") # 构造BIND请求包 addr_type = 1 if self.address_family == socket.AF_INET else 4 addr_bytes = socket.inet_aton(self.target_bind_addr[0]) if addr_type == 1 else socket.inet_pton(self.address_family, self.target_bind_addr[0]) port_bytes = self.target_bind_addr[1].to_bytes(2, byteorder='big') bind_request = b'\x05\x02\x00' + bytes([addr_type]) + addr_bytes + port_bytes self.socket.sendall(bind_request) # 解析代理返回的绑定结果 resp_header = self.socket.recv(4) if len(resp_header) != 4 or resp_header[0] != 0x05 or resp_header[1] != 0x00: raise Exception(f"SOCKS5 BIND请求失败,响应码: {resp_header[1]}") addr_type_resp = resp_header[3] if addr_type_resp == 1: self.bound_addr = socket.inet_ntoa(self.socket.recv(4)) elif addr_type_resp == 4: self.bound_addr = socket.inet_ntop(socket.AF_INET6, self.socket.recv(16)) else: raise Exception("不支持的地址类型") self.bound_port = int.from_bytes(self.socket.recv(2), byteorder='big') print(f"代理已绑定远程地址: {self.bound_addr}:{self.bound_port}") def accept(self): # 接收代理的新连接通知 resp = self.socket.recv(4) if len(resp) != 4 or resp[0] != 0x05 or resp[1] != 0x00: raise Exception("接收代理连接通知失败") # 解析发起请求的客户端地址(可选,可忽略) addr_type = resp[3] if addr_type == 1: client_addr = socket.inet_ntoa(self.socket.recv(4)) elif addr_type == 4: client_addr = socket.inet_ntop(socket.AF_INET6, self.socket.recv(16)) else: client_addr = "未知地址" client_port = int.from_bytes(self.socket.recv(2), byteorder='big') # 向代理发起CONNECT请求,建立与客户端的实际数据连接 connect_request = b'\x05\x01\x00\x01' + socket.inet_aton(self.proxy_host) + self.bound_port.to_bytes(2, byteorder='big') self.socket.sendall(connect_request) # 验证CONNECT响应 connect_resp = self.socket.recv(4) if connect_resp[1] != 0x00: raise Exception(f"CONNECT到代理绑定端口失败,响应码: {connect_resp[1]}") # 跳过响应中的地址信息段 addr_type_c = connect_resp[3] if addr_type_c == 1: self.socket.recv(4) elif addr_type_c == 4: self.socket.recv(16) self.socket.recv(2) # 返回与客户端的连接套接字及客户端地址 return (self.socket, (client_addr, client_port)) def serve_forever(self, poll_interval=0.5): self.socket.settimeout(poll_interval) while not stop_unittest_event.is_set(): try: self.handle_request() except (OSError, socket.timeout): pass
使用示例
if __name__ == "__main__": # 替换为你的SOCKS5代理地址和端口 PROXY_HOST = "your-socks5-proxy-host" PROXY_PORT = 1080 # 传给代理的绑定地址,0.0.0.0:0让代理随机分配端口 SERVER_ADDR = ("0.0.0.0", 0) # 使用默认HTTP请求处理器,也可以自定义 handler_class = http.server.SimpleHTTPRequestHandler server = SOCKS5BoundHTTPServer(SERVER_ADDR, handler_class, PROXY_HOST, PROXY_PORT) print("服务器启动,等待代理转发请求...") server.serve_forever()
注意事项
- 确保你的SOCKS5代理支持BIND命令,大部分公共代理会禁用该功能,需使用允许BIND的私有代理
- 代码仅实现了基础的IPv4支持,如需IPv6可对应调整地址解析逻辑
- 实际生产环境中需添加更多错误处理、超时控制和异常恢复逻辑
内容的提问来源于stack exchange,提问作者user304539
相关产品推荐
相关产品推荐

