server

package
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 10, 2026 License: MIT Imports: 31 Imported by: 0

Documentation

Overview

Package server implements a production-grade SOCKS5 server (RFC 1928) with CONNECT, BIND and UDP ASSOCIATE, pluggable authentication, rules, resolvers, middleware, metrics, admission control and graceful shutdown. On Linux the TCP relay uses splice(2) via io.Copy fast paths and the UDP relay moves batches of datagrams per syscall (recvmmsg/sendmmsg).

Index

Constants

This section is empty.

Variables

View Source
var ErrServerClosed = errors.New("socks5: server closed")

ErrServerClosed is returned by Serve, ServeContext, ListenAndServe and ListenAndServeTLS after a call to Shutdown or Close.

Functions

func SendReply

func SendReply(w io.Writer, rep uint8, bindAddr net.Addr) error

SendReply writes a SOCKS5 reply with the given response code to the client. For RepSuccess the bind address is encoded into BND.ADDR/BND.PORT; for error replies bindAddr is ignored and the zero IPv4 address is sent.

Types

type ConnState added in v0.3.2

type ConnState int

ConnState describes a connection lifecycle phase reported to the WithConnState hook.

const (
	// StateNew is reported right after the accept loop admits a connection.
	StateNew ConnState = iota
	// StateActive is reported when the SOCKS handshake begins.
	StateActive
	// StateClosed is reported after the connection finishes.
	StateClosed
)

Connection lifecycle states, in order of occurrence.

type CounterMetrics added in v1.0.0

type CounterMetrics struct {
	// contains filtered or unexported fields
}

CounterMetrics is a ready-to-use Metrics implementation backed by atomic counters. It is suitable for tests, expvar publishing, or as a starting point for custom backends.

func (*CounterMetrics) ConnAccepted added in v1.0.0

func (m *CounterMetrics) ConnAccepted()

ConnAccepted implements Metrics.

func (*CounterMetrics) ConnClosed added in v1.0.0

func (m *CounterMetrics) ConnClosed()

ConnClosed implements Metrics.

func (*CounterMetrics) ConnRejected added in v1.0.0

func (m *CounterMetrics) ConnRejected(RejectReason)

ConnRejected implements Metrics.

func (*CounterMetrics) RelayBytes added in v1.0.0

func (m *CounterMetrics) RelayBytes(n int64)

RelayBytes implements Metrics.

func (*CounterMetrics) Request added in v1.0.0

func (m *CounterMetrics) Request(byte)

Request implements Metrics.

func (*CounterMetrics) RequestDone added in v1.0.0

func (m *CounterMetrics) RequestDone(_ byte, err error)

RequestDone implements Metrics.

func (*CounterMetrics) Snapshot added in v1.0.0

func (m *CounterMetrics) Snapshot() MetricsSnapshot

Snapshot returns a consistent-enough copy of all counters for reporting.

type GPool

type GPool interface {
	Submit(f func()) error
}

GPool is a goroutine pool abstraction (e.g. ants). When installed with WithGPool, request handling is submitted to the pool instead of spawning a goroutine per task; a Submit error falls back to `go`.

type Logger

type Logger interface {
	// Infof logs an informational message in fmt.Printf style.
	Infof(format string, args ...any)
	// Errorf logs an error message in fmt.Printf style.
	Errorf(format string, args ...any)
}

Logger is the minimal logging interface consumed by the server. Wrap a standard library *log.Logger with NewLogger or a *slog.Logger with NewSlogLogger, or provide your own implementation via WithLogger.

type Metrics added in v1.0.0

type Metrics interface {
	// ConnAccepted is called when the accept loop admits a connection.
	ConnAccepted()
	// ConnClosed is called when an admitted connection finishes.
	ConnClosed()
	// ConnRejected is called when the accept loop refuses a connection
	// before the handshake (connection cap or rate limit).
	ConnRejected(reason RejectReason)
	// Request is called once per parsed SOCKS request with the command byte
	// (protocol.CommandConnect, CommandBind or CommandAssociate).
	Request(command byte)
	// RequestDone is called when the request finishes; err is nil on success.
	RequestDone(command byte, err error)
	// RelayBytes reports payload bytes moved by the proxy: once per finished
	// CONNECT/BIND tunnel (both directions summed) and once per relayed UDP
	// batch. The sum over time is total relayed traffic.
	RelayBytes(n int64)
}

