use std::{
convert::TryFrom,
net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6},
};
use bytes::{Buf, BufMut};
use rand::{Rng as _, RngCore, seq::SliceRandom as _};
use thiserror::Error;
use crate::{
LOC_CID_COUNT, MAX_CID_SIZE, MAX_STREAM_COUNT, RESET_TOKEN_SIZE, ResetToken, Side,
TIMER_GRANULARITY, TransportError, TransportErrorCode, VarInt,
cid_generator::ConnectionIdGenerator,
cid_queue::CidQueue,
coding::{BufExt, BufMutExt, UnexpectedEnd},
config::{EndpointConfig, ServerConfig, TransportConfig},
shared::ConnectionId,
};
mod error_handling;
#[cfg(test)]
mod error_tests;
#[cfg(test)]
mod integration_tests;
use error_handling::*;
macro_rules! apply_params {
($macro:ident) => {
$macro! {
max_idle_timeout(MaxIdleTimeout) = 0,
max_udp_payload_size(MaxUdpPayloadSize) = 65527,
initial_max_data(InitialMaxData) = 0,
initial_max_stream_data_bidi_local(InitialMaxStreamDataBidiLocal) = 0,
initial_max_stream_data_bidi_remote(InitialMaxStreamDataBidiRemote) = 0,
initial_max_stream_data_uni(InitialMaxStreamDataUni) = 0,
initial_max_streams_bidi(InitialMaxStreamsBidi) = 0,
initial_max_streams_uni(InitialMaxStreamsUni) = 0,
ack_delay_exponent(AckDelayExponent) = 3,
max_ack_delay(MaxAckDelay) = 25,
active_connection_id_limit(ActiveConnectionIdLimit) = 2,
}
};
}
macro_rules! make_struct {
{$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr_2021,)*} => {
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TransportParameters {
$($(#[$doc])* pub(crate) $name : VarInt,)*
pub(crate) disable_active_migration: bool,
pub(crate) max_datagram_frame_size: Option<VarInt>,
pub(crate) initial_src_cid: Option<ConnectionId>,
pub(crate) grease_quic_bit: bool,
pub(crate) min_ack_delay: Option<VarInt>,
pub(crate) nat_traversal: Option<NatTraversalConfig>,
pub(crate) rfc_nat_traversal: bool,
pub(crate) address_discovery: Option<AddressDiscoveryConfig>,
pub(crate) ack_receive_v1: bool,
pub(crate) pqc_algorithms: Option<PqcAlgorithms>,
pub(crate) original_dst_cid: Option<ConnectionId>,
pub(crate) retry_src_cid: Option<ConnectionId>,
pub(crate) stateless_reset_token: Option<ResetToken>,
pub(crate) preferred_address: Option<PreferredAddress>,
pub(crate) grease_transport_parameter: Option<ReservedTransportParameter>,
pub(crate) write_order: Option<[u8; TransportParameterId::SUPPORTED.len()]>,
}
impl TransportParameters {
pub(crate) fn default() -> Self {
Self {
$($name: VarInt::from_u32($default),)*
disable_active_migration: false,
max_datagram_frame_size: None,
initial_src_cid: None,
grease_quic_bit: false,
min_ack_delay: None,
nat_traversal: None,
rfc_nat_traversal: false,
address_discovery: None,
ack_receive_v1: false,
pqc_algorithms: None,
original_dst_cid: None,
retry_src_cid: None,
stateless_reset_token: None,
preferred_address: None,
grease_transport_parameter: None,
write_order: None,
}
}
}
}
}
apply_params!(make_struct);
impl TransportParameters {
pub(crate) fn new(
config: &TransportConfig,
endpoint_config: &EndpointConfig,
cid_gen: &dyn ConnectionIdGenerator,
initial_src_cid: ConnectionId,
server_config: Option<&ServerConfig>,
rng: &mut impl RngCore,
) -> Result<Self, TransportError> {
Ok(Self {
initial_src_cid: Some(initial_src_cid),
initial_max_streams_bidi: config.max_concurrent_bidi_streams,
initial_max_streams_uni: config.max_concurrent_uni_streams,
initial_max_data: config.receive_window,
initial_max_stream_data_bidi_local: config.stream_receive_window,
initial_max_stream_data_bidi_remote: config.stream_receive_window,
initial_max_stream_data_uni: config.stream_receive_window,
max_udp_payload_size: endpoint_config.max_udp_payload_size,
max_idle_timeout: config.max_idle_timeout.unwrap_or(VarInt(0)),
disable_active_migration: server_config.is_some_and(|c| !c.migration),
active_connection_id_limit: if cid_gen.cid_len() == 0 {
2 } else {
CidQueue::LEN as u32
}
.into(),
max_datagram_frame_size: config
.datagram_receive_buffer_size
.map(|x| (x.min(u16::MAX.into()) as u16).into()),
grease_quic_bit: endpoint_config.grease_quic_bit,
min_ack_delay: Some({
let micros = TIMER_GRANULARITY.as_micros();
let micros_u64 = u64::try_from(micros).unwrap_or_else(|_| {
tracing::error!("Timer granularity {} micros exceeds u64::MAX", micros);
1_000_000 });
VarInt::from_u64_bounded(micros_u64)
}),
grease_transport_parameter: Some(ReservedTransportParameter::random(rng)?),
write_order: Some({
let mut order = std::array::from_fn(|i| i as u8);
order.shuffle(rng);
order
}),
nat_traversal: config.nat_traversal_config.clone(),
rfc_nat_traversal: config.nat_traversal_config.is_some(), address_discovery: config.address_discovery_config,
ack_receive_v1: true,
pqc_algorithms: config.pqc_algorithms.clone(),
..Self::default()
})
}
pub(crate) fn validate_resumption_from(&self, cached: &Self) -> Result<(), TransportError> {
if cached.active_connection_id_limit > self.active_connection_id_limit
|| cached.initial_max_data > self.initial_max_data
|| cached.initial_max_stream_data_bidi_local > self.initial_max_stream_data_bidi_local
|| cached.initial_max_stream_data_bidi_remote > self.initial_max_stream_data_bidi_remote
|| cached.initial_max_stream_data_uni > self.initial_max_stream_data_uni
|| cached.initial_max_streams_bidi > self.initial_max_streams_bidi
|| cached.initial_max_streams_uni > self.initial_max_streams_uni
|| cached.max_datagram_frame_size > self.max_datagram_frame_size
|| cached.grease_quic_bit && !self.grease_quic_bit
|| cached.ack_receive_v1 && !self.ack_receive_v1
{
return Err(TransportError::PROTOCOL_VIOLATION(
"0-RTT accepted with incompatible transport parameters",
));
}
Ok(())
}
pub(crate) fn issue_cids_limit(&self) -> u64 {
self.active_connection_id_limit.0.min(LOC_CID_COUNT)
}
pub fn nat_traversal_config(&self) -> Option<&NatTraversalConfig> {
self.nat_traversal.as_ref()
}
pub fn supports_rfc_nat_traversal(&self) -> bool {
self.rfc_nat_traversal
}
pub fn pqc_algorithms(&self) -> Option<&PqcAlgorithms> {
self.pqc_algorithms.as_ref()
}
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub enum NatTraversalConfig {
#[default]
ClientSupport,
ServerSupport {
concurrency_limit: VarInt,
},
}
impl NatTraversalConfig {
pub fn client() -> Self {
Self::ClientSupport
}
pub fn server(concurrency_limit: VarInt) -> Result<Self, TransportError> {
if concurrency_limit.0 == 0 {
return Err(TransportError::TRANSPORT_PARAMETER_ERROR(
"concurrency_limit must be greater than 0",
));
}
if concurrency_limit.0 > 100 {
return Err(TransportError::TRANSPORT_PARAMETER_ERROR(
"concurrency_limit must not exceed 100",
));
}
Ok(Self::ServerSupport { concurrency_limit })
}
pub fn concurrency_limit(&self) -> Option<VarInt> {
match self {
Self::ClientSupport => None,
Self::ServerSupport { concurrency_limit } => Some(*concurrency_limit),
}
}
pub fn is_client(&self) -> bool {
matches!(self, Self::ClientSupport)
}
pub fn is_server(&self) -> bool {
matches!(self, Self::ServerSupport { .. })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum AddressDiscoveryConfig {
SendOnly,
ReceiveOnly,
#[default]
SendAndReceive,
}
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct PqcAlgorithms {
pub ml_kem_768: bool,
pub ml_dsa_65: bool,
}
impl AddressDiscoveryConfig {
pub fn to_value(&self) -> VarInt {
match self {
Self::SendOnly => VarInt::from_u32(0),
Self::ReceiveOnly => VarInt::from_u32(1),
Self::SendAndReceive => VarInt::from_u32(2),
}
}
pub fn from_value(value: VarInt) -> Result<Self, Error> {
match value.into_inner() {
0 => Ok(Self::SendOnly),
1 => Ok(Self::ReceiveOnly),
2 => Ok(Self::SendAndReceive),
_ => Err(Error::Malformed),
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) struct PreferredAddress {
pub(crate) address_v4: Option<SocketAddrV4>,
pub(crate) address_v6: Option<SocketAddrV6>,
pub(crate) connection_id: ConnectionId,
pub(crate) stateless_reset_token: ResetToken,
}
impl PreferredAddress {
fn wire_size(&self) -> u16 {
4 + 2 + 16 + 2 + 1 + self.connection_id.len() as u16 + 16
}
fn write<W: BufMut>(&self, w: &mut W) {
w.write(self.address_v4.map_or(Ipv4Addr::UNSPECIFIED, |x| *x.ip()));
w.write::<u16>(self.address_v4.map_or(0, |x| x.port()));
w.write(self.address_v6.map_or(Ipv6Addr::UNSPECIFIED, |x| *x.ip()));
w.write::<u16>(self.address_v6.map_or(0, |x| x.port()));
w.write::<u8>(self.connection_id.len() as u8);
w.put_slice(&self.connection_id);
w.put_slice(&self.stateless_reset_token);
}
fn read<R: Buf>(r: &mut R) -> Result<Self, Error> {
let ip_v4 = r.get::<Ipv4Addr>()?;
let port_v4 = r.get::<u16>()?;
let ip_v6 = r.get::<Ipv6Addr>()?;
let port_v6 = r.get::<u16>()?;
let cid_len = r.get::<u8>()?;
if r.remaining() < cid_len as usize || cid_len > MAX_CID_SIZE as u8 {
return Err(Error::Malformed);
}
let mut stage = [0; MAX_CID_SIZE];
r.copy_to_slice(&mut stage[0..cid_len as usize]);
let cid = ConnectionId::new(&stage[0..cid_len as usize]);
if r.remaining() < 16 {
return Err(Error::Malformed);
}
let mut token = [0; RESET_TOKEN_SIZE];
r.copy_to_slice(&mut token);
let address_v4 = if ip_v4.is_unspecified() && port_v4 == 0 {
None
} else {
Some(SocketAddrV4::new(ip_v4, port_v4))
};
let address_v6 = if ip_v6.is_unspecified() && port_v6 == 0 {
None
} else {
Some(SocketAddrV6::new(ip_v6, port_v6, 0, 0))
};
if address_v4.is_none() && address_v6.is_none() {
return Err(Error::IllegalValue);
}
Ok(Self {
address_v4,
address_v6,
connection_id: cid,
stateless_reset_token: token.into(),
})
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Error)]
pub enum Error {
#[error("parameter had illegal value")]
IllegalValue,
#[error("parameters were malformed")]
Malformed,
#[error("internal transport parameter encoding error")]
Internal,
}
impl From<Error> for TransportError {
fn from(e: Error) -> Self {
match e {
Error::IllegalValue => Self::TRANSPORT_PARAMETER_ERROR("illegal value"),
Error::Malformed => Self::TRANSPORT_PARAMETER_ERROR("malformed"),
Error::Internal => Self::INTERNAL_ERROR("transport parameter encoding failed"),
}
}
}
impl From<UnexpectedEnd> for Error {
fn from(_: UnexpectedEnd) -> Self {
Self::Malformed
}
}
impl TransportParameters {
pub fn write<W: BufMut>(&self, w: &mut W) -> Result<(), Error> {
macro_rules! write_var {
($value:expr) => {
w.write_var($value).map_err(|_| Error::Internal)?
};
}
for idx in self
.write_order
.as_ref()
.unwrap_or(&std::array::from_fn(|i| i as u8))
{
let index = *idx as usize;
let Some(&id) = TransportParameterId::SUPPORTED.get(index) else {
return Err(Error::Internal);
};
match id {
TransportParameterId::ReservedTransportParameter => {
if let Some(param) = self.grease_transport_parameter {
param.write(w)?;
}
}
TransportParameterId::StatelessResetToken => {
if let Some(ref x) = self.stateless_reset_token {
write_var!(id as u64);
write_var!(16);
w.put_slice(x);
}
}
TransportParameterId::DisableActiveMigration => {
if self.disable_active_migration {
write_var!(id as u64);
write_var!(0);
}
}
TransportParameterId::MaxDatagramFrameSize => {
if let Some(x) = self.max_datagram_frame_size {
write_var!(id as u64);
write_var!(x.size() as u64);
w.write(x);
}
}
TransportParameterId::PreferredAddress => {
if let Some(ref x) = self.preferred_address {
write_var!(id as u64);
write_var!(x.wire_size() as u64);
x.write(w);
}
}
TransportParameterId::OriginalDestinationConnectionId => {
if let Some(ref cid) = self.original_dst_cid {
write_var!(id as u64);
write_var!(cid.len() as u64);
w.put_slice(cid);
}
}
TransportParameterId::InitialSourceConnectionId => {
if let Some(ref cid) = self.initial_src_cid {
write_var!(id as u64);
write_var!(cid.len() as u64);
w.put_slice(cid);
}
}
TransportParameterId::RetrySourceConnectionId => {
if let Some(ref cid) = self.retry_src_cid {
write_var!(id as u64);
write_var!(cid.len() as u64);
w.put_slice(cid);
}
}
TransportParameterId::GreaseQuicBit => {
if self.grease_quic_bit {
write_var!(id as u64);
write_var!(0);
}
}
TransportParameterId::MinAckDelayDraft07 => {
if let Some(x) = self.min_ack_delay {
write_var!(id as u64);
write_var!(x.size() as u64);
w.write(x);
}
}
TransportParameterId::NatTraversal => {
if let Some(ref config) = self.nat_traversal {
match config {
NatTraversalConfig::ClientSupport => {
write_var!(id as u64);
write_var!(0); }
NatTraversalConfig::ServerSupport { concurrency_limit } => {
write_var!(id as u64);
write_var!(concurrency_limit.size() as u64);
write_var!(concurrency_limit.0);
}
}
}
}
TransportParameterId::AddressDiscovery => {
if let Some(ref config) = self.address_discovery {
write_var!(id as u64);
let value = config.to_value();
write_var!(value.size() as u64);
write_var!(value.into_inner());
}
}
TransportParameterId::RfcNatTraversal => {
if self.rfc_nat_traversal {
write_var!(id as u64);
write_var!(0); }
}
TransportParameterId::AckReceiveV1 => {
if self.ack_receive_v1 {
write_var!(id as u64);
write_var!(0);
}
}
TransportParameterId::PqcAlgorithms => {
if let Some(ref algorithms) = self.pqc_algorithms {
write_var!(id as u64);
let mut value = 0u8;
if algorithms.ml_kem_768 {
value |= 1 << 0;
}
if algorithms.ml_dsa_65 {
value |= 1 << 1;
}
write_var!(1u64); w.write(value);
}
}
id => {
macro_rules! write_params {
{$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr_2021,)*} => {
match id {
$(TransportParameterId::$id => {
if self.$name.0 != $default {
write_var!(id as u64);
let size = VarInt::try_from(self.$name.size())
.map_err(|_| Error::Internal)?;
write_var!(size.into_inner());
w.write(self.$name);
}
})*,
_ => {
return Err(Error::Internal);
}
}
}
}
apply_params!(write_params);
}
}
}
Ok(())
}
pub fn read<R: Buf>(side: Side, r: &mut R) -> Result<Self, Error> {
let mut params = Self::default();
macro_rules! param_state {
{$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr_2021,)*} => {{
struct ParamState {
$($name: bool,)*
}
ParamState {
$($name: false,)*
}
}}
}
let mut got = apply_params!(param_state);
while r.has_remaining() {
let id = r.get_var()?;
let len = r.get_var()?;
if (r.remaining() as u64) < len {
return Err(Error::Malformed);
}
let len = len as usize;
let Ok(id) = TransportParameterId::try_from(id) else {
r.advance(len);
continue;
};
match id {
TransportParameterId::OriginalDestinationConnectionId => {
decode_cid(len, &mut params.original_dst_cid, r)?
}
TransportParameterId::StatelessResetToken => {
if len != 16 || params.stateless_reset_token.is_some() {
return Err(Error::Malformed);
}
let mut tok = [0; RESET_TOKEN_SIZE];
r.copy_to_slice(&mut tok);
params.stateless_reset_token = Some(tok.into());
}
TransportParameterId::DisableActiveMigration => {
if len != 0 || params.disable_active_migration {
return Err(Error::Malformed);
}
params.disable_active_migration = true;
}
TransportParameterId::PreferredAddress => {
if params.preferred_address.is_some() {
return Err(Error::Malformed);
}
params.preferred_address = Some(PreferredAddress::read(&mut r.take(len))?);
}
TransportParameterId::InitialSourceConnectionId => {
decode_cid(len, &mut params.initial_src_cid, r)?
}
TransportParameterId::RetrySourceConnectionId => {
decode_cid(len, &mut params.retry_src_cid, r)?
}
TransportParameterId::MaxDatagramFrameSize => {
if len > 8 || params.max_datagram_frame_size.is_some() {
return Err(Error::Malformed);
}
params.max_datagram_frame_size = Some(r.get().map_err(|_| Error::Malformed)?);
}
TransportParameterId::GreaseQuicBit => match len {
0 => params.grease_quic_bit = true,
_ => return Err(Error::Malformed),
},
TransportParameterId::MinAckDelayDraft07 => {
params.min_ack_delay = Some(r.get().map_err(|_| Error::Malformed)?)
}
TransportParameterId::NatTraversal => {
if params.nat_traversal.is_some() {
return Err(Error::Malformed);
}
match len {
0 => {
params.nat_traversal = Some(NatTraversalConfig::ClientSupport);
}
_ if len > 0 => {
let limit = r.get_var()?;
if limit == 0 {
return Err(Error::IllegalValue);
}
params.nat_traversal = Some(NatTraversalConfig::ServerSupport {
concurrency_limit: VarInt::from_u64(limit)
.map_err(|_| Error::IllegalValue)?,
});
}
_ => {
return Err(Error::IllegalValue);
}
}
}
TransportParameterId::AddressDiscovery => {
if params.address_discovery.is_some() {
return Err(Error::Malformed);
}
let value = r.get_var()?;
let varint = VarInt::from_u64(value).map_err(|_| Error::Malformed)?;
params.address_discovery = Some(AddressDiscoveryConfig::from_value(varint)?);
}
TransportParameterId::RfcNatTraversal => {
if params.rfc_nat_traversal {
return Err(Error::Malformed);
}
if len != 0 {
return Err(Error::Malformed);
}
params.rfc_nat_traversal = true;
}
TransportParameterId::AckReceiveV1 => {
if params.ack_receive_v1 || len != 0 {
return Err(Error::Malformed);
}
params.ack_receive_v1 = true;
}
TransportParameterId::PqcAlgorithms => {
if params.pqc_algorithms.is_some() {
return Err(Error::Malformed);
}
if len != 1 {
return Err(Error::Malformed);
}
let value = r.get::<u8>()?;
params.pqc_algorithms = Some(PqcAlgorithms {
ml_kem_768: (value & (1 << 0)) != 0,
ml_dsa_65: (value & (1 << 1)) != 0,
});
}
_ => {
macro_rules! parse {
{$($(#[$doc:meta])* $name:ident ($id:ident) = $default:expr_2021,)*} => {
match id {
$(TransportParameterId::$id => {
let value = r.get::<VarInt>()?;
if len != value.size() || got.$name { return Err(Error::Malformed); }
params.$name = value.into();
got.$name = true;
})*
_ => r.advance(len),
}
}
}
apply_params!(parse);
}
}
}
validate_ack_delay_exponent(params.ack_delay_exponent.0 as u8)
.map_err(|_| Error::IllegalValue)?;
validate_max_ack_delay(params.max_ack_delay).map_err(|_| Error::IllegalValue)?;
validate_active_connection_id_limit(params.active_connection_id_limit)
.map_err(|_| Error::IllegalValue)?;
validate_max_udp_payload_size(params.max_udp_payload_size)
.map_err(|_| Error::IllegalValue)?;
if params.initial_max_streams_bidi.0 > MAX_STREAM_COUNT {
TransportParameterErrorHandler::log_validation_failure(
"initial_max_streams_bidi",
params.initial_max_streams_bidi.0,
&format!("must be <= {MAX_STREAM_COUNT}"),
"RFC 9000 Section 4.6-2",
);
return Err(Error::IllegalValue);
}
if params.initial_max_streams_uni.0 > MAX_STREAM_COUNT {
TransportParameterErrorHandler::log_validation_failure(
"initial_max_streams_uni",
params.initial_max_streams_uni.0,
&format!("must be <= {MAX_STREAM_COUNT}"),
"RFC 9000 Section 4.6-2",
);
return Err(Error::IllegalValue);
}
validate_min_ack_delay(params.min_ack_delay, params.max_ack_delay)
.map_err(|_| Error::IllegalValue)?;
validate_server_only_params(side, ¶ms).map_err(|_| Error::IllegalValue)?;
if let Some(ref pref_addr) = params.preferred_address {
if pref_addr.connection_id.is_empty() {
TransportParameterErrorHandler::log_semantic_error(
"preferred_address with empty connection_id",
"RFC 9000 Section 18.2-4.38.1",
);
return Err(Error::IllegalValue);
}
}
if let Some(ref nat_config) = params.nat_traversal {
match (side, nat_config) {
(Side::Server, NatTraversalConfig::ClientSupport) => {
tracing::debug!("Server received valid ClientSupport NAT traversal parameter");
}
(Side::Client, NatTraversalConfig::ServerSupport { concurrency_limit }) => {
tracing::debug!(
"Client received valid ServerSupport with concurrency_limit: {}",
concurrency_limit
);
}
(Side::Server, NatTraversalConfig::ServerSupport { concurrency_limit }) => {
tracing::debug!(
"P2P: Server received ServerSupport with concurrency_limit: {} (symmetric P2P)",
concurrency_limit
);
if concurrency_limit.0 == 0 || concurrency_limit.0 > 100 {
TransportParameterErrorHandler::log_validation_failure(
"nat_traversal_concurrency_limit",
concurrency_limit.0,
"1-100",
"draft-seemann-quic-nat-traversal-02",
);
return Err(Error::IllegalValue);
}
}
(Side::Client, NatTraversalConfig::ClientSupport) => {
tracing::debug!("P2P: Client received ClientSupport (symmetric P2P)");
}
}
}
Ok(params)
}
pub fn negotiated_nat_concurrency_limit(
&self,
local_config: &NatTraversalConfig,
) -> Option<u64> {
match (&self.nat_traversal, local_config) {
(
Some(NatTraversalConfig::ServerSupport {
concurrency_limit: remote,
}),
NatTraversalConfig::ServerSupport {
concurrency_limit: local,
},
) => Some(local.0.min(remote.0)),
(
Some(NatTraversalConfig::ServerSupport { concurrency_limit }),
NatTraversalConfig::ClientSupport,
)
| (
Some(NatTraversalConfig::ClientSupport),
NatTraversalConfig::ServerSupport { concurrency_limit },
) => Some(concurrency_limit.0),
_ => None,
}
}
pub fn supports_bidirectional_nat_traversal(&self) -> bool {
matches!(
&self.nat_traversal,
Some(NatTraversalConfig::ServerSupport { .. })
)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub(crate) struct ReservedTransportParameter {
id: VarInt,
payload: [u8; Self::MAX_PAYLOAD_LEN],
payload_len: usize,
}
impl ReservedTransportParameter {
fn random(rng: &mut impl RngCore) -> Result<Self, TransportError> {
let id = Self::generate_reserved_id(rng)?;
let payload_len = rng.gen_range(0..Self::MAX_PAYLOAD_LEN);
let payload = {
let mut slice = [0u8; Self::MAX_PAYLOAD_LEN];
rng.fill_bytes(&mut slice[..payload_len]);
slice
};
Ok(Self {
id,
payload,
payload_len,
})
}
fn write(&self, w: &mut impl BufMut) -> Result<(), Error> {
w.write_var(self.id.0).map_err(|_| Error::Internal)?;
w.write_var(self.payload_len as u64)
.map_err(|_| Error::Internal)?;
w.put_slice(&self.payload[..self.payload_len]);
Ok(())
}
fn generate_reserved_id(rng: &mut impl RngCore) -> Result<VarInt, TransportError> {
let id = {
let rand = rng.gen_range(0u64..(1 << 62) - 27);
let n = rand / 31;
31 * n + 27
};
debug_assert!(
id % 31 == 27,
"generated id does not have the form of 31 * N + 27"
);
VarInt::from_u64(id).map_err(|_| TransportError {
code: TransportErrorCode::INTERNAL_ERROR,
frame: None,
reason: "generated id does not fit into range of allowed transport parameter IDs"
.to_string(),
})
}
const MAX_PAYLOAD_LEN: usize = 16;
}
#[repr(u64)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum TransportParameterId {
OriginalDestinationConnectionId = 0x00,
MaxIdleTimeout = 0x01,
StatelessResetToken = 0x02,
MaxUdpPayloadSize = 0x03,
InitialMaxData = 0x04,
InitialMaxStreamDataBidiLocal = 0x05,
InitialMaxStreamDataBidiRemote = 0x06,
InitialMaxStreamDataUni = 0x07,
InitialMaxStreamsBidi = 0x08,
InitialMaxStreamsUni = 0x09,
AckDelayExponent = 0x0A,
MaxAckDelay = 0x0B,
DisableActiveMigration = 0x0C,
PreferredAddress = 0x0D,
ActiveConnectionIdLimit = 0x0E,
InitialSourceConnectionId = 0x0F,
RetrySourceConnectionId = 0x10,
ReservedTransportParameter = 0x1B,
MaxDatagramFrameSize = 0x20,
GreaseQuicBit = 0x2AB2,
MinAckDelayDraft07 = 0xFF04DE1B,
NatTraversal = 0x3d7e9f0bca12fea6,
RfcNatTraversal = 0x3d7e9f0bca12fea8,
AddressDiscovery = 0x9f81a176,
PqcAlgorithms = 0x50C0,
AckReceiveV1 = 0x50C1,
}
impl TransportParameterId {
const SUPPORTED: [Self; 26] = [
Self::MaxIdleTimeout,
Self::MaxUdpPayloadSize,
Self::InitialMaxData,
Self::InitialMaxStreamDataBidiLocal,
Self::InitialMaxStreamDataBidiRemote,
Self::InitialMaxStreamDataUni,
Self::InitialMaxStreamsBidi,
Self::InitialMaxStreamsUni,
Self::AckDelayExponent,
Self::MaxAckDelay,
Self::ActiveConnectionIdLimit,
Self::ReservedTransportParameter,
Self::StatelessResetToken,
Self::DisableActiveMigration,
Self::MaxDatagramFrameSize,
Self::PreferredAddress,
Self::OriginalDestinationConnectionId,
Self::InitialSourceConnectionId,
Self::RetrySourceConnectionId,
Self::GreaseQuicBit,
Self::MinAckDelayDraft07,
Self::NatTraversal,
Self::RfcNatTraversal,
Self::AddressDiscovery,
Self::PqcAlgorithms,
Self::AckReceiveV1,
];
}
impl std::cmp::PartialEq<u64> for TransportParameterId {
fn eq(&self, other: &u64) -> bool {
*other == (*self as u64)
}
}
impl TryFrom<u64> for TransportParameterId {
type Error = ();
fn try_from(value: u64) -> Result<Self, Self::Error> {
let param = match value {
id if Self::MaxIdleTimeout == id => Self::MaxIdleTimeout,
id if Self::MaxUdpPayloadSize == id => Self::MaxUdpPayloadSize,
id if Self::InitialMaxData == id => Self::InitialMaxData,
id if Self::InitialMaxStreamDataBidiLocal == id => Self::InitialMaxStreamDataBidiLocal,
id if Self::InitialMaxStreamDataBidiRemote == id => {
Self::InitialMaxStreamDataBidiRemote
}
id if Self::InitialMaxStreamDataUni == id => Self::InitialMaxStreamDataUni,
id if Self::InitialMaxStreamsBidi == id => Self::InitialMaxStreamsBidi,
id if Self::InitialMaxStreamsUni == id => Self::InitialMaxStreamsUni,
id if Self::AckDelayExponent == id => Self::AckDelayExponent,
id if Self::MaxAckDelay == id => Self::MaxAckDelay,
id if Self::ActiveConnectionIdLimit == id => Self::ActiveConnectionIdLimit,
id if Self::ReservedTransportParameter == id => Self::ReservedTransportParameter,
id if Self::StatelessResetToken == id => Self::StatelessResetToken,
id if Self::DisableActiveMigration == id => Self::DisableActiveMigration,
id if Self::MaxDatagramFrameSize == id => Self::MaxDatagramFrameSize,
id if Self::PreferredAddress == id => Self::PreferredAddress,
id if Self::OriginalDestinationConnectionId == id => {
Self::OriginalDestinationConnectionId
}
id if Self::InitialSourceConnectionId == id => Self::InitialSourceConnectionId,
id if Self::RetrySourceConnectionId == id => Self::RetrySourceConnectionId,
id if Self::GreaseQuicBit == id => Self::GreaseQuicBit,
id if Self::MinAckDelayDraft07 == id => Self::MinAckDelayDraft07,
id if Self::NatTraversal == id => Self::NatTraversal,
id if Self::RfcNatTraversal == id => Self::RfcNatTraversal,
id if Self::AddressDiscovery == id => Self::AddressDiscovery,
id if Self::PqcAlgorithms == id => Self::PqcAlgorithms,
id if Self::AckReceiveV1 == id => Self::AckReceiveV1,
_ => return Err(()),
};
Ok(param)
}
}
fn decode_cid(len: usize, value: &mut Option<ConnectionId>, r: &mut impl Buf) -> Result<(), Error> {
if len > MAX_CID_SIZE || value.is_some() || r.remaining() < len {
return Err(Error::Malformed);
}
*value = Some(ConnectionId::from_buf(r, len));
Ok(())
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_nat_traversal_transport_parameter_encoding_decoding() {
let client_config = NatTraversalConfig::ClientSupport;
let mut client_params = TransportParameters::default();
client_params.nat_traversal = Some(client_config);
let mut encoded = Vec::new();
client_params.write(&mut encoded).unwrap();
let server_decoded = TransportParameters::read(Side::Server, &mut encoded.as_slice())
.expect("Failed to decode client transport parameters");
assert!(server_decoded.nat_traversal.is_some());
let server_view = server_decoded.nat_traversal.unwrap();
assert!(matches!(server_view, NatTraversalConfig::ClientSupport));
let server_config = NatTraversalConfig::ServerSupport {
concurrency_limit: VarInt::from_u32(5),
};
let mut server_params = TransportParameters::default();
server_params.nat_traversal = Some(server_config);
let mut encoded = Vec::new();
server_params.write(&mut encoded).unwrap();
let client_decoded = TransportParameters::read(Side::Client, &mut encoded.as_slice())
.expect("Failed to decode server transport parameters");
assert!(client_decoded.nat_traversal.is_some());
let client_view = client_decoded.nat_traversal.unwrap();
assert!(matches!(
client_view,
NatTraversalConfig::ServerSupport { .. }
));
assert_eq!(client_view.concurrency_limit(), Some(VarInt::from_u32(5)));
}
#[test]
fn test_nat_traversal_parameter_without_peer_id() {
let config = NatTraversalConfig::ClientSupport;
let mut params = TransportParameters::default();
params.nat_traversal = Some(config);
let mut encoded = Vec::new();
params.write(&mut encoded).unwrap();
let decoded_params = TransportParameters::read(Side::Server, &mut encoded.as_slice())
.expect("Failed to decode transport parameters");
let decoded_config = decoded_params
.nat_traversal
.expect("NAT traversal config should be present");
assert!(matches!(decoded_config, NatTraversalConfig::ClientSupport));
let server_config = NatTraversalConfig::ServerSupport {
concurrency_limit: VarInt::from_u32(4),
};
let mut server_params = TransportParameters::default();
server_params.nat_traversal = Some(server_config);
let mut server_encoded = Vec::new();
server_params.write(&mut server_encoded).unwrap();
let decoded_server_params =
TransportParameters::read(Side::Client, &mut server_encoded.as_slice())
.expect("Failed to decode server transport parameters");
let decoded_server_config = decoded_server_params
.nat_traversal
.expect("Server NAT traversal config should be present");
assert!(matches!(
decoded_server_config,
NatTraversalConfig::ServerSupport { concurrency_limit } if concurrency_limit == VarInt::from_u32(4)
));
}
#[test]
fn test_transport_parameters_without_nat_traversal() {
let mut params = TransportParameters::default();
params.nat_traversal = None;
let mut encoded = Vec::new();
params.write(&mut encoded).unwrap();
let decoded_params = TransportParameters::read(Side::Client, &mut encoded.as_slice())
.expect("Failed to decode transport parameters");
assert!(decoded_params.nat_traversal.is_none());
}
#[test]
fn test_nat_traversal_draft_compliant_encoding() {
let client_config = NatTraversalConfig::ClientSupport;
let mut client_params = TransportParameters::default();
client_params.nat_traversal = Some(client_config);
let mut encoded = Vec::new();
client_params.write(&mut encoded).unwrap();
use bytes::Buf;
let mut cursor = &encoded[..];
while cursor.has_remaining() {
let id = VarInt::from_u64(cursor.get_var().unwrap()).unwrap();
let len = VarInt::from_u64(cursor.get_var().unwrap()).unwrap();
if id.0 == 0x3d7e9f0bca12fea6 {
assert_eq!(len.0, 0, "Client should send empty value");
break;
}
cursor.advance(len.0 as usize);
}
let server_config = NatTraversalConfig::ServerSupport {
concurrency_limit: VarInt::from_u32(5),
};
let mut server_params = TransportParameters::default();
server_params.nat_traversal = Some(server_config);
let mut encoded = Vec::new();
server_params.write(&mut encoded).unwrap();
let mut cursor = &encoded[..];
while cursor.has_remaining() {
let id = VarInt::from_u64(cursor.get_var().unwrap()).unwrap();
let len = VarInt::from_u64(cursor.get_var().unwrap()).unwrap();
if id.0 == 0x3d7e9f0bca12fea6 {
assert_eq!(len.0, 1, "Server should send 1-byte value");
let limit = cursor.chunk()[0];
assert_eq!(limit, 5, "Server should send concurrency limit");
break;
}
cursor.advance(len.0 as usize);
}
}
#[test]
fn test_nat_traversal_draft_compliant_decoding() {
let mut buf = Vec::new();
buf.write_var_or_debug_assert(0x3d7e9f0bca12fea6); buf.write_var_or_debug_assert(0);
let params = TransportParameters::read(Side::Server, &mut buf.as_slice())
.expect("Failed to decode transport parameters");
let config = params
.nat_traversal
.expect("NAT traversal should be present");
assert!(matches!(config, NatTraversalConfig::ClientSupport));
let mut buf = Vec::new();
buf.write_var_or_debug_assert(0x3d7e9f0bca12fea6); buf.write_var_or_debug_assert(1); buf.put_u8(7);
let params = TransportParameters::read(Side::Client, &mut buf.as_slice())
.expect("Failed to decode transport parameters");
let config = params
.nat_traversal
.expect("NAT traversal should be present");
assert!(matches!(
config,
NatTraversalConfig::ServerSupport { concurrency_limit } if concurrency_limit == VarInt::from_u32(7)
));
let mut buf = Vec::new();
buf.write_var_or_debug_assert(0x3d7e9f0bca12fea6); buf.write_var_or_debug_assert(2); buf.put_u8(7);
buf.put_u8(8);
let result = TransportParameters::read(Side::Client, &mut buf.as_slice());
assert!(result.is_err(), "Should fail with invalid length");
}
#[test]
fn test_nat_traversal_parameter_id() {
assert_eq!(
TransportParameterId::NatTraversal as u64,
0x3d7e9f0bca12fea6
);
}
#[test]
fn test_nat_traversal_simple_encoding() {
let mut client_params = TransportParameters::default();
client_params.nat_traversal = Some(NatTraversalConfig::ClientSupport);
let mut encoded = Vec::new();
client_params.write(&mut encoded).unwrap();
let decoded = TransportParameters::read(Side::Server, &mut encoded.as_slice())
.expect("Should decode client params");
assert!(matches!(
decoded.nat_traversal,
Some(NatTraversalConfig::ClientSupport)
));
let mut server_params = TransportParameters::default();
server_params.nat_traversal = Some(NatTraversalConfig::ServerSupport {
concurrency_limit: VarInt::from_u32(10),
});
let mut encoded = Vec::new();
server_params.write(&mut encoded).unwrap();
let decoded = TransportParameters::read(Side::Client, &mut encoded.as_slice())
.expect("Should decode server params");
match decoded.nat_traversal {
Some(NatTraversalConfig::ServerSupport { concurrency_limit }) => {
assert_eq!(concurrency_limit, VarInt::from_u32(10));
}
_ => panic!("Expected ServerSupport variant"),
}
}
#[test]
fn test_nat_traversal_config_validation() {
let client_config = NatTraversalConfig::ClientSupport;
assert!(client_config.is_client());
assert_eq!(client_config.concurrency_limit(), None);
let server_config = NatTraversalConfig::server(VarInt::from_u32(5)).unwrap();
assert!(server_config.is_server());
assert_eq!(server_config.concurrency_limit(), Some(VarInt::from_u32(5)));
let result = NatTraversalConfig::server(VarInt::from_u32(0));
assert!(result.is_err());
let result = NatTraversalConfig::server(VarInt::from_u32(101));
assert!(result.is_err());
let min_server = NatTraversalConfig::server(VarInt::from_u32(1)).unwrap();
assert_eq!(min_server.concurrency_limit(), Some(VarInt::from_u32(1)));
let max_server = NatTraversalConfig::server(VarInt::from_u32(100)).unwrap();
assert_eq!(max_server.concurrency_limit(), Some(VarInt::from_u32(100)));
}
#[test]
fn test_nat_traversal_role_validation() {
let mut buf = Vec::new();
buf.write_var_or_debug_assert(0x3d7e9f0bca12fea6); buf.write_var_or_debug_assert(0);
let result = TransportParameters::read(Side::Client, &mut buf.as_slice());
assert!(
result.is_ok(),
"P2P: Client should accept ClientSupport from peer for symmetric P2P"
);
let result = TransportParameters::read(Side::Server, &mut buf.as_slice());
assert!(
result.is_ok(),
"Server should accept ClientSupport from client"
);
let mut buf = Vec::new();
buf.write_var_or_debug_assert(0x3d7e9f0bca12fea6); buf.write_var_or_debug_assert(1); buf.put_u8(5);
let result = TransportParameters::read(Side::Server, &mut buf.as_slice());
assert!(
result.is_ok(),
"P2P: Server should accept ServerSupport from peer for symmetric P2P"
);
let result = TransportParameters::read(Side::Client, &mut buf.as_slice());
assert!(
result.is_ok(),
"Client should accept ServerSupport from server"
);
}
#[test]
fn test_nat_traversal_parameter_combinations() {
let nat_config = NatTraversalConfig::ClientSupport;
let mut params = TransportParameters::default();
params.nat_traversal = Some(nat_config);
params.max_idle_timeout = VarInt::from_u32(30000);
params.initial_max_data = VarInt::from_u32(1048576);
params.grease_quic_bit = true;
let mut encoded = Vec::new();
params.write(&mut encoded).unwrap();
assert!(!encoded.is_empty());
let decoded = TransportParameters::read(Side::Server, &mut encoded.as_slice())
.expect("Should decode successfully");
let decoded_config = decoded
.nat_traversal
.expect("NAT traversal should be present");
assert!(matches!(decoded_config, NatTraversalConfig::ClientSupport));
assert_eq!(decoded.max_idle_timeout, VarInt::from_u32(30000));
assert_eq!(decoded.initial_max_data, VarInt::from_u32(1048576));
assert!(decoded.grease_quic_bit);
}
#[test]
fn test_nat_traversal_default_config() {
let default_config = NatTraversalConfig::default();
assert!(matches!(default_config, NatTraversalConfig::ClientSupport));
assert!(default_config.is_client());
assert_eq!(default_config.concurrency_limit(), None);
}
#[test]
fn test_nat_traversal_endpoint_role_negotiation() {
let client_config = NatTraversalConfig::ClientSupport;
let mut client_params = TransportParameters::default();
client_params.nat_traversal = Some(client_config);
let mut client_encoded = Vec::new();
client_params.write(&mut client_encoded).unwrap();
let server_received =
TransportParameters::read(Side::Server, &mut client_encoded.as_slice())
.expect("Server should decode client params");
let server_view = server_received
.nat_traversal
.expect("NAT traversal should be present");
assert!(matches!(server_view, NatTraversalConfig::ClientSupport));
let server_config = NatTraversalConfig::ServerSupport {
concurrency_limit: VarInt::from_u32(8),
};
let mut server_params = TransportParameters::default();
server_params.nat_traversal = Some(server_config);
let mut server_encoded = Vec::new();
server_params.write(&mut server_encoded).unwrap();
let client_received =
TransportParameters::read(Side::Client, &mut server_encoded.as_slice())
.expect("Client should decode server params");
let client_view = client_received
.nat_traversal
.expect("NAT traversal should be present");
assert!(matches!(
client_view,
NatTraversalConfig::ServerSupport { concurrency_limit } if concurrency_limit == VarInt::from_u32(8)
));
}
#[test]
fn test_p2p_nat_traversal_both_server_support() {
let peer1_config = NatTraversalConfig::ServerSupport {
concurrency_limit: VarInt::from_u32(10),
};
let _peer2_config = NatTraversalConfig::ServerSupport {
concurrency_limit: VarInt::from_u32(5),
};
let mut peer1_params = TransportParameters::default();
peer1_params.nat_traversal = Some(peer1_config);
let mut encoded = Vec::new();
peer1_params.write(&mut encoded).unwrap();
let decoded = TransportParameters::read(Side::Server, &mut encoded.as_slice())
.expect("P2P: Server should accept ServerSupport from peer");
assert!(matches!(
decoded.nat_traversal,
Some(NatTraversalConfig::ServerSupport { concurrency_limit })
if concurrency_limit == VarInt::from_u32(10)
));
}
#[test]
fn test_p2p_nat_traversal_concurrency_negotiation() {
let local = NatTraversalConfig::ServerSupport {
concurrency_limit: VarInt::from_u32(10),
};
let mut remote_params = TransportParameters::default();
remote_params.nat_traversal = Some(NatTraversalConfig::ServerSupport {
concurrency_limit: VarInt::from_u32(5),
});
let negotiated = remote_params.negotiated_nat_concurrency_limit(&local);
assert_eq!(negotiated, Some(5));
let local2 = NatTraversalConfig::ServerSupport {
concurrency_limit: VarInt::from_u32(3),
};
let mut remote_params2 = TransportParameters::default();
remote_params2.nat_traversal = Some(NatTraversalConfig::ServerSupport {
concurrency_limit: VarInt::from_u32(8),
});
let negotiated2 = remote_params2.negotiated_nat_concurrency_limit(&local2);
assert_eq!(negotiated2, Some(3));
}
#[test]
fn test_p2p_nat_traversal_invalid_concurrency() {
let config = NatTraversalConfig::ServerSupport {
concurrency_limit: VarInt::from_u32(0), };
let mut params = TransportParameters::default();
params.nat_traversal = Some(config);
let mut encoded = Vec::new();
params.write(&mut encoded).unwrap();
let result = TransportParameters::read(Side::Server, &mut encoded.as_slice());
assert!(
matches!(result, Err(Error::IllegalValue)),
"Should reject concurrency_limit = 0"
);
}
#[test]
fn test_p2p_nat_traversal_max_concurrency() {
let config = NatTraversalConfig::ServerSupport {
concurrency_limit: VarInt::from_u32(101), };
let mut params = TransportParameters::default();
params.nat_traversal = Some(config);
let mut encoded = Vec::new();
params.write(&mut encoded).unwrap();
let result = TransportParameters::read(Side::Server, &mut encoded.as_slice());
assert!(
matches!(result, Err(Error::IllegalValue)),
"Should reject concurrency_limit > 100"
);
}
#[test]
fn test_p2p_both_client_support() {
let config = NatTraversalConfig::ClientSupport;
let mut params = TransportParameters::default();
params.nat_traversal = Some(config);
let mut encoded = Vec::new();
params.write(&mut encoded).unwrap();
let decoded = TransportParameters::read(Side::Client, &mut encoded.as_slice())
.expect("P2P: Client should accept ClientSupport from peer");
assert!(matches!(
decoded.nat_traversal,
Some(NatTraversalConfig::ClientSupport)
));
}
#[test]
fn test_p2p_helper_methods() {
let mut params_with_server = TransportParameters::default();
params_with_server.nat_traversal = Some(NatTraversalConfig::ServerSupport {
concurrency_limit: VarInt::from_u32(5),
});
assert!(params_with_server.supports_bidirectional_nat_traversal());
let mut params_with_client = TransportParameters::default();
params_with_client.nat_traversal = Some(NatTraversalConfig::ClientSupport);
assert!(!params_with_client.supports_bidirectional_nat_traversal());
let params_without_nat = TransportParameters::default();
assert!(!params_without_nat.supports_bidirectional_nat_traversal());
let local = NatTraversalConfig::ClientSupport;
let mut remote_params = TransportParameters::default();
remote_params.nat_traversal = Some(NatTraversalConfig::ServerSupport {
concurrency_limit: VarInt::from_u32(10),
});
let negotiated = remote_params.negotiated_nat_concurrency_limit(&local);
assert_eq!(negotiated, Some(10));
}
#[test]
fn test_traditional_client_server_unchanged() {
let client_config = NatTraversalConfig::ClientSupport;
let mut client_params = TransportParameters::default();
client_params.nat_traversal = Some(client_config);
let mut encoded = Vec::new();
client_params.write(&mut encoded).unwrap();
let server_decoded = TransportParameters::read(Side::Server, &mut encoded.as_slice())
.expect("Traditional client/server should still work");
assert!(matches!(
server_decoded.nat_traversal,
Some(NatTraversalConfig::ClientSupport)
));
}
#[test]
fn test_traditional_server_client_unchanged() {
let server_config = NatTraversalConfig::ServerSupport {
concurrency_limit: VarInt::from_u32(10),
};
let mut server_params = TransportParameters::default();
server_params.nat_traversal = Some(server_config);
let mut encoded = Vec::new();
server_params.write(&mut encoded).unwrap();
let client_decoded = TransportParameters::read(Side::Client, &mut encoded.as_slice())
.expect("Traditional server/client should still work");
assert!(matches!(
client_decoded.nat_traversal,
Some(NatTraversalConfig::ServerSupport { concurrency_limit })
if concurrency_limit == VarInt::from_u32(10)
));
}
#[test]
fn coding() {
let mut buf = Vec::new();
let params = TransportParameters {
initial_src_cid: Some(ConnectionId::new(&[])),
original_dst_cid: Some(ConnectionId::new(&[])),
initial_max_streams_bidi: 16u32.into(),
initial_max_streams_uni: 16u32.into(),
ack_delay_exponent: 2u32.into(),
max_udp_payload_size: 1200u32.into(),
preferred_address: Some(PreferredAddress {
address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
address_v6: Some(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 24, 0, 0)),
connection_id: ConnectionId::new(&[0x42]),
stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
}),
grease_quic_bit: true,
min_ack_delay: Some(2_000u32.into()),
..TransportParameters::default()
};
params.write(&mut buf).unwrap();
assert_eq!(
TransportParameters::read(Side::Client, &mut buf.as_slice()).unwrap(),
params
);
}
#[test]
fn reserved_transport_parameter_generate_reserved_id() {
use rand::rngs::mock::StepRng;
let mut rngs = [
StepRng::new(0, 1),
StepRng::new(1, 1),
StepRng::new(27, 1),
StepRng::new(31, 1),
StepRng::new(u32::MAX as u64, 1),
StepRng::new(u32::MAX as u64 - 1, 1),
StepRng::new(u32::MAX as u64 + 1, 1),
StepRng::new(u32::MAX as u64 - 27, 1),
StepRng::new(u32::MAX as u64 + 27, 1),
StepRng::new(u32::MAX as u64 - 31, 1),
StepRng::new(u32::MAX as u64 + 31, 1),
StepRng::new(u64::MAX, 1),
StepRng::new(u64::MAX - 1, 1),
StepRng::new(u64::MAX - 27, 1),
StepRng::new(u64::MAX - 31, 1),
StepRng::new(1 << 62, 1),
StepRng::new((1 << 62) - 1, 1),
StepRng::new((1 << 62) + 1, 1),
StepRng::new((1 << 62) - 27, 1),
StepRng::new((1 << 62) + 27, 1),
StepRng::new((1 << 62) - 31, 1),
StepRng::new((1 << 62) + 31, 1),
];
for rng in &mut rngs {
let id = ReservedTransportParameter::generate_reserved_id(rng).unwrap();
assert!(id.0 % 31 == 27)
}
}
#[test]
fn reserved_transport_parameter_ignored_when_read() {
let mut buf = Vec::new();
let reserved_parameter =
ReservedTransportParameter::random(&mut rand::thread_rng()).unwrap();
assert!(reserved_parameter.payload_len < ReservedTransportParameter::MAX_PAYLOAD_LEN);
assert!(reserved_parameter.id.0 % 31 == 27);
let _ = reserved_parameter.write(&mut buf);
assert!(!buf.is_empty());
let read_params = TransportParameters::read(Side::Server, &mut buf.as_slice()).unwrap();
assert_eq!(read_params, TransportParameters::default());
}
#[test]
fn read_semantic_validation() {
#[allow(clippy::type_complexity)]
let illegal_params_builders: Vec<Box<dyn FnMut(&mut TransportParameters)>> = vec![
Box::new(|t| {
let min_ack_delay = t.max_ack_delay.0 * 1_000 + 1;
t.min_ack_delay = Some(VarInt::from_u64(min_ack_delay).unwrap())
}),
Box::new(|t| {
t.preferred_address = Some(PreferredAddress {
address_v4: Some(SocketAddrV4::new(Ipv4Addr::LOCALHOST, 42)),
address_v6: None,
connection_id: ConnectionId::new(&[]),
stateless_reset_token: [0xab; RESET_TOKEN_SIZE].into(),
})
}),
];
for mut builder in illegal_params_builders {
let mut buf = Vec::new();
let mut params = TransportParameters::default();
builder(&mut params);
params.write(&mut buf).unwrap();
assert_eq!(
TransportParameters::read(Side::Server, &mut buf.as_slice()),
Err(Error::IllegalValue)
);
}
}
#[test]
fn resumption_params_validation() {
let high_limit = TransportParameters {
initial_max_streams_uni: 32u32.into(),
..TransportParameters::default()
};
let low_limit = TransportParameters {
initial_max_streams_uni: 16u32.into(),
..TransportParameters::default()
};
high_limit.validate_resumption_from(&low_limit).unwrap();
low_limit.validate_resumption_from(&high_limit).unwrap_err();
}
#[test]
fn test_address_discovery_parameter_id() {
assert_eq!(TransportParameterId::AddressDiscovery as u64, 0x9f81a176);
}
#[test]
fn test_address_discovery_config_struct() {
let send_only = AddressDiscoveryConfig::SendOnly;
let receive_only = AddressDiscoveryConfig::ReceiveOnly;
let send_receive = AddressDiscoveryConfig::SendAndReceive;
assert_eq!(send_only.to_value(), VarInt::from_u32(0));
assert_eq!(receive_only.to_value(), VarInt::from_u32(1));
assert_eq!(send_receive.to_value(), VarInt::from_u32(2));
}
#[test]
fn test_address_discovery_config_from_value() {
assert_eq!(
AddressDiscoveryConfig::from_value(VarInt::from_u32(0)).unwrap(),
AddressDiscoveryConfig::SendOnly
);
assert_eq!(
AddressDiscoveryConfig::from_value(VarInt::from_u32(1)).unwrap(),
AddressDiscoveryConfig::ReceiveOnly
);
assert_eq!(
AddressDiscoveryConfig::from_value(VarInt::from_u32(2)).unwrap(),
AddressDiscoveryConfig::SendAndReceive
);
assert!(AddressDiscoveryConfig::from_value(VarInt::from_u32(3)).is_err());
}
#[test]
fn test_transport_parameters_with_address_discovery() {
let mut params = TransportParameters::default();
assert!(params.address_discovery.is_none());
let config = AddressDiscoveryConfig::SendAndReceive;
params.address_discovery = Some(config);
assert!(params.address_discovery.is_some());
let stored_config = params.address_discovery.as_ref().unwrap();
assert_eq!(*stored_config, AddressDiscoveryConfig::SendAndReceive);
}
#[test]
fn test_address_discovery_parameter_encoding() {
let config = AddressDiscoveryConfig::SendAndReceive;
let mut params = TransportParameters::default();
params.address_discovery = Some(config);
let mut encoded = Vec::new();
params.write(&mut encoded).unwrap();
assert!(!encoded.is_empty());
}
#[test]
fn test_address_discovery_parameter_roundtrip() {
let config = AddressDiscoveryConfig::ReceiveOnly;
let mut params = TransportParameters::default();
params.address_discovery = Some(config);
let mut encoded = Vec::new();
params.write(&mut encoded).unwrap();
let decoded = TransportParameters::read(Side::Client, &mut encoded.as_slice())
.expect("Failed to decode transport parameters");
assert!(decoded.address_discovery.is_some());
let decoded_config = decoded.address_discovery.as_ref().unwrap();
assert_eq!(*decoded_config, AddressDiscoveryConfig::ReceiveOnly);
}
#[test]
fn test_address_discovery_disabled_by_default() {
let params = TransportParameters::default();
assert!(params.address_discovery.is_none());
}
#[test]
fn test_address_discovery_all_variants() {
for variant in [
AddressDiscoveryConfig::SendOnly,
AddressDiscoveryConfig::ReceiveOnly,
AddressDiscoveryConfig::SendAndReceive,
] {
let mut params = TransportParameters::default();
params.address_discovery = Some(variant);
let mut encoded = Vec::new();
params.write(&mut encoded).unwrap();
let decoded = TransportParameters::read(Side::Server, &mut encoded.as_slice())
.expect("Failed to decode");
assert_eq!(decoded.address_discovery, Some(variant));
}
}
#[test]
fn test_address_discovery_none_not_encoded() {
let mut params = TransportParameters::default();
params.address_discovery = None;
let mut encoded = Vec::new();
params.write(&mut encoded).unwrap();
let decoded = TransportParameters::read(Side::Client, &mut encoded.as_slice())
.expect("Failed to decode");
assert!(decoded.address_discovery.is_none());
}
#[test]
fn test_address_discovery_serialization_roundtrip() {
let config = AddressDiscoveryConfig::SendOnly;
let mut params = TransportParameters::default();
params.address_discovery = Some(config);
params.initial_max_data = VarInt::from_u32(1_000_000);
let mut encoded = Vec::new();
params.write(&mut encoded).unwrap();
let decoded = TransportParameters::read(Side::Client, &mut encoded.as_slice())
.expect("Failed to decode");
assert_eq!(
decoded.address_discovery,
Some(AddressDiscoveryConfig::SendOnly)
);
assert_eq!(decoded.initial_max_data, VarInt::from_u32(1_000_000));
}
#[test]
fn test_address_discovery_invalid_value() {
let mut encoded = Vec::new();
encoded.write_var_or_debug_assert(TransportParameterId::AddressDiscovery as u64);
encoded.write_var_or_debug_assert(1); encoded.write_var_or_debug_assert(3);
let result = TransportParameters::read(Side::Client, &mut encoded.as_slice());
assert!(result.is_err());
}
#[test]
fn test_address_discovery_edge_cases() {
let mut encoded = Vec::new();
encoded.write_var_or_debug_assert(TransportParameterId::AddressDiscovery as u64);
encoded.write_var_or_debug_assert(0);
let result = TransportParameters::read(Side::Client, &mut encoded.as_slice());
assert!(result.is_err());
let mut encoded = Vec::new();
encoded.write_var_or_debug_assert(TransportParameterId::AddressDiscovery as u64);
encoded.write_var_or_debug_assert(1); encoded.put_u8(255);
let result = TransportParameters::read(Side::Client, &mut encoded.as_slice());
assert!(result.is_err());
}
#[test]
fn test_address_discovery_malformed_length() {
let mut encoded = Vec::new();
encoded.write_var_or_debug_assert(TransportParameterId::AddressDiscovery as u64);
encoded.write_var_or_debug_assert(1);
let result = TransportParameters::read(Side::Client, &mut encoded.as_slice());
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::Malformed));
}
#[test]
fn test_address_discovery_duplicate_parameter() {
let mut encoded = Vec::new();
encoded.write_var_or_debug_assert(TransportParameterId::AddressDiscovery as u64);
encoded.write_var_or_debug_assert(1);
encoded.put_u8(0x80);
encoded.write_var_or_debug_assert(TransportParameterId::AddressDiscovery as u64);
encoded.write_var_or_debug_assert(1);
encoded.put_u8(0xC0);
let result = TransportParameters::read(Side::Client, &mut encoded.as_slice());
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::Malformed));
}
#[test]
fn test_address_discovery_with_other_parameters() {
let mut params = TransportParameters::default();
params.max_idle_timeout = VarInt::from_u32(30000);
params.initial_max_data = VarInt::from_u32(1_000_000);
params.address_discovery = Some(AddressDiscoveryConfig::SendAndReceive);
let mut encoded = Vec::new();
params.write(&mut encoded).unwrap();
let decoded = TransportParameters::read(Side::Client, &mut encoded.as_slice())
.expect("Failed to decode");
assert_eq!(decoded.max_idle_timeout, params.max_idle_timeout);
assert_eq!(decoded.initial_max_data, params.initial_max_data);
assert_eq!(
decoded.address_discovery,
Some(AddressDiscoveryConfig::SendAndReceive)
);
}
#[test]
fn test_pqc_algorithms_transport_parameter() {
let mut params = TransportParameters::default();
params.pqc_algorithms = Some(PqcAlgorithms {
ml_kem_768: true,
ml_dsa_65: true,
});
let mut encoded = Vec::new();
params.write(&mut encoded).unwrap();
let decoded = TransportParameters::read(Side::Client, &mut encoded.as_slice())
.expect("Failed to decode");
assert!(decoded.pqc_algorithms.is_some());
let pqc = decoded.pqc_algorithms.unwrap();
assert!(pqc.ml_kem_768);
assert!(pqc.ml_dsa_65);
}
#[test]
fn test_pqc_algorithms_all_combinations() {
for ml_kem in [false, true] {
for ml_dsa in [false, true] {
let mut params = TransportParameters::default();
params.pqc_algorithms = Some(PqcAlgorithms {
ml_kem_768: ml_kem,
ml_dsa_65: ml_dsa,
});
let mut encoded = Vec::new();
params.write(&mut encoded).unwrap();
let decoded = TransportParameters::read(Side::Client, &mut encoded.as_slice())
.expect("Failed to decode");
let pqc = decoded.pqc_algorithms.unwrap();
assert_eq!(pqc.ml_kem_768, ml_kem);
assert_eq!(pqc.ml_dsa_65, ml_dsa);
}
}
}
#[test]
fn test_pqc_algorithms_not_sent_when_none() {
let mut params = TransportParameters::default();
params.pqc_algorithms = None;
let mut encoded = Vec::new();
params.write(&mut encoded).unwrap();
let decoded = TransportParameters::read(Side::Client, &mut encoded.as_slice())
.expect("Failed to decode");
assert!(decoded.pqc_algorithms.is_none());
}
#[test]
fn test_pqc_algorithms_duplicate_parameter() {
let mut encoded = Vec::new();
encoded.write_var_or_debug_assert(TransportParameterId::PqcAlgorithms as u64);
encoded.write_var_or_debug_assert(1u64); encoded.write(0b1111u8);
encoded.write_var_or_debug_assert(TransportParameterId::PqcAlgorithms as u64);
encoded.write_var_or_debug_assert(1u64);
encoded.write(0b0000u8);
let result = TransportParameters::read(Side::Client, &mut encoded.as_slice());
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), Error::Malformed));
}
#[test]
fn test_ack_receive_v1_roundtrip() {
let mut params = TransportParameters::default();
params.ack_receive_v1 = true;
let mut encoded = Vec::new();
params.write(&mut encoded).expect("encode transport params");
let decoded = TransportParameters::read(Side::Client, &mut encoded.as_slice())
.expect("decode transport params");
assert!(decoded.ack_receive_v1);
}
mod comprehensive_tests {
include!("transport_parameters/tests.rs");
}
}