mod semantic;
mod value;
use core::{fmt, iter::FusedIterator};
use super::varint::{QuicVarInt, QuicVarIntParseError};
#[cfg(feature = "tls")]
use crate::tls::extensions::extension::TlsExtension;
#[cfg(feature = "tls")]
use crate::tls::types::TlsExtensionType;
pub use semantic::{
QuicTransportParameterHandshakeContext, QuicTransportParameterHandshakeError,
QuicTransportParameterSemanticError, QuicTransportParameterSender, QuicTransportParametersV1,
};
pub use value::{
QuicPreferredAddress, QuicTransportParameterValueError, QuicTransportParameterValueKind,
};
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct QuicTransportParameterId(u64);
impl QuicTransportParameterId {
pub const ORIGINAL_DESTINATION_CONNECTION_ID: Self = Self(0x00);
pub const MAX_IDLE_TIMEOUT: Self = Self(0x01);
pub const STATELESS_RESET_TOKEN: Self = Self(0x02);
pub const MAX_UDP_PAYLOAD_SIZE: Self = Self(0x03);
pub const INITIAL_MAX_DATA: Self = Self(0x04);
pub const INITIAL_MAX_STREAM_DATA_BIDI_LOCAL: Self = Self(0x05);
pub const INITIAL_MAX_STREAM_DATA_BIDI_REMOTE: Self = Self(0x06);
pub const INITIAL_MAX_STREAM_DATA_UNI: Self = Self(0x07);
pub const INITIAL_MAX_STREAMS_BIDI: Self = Self(0x08);
pub const INITIAL_MAX_STREAMS_UNI: Self = Self(0x09);
pub const ACK_DELAY_EXPONENT: Self = Self(0x0a);
pub const MAX_ACK_DELAY: Self = Self(0x0b);
pub const DISABLE_ACTIVE_MIGRATION: Self = Self(0x0c);
pub const PREFERRED_ADDRESS: Self = Self(0x0d);
pub const ACTIVE_CONNECTION_ID_LIMIT: Self = Self(0x0e);
pub const INITIAL_SOURCE_CONNECTION_ID: Self = Self(0x0f);
pub const RETRY_SOURCE_CONNECTION_ID: Self = Self(0x10);
pub const fn new(raw: u64) -> Self {
Self(raw)
}
pub const fn raw(self) -> u64 {
self.0
}
pub const fn is_reserved(self) -> bool {
self.0 % 31 == 27
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QuicTransportParameterField {
Id,
Length,
Value,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QuicTransportParameterParseError {
IncompleteVarInt {
field: QuicTransportParameterField,
offset: usize,
error: QuicVarIntParseError,
},
LengthNotRepresentable {
offset: usize,
value: u64,
},
LengthOverflow {
offset: usize,
length: usize,
},
IncompleteValue {
offset: usize,
required: usize,
available: usize,
},
DuplicateId {
id: QuicTransportParameterId,
first_offset: usize,
duplicate_offset: usize,
},
}
impl fmt::Display for QuicTransportParameterParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::IncompleteVarInt {
field,
offset,
error,
} => write!(
f,
"QUIC transport parameter {field:?} at offset {offset} cannot be parsed: {error}"
),
Self::LengthNotRepresentable { offset, value } => write!(
f,
"QUIC transport parameter length at offset {offset} has unrepresentable value {value}"
),
Self::LengthOverflow { offset, length } => write!(
f,
"QUIC transport parameter value at offset {offset} overflows with length {length}"
),
Self::IncompleteValue {
offset,
required,
available,
} => write!(
f,
"QUIC transport parameter value at offset {offset} is incomplete: need {required} bytes, have {available}"
),
Self::DuplicateId {
id,
first_offset,
duplicate_offset,
} => write!(
f,
"QUIC transport parameter ID {} is duplicated at offset {duplicate_offset}; first occurred at offset {first_offset}",
id.raw()
),
}
}
}
#[cfg(feature = "tls")]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum QuicTransportParametersTlsExtensionError {
WrongExtensionType {
actual: TlsExtensionType,
},
Parse(QuicTransportParameterParseError),
}
#[cfg(feature = "tls")]
impl fmt::Display for QuicTransportParametersTlsExtensionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::WrongExtensionType { actual } => write!(
f,
"TLS extension type {} is not QUIC transport parameters",
actual.raw()
),
Self::Parse(error) => {
write!(f, "TLS QUIC transport parameters cannot be parsed: {error}")
}
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QuicTransportParameter<'a> {
bytes: &'a [u8],
id: QuicVarInt<'a>,
length: QuicVarInt<'a>,
value: &'a [u8],
}
impl<'a> QuicTransportParameter<'a> {
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn id(self) -> QuicVarInt<'a> {
self.id
}
pub const fn parameter_id(self) -> QuicTransportParameterId {
QuicTransportParameterId::new(self.id.value())
}
pub const fn length(self) -> QuicVarInt<'a> {
self.length
}
pub const fn value(self) -> &'a [u8] {
self.value
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QuicTransportParameters<'a> {
bytes: &'a [u8],
}
impl<'a> QuicTransportParameters<'a> {
pub fn parse(bytes: &'a [u8]) -> Result<Self, QuicTransportParameterParseError> {
let mut remaining = bytes;
let mut offset = 0;
while !remaining.is_empty() {
let (parameter, suffix) = parse_at(remaining, offset)?;
if let Some(first_offset) = first_id_offset(bytes, offset, parameter.parameter_id()) {
return Err(QuicTransportParameterParseError::DuplicateId {
id: parameter.parameter_id(),
first_offset,
duplicate_offset: offset,
});
}
let consumed = parameter.as_bytes().len();
remaining = suffix;
offset += consumed;
}
Ok(Self { bytes })
}
pub fn validate_v1(
self,
sender: QuicTransportParameterSender,
) -> Result<QuicTransportParametersV1<'a>, QuicTransportParameterSemanticError> {
semantic::validate(self.bytes, sender)
}
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn iter(self) -> QuicTransportParameterIter<'a> {
QuicTransportParameterIter::new(self.bytes)
}
}
#[cfg(feature = "tls")]
impl<'a> TlsExtension<'a> {
pub fn quic_transport_parameters(
&self,
) -> Result<QuicTransportParameters<'a>, QuicTransportParametersTlsExtensionError> {
if self.extension_type() != TlsExtensionType::QUIC_TRANSPORT_PARAMETERS {
return Err(
QuicTransportParametersTlsExtensionError::WrongExtensionType {
actual: self.extension_type(),
},
);
}
QuicTransportParameters::parse(self.data())
.map_err(QuicTransportParametersTlsExtensionError::Parse)
}
}
#[derive(Clone, Debug)]
pub struct QuicTransportParameterIter<'a> {
remaining: &'a [u8],
offset: usize,
failed: bool,
}
impl<'a> QuicTransportParameterIter<'a> {
pub(super) const fn new(bytes: &'a [u8]) -> Self {
Self {
remaining: bytes,
offset: 0,
failed: false,
}
}
}
impl<'a> Iterator for QuicTransportParameterIter<'a> {
type Item = Result<QuicTransportParameter<'a>, QuicTransportParameterParseError>;
fn next(&mut self) -> Option<Self::Item> {
if self.failed || self.remaining.is_empty() {
return None;
}
match parse_at(self.remaining, self.offset) {
Ok((parameter, suffix)) => {
let consumed = parameter.as_bytes().len();
self.remaining = suffix;
self.offset += consumed;
Some(Ok(parameter))
}
Err(error) => {
self.failed = true;
self.remaining = &[];
Some(Err(error))
}
}
}
}
impl FusedIterator for QuicTransportParameterIter<'_> {}
fn parse_at<'a>(
bytes: &'a [u8],
offset: usize,
) -> Result<(QuicTransportParameter<'a>, &'a [u8]), QuicTransportParameterParseError> {
let id = QuicVarInt::parse(bytes).map_err(|error| {
QuicTransportParameterParseError::IncompleteVarInt {
field: QuicTransportParameterField::Id,
offset,
error,
}
})?;
let length_start = id.byte_len();
let length_offset = offset + length_start;
let length = QuicVarInt::parse(&bytes[length_start..]).map_err(|error| {
QuicTransportParameterParseError::IncompleteVarInt {
field: QuicTransportParameterField::Length,
offset: length_offset,
error,
}
})?;
let value_start = length_start + length.byte_len();
let value_offset = offset + value_start;
let value_length = usize::try_from(length.value()).map_err(|_| {
QuicTransportParameterParseError::LengthNotRepresentable {
offset: length_offset,
value: length.value(),
}
})?;
let value_end = value_start.checked_add(value_length).ok_or(
QuicTransportParameterParseError::LengthOverflow {
offset: value_offset,
length: value_length,
},
)?;
let available = bytes.len() - value_start;
if available < value_length {
return Err(QuicTransportParameterParseError::IncompleteValue {
offset: value_offset,
required: value_length,
available,
});
}
Ok((
QuicTransportParameter {
bytes: &bytes[..value_end],
id,
length,
value: &bytes[value_start..value_end],
},
&bytes[value_end..],
))
}
fn first_id_offset(bytes: &[u8], end: usize, id: QuicTransportParameterId) -> Option<usize> {
let mut remaining = &bytes[..end];
let mut offset = 0;
while !remaining.is_empty() {
let (parameter, suffix) = match parse_at(remaining, offset) {
Ok(parsed) => parsed,
Err(_) => return None,
};
if parameter.parameter_id() == id {
return Some(offset);
}
offset += parameter.as_bytes().len();
remaining = suffix;
}
None
}