Documentation
¶
Index ¶
- Constants
- func PeekClientHello(conn net.Conn) (sni string, wrapped net.Conn, err error)
- func Relay(ctx context.Context, logger *log.Entry, src, dst net.Conn, ...) (srcToDst, dstToSrc int64)
- type DialResolver
- type RelayObserver
- type Route
- type RouteType
- type Router
- func (r *Router) AddRoute(host SNIHost, route Route)
- func (r *Router) Drain(timeout time.Duration) bool
- func (r *Router) HTTPListener() net.Listener
- func (r *Router) IsEmpty() bool
- func (r *Router) RemoveFallback(svcID types.ServiceID)
- func (r *Router) RemoveRoute(host SNIHost, svcID types.ServiceID)
- func (r *Router) Serve(ctx context.Context, ln net.Listener) error
- func (r *Router) SetAccessLogger(l l4Logger)
- func (r *Router) SetFallback(route Route)
- func (r *Router) SetGeo(geo restrict.GeoResolver)
- func (r *Router) SetObserver(obs RelayObserver)
- type SNIHost
Constants ¶
const ( // DefaultDrainTimeout is the default grace period for in-flight relay // connections to finish during shutdown. DefaultDrainTimeout = 30 * time.Second // DefaultMaxRelayConns is the default cap on concurrent TCP relay connections per router. DefaultMaxRelayConns = 4096 )
const DefaultIdleTimeout = 5 * time.Minute
DefaultIdleTimeout is the default idle timeout for TCP relay connections. A zero value disables idle timeout checking.
Variables ¶
This section is empty.
Functions ¶
func PeekClientHello ¶
PeekClientHello reads the TLS ClientHello from conn, extracts the SNI server name, and returns a wrapped connection that replays the peeked bytes transparently. If the data is not a valid TLS ClientHello or contains no SNI extension, sni is empty and err is nil.
ECH/ESNI: When the client uses Encrypted Client Hello (TLS 1.3), the real server name is encrypted inside the encrypted_client_hello extension. This parser only reads the cleartext server_name extension (type 0x0000), so ECH connections return sni="" and are routed through the fallback path (or HTTP channel), which is the correct behavior for a transparent proxy that does not terminate TLS.
func Relay ¶
func Relay(ctx context.Context, logger *log.Entry, src, dst net.Conn, idleTimeout time.Duration) (srcToDst, dstToSrc int64)
Relay copies data bidirectionally between src and dst until both sides are done or the context is canceled. When idleTimeout is non-zero, each direction's read is deadline-guarded; if no data flows within the timeout the connection is torn down. When one direction finishes, it half-closes the write side of the destination (if supported) to signal EOF, allowing the other direction to drain gracefully before the full connection teardown.
Types ¶
type DialResolver ¶
type DialResolver func(accountID types.AccountID) (types.DialContextFunc, error)
DialResolver returns a DialContextFunc for the given account.
type RelayObserver ¶
type RelayObserver interface {
TCPRelayStarted(accountID types.AccountID)
TCPRelayEnded(accountID types.AccountID, duration time.Duration, srcToDst, dstToSrc int64)
TCPRelayDialError(accountID types.AccountID)
TCPRelayRejected(accountID types.AccountID)
}
RelayObserver receives callbacks for TCP relay lifecycle events. All methods must be safe for concurrent use.
type Route ¶
type Route struct {
Type RouteType
AccountID types.AccountID
ServiceID types.ServiceID
// Domain is the service's configured domain, used for access log entries.
Domain string
// Protocol is the frontend protocol (tcp, tls), used for access log entries.
Protocol accesslog.Protocol
// Target is the backend address for TCP relay (e.g. "10.0.0.5:5432").
Target string
// ProxyProtocol enables sending a PROXY protocol v2 header to the backend.
ProxyProtocol bool
// DialTimeout overrides the default dial timeout for this route.
// Zero uses defaultDialTimeout.
DialTimeout time.Duration
// SessionIdleTimeout overrides the default idle timeout for relay connections.
// Zero uses DefaultIdleTimeout.
SessionIdleTimeout time.Duration
// Filter holds connection-level IP/geo restrictions. Nil means no restrictions.
Filter *restrict.Filter
}
Route describes where a connection for a given SNI should be sent.
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router accepts raw TCP connections on a shared listener, peeks at the TLS ClientHello to extract the SNI, and routes the connection to either the HTTP reverse proxy or a direct TCP relay.
func NewPortRouter ¶
func NewPortRouter(logger *log.Logger, dialResolve DialResolver) *Router
NewPortRouter creates a Router for a dedicated port without an HTTP channel. Connections that don't match any SNI route fall through to the fallback relay (if set) or are closed.
func (*Router) AddRoute ¶
AddRoute registers an SNI route. Multiple routes for the same host are stored and resolved by priority at lookup time (HTTP > TCP). Empty host is ignored to prevent conflicts with ECH/ESNI fallback.
func (*Router) Drain ¶
Drain prevents new relay connections from starting and waits for all in-flight connection handlers and active relays to finish, up to the given timeout. Returns true if all completed, false on timeout.
func (*Router) HTTPListener ¶
HTTPListener returns a net.Listener that yields connections routed to the HTTP handler. Use this with http.Server.ServeTLS.
func (*Router) RemoveFallback ¶
RemoveFallback clears the catch-all fallback route and closes any active relay connections for the given service.
func (*Router) RemoveRoute ¶
RemoveRoute removes the route for the given host and service ID. Active relay connections for the service are closed immediately. If other routes remain for the host, they are preserved.
func (*Router) Serve ¶
Serve accepts connections from ln and routes them based on SNI. It blocks until ctx is canceled or ln is closed, then drains active relay connections up to DefaultDrainTimeout.
func (*Router) SetAccessLogger ¶
func (r *Router) SetAccessLogger(l l4Logger)
SetAccessLogger sets the L4 access logger. Must be called before Serve.
func (*Router) SetFallback ¶
SetFallback registers a catch-all route for connections that don't match any SNI route. On a port router this handles plain TCP relay; on the main router it takes priority over the HTTP channel.
func (*Router) SetGeo ¶
func (r *Router) SetGeo(geo restrict.GeoResolver)
SetGeo sets the geolocation lookup used for country-based restrictions.
func (*Router) SetObserver ¶
func (r *Router) SetObserver(obs RelayObserver)
SetObserver sets the relay lifecycle observer. Must be called before Serve.