use core::iter::FusedIterator;
use super::header::{QuicConnectionIdField, QuicLongHeader, QuicLongPacketType, QuicVersion};
use super::parse::QuicPacketParseError;
const RETRY_INTEGRITY_TAG_LEN: usize = 16;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QuicVersionNegotiationPacket<'a> {
bytes: &'a [u8],
header: QuicLongHeader<'a>,
versions: &'a [u8],
}
impl<'a> QuicVersionNegotiationPacket<'a> {
pub(crate) const fn from_validated(
bytes: &'a [u8],
header: QuicLongHeader<'a>,
versions: &'a [u8],
) -> Self {
Self {
bytes,
header,
versions,
}
}
pub fn parse(bytes: &'a [u8]) -> Result<Self, QuicPacketParseError> {
let header = QuicLongHeader::parse(bytes)?;
if header.version() != QuicVersion::NEGOTIATION {
return Err(QuicPacketParseError::WrongVersion {
version: header.version(),
});
}
let versions = header.version_specific();
if versions.is_empty() {
return Err(QuicPacketParseError::EmptyVersionList);
}
if !versions.len().is_multiple_of(4) {
return Err(QuicPacketParseError::MisalignedVersionList {
length: versions.len(),
});
}
Ok(Self {
bytes,
header,
versions,
})
}
pub const fn header(self) -> QuicLongHeader<'a> {
self.header
}
pub const fn version_list_bytes(self) -> &'a [u8] {
self.versions
}
pub const fn versions(self) -> QuicVersionIter<'a> {
QuicVersionIter {
bytes: self.versions,
}
}
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
}
#[derive(Clone, Debug)]
pub struct QuicVersionIter<'a> {
bytes: &'a [u8],
}
impl Iterator for QuicVersionIter<'_> {
type Item = QuicVersion;
fn next(&mut self) -> Option<Self::Item> {
let (version, remainder) = self.bytes.split_first_chunk::<4>()?;
self.bytes = remainder;
Some(QuicVersion::new(u32::from_be_bytes(*version)))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let length = self.len();
(length, Some(length))
}
}
impl ExactSizeIterator for QuicVersionIter<'_> {
fn len(&self) -> usize {
self.bytes.len() / 4
}
}
impl FusedIterator for QuicVersionIter<'_> {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QuicRetryPacket<'a> {
bytes: &'a [u8],
header: QuicLongHeader<'a>,
packet_type: QuicLongPacketType,
token: &'a [u8],
integrity_tag: &'a [u8],
}
impl<'a> QuicRetryPacket<'a> {
pub(crate) const fn from_validated(
bytes: &'a [u8],
header: QuicLongHeader<'a>,
packet_type: QuicLongPacketType,
token: &'a [u8],
integrity_tag: &'a [u8],
) -> Self {
Self {
bytes,
header,
packet_type,
token,
integrity_tag,
}
}
pub fn parse(bytes: &'a [u8]) -> Result<Self, QuicPacketParseError> {
let header = QuicLongHeader::parse(bytes)?;
let version = header.version();
if version != QuicVersion::V1 && version != QuicVersion::V2 {
return Err(QuicPacketParseError::UnsupportedVersion { version });
}
if !header.has_fixed_bit() {
return Err(QuicPacketParseError::FixedBitNotSet {
first_byte: header.first_byte(),
});
}
validate_connection_id(
QuicConnectionIdField::Destination,
header.destination_connection_id().len(),
)?;
validate_connection_id(
QuicConnectionIdField::Source,
header.source_connection_id().len(),
)?;
let packet_type = header
.long_packet_type()
.ok_or(QuicPacketParseError::UnsupportedVersion { version })?;
if packet_type != QuicLongPacketType::Retry {
return Err(QuicPacketParseError::NotRetry { packet_type });
}
let header_end = header.as_bytes().len();
let required = header_end.checked_add(RETRY_INTEGRITY_TAG_LEN).ok_or(
QuicPacketParseError::LengthOverflow {
offset: header_end,
length: RETRY_INTEGRITY_TAG_LEN,
},
)?;
if bytes.len() < required {
return Err(QuicPacketParseError::Incomplete {
required,
available: bytes.len(),
});
}
if bytes.len() == required {
return Err(QuicPacketParseError::EmptyRetryToken);
}
let tag_start = bytes.len() - RETRY_INTEGRITY_TAG_LEN;
Ok(Self {
bytes,
header,
packet_type,
token: &bytes[header_end..tag_start],
integrity_tag: &bytes[tag_start..],
})
}
pub const fn header(self) -> QuicLongHeader<'a> {
self.header
}
pub const fn version(self) -> QuicVersion {
self.header.version()
}
pub const fn packet_type(self) -> QuicLongPacketType {
self.packet_type
}
pub const fn token(self) -> &'a [u8] {
self.token
}
pub const fn integrity_tag(self) -> &'a [u8] {
self.integrity_tag
}
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
}
fn validate_connection_id(
field: QuicConnectionIdField,
length: usize,
) -> Result<(), QuicPacketParseError> {
if length > 20 {
return Err(QuicPacketParseError::ConnectionIdTooLong { field, length });
}
Ok(())
}