feat(transport,tunnel): implement Wave 3 — QUIC transport + split-tunnel router

aura-transport: quinn 0.11 endpoint with HTTP/3 mimicry (ALPN h3/h3-29,
Chrome-like transport params), outer-TLS accept-any (real auth is the inner
Aura handshake), packet padding to HTTPS sizes; AuraServer/AuraClient drive the
proto handshake over a QUIC bidi stream; AuraConnection impls
aura_proto::PacketConnection (full-duplex via Session::split + per-half mutex).
14 tests incl. a real-QUIC loopback end-to-end (crypto+pki+proto+transport).

aura-tunnel: RouteTable (longest-prefix split-tunnel classify), AuraDns
(hickory) host-route registration, AuraRouter over a PacketIo TUN seam +
Arc<dyn PacketConnection>, AuraTun (tun 0.8 unix; wintun cfg-gated Windows).
10 tests (route classify/priority, dst-IP parse, mock router). send_direct is a
v1 stub. Whole workspace: tests green, clippy clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
xah30
2026-05-25 18:26:39 +03:00
parent 0a045c248d
commit c19a6c5586
14 changed files with 1887 additions and 4 deletions
+87
View File
@@ -0,0 +1,87 @@
//! HTTPS/H3 mimicry configuration (project §7, "outer = mimicry").
//!
//! The outer QUIC/TLS layer is meant to look like ordinary browser HTTP/3 traffic so a passive
//! observer sees what appears to be a connection to a CDN, not a VPN. That disguise is *not* the
//! security boundary — see the crate docs and [`crate::quic::AcceptAnyServerCert`]; the real mutual
//! authentication happens in the inner Aura proto handshake. This module just centralizes the
//! browser-flavored knobs (ALPN, a default SNI, transport tuning) so they are set consistently.
use std::time::Duration;
/// ALPN protocol identifiers advertised on the outer TLS handshake.
///
/// `h3` (RFC 9114) and `h3-29` (a still-seen draft) are exactly what Chrome offers for HTTP/3, so
/// advertising them makes the ClientHello/ServerHello ALPN extension indistinguishable from a real
/// browser's. Both client and server must agree, so they share this list.
pub const ALPN_H3: &[&[u8]] = &[b"h3", b"h3-29"];
/// A plausible default SNI to present when the caller does not specify one.
///
/// Picking a generic CDN-looking hostname keeps the Server Name Indication from screaming "VPN".
/// Callers should normally pass their own camouflage hostname to [`crate::AuraClient::connect`];
/// this is only a fallback.
pub const DEFAULT_SNI: &str = "cdn.example.com";
/// Return the ALPN list as owned `Vec<Vec<u8>>`, the shape rustls' `alpn_protocols` field wants.
#[must_use]
pub fn alpn_protocols() -> Vec<Vec<u8>> {
ALPN_H3.iter().map(|p| p.to_vec()).collect()
}
/// Chrome-like QUIC transport timing/flow-control knobs (project §7.1).
///
/// These values mirror what a Chromium HTTP/3 connection uses closely enough that the resulting
/// idle-timeout / keep-alive / flow-control behavior is unremarkable on the wire:
///
/// * `max_idle_timeout` ~ 30s — Chrome's default idle timeout.
/// * `keep_alive_interval` ~ 15s — half the idle timeout, so an otherwise-quiet tunnel stays up.
/// * `max_concurrent_bidi_streams` = 100 — a browser-ish concurrency ceiling.
/// * receive windows ~ 10 MB — generous stream/connection flow-control windows so bulk transfer is
/// not throttled (and matches a browser doing large downloads).
///
/// Returned by value so callers wrap it in `Arc` and hand it to both the client and server
/// `quinn::*Config` (keeping the two ends symmetric, which also aids the disguise).
#[must_use]
pub fn chrome_quic_transport_config() -> quinn::TransportConfig {
/// ~10 MB flow-control window (stream and connection level).
const RECV_WINDOW: u32 = 10 * 1024 * 1024;
let mut tc = quinn::TransportConfig::default();
// 30s idle timeout. `IdleTimeout::try_from(Duration)` only fails for absurdly large durations;
// 30s is always representable, so the expect is unreachable in practice.
let idle = quinn::IdleTimeout::try_from(Duration::from_secs(30))
.expect("30s is a valid QUIC idle timeout");
tc.max_idle_timeout(Some(idle));
tc.keep_alive_interval(Some(Duration::from_secs(15)));
tc.max_concurrent_bidi_streams(100u32.into());
// Keep uni-streams modest; the Aura tunnel only uses one bidi stream, but a browser-like profile
// still permits a handful of unidirectional streams (e.g. H3 control/QPACK streams).
tc.max_concurrent_uni_streams(100u32.into());
tc.stream_receive_window(RECV_WINDOW.into());
tc.receive_window((RECV_WINDOW * 2).into());
tc.send_window(u64::from(RECV_WINDOW) * 2);
tc
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn alpn_is_h3() {
assert_eq!(ALPN_H3, &[b"h3".as_slice(), b"h3-29".as_slice()]);
let owned = alpn_protocols();
assert_eq!(owned, vec![b"h3".to_vec(), b"h3-29".to_vec()]);
}
#[test]
fn transport_config_builds() {
// §7.1: must construct without panicking (the IdleTimeout conversion is the only fallible
// step, and 30s is always valid).
let _tc = chrome_quic_transport_config();
}
}