use super::{QuicInfo, QuicPacketType};
pub fn try_decode_quic(data: &[u8], offset: usize) -> Option<QuicInfo> {
let d = data.get(offset..)?;
if d.len() < 6 {
return None;
}
let b0 = d[0];
if b0 & 0x80 == 0 {
return None;
}
let version = u32::from_be_bytes([d[1], d[2], d[3], d[4]]);
let packet_type = if version == 0 {
QuicPacketType::VersionNegotiation
} else {
match (b0 & 0x30) >> 4 {
0 => QuicPacketType::Initial,
1 => QuicPacketType::ZeroRtt,
2 => QuicPacketType::Handshake,
_ => QuicPacketType::Retry,
}
};
let mut pos = 5;
let dcid = read_cid(d, &mut pos)?;
let scid = read_cid(d, &mut pos)?;
Some(QuicInfo {
long_header: true,
version: Some(version),
packet_type,
dcid,
scid,
sni: None,
header_range: (offset, offset + pos.min(d.len())),
})
}
fn read_cid(d: &[u8], pos: &mut usize) -> Option<Vec<u8>> {
let len = *d.get(*pos)? as usize;
*pos += 1;
if len > 20 || *pos + len > d.len() {
return None;
}
let cid = d[*pos..*pos + len].to_vec();
*pos += len;
Some(cid)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_initial_packet() {
let mut pkt = vec![0xC0, 0x00, 0x00, 0x00, 0x01, 0x04, 1, 2, 3, 4, 0x00];
pkt.extend_from_slice(&[0xAA; 8]); let q = try_decode_quic(&pkt, 0).unwrap();
assert!(q.long_header);
assert_eq!(q.version, Some(1));
assert_eq!(q.packet_type, QuicPacketType::Initial);
assert_eq!(q.dcid, vec![1, 2, 3, 4]);
assert!(q.scid.is_empty());
assert!(q.sni.is_none());
}
#[test]
fn test_version_negotiation() {
let pkt = vec![0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let q = try_decode_quic(&pkt, 0).unwrap();
assert_eq!(q.packet_type, QuicPacketType::VersionNegotiation);
assert_eq!(q.version, Some(0));
}
#[test]
fn test_short_header_ignored() {
let pkt = vec![0x40, 1, 2, 3, 4, 5];
assert!(try_decode_quic(&pkt, 0).is_none());
}
}