Skip to content

fix(go-core): local_address binding, Do() field drops, TLS13AutoRetry JA3 corruption (replaces #40)#65

Merged
Danny-Dasilva merged 6 commits intomainfrom
pr-40-go-core-fixed
May 7, 2026
Merged

fix(go-core): local_address binding, Do() field drops, TLS13AutoRetry JA3 corruption (replaces #40)#65
Danny-Dasilva merged 6 commits intomainfrom
pr-40-go-core-fixed

Conversation

@Danny-Dasilva
Copy link
Copy Markdown
Owner

Summary

Replacement for #40 (Stefan Meinecke). Same three Go-core fixes, rebased on current main with conflicts resolved against #51 (IdleConnTimeout 60s) and #52 (DisableKeepAlives propagation) which both landed on the transport layer after #40 was opened.

Fixes (preserved from #40)

  1. local_address binding — outbound IP selection now actually binds.
  2. Do() field drops — request fields no longer dropped on Do (ServerName/TLS13AutoRetry/DisableGrease) + dispatchSSEAsync infinite-loop on cancel/EOF.
  3. TLS13AutoRetry JA3 supported_groups corruption — preserves the original ClientHello fingerprint on TLS 1.3 retry. This is the most critical fix: previously every Firefox/Safari user (who advertise FFDHE/P-384/P-521) was getting silently corrupted JA3 fingerprints whenever TLS13AutoRetry kicked in.

Conflict resolution

The only conflict was in golang/client.go getOrCreateClient() — combined #40's localAddress parameter with #52's browser.DisableKeepAlives = true propagation when enableConnectionReuse=false. Both behaviors retained; neither dropped.

Co-authored-by: Stefan Meinecke smeinecke@users.noreply.github.com

Test plan

  • go vet ./... — no new warnings vs main (one pre-existing index.go cancel-leak warning shifted by one line; PR actually removed an unreachable-code warning)
  • go test ./... -short clean
  • uv run pytest -m "not live" clean post-rebase

Closes #40 once merged.

smeinecke and others added 6 commits April 28, 2026 11:34
… a specific local IP

- Add default_local_address to config mapping and validation
- Add local_address field to Request schema with serialization
- Thread localAddress through Go client pooling, key generation, and dialer creation
- Apply local IP binding to both direct connections and proxy connections
- Validate local_address is a valid IP address before use
…sync infinite loop on cancel/EOF

1. Do() silently dropped ServerName, TLS13AutoRetry, and DisableGrease when building the Browser struct. These fields were added to processRequest() in upstream commits 36f5171 (Draft Release #400, adds ServerName+TLS13AutoRetry) and a483b68 (Release 2.0.4 #390, adds DisableGrease) but were never propagated to Do().

2. dispatchSSEAsync used bare `break` inside `for { select { ... } }`, which only breaks out of the select, not the outer loop — causing an infinite spin on context cancellation, EOF, or read errors. Introduced in upstream 5990fef (Major Release 2.0.0 #387). Fixed by adding a `sseLoop:` label and using `break sseLoop`.
…grade

convertJA3ForTLS13 was hard-coding tokens[3] = "29-23", silently dropping
P-384 (24), P-521 (25), FFDHE2048 (256), FFDHE3072 (257) and any other
groups from the JA3 string before the first handshake attempt. This made
TLS13AutoRetry (enabled by default) corrupt the fingerprint being applied.

Fix: filter the groups field using isTLS13CompatibleCurve() instead of
replacing it, preserving all RFC 8446 §4.2.7 compatible NamedGroups in
their original order. Also extend isTLS13CompatibleCurve() to include
FFDHE groups (256-260) which are valid TLS 1.3 NamedGroups per RFC 7919
and are advertised by Firefox/Safari in the supported_groups extension.
The PR #40 / #65 localAddress implementation silently dropped the local-IP
bind on three proxy/transport paths. This change closes those gaps so the
LocalAddr the user provided actually controls the kernel-level bind on
the client->proxy socket on every supported scheme.

Gaps addressed (identified in the code-trace and network-research reports
at .claude/cache/agents/{code-trace,network-research}-localaddress-proxy):

1. SOCKS proxies (connect.go)
   The localAddress -> baseDialer block ran AFTER the scheme switch's
   socks5/socks5h/socks4 early returns, so the SOCKS dialers never saw
   LocalAddr. baseDialer is now built before the switch and:
   - socks5/socks5h: passed as the forward Dialer to proxy.SOCKS5, so
     the TCP-to-proxy connect honors LocalAddr.
   - socks4: when localAddress is set, h12.io/socks (which always uses
     net.DialTimeout internally with no LocalAddr hook) is bypassed via
     a small in-package socks4ContextDialer that performs the CONNECT
     handshake over a baseDialer.DialContext'd conn. With localAddress
     unset, the existing h12.io/socks path is preserved verbatim.

2. HTTPS proxies (connect.go)
   The default TLS-to-proxy step used tls.Dial(network, addr, &tlsConf),
   which bypasses any custom net.Dialer and silently dropped LocalAddr.
   Replaced with c.tlsDialer.DialContext (LocalAddr-aware) followed by
   tls.Client + HandshakeContext so the bind takes effect on the
   client->HTTPS-proxy TCP socket.

3. HTTP/3 / QUIC (http3.go)
   net.ListenPacket("udp", "") ignored localAddress entirely. Threaded
   LocalAddress through Browser -> roundTripper -> http3Dial; when set,
   the UDP socket is bound to <ip>:0. Empty localAddress preserves the
   prior kernel-chooses behavior.

Also expands the cycleTLSRequestOptions.LocalAddress field comment to
document that, when a Proxy is in the path, the bind applies to the
client->proxy hop only — the proxy host always sees this IP, but the
destination server never does. This is routing/interface control, not
an anonymizer against the proxy itself.

Verified: go build ./..., go vet ./... (only pre-existing index.go cancel
warnings remain, line numbers shifted by docstring), go test ./... -short
all pass on Go 1.26.0.
@Danny-Dasilva Danny-Dasilva merged commit 633710f into main May 7, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants