Documentation
¶
Overview ¶
Package gproxy implements a gnet-based event-driven MTProxy server.
Index ¶
- func CheckFrameSize(size int) bool
- func IsUnixSocket(addr string) bool
- func Run(cfg *Config, logger Logger) (shutdown func(), errCh <-chan error)
- func Zeroize(b []byte)
- func ZeroizeArray32(arr *[32]byte)
- type BufferPool
- type Config
- type ConnContext
- func (c *ConnContext) Cleanup()
- func (c *ConnContext) DCID() int
- func (c *ConnContext) ID() uint64
- func (c *ConnContext) LogPrefix() string
- func (c *ConnContext) RealClientAddr(fallback net.Addr) net.Addr
- func (c *ConnContext) Relay() *RelayContext
- func (c *ConnContext) SetRealClientAddr(addr net.Addr)
- func (c *ConnContext) SetRelay(r *RelayContext)
- func (c *ConnContext) SetSpliceConn(conn net.Conn)
- func (c *ConnContext) SetState(state ConnState)
- func (c *ConnContext) SpliceConn() net.Conn
- func (c *ConnContext) State() ConnState
- type ConnLimiter
- type ConnState
- type DCConnContext
- type DesyncDetector
- type HotReloadConfig
- type HotReloader
- type Logger
- type ProxyHandler
- func (h *ProxyHandler) ApplyHotConfig(cfg *Config)
- func (h *ProxyHandler) OnBoot(eng gnet.Engine) gnet.Action
- func (h *ProxyHandler) OnClose(c gnet.Conn, err error) gnet.Action
- func (h *ProxyHandler) OnOpen(c gnet.Conn) ([]byte, gnet.Action)
- func (h *ProxyHandler) OnShutdown(eng gnet.Engine)
- func (h *ProxyHandler) OnTraffic(c gnet.Conn) gnet.Action
- type ProxyProtoResult
- type RelayContext
- type ReplayCache
- type Secret
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func CheckFrameSize ¶ added in v0.1.8
CheckFrameSize checks if a frame size indicates desync. Returns true if the frame size is abnormally large (likely desync).
func IsUnixSocket ¶ added in v0.1.4
IsUnixSocket returns true if the bind address is a Unix socket.
func Run ¶
Run starts the proxy with graceful shutdown support using gnet. Returns a shutdown function that can be called to stop the server.
func Zeroize ¶ added in v0.1.8
func Zeroize(b []byte)
Zeroize overwrites a byte slice with zeros. This is used for secure cleanup of sensitive data like session IDs.
Note: Go's compiler may optimize away zeroing of "dead" variables. For maximum security, call this before the variable goes out of scope while it's still reachable.
func ZeroizeArray32 ¶ added in v0.1.8
func ZeroizeArray32(arr *[32]byte)
ZeroizeArray32 zeros a 32-byte array in place.
Types ¶
type BufferPool ¶ added in v0.1.8
type BufferPool struct {
// contains filtered or unexported fields
}
BufferPool is a sync.Pool wrapper for reusable byte buffers.
func NewBufferPool ¶ added in v0.1.8
func NewBufferPool(size int) *BufferPool
NewBufferPool creates a new buffer pool with the given buffer size.
func (*BufferPool) Get ¶ added in v0.1.8
func (bp *BufferPool) Get() *[]byte
Get retrieves a buffer from the pool. The returned buffer may contain stale data - caller should slice or overwrite.
func (*BufferPool) Put ¶ added in v0.1.8
func (bp *BufferPool) Put(buf *[]byte)
Put returns a buffer to the pool. The buffer should not be used after calling Put.
type Config ¶
type Config struct {
// Secrets is the list of allowed proxy secrets.
Secrets []Secret
Host string // Default SNI hostname (from first secret)
// Network
BindAddr string
// TLS Fronting
MaskHost string // Domain to mimic (SNI validation, proxy links)
MaskPort int // Default port
FetchRealCert bool
SpliceUnrecognized bool
CertRefreshHours int
// Certificate fetching (where to connect to get real cert)
// Defaults to MaskHost:MaskPort if not set
CertHost string
CertPort int
// Splice target (where to forward unrecognized clients)
// Defaults to MaskHost:MaskPort if not set
SpliceHost string
SplicePort int
SpliceProxyProtocol int // 0 = off, 1 = v1 (text), 2 = v2 (binary)
// Performance
IPPreference dc.IPPreference
IdleTimeout time.Duration
TimeSkewTolerance time.Duration
// Upstream (DC connection)
Socks5Addr string // SOCKS5 proxy for DC connections (e.g., "127.0.0.1:1080")
// Incoming connection handling
ProxyProtocol bool // Accept incoming PROXY protocol headers
MaxConnectionsPerIP int // Per IP+secret limit, 0 = unlimited
// Backpressure
MaxWriteBuffer int // Max pending bytes per connection before closing (0 = 4MB default)
// gnet-specific
Multicore bool // Use multiple event loops
ReusePort bool // Enable SO_REUSEPORT
LockOSThread bool // Lock goroutines to OS threads
NumEventLoop int // Number of event loops (0 = auto)
}
Config configures the gnet proxy server.
type ConnContext ¶
type ConnContext struct {
// contains filtered or unexported fields
}
ConnContext holds per-connection state for the gnet event handler.
func NewConnContext ¶
func NewConnContext() *ConnContext
NewConnContext creates a new connection context.
func (*ConnContext) Cleanup ¶ added in v0.1.8
func (c *ConnContext) Cleanup()
Cleanup zeros sensitive data in the connection context. Should be called when the connection is closed. Note: cipher.Stream internal state cannot be zeroed (opaque Go types).
func (*ConnContext) DCID ¶ added in v0.1.8
func (c *ConnContext) DCID() int
DCID returns the DC ID this connection is using (0 if not yet determined).
func (*ConnContext) ID ¶ added in v0.1.4
func (c *ConnContext) ID() uint64
ID returns the connection ID.
func (*ConnContext) LogPrefix ¶ added in v0.1.4
func (c *ConnContext) LogPrefix() string
LogPrefix returns a log prefix like "#123" or "#123:user1".
func (*ConnContext) RealClientAddr ¶ added in v0.1.4
func (c *ConnContext) RealClientAddr(fallback net.Addr) net.Addr
RealClientAddr returns the real client address from PROXY protocol. Falls back to the provided gnet connection's remote address if not set.
func (*ConnContext) Relay ¶
func (c *ConnContext) Relay() *RelayContext
Relay returns the relay context (lock-free, may be nil).
func (*ConnContext) SetRealClientAddr ¶ added in v0.1.4
func (c *ConnContext) SetRealClientAddr(addr net.Addr)
SetRealClientAddr sets the real client address from PROXY protocol.
func (*ConnContext) SetRelay ¶
func (c *ConnContext) SetRelay(r *RelayContext)
SetRelay sets the relay context and transitions to relay state.
func (*ConnContext) SetSpliceConn ¶ added in v0.1.2
func (c *ConnContext) SetSpliceConn(conn net.Conn)
SetSpliceConn sets the splice connection.
func (*ConnContext) SetState ¶
func (c *ConnContext) SetState(state ConnState)
SetState sets the connection state (lock-free).
func (*ConnContext) SpliceConn ¶ added in v0.1.2
func (c *ConnContext) SpliceConn() net.Conn
SpliceConn returns the splice connection (lock-free, may be nil).
func (*ConnContext) State ¶
func (c *ConnContext) State() ConnState
State returns the current connection state (lock-free).
type ConnLimiter ¶ added in v0.1.4
type ConnLimiter struct {
// contains filtered or unexported fields
}
ConnLimiter limits concurrent connections per IP+secret combination. Uses sharded maps and atomic counters for minimal contention.
func NewConnLimiter ¶ added in v0.1.4
func NewConnLimiter(maxConns int) *ConnLimiter
NewConnLimiter creates a new connection limiter. maxConns <= 0 disables limiting.
func (*ConnLimiter) ActiveConnections ¶ added in v0.1.4
func (l *ConnLimiter) ActiveConnections() int64
ActiveConnections returns the total number of active connections being tracked. This is O(n) and should only be used for metrics, not in hot path.
func (*ConnLimiter) Release ¶ added in v0.1.4
func (l *ConnLimiter) Release(key string)
Release releases a connection slot. key must be the value returned by TryAcquire.
func (*ConnLimiter) TryAcquire ¶ added in v0.1.4
TryAcquire attempts to acquire a connection slot for the given IP+secret. Returns the key (for Release) and success status. If maxConns is 0, always succeeds (limiting disabled).
type ConnState ¶
type ConnState int32
ConnState represents the current state of a client connection.
const ( StateReadProxyProto ConnState = iota // Need PROXY protocol header (optional) StateReadTLSHeader // Need 5 bytes for TLS record header StateReadTLSPayload // Need header.length bytes for payload StateReadO2Frame // Need 64 bytes for obfuscated2 frame StateDialingDC // Async dial in progress StateRelaying // Bidirectional relay active StateSplicing // Forward to mask host (invalid client) StateClosed // Connection is closing )
type DCConnContext ¶ added in v0.1.2
type DCConnContext struct {
// Link back to client connection
ClientConn gnet.Conn
// Client context for state access
ClientCtx *ConnContext
// DC ciphers (proxy <-> DC)
DCEncrypt cipher.Stream
DCDecrypt cipher.Stream
// Client ciphers (cached from relay context)
ClientEncrypt cipher.Stream // encrypt TO client
// Flow control: DC connection reference for wake mechanism
DCConn gnet.Conn
}
DCConnContext holds per-DC-connection state.
type DesyncDetector ¶ added in v0.1.8
type DesyncDetector struct {
// contains filtered or unexported fields
}
DesyncDetector tracks and reports protocol desynchronization events. Desync typically happens when crypto state diverges, causing decryption to produce garbage that looks like impossibly large frames.
func NewDesyncDetector ¶ added in v0.1.8
func NewDesyncDetector() *DesyncDetector
NewDesyncDetector creates a new desync detector.
func (*DesyncDetector) Report ¶ added in v0.1.8
func (d *DesyncDetector) Report( ctx *ConnContext, frameSize int, direction string, logger Logger, ) bool
Report reports a potential desync event. Returns true if this event was logged (not deduplicated). direction is "c2dc" (client to DC) or "dc2c" (DC to client).
type HotReloadConfig ¶ added in v0.1.8
type HotReloadConfig struct {
ConfigPath string // Path to config file
LoadConfig func() (*Config, string, error) // Config loader function
Handler *ProxyHandler
Logger Logger
SetLogFn func(level string) // Function to set log level
}
HotReloadConfig contains configuration for the hot reloader.
type HotReloader ¶ added in v0.1.8
type HotReloader struct {
// contains filtered or unexported fields
}
HotReloader watches a config file and reloads hot fields on change. Supports both file watching (fsnotify) and SIGHUP.
func NewHotReloader ¶ added in v0.1.8
func NewHotReloader(cfg HotReloadConfig) *HotReloader
NewHotReloader creates a new hot reloader.
func (*HotReloader) Start ¶ added in v0.1.8
func (r *HotReloader) Start()
Start begins watching for config changes. Returns immediately; watching runs in background goroutines.
func (*HotReloader) Stop ¶ added in v0.1.8
func (r *HotReloader) Stop()
Stop stops the hot reloader.
type Logger ¶
type Logger interface {
Debug(format string, args ...any)
Info(format string, args ...any)
Warn(format string, args ...any)
Error(format string, args ...any)
}
Logger interface for proxy logging.
type ProxyHandler ¶
type ProxyHandler struct {
gnet.BuiltinEventEngine
// contains filtered or unexported fields
}
ProxyHandler implements gnet.EventHandler for the MTProxy server.
func NewProxyHandler ¶
func NewProxyHandler(cfg *Config, logger Logger) *ProxyHandler
NewProxyHandler creates a new gnet proxy handler.
func RunWithHandler ¶ added in v0.1.8
func RunWithHandler(cfg *Config, logger Logger) (shutdown func(), handler *ProxyHandler, errCh <-chan error)
RunWithHandler starts the proxy and returns the handler for hot-reload. Returns a shutdown function, the handler (for hot-reload), and error channel.
func (*ProxyHandler) ApplyHotConfig ¶ added in v0.1.8
func (h *ProxyHandler) ApplyHotConfig(cfg *Config)
ApplyHotConfig applies hot-reloadable configuration changes. Only certain fields can be changed at runtime; others require restart. Hot-reloadable fields:
- IdleTimeout: affects new connections only (existing keep their timeout)
Non-hot fields (require restart):
- BindAddr, Secrets, MaskHost/Port, ProxyProtocol, MaxConnectionsPerIP
func (*ProxyHandler) OnBoot ¶
func (h *ProxyHandler) OnBoot(eng gnet.Engine) gnet.Action
OnBoot is called when the gnet engine starts.
func (*ProxyHandler) OnShutdown ¶
func (h *ProxyHandler) OnShutdown(eng gnet.Engine)
OnShutdown is called when the gnet engine shuts down.
type ProxyProtoResult ¶ added in v0.1.4
type ProxyProtoResult struct {
SrcAddr net.Addr // Source (client) address
DstAddr net.Addr // Destination (server) address
HeaderLen int // Total bytes consumed by header
IsLocal bool // True if LOCAL command (health check)
}
ProxyProtoResult holds the parsed PROXY protocol header result.
func ParseProxyProtocol ¶ added in v0.1.4
func ParseProxyProtocol(data []byte) (*ProxyProtoResult, error)
ParseProxyProtocol attempts to parse a PROXY protocol v1 or v2 header. Returns nil if the data doesn't start with a valid PROXY protocol header. Returns error if header is malformed or incomplete.
type RelayContext ¶
type RelayContext struct {
// Client ciphers (client <-> proxy)
Encryptor cipher.Stream // encrypt data TO client
Decryptor cipher.Stream // decrypt data FROM client
// DC connection and ciphers (proxy <-> DC)
DCConn gnet.Conn // gnet connection to Telegram DC (enrolled in dcClient)
DCEncrypt cipher.Stream // encrypt data TO DC
DCDecrypt cipher.Stream // decrypt data FROM DC
}
RelayContext holds immutable relay state set once after handshake. Read without locking via atomic pointer.
type ReplayCache ¶
type ReplayCache struct {
// contains filtered or unexported fields
}
ReplayCache detects replay attacks by tracking seen session IDs. Uses sharded expirable LRU caches for:
- Proper LRU eviction (oldest entries removed first)
- Automatic TTL expiration
- Reduced lock contention (64 shards)
func NewReplayCache ¶
func NewReplayCache(maxSize int, ttl time.Duration) *ReplayCache
NewReplayCache creates a new replay cache. maxSize is the total capacity across all shards. ttl is how long entries are kept before automatic expiration.
func (*ReplayCache) Len ¶ added in v0.1.8
func (c *ReplayCache) Len() int
Len returns the total number of entries across all shards.
func (*ReplayCache) Seen ¶
func (c *ReplayCache) Seen(sessionID []byte) bool
Seen checks if the session ID was seen before and adds it if not. Returns true if this is a replay attack, false if new. This operation is atomic (check-and-add).