use super::parse::QuicPacketParseError;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct QuicConnectionId<'a>(&'a [u8]);
impl<'a> QuicConnectionId<'a> {
pub const fn new(bytes: &'a [u8]) -> Self {
Self(bytes)
}
pub const fn as_bytes(self) -> &'a [u8] {
self.0
}
pub const fn len(self) -> usize {
self.0.len()
}
pub const fn is_empty(self) -> bool {
self.0.is_empty()
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum QuicConnectionIdField {
Destination,
Source,
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct QuicVersion(u32);
impl QuicVersion {
pub const NEGOTIATION: Self = Self(0);
pub const V1: Self = Self(1);
pub const V2: Self = Self(0x6b33_43cf);
pub const fn new(raw: u32) -> Self {
Self(raw)
}
pub const fn raw(self) -> u32 {
self.0
}
pub const fn is_reserved(self) -> bool {
self.0 & 0x0f0f_0f0f == 0x0a0a_0a0a
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum QuicLongPacketType {
Initial,
ZeroRtt,
Handshake,
Retry,
}
impl QuicLongPacketType {
pub const fn from_raw(version: QuicVersion, raw_type: u8) -> Option<Self> {
if raw_type > 3 {
return None;
}
match version {
QuicVersion::V1 => match raw_type {
0 => Some(Self::Initial),
1 => Some(Self::ZeroRtt),
2 => Some(Self::Handshake),
_ => Some(Self::Retry),
},
QuicVersion::V2 => match raw_type {
0 => Some(Self::Retry),
1 => Some(Self::Initial),
2 => Some(Self::ZeroRtt),
_ => Some(Self::Handshake),
},
_ => None,
}
}
pub const fn raw_type(self, version: QuicVersion) -> Option<u8> {
match version {
QuicVersion::V1 => match self {
Self::Initial => Some(0),
Self::ZeroRtt => Some(1),
Self::Handshake => Some(2),
Self::Retry => Some(3),
},
QuicVersion::V2 => match self {
Self::Initial => Some(1),
Self::ZeroRtt => Some(2),
Self::Handshake => Some(3),
Self::Retry => Some(0),
},
_ => None,
}
}
}
const HEADER_FORM_BIT: u8 = 0x80;
const FIXED_BIT: u8 = 0x40;
const LONG_TYPE_SHIFT: u8 = 4;
const LONG_TYPE_MASK: u8 = 0x03;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct QuicShortHeaderContext {
destination_connection_id_len: usize,
}
impl QuicShortHeaderContext {
pub const fn new(destination_connection_id_len: usize) -> Self {
Self {
destination_connection_id_len,
}
}
pub const fn destination_connection_id_len(self) -> usize {
self.destination_connection_id_len
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QuicLongHeader<'a> {
bytes: &'a [u8],
first_byte: u8,
version: QuicVersion,
destination_connection_id: QuicConnectionId<'a>,
source_connection_id: QuicConnectionId<'a>,
version_specific: &'a [u8],
}
impl<'a> QuicLongHeader<'a> {
pub(crate) const fn from_validated(
bytes: &'a [u8],
first_byte: u8,
version: QuicVersion,
destination_connection_id: QuicConnectionId<'a>,
source_connection_id: QuicConnectionId<'a>,
version_specific: &'a [u8],
) -> Self {
Self {
bytes,
first_byte,
version,
destination_connection_id,
source_connection_id,
version_specific,
}
}
pub fn parse(bytes: &'a [u8]) -> Result<Self, QuicPacketParseError> {
let first_byte = first_byte(bytes)?;
if first_byte & HEADER_FORM_BIT == 0 {
return Err(QuicPacketParseError::WrongHeaderForm {
expected_long: true,
first_byte,
});
}
require(bytes, 5)?;
let version =
QuicVersion::new(u32::from_be_bytes([bytes[1], bytes[2], bytes[3], bytes[4]]));
require(bytes, 6)?;
let destination_len = usize::from(bytes[5]);
let destination_end = field_end(6, destination_len)?;
require(bytes, destination_end)?;
let destination_connection_id = QuicConnectionId::new(&bytes[6..destination_end]);
let source_len_offset = destination_end;
let source_start = field_end(source_len_offset, 1)?;
require(bytes, source_start)?;
let source_len = usize::from(bytes[source_len_offset]);
let source_end = field_end(source_start, source_len)?;
require(bytes, source_end)?;
Ok(Self {
bytes: &bytes[..source_end],
first_byte,
version,
destination_connection_id,
source_connection_id: QuicConnectionId::new(&bytes[source_start..source_end]),
version_specific: &bytes[source_end..],
})
}
pub const fn first_byte(self) -> u8 {
self.first_byte
}
pub const fn has_fixed_bit(self) -> bool {
self.first_byte & FIXED_BIT != 0
}
pub const fn raw_type_bits(self) -> u8 {
(self.first_byte >> LONG_TYPE_SHIFT) & LONG_TYPE_MASK
}
pub const fn version(self) -> QuicVersion {
self.version
}
pub const fn destination_connection_id(self) -> QuicConnectionId<'a> {
self.destination_connection_id
}
pub const fn source_connection_id(self) -> QuicConnectionId<'a> {
self.source_connection_id
}
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
pub const fn version_specific(self) -> &'a [u8] {
self.version_specific
}
pub const fn long_packet_type(self) -> Option<QuicLongPacketType> {
QuicLongPacketType::from_raw(self.version, self.raw_type_bits())
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct QuicShortHeader<'a> {
bytes: &'a [u8],
first_byte: u8,
destination_connection_id: QuicConnectionId<'a>,
protected_remainder: &'a [u8],
}
impl<'a> QuicShortHeader<'a> {
pub(crate) const fn from_validated(
bytes: &'a [u8],
first_byte: u8,
destination_connection_id: QuicConnectionId<'a>,
protected_remainder: &'a [u8],
) -> Self {
Self {
bytes,
first_byte,
destination_connection_id,
protected_remainder,
}
}
pub fn parse(
bytes: &'a [u8],
context: QuicShortHeaderContext,
) -> Result<Self, QuicPacketParseError> {
let first_byte = first_byte(bytes)?;
if first_byte & HEADER_FORM_BIT != 0 {
return Err(QuicPacketParseError::WrongHeaderForm {
expected_long: false,
first_byte,
});
}
let destination_end = field_end(1, context.destination_connection_id_len())?;
require(bytes, destination_end)?;
Ok(Self {
bytes,
first_byte,
destination_connection_id: QuicConnectionId::new(&bytes[1..destination_end]),
protected_remainder: &bytes[destination_end..],
})
}
pub const fn first_byte(self) -> u8 {
self.first_byte
}
pub const fn has_fixed_bit(self) -> bool {
self.first_byte & FIXED_BIT != 0
}
pub const fn destination_connection_id(self) -> QuicConnectionId<'a> {
self.destination_connection_id
}
pub const fn protected_remainder(self) -> &'a [u8] {
self.protected_remainder
}
pub const fn as_bytes(self) -> &'a [u8] {
self.bytes
}
}
fn first_byte(bytes: &[u8]) -> Result<u8, QuicPacketParseError> {
bytes
.first()
.copied()
.ok_or(QuicPacketParseError::Incomplete {
required: 1,
available: 0,
})
}
fn field_end(offset: usize, length: usize) -> Result<usize, QuicPacketParseError> {
offset
.checked_add(length)
.ok_or(QuicPacketParseError::LengthOverflow { offset, length })
}
fn require(bytes: &[u8], required: usize) -> Result<(), QuicPacketParseError> {
if bytes.len() < required {
return Err(QuicPacketParseError::Incomplete {
required,
available: bytes.len(),
});
}
Ok(())
}