mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-07-22 07:48:27 +00:00
The JNI wrapper answered every failed client.connect() with SOCKS reply 0x05,
so a domain that no longer exists, an exit that timed out, and a genuinely
refused port were indistinguishable. Java renders 0x05 as
SocketException("SOCKS: Connection refused"), so the whole failure taxonomy
collapsed into one opaque string and callers could only apply their most
generic retry policy. On a cold start with the default settings — torType
INTERNAL and newRelaysViaTor true, so the entire outbox fan-out is routed
through Tor — 639 of roughly 768 relay failures arrived this way.
This is why the DNS classification added earlier was effectively dead code for
default users: name resolution happens at the exit, so UnknownHostException is
never raised locally.
socks_reply_for() maps ErrorKind onto the codes Java surfaces with distinct
messages, so a caller can tell "this relay is gone" from "this circuit had a
bad minute". No new dependency — arti-client re-exports ErrorKind and HasKind.
RemoteHostResolutionFailed is mapped to 0x04 even though Arti documents it as
retryable, because an exit's resolver failing is not proof the name is dead.
The caller's response to 0x04 is a bounded backoff rather than permanent
condemnation, which is a retry, just a slower one; probing the relays that
produced this error found 17 of 21 to be NXDOMAIN from an ordinary resolver, so
the conservative reading costs far more than it saves. Changing that arm to
0x01 restores Arti's reading if that tradeoff is ever judged wrong.
Verified on device: Java now reports "SOCKS: TTL expired", "SOCKS: Host
unreachable", "SOCKS: Connection not allowed by ruleset" and friends where
everything was previously "SOCKS: Connection refused".
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>