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>
4.1 KiB
4.1 KiB
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
run_listenerbinds a TCP socket, builds the TLS acceptor fromServerTlsMaterial, and starts accepting connections.handle_listener_connectionperforms the TLS accept, verifies the client certificate (unless insecure), reads the HTTP POST to/tunnel, and validates theX-Rustunnel-Tokenheader withconstant_time_compare.- On auth success, it calls
handle_framing_serverto switch to binary framing. handle_framing_serverreads 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.- If the listener was started with
--socks, a server-side SOCKS5 proxy runs viarun_socks5_proxy, receiving the activeStreamMuxthrough awatch::channel.
Connector side
run_connector_with_socksbuilds the client TLS config and starts two concurrent pieces: a reconnect loop and a permanently-bound SOCKS5 proxy.run_reconnect_looprepeatedly 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.- On success, the loop creates a
StreamMux, publishes it via thewatch::channel, and waits for the framing task to finish (indicating disconnection). - When the tunnel drops, the loop logs the disconnection and retries.
run_socks5_proxyaccepts SOCKS5 connections. For each connection, it checks if theStreamMuxis available, opens a stream viamux.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.
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) andauthenticate_tunnel(connector) insrc/tunnel.rs. - To change reconnect strategy: modify
run_reconnect_loopinsrc/tunnel.rs. - To add listener-side SOCKS5 behavior: the server-side SOCKS5 proxy is currently minimal; expand
run_socks5_proxyinsrc/tunnel.rs.
Key source files
| File | Purpose |
|---|---|
src/tunnel.rs |
Full tunnel engine: listener, connector, SOCKS5 proxy, reconnect, framing loops, stream mux |