Skip to content
This repository was archived by the owner on Jun 15, 2026. It is now read-only.

Commit 8a1e144

Browse files
committed
Smux added - Dynamic parallel tunnel instead of fixed one
1 parent 9c10e4d commit 8a1e144

7 files changed

Lines changed: 659 additions & 125 deletions

File tree

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@
1515
| Control channel | Spoofed ICMP Echo Request/Reply (looks like ping traffic) |
1616
| Reliable delivery | Selective Repeat ARQ with per-packet retransmission |
1717
| Congestion control | TCP-like AIMD: slow-start, congestion avoidance, fast retransmit, RTT estimation (RFC 6298) |
18+
| Stream multiplexing | SMUX-style framed multiplexing of many proxy streams over shared transport lanes |
1819
| SOCKS5 proxy | Local proxy on port 1080 – route any app through the tunnel |
1920
| Whitelist validation | Packets from unknown IPs are silently dropped |
20-
| Multiple tunnels | Configurable number of parallel independent tunnels |
21+
| Dynamic parallel tunnels | Runtime scales active lanes from 1 up to `tunnel_count` as concurrent streams increase |
2122
| Pre-shared key | Optional PSK for packet authentication |
2223

2324
## Prerequisites
@@ -79,7 +80,7 @@ After starting the client, configure your applications (browser, curl, etc.) to
7980
| `allowed_peers` | both | Extra IPs to whitelist |
8081
| `interface` | both | Network interface (e.g. `eth0`) |
8182
| `socks5_port` | client | Local SOCKS5 proxy port (default `1080`) |
82-
| `tunnel_count` | both | Number of parallel tunnels (default `4`) |
83+
| `tunnel_count` | both | Max dynamic parallel lanes used by smux transport (default `4`) |
8384
| `mtu` | both | Max payload bytes per packet (default `1380`) |
8485
| `initial_cwnd` | both | Initial congestion window in packets (default `10`) |
8586

@@ -131,7 +132,7 @@ Refer to `config/client.toml` and `config/server.toml` for complete configuratio
131132

132133
## Performance Tips
133134

134-
- Increase `tunnel_count` for higher parallelism on fast links.
135+
- Increase `tunnel_count` to allow more dynamic parallel transport lanes on busy links.
135136
- Tune `mtu` to match path MTU (`tracepath` helps).
136137
- Raise `initial_cwnd` (e.g. `30`) on low-latency links to reduce slow-start
137138
ramp-up time.

src/bin/client.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use clap::Parser;
1414

1515
use candy_spoof::config::Config;
1616
use candy_spoof::raw_socket::{RawReceiver, RawSender};
17+
use candy_spoof::smux::SmuxClient;
1718
use candy_spoof::socks5::run_socks5;
1819
use candy_spoof::tunnel::{PeerAddr, TunnelManager};
1920

@@ -67,6 +68,7 @@ async fn main() -> Result<()> {
6768
is_server: false,
6869
};
6970
let manager = TunnelManager::new(sender, peer_addr, cfg.clone());
71+
let smux = SmuxClient::new(cfg.clone(), manager.clone()).await?;
7072

7173
// ── Background task: process incoming packets ─────────────────────────────
7274
let mgr2 = manager.clone();
@@ -98,7 +100,7 @@ async fn main() -> Result<()> {
98100
});
99101

100102
// ── Foreground: SOCKS5 proxy ──────────────────────────────────────────────
101-
run_socks5(cfg, manager).await?;
103+
run_socks5(cfg, smux).await?;
102104

103105
Ok(())
104106
}

src/bin/server.rs

Lines changed: 5 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,12 @@
99
use std::sync::Arc;
1010
use std::time::Duration;
1111

12-
use anyhow::{Context, Result};
13-
use bytes::Bytes;
12+
use anyhow::Result;
1413
use clap::Parser;
15-
use tokio::io::{AsyncReadExt, AsyncWriteExt};
16-
use tokio::net::TcpStream;
1714

1815
use candy_spoof::config::Config;
1916
use candy_spoof::raw_socket::{RawReceiver, RawSender};
17+
use candy_spoof::smux::SmuxServer;
2018
use candy_spoof::tunnel::{PeerAddr, TunnelManager};
2119

2220
#[derive(Parser, Debug)]
@@ -62,6 +60,7 @@ async fn main() -> Result<()> {
6260
is_server: true,
6361
};
6462
let manager = TunnelManager::new(sender, peer_addr, cfg.clone());
63+
let smux = SmuxServer::new(cfg.clone(), manager.clone()).await?;
6564

