mirror of
https://github.com/n0-computer/noq.git
synced 2026-09-22 19:24:08 +00:00
Detect stateless resets with malformed headers
A valid stateless reset packet might not be large enough to contain the expected connection ID, which can cause header decoding to fail.
This commit is contained in:
committed by
Dirkjan Ochtman
parent
1d390af2fa
commit
fbccb656cc
@@ -1865,8 +1865,14 @@ impl Connection {
|
||||
None
|
||||
};
|
||||
|
||||
let packet = partial_decode.data();
|
||||
let stateless_reset = packet.len() >= RESET_TOKEN_SIZE + 5
|
||||
&& self.peer_params.stateless_reset_token.as_deref()
|
||||
== Some(&packet[packet.len() - RESET_TOKEN_SIZE..]);
|
||||
|
||||
match partial_decode.finish(header_crypto) {
|
||||
Ok(packet) => self.handle_packet(now, remote, ecn, packet),
|
||||
Ok(packet) => self.handle_packet(now, remote, ecn, Some(packet), stateless_reset),
|
||||
Err(_) if stateless_reset => self.handle_packet(now, remote, ecn, None, true),
|
||||
Err(e) => {
|
||||
trace!("unable to complete packet decoding: {}", e);
|
||||
}
|
||||
@@ -1878,15 +1884,18 @@ impl Connection {
|
||||
now: Instant,
|
||||
remote: SocketAddr,
|
||||
ecn: Option<EcnCodepoint>,
|
||||
mut packet: Packet,
|
||||
packet: Option<Packet>,
|
||||
stateless_reset: bool,
|
||||
) {
|
||||
trace!(
|
||||
"got {:?} packet ({} bytes) from {} using id {}",
|
||||
packet.header.space(),
|
||||
packet.payload.len() + packet.header_data.len(),
|
||||
remote,
|
||||
packet.header.dst_cid(),
|
||||
);
|
||||
if let Some(ref packet) = packet {
|
||||
trace!(
|
||||
"got {:?} packet ({} bytes) from {} using id {}",
|
||||
packet.header.space(),
|
||||
packet.payload.len() + packet.header_data.len(),
|
||||
remote,
|
||||
packet.header.dst_cid(),
|
||||
);
|
||||
}
|
||||
|
||||
if self.is_handshaking() && remote != self.path.remote {
|
||||
debug!("discarding packet with unexpected remote during handshake");
|
||||
@@ -1895,15 +1904,14 @@ impl Connection {
|
||||
|
||||
let was_closed = self.state.is_closed();
|
||||
let was_drained = self.state.is_drained();
|
||||
let stateless_reset = self
|
||||
.peer_params
|
||||
.stateless_reset_token
|
||||
.map_or(false, |token| {
|
||||
packet.payload.len() >= RESET_TOKEN_SIZE
|
||||
&& packet.payload[packet.payload.len() - RESET_TOKEN_SIZE..] == token[..]
|
||||
});
|
||||
|
||||
let result = match self.decrypt_packet(now, &mut packet) {
|
||||
let decrypted = match packet {
|
||||
None => Err(None),
|
||||
Some(mut packet) => self
|
||||
.decrypt_packet(now, &mut packet)
|
||||
.map(move |number| (packet, number)),
|
||||
};
|
||||
let result = match decrypted {
|
||||
Err(Some(e)) => {
|
||||
warn!("illegal packet: {}", e);
|
||||
Err(e.into())
|
||||
@@ -1929,7 +1937,7 @@ impl Connection {
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(number) => {
|
||||
Ok((packet, number)) => {
|
||||
let span = match number {
|
||||
Some(pn) => trace_span!("recv", space = ?packet.header.space(), pn),
|
||||
None => trace_span!("recv", space = ?packet.header.space()),
|
||||
|
||||
@@ -175,13 +175,10 @@ fn server_stateless_reset() {
|
||||
|
||||
let mut pair = Pair::new(endpoint_config.clone(), server_config());
|
||||
let (client_ch, _) = pair.connect();
|
||||
pair.drive(); // Flush any post-handshake frames
|
||||
pair.server.endpoint = Endpoint::new(endpoint_config, Some(Arc::new(server_config())));
|
||||
// Send something big enough to allow room for a smaller stateless reset.
|
||||
pair.client.connections.get_mut(&client_ch).unwrap().close(
|
||||
pair.time,
|
||||
VarInt(42),
|
||||
(&[0xab; 128][..]).into(),
|
||||
);
|
||||
// Force the server to generate the smallest possible stateless reset
|
||||
pair.client.connections.get_mut(&client_ch).unwrap().ping();
|
||||
info!("resetting");
|
||||
pair.drive();
|
||||
assert_matches!(
|
||||
|
||||
Reference in New Issue
Block a user