mirror of
https://github.com/go-gost/x.git
synced 2026-10-19 16:16:32 +00:00
0954e57dcb40927157d5402a60e7f67142087d21
Since 3.3.0 a transparent red service with sniffing enabled closed every
connection whose protocol Sniff does not classify as HTTP or TLS. SSH,
Redis, and any raw TCP — plus server-speaks-first protocols such as SMTP,
where Sniff returns "" after sniffing.timeout — took no matching branch
of the switch on proto, so sniffErr stayed nil and the handler returned
nil, closing the connection via the deferred conn.Close(). No dial was
attempted, so the upstream node never saw the connection, and nothing was
logged above info.
This traces to c23a2f1, which introduced the error-returning switch in
order to replay sniffed bytes for the empty-SNI case (#891). Before it,
the switch had no error variable and simply fell out into raw
forwarding, so every unclassified protocol was proxied as-is.
sniffing.fallback is not a usable workaround: it also changes dial() so
that a failure to reach the sniffed host re-dials the original IP, where
domain-based hop/node bypass rules no longer match. That can route a
domain through a node its bypass excludes.
Mark the two handled protocols explicitly and let everything else fall
through to raw forwarding unconditionally, restoring the 3.2.x
behaviour. sniffing.fallback keeps its intended meaning: it governs only
what happens after HTTP/TLS handling itself fails (#891).
Closes #913
x
The implementation layer for the GOST proxy framework. Every interface defined in core/ is implemented here — handlers, listeners, dialers, connectors, plus config parsing, registries, and all cross-cutting concerns.
Package layout
| Directory | Purpose |
|---|---|
handler/ |
Protocol handlers (http, socks4/5, ss, ssh, tunnel, tun, dns, redirect, etc.) |
listener/ |
Protocol listeners (tcp, tls, ws, http2/3, quic, kcp, icmp, tun, udp, etc.) |
dialer/ |
Outbound dialers (tcp, tls, ws, http2/3, quic, grpc, ssh, wg, kcp, icmp, etc.) |
connector/ |
Destination connectors (http, socks4/5, ss, ssh, relay, tunnel, direct, etc.) |
config/ |
Config struct, YAML/JSON parsing, and service construction |
registry/ |
Typed global registries for all component types |
service/ |
Accept loop wiring listener + handler |
chain/ |
Multi-hop forwarding chain and route implementation |
router/ |
Route table router (destination-based routing) |
hop/ |
Node group with load-balanced selection |
selector/ |
Load-balancing strategies (round-robin, random, weighted, hash) |
auth/, bypass/, admission/ |
Authentication, bypass rules, admission control |
resolver/, hosts/ |
DNS resolution and host mapping |
limiter/ |
Traffic, connection, and rate limiters |
recorder/, observer/ |
Traffic recording and observability |
ingress/, sd/, routing/ |
Ingress control, service discovery, routing rules |
logger/, metrics/, api/ |
Logging, Prometheus metrics, Web API |
metadata/, ctx/ |
Metadata key-value system and context propagation |
internal/ |
Shared internals — not importable externally |
Component pattern
Every handler, listener, dialer, and connector follows the same pattern: init() registers a constructor into a global registry, the constructor takes functional options, and Init(metadata.Metadata) extracts typed configuration from the metadata key-value map.
See CLAUDE.md for detailed architecture and conventions.
Languages
Go
100%