fix: SOCKS5 隧道连接超时
修复子线程事件循环中 asyncio.wait_for 包装 open_connection 导致的读取取消问题。 改用 socket 层面的 timeout 设置,不再用 wait_for 包装。 同时修复 _reply 中 writer.write 不需要 await 的问题。
This commit is contained in:
+13
-10
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user