use std::collections::HashSet;
use std::mem::size_of;
use crate::ConnectionId;
use crate::Error;
use crate::Result;
use crate::MAX_STREAM_ID;
#[cfg(feature = "qlog")]
use crate::crypto;
#[cfg(feature = "qlog")]
use qlog::events::quic::TransportInitiator;
#[cfg(feature = "qlog")]
use qlog::events::EventData;
#[derive(Clone, Debug, PartialEq)]
pub struct UnknownTransportParameter<T> {
pub id: u64,
pub value: T,
}
impl<T> UnknownTransportParameter<T> {
pub fn is_reserved(&self) -> bool {
let n = (self.id - 27) / 31;
self.id == 31 * n + 27
}
}
#[cfg(feature = "qlog")]
impl From<UnknownTransportParameter<Vec<u8>>>
for qlog::events::quic::UnknownTransportParameter
{
fn from(value: UnknownTransportParameter<Vec<u8>>) -> Self {
Self {
id: value.id,
value: qlog::HexSlice::maybe_string(Some(value.value.as_slice()))
.unwrap_or_default(),
}
}
}
impl From<UnknownTransportParameter<&[u8]>>
for UnknownTransportParameter<Vec<u8>>
{
fn from(value: UnknownTransportParameter<&[u8]>) -> Self {
Self {
id: value.id,
value: value.value.to_vec(),
}
}
}
#[derive(Clone, Debug, PartialEq, Default)]
pub struct UnknownTransportParameters {
pub capacity: usize,
pub parameters: Vec<UnknownTransportParameter<Vec<u8>>>,
}
impl UnknownTransportParameters {
pub fn push(&mut self, new: UnknownTransportParameter<&[u8]>) -> Result<()> {
let new_unknown_tp_size = new.value.len() + size_of::<u64>();
if new_unknown_tp_size < self.capacity {
self.capacity -= new_unknown_tp_size;
self.parameters.push(new.into());
Ok(())
} else {
Err(octets::BufferTooShortError.into())
}
}
}
pub struct UnknownTransportParameterIterator<'a> {
index: usize,
parameters: &'a Vec<UnknownTransportParameter<Vec<u8>>>,
}
impl<'a> IntoIterator for &'a UnknownTransportParameters {
type IntoIter = UnknownTransportParameterIterator<'a>;
type Item = &'a UnknownTransportParameter<Vec<u8>>;
fn into_iter(self) -> Self::IntoIter {
UnknownTransportParameterIterator {
index: 0,
parameters: &self.parameters,
}
}
}
impl<'a> Iterator for UnknownTransportParameterIterator<'a> {
type Item = &'a UnknownTransportParameter<Vec<u8>>;
fn next(&mut self) -> Option<Self::Item> {
let result = self.parameters.get(self.index);
self.index += 1;
result
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct TransportParams {
pub original_destination_connection_id: Option<ConnectionId<'static>>,
pub max_idle_timeout: u64,
pub stateless_reset_token: Option<u128>,
pub max_udp_payload_size: u64,
pub initial_max_data: u64,
pub initial_max_stream_data_bidi_local: u64,
pub initial_max_stream_data_bidi_remote: u64,
pub initial_max_stream_data_uni: u64,
pub initial_max_streams_bidi: u64,
pub initial_max_streams_uni: u64,
pub ack_delay_exponent: u64,
pub max_ack_delay: u64,
pub disable_active_migration: bool,
pub active_conn_id_limit: u64,
pub initial_source_connection_id: Option<ConnectionId<'static>>,
pub retry_source_connection_id: Option<ConnectionId<'static>>,
pub max_datagram_frame_size: Option<u64>,
pub unknown_params: Option<UnknownTransportParameters>,
}
impl Default for TransportParams {
fn default() -> TransportParams {
TransportParams {
original_destination_connection_id: None,
max_idle_timeout: 0,
stateless_reset_token: None,
max_udp_payload_size: 65527,
initial_max_data: 0,
initial_max_stream_data_bidi_local: 0,
initial_max_stream_data_bidi_remote: 0,
initial_max_stream_data_uni: 0,
initial_max_streams_bidi: 0,
initial_max_streams_uni: 0,
ack_delay_exponent: 3,
max_ack_delay: 25,
disable_active_migration: false,
active_conn_id_limit: 2,
initial_source_connection_id: None,
retry_source_connection_id: None,
max_datagram_frame_size: None,
unknown_params: Default::default(),
}
}
}
impl TransportParams {
pub(crate) fn decode(
buf: &[u8], is_server: bool, unknown_size: Option<usize>,
) -> Result<TransportParams> {
let mut params = octets::Octets::with_slice(buf);
let mut seen_params = HashSet::new();
let mut tp = TransportParams::default();
if let Some(unknown_transport_param_tracking_size) = unknown_size {
tp.unknown_params = Some(UnknownTransportParameters {
capacity: unknown_transport_param_tracking_size,
parameters: vec![],
});
}
while params.cap() > 0 {
let id = params.get_varint()?;
if seen_params.contains(&id) {
return Err(Error::InvalidTransportParam);
}
seen_params.insert(id);
let mut val = params.get_bytes_with_varint_length()?;
match id {
0x0000 => {
if is_server {
return Err(Error::InvalidTransportParam);
}
tp.original_destination_connection_id =
Some(val.to_vec().into());
},
0x0001 => {
tp.max_idle_timeout = val.get_varint()?;
},
0x0002 => {
if is_server {
return Err(Error::InvalidTransportParam);
}
tp.stateless_reset_token = Some(u128::from_be_bytes(
val.get_bytes(16)?
.to_vec()
.try_into()
.map_err(|_| Error::BufferTooShort)?,
));
},
0x0003 => {
tp.max_udp_payload_size = val.get_varint()?;
if tp.max_udp_payload_size < 1200 {
return Err(Error::InvalidTransportParam);
}
},
0x0004 => {
tp.initial_max_data = val.get_varint()?;
},
0x0005 => {
tp.initial_max_stream_data_bidi_local = val.get_varint()?;
},
0x0006 => {
tp.initial_max_stream_data_bidi_remote = val.get_varint()?;
},
0x0007 => {
tp.initial_max_stream_data_uni = val.get_varint()?;
},
0x0008 => {
let max = val.get_varint()?;
if max > MAX_STREAM_ID {
return Err(Error::InvalidTransportParam);
}
tp.initial_max_streams_bidi = max;
},
0x0009 => {
let max = val.get_varint()?;
if max > MAX_STREAM_ID {
return Err(Error::InvalidTransportParam);
}
tp.initial_max_streams_uni = max;
},
0x000a => {
let ack_delay_exponent = val.get_varint()?;
if ack_delay_exponent > 20 {
return Err(Error::InvalidTransportParam);
}
tp.ack_delay_exponent = ack_delay_exponent;
},
0x000b => {
let max_ack_delay = val.get_varint()?;
if max_ack_delay >= 2_u64.pow(14) {
return Err(Error::InvalidTransportParam);
}
tp.max_ack_delay = max_ack_delay;
},
0x000c => {
tp.disable_active_migration = true;
},
0x000d => {
if is_server {
return Err(Error::InvalidTransportParam);
}
},
0x000e => {
let limit = val.get_varint()?;
if limit < 2 {
return Err(Error::InvalidTransportParam);
}
tp.active_conn_id_limit = limit;
},
0x000f => {
tp.initial_source_connection_id = Some(val.to_vec().into());
},
0x00010 => {
if is_server {
return Err(Error::InvalidTransportParam);
}
tp.retry_source_connection_id = Some(val.to_vec().into());
},
0x0020 => {
tp.max_datagram_frame_size = Some(val.get_varint()?);
},
unknown_tp_id => {
if let Some(unknown_params) = &mut tp.unknown_params {
let _ = unknown_params.push(UnknownTransportParameter {
id: unknown_tp_id,
value: val.buf(),
});
}
},
}
}
Ok(tp)
}
pub(crate) fn encode_param(
b: &mut octets::OctetsMut, ty: u64, len: usize,
) -> Result<()> {
b.put_varint(ty)?;
b.put_varint(len as u64)?;
Ok(())
}
pub(crate) fn encode<'a>(
tp: &TransportParams, is_server: bool, out: &'a mut [u8],
) -> Result<&'a mut [u8]> {
let mut b = octets::OctetsMut::with_slice(out);
if is_server {
if let Some(ref odcid) = tp.original_destination_connection_id {
TransportParams::encode_param(&mut b, 0x0000, odcid.len())?;
b.put_bytes(odcid)?;
}
};
if tp.max_idle_timeout != 0 {
assert!(tp.max_idle_timeout <= octets::MAX_VAR_INT);
TransportParams::encode_param(
&mut b,
0x0001,
octets::varint_len(tp.max_idle_timeout),
)?;
b.put_varint(tp.max_idle_timeout)?;
}
if is_server {
if let Some(ref token) = tp.stateless_reset_token {
TransportParams::encode_param(&mut b, 0x0002, 16)?;
b.put_bytes(&token.to_be_bytes())?;
}
}
if tp.max_udp_payload_size != 0 {
assert!(tp.max_udp_payload_size <= octets::MAX_VAR_INT);
TransportParams::encode_param(
&mut b,
0x0003,
octets::varint_len(tp.max_udp_payload_size),
)?;
b.put_varint(tp.max_udp_payload_size)?;
}
if tp.initial_max_data != 0 {
assert!(tp.initial_max_data <= octets::MAX_VAR_INT);
TransportParams::encode_param(
&mut b,
0x0004,
octets::varint_len(tp.initial_max_data),
)?;
b.put_varint(tp.initial_max_data)?;
}
if tp.initial_max_stream_data_bidi_local != 0 {
assert!(tp.initial_max_stream_data_bidi_local <= octets::MAX_VAR_INT);
TransportParams::encode_param(
&mut b,
0x0005,
octets::varint_len(tp.initial_max_stream_data_bidi_local),
)?;
b.put_varint(tp.initial_max_stream_data_bidi_local)?;
}
if tp.initial_max_stream_data_bidi_remote != 0 {
assert!(
tp.initial_max_stream_data_bidi_remote <= octets::MAX_VAR_INT
);
TransportParams::encode_param(
&mut b,
0x0006,
octets::varint_len(tp.initial_max_stream_data_bidi_remote),
)?;
b.put_varint(tp.initial_max_stream_data_bidi_remote)?;
}
if tp.initial_max_stream_data_uni != 0 {
assert!(tp.initial_max_stream_data_uni <= octets::MAX_VAR_INT);
TransportParams::encode_param(
&mut b,
0x0007,
octets::varint_len(tp.initial_max_stream_data_uni),
)?;
b.put_varint(tp.initial_max_stream_data_uni)?;
}
if tp.initial_max_streams_bidi != 0 {
assert!(tp.initial_max_streams_bidi <= octets::MAX_VAR_INT);
TransportParams::encode_param(
&mut b,
0x0008,
octets::varint_len(tp.initial_max_streams_bidi),
)?;
b.put_varint(tp.initial_max_streams_bidi)?;
}
if tp.initial_max_streams_uni != 0 {
assert!(tp.initial_max_streams_uni <= octets::MAX_VAR_INT);
TransportParams::encode_param(
&mut b,
0x0009,
octets::varint_len(tp.initial_max_streams_uni),
)?;
b.put_varint(tp.initial_max_streams_uni)?;
}
if tp.ack_delay_exponent != 0 {
assert!(tp.ack_delay_exponent <= octets::MAX_VAR_INT);
TransportParams::encode_param(
&mut b,
0x000a,
octets::varint_len(tp.ack_delay_exponent),
)?;
b.put_varint(tp.ack_delay_exponent)?;
}
if tp.max_ack_delay != 0 {
assert!(tp.max_ack_delay <= octets::MAX_VAR_INT);
TransportParams::encode_param(
&mut b,
0x000b,
octets::varint_len(tp.max_ack_delay),
)?;
b.put_varint(tp.max_ack_delay)?;
}
if tp.disable_active_migration {
TransportParams::encode_param(&mut b, 0x000c, 0)?;
}
if tp.active_conn_id_limit != 2 {
assert!(tp.active_conn_id_limit <= octets::MAX_VAR_INT);
TransportParams::encode_param(
&mut b,
0x000e,
octets::varint_len(tp.active_conn_id_limit),
)?;
b.put_varint(tp.active_conn_id_limit)?;
}
if let Some(scid) = &tp.initial_source_connection_id {
TransportParams::encode_param(&mut b, 0x000f, scid.len())?;
b.put_bytes(scid)?;
}
if is_server {
if let Some(scid) = &tp.retry_source_connection_id {
TransportParams::encode_param(&mut b, 0x0010, scid.len())?;
b.put_bytes(scid)?;
}
}
if let Some(max_datagram_frame_size) = tp.max_datagram_frame_size {
assert!(max_datagram_frame_size <= octets::MAX_VAR_INT);
TransportParams::encode_param(
&mut b,
0x0020,
octets::varint_len(max_datagram_frame_size),
)?;
b.put_varint(max_datagram_frame_size)?;
}
let out_len = b.off();
Ok(&mut out[..out_len])
}
#[cfg(feature = "qlog")]
pub fn to_qlog(
&self, initiator: TransportInitiator, cipher: Option<crypto::Algorithm>,
) -> EventData {
let original_destination_connection_id = qlog::HexSlice::maybe_string(
self.original_destination_connection_id.as_ref(),
);
let stateless_reset_token = qlog::HexSlice::maybe_string(
self.stateless_reset_token.map(|s| s.to_be_bytes()).as_ref(),
);
let tls_cipher: Option<String> = cipher.map(|f| format!("{f:?}"));
EventData::QuicParametersSet(Box::new(
qlog::events::quic::ParametersSet {
initiator: Some(initiator),
tls_cipher,
original_destination_connection_id,
stateless_reset_token,
disable_active_migration: Some(self.disable_active_migration),
max_idle_timeout: Some(self.max_idle_timeout),
max_udp_payload_size: Some(self.max_udp_payload_size),
ack_delay_exponent: Some(self.ack_delay_exponent),
max_ack_delay: Some(self.max_ack_delay),
active_connection_id_limit: Some(self.active_conn_id_limit),
initial_max_data: Some(self.initial_max_data),
initial_max_stream_data_bidi_local: Some(
self.initial_max_stream_data_bidi_local,
),
initial_max_stream_data_bidi_remote: Some(
self.initial_max_stream_data_bidi_remote,
),
initial_max_stream_data_uni: Some(
self.initial_max_stream_data_uni,
),
initial_max_streams_bidi: Some(self.initial_max_streams_bidi),
initial_max_streams_uni: Some(self.initial_max_streams_uni),
unknown_parameters: self
.unknown_params
.as_ref()
.map(|unknown_params| {
unknown_params
.into_iter()
.cloned()
.map(
Into::<
qlog::events::quic::UnknownTransportParameter,
>::into,
)
.collect()
})
.unwrap_or_default(),
..Default::default()
},
))
}
}