* **New Features** * Multiplexed TLS port: HTTP API and a custom stream protocol can share one port via ALPN. * Agent-side TCP and DTLS/UDP stream tunneling with health-check support and runtime capability detection. * Agents now advertise per-agent stream support (TCP/UDP). * **Documentation** * Added comprehensive stream protocol documentation. * **Tests** * Extended integration and concurrency tests covering multiplexing, TCP/UDP streams, and health checks. * **Chores** * Compose/template updated to expose both TCP and UDP ports.
8.0 KiB
Stream proxy protocol
This package implements a small header-based handshake that allows an authenticated client to request forwarding to a (host, port) destination. It supports both TCP-over-TLS and UDP-over-DTLS transports.
Overview
graph TD
subgraph Client
TC[TCPClient] -->|TLS| TSS[TCPServer]
UC[UDPClient] -->|DTLS| USS[UDPServer]
end
subgraph Stream Protocol
H[StreamRequestHeader]
end
TSS -->|Redirect| DST1[Destination TCP]
USS -->|Forward UDP| DST2[Destination UDP]
Header
The on-wire header is a fixed-size binary blob:
Version(8 bytes)HostLength(1 byte)Host(255 bytes, NUL padded)PortLength(1 byte)Port(5 bytes, NUL padded)Flag(1 byte, protocol flags)Checksum(4 bytes, big-endian CRC32)
Total: headerSize = 8 + 1 + 255 + 1 + 5 + 1 + 4 = 275 bytes.
Checksum is crc32.ChecksumIEEE(header[0:headerSize-4]).
Flags
The Flag field is a bitmask of protocol flags defined by FlagType:
| Flag | Value | Purpose |
|---|---|---|
FlagCloseImmediately |
1 |
Health check probe - server closes immediately after validating header |
See FlagType and FlagCloseImmediately.
See StreamRequestHeader.
File Structure
| File | Purpose |
|---|---|
header.go |
Stream request header structure and validation. |
tcp_client.go |
TCP client implementation with TLS transport. |
tcp_server.go |
TCP server implementation for handling stream requests. |
udp_client.go |
UDP client implementation with DTLS transport. |
udp_server.go |
UDP server implementation for handling DTLS stream requests. |
common.go |
Connection manager and shared constants. |
Constants
| Constant | Value | Purpose |
|---|---|---|
StreamALPN |
"godoxy-agent-stream/1" |
TLS ALPN protocol for stream multiplexing. |
headerSize |
275 bytes |
Total size of the stream request header. |
dialTimeout |
10s |
Timeout for establishing destination connections. |
readDeadline |
10s |
Read timeout for UDP destination sockets. |
FlagCloseImmediately |
1 |
Flag for health check probe - server closes immediately |
See common.go.
Public API
Types
StreamRequestHeader
Represents the on-wire protocol header used to negotiate a stream tunnel.
type StreamRequestHeader struct {
Version [8]byte // Fixed to "0.1.0" with NUL padding
HostLength byte // Actual host name length (0-255)
Host [255]byte // NUL-padded host name
PortLength byte // Actual port string length (0-5)
Port [5]byte // NUL-padded port string
Flag FlagType // Protocol flags (e.g., FlagCloseImmediately)
Checksum [4]byte // CRC32 checksum of header without checksum
}
Methods:
NewStreamRequestHeader(host, port string) (*StreamRequestHeader, error)- Creates a header for the given host and port. Returns error if host exceeds 255 bytes or port exceeds 5 bytes.NewStreamHealthCheckHeader() *StreamRequestHeader- Creates a header withFlagCloseImmediatelyset for health check probes.Validate() bool- Validates the version and checksum.GetHostPort() (string, string)- Extracts the host and port from the header.ShouldCloseImmediately() bool- Returns true ifFlagCloseImmediatelyis set.
TCP Functions
NewTCPClient()- Creates a TLS client connection and sends the stream header.NewTCPServerHandler()- Creates a handler for ALPN-multiplexed connections (no listener).NewTCPServerFromListener()- Wraps an existing TLS listener.NewTCPServer()- Creates a fully-configured TCP server with TLS listener.
UDP Functions
NewUDPClient()- Creates a DTLS client connection and sends the stream header.NewUDPServer()- Creates a DTLS server listening on the given UDP address.
Health Check Probes
The protocol supports health check probes using the FlagCloseImmediately flag. When a client sends a header with this flag set, the server validates the header and immediately closes the connection without establishing a destination tunnel.
This is useful for:
- Connectivity testing between agent and server
- Verifying TLS/DTLS handshake and mTLS authentication
- Monitoring stream protocol availability
Usage:
header := stream.NewStreamHealthCheckHeader()
// Send header over TLS/DTLS connection
// Server will validate and close immediately
Both TCP and UDP servers silently handle health check probes without logging errors.
See NewStreamHealthCheckHeader() and FlagCloseImmediately.
TCP behavior
- Client establishes a TLS connection to the stream server.
- Client sends exactly one header as a handshake.
- After the handshake, both sides proxy raw TCP bytes between client and destination.
Server reads the header using io.ReadFull to avoid dropping bytes.
See NewTCPClient() and (*TCPServer).redirect().
UDP-over-DTLS behavior
- Client establishes a DTLS connection to the stream server.
- Client sends exactly one header as a handshake.
- After the handshake, both sides proxy raw UDP datagrams:
- client -> destination: DTLS payload is written to destination
UDPConn - destination -> client: destination payload is written back to the DTLS connection
- client -> destination: DTLS payload is written to destination
Responses do not include a header.
The UDP server uses a bidirectional forwarding model:
- One goroutine forwards from client to destination
- Another goroutine forwards from destination to client
The destination reader uses readDeadline to periodically wake up and check for context cancellation. Timeouts do not terminate the session.
See NewUDPClient() and (*UDPServer).handleDTLSConnection().
Connection Management
Both TCPServer and UDPServer create a dedicated destination connection per incoming stream session and close it when the session ends (no destination connection reuse).
Error Handling
| Error | Description |
|---|---|
ErrInvalidHeader |
Header validation failed (version or checksum). |
ErrCloseImmediately |
Health check probe - server closed immediately. |
Errors from connection creation are propagated to the caller.
See header.go.
Integration
This package is used by the agent to provide stream tunneling capabilities. See the parent agent package for integration details with the GoDoxy server.
Certificate Requirements
Both TCP and UDP servers require:
- CA certificate for client verification
- Server certificate for TLS/DTLS termination
Both clients require:
- CA certificate for server verification
- Client certificate for mTLS authentication
ALPN Protocol
The StreamALPN constant ("godoxy-agent-stream/1") is used to multiplex stream tunnel traffic and HTTPS API traffic on the same port. Connections negotiating this ALPN are routed to the stream handler.