feat(transport): TCP/443 fallback + unified dialer with UDP->TCP->QUIC handover
- tcp.rs: Aura proto handshake + Session directly over TcpStream (TcpServer/
TcpClient/TcpConnection: PacketConnection), with an optional light HTTP/1.1
masquerade preamble. Fallback for UDP-blocking networks. (Full TLS-443 mimicry
is a documented follow-up.)
- dial.rs: TransportMode {Udp,Tcp,Quic}, Endpoints, DialConfig; client `dial()`
tries transports in order and hands over on failure/timeout; MultiServer binds
and accepts on every enabled transport at once (TCP/QUIC multi-client; UDP
single-peer-per-accept in v1).
- Tests: tcp loopback (plain + masquerade), dial handover (dead TCP -> UDP).
clippy/fmt clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
//! End-to-end loopback test for the TCP fallback transport: real TCP on 127.0.0.1, full Aura
|
||||
//! mutual-auth handshake, packet echo — with the HTTP masquerade both off and on.
|
||||
|
||||
use aura_pki::AuraCa;
|
||||
use aura_proto::{ClientConfig, PacketConnection, ServerConfig};
|
||||
use aura_transport::{TcpClient, TcpOpts, TcpServer};
|
||||
|
||||
/// Mint a fresh CA + server("localhost") + client("client-tcp") and build the proto configs.
|
||||
fn make_configs() -> (ServerConfig, ClientConfig) {
|
||||
let ca = AuraCa::generate("Aura Test CA").expect("generate CA");
|
||||
let server = ca.issue_server_cert("localhost").expect("issue server cert");
|
||||
let client = ca.issue_client_cert("client-tcp").expect("issue client cert");
|
||||
let ca_pem = ca.ca_cert_pem();
|
||||
let scfg = ServerConfig {
|
||||
ca_cert_pem: ca_pem.clone(),
|
||||
server_cert_pem: server.cert_pem,
|
||||
server_key_pem: server.key_pem,
|
||||
};
|
||||
let ccfg = ClientConfig {
|
||||
ca_cert_pem: ca_pem,
|
||||
client_cert_pem: client.cert_pem,
|
||||
client_key_pem: client.key_pem,
|
||||
server_name: "localhost".to_string(),
|
||||
};
|
||||
(scfg, ccfg)
|
||||
}
|
||||
|
||||
async fn run_case(opts: TcpOpts) {
|
||||
let (scfg, ccfg) = make_configs();
|
||||
let server = TcpServer::bind("127.0.0.1:0".parse().unwrap(), scfg, opts.clone())
|
||||
.await
|
||||
.expect("bind server");
|
||||
let addr = server.local_addr().expect("local addr");
|
||||
|
||||
let server_task = tokio::spawn(async move {
|
||||
let conn = server.accept().await.expect("server handshake");
|
||||
assert_eq!(conn.peer_id(), Some("client-tcp"), "verified client id");
|
||||
// Echo three packets back to the client.
|
||||
for _ in 0..3 {
|
||||
let pkt = conn.recv_packet().await.expect("server recv");
|
||||
conn.send_packet(&pkt).await.expect("server echo");
|
||||
}
|
||||
});
|
||||
|
||||
let client = TcpClient::connect(addr, ccfg, opts)
|
||||
.await
|
||||
.expect("client handshake");
|
||||
|
||||
// Exchange packets of varying sizes (incl. a large one) and assert the echo matches.
|
||||
for i in 0..3u16 {
|
||||
let payload = vec![(i as u8).wrapping_add(1); 100 + (i as usize) * 600]; // 100, 700, 1300 bytes
|
||||
client.send_packet(&payload).await.expect("client send");
|
||||
let echoed = client.recv_packet().await.expect("client recv");
|
||||
assert_eq!(echoed, payload, "round-trip payload mismatch");
|
||||
}
|
||||
|
||||
server_task.await.expect("server task");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn tcp_loopback_end_to_end_plain() {
|
||||
run_case(TcpOpts::default()).await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn tcp_loopback_end_to_end_masquerade() {
|
||||
run_case(TcpOpts {
|
||||
masquerade: true,
|
||||
host: "cdn.example.com".to_string(),
|
||||
})
|
||||
.await;
|
||||
}
|
||||
Reference in New Issue
Block a user