1
0

util: refactor Tor-detection to be async

- on my PC, with Tor Browser running (socks proxy on port 9150), detect_tor_socks_proxy took ~4.01 seconds
- this was because we probed port 9050, 2 sec timeout, 9051, 2 sec timeout, 9150, ~few ms to succeed
- instead we now probe all ports concurrently
This commit is contained in:
SomberNight
2025-03-05 14:31:53 +00:00
parent f1e9abf04e
commit 5dc2ae243e
2 changed files with 45 additions and 28 deletions

View File

@@ -1547,33 +1547,51 @@ class NetworkJobOnDefaultServer(Logger, ABC):
return s
def detect_tor_socks_proxy() -> Optional[Tuple[str, int]]:
async def detect_tor_socks_proxy() -> Optional[Tuple[str, int]]:
# Probable ports for Tor to listen at
candidates = [
("127.0.0.1", 9050),
("127.0.0.1", 9051),
("127.0.0.1", 9150),
]
for net_addr in candidates:
if is_tor_socks_port(*net_addr):
return net_addr
return None
proxy_addr = None
async def test_net_addr(net_addr):
is_tor = await is_tor_socks_port(*net_addr)
# set result, and cancel remaining probes
if is_tor:
nonlocal proxy_addr
proxy_addr = net_addr
await group.cancel_remaining()
async with OldTaskGroup() as group:
for net_addr in candidates:
await group.spawn(test_net_addr(net_addr))
return proxy_addr
def is_tor_socks_port(host: str, port: int) -> bool:
@log_exceptions
async def is_tor_socks_port(host: str, port: int) -> bool:
# mimic "tor-resolve 0.0.0.0".
# see https://github.com/spesmilo/electrum/issues/7317#issuecomment-1369281075
# > this is a socks5 handshake, followed by a socks RESOLVE request as defined in
# > [tor's socks extension spec](https://github.com/torproject/torspec/blob/7116c9cdaba248aae07a3f1d0e15d9dd102f62c5/socks-extensions.txt#L63),
# > resolving 0.0.0.0, which being an IP, tor resolves itself without needing to ask a relay.
writer = None
try:
with socket.create_connection((host, port), timeout=10) as s:
# mimic "tor-resolve 0.0.0.0".
# see https://github.com/spesmilo/electrum/issues/7317#issuecomment-1369281075
# > this is a socks5 handshake, followed by a socks RESOLVE request as defined in
# > [tor's socks extension spec](https://github.com/torproject/torspec/blob/7116c9cdaba248aae07a3f1d0e15d9dd102f62c5/socks-extensions.txt#L63),
# > resolving 0.0.0.0, which being an IP, tor resolves itself without needing to ask a relay.
s.send(b'\x05\x01\x00\x05\xf0\x00\x03\x070.0.0.0\x00\x00')
if s.recv(1024) == b'\x05\x00\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00':
async with async_timeout(10):
reader, writer = await asyncio.open_connection(host, port)
writer.write(b'\x05\x01\x00\x05\xf0\x00\x03\x070.0.0.0\x00\x00')
await writer.drain()
data = await reader.read(1024)
if data == b'\x05\x00\x05\x00\x00\x01\x00\x00\x00\x00\x00\x00':
return True
except socket.error:
pass
return False
return False
except (OSError, asyncio.TimeoutError):
return False
finally:
if writer:
writer.close()
AS_LIB_USER_I_WANT_TO_MANAGE_MY_OWN_ASYNCIO_LOOP = False # used by unit tests