common

package
v1.17.0 Latest Latest
Warning

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

Go to latest
Published: Jul 12, 2026 License: BSD-3-Clause Imports: 29 Imported by: 0

Documentation

Overview

address.go provides DNS caching, address resolution helpers, target rotation, and the outbound dial functions used across the tunnel data paths.

Package common provides shared core components and utilities used across the nodepass system. It includes configuration management, network address resolution, protocol detection, encryption/authentication, and common data structures for servers, clients, and masters.

config.go parses tunnel configuration from URL query parameters and populates the Common struct fields: pool capacity, run mode, TLS code, rate limits, slot limits, DNS TTL, load-balancing strategy, and protocol block lists.

control.go contains the control-channel management for pooled tunnel mode. The control connection is a dedicated logical channel multiplexed over the first connection taken from the tunnel pool. It carries JSON-encoded Signal messages (XOR-obfuscated and base64-encoded) that coordinate TCP/UDP flow start, health pings, TLS verification, and pool flush requests.

crypto.go provides cryptographic helpers: ephemeral TLS certificate generation, XOR-based signal obfuscation, HMAC-based authentication tokens, HTTPS handshake pre-auth handling, and mutual TLS fingerprint verification used by the optional TLS code-1 handshake.

logger.go provides the levelled, colour-capable logger used throughout nodepass. Logger is tightly coupled to Common (every tunnel endpoint carries a *Logger).

protocol.go contains helpers for protocol-level operations: emitting PROXY Protocol v1 headers and detecting/blocking unwanted application protocols (SOCKS4/5, plain HTTP, TLS ClientHello) at the connection boundary.

runtime.go provides lifecycle helpers for initialising and tearing down a Common instance: rate limiter, context, listeners, and graceful shutdown.

single.go implements single-connection mode: the periodic event loop and TCP/UDP data paths. In single mode each accepted connection is immediately paired with a fresh dial to the target without a pre-established pool.

tunnel.go contains the pooled-mode data-path entry point (TunnelLoop), the TCP and UDP data loops, and the signal dispatcher (TunnelOnce). It handles both inbound target connections (server-side) and outbound pool connections (client-side) across TCP and UDP.

util.go provides small, self-contained utility functions: environment variable helpers and a generic channel drainer.

Index

Constants

View Source
const (
	// ContextCheckInterval is the polling interval for checking context cancellation in blocking I/O operations.
	// Used to detect shutdown signals without blocking indefinitely on network reads/writes.
	ContextCheckInterval = 50 * time.Millisecond

	// DefaultDNSTTL is the time-to-live duration for cached DNS resolution results. Addresses are re-resolved
	// after this TTL expires to support dynamic target address changes and prevent stale DNS data.
	DefaultDNSTTL = 5 * time.Minute

	// DefaultMinPool is the minimum number of pre-established tunnel connections to maintain in the connection pool.
	// The pool manager will continuously dial new connections until this threshold is reached.
	DefaultMinPool = 64

	// DefaultMaxPool is the maximum number of tunnel connections allowed in the pool. Once this limit is reached,
	// the pool stops accepting new connections until existing ones are released.
	DefaultMaxPool = 1024

	// DefaultServerName is the default TLS Server Name Indication (SNI) hostname when not explicitly provided.
	// Set to "none" to indicate either IP-based connection or omission of SNI field in the TLS ClientHello.
	DefaultServerName = "none"

	// DefaultLBStrategy specifies the default load-balancing strategy when multiple target addresses are available:
	// "0" = round-robin (sequential cycling), "1" = latency-based (lowest response time), "2" = failover (primary until timeout).
	DefaultLBStrategy = "0"

	// DefaultRunMode specifies the default operational mode: "0" = auto-detect (single vs. pool based on config),
	// "1" = single-connection mode (no pool, fresh dial per client), "2" = always use connection pool.
	DefaultRunMode = "0"

	// DefaultPoolType specifies the default tunnel pool backend:
	// "0" = TCP, "1" = QUIC, "2" = WebSocket, "3" = HTTP/2.
	DefaultPoolType = "0"

	// DefaultDialerIP specifies the default source IP for outbound tunnel connections. "auto" means the OS selects
	// the source address using the system's default route; can be overridden with a specific IPv4 or IPv6 address.
	DefaultDialerIP = "auto"

	// DefaultReadTimeout is the default per-connection read deadline (0 = no deadline, infinite wait).
	// Can be overridden per tunnel via URL parameters to enforce maximum time for receiving data on a connection.
	DefaultReadTimeout = 0 * time.Second

	// DefaultRateLimit is the default token-bucket rate limit in bytes per second (0 = unlimited).
	// When enabled (non-zero), controls maximum throughput on tunnel data flows to prevent link saturation.
	DefaultRateLimit = 0

	// DefaultSlotLimit is the maximum number of concurrent connection slots that can be allocated.
	// Prevents unbounded resource allocation when handling many simultaneous tunnels and connections.
	DefaultSlotLimit = 65536

	// DefaultProxyProtocol specifies whether to emit PROXY Protocol v1 headers on outbound connections:
	// "0" = disabled (default), "1" = enabled. Required by some upstream proxies or load balancers that expect HAProxy-compatible headers.
	DefaultProxyProtocol = "0"

	// DefaultBlockProtocol is a bitmask string controlling which application protocols to block at the connection boundary:
	// "1" = block SOCKS4/SOCKS5, "2" = block HTTP CONNECT, "4" = block TLS ClientHello. Multiple flags can be combined.
	DefaultBlockProtocol = "0"

	// DefaultTCPStrategy is reserved for future TCP-specific strategy selection. Currently unused but retained for forward compatibility.
	DefaultTCPStrategy = "0"

	// DefaultUDPStrategy is reserved for future UDP-specific strategy selection. Currently unused but retained for forward compatibility.
	DefaultUDPStrategy = "0"
)

Default configuration values used when URL query parameters are absent.

View Source
const (
	// AnsiBlue is the ANSI escape code for blue text, used for Debug level messages.
	AnsiBlue = "\033[34m"

	// AnsiGreen is the ANSI escape code for green text, used for Info level messages.
	AnsiGreen = "\033[32m"

	// AnsiYellow is the ANSI escape code for yellow text, used for Warn level messages.
	AnsiYellow = "\033[33m"

	// AnsiRed is the ANSI escape code for red text, used for Error level messages.
	AnsiRed = "\033[31m"

	// AnsiCyan is the ANSI escape code for cyan text, used for Event level messages.
	AnsiCyan = "\033[36m"

	// ResetColor is the ANSI escape code that resets text formatting to default (clears color and style).
	ResetColor = "\033[0m"
)

ANSI colour codes (SGR - Select Graphic Rendition) used when colour output is enabled. These codes work on most Unix/Linux terminals and are wrapped around log level text.

Variables

