CI / cargo fmt (push) Has been cancelled
CI / cargo clippy (macos-latest) (push) Has been cancelled
CI / cargo clippy (ubuntu-latest) (push) Has been cancelled
CI / cargo clippy (windows-latest) (push) Has been cancelled
CI / cargo test (macos-latest) (push) Has been cancelled
CI / cargo test (ubuntu-latest) (push) Has been cancelled
CI / cargo test (windows-latest) (push) Has been cancelled
CI / cargo build (macos-latest) (push) Has been cancelled
CI / cargo build (ubuntu-latest) (push) Has been cancelled
CI / cargo build (windows-latest) (push) Has been cancelled
CI / cargo build --release (macos-latest) (push) Has been cancelled
CI / cargo build --release (ubuntu-latest) (push) Has been cancelled
CI / cargo build --release (windows-latest) (push) Has been cancelled
CI / CLI smoke (macos-latest) (push) Has been cancelled
CI / CLI smoke (ubuntu-latest) (push) Has been cancelled
CI / CLI smoke (windows-latest) (push) Has been cancelled
CI / Minimal E2E (macos-latest) (push) Has been cancelled
CI / Minimal E2E (ubuntu-latest) (push) Has been cancelled
CI / Minimal E2E (windows-latest) (push) Has been cancelled
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
70 lines
4.1 KiB
Markdown
70 lines
4.1 KiB
Markdown
# Tunnel engine
|
|
|
|
The tunnel engine in `src/tunnel.rs` is the largest module (~3,500 lines). It implements the listener, connector, SOCKS5 proxy, stream multiplexer, reconnect loop, and HTTP auth handshake.
|
|
|
|
## Purpose
|
|
|
|
The tunnel engine establishes a persistent, authenticated, mTLS-encrypted tunnel between a listener and a connector, then multiplexes multiple SOCKS5 streams over that single connection.
|
|
|
|
## Key abstractions
|
|
|
|
| Type | File | Description |
|
|
| ---- | ---- | ----------- |
|
|
| `ListenerConfig` | `src/tunnel.rs` | Bind address, optional SOCKS5 address, TLS material, auth token, insecure flag |
|
|
| `ConnectorConfig` | `src/tunnel.rs` | Target host/port, TLS material, auth token, insecure flag |
|
|
| `ServerTlsMaterial` | `src/tunnel.rs` | Enum: file paths or embedded PEM strings for server-side TLS |
|
|
| `ClientTlsMaterial` | `src/tunnel.rs` | Enum: file paths or embedded PEM strings for client-side TLS |
|
|
| `StreamMux` | `src/tunnel.rs` | Connector-side multiplexer: manages stream IDs, data channels, and frame dispatch |
|
|
| `ServerStreamEntry` | `src/tunnel.rs` | Listener-side target write half for an active stream |
|
|
| `ConnectorStreamEntry` | `src/tunnel.rs` | Connector-side data channel and reply channel for an active stream |
|
|
|
|
## How it works
|
|
|
|
### Listener side
|
|
|
|
1. `run_listener` binds a TCP socket, builds the TLS acceptor from `ServerTlsMaterial`, and starts accepting connections.
|
|
2. `handle_listener_connection` performs the TLS accept, verifies the client certificate (unless insecure), reads the HTTP POST to `/tunnel`, and validates the `X-Rustunnel-Token` header with `constant_time_compare`.
|
|
3. On auth success, it calls `handle_framing_server` to switch to binary framing.
|
|
4. `handle_framing_server` reads frames from the tunnel. A CONNECT frame triggers a TCP connection to the target address (opened from the listener side). DATA frames are forwarded to the target. CLOSE frames shut down the target connection.
|
|
5. If the listener was started with `--socks`, a server-side SOCKS5 proxy runs via `run_socks5_proxy`, receiving the active `StreamMux` through a `watch::channel`.
|
|
|
|
### Connector side
|
|
|
|
1. `run_connector_with_socks` builds the client TLS config and starts two concurrent pieces: a reconnect loop and a permanently-bound SOCKS5 proxy.
|
|
2. `run_reconnect_loop` repeatedly attempts to connect to the listener with exponential backoff (1s to 30s). Each attempt re-runs all security gates: TLS handshake, server cert verification, and auth token exchange.
|
|
3. On success, the loop creates a `StreamMux`, publishes it via the `watch::channel`, and waits for the framing task to finish (indicating disconnection).
|
|
4. When the tunnel drops, the loop logs the disconnection and retries.
|
|
5. `run_socks5_proxy` accepts SOCKS5 connections. For each connection, it checks if the `StreamMux` is available, opens a stream via `mux.open_stream()`, and performs bidirectional forwarding.
|
|
|
|
### Stream multiplexing
|
|
|
|
`StreamMux::open_stream` allocates the next odd stream ID, registers a data channel and reply channel, sends a CONNECT frame, and waits for a CONNECT_REPLY. The caller receives a `mpsc::Receiver<Bytes>` for tunnel-to-SOCKS5 data.
|
|
|
|
`StreamMux::dispatch_frame` routes inbound CONNECT_REPLY, DATA, CLOSE, and ERROR frames to the correct stream by ID.
|
|
|
|
```mermaid
|
|
graph TD
|
|
A[SOCKS5 client] -->|CONNECT| B[SOCKS5 proxy]
|
|
B -->|open_stream| C[StreamMux]
|
|
C -->|CONNECT frame| D[Framing writer]
|
|
D -->|TLS| E[Listener framing]
|
|
E -->|decode target| F[TCP connect to target]
|
|
F -->|DATA| E
|
|
E -->|DATA frame| D
|
|
D -->|dispatch| C
|
|
C -->|data_rx| B
|
|
B -->|raw bytes| A
|
|
```
|
|
|
|
## Entry points for modification
|
|
|
|
- To change auth behavior: modify `handle_listener_connection` (listener) and `authenticate_tunnel` (connector) in `src/tunnel.rs`.
|
|
- To change reconnect strategy: modify `run_reconnect_loop` in `src/tunnel.rs`.
|
|
- To add listener-side SOCKS5 behavior: the server-side SOCKS5 proxy is currently minimal; expand `run_socks5_proxy` in `src/tunnel.rs`.
|
|
|
|
## Key source files
|
|
|
|
| File | Purpose |
|
|
| ---- | ------- |
|
|
| `src/tunnel.rs` | Full tunnel engine: listener, connector, SOCKS5 proxy, reconnect, framing loops, stream mux |
|