test: classify SOCKS5 peers via getpeerinfo addrbind

The SOCKS5 destinations factory classified connections by scanning
debug.log for connection attempts to the requested address and port.
The destination is not unique per connection, so the log cannot
identify which attempt is being served: first-match returned a stale
type when an automatic connection reused an address private broadcast
had already used, and latest-match still breaks if two attempts to the
same address overlap.

Match the exact connection instead: the source addr:port of the
proxy's client socket equals the node's addrbind for that peer, so
looking it up in getpeerinfo identifies precisely the connection being
served and returns its connection_type. This also stops treating
debug.log contents as a stable interface.

Co-authored-by: Greg Sanders <gsanders87@gmail.com>
This commit is contained in:
Henry Romp
2026-08-03 18:31:01 -04:00
parent 1ed14c6122
commit 4e8c4bc794
3 changed files with 27 additions and 26 deletions

View File

@@ -104,6 +104,7 @@ class Socks5Configuration():
# and it decides where the connection is redirected to. It is passed:
# - the address the client requested to connect to
# - the port the client requested to connect to
# - the client's socket address as seen by the proxy, formatted as host:port
# It is supposed to return an object like:
# {
# "actual_to_addr": "127.0.0.1"
@@ -140,8 +141,9 @@ class Socks5Connection():
"""Handle socks5 request according to RFC1928."""
log_exception_prefix = "Socks5Connection.handle(): "
try:
proxy_client = format_sock(self.conn, local=False)
log_exception_prefix = ("Socks5Connection.handle("
f"client={format_sock(self.conn, local=False)}, "
f"client={proxy_client}, "
f"proxy={format_sock(self.conn, local=True)}): ")
# Verify socks version
@@ -193,7 +195,9 @@ class Socks5Connection():
port_hi,port_lo = recvall(self.conn, 2)
port = (port_hi << 8) | port_lo
# Send dummy response
# Reply SUCCESS before calling destinations_factory, so the client can finish
# establishing the connection and register the peer; factories that consult
# getpeerinfo depend on that order.
self.conn.sendall(bytearray([0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
cmdin = Socks5Command(cmd, atyp, addr, port, username, password)
@@ -205,7 +209,7 @@ class Socks5Connection():
if self.serv.is_running():
if self.serv.conf.destinations_factory is not None:
dest = self.serv.conf.destinations_factory(requested_to_addr, port)
dest = self.serv.conf.destinations_factory(requested_to_addr, port, proxy_client)
if dest is not None:
logger.debug(f"Serving connection to {requested_to}, will redirect it to "
f"{dest['actual_to_addr']}:{dest['actual_to_port']} instead")