Metrics receives server lifecycle events for observability backends (Prometheus, OpenTelemetry, expvar, ...). All methods are invoked inline on the serving path, so implementations must be safe for concurrent use and return quickly; anything slow should be offloaded by the implementation.

Install with WithMetrics. When no Metrics is configured the server skips every call site with a single nil check — there is no other overhead.

type MetricsSnapshot added in v1.0.0

type MetricsSnapshot struct {
	Accepted     int64 // connections admitted by the accept loop
	Closed       int64 // admitted connections that have finished
	Rejected     int64 // connections refused before the handshake
	Requests     int64 // SOCKS requests started
	RequestFails int64 // SOCKS requests that returned an error
	RelayedBytes int64 // total payload bytes relayed (TCP tunnels + UDP)
}

MetricsSnapshot is a point-in-time copy of CounterMetrics values.

type Option

type Option func(s *Server)

Option configures a Server at construction time; pass options to New.

func WithAssociateHandle

func WithAssociateHandle(h func(ctx context.Context, writer io.Writer, req *handler.Request) error) Option

WithAssociateHandle replaces the default UDP ASSOCIATE handler.

func WithAssociateMiddleware

func WithAssociateMiddleware(m handler.Middleware) Option

WithAssociateMiddleware appends middleware executed before the UDP ASSOCIATE handler.

func WithAuthMethods

func WithAuthMethods(authMethods []auth.Authenticator) Option

WithAuthMethods appends custom authenticators to the method negotiation list.

func WithBaseContext added in v0.3.2

func WithBaseContext(fn func(net.Listener) context.Context) Option

WithBaseContext installs a base context factory that is invoked once per listener. ServeContext derives each connection context from the returned value.

func WithBindAcceptTimeout

func WithBindAcceptTimeout(d time.Duration) Option

WithBindAcceptTimeout sets how long the server waits for the peer during BIND.

func WithBindHandle

func WithBindHandle(h func(ctx context.Context, writer io.Writer, req *handler.Request) error) Option

WithBindHandle replaces the default BIND handler.

func WithBindIP

func WithBindIP(ip net.IP) Option

WithBindIP sets the bind address used for BIND/UDP sockets.

func WithBindMiddleware

func WithBindMiddleware(m handler.Middleware) Option

WithBindMiddleware appends middleware executed before the BIND handler.

func WithBindPeerCheckIPOnly

func WithBindPeerCheckIPOnly(b bool) Option

WithBindPeerCheckIPOnly switches peer validation to IP-only (ignoring port).

func WithBufferPool

func WithBufferPool(pool buffer.BufPool) Option

WithBufferPool sets the buffer pool used by the proxy I/O fast-paths.

func WithConnContext added in v0.3.2

func WithConnContext(fn func(ctx context.Context, conn net.Conn) context.Context) Option

WithConnContext decorates the per-connection context before handlers and dialers run. The provided ctx is derived from ServeContext; return nil to keep the original value.

func WithConnMetadata added in v0.3.2

func WithConnMetadata(fn func(net.Conn) map[string]string) Option

WithConnMetadata installs a callback used to attach static metadata to handler.Request.Metadata. The returned map is shallow-copied per connection.

func WithConnState added in v0.3.2

func WithConnState(fn func(net.Conn, ConnState)) Option

WithConnState registers a hook that receives connection lifecycle transitions (StateNew, StateActive, StateClosed).

func WithConnectHandle

func WithConnectHandle(h func(ctx context.Context, writer io.Writer, req *handler.Request) error) Option

WithConnectHandle replaces the default CONNECT handler.

func WithConnectMiddleware

func WithConnectMiddleware(m handler.Middleware) Option

WithConnectMiddleware appends middleware executed before the CONNECT handler.

func WithConnectionLogging added in v0.4.1

func WithConnectionLogging(enabled bool) Option

WithConnectionLogging enables or disables per-connection accept/close logs.

func WithConnectionRateLimit added in v1.0.0

func WithConnectionRateLimit(perSecond float64, burst int) Option