View Source
var (
	// SemaphoreLimit is the maximum buffer depth for SignalChan and WriteChan. Controls how many signals
	// can be queued before the sender blocks. Configurable via NP_SEMAPHORE_LIMIT environment variable.
	SemaphoreLimit = GetEnvAsInt("NP_SEMAPHORE_LIMIT", 65536)

	// TCPDataBufSize is the size in bytes of each TCP read/write buffer allocated from the buffer pool.
	// Larger values reduce syscalls but increase memory overhead. Configurable via NP_TCP_DATA_BUF_SIZE.
	TCPDataBufSize = GetEnvAsInt("NP_TCP_DATA_BUF_SIZE", 16384)

	// UDPDataBufSize is the size in bytes of each UDP read/write buffer allocated from the buffer pool.
	// Must be large enough to accommodate the maximum UDP datagram size. Configurable via NP_UDP_DATA_BUF_SIZE.
	UDPDataBufSize = GetEnvAsInt("NP_UDP_DATA_BUF_SIZE", 16384)

	// HandshakeTimeout is the maximum duration allowed for the initial TLS and control-channel handshake.
	// Exceeded timeout aborts the tunnel initialization. Configurable via NP_HANDSHAKE_TIMEOUT.
	HandshakeTimeout = GetEnvAsDuration("NP_HANDSHAKE_TIMEOUT", 5*time.Second)

	// TCPDialTimeout is the timeout for establishing outbound TCP connections to target addresses.
	// Exceeded timeout causes the dial to fail and fallback to the next target. Configurable via NP_TCP_DIAL_TIMEOUT.
	TCPDialTimeout = GetEnvAsDuration("NP_TCP_DIAL_TIMEOUT", 5*time.Second)

	// UDPDialTimeout is the timeout for establishing outbound UDP sockets to target addresses.
	// Exceeded timeout causes the dial to fail and fallback to the next target. Configurable via NP_UDP_DIAL_TIMEOUT.
	UDPDialTimeout = GetEnvAsDuration("NP_UDP_DIAL_TIMEOUT", 5*time.Second)

	// UDPReadTimeout is the read timeout for UDP sessions (both target-side and tunnel-side).
	// Exceeded timeout aborts the session and releases the UDP connection. Configurable via NP_UDP_READ_TIMEOUT.
	UDPReadTimeout = GetEnvAsDuration("NP_UDP_READ_TIMEOUT", 30*time.Second)

	// PoolGetTimeout is the timeout for retrieving a connection from the tunnel pool. Exceeded timeout returns
	// an error and causes the associated data transfer to fail. Configurable via NP_POOL_GET_TIMEOUT.
	PoolGetTimeout = GetEnvAsDuration("NP_POOL_GET_TIMEOUT", 5*time.Second)

	// MinPoolInterval is the minimum interval between pool expansion checks. Prevents rapid repeated dials
	// when the pool is below capacity. Configurable via NP_MIN_POOL_INTERVAL.
	MinPoolInterval = GetEnvAsDuration("NP_MIN_POOL_INTERVAL", 100*time.Millisecond)

	// MaxPoolInterval is the maximum interval between pool expansion checks. After inactivity, the pool waits
	// up to this duration before checking if more connections are needed. Configurable via NP_MAX_POOL_INTERVAL.
	MaxPoolInterval = GetEnvAsDuration("NP_MAX_POOL_INTERVAL", 1*time.Second)

	// ReportInterval is the frequency for emitting CHECK_POINT log events that include latency, slot counts,
	// and byte counters. Also used for health checks and pool flush rate-limiting. Configurable via NP_REPORT_INTERVAL.
	ReportInterval = GetEnvAsDuration("NP_REPORT_INTERVAL", 5*time.Second)

	// FallbackInterval is the duration before the failover load-balancing strategy resets back to the primary target.
	// After this interval, the primary target is retried even if it previously failed. Configurable via NP_FALLBACK_INTERVAL.
	FallbackInterval = GetEnvAsDuration("NP_FALLBACK_INTERVAL", 5*time.Minute)

	// ServiceCooldown is the duration to wait before attempting to restart a tunnel after graceful shutdown.
	// Allows time for cleanup, connection draining, and resource release. Configurable via NP_SERVICE_COOLDOWN.
	ServiceCooldown = GetEnvAsDuration("NP_SERVICE_COOLDOWN", 3*time.Second)

	// ShutdownTimeout is the maximum duration allowed for the Stop() method to complete. Exceeded timeout
	// indicates forceful shutdown may be needed. Configurable via NP_SHUTDOWN_TIMEOUT.
	ShutdownTimeout = GetEnvAsDuration("NP_SHUTDOWN_TIMEOUT", 5*time.Second)

	// ReloadInterval is the frequency for checking if the tunnel configuration has changed and needs reloading.
	// Used in daemon/continuous-run scenarios. Configurable via NP_RELOAD_INTERVAL.
	ReloadInterval = GetEnvAsDuration("NP_RELOAD_INTERVAL", 1*time.Hour)
)

Environment-tunable runtime parameters. All can be overridden via environment variables with the NP_ prefix. Example: export NP_TCP_DATA_BUF_SIZE=32768 to change TCP buffer size at runtime.

LevelColors maps each LogLevel to its corresponding ANSI colour code for colourized terminal output. Empty string for None level (no color). Colors are disabled if Logger.ColorEnabled is false.

View Source
var LevelStrings = map[LogLevel]string{
	None:  "NONE",
	Debug: "DEBUG",
	Info:  "INFO",
	Warn:  "WARN",
	Error: "ERROR",
	Event: "EVENT",
}

LevelStrings maps each LogLevel constant to its human-readable display label for log output.

Functions

func Drain

func Drain[T any](ch <-chan T)

Drain empties a channel by consuming all buffered values without blocking. Non-blocking drain using select{case <-ch: default: return}. Used during Stop() to release any goroutines waiting on full channels (SignalChan, WriteChan, VerifyChan) so that they can be garbage collected.

func GetEnvAsDuration

func GetEnvAsDuration(name string, defaultValue time.Duration) time.Duration

GetEnvAsDuration reads a time.Duration from the named environment variable and returns it. Returns defaultValue when the variable is absent, unparseable, or has a negative value. Accepts Go duration format strings (e.g., "5s", "100ms", "1h"). Used by the initialization code to load environment-tunable timeout and interval parameters (NP_* variables).

func GetEnvAsInt

func GetEnvAsInt(name string, defaultValue int) int

GetEnvAsInt reads an integer from the named environment variable and returns it. Returns defaultValue when the variable is absent, non-numeric, or has a negative value. Used by the initialization code to load environment-tunable runtime parameters (NP_* variables).

func NewTLSConfig

func NewTLSConfig() (*tls.Config, error)

NewTLSConfig generates an ephemeral ECDSA P-256 self-signed certificate in RAM and returns a tls.Config. The certificate is valid for one year, never stored on disk, and automatically discarded on shutdown. Provides forward secrecy: each tunnel instance gets a unique certificate, preventing replay across restarts. Used by both server and client sides to establish encrypted TLS connections with certificate-based validation.

Types

type Addrs

type Addrs struct {
	// TunnelTCPAddr is the resolved TCP address of the tunnel endpoint (from TunnelAddr).
	// Cached during GetAddress and used as fallback if dynamic re-resolution fails.
	TunnelTCPAddr *net.TCPAddr

	// TunnelUDPAddr is the resolved UDP address of the tunnel endpoint (from TunnelAddr).
	// Cached during GetAddress and used as fallback if dynamic re-resolution fails.
	TunnelUDPAddr *net.UDPAddr

	// TargetTCPAddrs holds the resolved TCP addresses for each configured target (from TargetAddrs).
	// Populated during GetAddress from comma-separated target list. Used for TCP connections and probing.
	TargetTCPAddrs []*net.TCPAddr

	// TargetUDPAddrs holds the resolved UDP addresses for each configured target (from TargetAddrs).
	// Populated during GetAddress from comma-separated target list. Kept synchronized with TargetTCPAddrs.
	TargetUDPAddrs []*net.UDPAddr

	// TargetIdx is the current target index for load-balancing, accessed atomically.
	// Meaning depends on LBStrategy: round-robin (incremented each dial), latency-best (set by ProbeBestTarget),
	// or failover (reset after FallbackInterval).
	TargetIdx uint64

	// LastFallback is the timestamp (in nanoseconds, UnixNano) of the last failover reset.
	// Used by the "2" (failover) strategy to determine when to reset to the primary target.
	LastFallback uint64

	// BestLatency is the round-trip latency (in milliseconds) of the lowest-latency target, accessed atomically.
	// Updated by ProbeBestTarget when used with the "1" (latency-based) load-balancing strategy.
	BestLatency int32

	// DNSCacheEntries maps address strings to *DnsCacheEntry for caching resolved addresses.
	// Uses sync.Map for lock-free concurrent reads. Entries expire after DNSCacheTTL duration.
	DNSCacheEntries sync.Map
}

