mirror of
https://github.com/go-gost/x.git
synced 2026-10-19 16:16:32 +00:00
Adds SOCKS5 handler and connector support for Tor-specific RESOLVE (0xF0) and RESOLVE_PTR (0xF1) commands, enabling tools like torsocks and tor-resolve to work through GOST when forwarding to a Tor SOCKS5 proxy. - Define CmdResolve (0xF0) and CmdResolvePTR (0xF1) in x/internal/util/socks - Add ContextWithSocks5Cmd/Socks5CmdFromContext for per-request command override - Connector reads context command, sends it upstream, returns resolveConn - resolveConn wraps net.Conn and exposes resolved addr via Metadata() - Handler dispatches 0xF0/0xF1 to handleResolve/handleResolvePTR - Gate behind enableTor metadata (tor=true), reject with CmdUnsupported by default - 9 unit tests across ctx/, connector/, handler/ packages
153 lines
4.5 KiB
Go
153 lines
4.5 KiB
Go
// Package ctx provides typed context keys and helpers for passing per-request
|
|
// data (session ID, addresses, client ID, hash source) through the handler chain.
|
|
package ctx
|
|
|
|
import (
|
|
"context"
|
|
"net"
|
|
)
|
|
|
|
// Context is implemented by connections that carry a [context.Context].
|
|
type Context interface {
|
|
Context() context.Context
|
|
}
|
|
|
|
type srcAddrKey struct{}
|
|
|
|
// ContextWithSrcAddr returns a copy of ctx that carries the source address addr.
|
|
func ContextWithSrcAddr(ctx context.Context, addr net.Addr) context.Context {
|
|
return context.WithValue(ctx, srcAddrKey{}, addr)
|
|
}
|
|
|
|
// SrcAddrFromContext returns the source address stored in ctx, or nil if none is set.
|
|
func SrcAddrFromContext(ctx context.Context) net.Addr {
|
|
v, _ := ctx.Value(srcAddrKey{}).(net.Addr)
|
|
return v
|
|
}
|
|
|
|
type dstAddrKey struct{}
|
|
|
|
// ContextWithDstAddr returns a copy of ctx that carries the destination address addr.
|
|
func ContextWithDstAddr(ctx context.Context, addr net.Addr) context.Context {
|
|
return context.WithValue(ctx, dstAddrKey{}, addr)
|
|
}
|
|
|
|
// DstAddrFromContext returns the destination address stored in ctx, or nil if none is set.
|
|
func DstAddrFromContext(ctx context.Context) net.Addr {
|
|
v, _ := ctx.Value(dstAddrKey{}).(net.Addr)
|
|
return v
|
|
}
|
|
|
|
type (
|
|
// Sid is a session identifier carried in the context.
|
|
Sid string
|
|
sidKey struct{}
|
|
)
|
|
|
|
// String implements fmt.Stringer.
|
|
func (s Sid) String() string {
|
|
return string(s)
|
|
}
|
|
|
|
// ContextWithSid returns a copy of ctx that carries the session ID sid.
|
|
func ContextWithSid(ctx context.Context, sid Sid) context.Context {
|
|
return context.WithValue(ctx, sidKey{}, sid)
|
|
}
|
|
|
|
// SidFromContext returns the session ID stored in ctx, or the zero value if none is set.
|
|
func SidFromContext(ctx context.Context) Sid {
|
|
v, _ := ctx.Value(sidKey{}).(Sid)
|
|
return v
|
|
}
|
|
|
|
type (
|
|
hashKey struct{}
|
|
// Hash carries the hash source used by selectors for sticky load balancing.
|
|
Hash struct {
|
|
Source string
|
|
}
|
|
)
|
|
|
|
// ContextWithHash returns a copy of ctx that carries the hash source h.
|
|
func ContextWithHash(ctx context.Context, h *Hash) context.Context {
|
|
return context.WithValue(ctx, hashKey{}, h)
|
|
}
|
|
|
|
// HashFromContext returns the hash source stored in ctx, or nil if none is set.
|
|
func HashFromContext(ctx context.Context) *Hash {
|
|
v, _ := ctx.Value(hashKey{}).(*Hash)
|
|
return v
|
|
}
|
|
|
|
type (
|
|
// ClientID is a client identifier used for hash-based load balancing.
|
|
ClientID string
|
|
clientIDKey struct{}
|
|
)
|
|
|
|
// String implements fmt.Stringer.
|
|
func (c ClientID) String() string {
|
|
return string(c)
|
|
}
|
|
|
|
// ContextWithClientID returns a copy of ctx that carries the client ID clientID.
|
|
func ContextWithClientID(ctx context.Context, clientID ClientID) context.Context {
|
|
return context.WithValue(ctx, clientIDKey{}, clientID)
|
|
}
|
|
|
|
// ClientIDFromContext returns the client ID stored in ctx, or the zero value if none is set.
|
|
func ClientIDFromContext(ctx context.Context) ClientID {
|
|
v, _ := ctx.Value(clientIDKey{}).(ClientID)
|
|
return v
|
|
}
|
|
|
|
type peerCertKey struct{}
|
|
|
|
// PeerCert carries the verified mTLS client certificate identity.
|
|
type PeerCert struct {
|
|
CN string
|
|
SANs []string
|
|
Fingerprint string // SHA-256 hex of cert.Raw
|
|
}
|
|
|
|
// ContextWithPeerCert returns a copy of ctx that carries the peer cert.
|
|
func ContextWithPeerCert(ctx context.Context, cert *PeerCert) context.Context {
|
|
return context.WithValue(ctx, peerCertKey{}, cert)
|
|
}
|
|
|
|
// PeerCertFromContext returns the peer cert stored in ctx, or nil.
|
|
func PeerCertFromContext(ctx context.Context) *PeerCert {
|
|
v, _ := ctx.Value(peerCertKey{}).(*PeerCert)
|
|
return v
|
|
}
|
|
|
|
type socks5CmdKey struct{}
|
|
|
|
// ContextWithSocks5Cmd returns a copy of ctx that carries a SOCKS5 command
|
|
// override. Connectors use this to send a non-CONNECT command (e.g. Tor
|
|
// RESOLVE/RESOLVE_PTR) to the upstream proxy.
|
|
func ContextWithSocks5Cmd(ctx context.Context, cmd uint8) context.Context {
|
|
return context.WithValue(ctx, socks5CmdKey{}, cmd)
|
|
}
|
|
|
|
// Socks5CmdFromContext returns the SOCKS5 command override stored in ctx.
|
|
// The bool is false if no override is present.
|
|
func Socks5CmdFromContext(ctx context.Context) (uint8, bool) {
|
|
v, ok := ctx.Value(socks5CmdKey{}).(uint8)
|
|
return v, ok
|
|
}
|
|
|
|
// labelsKey saves the static service labels.
|
|
type labelsKey struct{}
|
|
|
|
// ContextWithLabels returns a new context carrying the given service labels.
|
|
func ContextWithLabels(ctx context.Context, labels map[string]string) context.Context {
|
|
return context.WithValue(ctx, labelsKey{}, labels)
|
|
}
|
|
|
|
// LabelsFromContext returns the service labels stored in the context, or nil.
|
|
func LabelsFromContext(ctx context.Context) map[string]string {
|
|
v, _ := ctx.Value(labelsKey{}).(map[string]string)
|
|
return v
|
|
}
|