Commit Graph

36 Commits

Author SHA1 Message Date
ginuerzh fb0cf72446 fix(forwarder): record post-rewrite URI in HTTP recorder
After RewriteURL mutates req.URL.Path, ro.HTTP.URI still held the
pre-rewrite value because the recorder object was initialized before
the rewrite loop. Update ro.HTTP.URI to match the rewritten path so
the recorded HTTP request reflects the URI actually sent upstream.
2026-06-27 18:50:46 +08:00
ginuerzh a32ffd5608 fix(sniffing): pass bypass option to sniffer HandleHTTP/HandleTLS calls
When sniffing is enabled, the sniffer extracts the real domain from HTTP
Host header or TLS SNI and checks bypass rules against it. However, most
handlers were not passing WithBypass(...) to the sniffer, so the sniffer
internal bypass check was always skipped.

Additionally, the TLS-to-HTTP fallback paths in internal util sniffer
and forwarder also omitted bypass when constructing options for the
recursive HandleHTTP call after decrypting TLS.

Add WithBypass to all sniffer call sites that were missing it across
handlers (http, relay, socks4, socks5, ss, sshd, unix) and internal
TLS fallback paths.

Also fix import ordering in handler/http/connect.go.

Fixes go-gost/gost#874
2026-06-23 20:54:26 +08:00
ginuerzh e814852ca1 fix(sniffing): re-dial upstream on HTTP Host change + surface no-SNI errors
sniffer_http:
When DNS override directs multiple domains to the same proxy IP, the
browser may reuse a keep-alive connection for a different host. The
HTTP keep-alive loop now detects Host header changes and re-dials a
new upstream connection with correct node selection, preventing CDN
errors (Fastly unknown domain, CloudFront 403).

sniffer_tls:
Return a descriptive error when TLS ClientHello has no SNI, instead
of silently returning nil. This makes the connection drop visible in
logs and recorder output.