Addrs holds resolved network addresses and load-balancer state. It is embedded in Common and populated by GetAddress during configuration and by Resolve/ResolveAddr during runtime as DNS entries expire. All address slices are populated together and remain synchronized.

type Common

type Common struct {
	// Config embeds all parsed URL configuration (inherited by composition).
	Config

	// Addrs embeds all resolved network addresses and load-balancer state (inherited by composition).
	Addrs

	// Stats embeds atomic counters for TCP/UDP byte transfers, slot allocations, and error tracking.
	*transport.Stats

	// Buffers embeds the pooled byte-slice allocators for TCP and UDP read/write buffers.
	*transport.Buffers

	// Logger is the structured logger for this tunnel instance. Thread-safe, supports multiple log levels.
	Logger *Logger

	// TLSConfig holds the TLS configuration including the ephemeral self-signed certificate.
	// Nil indicates plaintext (no TLS wrapping). Generated once at init via NewTLSConfig.
	TLSConfig *tls.Config

	// TunnelListener is the local TCP listener that accepts incoming tunnel connections.
	// In client mode, this is bound to the tunnel address and receives client traffic.
	TunnelListener net.Listener

	// TargetListener is the local TCP listener that accepts incoming target connections.
	// In server mode (DataFlow "-"), this listens on the target address for inbound traffic.
	TargetListener *net.TCPListener

	// ControlConn is the dedicated control-channel connection (claimed with ID "00000000" from the pool).
	// Multiplexes inbound/outbound Signal messages for pool coordination, health checks, and TLS verification.
	ControlConn net.Conn

	// TunnelUDPConn is the tunnel-side UDP socket wrapped with statistics tracking and optional rate limiting.
	// In client mode, receives UDP from clients; in server mode, forwards UDP to targets.
	TunnelUDPConn *transport.StatConn

	// TargetUDPConn is the target-side UDP socket wrapped with statistics tracking and optional rate limiting.
	// In server mode, receives UDP from targets; in client mode, forwards UDP to tunnel endpoint.
	TargetUDPConn *transport.StatConn

	// TargetUDPSession maps session keys (client address strings) to net.Conn for active UDP flows.
	// Each unique client address gets a persistent UDP session to the target. Sessions are cleaned up on timeout.
	TargetUDPSession sync.Map

	// TunnelPool is the connection pool managing pre-established tunnel connections.
	// Implements TransportPool interface; clients retrieve connections by ID, servers add connections.
	TunnelPool TransportPool

	// RateLimiter is the token-bucket rate limiter for bandwidth control (nil when RateLimit == 0).
	// When configured, limits bytes/second throughput on data flows to prevent saturation.
	RateLimiter *transport.RateLimiter

	// BufReader is a buffered reader wrapping ControlConn for efficient line-buffered signal reception.
	// Used to read newline-delimited encoded signals from the remote peer.
	BufReader *bufio.Reader

	// SignalChan receives decoded Signal objects dispatched from TunnelQueue (pooled mode).
	// Buffered channel with capacity SemaphoreLimit to prevent deadlocks. Drained on Stop().
	SignalChan chan Signal

	// WriteChan queues encoded Signal bytes to be written to ControlConn by the writer goroutine.
	// Buffered channel with capacity SemaphoreLimit. Drained on Stop().
	WriteChan chan []byte

	// VerifyChan is closed/sent when TLS fingerprint verification succeeds (TLS code-1 feature).
	// Blocks TunnelLoop until verification completes or context is cancelled. Drained on Stop().
	VerifyChan chan struct{}

	// Ctx is the cancellation context for this tunnel run. Cancelled by calling Cancel() to initiate shutdown.
	// All goroutines poll Ctx.Done() to detect shutdown signals.
	Ctx context.Context

	// Cancel is the context cancellation function. Call to initiate graceful shutdown of this tunnel instance.
	// Cancelling Ctx causes all goroutines to exit; Stop() should be called afterward for cleanup.
	Cancel context.CancelFunc

	// HandshakeStart records the timestamp when the handshake sequence began (for latency measurement).
	// Logged when control connection is established to measure total tunnel setup time.
	HandshakeStart time.Time

	// CheckPoint records the timestamp of the last ping sent (for round-trip-time measurement).
	// Updated by HealthCheck before sending "ping" signals; used to measure RTT via "pong" response.
	CheckPoint time.Time
}

Common holds all shared state for a tunnel endpoint (client or server) and is embedded directly into Client and Server structs. Each instance represents a single active tunnel with independent configuration, connections, and runtime state. Common must be created via NewCommon.

func NewCommon

func NewCommon(logger *Logger, tlsConfig *tls.Config) Common

NewCommon creates a Common instance wired with the given logger, TLS config, and pre-allocated channels and buffer pools. Caller must separately invoke InitConfig() to parse URL parameters, InitContext() to set up the cancellation context, and InitTunnelListener/InitTargetListener to set up the network listeners. The Stats and Buffers are allocated fresh for each instance.

func (*Common) ClearCache

func (c *Common) ClearCache()

ClearCache removes all entries from the DNS cache by iterating and deleting each cached entry. Called during Stop() to release memory and ensure stale entries are not used if the tunnel is restarted. Safe to call multiple times (subsequent calls are no-ops).

func (*Common) CommonShutdown

func (c *Common) CommonShutdown(ctx context.Context, stopFunc func()) error

CommonShutdown executes stopFunc in a background goroutine and waits for it to complete before the provided context expires. Returns nil when stopFunc completes successfully, or a context error if the shutdown deadline is exceeded. Useful for coordinating graceful shutdown with a deadline to prevent indefinite blocking on stuck Stop() calls.

func (*Common) Decode

func (c *Common) Decode(data []byte) ([]byte, error)

Decode reverses Encode: strips the trailing newline, base64-decodes the string, then XOR-deobfuscates with TunnelKey and returns the original plaintext signal bytes. Returns an error if base64 decoding fails.

func (*Common) DetectBlockProtocol

func (c *Common) DetectBlockProtocol(conn net.Conn) (string, net.Conn)

DetectBlockProtocol peeks at the first 8 bytes of a connection and identifies the application protocol from the byte signature (SOCKS4, SOCKS5, HTTP, or TLS). If the detected protocol is enabled in BlockProtocol bitmask, returns the protocol name and the caller should drop the connection. The returned net.Conn is always wrapped with a ReaderConn containing a *bufio.Reader so that peeked bytes are replayed correctly on subsequent reads (preventing data loss during protocol detection). Protocol signatures:

  • SOCKS4: first byte 0x04, second byte 0x01 or 0x02
  • SOCKS5: first byte 0x05, second byte 0x01-0x03
  • HTTP: first byte 'A'-'Z' (method), followed by space
  • TLS: first byte 0x16 (TLS record type: handshake)

func (*Common) DialWithRotation

func (c *Common) DialWithRotation(network string, timeout time.Duration) (net.Conn, error)

DialWithRotation dials a target connection using the configured load-balancing strategy:

  • "0" (round-robin): increments the index on every call, cycling through targets sequentially
  • "1" (latency-best): uses the index last set by ProbeBestTarget, sticking with lowest latency
  • "2" (failover): sticks to the primary target until FallbackInterval elapses, then resets to try primary again

On failure of the initially chosen target, it walks through remaining addresses in order. Returns the first successful connection or an error wrapping the last failure. Used for all outbound tunnel dials to support various load-balancing and failover strategies.

func (*Common) Encode

func (c *Common) Encode(data []byte) []byte

Encode XOR-obfuscates data with TunnelKey, base64-encodes the result, appends a newline, and returns the wire-ready signal frame (encoded as bytes). Reverses with Decode.

func (*Common) FormatCertFingerprint

func (c *Common) FormatCertFingerprint(certRaw []byte) string

FormatCertFingerprint returns the SHA-256 fingerprint of a DER-encoded certificate as a hex string. Used in TLS code-1 feature to identify and compare certificates for mutual verification.

