fix(client): reject invalid timing parameters earlier

This commit is contained in:
Nils Andreas Svee committed 2026-05-08 10:23:42 +07:00
1 parent b994fb9273
commit 2a780337f2
1 file changed
+33 -12
+33 -12
View File
@@ -585,14 +585,11 @@ impl Client {
self.recv_buffer.resize(recv_buffer_size, 0); self.recv_buffer.resize(recv_buffer_size, 0);
self.phase = ClientPhase::Open { token: reply.token }; self.phase = ClientPhase::Open { token: reply.token };
let duration_ns = negotiated.params.duration_ns; let end_mono = if negotiated.params.duration_ns > 0 {
let end_mono = if duration_ns > 0 { Some(negotiated_end_mono(
let duration = Duration::from_nanos(duration_ns as u64); now.mono,
Some( negotiated.params.duration_ns,
now.mono )?)
.checked_add(duration)
.ok_or(ClientError::DurationOverflow)?,
)
} else { } else {
None None
}; };
@@ -829,8 +826,11 @@ fn params_from_config(config: &ClientConfig) -> Result<Params, ClientError> {
validate_protocol_config(config)?; validate_protocol_config(config)?;
Ok(Params { Ok(Params {
protocol_version: PROTOCOL_VERSION, protocol_version: PROTOCOL_VERSION,
duration_ns: duration_to_ns(config.duration.unwrap_or_default())?, duration_ns: match config.duration {
interval_ns: duration_to_ns(config.interval)?, Some(duration) => config_duration_to_ns("duration", duration)?,
None => 0,
},
interval_ns: config_duration_to_ns("interval", config.interval)?,
length: i64::from(config.length), length: i64::from(config.length),
received_stats: config.received_stats, received_stats: config.received_stats,
stamp_at: config.stamp_at, stamp_at: config.stamp_at,
@@ -841,6 +841,16 @@ fn params_from_config(config: &ClientConfig) -> Result<Params, ClientError> {
} }
fn validate_protocol_config(config: &ClientConfig) -> Result<(), ClientError> { fn validate_protocol_config(config: &ClientConfig) -> Result<(), ClientError> {
if config.duration == Some(Duration::ZERO) {
return Err(ClientError::InvalidConfig {
reason: "duration must be greater than zero; use None for continuous mode".to_owned(),
});
}
if config.interval == Duration::ZERO {
return Err(ClientError::InvalidConfig {
reason: "interval must be greater than zero".to_owned(),
});
}
if config.dscp > MAX_DSCP_CODEPOINT { if config.dscp > MAX_DSCP_CODEPOINT {
return Err(ClientError::InvalidConfig { return Err(ClientError::InvalidConfig {
reason: format!("dscp must be <= {MAX_DSCP_CODEPOINT}"), reason: format!("dscp must be <= {MAX_DSCP_CODEPOINT}"),
@@ -869,8 +879,19 @@ fn validate_protocol_config(config: &ClientConfig) -> Result<(), ClientError> {
Ok(()) Ok(())
} }
fn duration_to_ns(duration: Duration) -> Result<i64, ClientError> { fn config_duration_to_ns(field: &str, duration: Duration) -> Result<i64, ClientError> {
i64::try_from(duration.as_nanos()).map_err(|_| ClientError::DurationOverflow) i64::try_from(duration.as_nanos()).map_err(|_| ClientError::InvalidConfig {
reason: format!("{field} is too large to encode as nanoseconds"),
})
}
fn negotiated_end_mono(start: Instant, duration_ns: i64) -> Result<Instant, ClientError> {
debug_assert!(duration_ns > 0);
start
.checked_add(Duration::from_nanos(duration_ns as u64))
.ok_or_else(|| ClientError::NegotiationRejected {
reason: "duration is too large to schedule".to_owned(),
})
} }
#[cfg(test)] #[cfg(test)]