Fixes go-gost/gost#479
2026-06-21 16:37:47 +08:00
ginuerzh f09a7a69b3 fix(sniffing): return early on empty SNI to avoid 'missing port in address' errors (#645)
When TLS ClientHello has no SNI extension (ServerName is empty),
normalizeHost returns empty string. Both sniffer_tls.go paths (sniffing
and forwarder packages) previously still called dial with an empty host,
cascading 'missing port in address' errors through connector, router,
and service layers.

Now returns early with a debug-level log, silently closing the
connection instead of attempting to dial upstream.
2026-06-05 20:46:00 +08:00
ginuerzh 382d47ea12 fix: nil HTTP recorder ref in WebSocket goroutines, add tunnel loop detection
- handler/http/websocket, forwarder/sniffer_ws, sniffing/sniffer_ws: nil
  ro2.HTTP in WebSocket copy goroutines to avoid data races on the shared
  HTTP recorder object
- handler/tunnel/bind: remove ingress rule check that incorrectly
  overrode the endpoint host for non-matching ingress rules
- handler/tunnel/entrypoint: add forwarding loop detection by checking
  Gost-Forwarded-Node header for the entrypoint's own node ID
2026-06-01 18:28:01 +08:00
ginuerzh cf89192211 feat(forwarder/sniffer): add HTTP request body rewriting, deprecate rewriteBody in favor of rewriteResponseBody
- Add RewriteRequestBody field to HTTPNodeConfig (yaml: rewriteRequestBody)
- Add RewriteRequestBody field to HTTPNodeSettings in core chain
- Add rewriteReqBody function symmetric to rewriteRespBody in sniffer_rewrite.go
- Wire up rewriteReqBody call in httpRoundTrip before body wrapping for recording
- Add new RewriteResponseBody config field, deprecate old RewriteBody
- Parse both RewriteRequestBody and RewriteResponseBody in node config parsing
- Add 10 test cases covering nil, skip, content-type filter, and chained rewrites
- Bump core dependency to v0.4.1
- Fix: clone response header map in internal/util/sniffing/sniffer_http.go
2026-05-31 19:46:41 +08:00
ginuerzh 62ce7e189e fix(forwarder/sniffer_http): clone response header map and sync request header to recorder
Fix two data races in Sniffer.httpRoundTrip:
- resp.Header was assigned directly to ro.HTTP.Response.Header without
  cloning, creating shared mutable state between the response and recorder.
- httpSettings.RequestHeader was set on req.Header but not propagated to
  ro.HTTP.Request.Header, causing the recorder to miss configured headers.
2026-05-31 18:02:13 +08:00
ginuerzh 722dde5cfc fix(handler/forward): split readTimeout from pipe idleTimeout to prevent 15s download abort
The readTimeout field (default 15s) was being applied via xnet.Pipe to both
directions of a bidirectional proxy connection. During asymmetric transfers
(e.g. HTTP file download), the direction reading from the tunnel sees no
data after the initial request is forwarded, causing SetReadDeadline to fire
after 15s and abort the entire transfer.

Fix: add a separate idleTimeout field (default 0=disabled) to the metadata
structs in both forward/local and forward/remote handlers, and switch
xnet.Pipe to use idleTimeout instead of readTimeout. The readTimeout field
now only applies to the initial protocol sniffing/handshake phase.

Also document readTimeout vs idleTimeout semantics across all 24 locations
in the x/ module where these timeouts appear:
- readTimeout: handshake sniffing deadline (handlers), upstream response
  header timeout (http.Transport), or transport-level read deadline
- idleTimeout: idle read deadline per Pipe direction (0=disabled)
- ReadTimeout on Sniffer/SnifferBuilder: upstream response header/TLS
  handshake read timeout during sniffing
2026-05-31 17:05:21 +08:00
ginuerzh 3646c23398 refactor(forwarder): extract resolveHTTPNode and resolveTLSNode helpers, add tests
Pull node-resolution logic out of dial/dialTLS into pure resolveHTTPNode
and resolveTLSNode helpers. This separates node selection (bypass, hop,
fallback) from connection establishment, making both independently
testable. Add 21 new tests covering bypass, hop selection, nil-hop,
addr fallback, upgrade response type mismatch, websocket frames,
response body rewriting, and h2 preface validation.
2026-05-26 23:15:15 +08:00
ginuerzh aba7e2a256 refactor(forwarder): split sniffer monolith into focused files, extract testable helpers
Split ~1250-line sniffer.go into sniffer_http.go (HTTP handling, dial, round-trip),
sniffer_tls.go (TLS MITM, termination), sniffer_h2.go (HTTP/2), sniffer_ws.go
(WebSocket frame copy/record), and sniffer_rewrite.go (upgrade, body rewrite).

Extract clampBodySize, normalizeHost, effectiveReadTimeout as package-level
functions to simplify testing. Add 22 tests covering pure functions, option
setters, HTTP proxy integration, WebSocket frame copy, and edge cases.
2026-05-26 22:48:27 +08:00
ginuerzh 92564a0c8c fix(forwarder): ReadTimeout race, goroutine leak, PeerCertificates panic, stale error return
- Use local readTimeout copy in HandleOptions instead of mutating shared
  Sniffer.ReadTimeout, eliminating a data race under concurrent calls
- Close both connections in sniffingWebsocketFrame when one direction
  errors, preventing indefinite goroutine blockage
- Add bounds check on PeerCertificates before indexing in terminateTLS
- Discard non-fatal ParseServerHello error instead of returning it after
  successful xnet.Pipe
- Log res.Write errors in bypass/bad-gateway/connect-failure paths
- Add doc comments to all 15 exported symbols
2026-05-26 21:58:30 +08:00
ginuerzh e2072399dd fix sniffing for websocket 2025-09-04 21:30:49 +08:00
ginuerzh c7d16962ec add service option for plugin 2025-08-29 23:36:31 +08:00
ginuerzh 52289bea6c add network param for sniffing 2025-08-13 21:25:35 +08:00
ginuerzh 589c185053 forwarder: connect to sni host for empty node 2025-08-09 12:43:09 +08:00
ginuerzh b597467858 add context for conn 2025-08-04 19:29:38 +08:00
ginuerzh db21de831a fix xnet.Pipe 2025-08-01 23:00:50 +08:00
ginuerzh 208d18125c replace xnet.Transport by xnet.Pipe 2025-07-28 20:52:15 +08:00
ginuerzh 35a049fb03 rename vtun to tungo 2025-07-26 16:17:04 +08:00
ginuerzh d9a2f44a78 add src field for logger and recorder 2025-07-02 21:35:02 +08:00
ginuerzh d819fd3041 support http-100 continue for sniffer 2025-06-24 21:15:38 +08:00
ginuerzh 06d993023f add traffic reset for stats 2024-12-24 20:11:22 +08:00
ginuerzh 2693ba61c4 forwarder: fix ip matcher for tls 2024-12-18 19:05:19 +08:00
ginuerzh 2e8a5d6f51 fix http keepalive for forwarding 2024-11-17 16:16:20 +08:00
ginuerzh c2aedbabd9 add routing rule for node 2024-11-15 20:28:47 +08:00
ginuerzh 3db20563d2 add custom http response header for reverse proxy 2024-11-07 11:04:42 +08:00
ginuerzh a12870e766 add compression flag option for http request 2024-11-02 20:41:23 +08:00
ginuerzh 88ba01b3dd add redirect for handler recorder object 2024-10-31 19:14:21 +08:00
ginuerzh a97ac1f30a fix http body recorder 2024-10-22 22:51:33 +08:00
ginuerzh 3ca253537b fix race condition for sniffing 2024-10-20 19:19:16 +08:00
ginuerzh 41f527aa46 sniffing: full websocket frame recording 2024-10-19 20:40:07 +08:00
ginuerzh 6b932e35bf sniffing websocket frame 2024-10-19 19:36:06 +08:00
ginuerzh 7e51404ae5 fix handler sniffing 2024-10-16 23:01:12 +08:00
ginuerzh 51ca9f620a add transport for tunnel entrypoint 2024-10-15 18:47:36 +08:00
ginuerzh 5bba004324 add keepalive option for tunnel entrypoint 2024-10-14 23:09:07 +08:00
ginuerzh 0ffed81601 add sniffer for forwarding handler 2024-10-14 21:18:38 +08:00