review-003 N-8: the worthwhile residue probed — the BIND Domain-reply wire helper + table, the refusal-recode unit pins, the serve() wrappers, the len/is_empty accessors, the bridge later-batch garbage
This commit is contained in:
1 parent
b43bf5462b
commit
b43cbd24ba
3 files changed
+293
-7
No files matched your search
@@ -104,6 +104,14 @@ impl VanillaClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// The dial without the panic (the `serve` wrapper's bind races
|
||||
/// the test's connect — the retry loop's probe).
|
||||
async fn try_dial(addr: SocketAddr) -> std::io::Result<Self> {
|
||||
Ok(Self {
|
||||
stream: TcpStream::connect(addr).await?,
|
||||
})
|
||||
}
|
||||
|
||||
/// Greeting `05 02 00` (password auth offered — the cred-bearing
|
||||
/// door's shape) → the method-selection read.
|
||||
async fn greet_password(&mut self) -> u8 {
|
||||
@@ -1018,3 +1026,79 @@ async fn associate_session_with_token_round_trip() {
|
||||
);
|
||||
session.shutdown().await.expect("shutdown");
|
||||
}
|
||||
|
||||
/// The `serve()` wrapper (review 003 N-8: every test drove `serve_on`
|
||||
/// — the bind-then-serve wrapper round-trips too; the NoAuth raw-pipe
|
||||
/// shape, the port probed up front so the wrapper's own bind lands on
|
||||
/// it).
|
||||
#[tokio::test]
|
||||
async fn front_door_serve_wrapper_round_trips() {
|
||||
let echo = echo_tcp_server().await;
|
||||
let dial = alksocks::tcp_dial();
|
||||
let topo = harness::wire_consumer_side_with(dial, None, None, None).await;
|
||||
|
||||
// A free loopback port, probed up front (the door binds exactly
|
||||
// it — the wrapper's own bind decision). The connect retries:
|
||||
// the wrapper's bind races the probe's teardown (the OS may hold
|
||||
// the freed port briefly).
|
||||
let mut addr = None;
|
||||
for _ in 0..50 {
|
||||
let probe = TcpListener::bind("127.0.0.1:0").await.expect("probe bind");
|
||||
let candidate = probe.local_addr().expect("probe addr");
|
||||
drop(probe);
|
||||
tokio::time::sleep(Duration::from_millis(10)).await;
|
||||
if TcpStream::connect(candidate).await.is_err() {
|
||||
addr = Some(candidate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let addr = addr.expect("a genuinely free port");
|
||||
|
||||
let door = FrontDoor::builder()
|
||||
.bind(addr)
|
||||
.build()
|
||||
.expect("the door builds");
|
||||
let serve = tokio::spawn({
|
||||
let client = Arc::clone(&topo.consumer);
|
||||
async move { door.serve(client).await.expect("the door serves") }
|
||||
});
|
||||
|
||||
let mut vanilla = loop {
|
||||
if let Ok(v) = VanillaClient::try_dial(addr).await {
|
||||
break v;
|
||||
}
|
||||
tokio::time::sleep(Duration::from_millis(20)).await;
|
||||
};
|
||||
let method = timeout(BOUND, vanilla.greet_no_auth())
|
||||
.await
|
||||
.expect("greeting");
|
||||
assert_eq!(method, consts::SOCKS5_AUTH_METHOD_NONE);
|
||||
let code = timeout(BOUND, vanilla.connect_v4(echo))
|
||||
.await
|
||||
.expect("CONNECT");
|
||||
assert_eq!(code, consts::SOCKS5_REPLY_SUCCEEDED);
|
||||
vanilla.stream.write_all(b"served").await.unwrap();
|
||||
let mut buf = [0u8; 6];
|
||||
timeout(BOUND, vanilla.stream.read_exact(&mut buf))
|
||||
.await
|
||||
.expect("echo")
|
||||
.expect("read");
|
||||
assert_eq!(&buf, b"served");
|
||||
serve.abort();
|
||||
}
|
||||
|
||||
/// The credential table's observability accessors (review 003 N-8:
|
||||
/// `len`/`is_empty` had no caller — one probe pins both).
|
||||
#[test]
|
||||
fn credential_table_len_and_is_empty_accessors() {
|
||||
let empty = alksocks::CredentialTable::new(vec![]);
|
||||
assert!(empty.is_empty());
|
||||
assert_eq!(empty.len(), 0);
|
||||
let one = alksocks::CredentialTable::new(vec![alksocks::DoorCredential {
|
||||
username: "u".to_string(),
|
||||
password: "p".to_string(),
|
||||
identity: harness::consumer_identity(),
|
||||
}]);
|
||||
assert!(!one.is_empty());
|
||||
assert_eq!(one.len(), 1);
|
||||
}
|
||||
@@ -455,3 +455,49 @@ async fn fresh_association_succeeds_after_teardown() {
|
||||
assert_eq!(payload, b"fresh");
|
||||
serve2.abort();
|
||||
}
|
||||
|
||||
/// The `serve()` wrapper (review 003 N-8: every test drove `serve_on`
|
||||
/// — the bind-then-serve wrapper round-trips too; the port is
|
||||
/// configured explicitly so the vanilla client can address the
|
||||
/// relay).
|
||||
#[tokio::test]
|
||||
async fn udp_door_serve_wrapper_round_trips() {
|
||||
init_trace();
|
||||
let echo_addr = udp_echo_server().await;
|
||||
let topo = harness::wire_consumer_side_with(
|
||||
echo_dial(),
|
||||
Some(alksocks::udp_egress()),
|
||||
None,
|
||||
Some(harness::consumer_identity()),
|
||||
)
|
||||
.await;
|
||||
|
||||
// A free loopback port, probed up front (the door binds exactly
|
||||
// it — the wrapper's own bind decision).
|
||||
let probe = UdpSocket::bind("127.0.0.1:0").await.expect("probe bind");
|
||||
let relay_addr = probe.local_addr().expect("probe addr");
|
||||
drop(probe);
|
||||
|
||||
let client = UdpSocket::bind("127.0.0.1:0").await.expect("client socket");
|
||||
let door = UdpFrontDoor::builder()
|
||||
.bind(relay_addr)
|
||||
.build()
|
||||
.expect("the door builds");
|
||||
let _serve = tokio::spawn({
|
||||
let client = Arc::clone(&topo.consumer);
|
||||
async move { door.serve(client).await.expect("the door serves") }
|
||||
});
|
||||
|
||||
client
|
||||
.send_to(&rfc_datagram(echo_addr, b"wrapped"), relay_addr)
|
||||
.await
|
||||
.expect("vanilla sendto the relay");
|
||||
let mut rx = vec![0u8; 65535];
|
||||
let (n, from) = timeout(BOUND, client.recv_from(&mut rx))
|
||||
.await
|
||||
.expect("reply within bound")
|
||||
.expect("recv");
|
||||
assert_eq!(from, relay_addr, "the reply comes from the wrapper's bind");
|
||||
let (_, payload) = parse_rfc_datagram(&rx[..n]);
|
||||
assert_eq!(payload, b"wrapped");
|
||||
}
|
||||
Reference in new issue
Block a user