119 lines
4.1 KiB
Rust
119 lines
4.1 KiB
Rust
use std::{net::SocketAddr, time::Duration};
|
|
|
|
use irtt_proto::{decode_echo_reply, decode_open_reply, encode_request, Params, RequestToEncode};
|
|
use irtt_server::{ServerConfig, ServerCore};
|
|
|
|
// Admission, authentication, endpoint ownership and capacity are checked through
|
|
// the public packet interface, with actual OS-generated session tokens.
|
|
#[test]
|
|
fn authenticated_sessions_are_bounded_and_owned_by_the_opening_endpoint() {
|
|
let key = b"session-behavior";
|
|
let peer: SocketAddr = "127.0.0.1:40000".parse().unwrap();
|
|
let other: SocketAddr = "127.0.0.1:40001".parse().unwrap();
|
|
let mut server = ServerCore::new(
|
|
ServerConfig::default()
|
|
.with_hmac_key(key)
|
|
.with_max_sessions(1)
|
|
.with_min_send_interval(Duration::ZERO),
|
|
);
|
|
let params = Params {
|
|
received_stats: irtt_proto::ReceivedStats::Both,
|
|
..Params::with_protocol_defaults()
|
|
};
|
|
let open = encode_request(
|
|
RequestToEncode::Open {
|
|
params: ¶ms,
|
|
no_test: false,
|
|
},
|
|
Some(key),
|
|
)
|
|
.unwrap();
|
|
let unauthenticated = encode_request(
|
|
RequestToEncode::Open {
|
|
params: ¶ms,
|
|
no_test: false,
|
|
},
|
|
None,
|
|
)
|
|
.unwrap();
|
|
for packet in [
|
|
&[][..],
|
|
&[0; 16][..],
|
|
&open[..open.len() - 1],
|
|
&unauthenticated,
|
|
] {
|
|
assert!(server.handle_datagram(peer, packet).unwrap().is_none());
|
|
assert_eq!(server.session_count(), 0);
|
|
}
|
|
let reply = server.handle_datagram(peer, &open).unwrap().unwrap();
|
|
let opened = decode_open_reply(reply.bytes(), Some(key)).unwrap();
|
|
assert_ne!(opened.token, 0);
|
|
assert!(server.handle_datagram(other, &open).unwrap().is_none());
|
|
assert_eq!(server.session_count(), 1);
|
|
let echo = encode_request(
|
|
RequestToEncode::Echo {
|
|
token: opened.token,
|
|
sequence: 0,
|
|
params: &opened.params,
|
|
payload: &[],
|
|
},
|
|
Some(key),
|
|
)
|
|
.unwrap();
|
|
let close = encode_request(
|
|
RequestToEncode::Close {
|
|
token: opened.token,
|
|
},
|
|
Some(key),
|
|
)
|
|
.unwrap();
|
|
assert!(server.handle_datagram(other, &echo).unwrap().is_none());
|
|
assert!(server.handle_datagram(other, &close).unwrap().is_none());
|
|
let mut corrupted = echo.clone();
|
|
corrupted[4] ^= 1;
|
|
assert!(server.handle_datagram(peer, &corrupted).unwrap().is_none());
|
|
let reply = server.handle_datagram(peer, &echo).unwrap().unwrap();
|
|
let echoed = decode_echo_reply(reply.bytes(), &opened.params, Some(key)).unwrap();
|
|
assert_eq!(echoed.sequence, 0);
|
|
assert_eq!(echoed.recv_count, Some(1));
|
|
assert!(server.handle_datagram(peer, &close).unwrap().is_none());
|
|
assert_eq!(server.session_count(), 0);
|
|
assert!(server.handle_datagram(peer, &echo).unwrap().is_none());
|
|
assert!(server.handle_datagram(other, &open).unwrap().is_some());
|
|
}
|
|
|
|
// Zero allowance and zero idle lifetime expose the boundary behavior directly,
|
|
// without scripting clock reads or inspecting session/rate representations.
|
|
#[test]
|
|
fn zero_burst_and_zero_idle_timeout_silently_refuse_echoes() {
|
|
let peer: SocketAddr = "127.0.0.1:40000".parse().unwrap();
|
|
for config in [
|
|
ServerConfig::default().with_burst_allowance(0),
|
|
ServerConfig::default().with_idle_timeout(Duration::ZERO),
|
|
] {
|
|
let mut server = ServerCore::new(config);
|
|
let params = Params::with_protocol_defaults();
|
|
let open = encode_request(
|
|
RequestToEncode::Open {
|
|
params: ¶ms,
|
|
no_test: false,
|
|
},
|
|
None,
|
|
)
|
|
.unwrap();
|
|
let reply = server.handle_datagram(peer, &open).unwrap().unwrap();
|
|
let opened = decode_open_reply(reply.bytes(), None).unwrap();
|
|
let echo = encode_request(
|
|
RequestToEncode::Echo {
|
|
token: opened.token,
|
|
sequence: 0,
|
|
params: &opened.params,
|
|
payload: &[],
|
|
},
|
|
None,
|
|
)
|
|
.unwrap();
|
|
assert!(server.handle_datagram(peer, &echo).unwrap().is_none());
|
|
}
|
|
}
|