fix: SOCKS5 隧道连接超时

修复子线程事件循环中 asyncio.wait_for 包装 open_connection 导致的读取取消问题。
改用 socket 层面的 timeout 设置,不再用 wait_for 包装。

同时修复 _reply 中 writer.write 不需要 await 的问题。
This commit is contained in:
Your Name
2026-07-16 18:50:43 +08:00
parent a65ee75894
commit 4fbb63e0b9
+13 -10
View File
@@ -4,6 +4,7 @@ import logging
import struct import struct
import time import time
import uuid import uuid
import socket as _socket
import ipaddress import ipaddress
from datetime import datetime, timezone from datetime import datetime, timezone
@@ -127,11 +128,11 @@ class ConnectionHandler:
methods = header[2:2 + n_methods] methods = header[2:2 + n_methods]
# 检查是否支持 # 检查是否支持
if METHOD_USERPASS in methods and self.config.auth_method == "userpass": if METHOD_USERPASS in methods and self.config.auth_method == "userpass":
await self.writer.write(bytes([SOCKS_VERSION, METHOD_USERPASS])) self.writer.write(bytes([SOCKS_VERSION, METHOD_USERPASS]))
elif METHOD_NO_AUTH in methods: elif METHOD_NO_AUTH in methods:
await self.writer.write(bytes([SOCKS_VERSION, METHOD_NO_AUTH])) self.writer.write(bytes([SOCKS_VERSION, METHOD_NO_AUTH]))
else: else:
await self.writer.write(bytes([SOCKS_VERSION, METHOD_NOT_ACCEPTED])) self.writer.write(bytes([SOCKS_VERSION, METHOD_NOT_ACCEPTED]))
return False return False
await self.writer.drain() await self.writer.drain()
return True return True
@@ -177,14 +178,14 @@ class ConnectionHandler:
log.warning("认证失败: %s (user=%s)", self.src_ip, username) log.warning("认证失败: %s (user=%s)", self.src_ip, username)
await self.user_service.log_event("auth_fail", username, await self.user_service.log_event("auth_fail", username,
self.src_ip, "密码错误或账号不可用") self.src_ip, "密码错误或账号不可用")
await self.writer.write(bytes([1, 0x01])) # auth fail self.writer.write(bytes([1, 0x01])) # auth fail
await self.writer.drain() await self.writer.drain()
return False return False
self.username = username self.username = username
log.info("[%s] 认证成功: %s (from %s)", self.conn_id, username, self.src_ip) log.info("[%s] 认证成功: %s (from %s)", self.conn_id, username, self.src_ip)
await self.user_service.log_event("auth_success", username, self.src_ip) await self.user_service.log_event("auth_success", username, self.src_ip)
await self.writer.write(bytes([1, 0x00])) # auth ok self.writer.write(bytes([1, 0x00])) # auth ok
await self.writer.drain() await self.writer.drain()
return True return True
@@ -273,11 +274,13 @@ class ConnectionHandler:
# 连接目标 # 连接目标
try: try:
reader, writer = await asyncio.wait_for( # 注意:不用 asyncio.wait_for 包装 open_connection
asyncio.open_connection(addr, port), # 在子线程事件循环中 wait_for 会导致连接成功后读取被取消
timeout=self.config.timeout sock = _socket.socket(_socket.AF_INET, _socket.SOCK_STREAM)
) sock.settimeout(self.config.timeout)
except (asyncio.TimeoutError, OSError) as e: sock.connect((addr, port))
reader, writer = await asyncio.open_connection(sock=sock)
except (OSError, Exception) as e:
log.warning("[%s] 无法连接 %s:%d: %s", self.conn_id, addr, port, e) log.warning("[%s] 无法连接 %s:%d: %s", self.conn_id, addr, port, e)
rep = REP_CONN_REFUSED if "refused" in str(e).lower() else REP_HOST_UNREACHABLE rep = REP_CONN_REFUSED if "refused" in str(e).lower() else REP_HOST_UNREACHABLE
await self._reply(rep) await self._reply(rep)