WithConnectionRateLimit limits accepted connections per source IP using a token bucket: each source may open bursts of up to burst connections and sustain perSecond connections per second afterwards. Excess connections are closed before the handshake. perSecond <= 0 disables the limiter; burst < 1 is raised to 1. Memory is bounded under spoofed-source floods: idle buckets are swept once the table grows past an internal threshold.

func WithCredential

func WithCredential(cs auth.CredentialStore) Option

WithCredential provides a credential store used by the default user/pass authenticator.

func WithDial

func WithDial(dial func(ctx context.Context, network, addr string) (net.Conn, error)) Option

WithDial provides a custom dial function invoked for CONNECT/BIND/ASSOCIATE.

func WithDialAndRequest

func WithDialAndRequest(dial func(ctx context.Context, network, addr string, req *handler.Request) (net.Conn, error)) Option

WithDialAndRequest is like WithDial but also exposes the parsed request.

func WithDialFQDN added in v1.0.0

func WithDialFQDN(enabled bool) Option

WithDialFQDN controls how CONNECT requests carrying a domain name are dialed. When enabled the server skips its own resolution step and hands the hostname straight to the dialer, restoring the net.Dialer dual-stack "Happy Eyeballs" fallback (RFC 8305) and per-attempt address selection. The configured resolver is then bypassed for CONNECT, and rules, rewriters and middleware observe a request whose DestAddr still carries the FQDN with a nil IP. BIND and UDP ASSOCIATE are unaffected.

func WithDialer

func WithDialer(d net.Dialer) Option

WithDialer sets a custom net.Dialer for outbound connections when a custom dial is not provided.

func WithGPool

func WithGPool(pool GPool) Option

WithGPool registers a goroutine pool for request handling.

func WithHandshakeTimeout

func WithHandshakeTimeout(d time.Duration) Option

WithHandshakeTimeout sets a deadline for initial negotiation and request parsing. Zero disables the handshake deadline.

func WithLinkQuality added in v0.4.1

func WithLinkQuality(tr *linkquality.Tracker) Option

WithLinkQuality enables link quality tracking for outbound hops.

func WithLogger

func WithLogger(l Logger) Option

WithLogger replaces the server logger implementation.

func WithMaxConnections added in v1.0.0

func WithMaxConnections(n int) Option

WithMaxConnections caps the number of concurrently served connections. When the cap is reached the accept loop closes new connections immediately, before any SOCKS traffic, with an O(1) check. n <= 0 means unlimited.

func WithMetrics added in v1.0.0

func WithMetrics(m Metrics) Option

WithMetrics installs a Metrics implementation that receives connection, request and relay events. Pass nil to disable (the default); when disabled the only cost on the serving path is a nil check.

func WithResolver

func WithResolver(res resolver.NameResolver) Option

WithResolver overrides the DNS resolver used for FQDN targets.

func WithRewriter

func WithRewriter(rew handler.AddressRewriter) Option

WithRewriter installs an address rewriter that can mutate the destination before dialing.

func WithRule

func WithRule(rule rules.RuleSet) Option

WithRule sets the ACL evaluated before dialing the upstream target.

func WithTCPKeepAlive

func WithTCPKeepAlive(period time.Duration) Option

WithTCPKeepAlive enables TCP keepalives on accepted connections with the given period. Zero disables keepalives.

func WithUDPAssociateLimits

func WithUDPAssociateLimits(maxPeers int, idleTimeout time.Duration) Option

WithUDPAssociateLimits configures UDP ASSOCIATE peer limits and idle cleanup. If maxPeers <= 0, unlimited peers are allowed. If idleTimeout <= 0, peers are not GC'd by idle.

func WithUseBindIpBaseResolveAsUdpAddr

func WithUseBindIpBaseResolveAsUdpAddr(b bool) Option

WithUseBindIpBaseResolveAsUdpAddr forces UDP ASSOCIATE replies to advertise the bind IP.

type RejectReason added in v1.0.0

type RejectReason string

RejectReason explains why the accept loop refused a connection before the SOCKS handshake started.

const (
	// RejectMaxConnections means the WithMaxConnections cap was reached.
	RejectMaxConnections RejectReason = "max_connections"
	// RejectRateLimited means the per-source rate limit configured with
	// WithConnectionRateLimit was exceeded.
	RejectRateLimited RejectReason = "rate_limited"
)

