6c14c0d103
Foundation for v3.1 onion routing (client → entry-relay → exit-server).
The relay/circuit runtime is implemented in a follow-up commit; this
scaffold lands the wire-level control extensions and the config schema:
- aura-proto: ControlKind gains ExtendBridge (client→relay), CircuitReady
(relay→client), CircuitFailed (relay→client, with utf-8 reason); helpers
encode_extend_bridge / decode_extend_bridge (1-byte family + 4/16 addr
bytes + u16 port). Integration test in tests/control_extend.rs covers
IPv4/IPv6 roundtrip + full magic-envelope wrap.
- aura-cli config: [server.relay] {enabled, allow_extend_to} +
[client.circuit] {enabled, hops} sections; relay_whitelist() helper
parses IP:port literals. All new fields serde-default, back-compat.
- crl_push.rs touched only to leave the new ControlKinds passing through
the existing magic-envelope dispatcher unchanged.
Workspace: 247 tests passed (+12), clippy/fmt clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
71 lines
2.8 KiB
Rust
71 lines
2.8 KiB
Rust
//! Integration test for v3.1 multi-hop control envelope payloads (`ExtendBridge`).
|
|
//!
|
|
//! Mirrors `frame.rs`'s in-crate unit coverage but at the integration level so an external
|
|
//! consumer of `aura-proto` (the CLI's `circuit` module) sees the same wire layout.
|
|
|
|
use std::net::SocketAddr;
|
|
|
|
use aura_proto::{
|
|
decode_control_envelope, decode_extend_bridge, encode_control_envelope, encode_extend_bridge,
|
|
ControlKind,
|
|
};
|
|
|
|
#[test]
|
|
fn extend_bridge_payload_roundtrips_ipv4() {
|
|
let addr: SocketAddr = "203.0.113.42:443".parse().unwrap();
|
|
let payload = encode_extend_bridge(addr);
|
|
assert_eq!(payload.len(), 1 + 4 + 2);
|
|
let got = decode_extend_bridge(&payload).expect("decode v4");
|
|
assert_eq!(got, addr);
|
|
}
|
|
|
|
#[test]
|
|
fn extend_bridge_payload_roundtrips_ipv6() {
|
|
let addr: SocketAddr = "[2001:db8::dead:beef]:1234".parse().unwrap();
|
|
let payload = encode_extend_bridge(addr);
|
|
assert_eq!(payload.len(), 1 + 16 + 2);
|
|
let got = decode_extend_bridge(&payload).expect("decode v6");
|
|
assert_eq!(got, addr);
|
|
}
|
|
|
|
#[test]
|
|
fn extend_bridge_via_full_envelope() {
|
|
// Build the bytes the client actually sends over the wire: the envelope wraps the payload.
|
|
let addr: SocketAddr = "10.0.0.5:443".parse().unwrap();
|
|
let payload = encode_extend_bridge(addr);
|
|
let envelope = encode_control_envelope(ControlKind::ExtendBridge, &payload);
|
|
let (kind, decoded_payload) = decode_control_envelope(&envelope).unwrap().unwrap();
|
|
assert_eq!(kind, ControlKind::ExtendBridge);
|
|
let got_addr = decode_extend_bridge(&decoded_payload).expect("decode addr from envelope");
|
|
assert_eq!(got_addr, addr);
|
|
}
|
|
|
|
#[test]
|
|
fn extend_bridge_rejects_malformed_payload() {
|
|
assert!(decode_extend_bridge(&[]).is_err());
|
|
assert!(decode_extend_bridge(&[4u8]).is_err()); // family but truncated
|
|
assert!(decode_extend_bridge(&[4u8, 1, 2, 3, 4]).is_err()); // missing port bytes
|
|
assert!(decode_extend_bridge(&[4u8, 1, 2, 3, 4, 0, 0, 99]).is_err()); // extra byte
|
|
assert!(decode_extend_bridge(&[6u8, 0, 0]).is_err()); // v6 truncated
|
|
assert!(decode_extend_bridge(&[7u8, 0, 0, 0, 0, 0, 0]).is_err()); // unknown family
|
|
}
|
|
|
|
#[test]
|
|
fn circuit_ready_envelope_has_empty_payload() {
|
|
let envelope = encode_control_envelope(ControlKind::CircuitReady, &[]);
|
|
let (kind, payload) = decode_control_envelope(&envelope).unwrap().unwrap();
|
|
assert_eq!(kind, ControlKind::CircuitReady);
|
|
assert!(payload.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn circuit_failed_carries_utf8_reason() {
|
|
let envelope = encode_control_envelope(ControlKind::CircuitFailed, b"not in allow_extend_to");
|
|
let (kind, payload) = decode_control_envelope(&envelope).unwrap().unwrap();
|
|
assert_eq!(kind, ControlKind::CircuitFailed);
|
|
assert_eq!(
|
|
std::str::from_utf8(&payload).unwrap(),
|
|
"not in allow_extend_to"
|
|
);
|
|
}
|