use crate::checks::application::quic::{
validate_fixed_bit, validate_length_field, validate_long_header, validate_payload_available,
validate_version,
};
#[cfg_attr(doc, aquamarine::aquamarine)]
#[derive(Debug, Clone)]
pub enum QuicPacket {
Initial {
header: QuicLongHeader,
token: Vec<u8>,
payload: QuicPayload,
},
Handshake {
header: QuicLongHeader,
payload: QuicPayload,
},
OtherLong {
header: QuicLongHeader,
payload: QuicPayload,
},
}
#[derive(Debug, Clone)]
pub struct QuicLongHeader {
pub header_form_long: bool,
pub fixed_bit: bool,
pub packet_type: QuicPacketType,
pub version: u32,
pub dcid: ConnectionId,
pub scid: ConnectionId,
pub pn_length: u8,
pub length_field: u64,
pub packet_number: Option<u64>,
}
#[derive(Debug, Clone, Copy)]
pub enum QuicPacketType {
Initial,
ZeroRtt,
Handshake,
Retry,
Unknown(u8),
}
#[derive(Debug, Clone)]
pub struct ConnectionId {
pub len: u8,
pub bytes: Vec<u8>,
}
#[derive(Debug, Clone)]
pub enum QuicPayload {
Frames(Vec<QuicFrame>),
EncryptedPayload(Vec<u8>),
}
#[derive(Debug, Clone)]
pub enum QuicFrame {
Ack(AckFrame),
Crypto(CryptoFrame),
Padding { length: u64 },
Ping,
Unknown { frame_type: u64, raw: Vec<u8> },
}
#[derive(Debug, Clone)]
pub struct AckFrame {
pub largest_acknowledged: u64,
pub ack_delay_us: u64,
pub ack_range_count: u64,
pub first_ack_range: u64,
pub additional_ranges: Vec<AckRange>,
}
#[derive(Debug, Clone)]
pub struct AckRange {
pub gap: u64,
pub ack_range_len: u64,
}
#[derive(Debug, Clone)]
pub struct CryptoFrame {
pub offset: u64,
pub length: u64,
pub data: Vec<u8>,
}
struct Cur<'a> {
b: &'a [u8],
i: usize,
}
impl<'a> Cur<'a> {
fn new(b: &'a [u8]) -> Self {
Self { b, i: 0 }
}
fn left(&self) -> usize {
self.b.len().saturating_sub(self.i)
}
fn take(&mut self, n: usize) -> Result<&'a [u8], crate::parse::application::ApplicationError> {
if self.left() < n {
return Err(crate::parse::application::ApplicationError::QuicParseError);
}
let s = &self.b[self.i..self.i + n];
self.i += n;
Ok(s)
}
fn take_u8(&mut self) -> Result<u8, crate::parse::application::ApplicationError> {
Ok(*self.take(1)?.first().unwrap())
}
}
impl<'a> Clone for Cur<'a> {
fn clone(&self) -> Self {
Self {
b: self.b,
i: self.i,
}
}
}
impl TryFrom<&[u8]> for QuicPacket {
type Error = crate::parse::application::ApplicationError;
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
fn err<S: Into<String>>(_s: S) -> crate::parse::application::ApplicationError {
crate::parse::application::ApplicationError::QuicParseError
}
fn read_varint(cur: &mut Cur) -> Result<u64, crate::parse::application::ApplicationError> {
let first = cur.take_u8()?;
let prefix = first >> 6; let (total, mask): (usize, u64) = match prefix {
0 => (1, 0b0011_1111),
1 => (2, 0b0011_1111),
2 => (4, 0b0011_1111),
3 => (8, 0b0011_1111),
_ => unreachable!(),
};
let mut val = (first & (mask as u8)) as u64;
for _ in 1..total {
val = (val << 8) | (cur.take_u8()? as u64);
}
Ok(val)
}
fn read_cid(
cur: &mut Cur,
) -> Result<ConnectionId, crate::parse::application::ApplicationError> {
let len = cur.take_u8()?;
let bytes = cur.take(len as usize)?.to_vec();
Ok(ConnectionId { len, bytes })
}
let mut cur = Cur::new(buf);
let b0 = cur.take_u8()?;
let header_form_long = (b0 & 0b1000_0000) != 0;
validate_long_header(header_form_long)?;
let fixed_bit = (b0 & 0b0100_0000) != 0;
validate_fixed_bit(fixed_bit)?;
let lptype = (b0 >> 4) & 0b11; let _reserved = (b0 >> 2) & 0b11; let pn_len_code = b0 & 0b11; let pn_length = pn_len_code + 1;
let packet_type = match lptype {
0 => QuicPacketType::Initial,
1 => QuicPacketType::ZeroRtt,
2 => QuicPacketType::Handshake,
3 => QuicPacketType::Retry,
x => QuicPacketType::Unknown(x),
};
let ver_bytes = cur.take(4)?;
let version = u32::from_be_bytes([ver_bytes[0], ver_bytes[1], ver_bytes[2], ver_bytes[3]]);
validate_version(version)?;
let dcid = read_cid(&mut cur)?;
let scid = read_cid(&mut cur)?;
let mut header = QuicLongHeader {
header_form_long,
fixed_bit,
packet_type,
version,
dcid,
scid,
pn_length,
length_field: 0, packet_number: None, };
match packet_type {
QuicPacketType::Initial => {
let token_len = read_varint(&mut cur)? as usize;
let token = cur.take(token_len)?.to_vec();
let length_field = read_varint(&mut cur)?;
header.length_field = length_field;
let pn_raw = cur.take(header.pn_length as usize)?;
let mut pn: u64 = 0;
for &b in pn_raw {
pn = (pn << 8) | (b as u64);
}
header.packet_number = Some(pn);
let remaining_for_payload = validate_length_field(length_field, header.pn_length)?;
validate_payload_available(cur.left(), remaining_for_payload)?;
let payload_bytes = cur.take(remaining_for_payload)?.to_vec();
Ok(QuicPacket::Initial {
header,
token,
payload: QuicPayload::EncryptedPayload(payload_bytes),
})
}
QuicPacketType::Handshake => {
let length_field = read_varint(&mut cur)?;
header.length_field = length_field;
let pn_raw = cur.take(header.pn_length as usize)?;
let mut pn: u64 = 0;
for &b in pn_raw {
pn = (pn << 8) | (b as u64);
}
header.packet_number = Some(pn);
let remaining_for_payload = validate_length_field(length_field, header.pn_length)?;
validate_payload_available(cur.left(), remaining_for_payload)?;
let payload_bytes = cur.take(remaining_for_payload)?.to_vec();
Ok(QuicPacket::Handshake {
header,
payload: QuicPayload::EncryptedPayload(payload_bytes),
})
}
QuicPacketType::ZeroRtt => {
let length_field = read_varint(&mut cur)?;
header.length_field = length_field;
let pn_raw = cur.take(header.pn_length as usize)?;
let mut pn: u64 = 0;
for &b in pn_raw {
pn = (pn << 8) | (b as u64);
}
header.packet_number = Some(pn);
let remaining_for_payload = validate_length_field(length_field, header.pn_length)?;
validate_payload_available(cur.left(), remaining_for_payload)?;
let payload_bytes = cur.take(remaining_for_payload)?.to_vec();
Ok(QuicPacket::OtherLong {
header,
payload: QuicPayload::EncryptedPayload(payload_bytes),
})
}
QuicPacketType::Retry => {
let rest = cur.take(cur.left())?.to_vec();
Ok(QuicPacket::OtherLong {
header,
payload: QuicPayload::EncryptedPayload(rest),
})
}
QuicPacketType::Unknown(_t) => {
let mut snapshot = cur.clone();
let length_field = match read_varint(&mut cur) {
Ok(v) => v,
Err(_) => {
let rest = snapshot
.take(snapshot.left())
.map_err(|_| err("internal"))?
.to_vec();
return Ok(QuicPacket::OtherLong {
header,
payload: QuicPayload::EncryptedPayload(rest),
});
}
};
header.length_field = length_field;
let pn_raw = cur.take(header.pn_length as usize)?;
let mut pn: u64 = 0;
for &b in pn_raw {
pn = (pn << 8) | (b as u64);
}
header.packet_number = Some(pn);
let remaining_for_payload = validate_length_field(length_field, header.pn_length)?;
validate_payload_available(cur.left(), remaining_for_payload)?;
let payload_bytes = cur.take(remaining_for_payload)?.to_vec();
Ok(QuicPacket::OtherLong {
header,
payload: QuicPayload::EncryptedPayload(payload_bytes),
})
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_quic_initial_minimal() {
let mut buf = Vec::new();
buf.push(0xC0); buf.extend_from_slice(&0x00000001u32.to_be_bytes());
buf.push(0); buf.push(0); buf.push(0x00); buf.push(0x01); buf.push(0x00);
let pkt = QuicPacket::try_from(buf.as_slice()).expect("must parse");
match pkt {
QuicPacket::Initial {
header,
token,
payload,
} => {
assert_eq!(header.version, 1);
assert!(token.is_empty());
matches!(payload, QuicPayload::EncryptedPayload(ref v) if v.is_empty());
}
_ => panic!("expected Initial"),
}
}
#[test]
fn test_error_not_long_header() {
let mut buf = Vec::new();
buf.push(0x40);
buf.extend_from_slice(&0x00000001u32.to_be_bytes()); buf.push(0); buf.push(0); let res = QuicPacket::try_from(buf.as_slice());
assert!(matches!(
res,
Err(crate::parse::application::ApplicationError::QuicParseError)
));
}
#[test]
fn test_error_fixed_bit_zero() {
let mut buf = Vec::new();
buf.push(0x80);
buf.extend_from_slice(&0x00000001u32.to_be_bytes()); buf.push(0); buf.push(0); buf.push(0x00);
buf.push(0x01);
buf.push(0x00);
let res = QuicPacket::try_from(buf.as_slice());
assert!(matches!(
res,
Err(crate::parse::application::ApplicationError::QuicParseError)
));
}
#[test]
fn test_error_truncated_payload_length() {
let mut buf = Vec::new();
buf.push(0xC0);
buf.extend_from_slice(&0x00000001u32.to_be_bytes());
buf.push(0); buf.push(0); buf.push(0x00);
buf.push(0x05);
buf.push(0xAA);
buf.extend_from_slice(&[0x01, 0x02]);
let res = QuicPacket::try_from(buf.as_slice());
assert!(matches!(
res,
Err(crate::parse::application::ApplicationError::QuicParseError)
));
}
#[test]
fn test_initial_pkn_1_ack() {
let bytes = hex::decode(
"c4000000010008f409517248c4ab52004016dae7c01a42788f5049396532534a03ae8ebd63bf94e4",
)
.expect("Invalid hex string");
let parsed = QuicPacket::try_from(bytes.as_slice()).expect("must parse");
match parsed {
QuicPacket::Initial {
header,
token,
payload,
} => {
assert!(header.header_form_long);
assert!(header.fixed_bit);
assert!(matches!(header.packet_type, QuicPacketType::Initial));
assert_eq!(header.version, 1);
assert_eq!(header.dcid.len, 0);
assert!(header.dcid.bytes.is_empty());
assert_eq!(header.scid.len, 8);
assert_eq!(header.scid.bytes, hex::decode("f409517248c4ab52").unwrap());
assert!(token.is_empty());
assert_eq!(header.length_field, 22);
assert_eq!(header.pn_length, 1);
assert_eq!(header.packet_number, Some(0xDA));
match payload {
QuicPayload::EncryptedPayload(v) => {
assert_eq!(
v,
hex::decode("e7c01a42788f5049396532534a03ae8ebd63bf94e4").unwrap()
);
}
_ => panic!("expected EncryptedPayload"),
}
}
_ => panic!("expected Initial"),
}
}
}