mirror of
https://github.com/yusing/godoxy.git
synced 2026-03-25 10:31:30 +01:00
* **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.
198 lines
8.0 KiB
Markdown
198 lines
8.0 KiB
Markdown
# 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
|
|
|
|
```mermaid
|
|
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`](header.go:26) and [`FlagCloseImmediately`](header.go:28).
|
|
|
|
See [`StreamRequestHeader`](header.go:30).
|
|
|
|
## File Structure
|
|
|
|
| File | Purpose |
|
|
| ----------------------------------- | ------------------------------------------------------------ |
|
|
| [`header.go`](header.go) | Stream request header structure and validation. |
|
|
| [`tcp_client.go`](tcp_client.go:12) | TCP client implementation with TLS transport. |
|
|
| [`tcp_server.go`](tcp_server.go:13) | TCP server implementation for handling stream requests. |
|
|
| [`udp_client.go`](udp_client.go:13) | UDP client implementation with DTLS transport. |
|
|
| [`udp_server.go`](udp_server.go:17) | UDP server implementation for handling DTLS stream requests. |
|
|
| [`common.go`](common.go:11) | 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`](common.go:11).
|
|
|
|
## Public API
|
|
|
|
### Types
|
|
|
|
#### `StreamRequestHeader`
|
|
|
|
Represents the on-wire protocol header used to negotiate a stream tunnel.
|
|
|
|
```go
|
|
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 with `FlagCloseImmediately` set 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 if `FlagCloseImmediately` is set.
|
|
|
|
### TCP Functions
|
|
|
|
- [`NewTCPClient()`](tcp_client.go:26) - Creates a TLS client connection and sends the stream header.
|
|
- [`NewTCPServerHandler()`](tcp_server.go:24) - Creates a handler for ALPN-multiplexed connections (no listener).
|
|
- [`NewTCPServerFromListener()`](tcp_server.go:36) - Wraps an existing TLS listener.
|
|
- [`NewTCPServer()`](tcp_server.go:45) - Creates a fully-configured TCP server with TLS listener.
|
|
|
|
### UDP Functions
|
|
|
|
- [`NewUDPClient()`](udp_client.go:27) - Creates a DTLS client connection and sends the stream header.
|
|
- [`NewUDPServer()`](udp_server.go:26) - 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:**
|
|
|
|
```go
|
|
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()`](header.go:66) and [`FlagCloseImmediately`](header.go:28).
|
|
|
|
## TCP behavior
|
|
|
|
1. Client establishes a TLS connection to the stream server.
|
|
2. Client sends exactly one header as a handshake.
|
|
3. 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()`](tcp_client.go:26) and [`(*TCPServer).redirect()`](tcp_server.go:116).
|
|
|
|
## UDP-over-DTLS behavior
|
|
|
|
1. Client establishes a DTLS connection to the stream server.
|
|
2. Client sends exactly one header as a handshake.
|
|
3. 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
|
|
|
|
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()`](udp_client.go:27) and [`(*UDPServer).handleDTLSConnection()`](udp_server.go:89).
|
|
|
|
## 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`](header.go:23).
|
|
|
|
## Integration
|
|
|
|
This package is used by the agent to provide stream tunneling capabilities. See the parent [`agent`](../README.md) 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.
|