func (*Common) GenerateAuthToken

func (c *Common) GenerateAuthToken() string

GenerateAuthToken produces an HMAC-SHA256 authentication token derived from TunnelKey. Used for pre-authentication with HTTP CONNECT proxies that require credentials. Returns the hex-encoded HMAC value.

func (*Common) GetAddress

func (c *Common) GetAddress() error

GetAddress parses the tunnel and target addresses from the parsed URL, resolves them to both TCP and UDP net.Addr values, validates them, and stores the results in the corresponding Common fields. Returns an error if any address is missing, unresolvable, or if the tunnel port conflicts with a target address on the loopback interface (indicating misconfiguration). Must be called first during InitConfig.

func (*Common) GetBlockProtocol

func (c *Common) GetBlockProtocol()

GetBlockProtocol reads the "block" query parameter, which is a bitmask string controlling protocol blocking: "1" blocks SOCKS4/SOCKS5, "2" blocks HTTP CONNECT, "4" blocks TLS ClientHello. Multiple protocols can be combined (e.g. "12" blocks both SOCKS and HTTP). Useful for preventing tunnel abuse. Also parses and sets boolean flags (BlockSOCKS, BlockHTTP, BlockTLS) for fast runtime checking.

func (*Common) GetCoreType

func (c *Common) GetCoreType()

GetCoreType reads the URL scheme and stores it in CoreType. Valid values are "client" or "server". Used to determine the role of this tunnel endpoint (initiator or responder).

func (*Common) GetDNSTTL

func (c *Common) GetDNSTTL()

GetDNSTTL reads the "dns" query parameter (e.g. dns=10m) and sets the DNS cache TTL. Falls back to DefaultDNSTTL when the parameter is absent, unparseable, or has a negative value. Determines how long cached DNS entries remain valid before re-resolution.

func (*Common) GetDialFunc

func (c *Common) GetDialFunc(network string, timeout time.Duration) func(string) (net.Conn, error)

GetDialFunc returns a dial function for the given network and timeout that respects DialerIP binding. When DialerIP is "auto", the OS chooses the source address. When DialerIP is a specific IP, the returned function binds outbound connections to that IP and validates that the IP matches the expected address family (IPv4 vs IPv6) based on DialerIPv6. If the IP is invalid or mismatched, it falls back to default dialing. The returned function signature is func(string) (net.Conn, error) suitable for net.Dialer.Dial.

func (*Common) GetDialerIP

func (c *Common) GetDialerIP()

GetDialerIP reads the "dial" query parameter to bind outbound connections to a specific local IP address. "auto" (default) lets the OS choose using the system default route. An invalid IP address logs a warning and falls back to "auto". Used to support multi-homed systems or specific network interface selection.

func (*Common) GetLBStrategy

func (c *Common) GetLBStrategy()

GetLBStrategy reads the "lbs" query parameter and sets the load-balancing strategy: "0"=round-robin (default, stateless sequential cycling), "1"=lowest-latency (uses ProbeBestTarget result), "2"=primary-failover (sticks to primary until FallbackInterval elapses).

func (*Common) GetPoolCapacity

func (c *Common) GetPoolCapacity()

GetPoolCapacity reads "min" and "max" query parameters to set the connection pool bounds. Both default to DefaultMinPool/DefaultMaxPool when absent, unparseable, or non-positive. MinPoolCapacity determines how many connections to maintain; MaxPoolCapacity prevents unbounded growth.

func (*Common) GetPoolType

func (c *Common) GetPoolType()

GetPoolType reads the "type" query parameter and selects the tunnel pool backend. NodePass keeps this query key for URL compatibility while exposing it as the --pool CLI flag. QUIC requires TLS material, so tls=0 is upgraded to code-1.

func (*Common) GetProxyProtocol

func (c *Common) GetProxyProtocol()

GetProxyProtocol reads the "proxy" query parameter. "1" enables emitting PROXY Protocol v1 headers on outbound connections to the target. Required when the tunnel is behind upstream proxies that expect HAProxy-compatible headers.

func (*Common) GetRateLimit

func (c *Common) GetRateLimit()

GetRateLimit reads the "rate" query parameter in Megabits per second and converts to bytes/s for the token bucket. Conversion: Mbit/s * 125000 = bytes/s (1 Mbit = 125000 bytes). 0 (default) disables rate limiting (unlimited). Used to prevent the tunnel from saturating the underlying link or exceeding service quotas.

func (*Common) GetReadTimeout

func (c *Common) GetReadTimeout()

GetReadTimeout reads the "read" query parameter and sets a per-connection read deadline (e.g. read=30s). 0 (default) disables the deadline, allowing indefinite reads. Used to prevent connections from hanging indefinitely when the peer stops sending data. Falls back to DefaultReadTimeout when parameter is absent or invalid.

func (*Common) GetRunMode

func (c *Common) GetRunMode()

GetRunMode reads the "mode" query parameter: "0"=auto-detect (single for one target, pool for multiple), "1"=single-connection (fresh dial per connection, no pool), "2"=pool mode (always use connection pool).

func (*Common) GetServerName

func (c *Common) GetServerName()

GetServerName sets the TLS Server Name Indication (SNI) hostname for the tunnel endpoint. The "sni" query parameter takes precedence. If absent, the tunnel hostname is used when it is a DNS name. IP addresses and empty hostnames fall back to DefaultServerName ("none") to indicate IP-based or no-SNI connection.

func (*Common) GetSlotLimit

func (c *Common) GetSlotLimit()

GetSlotLimit reads the "slot" query parameter and sets the maximum number of simultaneous TCP/UDP connections. Prevents unbounded resource allocation when handling many concurrent tunnel clients. Defaults to DefaultSlotLimit (65536).

func (*Common) GetTCPStrategy

func (c *Common) GetTCPStrategy()

GetTCPStrategy reads the "notcp" query parameter. "1" suppresses the TCP data path entirely, leaving only UDP active. Used to disable TCP tunneling when only UDP is desired.

func (*Common) GetTargetAddrsString

func (c *Common) GetTargetAddrsString() string

GetTargetAddrsString returns all resolved target TCP addresses formatted as a comma-separated string. Useful for logging and debugging to show which targets are currently configured. Returns empty string if no targets are configured.

func (*Common) GetTunnelKey

func (c *Common) GetTunnelKey()

GetTunnelKey derives the pre-shared tunnel key from the URL for XOR obfuscation of control signals. If a username is present in the URL, it is used directly. Otherwise, a stable 4-byte FNV-1a hash of the port string is computed and hex-encoded. This key must match between client and server.

func (*Common) GetTunnelTCPAddr

func (c *Common) GetTunnelTCPAddr() (*net.TCPAddr, error)

GetTunnelTCPAddr re-resolves the tunnel address through the DNS cache and returns its TCP form. Falls back to the pre-cached TunnelTCPAddr on resolution failure, ensuring continuity. Used when fresh resolution is needed (e.g., periodic re-resolution for dynamic addresses).

func (*Common) GetTunnelUDPAddr

func (c *Common) GetTunnelUDPAddr() (*net.UDPAddr, error)

GetTunnelUDPAddr re-resolves the tunnel address through the DNS cache and returns its UDP form. Falls back to the pre-cached TunnelUDPAddr on resolution failure, ensuring continuity. Used when fresh resolution is needed (e.g., periodic re-resolution for dynamic addresses).

func (*Common) GetUDPStrategy

func (c *Common) GetUDPStrategy()

GetUDPStrategy reads the "noudp" query parameter. "1" suppresses the UDP data path entirely, leaving only TCP active. Used to disable UDP tunneling when only TCP is desired.

func (*Common) HandlePreAuth

func (c *Common) HandlePreAuth(w http.ResponseWriter, r *http.Request)

HandlePreAuth hijacks an HTTPS CONNECT request that has passed VerifyPreAuth, dials the target, sends "200 Connection established" to the client, and bidirectionally forwards data between them. Used when the tunnel is accessed through a sampling request that requires pre-authentication.

