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:
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user