Rejection reasons reported to Metrics.ConnRejected.

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server is a SOCKS5 server (RFC 1928) supporting CONNECT, BIND and UDP ASSOCIATE. Construct it with New and the With* options; the zero value is not usable. All exported methods are safe for concurrent use.

func New

func New(opts ...Option) *Server

New builds a Server with the given options. Without WithCredential or WithAuthMethods the server accepts unauthenticated clients (NoAuth).

func (*Server) Close added in v1.0.0

func (sf *Server) Close() error

Close immediately closes all listeners and tears down every active connection. For a graceful stop, use Shutdown.

func (*Server) LinkQualityTracker added in v0.4.1

func (sf *Server) LinkQualityTracker() *linkquality.Tracker

LinkQualityTracker returns the tracker used for outbound hops, if enabled.

func (*Server) ListenAndServe

func (sf *Server) ListenAndServe(network, addr string) error

ListenAndServe listens on the network address and serves SOCKS5 until the server is shut down. It returns ErrServerClosed after Shutdown or Close.

func (*Server) ListenAndServeTLS

func (sf *Server) ListenAndServeTLS(network, addr string, c *tls.Config) error

ListenAndServeTLS is ListenAndServe over a TLS listener. With tls.RequireAndVerifyClientCert the client certificate details are exposed to rules and handlers via the auth context (tls.* payload keys).

func (*Server) Proxy

func (sf *Server) Proxy(dst io.Writer, src io.Reader) error

Proxy copies data from src to dst. It prefers optimized io.Copy paths (ReaderFrom/WriterTo) and uses the shared buffer pool for the generic copy case to avoid per-call allocations.

func (*Server) Serve

func (sf *Server) Serve(l net.Listener) error

Serve accepts and serves SOCKS5 connections on l until the server is shut down. It returns ErrServerClosed after Shutdown or Close.

func (*Server) ServeConn

func (sf *Server) ServeConn(conn net.Conn) error

ServeConn serves a single, already-accepted connection. Admission control (WithMaxConnections, WithConnectionRateLimit) is not applied: it belongs to the accept loop, and direct callers manage their own.

func (*Server) ServeConnContext added in v0.3.2

func (sf *Server) ServeConnContext(ctx context.Context, conn net.Conn) error

ServeConnContext is like ServeConn but binds the provided context to the connection lifecycle.

func (*Server) ServeContext added in v0.3.2

func (sf *Server) ServeContext(ctx context.Context, l net.Listener) error

ServeContext serves SOCKS5 on l until ctx is done, Shutdown/Close is called, or an unrecoverable error occurs. Canceling ctx tears down every active connection; Shutdown lets in-flight connections finish.

func (*Server) Shutdown added in v1.0.0

func (sf *Server) Shutdown(ctx context.Context) error

Shutdown gracefully stops the server: it closes all listeners so no new connections are accepted, then polls until active connections drain or ctx is done. If ctx expires first, Shutdown returns ctx.Err() with connections still running; call Close to force them down.

type SlogLogger added in v1.0.0

type SlogLogger struct {
	// contains filtered or unexported fields
}

SlogLogger adapts a structured *slog.Logger to the Logger interface. Messages are emitted at Info and Error levels on the wrapped logger.

func NewSlogLogger added in v1.0.0

func NewSlogLogger(l *slog.Logger) *SlogLogger

NewSlogLogger wraps a *slog.Logger into a server Logger. Passing nil uses slog.Default().

func (*SlogLogger) Errorf added in v1.0.0

func (sf *SlogLogger) Errorf(format string, args ...any)

Errorf implements Logger.

func (*SlogLogger) Infof added in v1.0.0

func (sf *SlogLogger) Infof(format string, args ...any)

Infof implements Logger.

type Std

type Std struct {
	*log.Logger
}

Std adapts a standard library *log.Logger to the Logger interface, prefixing messages with their level.

func NewLogger

func NewLogger(l *log.Logger) *Std

NewLogger wraps a *log.Logger into a server Logger.

func (Std) Errorf

func (sf Std) Errorf(format string, args ...any)

Errorf implements Logger.

func (Std) Infof added in v0.4.1

func (sf Std) Infof(format string, args ...any)

Infof implements Logger.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL