Commit Graph

130 Commits

Author SHA1 Message Date
ginuerzh c45d27113e fix(dialer): evict dead SSHD sessions before reuse to prevent channel-open failure (#382)
When an SSH session behind an SSHD dialer dies silently (NAT timeout,
server disconnect, idle TCP drop), IsClosed() may still return false
because the health-check goroutine hasn't detected the failure yet.
Returning this dead cached session to the connector causes:

  ssh: unexpected packet in response to channel open:

Add a Ping(req)-based liveness check before returning a cached session.
If the ping fails, evict the session so the next Dial rebuilds a fresh
connection. This matches the dead-session eviction pattern already used
by the SSH, KCP, QUIC, and ICMP dialers.

Also export the ping-as-health-check as Session.Ping(timeout) so it can
be used by external health probes. Retain the existing ping() helper
unmodified.
2026-06-21 13:02:10 +08:00
ginuerzh a86b5ab66b feat(ss): support none/dummy cipher via no-op AEAD implementation
The go-gost fork of go-shadowsocks2 dropped built-in support for the
none/dummy cipher that was present in the original go-shadowsocks2.
Combined with the SS components' hard requirement for auth (and
therefore a valid cipher), this made gost fail to start with
method=none or method=dummy.

Add a no-op cipher in x/internal/util/ss/none/ that implements the
core.ShadowCipher interface using the standard go-shadowsocks2 AEAD
framing (salt + length-prefixed chunks, target address embedding)
without actual encryption.  SaltSize=16 ensures random salts on every
connection to avoid bloom-ring replay detection.

Update SS TCP connector/handler and SS UDP connector/handler to detect
method=none/dummy (case-insensitive) and use the no-op cipher instead
of utils.NewClientConfig/NewServerConfig.

Fixes go-gost/x#105
2026-06-17 22:18:23 +08:00
ginuerzh 3b3eaa609a fix(listener/pht): set X-Accel-Buffering to fix hang behind Nginx (issue #721)
The /pull handler streams data as base64 chunks with a flush per chunk.
With Nginx proxy_buffering on (the default), these tiny chunks and the
heartbeat newlines are buffered and never flushed to the client, so
active data transfer hangs until the buffer fills or the upstream closes.

Set X-Accel-Buffering: no on the pull response so Nginx forwards each
flushed chunk immediately without buffering. Nginx consumes the header
(it is not forwarded to the client) and it is inert when no proxy sits
in front of the listener, so it is safe to set unconditionally.

Covers pht, phts and h3, which share the same Server handler.
2026-06-14 14:15:22 +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 dd19e4387a feat(tls): add rejectUnknownSNI support for TLS listeners
Adds RejectUnknownSNI and ServerNames fields to TLSConfig. When enabled,
TLS handshakes with missing or non-allowlisted SNI are rejected at the
handshake level via GetConfigForClient before any certificate is sent.

Works across all TLS-based listener types (tls, mtls, ws, mws, http2,
grpc, http3) since they share the same *tls.Config from LoadServerConfig.
2026-05-31 22:11:29 +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 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 a2cef9639d test(sniffing): add 20 unit tests, coverage from 34.2% to 58.9%
Add tests for Sniff/isHTTP protocol detection, setHeader, h2Handler.ServeHTTP
(mock RoundTripper), HandleHTTP bypass/body-recording, HandleTLS error path,
handleUpgradeResponse websocket path, and copyWebsocketFrame read error.
2026-05-27 21:30:52 +08:00
ginuerzh 4f526c2aee refactor(sniffing): split sniffer monolith into focused files, extract testable helpers
Split ~850-line sniffer.go into 6 focused files (http, h2, tls, ws,
rewrite, test), extract pure helpers (clampBodySize, normalizeHost,
effectiveReadTimeout), fix "DeafultSampleRate" typo, add doc comments
on all exported symbols, and add 22 unit tests.
2026-05-27 21:21:40 +08:00
ginuerzh 87b055a83b fix(icmp): ICMP tunnel not working on Windows due to wildcard bind (#98)
On Windows, icmp.ListenPacket("ip4:icmp", "0.0.0.0") does not reliably
deliver ICMP packets (golang/go#38427). Add a platform-specific
ListenPacket helper that binds to a discovered interface address and
enables SIO_RCVALL via WSAIoctl for promiscuous receive.

Fixes go-gost/x#36
2026-05-27 21:02:26 +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 039bcc9af9 fix(resolver): fix IPv4/IPv6 fallback asymmetry, data race, and caller mutation bugs
Normalize Only/Prefer values (ip4→ipv4, ip6→ipv6) so downstream
comparisons match correctly. Fix asymmetric fallback where caller
network=ip6 fell through to IPv4 but ip4 didn't fall through to IPv6
— both branches now check hard constraints (server.Only || callerPref)
only, allowing soft Prefer to fall back. Fix data race on cache item.ts
by reading it inside RLock. Fix Store mutating caller's dns.Msg by
copying before TTL override. Add singleflight dedup, semaphore-bounded
async refresh, DNS rcode error checking, and bump EDNS0 UDPSize to
4096. Add 25 tests.
2026-05-25 21:44:58 +08:00
ginuerzh 3f73c82d00 fix(ss): address review findings for PR #96 — nil guards, resource leaks, dead code
- Bump go-shadowsocks2 to v0.1.3 (new API symbols)
- Return error instead of nil when Target() is empty in TCP handler
- Reset wbuf unconditionally on write error to prevent unbounded growth
- Add nil guard on targetAddr before WriteTo to prevent panic
- Remove duplicate NewClientConfig call and dead ClientConfig literal
- Remove unused noDelay metadata field
- Fix buffer-size guard to catch negative values (len(b)==0 → bufSize<=0)
- Add address context to parse/session error messages
2026-05-22 21:40:33 +08:00
kLiHz 3c8995027a adapt newest go-shadowsocks2 lib 2026-05-22 13:35:01 +08:00
ginuerzh 01a2bbaa11 fix(masque): address review findings for PR #76
- Return errors from MasqueConn.Read/Write instead of silently
  succeeding (prevents infinite busy-loops and silent data loss
  if these methods are accidentally called).
- Set recorder Network field in protocol dispatch switch rather
  than hardcoding "udp", so early errors don't misreport type.
- Clarify that DatagramConn context-ID handling only supports
  context ID 0 (sufficient for CONNECT-UDP per RFC 9298).
2026-05-21 22:50:29 +08:00
ginuerzh 66b53580af merge: PR #76 — MASQUE CONNECT-UDP and CONNECT-TCP support (RFC 9298/9114)
Extend MASQUE implementation to support TCP tunneling via standard
HTTP/3 CONNECT method, in addition to UDP via CONNECT-UDP.
2026-05-21 22:50:23 +08:00
Vasileios Papadimas 96f35bd7aa feat: add custom header support to PHT protocol
- Add Header field to PHT Client and clientConn structs
- Support custom headers in authorize, push, and pull requests
- Add metadata parsing for header configuration as map[string]string
- Enables PHT usage with header-based authentication (e.g., Cloudflare Access)

Backwards compatible - headers are optional.
2026-04-21 20:51:17 +08:00
David Manouchehri 7625973ca1 Add MASQUE/CONNECT-UDP support (RFC 9298)
Implement UDP tunneling over HTTP/3 using HTTP Datagrams (RFC 9297):
- Add masque handler for server-side CONNECT-UDP
- Add masque connector and h3-masque dialer for client-side
- Add enableDatagrams option to HTTP/3 listener
- Add shared utilities for datagram connections and path parsing

Resource management and connection caching:
- Add deferred stream cleanup in connector on error paths
- Add IsClosed() and Close() methods to Client for proper session management
- Clean up stale cached clients in dialer before reuse
- Close underlying stream when DatagramConn is closed
- Move RequestStream opening from connector to dialer to enable dead
  connection detection and cache invalidation (follows QUIC dialer pattern)
2026-04-21 19:53:07 +08:00
David Manouchehri c5f91232cb Add TCP CONNECT support to MASQUE (RFC 9114)
Extend MASQUE implementation to support TCP tunneling via standard HTTP/3
CONNECT method, in addition to existing UDP support via CONNECT-UDP:

- Add StreamConn type for TCP data transfer over HTTP/3 stream body
- Update handler to dispatch based on :protocol pseudo-header:
  - "connect-udp" for UDP (RFC 9298)
  - Empty/"HTTP/3.0" for TCP (RFC 9114)
- Add handleConnectTCP() using bidirectional stream relay
- Update connector to support both TCP and UDP networks
- Add connectTCP() for standard CONNECT requests

TCP uses :authority for target address and stream body for data.
UDP uses path template and HTTP/3 datagrams for data.
2025-12-28 23:47:51 +00:00
David Manouchehri 1752b29df9 Add MASQUE/CONNECT-UDP support (RFC 9298)
Implement UDP tunneling over HTTP/3 using HTTP Datagrams (RFC 9297):
- Add masque handler for server-side CONNECT-UDP
- Add masque connector and h3-masque dialer for client-side
- Add enableDatagrams option to HTTP/3 listener
- Add shared utilities for datagram connections and path parsing

Resource management and connection caching:
- Add deferred stream cleanup in connector on error paths
- Add IsClosed() and Close() methods to Client for proper session management
- Clean up stale cached clients in dialer before reuse
- Close underlying stream when DatagramConn is closed
- Move RequestStream opening from connector to dialer to enable dead
  connection detection and cache invalidation (follows QUIC dialer pattern)
2025-12-28 22:16:54 +00:00
RMT 0c97db3c05 Adapt new shadowsocks library.
1. Modify ss/ssu handler and connector
2. Add users to metadata of ss and ssu handler
2025-11-11 12:37:33 +08:00
eWloYW8 eae322c7ba feat: support custom ComponentID for Windows TAP device configuration 2025-10-11 22:01:24 +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 ad5cf6fd61 add proxyProtocol support for dialer 2025-08-03 15:36:29 +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 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 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 3ec2f9d487 update quic 2025-06-25 21:06:23 +08:00
ginuerzh ce3c28baf5 rm deprecated library shadowsocks-go/shadowsocks 2025-06-25 21:05:39 +08:00
ginuerzh d819fd3041 support http-100 continue for sniffer 2025-06-24 21:15:38 +08:00
ginuerzh 04d1587a77 set dns for tun 2025-02-25 20:39:33 +08:00
ginuerzh e0915affda use fixed buffer size 2025-02-18 17:38:27 +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 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