cultnet-rs: pin every RUDP stray drop site with an exact count

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-29 18:21:58 +02:00
commit 2ef7ae92c4

View file

@ -50,6 +50,24 @@ fn send_to(socket: &UdpSocket, to: SocketAddr, packet: &CultNetRudpPacket) -> Re
Ok(())
}
/// How many datagrams `send_stray` puts on the wire for `kind`.
fn datagrams(kind: Stray) -> u64 {
match kind {
Stray::Malformed => 2,
Stray::ForeignConnectionId => 4,
Stray::UnadmittedSender => 3,
}
}
/// A packet the sender's own session accepted for sending and its receiver's
/// session refuses: a fragment with no fragment id.
fn poisoned(session: &mut CultNetRudpSession) -> Result<CultNetRudpPacket> {
let mut packet = session.send("schema", b"poison".to_vec(), reliable())?;
packet.fragment_count = 2;
packet.fragment_id = 0;
Ok(packet)
}
fn send_stray(kind: Stray, from: &UdpSocket, to: SocketAddr) -> Result<()> {
match kind {
Stray::Malformed => {
@ -98,7 +116,7 @@ fn hub_keeps_serving_its_session(kind: Stray) -> Result<()> {
send_stray(kind, &stray, hub_addr)?;
std::thread::sleep(Duration::from_millis(20));
while hub.receive_event_once()?.is_some() {}
assert!(hub.stats().packets_dropped > 0, "strays are counted");
assert_eq!(hub.stats().packets_dropped, datagrams(kind), "every stray is counted once");
real.send("schema", b"still here".to_vec())?;
std::thread::sleep(Duration::from_millis(20));
@ -174,7 +192,7 @@ fn client_keeps_its_session(kind: Stray) -> Result<()> {
.receive_once()?
.expect("the real frame behind the strays");
assert_eq!(frame.payload, b"real");
assert!(client.stats().packets_dropped > 0, "strays are counted");
assert_eq!(client.stats().packets_dropped, datagrams(kind), "every stray is counted once");
Ok(())
}
@ -206,7 +224,8 @@ fn server_mode_keeps_its_peer(kind: Stray) -> Result<()> {
.receive_once()?
.expect("the real frame behind the strays");
assert_eq!(frame.payload, b"real");
assert!(server.stats().packets_dropped > 0, "strays are counted");
// Once from an unrelated address, once from the peer's own.
assert_eq!(server.stats().packets_dropped, 2 * datagrams(kind), "every stray is counted once");
Ok(())
}
@ -253,3 +272,111 @@ per_stray!(
server_mode_survives_malformed_frames,
server_mode_survives_foreign_connection_ids
);
#[test]
fn hub_drops_a_packet_its_admitted_peer_session_refuses() -> Result<()> {
let mut hub = CultNetRudpServerHub::new(CultNetRudpServerHubOptions::new(
"hub",
socket()?,
CONNECTION_ID,
))?;
let hub_addr = hub.local_addr()?;
let peer = socket()?;
let mut peer_session = raw_session(CONNECTION_ID);
send_to(&peer, hub_addr, &peer_session.create_connect(0, b"peer".to_vec())?)?;
assert!(matches!(
hub.receive_event_once()?,
Some(CultNetRudpServerEvent::Connected { .. })
));
let mut buffer = vec![0_u8; 65_535];
let (received, _) = peer.recv_from(&mut buffer)?;
peer_session.receive(&decode_rudp_packet(&buffer[..received])?, 0)?;
send_to(&peer, hub_addr, &poisoned(&mut peer_session)?)?;
assert!(hub.receive_event_once()?.is_none());
assert_eq!(hub.stats().packets_dropped, 1);
assert_eq!(hub.sessions().len(), 1);
send_to(
&peer,
hub_addr,
&peer_session.send("schema", b"still here".to_vec(), reliable())?,
)?;
match hub.receive_event_once()? {
Some(CultNetRudpServerEvent::Frame { frame, .. }) => {
assert_eq!(frame.payload, b"still here");
}
other => panic!("the peer must still be served, got {other:?}"),
}
Ok(())
}
#[test]
fn client_drops_a_packet_its_session_refuses() -> Result<()> {
let server = socket()?;
let server_addr = server.local_addr()?;
let mut client = CultNetRudpSocketTransportConnection::new(
CultNetRudpSocketTransportOptions::client("client", socket()?, server_addr, CONNECTION_ID),
)?;
client.connect(b"hello".to_vec())?;
let mut buffer = vec![0_u8; 65_535];
let (received, client_addr) = server.recv_from(&mut buffer)?;
let mut server_session = raw_session(CONNECTION_ID);
let accept =
server_session.accept_connect(&decode_rudp_packet(&buffer[..received])?, 0, Vec::new())?;
send_to(&server, client_addr, &accept)?;
let _ = client.receive_once()?;
send_to(&server, client_addr, &poisoned(&mut server_session)?)?;
assert!(client.receive_once()?.is_none());
assert_eq!(client.stats().packets_dropped, 1);
send_to(
&server,
client_addr,
&server_session.send("schema", b"real".to_vec(), reliable())?,
)?;
assert_eq!(client.receive_once()?.expect("the real frame").payload, b"real");
Ok(())
}
#[test]
fn server_mode_drops_what_is_not_its_peer_or_what_its_session_refuses() -> Result<()> {
let server_socket = socket()?;
let server_addr = server_socket.local_addr()?;
let mut server = CultNetRudpSocketTransportConnection::new(
CultNetRudpSocketTransportOptions::server("server", server_socket, CONNECTION_ID),
)?;
let stray = socket()?;
// Before any peer: a right-id packet that is not a Connect claims nothing.
send_stray(Stray::UnadmittedSender, &stray, server_addr)?;
assert!(server.receive_once()?.is_none());
assert_eq!(server.stats().packets_dropped, 3);
let peer = socket()?;
let mut peer_session = raw_session(CONNECTION_ID);
send_to(&peer, server_addr, &peer_session.create_connect(0, b"peer".to_vec())?)?;
let _ = server.receive_once()?;
let mut buffer = vec![0_u8; 65_535];
let (received, _) = peer.recv_from(&mut buffer)?;
peer_session.receive(&decode_rudp_packet(&buffer[..received])?, 0)?;
assert_eq!(server.stats().packets_dropped, 3);
// After the peer is known: right-id packets from any other address.
send_stray(Stray::UnadmittedSender, &stray, server_addr)?;
assert!(server.receive_once()?.is_none());
assert_eq!(server.stats().packets_dropped, 6);
// A packet from the peer that the session itself refuses.
send_to(&peer, server_addr, &poisoned(&mut peer_session)?)?;
assert!(server.receive_once()?.is_none());
assert_eq!(server.stats().packets_dropped, 7);
send_to(
&peer,
server_addr,
&peer_session.send("schema", b"real".to_vec(), reliable())?,
)?;
assert_eq!(server.receive_once()?.expect("the real frame").payload, b"real");
Ok(())
}