func (*Common) HealthCheck

func (c *Common) HealthCheck() error

HealthCheck monitors tunnel health on a ReportInterval ticker. Performs the following: 1. Initiates TLS code-1 verification if enabled (sends IncomingVerify after initial delay) 2. Monitors pool error rate and triggers flush when ErrorCount > Active()/2 3. Probes best latency target when LBStrategy "1" is active (latency-based load-balancing) 4. Sends periodic ping signals and measures round-trip latency (logged in CHECK_POINT events)

func (*Common) IncomingVerify

func (c *Common) IncomingVerify()

IncomingVerify initiates TLS code-1 certificate fingerprint verification (client-side in pooled mode). Waits for the pool to be ready, retrieves a test connection, extracts the certificate fingerprint, and sends a "verify" signal to the remote side for comparison. Cancels the tunnel on errors. Called as a background goroutine during HealthCheck when TLSCode == "1".

func (*Common) InitConfig

func (c *Common) InitConfig() error

InitConfig orchestrates all configuration parsers in the correct dependency order. Must be called once after creating a Common instance with a parsed URL (NewCommon + URL assignment). GetAddress must run first (sets TunnelAddr, TargetAddrs, ServerName, ServerPort as side effects). All other getters run afterward in any order. Returns an error if address parsing, resolution, or validation fails.

func (*Common) InitContext

func (c *Common) InitContext()

InitContext cancels any previous cancellation function (from a prior run) and creates a fresh cancellable context. Must be called at the start of each Start() invocation so that per-run goroutines see a clean cancellation signal and previous goroutines can exit. Uses context.Background() as the base context (no parent timeout).

func (*Common) InitRateLimiter

func (c *Common) InitRateLimiter()

InitRateLimiter creates a token-bucket RateLimiter if RateLimit > 0, limiting bandwidth. Both the fill rate (tokens added per second) and burst capacity equal RateLimit (in bytes/s). When RateLimit == 0, the limiter is nil and throughput is unlimited. Must be called after Config is populated and before any data transfer begins.

func (*Common) InitTargetListener

func (c *Common) InitTargetListener() error

InitTargetListener opens the TCP and/or UDP listeners bound to the first target address. Used when the local side accepts traffic destined for the remote target (DataFlow "-"). TCP listener is opened when DisableTCP != "1" and at least one TCP target address exists. UDP listener is opened when DisableUDP != "1" and at least one UDP target address exists. Both listeners are wrapped with statistics tracking (StatConn) for traffic counting and rate limiting. Returns error if any listener fails to open or if no target addresses are available.

func (*Common) InitTunnelListener

func (c *Common) InitTunnelListener() error

InitTunnelListener opens the TCP and/or UDP listeners bound to the tunnel address. In client mode, these listeners accept inbound connections from clients and forward them over the tunnel. In server mode, these listeners are used differently (via TargetListener instead). TCP listener is opened unless DisableTCP == "1" or CoreType is "client" and NOT "server". UDP listener is opened unless DisableUDP == "1" or CoreType is "client" and NOT "server". TLS wrapping (if configured) is applied by the caller after this function returns. Returns error if any listener fails to open (address in use, permission denied, etc.).

func (*Common) NextTargetIdx

func (c *Common) NextTargetIdx() int

NextTargetIdx atomically increments the round-robin index, wraps it to stay within target range, and returns the index for the next target address. Used by round-robin load-balancing strategy ("0"). Thread-safe via atomic operations; always succeeds.

func (*Common) OutgoingVerify

func (c *Common) OutgoingVerify(signal Signal)

OutgoingVerify handles incoming "verify" signals from TLS code-1 verification (server-side in pooled mode). Waits for the pool to be ready, retrieves the designated pool connection, extracts its certificate fingerprint, and compares it against the fingerprint received in the signal. On mismatch, cancels the context (aborting the tunnel). On match, signals VerifyChan to unblock TunnelLoop. Called as a background goroutine from TunnelOnce.

func (*Common) ProbeBestTarget

func (c *Common) ProbeBestTarget() int

ProbeBestTarget concurrently TCP-pings all configured target addresses and selects the best one. Updates TargetIdx (atomically) to point to the lowest-latency target and updates BestLatency. Returns the best observed latency in milliseconds, or 0 if all targets are unreachable. Used by the latency-based load-balancing strategy ("1") to find the fastest target.

func (*Common) Resolve

func (c *Common) Resolve(network, address string) (any, error)

Resolve resolves the given address string to both TCP and UDP net.Addr values, caching the result for DNSCacheTTL duration to avoid repeated lookups on hot paths. Expired cache entries are evicted and re-resolved on demand. Returns the resolved address for the requested network ("tcp" or "udp"), or an error if resolution fails. Cache hits are very fast; misses trigger DNS resolution.

func (*Common) ResolveAddr

func (c *Common) ResolveAddr(network, address string) (any, error)

ResolveAddr resolves a host:port address for the given network ("tcp" or "udp"). Bare IP addresses bypass the DNS cache and are resolved directly via net.ResolveTCPAddr/net.ResolveUDPAddr. Hostname-based addresses are resolved through the DNS cache via Resolve, supporting dynamic updates via TTL expiry. Returns an error if the address format is invalid or resolution fails.

func (*Common) ResolveTarget

func (c *Common) ResolveTarget(network string, idx int) (any, error)

ResolveTarget re-resolves the target address at index idx through the DNS cache. On error, it falls back to the pre-resolved address stored during initialization (TargetTCPAddrs/TargetUDPAddrs). Returns an error if the index is out of range. Used to support dynamic target address updates.

func (*Common) SendProxyV1Header

func (c *Common) SendProxyV1Header(ip string, conn net.Conn) error

SendProxyV1Header writes a PROXY Protocol v1 header to the outbound connection when ProxyProtocol == "1". The header encodes the original client address (from the ip parameter) and the target server address so that upstream proxies or load balancers can log the real client IP and reconstruct the original flow. Does nothing (returns nil) when ProxyProtocol is not "1", allowing transparent passthrough without headers. Header format: "PROXY TCP4|TCP6 <srcIP> <dstIP> <srcPort> <dstPort>\r\n"

func (*Common) SetControlConn

func (c *Common) SetControlConn() error

SetControlConn waits for the tunnel pool to become ready, then claims the special control connection (ID "00000000") from the pool. Starts a background writer goroutine that drains WriteChan onto the wire in real-time, and sets up a buffered reader for efficient inbound signal reception. Returns an error on HandshakeTimeout or if the pool connection cannot be obtained.

func (*Common) SingleControl

func (c *Common) SingleControl() error

SingleControl orchestrates single-connection mode control plane by launching data loops concurrently: 1. SingleEventLoop: emits periodic CHECK_POINT metric events (if targets exist) 2. SingleTCPLoop: accepts TCP connections and dials targets (if TCP enabled) 3. SingleUDPLoop: handles UDP datagrams and sessions (if UDP enabled) Returns the first error any goroutine produces, or context cancellation error.

func (*Common) SingleEventLoop

func (c *Common) SingleEventLoop() error

SingleEventLoop emits periodic CHECK_POINT metric events on ReportInterval ticker for the lifetime of the tunnel. Each event logs: RunMode, latency probe result (ProbeBestTarget), pool count (0 for single mode), TCP/UDP slot counts, and byte counters (TCPRX/TCPTX/UDPRX/UDPTX) for monitoring and metrics extraction.

func (*Common) SingleTCPLoop

func (c *Common) SingleTCPLoop() error

SingleTCPLoop is the main TCP data loop for single-connection mode. For each accepted connection: 1. Wraps with statistics tracking and rate limiting 2. Enforces TCP slot limits (returning error if limit reached) 3. Detects and blocks unwanted protocols (SOCKS, HTTP, TLS) if enabled 4. Dials a target using load-balanced rotation strategy 5. Optionally emits PROXY Protocol v1 header for real client IP 6. Runs DataExchange between client and target concurrently Returns error when listener closes or context is cancelled.

func (*Common) SingleUDPLoop

func (c *Common) SingleUDPLoop() error

SingleUDPLoop is the main UDP data loop for single-connection mode. For each inbound datagram: 1. Creates a session key from the client source address 2. For new sessions: dials a fresh target, stores the connection, and spawns a background reader 3. For existing sessions: reuses the stored connection 4. Enforces UDP slot limits 5. Forwards the datagram to the target and receives responses back to the client Returns error when listener closes, persistent read/write failures occur, or context is cancelled.

func (*Common) Stop

func (c *Common) Stop()

Stop performs graceful shutdown of the tunnel instance by: 1. Cancelling the context (signals all goroutines to exit) 2. Closing the tunnel pool (all pre-established connections) 3. Closing all UDP sessions (target-side) 4. Closing all listeners (tunnel and target, TCP and UDP) 5. Closing the control connection 6. Draining buffered channels (SignalChan, WriteChan, VerifyChan) to release blocked goroutines 7. Resetting the rate limiter 8. Clearing the DNS cache Safe to call multiple times; subsequent calls are no-ops. Does not wait for goroutines to exit; caller should wait if needed using context or other synchronization.

func (*Common) TcpPing

func (c *Common) TcpPing(idx int) int

TcpPing measures TCP round-trip latency (in milliseconds) to the target at index idx by attempting a TCP connection and timing how long it takes. Returns 0 on any failure (timeout, connection refused, etc.). Used by ProbeBestTarget to compare latencies across multiple targets.

func (*Common) TunnelControl

func (c *Common) TunnelControl() error

TunnelControl orchestrates pooled-mode control plane by launching three concurrent goroutines: 1. TunnelOnce: signal dispatcher (handles individual TCP/UDP transfers) 2. TunnelQueue: inbound signal reader (receives and queues signals from peer) 3. HealthCheck: health monitoring (ping/pong, pool flush, latency probing) Returns the first error any goroutine produces, or context cancellation error.

func (*Common) TunnelLoop

func (c *Common) TunnelLoop()

TunnelLoop blocks until the tunnel pool is ready, optionally waits for TLS fingerprint verification to complete (TLS code-1 feature), then starts the TCP and/or UDP data loops concurrently. Returns immediately when context is cancelled. Entry point for pooled-mode data transfer.

func (*Common) TunnelOnce

func (c *Common) TunnelOnce() error

TunnelOnce is the signal dispatcher loop for pooled mode (client-side). Reads from SignalChan and dispatches each Signal to the appropriate handler based on ActionType:

  • "verify": TLS code-1 fingerprint verification (calls OutgoingVerify)
  • "tcp": initiate TCP transfer (calls TunnelTCPOnce)
  • "udp": initiate UDP transfer (calls TunnelUDPOnce)
  • "flush": trigger pool flush (closes and re-establishes connections)
  • "ping": reply with "pong" signal (health check response)
  • "pong": log CHECK_POINT event with metrics (latency, pool active count, slot/byte counters)

func (*Common) TunnelQueue

func (c *Common) TunnelQueue() error

TunnelQueue reads newline-delimited encoded signals from the buffered control-connection reader, decodes each one, and dispatches it to SignalChan for processing by TunnelOnce. Returns an error when the connection breaks, decoding fails persistently, the queue is full, or the context is cancelled.

func (*Common) TunnelTCPLoop

func (c *Common) TunnelTCPLoop()

TunnelTCPLoop is the TCP data loop for pooled mode (server-side). For each incoming connection: 1. Wraps with statistics tracking and rate limiting 2. Enforces TCP slot limits 3. Detects and blocks unwanted protocols if enabled 4. Retrieves a tunnel pool connection 5. Sends a "tcp" signal to the client-side to initiate the corresponding transfer 6. Runs DataExchange between the target and tunnel connection concurrently Returns when listener closes or context is cancelled.

func (*Common) TunnelTCPOnce

func (c *Common) TunnelTCPOnce(signal Signal)

TunnelTCPOnce handles a single TCP transfer initiated by a remote "tcp" signal (client-side). 1. Retrieves the designated pool connection by ID 2. Enforces TCP slot limits 3. Dials a target using load-balanced rotation strategy 4. Wraps target connection with statistics tracking and rate limiting 5. Optionally emits PROXY Protocol v1 header with original client address 6. Runs DataExchange between tunnel and target concurrently Called as a background goroutine from TunnelOnce.

func (*Common) TunnelUDPLoop

func (c *Common) TunnelUDPLoop()

TunnelUDPLoop is the UDP data loop for pooled mode (server-side). For each inbound datagram: 1. Creates a session key from the client address 2. For new sessions: retrieves a pool connection, stores it, and spawns a background reader 3. For existing sessions: reuses the stored connection 4. Enforces UDP slot limits 5. Sends a "udp" signal to the client-side to initiate the corresponding transfer (new sessions only) 6. Writes the datagram to the tunnel connection and reads responses back Returns when listener closes or context is cancelled.

func (*Common) TunnelUDPOnce

func (c *Common) TunnelUDPOnce(signal Signal)

TunnelUDPOnce handles a single UDP flow initiated by a remote "udp" signal (client-side). For new sessions: 1. Enforces UDP slot limits 2. Dials a target using load-balanced rotation strategy 3. Wraps with statistics tracking and rate limiting 4. Stores the connection in TargetUDPSession by key 5. Spawns two bidirectional forwarding goroutines (tunnel→target and target→tunnel) For existing sessions: reuses the stored connection for forwarding datagrams. Called as a background goroutine from TunnelOnce.

func (*Common) VerifyAuthToken

func (c *Common) VerifyAuthToken(token string) bool

VerifyAuthToken performs constant-time comparison of the provided token against the expected HMAC generated by GenerateAuthToken. Safe against timing attacks.

func (*Common) VerifyPreAuth

func (c *Common) VerifyPreAuth(r *http.Request) bool

VerifyPreAuth checks whether an HTTP request carries valid Basic Proxy-Authorization credentials where the username matches TunnelKey. Returns true if credentials are valid and authenticated. Used to validate pre-authentication headers on HTTP CONNECT proxy requests.

func (*Common) Xor

func (c *Common) Xor(data []byte) []byte

Xor XOR-obfuscates data in-place using TunnelKey as a repeating cipher key and returns the same slice. Intentionally lightweight and not cryptographically strong; provides wire-format obfuscation only. Called by Encode during signal transmission and by Decode during signal reception.

type Config

type Config struct {
	// ParsedURL is the parsed URL object (scheme://[user]@host:port/target?query) used to configure this instance.
	ParsedURL *url.URL

	// CoreType identifies the role of this instance: "client" (initiates tunnels) or "server" (accepts tunnel connections).
	CoreType string

	// RunMode specifies the operational mode: "0"=auto-detect (single for one target, pool for multiple),
	// "1"=single-connection (fresh dial per client, no pool), "2"=always use connection pool.
	RunMode string

	// DataFlow indicates the direction of target traffic: "+" (toward target) or "-" (toward tunnel).
	// Used to determine whether to forward client traffic to target ("+") or accept target responses ("-").
	DataFlow string

	// PoolType selects the transport backend used by the tunnel pool:
	// "0" = TCP, "1" = QUIC, "2" = WebSocket, "3" = HTTP/2.
	PoolType string

	// TLSCode enables optional TLS-specific features: "1" enables mutual certificate fingerprint verification
	// on the RAM-generated ephemeral certificates (TLS code-1 feature).
	TLSCode string

	// TunnelKey is the pre-shared key for XOR obfuscation of control-channel signals. Derived from URL username
	// if present, otherwise computed as FNV-1a hash of the tunnel port. Must match between client and server.
	TunnelKey string

	// TunnelAddr is the resolved host:port address of the tunnel endpoint (client connects to this, server listens on this).
	TunnelAddr string

	// TargetAddrs holds the raw target address strings as provided in the URL path (comma-separated, e.g. "10.0.1.1:443,10.0.1.2:443").
	TargetAddrs []string

	// ServerName is the TLS Server Name Indication (SNI) hostname sent in TLS ClientHello. Set to "none"
	// for IP-based connections or to explicitly omit SNI. Used for tunnel-endpoint TLS sessions.
	ServerName string

	// ServerPort is the port component extracted from TunnelAddr. Used for logging and port conflict detection.
	ServerPort string

	// ClientIP is the remote client IP address for informational purposes. Not actively used in tunneling logic.
	ClientIP string

	// DialerIP specifies the source IP for outbound tunnel connections. "auto" means the OS chooses the address;
	// otherwise must be a valid IPv4 or IPv6 address bound to the local machine. Validated during dial operations.
	DialerIP string

	// DialerIPv6 is true when DialerIP is an IPv6 address, false for IPv4. Used to validate address family consistency during dial operations.
	DialerIPv6 bool

	// LBStrategy specifies the load-balancing strategy when multiple targets are configured:
	// "0" = round-robin (sequential, stateless), "1" = latency-based (use lowest-latency target),
	// "2" = failover (primary until FallbackInterval elapses, then reset).
	LBStrategy string

	// DNSCacheTTL is the time-to-live duration for cached DNS resolution results. Expired entries are re-resolved.
	// Allows supporting dynamic target address changes without restarting the tunnel.
	DNSCacheTTL time.Duration

	// MinPoolCapacity is the minimum number of pre-established tunnel connections to maintain in the pool.
	// The pool manager continuously dials new connections until this threshold is reached.
	MinPoolCapacity int

	// MaxPoolCapacity is the maximum number of tunnel connections allowed in the pool. The pool stops accepting
	// new connections once this limit is reached until existing ones are released.
	MaxPoolCapacity int

	// ProxyProtocol controls PROXY Protocol v1 header emission: "0" = disabled (default),
	// "1" = enabled. Required by some upstream proxies or load balancers that expect HAProxy-compatible headers.
	ProxyProtocol string

	// BlockProtocol is a bitmask string controlling protocol blocking at the connection boundary:
	// "1" = block SOCKS4/SOCKS5, "2" = block HTTP CONNECT, "4" = block TLS ClientHello.
	// Multiple flags can be combined (e.g. "12" blocks SOCKS and HTTP).
	BlockProtocol string

	// BlockSOCKS is true when SOCKS4/SOCKS5 protocols should be detected and rejected (parsed from BlockProtocol).
	BlockSOCKS bool

	// BlockHTTP is true when HTTP CONNECT and plain HTTP protocols should be detected and rejected (parsed from BlockProtocol).
	BlockHTTP bool

	// BlockTLS is true when TLS ClientHello (HTTPS/TLS handshakes) should be detected and rejected (parsed from BlockProtocol).
	BlockTLS bool

	// DisableTCP is "1" to suppress the TCP tunnel entirely (only UDP active). "0" (default) enables TCP.
	DisableTCP string

	// DisableUDP is "1" to suppress the UDP tunnel entirely (only TCP active). "0" (default) enables UDP.
	DisableUDP string

	// RateLimit is the token-bucket rate limit in bytes per second. 0 = unlimited throughput.
	// When non-zero, controls maximum bandwidth on tunnel data flows to prevent link saturation.
	RateLimit int

	// ReadTimeout is the per-connection read deadline. 0 (default) disables read deadlines.
	// Non-zero values enforce maximum time for receiving data on a connection; exceeded timeout aborts the connection.
	ReadTimeout time.Duration
}

Config holds all configuration fields parsed from the tunnel URL. It is embedded in Common and populated by the InitConfig method, which runs all individual configuration parser functions in the correct dependency order. Configuration is read once at startup and is immutable afterward.

type DnsCacheEntry

type DnsCacheEntry struct {
	// TCPAddr is the resolved TCP form of the hostname (host:port parsed to *net.TCPAddr).
	TCPAddr *net.TCPAddr

	// UDPAddr is the resolved UDP form of the hostname (host:port parsed to *net.UDPAddr).
	UDPAddr *net.UDPAddr

	// ExpiredAt is the timestamp when this entry becomes invalid and should be re-resolved.
	// Set to now + DNSCacheTTL when the entry is created. Entries are evicted on-demand after expiration.
	ExpiredAt time.Time
}

DnsCacheEntry caches both the TCP and UDP resolved forms of a single hostname so that a single DNS lookup operation satisfies both protocol families. Entries are stored in Common.DNSCacheEntries and are looked up during address resolution via Resolve().

type LogAdapter

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

LogAdapter bridges the Logger to the standard library log.Logger interface, allowing third-party libraries that expect *log.Logger to write through the Logger at Debug level. Each line is prefixed with "Internal:" to distinguish library-generated logs from application logs.

func (*LogAdapter) Write

func (a *LogAdapter) Write(p []byte) (n int, err error)

Write implements io.Writer for LogAdapter, forwarding stdlib log lines to the Logger at Debug level. Trailing whitespace is trimmed before output. Called whenever a third-party library writes to the adapted *log.Logger. Returns the number of bytes consumed (always len(p)) and nil error to satisfy io.Writer interface.

type LogLevel

type LogLevel int

LogLevel is an ordered severity level for log messages, used to filter output at runtime. Higher numeric values indicate greater severity; only messages at or above the configured level are output.

const (
	// None suppresses all log output. Useful for disabling logging entirely.
	None LogLevel = iota

	// Debug is the lowest severity level, used for verbose diagnostic output. Includes goroutine lifecycle,
	// connection state changes, buffer allocation/deallocation, and detailed protocol traces.
	Debug

	// Info is the normal operational message level. Includes tunnel lifecycle (handshake start, pool established),
	// successful connections, and normal flow of events. Suitable for production logging.
	Info

	// Warn is used for recoverable problems that don't prevent operation: DNS resolution falls back to cached value,
	// target connection attempt fails but failover to next target succeeds, or protocol detection detects blocked traffic.
	Warn

	// Error is used for non-fatal errors that impact a single connection or session: failed dial, read error on a data connection,
	// or slot limit reached. The tunnel remains active but individual transfers may be dropped.
	Error

	// Event is used for structured CHECK_POINT metric events that are parsed by monitoring systems.
	// Contains timing, throughput, and connection count statistics. Separate from general logging.
	Event
)

Log-level constants in ascending order of severity. Note that None silences all output except where explicitly forced.

type Logger

type Logger struct {
	// Mu is the mutex that guards concurrent access to Level, ColorEnabled, and WriteLog.
	// Acquired during level changes and during each log write to serialize output.
	Mu sync.Mutex

	// Level is the minimum LogLevel that will be output. Messages below this level are silently dropped.
	// Can be changed at runtime via SetLogLevel.
	Level LogLevel

	// ColorEnabled controls whether ANSI colour codes are included in log output.
	// Can be toggled at runtime via EnableColor.
	ColorEnabled bool

	// TimeFormat is the Go time layout string used to format log timestamps.
	// Default is "2006-01-02 15:04:05.000" (date + time with millisecond precision).
	TimeFormat string
}

Logger is the central logging object used throughout nodepass. All methods are safe for concurrent use because all modifications to Level and ColorEnabled are guarded by the Mu mutex. Each Common instance carries its own Logger, allowing per-tunnel log level and color configuration.

func NewLogger

func NewLogger(logLevel LogLevel, enableColor bool) *Logger

NewLogger creates a Logger at the given level with optional ANSI colour output. Invalid log levels are clamped to Info level for safety. The default time format is "2006-01-02 15:04:05.000" (date, time, and milliseconds). All Logger instances start with an unlocked mutex.

func (*Logger) Debug

func (l *Logger) Debug(format string, v ...any)

Debug logs a message at Debug level. Calls DoLog with Debug level; dropped if Logger.Level > Debug. Used for verbose diagnostic output: goroutine lifecycle, connection state changes, buffer operations, protocol traces.

func (*Logger) DoLog

func (l *Logger) DoLog(logLevel LogLevel, format string, v ...any)

DoLog emits a message at the given level. Messages below the Logger's minimum level are silently dropped. Invalid log levels are clamped to Info. All output is serialized through the mutex to prevent interleaved writes. The message is formatted using fmt.Sprintf before being passed to WriteLog. This is the central logging function; convenience methods (Debug, Info, Warn, Error, Event) call this with their specific level.

func (*Logger) EnableColor

func (l *Logger) EnableColor(enable bool)

EnableColor toggles ANSI colour output on/off. Acquires the mutex to serialize the change. No-op if the new state matches the current ColorEnabled value. Safe to call concurrently with log operations.

func (*Logger) Error

func (l *Logger) Error(format string, v ...any)

Error logs a message at Error level. Calls DoLog with Error level; dropped if Logger.Level > Error. Used for non-fatal errors: failed dial, read error on connection, slot limit reached, transfer interrupted.

func (*Logger) Event

func (l *Logger) Event(format string, v ...any)

Event logs a message at Event level, typically used for structured CHECK_POINT metric lines. Calls DoLog with Event level; Event level is the highest severity and is typically never filtered. Format: "CHECK_POINT|KEY1=VALUE1|KEY2=VALUE2|..." for machine parsing and metrics extraction.

func (*Logger) GetLogLevel

func (l *Logger) GetLogLevel() LogLevel

GetLogLevel returns the current minimum log level. Acquires the mutex to read consistently and returns the snapshot value. Safe to call concurrently with SetLogLevel and log operations.

func (*Logger) Info

func (l *Logger) Info(format string, v ...any)

Info logs a message at Info level. Calls DoLog with Info level; dropped if Logger.Level > Info. Used for normal operational messages: tunnel lifecycle, successful connections, normal flow events.

func (*Logger) SetLogLevel

func (l *Logger) SetLogLevel(logLevel LogLevel)

SetLogLevel atomically updates the minimum log level to control verbosity. Acquires the mutex to serialize the change. No-op if the new level equals the current level. Safe to call concurrently with log operations.

func (*Logger) StdLogger

func (l *Logger) StdLogger() *log.Logger

StdLogger returns a *log.Logger that writes to this Logger at Debug level using the LogAdapter bridge. Useful for third-party libraries that accept a *log.Logger and expect to emit logs independently. Output will be prefixed with "Internal:" and filtered by the Logger's minimum level.

func (*Logger) Warn

func (l *Logger) Warn(format string, v ...any)

Warn logs a message at Warn level. Calls DoLog with Warn level; dropped if Logger.Level > Warn. Used for recoverable problems: DNS fallback, connection retry success, detected blocked traffic, non-fatal issues.

func (*Logger) WriteLog

func (l *Logger) WriteLog(level LogLevel, timestamp, levelStr, message string)

WriteLog is the low-level write path called after the mutex is acquired and level filtering is complete. It formats and prints the log line to stdout with optional ANSI colour codes around the level label. Format with color: "TIMESTAMP <COLOR>LEVEL<RESET> MESSAGE" Format without color: "TIMESTAMP LEVEL MESSAGE" Must be called with the mutex held to prevent concurrent output corruption.

type ReaderConn

type ReaderConn struct {
	// net.Conn is the underlying network connection (embedded).
	net.Conn

	// Reader is the replacement reader, typically a *bufio.Reader that has peeked at the connection bytes.
	// Reads are performed through this reader rather than directly on the connection.
	Reader io.Reader
}

ReaderConn wraps a net.Conn with a replacement Reader (usually a *bufio.Reader), allowing peeked bytes from protocol detection (DetectBlockProtocol) to be re-read as part of the connection stream. This ensures that bytes consumed during protocol detection are not lost and are properly replayed to the application layer.

func (*ReaderConn) Read

func (rc *ReaderConn) Read(b []byte) (int, error)

Read implements net.Conn.Read by reading from the replacement Reader instead of directly from the underlying connection. This ensures any peeked bytes from protocol detection are replayed correctly to the caller, preventing data loss during the protocol detection phase.

type Signal

type Signal struct {
	// ActionType specifies the action to perform. Valid values are:
	//   - "tcp": initiate a TCP data transfer (server sends to client)
	//   - "udp": initiate a UDP data transfer (server sends to client)
	//   - "verify": TLS certificate fingerprint verification (either side initiates)
	//   - "ping": health check request (server sends to client)
	//   - "pong": health check response (client sends to server)
	//   - "flush": request pool flush (server sends to client)
	ActionType string `json:"action"`

	// RemoteAddr is the originating client address (e.g., "192.168.1.100:54321") for TCP/UDP signals.
	// Included when the remote peer initiates a data transfer; omitted for control signals like ping/flush.
	RemoteAddr string `json:"remote,omitempty"`

	// PoolConnID is the unique identifier of the tunnel pool connection to be used for this transfer.
	// Assigned by the originating side when retrieving the connection from the pool. Special ID "00000000"
	// reserves the control connection. Omitted for control signals like ping/pong/flush.
	PoolConnID string `json:"id,omitempty"`

	// Fingerprint is the SHA-256 certificate fingerprint (as hex string) for TLS code-1 verification.
	// Included only in "verify" signals. Compared by the remote side to ensure mutual certificate match.
	Fingerprint string `json:"fp,omitempty"`
}

Signal is the wire format for control messages exchanged over the control connection between client and server. Signals coordinate tunnel data transfers, health checks, TLS verification, and pool management. Each Signal is JSON-encoded, XOR-obfuscated with TunnelKey, base64-encoded, and newline-terminated before transmission. Received signals are decoded and dispatched by TunnelQueue.

type TransportPool

type TransportPool interface {
	// IncomingGet retrieves a connection from the incoming queue (server-side), assigning it a unique ID.
	// Returns (id, connection, error) or error if timeout expires before a connection is available.
	IncomingGet(timeout time.Duration) (string, net.Conn, error)

	// OutgoingGet retrieves a specific connection from the outgoing queue by ID (client-side).
	// Returns the connection or error if the ID is not found or timeout expires.
	OutgoingGet(id string, timeout time.Duration) (net.Conn, error)

	// Flush closes all connections in the pool and clears the queues, forcing re-establishment.
	// Used in response to health check failures or explicit flush signals.
	Flush()

	// Close closes all pool connections and resources, preventing further access.
	// Called during tunnel shutdown to clean up the pool.
	Close()

	// Ready returns true if the pool is initialized and accepting connections (minimum capacity reached).
	// Returns false during initialization or if pool is stopped.
	Ready() bool

	// Active returns the current number of active (alive, not idle) connections in the pool.
	// Used for monitoring and health check decisions.
	Active() int

	// Capacity returns the maximum number of connections allowed in the pool (from MaxPoolCapacity).
	// Used to enforce hard limits on pool growth.
	Capacity() int

	// Interval returns the current interval between pool expansion checks.
	// Starts at MinPoolInterval and gradually backs off toward MaxPoolInterval during idle periods.
	Interval() time.Duration

	// AddError increments the error counter. Called when a connection fails or data transfer is interrupted.
	// Used by HealthCheck to decide when to flush the pool.
	AddError()

	// ErrorCount returns the current error count for the pool.
	// When ErrorCount > Active()/2, a flush is triggered.
	ErrorCount() int

	// ResetError resets the error counter to zero. Called after a successful flush to start fresh error tracking.
	ResetError()
}

TransportPool abstracts the connection pool interface used to manage pre-established tunnel connections. Implementations (e.g., client.Pool, server.Pool) maintain pooled connections, handle connection lifecycle, and provide thread-safe access via IncomingGet/OutgoingGet. Used in pooled ("pool") mode.

Jump to

Keyboard shortcuts

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