6665
// ── Periodic housekeeping ────────────────────────────────────────────────
6766
let mgr_tick = manager.clone();
@@ -93,10 +92,9 @@ async fn main() -> Result<()> {
9392
{
9493
Ok(Some((syn_pkt, src_ip))) => {
9594
// New tunnel request – accept it and spawn a session handler.
96-
let mgr2 = manager.clone();
97-
let cfg2 = cfg.clone();
95+
let smux2 = smux.clone();
9896
tokio::spawn(async move {
99-
if let Err(e) = handle_new_tunnel(syn_pkt, src_ip, mgr2, cfg2).await {
97+
if let Err(e) = smux2.attach_syn(syn_pkt, src_ip).await {
10098
log::warn!("session error: {}", e);
10199
}
102100
});
@@ -108,94 +106,3 @@ async fn main() -> Result<()> {
108106

109107
Ok(())
110108
}
111-
112-
// ── New-tunnel handler ────────────────────────────────────────────────────────
113-
114-
/// Accept a SYN, wait for the first DATA packet containing the CONNECT
115-
/// destination, open a TCP connection to that destination, and relay data.
116-
async fn handle_new_tunnel(
117-
syn: candy_spoof::packet::CandyPacket,
118-
src_ip: std::net::Ipv4Addr,
119-
manager: TunnelManager,
120-
cfg: Arc<Config>,
121-
) -> Result<()> {
122-
let (tunnel_id, mut app_rx, net_tx) = manager
123-
.accept_syn(syn, src_ip)
124-
.await
125-
.context("accept_syn")?;
126-
127-
// First message from the client is the CONNECT destination.
128-
let first_msg = tokio::time::timeout(Duration::from_secs(15), app_rx.recv())
129-
.await
130-
.context("timeout waiting for CONNECT meta")?
131-
.context("tunnel closed before CONNECT meta")?;
132-
133-
let meta = String::from_utf8_lossy(&first_msg);
134-
let (target_host, target_port) = parse_connect_meta(&meta)?;
135-
136-
log::info!(
137-
"tunnel {} forwarding to {}:{}",
138-
tunnel_id,
139-
target_host,
140-
target_port
141-
);
142-
143-
// Open TCP connection to the target.
144-
let target_addr = format!("{}:{}", target_host, target_port);
145-
let tcp_stream = TcpStream::connect(&target_addr)
146-
.await
147-
.with_context(|| format!("connect to {}", target_addr))?;
148-
149-
let (mut tcp_r, mut tcp_w) = tcp_stream.into_split();
150-
let mtu = cfg.mtu;
151-
152-
// Tunnel → TCP
153-
let t_to_tcp = tokio::spawn(async move {
154-
loop {
155-
match app_rx.recv().await {
156-
Some(data) => {
157-
if tcp_w.write_all(&data).await.is_err() { break; }
158-
}
159-
None => break,
160-
}
161-
}
162-
});
163-
164-
// TCP → tunnel
165-
let net_tx2 = net_tx;
166-
let tcp_to_t = tokio::spawn(async move {
167-
let mut buf = vec![0u8; mtu];
168-
loop {
169-
match tcp_r.read(&mut buf).await {
170-
Ok(0) | Err(_) => break,
171-
Ok(n) => {
172-
let chunk = Bytes::copy_from_slice(&buf[..n]);
173-
if net_tx2.send(chunk).await.is_err() { break; }
174-
}
175-
}
176-
}
177-
});
178-
179-
tokio::select! {
180-
_ = t_to_tcp => {}
181-
_ = tcp_to_t => {}
182-
}
183-
184-
manager.close_tunnel(tunnel_id).await;
185-
Ok(())
186-
}
187-
188-
fn parse_connect_meta(meta: &str) -> Result<(String, u16)> {
189-
// Expected format: "CONNECT host:port"
190-
let rest = meta
191-
.strip_prefix("CONNECT ")
192-
.context("missing CONNECT prefix in meta")?;
193-
let (host, port_str) = rest
194-
.rsplit_once(':')
195-
.context("missing ':' in CONNECT meta")?;
196-
let port = port_str
197-
.trim()
198-
.parse::<u16>()
199-
.context("invalid port in CONNECT meta")?;
200-
Ok((host.to_string(), port))
201-
}

src/config.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ pub struct Config {
3838
#[serde(default)]
3939
pub allowed_peers: Vec<Ipv4Addr>,
4040

41-
/// Number of independent parallel tunnels to maintain.
41+
/// Maximum number of parallel smux transport tunnels.
42+
///
43+
/// Runtime dynamically scales active lanes from 1 up to this value based
44+
/// on concurrent stream count.
4245
#[serde(default = "default_tunnel_count")]
4346
pub tunnel_count: usize,
4447

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ pub mod congestion;
55
pub mod raw_socket;
66
pub mod tunnel;
77
pub mod socks5;
8+
pub mod smux;

0 commit comments

Comments
 (0)