Files
AuraVPN/crates/aura-transport/tests/dial_fallback.rs
T
xah30 d5b9a8611d feat(cli): select transport in config; server MultiServer + client dial handover
- aura-cli config gains [transport] (order + per-transport ports + obfuscate/
  masquerade); server binds all enabled transports via MultiServer, client uses
  dial() with UDP->TCP->QUIC handover. Config examples updated; backward-compatible
  (defaults to udp,tcp,quic). 21 cli tests incl. a real-UDP-transport loopback.
- docs/sing-box.md: integration approach note (process-bridge now; native Go
  outbound for phones, with crypto-library mapping + KAT requirement).
- Normalize rustfmt across the v2 transport files (tcp/dial/udp contract).

Whole workspace: 97 tests pass, clippy -D warnings clean, fmt clean. Deploy flow
(pki init/issue-server/issue-client) validated with the release binary.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 21:41:59 +03:00

79 lines
2.7 KiB
Rust

//! Verifies the client dialer's handover: when an earlier transport in the order is unreachable,
//! `dial` moves on to the next and connects.
use std::net::SocketAddr;
use std::time::Duration;
use aura_pki::AuraCa;
use aura_proto::{ClientConfig, PacketConnection, ServerConfig};
use aura_transport::{dial, DialConfig, Endpoints, TcpOpts, TransportMode, UdpOpts, UdpServer};
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-dial")
.expect("issue client cert");
let ca_pem = ca.ca_cert_pem();
(
ServerConfig {
ca_cert_pem: ca_pem.clone(),
server_cert_pem: server.cert_pem,
server_key_pem: server.key_pem,
},
ClientConfig {
ca_cert_pem: ca_pem,
client_cert_pem: client.cert_pem,
client_key_pem: client.key_pem,
server_name: "localhost".to_string(),
},
)
}
#[tokio::test]
async fn dial_falls_back_from_dead_tcp_to_udp() {
let (scfg, ccfg) = make_configs();
// A real UDP server (the working fallback target).
let udp_server = UdpServer::bind("127.0.0.1:0".parse().unwrap(), scfg, UdpOpts::default())
.expect("bind udp");
let udp_addr = udp_server.local_addr().expect("udp addr");
let srv = tokio::spawn(async move {
let conn = udp_server.accept().await.expect("server accept");
let p = conn.recv_packet().await.expect("server recv");
conn.send_packet(&p).await.expect("server echo");
});
// Port 1 on loopback has nothing listening → TCP connect is refused fast.
let dead_tcp: SocketAddr = "127.0.0.1:1".parse().unwrap();
let cfg = DialConfig {
endpoints: Endpoints {
udp: Some(udp_addr),
tcp: Some(dead_tcp),
quic: None,
},
sni: "cdn.example.com".to_string(),
// Deliberately try the (dead) TCP first to force the handover to UDP.
order: vec![TransportMode::Tcp, TransportMode::Udp],
udp: UdpOpts::default(),
tcp: TcpOpts::default(),
attempt_timeout: Duration::from_secs(3),
};
let (conn, mode) = dial(ccfg, cfg).await.expect("dial should fall back to UDP");
assert_eq!(
mode,
TransportMode::Udp,
"must hand over to UDP after TCP is refused"
);
conn.send_packet(b"hello-fallback").await.expect("send");
let echoed = conn.recv_packet().await.expect("recv");
assert_eq!(echoed, b"hello-fallback");
srv.await.expect("server task");
}