diff --git a/crates/irtt-client/src/config.rs b/crates/irtt-client/src/config.rs index 0db956e..d53254f 100644 --- a/crates/irtt-client/src/config.rs +++ b/crates/irtt-client/src/config.rs @@ -1,12 +1,12 @@ use std::{net::SocketAddr, time::Duration}; +pub use irtt_proto::MAX_SERVER_FILL_BYTES; use irtt_proto::{Clock, ReceivedStats, StampAt}; pub(crate) const DEFAULT_PORT: u16 = 2112; pub const MAX_DSCP_CODEPOINT: u8 = 63; pub const MAX_TTL: u32 = 255; pub const MAX_UDP_PAYLOAD_LENGTH: u32 = 65_507; -pub const MAX_SERVER_FILL_BYTES: usize = 32; pub(crate) const DEFAULT_DURATION: Duration = Duration::from_secs(3); pub(crate) const DEFAULT_INTERVAL: Duration = Duration::from_secs(1); pub(crate) const DEFAULT_OPEN_TIMEOUTS: [Duration; 4] = [ diff --git a/crates/irtt-proto/src/lib.rs b/crates/irtt-proto/src/lib.rs index 36311a9..06a352b 100644 --- a/crates/irtt-proto/src/lib.rs +++ b/crates/irtt-proto/src/lib.rs @@ -17,7 +17,7 @@ pub use flags::*; pub use hmac::{compute_hmac, compute_hmac_in_place, verify_hmac}; pub use layout::{echo_header_len, echo_packet_len, PacketLayout}; pub use open::{decode_open_reply, encode_open_request, OpenReply, OpenRequest}; -pub use params::{Clock, Params, ReceivedStats, ServerFill, StampAt}; +pub use params::{Clock, Params, ReceivedStats, ServerFill, StampAt, MAX_SERVER_FILL_BYTES}; pub const MAGIC: [u8; 3] = [0x14, 0xA7, 0x5B]; pub const PROTOCOL_VERSION: i64 = 1; diff --git a/crates/irtt-proto/src/params.rs b/crates/irtt-proto/src/params.rs index 2b58805..0bbb597 100644 --- a/crates/irtt-proto/src/params.rs +++ b/crates/irtt-proto/src/params.rs @@ -1,5 +1,7 @@ use crate::{varint, ProtoError, Result, PROTOCOL_VERSION}; +pub const MAX_SERVER_FILL_BYTES: usize = 32; + #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct Params { pub protocol_version: i64, @@ -59,6 +61,12 @@ impl Params { pos += used; let len = usize::try_from(len) .map_err(|_| ProtoError::ParameterLengthTooLarge { tag, length: len })?; + if len > MAX_SERVER_FILL_BYTES { + return Err(ProtoError::ParameterLengthTooLarge { + tag, + length: len as u64, + }); + } if input.len().saturating_sub(pos) < len { return Err(ProtoError::MalformedParams); } @@ -206,6 +214,14 @@ mod tests { assert_eq!(Params::decode(¶ms.encode()), Ok(params)); } + fn encode_server_fill_value(value: &[u8]) -> Vec { + let mut encoded = Vec::new(); + varint::encode_uvarint(9, &mut encoded); + varint::encode_uvarint(value.len() as u64, &mut encoded); + encoded.extend_from_slice(value); + encoded + } + #[test] fn params_round_trip() { let params = Params { @@ -323,6 +339,29 @@ mod tests { }); } + #[test] + fn server_fill_decode_accepts_max_length() { + let value = b"0123456789abcdef0123456789abcdef"; + let params = Params::decode(&encode_server_fill_value(value)).unwrap(); + + assert_eq!( + params.server_fill, + Some(ServerFill { + value: "0123456789abcdef0123456789abcdef".to_owned(), + }) + ); + } + + #[test] + fn server_fill_decode_rejects_oversized_length() { + let value = b"0123456789abcdef0123456789abcdefx"; + + assert_eq!( + Params::decode(&encode_server_fill_value(value)), + Err(ProtoError::ParameterLengthTooLarge { tag: 9, length: 33 }) + ); + } + #[test] fn server_fill_tag_and_length_are_encoded_before_utf8_bytes() { let params = Params {