Commit Graph

80 Commits

Author SHA1 Message Date
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 ea3ca94e53 fix(handler): move auth before request validation so probe resistance works (#875)
Probe resistance (probeResist=code:403) was always returning 400 Bad
Request instead of the configured status code because the
non-proxy-form request validation check (req.URL.Scheme != "http")
ran before Authenticate(). Browser/scanner probes send GET / HTTP/1.1
which has an empty URL scheme, so they were rejected at the validation
gate before probe resistance ever had a chance to respond.

Swap the order: run Authenticate() first so probe resistance can
intercept non-proxy-form probes with the configured decoy response.
Legitimate clients with correct credentials proceed to request
validation and routing as before.
2026-06-23 19:57:45 +08:00
ginuerzh 95874c53f5 fix: add status field to service list/detail APIs, unify observeStats retry pattern, fix router bugs
- api: add fillServiceStatus helper and call it from getServiceList/getService
  so status field appears in service list and detail API responses
- observeStats: unify retry pattern across all 9 handlers (http, http2, masque,
  relay, router, socks4, socks5, tungo, tunnel) — buffer events on failure,
  continue to skip fresh collection, clear on success; fix event-loss bug
  where interim events were dropped during retry cycles
- handler/router: check WriteTo/Write return errors in associate.go and
  entrypoint.go; fix DelConnector and ConnectorPool.Del using RLock instead
  of Lock (write under read lock); remove unused fields t and cancel
2026-06-03 22:12:08 +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 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 a33c6f4a92 test(handler/http): add 25+ tests for probe resistance, MITM, bypass, and edge cases
+698/-3 across auth_test, connect_test, handler_test, metadata_test,
proxy_test, and websocket_test. Covers probeResistanceResponse (web/file/
host/code types), handleRequest (bypass/UDP-disabled/CONNECT edge cases),
setupTrafficLimiter with observer, observeStats lifecycle, MITM metadata,
HTTP/1.0 keep-alive proxyRoundTrip, websocket body recording, and sniff
timeout handling. Fixes SetDeadline signatures from any→time.Time.
2026-05-30 17:54:27 +08:00
ginuerzh 23b58ddd23 refactor(handler/http): extract Authenticator, SnifferBuilder, and normalizeRequest as testable units
Extract three pure-logic components from httpHandler to eliminate I/O
coupling from auth and sniffing construction, enabling synchronous unit
tests and reducing per-request allocation in sniffAndHandle:

- Authenticator struct with AuthResult return type — auth decisions no
  longer write to the connection; callers handle response I/O. Auth
  tests drop net.Pipe/goroutines for direct return-value assertions.
- SnifferBuilder pre-built in Init and reused per-connection via Build(),
  replacing inline sniffing.Sniffer{} construction in sniffAndHandle.
- normalizeRequest extracted to util.go with NormalizedRequest type,
  collapsing 20 lines of inline URL/normalisation into a single call.
- knockMatch extracted as standalone pure function.
- clampBodySize exported as ClampBodySize for cross-package use.
- util_test.go with 22 tests covering utility functions.
- helpers_test.go consolidates shared test fakes (logger, observer, conn).
2026-05-29 00:14:44 +08:00
ginuerzh c246a1d4fa feat(handler/http,http2): support comma-separated hostnames in probeResistance knock
Knock previously accepted a single hostname. Now it accepts multiple
comma-separated hostnames; probe resistance is bypassed if the request
hostname matches any entry in the list. Matching is case-insensitive
and whitespace around entries is trimmed.
2026-05-28 23:20:27 +08:00
ginuerzh 3246b39c93 refactor(handler/http): split handler monolith into 7 focused files with 96 unit tests
Extract the 1024-line handler.go into concern-focused files:
  util.go       — 5 pure functions (decodeServerName, basicProxyAuth, upgradeType,
                  normalizeHostPort, buildConnectResponse) — 100% coverage
  auth.go       — authenticate (5 probe-resistance strategies + knock) + checkRateLimit
  connect.go    — handleConnect, sniffAndHandle, dial, setupTrafficLimiter
  proxy.go      — handleProxy keep-alive loop, proxyRoundTrip, handleUpgradeResponse
  websocket.go  — WebSocket frame sniffing/recording with rate-limited sampling
  udp.go        — UDP-over-HTTP relay with SOCKS5 tunnel
  metadata.go   — metadata struct, parseMetadata, probeResistance type

Key improvements:
- Transport injection (http.RoundTripper field) enables testing without real network
- Nil Router guards in dial, handleUDP, and nil Addr guard in checkRateLimit
- Pure functions converted from methods to package-level (no state dependency)
- setupTrafficLimiter returns cleanup closure to preserve defer lifetime
- handleConnect accepts caller's resp for correct recorder status capture
- Comprehensive package doc with full request-flow documentation

96 tests: 100% on pure functions, 86% on metadata parsing, 55% overall (integration
paths verified by e2e tests).
2026-05-28 23:06:37 +08:00
ginuerzh f4be111420 fix(handler/http): host validation with port split, sniffer ownership guard, body leak, probe resistance hardening
- Split host:port in Host header before DNS/IP validation, accept bare IPs
- Guard sniffer connection ownership with snifferHandled flag to prevent
  double-close when sniffers take over the tunnel
- Close req.Body in handleProxy keep-alive loop on both error and success
  paths to prevent goroutine leaks from idle HTTP transport connections
- Log all resp.Write errors (5 call sites) instead of silently discarding
- Respect resp.Close for all responses, not just ContentLength >= 0,
  fixing unnecessarily closed chunked keep-alive connections
- Harden probe resistance: validate strconv.Atoi result, add 15s timeout
  to web redirect HTTP client, document pr.Knock empty/set/mismatch logic
2026-05-28 21:32:06 +08:00
Andrew Beresford 290e9c37d5 Make pipe read timeout configurable via idleTimeout metadata
The 30s hardcoded readTimeout in Pipe() caused all CONNECT tunnel
connections to be hard-closed after 30s of inactivity, breaking
WebSocket and long-polling connections through the proxy.

Pipe() now accepts a WithReadTimeout(d) option. When d is 0 (the
default) no read deadline is set, relying on TCP keepalives or context
cancellation to detect dead connections instead.

The HTTP handler exposes this as the idleTimeout metadata key.

Fixes: https://github.com/go-gost/x/issues/91
2026-05-08 11:31:53 +01: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 b597467858 add context for conn 2025-08-04 19:29:38 +08:00
ginuerzh f71351f5ef add interface xnet.SrcAddr/DstAddr 2025-08-03 10:15:53 +08:00
ginuerzh db21de831a fix xnet.Pipe 2025-08-01 23:00:50 +08:00
ginuerzh a5309eee97 add buffer size option for udp connection 2025-07-30 21:40:53 +08:00
ginuerzh b64e5901b6 http: fix response for connect request 2025-07-30 21:40:31 +08:00
ginuerzh 208d18125c replace xnet.Transport by xnet.Pipe 2025-07-28 20:52:15 +08:00
ginuerzh afcf4a5252 parse real client IP 2025-07-15 19:59:10 +08:00
ginuerzh d9a2f44a78 add src field for logger and recorder 2025-07-02 21:35:02 +08:00
ginuerzh e0915affda use fixed buffer size 2025-02-18 17:38:27 +08:00
wzxjohn 11bca5bc3d fix(http): set Proxy-Agent header after authenticate to avoid leak #42 2025-02-18 15:30:56 +08:00
ginuerzh fc12e33786 router handler: add cache for sd service 2025-02-09 16:16:44 +08:00
ginuerzh 379d9a73ad service: add limiter.scope option 2025-01-20 23:22:58 +08:00
ginuerzh 06d993023f add traffic reset for stats 2024-12-24 20:11:22 +08:00
ginuerzh 79b6b9138e add limiterRefreshInterval option for limiter 2024-11-14 20:16:09 +08:00
ginuerzh a12870e766 add compression flag option for http request 2024-11-02 20:41:23 +08:00
ginuerzh edb9a992e2 http: fix http/1.0 connection close 2024-11-01 18:17:50 +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 a0cbee8817 use http.transport for non-connect http tunnel 2024-10-17 21:57:20 +08:00
ginuerzh 7e51404ae5 fix handler sniffing 2024-10-16 23:01:12 +08:00
ginuerzh 618d042001 add input/ouput fields for log 2024-10-16 22:16:34 +08:00
ginuerzh 24547b4332 add ClientAddr for websocket conn 2024-10-16 20:46:45 +08:00
ginuerzh 5bba004324 add keepalive option for tunnel entrypoint 2024-10-14 23:09:07 +08:00
ginuerzh 27242d0b66 recorder: add input/output traffic stats for handler recorder object 2024-10-11 20:32:56 +08:00
ginuerzh 9a56c8fba3 add keepalive option for http handler 2024-10-10 16:52:01 +08:00
ginuerzh c42a44abb6 add sniffer utility 2024-10-02 22:51:23 +08:00
ginuerzh 3c8add4b82 add traffic sniffing for handler 2024-09-27 15:55:28 +08:00
ginuerzh e22aa91571 add route for recorder 2024-09-24 20:20:15 +08:00
ginuerzh e09ec08881 add sid for connector log 2024-09-20 18:20:57 +08:00
ginuerzh e37213ac01 add http body for handler recorder 2024-09-16 19:44:15 +08:00
ginuerzh b39aa63f75 add clientIP for handler recorder object 2024-09-15 18:28:24 +08:00
ginuerzh f681f7aa03 update handler recorder object 2024-09-15 10:51:23 +08:00
ginuerzh 69455ace9d add handler recorder 2024-09-14 23:21:25 +08:00
ginuerzh 31c482c649 add scope param for traffic limiter 2024-08-31 09:30:04 +08:00