From bbaf22ec4212f78df3d451508cd91537697445f8 Mon Sep 17 00:00:00 2001 From: Jackson Sippe Date: Thu, 11 Jul 2024 19:41:50 +0000 Subject: [PATCH 1/3] Add token and QUIC packet type support --- core/src/protocols/stream/quic/header.rs | 36 ++++++- core/src/protocols/stream/quic/mod.rs | 18 ++-- core/src/protocols/stream/quic/parser.rs | 125 ++++++++++++++++++----- traces/README.md | 3 +- traces/quic_retry.pcapng | Bin 0 -> 14146 bytes 5 files changed, 146 insertions(+), 36 deletions(-) create mode 100644 traces/quic_retry.pcapng diff --git a/core/src/protocols/stream/quic/header.rs b/core/src/protocols/stream/quic/header.rs index 76fd011b..f9386c3a 100644 --- a/core/src/protocols/stream/quic/header.rs +++ b/core/src/protocols/stream/quic/header.rs @@ -2,16 +2,21 @@ use serde::Serialize; +use crate::protocols::stream::quic::parser::QuicError; + /// Quic Long Header #[derive(Debug, Serialize, Clone)] pub struct QuicLongHeader { - pub packet_type: u8, + pub packet_type: LongHeaderPacketType, pub type_specific: u8, pub version: u32, - pub dcid_len: u8, // length of dcid in bytes - pub dcid: String, // hex string - pub scid_len: u8, // length of scid in bytes - pub scid: String, // hex string + pub dcid_len: u8, // length of dcid in bytes + pub dcid: String, // hex string + pub scid_len: u8, // length of scid in bytes + pub scid: String, // hex string + pub token_len: Option, // length of token in bytes, if packet is of type Init or Retry + pub token: Option, // hex string, if packet is of type Init or Retry + pub retry_tag: Option, // hex string, if packet is of type Retry } /// Quic Short Header @@ -22,3 +27,24 @@ pub struct QuicShortHeader { #[serde(skip)] pub dcid_bytes: Vec, } + +// Long Header Packet Types from RFC 9000 Table 5 +#[derive(Debug, Clone, Serialize, Copy)] +pub enum LongHeaderPacketType { + Initial, + ZeroRTT, + Handshake, + Retry, +} + +impl LongHeaderPacketType { + pub fn from_u8(value: u8) -> Result { + match value { + 0x00 => Ok(LongHeaderPacketType::Initial), + 0x01 => Ok(LongHeaderPacketType::ZeroRTT), + 0x02 => Ok(LongHeaderPacketType::Handshake), + 0x03 => Ok(LongHeaderPacketType::Retry), + _ => Err(QuicError::UnknowLongHeaderPacketType), + } + } +} diff --git a/core/src/protocols/stream/quic/mod.rs b/core/src/protocols/stream/quic/mod.rs index 27c5e9ec..0bb2067a 100644 --- a/core/src/protocols/stream/quic/mod.rs +++ b/core/src/protocols/stream/quic/mod.rs @@ -23,6 +23,8 @@ TODO: support HTTP/3 pub(crate) mod parser; pub use self::header::{QuicLongHeader, QuicShortHeader}; +use header::LongHeaderPacketType; +use parser::QuicError; use serde::Serialize; pub(crate) mod header; @@ -36,7 +38,7 @@ pub struct QuicPacket { pub long_header: Option, /// The number of bytes contained in the estimated payload - pub payload_bytes_count: u16, + pub payload_bytes_count: Option, } impl QuicPacket { @@ -52,10 +54,10 @@ impl QuicPacket { } /// Returns the packet type of the Quic packet - pub fn packet_type(&self) -> u8 { + pub fn packet_type(&self) -> Result { match &self.long_header { - Some(long_header) => long_header.packet_type, - None => 0, + Some(long_header) => Ok(long_header.packet_type), + None => Err(QuicError::NoLongHeader), } } @@ -102,7 +104,11 @@ impl QuicPacket { } /// Returns the number of bytes in the payload of the Quic packet - pub fn payload_bytes_count(&self) -> u16 { - self.payload_bytes_count + pub fn payload_bytes_count(&self) -> u64 { + if let Some(count) = self.payload_bytes_count { + count + } else { + 0 + } } } diff --git a/core/src/protocols/stream/quic/parser.rs b/core/src/protocols/stream/quic/parser.rs index 063c7805..a87bf665 100644 --- a/core/src/protocols/stream/quic/parser.rs +++ b/core/src/protocols/stream/quic/parser.rs @@ -2,7 +2,9 @@ //! Custom Quic Parser with many design choices borrowed from //! [Wireshark Quic Disector](https://gitlab.com/wireshark/wireshark/-/blob/master/epan/dissectors/packet-quic.c) //! -use crate::protocols::stream::quic::header::{QuicLongHeader, QuicShortHeader}; +use crate::protocols::stream::quic::header::{ + LongHeaderPacketType, QuicLongHeader, QuicShortHeader, +}; use crate::protocols::stream::quic::QuicPacket; use crate::protocols::stream::{ ConnParsable, ConnState, L4Pdu, ParseResult, ProbeResult, Session, SessionData, @@ -123,6 +125,9 @@ pub enum QuicError { PacketTooShort, UnknownVersion, ShortHeader, + UnknowLongHeaderPacketType, + NoLongHeader, + UnsupportedVarLen, } impl QuicPacket { @@ -134,50 +139,119 @@ impl QuicPacket { .join("") } + // Calculate the length of a variable length encoding + // See RFC 9000 Section 16 for details + pub fn get_var_len(a: u8) -> Result { + let two_msb = a >> 6; + match two_msb { + 0b00 => Ok(1), + 0b01 => Ok(2), + 0b10 => Ok(4), + 0b11 => Ok(8), + _ => Err(QuicError::UnsupportedVarLen), + } + } + + // Masks variable length encoding and returns u64 value for remainder of field + fn slice_to_u64(data: &[u8]) -> Result { + if data.len() > 8 { + return Err(QuicError::UnsupportedVarLen); + } + + let mut result: u64 = 0; + for &byte in data { + result = (result << 8) | u64::from(byte); + } + result &= !(0b11 << ((data.len() * 8) - 2)); // Var length encoding mask + Ok(result) + } + /// Parses Quic packet from bytes pub fn parse_from(data: &[u8]) -> Result { + let mut offset = 0; if data.len() <= 2 { return Err(QuicError::PacketTooShort); } - if (data[0] & 0x40) == 0 { + if (data[offset] & 0x40) == 0 { return Err(QuicError::FixedBitNotSet); } - if (data[0] & 0x80) != 0 { + if (data[offset] & 0x80) != 0 { + let packet_type = LongHeaderPacketType::from_u8((data[offset] & 0x30) >> 4)?; + let type_specific = data[offset] & 0x0F; + offset += 1; // Long Header if data.len() < 7 { return Err(QuicError::PacketTooShort); } - let version = ((data[1] as u32) << 24) - | ((data[2] as u32) << 16) - | ((data[3] as u32) << 8) - | (data[4] as u32); + let version = ((data[offset] as u32) << 24) + | ((data[offset + 1] as u32) << 16) + | ((data[offset + 2] as u32) << 8) + | (data[offset + 3] as u32); if QuicVersion::from_u32(version) == QuicVersion::Unknown { return Err(QuicError::UnknownVersion); } - - let packet_type = (data[0] & 0x30) >> 4; - let type_specific = data[0] & 0x0F; - - let dcid_len = data[5]; - let dcid_start = 6; + offset += 4; + let dcid_len = data[offset]; + offset += 1; // There's a +2 in this size check because we need enough space to check the SCID length - if data.len() < (dcid_start + dcid_len as usize + 2) { + if data.len() < (offset + dcid_len as usize + 2) { return Err(QuicError::PacketTooShort); } - let dcid_bytes = &data[dcid_start..dcid_start + dcid_len as usize]; + let dcid_bytes = &data[offset..offset + dcid_len as usize]; let dcid = QuicPacket::vec_u8_to_hex_string(dcid_bytes); - let scid_len = data[dcid_start + dcid_len as usize]; - let scid_start = dcid_start + dcid_len as usize + 1; - if data.len() < (scid_start + scid_len as usize + 1) { + offset += dcid_len as usize; + let scid_len = data[offset]; + offset += 1; + if data.len() < (offset + scid_len as usize + 1) { return Err(QuicError::PacketTooShort); } - let scid_bytes = &data[scid_start..scid_start + scid_len as usize]; + let scid_bytes = &data[offset..offset + scid_len as usize]; let scid = QuicPacket::vec_u8_to_hex_string(scid_bytes); + offset += scid_len as usize; + + let token_len; + let token; + let packet_len; + let retry_tag; + + match packet_type { + LongHeaderPacketType::Initial => { + retry_tag = None; + let token_len_len = QuicPacket::get_var_len(data[offset])?; + token_len = Some(QuicPacket::slice_to_u64( + &data[offset..offset + token_len_len], + )?); + offset += token_len_len; + let token_bytes = &data[offset..offset + token_len.unwrap() as usize]; + token = Some(QuicPacket::vec_u8_to_hex_string(token_bytes)); + offset += token_len.unwrap() as usize; + let packet_len_len = QuicPacket::get_var_len(data[offset])?; + packet_len = Some(QuicPacket::slice_to_u64( + &data[offset..offset + packet_len_len], + )?); + } + LongHeaderPacketType::ZeroRTT | LongHeaderPacketType::Handshake => { + token_len = None; + token = None; + retry_tag = None; + let packet_len_len = QuicPacket::get_var_len(data[offset])?; + packet_len = Some(QuicPacket::slice_to_u64( + &data[offset..offset + packet_len_len], + )?); + } + LongHeaderPacketType::Retry => { + packet_len = None; + token_len = Some((data.len() - offset - 16) as u64); + let token_bytes = &data[offset..offset + token_len.unwrap() as usize]; + token = Some(QuicPacket::vec_u8_to_hex_string(token_bytes)); + offset += token_len.unwrap() as usize; + let retry_tag_bytes = &data[offset..offset + 16]; + retry_tag = Some(QuicPacket::vec_u8_to_hex_string(retry_tag_bytes)); + } + } - // Counts all bytes remaining - let payload_bytes_count = data.len() - scid_start - scid_len as usize; Ok(QuicPacket { - payload_bytes_count: payload_bytes_count as u16, + payload_bytes_count: packet_len, short_header: None, long_header: Some(QuicLongHeader { packet_type, @@ -187,6 +261,9 @@ impl QuicPacket { dcid, scid_len, scid, + token_len, + token, + retry_tag, }), }) } else { @@ -197,14 +274,14 @@ impl QuicPacket { } let dcid_bytes = data[1..1 + max_dcid_len].to_vec(); // Counts all bytes remaining - let payload_bytes_count = data.len() - 1 - max_dcid_len; + let payload_bytes_count = Some((data.len() - 1 - max_dcid_len) as u64); Ok(QuicPacket { short_header: Some(QuicShortHeader { dcid: None, dcid_bytes, }), long_header: None, - payload_bytes_count: payload_bytes_count as u16, + payload_bytes_count, }) } } diff --git a/traces/README.md b/traces/README.md index 109fcb91..e5c94e44 100644 --- a/traces/README.md +++ b/traces/README.md @@ -5,4 +5,5 @@ A collection of sample packet captures pulled from a variety of sources. | Trace | Source | Description | |--------------------|-------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------| | `small_flows.pcap` | [Tcpreplay Sample Captures](https://tcpreplay.appneta.com/wiki/captures.html) | A synthetic combination of a few different applications and protocols at a relatively low network traffic rate. | -| `tls_ciphers.pcap` | [Wireshark Sample Captures](https://wiki.wireshark.org/SampleCaptures) | OpenSSL client/server GET requests over TLS 1.2 with 73 different cipher suites. | +| `tls_ciphers.pcap` | [Wireshark Sample Captures](https://wiki.wireshark.org/SampleCaptures) | OpenSSL client/server GET requests over TLS 1.2 with 73 different cipher suites. | +| `quic_retry.pcapng`| [Wireshark Issue](https://gitlab.com/wireshark/wireshark/-/issues/18757) | An example of a QUIC Retry Packet. Original Pcap modified to remove CookedLinux and add Ether | \ No newline at end of file diff --git a/traces/quic_retry.pcapng b/traces/quic_retry.pcapng new file mode 100644 index 0000000000000000000000000000000000000000..d6398e96dceedbeb3d67b196331ba181c3062d4b GIT binary patch literal 14146 zcmeI1bx@XF*Y+>E1*8$AyHmPLq&uY>kp@Ae8|el?LTQvn8U&
    i>KJHG4W8Qytr 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 literal 0 HcmV?d00001 From 2ddc5eaba8f32f6cdc44636d6231a2f96e4a858b Mon Sep 17 00:00:00 2001 From: Jackson Sippe Date: Thu, 11 Jul 2024 21:06:22 +0000 Subject: [PATCH 2/3] Improve length validation, add offset to short header branch --- core/src/protocols/stream/quic/parser.rs | 57 +++++++++++++++++------- 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/core/src/protocols/stream/quic/parser.rs b/core/src/protocols/stream/quic/parser.rs index a87bf665..ba7c9d85 100644 --- a/core/src/protocols/stream/quic/parser.rs +++ b/core/src/protocols/stream/quic/parser.rs @@ -166,23 +166,30 @@ impl QuicPacket { Ok(result) } + fn validate_length(data: &[u8], acc_len: usize) -> Result<(), QuicError> { + if data.len() < acc_len { + return Err(QuicError::PacketTooShort) + } + Ok(()) + } + /// Parses Quic packet from bytes pub fn parse_from(data: &[u8]) -> Result { let mut offset = 0; - if data.len() <= 2 { - return Err(QuicError::PacketTooShort); - } + QuicPacket::validate_length(data, offset + 1)?; + // Check the fixed bit if (data[offset] & 0x40) == 0 { return Err(QuicError::FixedBitNotSet); } + // Check the Header form if (data[offset] & 0x80) != 0 { + // Long Header + // Parse packet type let packet_type = LongHeaderPacketType::from_u8((data[offset] & 0x30) >> 4)?; - let type_specific = data[offset] & 0x0F; + let type_specific = data[offset] & 0x0F; // Remainder of information from header byte, Reserved and protected packet number length offset += 1; - // Long Header - if data.len() < 7 { - return Err(QuicError::PacketTooShort); - } + // Parse version + QuicPacket::validate_length(data, offset + 4)?; let version = ((data[offset] as u32) << 24) | ((data[offset + 1] as u32) << 16) | ((data[offset + 2] as u32) << 8) @@ -191,20 +198,19 @@ impl QuicPacket { return Err(QuicError::UnknownVersion); } offset += 4; + // Parse DCID + QuicPacket::validate_length(data, offset + 1)?; let dcid_len = data[offset]; offset += 1; - // There's a +2 in this size check because we need enough space to check the SCID length - if data.len() < (offset + dcid_len as usize + 2) { - return Err(QuicError::PacketTooShort); - } + QuicPacket::validate_length(data, offset + dcid_len as usize)?; let dcid_bytes = &data[offset..offset + dcid_len as usize]; let dcid = QuicPacket::vec_u8_to_hex_string(dcid_bytes); offset += dcid_len as usize; + // Parse SCID + QuicPacket::validate_length(data, offset + 1)?; let scid_len = data[offset]; offset += 1; - if data.len() < (offset + scid_len as usize + 1) { - return Err(QuicError::PacketTooShort); - } + QuicPacket::validate_length(data, offset + scid_len as usize)?; let scid_bytes = &data[offset..offset + scid_len as usize]; let scid = QuicPacket::vec_u8_to_hex_string(scid_bytes); offset += scid_len as usize; @@ -217,15 +223,22 @@ impl QuicPacket { match packet_type { LongHeaderPacketType::Initial => { retry_tag = None; + // Parse token + QuicPacket::validate_length(data, offset + 1)?; let token_len_len = QuicPacket::get_var_len(data[offset])?; + QuicPacket::validate_length(data, offset + token_len_len)?; token_len = Some(QuicPacket::slice_to_u64( &data[offset..offset + token_len_len], )?); offset += token_len_len; + QuicPacket::validate_length(data, offset + token_len.unwrap() as usize)?; let token_bytes = &data[offset..offset + token_len.unwrap() as usize]; token = Some(QuicPacket::vec_u8_to_hex_string(token_bytes)); offset += token_len.unwrap() as usize; + QuicPacket::validate_length(data, offset + 1)?; + // Parse payload length let packet_len_len = QuicPacket::get_var_len(data[offset])?; + QuicPacket::validate_length(data, offset + packet_len_len)?; packet_len = Some(QuicPacket::slice_to_u64( &data[offset..offset + packet_len_len], )?); @@ -234,7 +247,10 @@ impl QuicPacket { token_len = None; token = None; retry_tag = None; + // Parse payload length + QuicPacket::validate_length(data, offset + 1)?; let packet_len_len = QuicPacket::get_var_len(data[offset])?; + QuicPacket::validate_length(data, offset + packet_len_len)?; packet_len = Some(QuicPacket::slice_to_u64( &data[offset..offset + packet_len_len], )?); @@ -242,9 +258,13 @@ impl QuicPacket { LongHeaderPacketType::Retry => { packet_len = None; token_len = Some((data.len() - offset - 16) as u64); + // Parse retry token + QuicPacket::validate_length(data, offset + token_len.unwrap() as usize)?; let token_bytes = &data[offset..offset + token_len.unwrap() as usize]; token = Some(QuicPacket::vec_u8_to_hex_string(token_bytes)); offset += token_len.unwrap() as usize; + // Parse retry tag + QuicPacket::validate_length(data, offset + 16)?; let retry_tag_bytes = &data[offset..offset + 16]; retry_tag = Some(QuicPacket::vec_u8_to_hex_string(retry_tag_bytes)); } @@ -268,13 +288,16 @@ impl QuicPacket { }) } else { // Short Header + offset += 1; let mut max_dcid_len = 20; if data.len() < 1 + max_dcid_len { max_dcid_len = data.len() - 1; } - let dcid_bytes = data[1..1 + max_dcid_len].to_vec(); + // Parse DCID + let dcid_bytes = data[offset..offset + max_dcid_len].to_vec(); + offset += max_dcid_len; // Counts all bytes remaining - let payload_bytes_count = Some((data.len() - 1 - max_dcid_len) as u64); + let payload_bytes_count = Some((data.len() - offset) as u64); Ok(QuicPacket { short_header: Some(QuicShortHeader { dcid: None, From 4f52f0a4dc05fd72f7d0a8e65e3a4b6de53232e1 Mon Sep 17 00:00:00 2001 From: Jackson Sippe Date: Fri, 12 Jul 2024 17:03:11 +0000 Subject: [PATCH 3/3] Create data access function --- core/src/protocols/stream/quic/parser.rs | 112 ++++++++++++----------- 1 file changed, 61 insertions(+), 51 deletions(-) diff --git a/core/src/protocols/stream/quic/parser.rs b/core/src/protocols/stream/quic/parser.rs index ba7c9d85..ab327cbe 100644 --- a/core/src/protocols/stream/quic/parser.rs +++ b/core/src/protocols/stream/quic/parser.rs @@ -128,6 +128,7 @@ pub enum QuicError { UnknowLongHeaderPacketType, NoLongHeader, UnsupportedVarLen, + InvalidDataIndices, } impl QuicPacket { @@ -166,52 +167,51 @@ impl QuicPacket { Ok(result) } - fn validate_length(data: &[u8], acc_len: usize) -> Result<(), QuicError> { - if data.len() < acc_len { - return Err(QuicError::PacketTooShort) + fn access_data(data: &[u8], start: usize, end: usize) -> Result<&[u8], QuicError> { + if end < start { + return Err(QuicError::InvalidDataIndices); } - Ok(()) + if data.len() < end { + return Err(QuicError::PacketTooShort); + } + Ok(&data[start..end]) } /// Parses Quic packet from bytes pub fn parse_from(data: &[u8]) -> Result { let mut offset = 0; - QuicPacket::validate_length(data, offset + 1)?; + let packet_header_byte = QuicPacket::access_data(data, offset, offset + 1)?[0]; + offset += 1; // Check the fixed bit - if (data[offset] & 0x40) == 0 { + if (packet_header_byte & 0x40) == 0 { return Err(QuicError::FixedBitNotSet); } // Check the Header form - if (data[offset] & 0x80) != 0 { + if (packet_header_byte & 0x80) != 0 { // Long Header // Parse packet type - let packet_type = LongHeaderPacketType::from_u8((data[offset] & 0x30) >> 4)?; - let type_specific = data[offset] & 0x0F; // Remainder of information from header byte, Reserved and protected packet number length - offset += 1; - // Parse version - QuicPacket::validate_length(data, offset + 4)?; - let version = ((data[offset] as u32) << 24) - | ((data[offset + 1] as u32) << 16) - | ((data[offset + 2] as u32) << 8) - | (data[offset + 3] as u32); + let packet_type = LongHeaderPacketType::from_u8((packet_header_byte & 0x30) >> 4)?; + let type_specific = packet_header_byte & 0x0F; // Remainder of information from header byte, Reserved and protected packet number length + // Parse version + let version_bytes = QuicPacket::access_data(data, offset, offset + 4)?; + let version = ((version_bytes[0] as u32) << 24) + | ((version_bytes[1] as u32) << 16) + | ((version_bytes[2] as u32) << 8) + | (version_bytes[3] as u32); if QuicVersion::from_u32(version) == QuicVersion::Unknown { return Err(QuicError::UnknownVersion); } offset += 4; // Parse DCID - QuicPacket::validate_length(data, offset + 1)?; - let dcid_len = data[offset]; + let dcid_len = QuicPacket::access_data(data, offset, offset + 1)?[0]; offset += 1; - QuicPacket::validate_length(data, offset + dcid_len as usize)?; - let dcid_bytes = &data[offset..offset + dcid_len as usize]; + let dcid_bytes = QuicPacket::access_data(data, offset, offset + dcid_len as usize)?; let dcid = QuicPacket::vec_u8_to_hex_string(dcid_bytes); offset += dcid_len as usize; // Parse SCID - QuicPacket::validate_length(data, offset + 1)?; - let scid_len = data[offset]; + let scid_len = QuicPacket::access_data(data, offset, offset + 1)?[0]; offset += 1; - QuicPacket::validate_length(data, offset + scid_len as usize)?; - let scid_bytes = &data[offset..offset + scid_len as usize]; + let scid_bytes = QuicPacket::access_data(data, offset, offset + scid_len as usize)?; let scid = QuicPacket::vec_u8_to_hex_string(scid_bytes); offset += scid_len as usize; @@ -219,53 +219,64 @@ impl QuicPacket { let token; let packet_len; let retry_tag; - + // Parse packet type specific fields match packet_type { LongHeaderPacketType::Initial => { retry_tag = None; // Parse token - QuicPacket::validate_length(data, offset + 1)?; - let token_len_len = QuicPacket::get_var_len(data[offset])?; - QuicPacket::validate_length(data, offset + token_len_len)?; - token_len = Some(QuicPacket::slice_to_u64( - &data[offset..offset + token_len_len], - )?); + let token_len_len = QuicPacket::get_var_len( + QuicPacket::access_data(data, offset, offset + 1)?[0], + )?; + token_len = Some(QuicPacket::slice_to_u64(QuicPacket::access_data( + data, + offset, + offset + token_len_len, + )?)?); offset += token_len_len; - QuicPacket::validate_length(data, offset + token_len.unwrap() as usize)?; - let token_bytes = &data[offset..offset + token_len.unwrap() as usize]; + let token_bytes = QuicPacket::access_data( + data, + offset, + offset + token_len.unwrap() as usize, + )?; token = Some(QuicPacket::vec_u8_to_hex_string(token_bytes)); offset += token_len.unwrap() as usize; - QuicPacket::validate_length(data, offset + 1)?; // Parse payload length - let packet_len_len = QuicPacket::get_var_len(data[offset])?; - QuicPacket::validate_length(data, offset + packet_len_len)?; - packet_len = Some(QuicPacket::slice_to_u64( - &data[offset..offset + packet_len_len], - )?); + let packet_len_len = QuicPacket::get_var_len( + QuicPacket::access_data(data, offset, offset + 1)?[0], + )?; + packet_len = Some(QuicPacket::slice_to_u64(QuicPacket::access_data( + data, + offset, + offset + packet_len_len, + )?)?); } LongHeaderPacketType::ZeroRTT | LongHeaderPacketType::Handshake => { token_len = None; token = None; retry_tag = None; // Parse payload length - QuicPacket::validate_length(data, offset + 1)?; - let packet_len_len = QuicPacket::get_var_len(data[offset])?; - QuicPacket::validate_length(data, offset + packet_len_len)?; - packet_len = Some(QuicPacket::slice_to_u64( - &data[offset..offset + packet_len_len], - )?); + let packet_len_len = QuicPacket::get_var_len( + QuicPacket::access_data(data, offset, offset + 1)?[0], + )?; + packet_len = Some(QuicPacket::slice_to_u64(QuicPacket::access_data( + data, + offset, + offset + packet_len_len, + )?)?); } LongHeaderPacketType::Retry => { packet_len = None; token_len = Some((data.len() - offset - 16) as u64); // Parse retry token - QuicPacket::validate_length(data, offset + token_len.unwrap() as usize)?; - let token_bytes = &data[offset..offset + token_len.unwrap() as usize]; + let token_bytes = QuicPacket::access_data( + data, + offset, + offset + token_len.unwrap() as usize, + )?; token = Some(QuicPacket::vec_u8_to_hex_string(token_bytes)); offset += token_len.unwrap() as usize; // Parse retry tag - QuicPacket::validate_length(data, offset + 16)?; - let retry_tag_bytes = &data[offset..offset + 16]; + let retry_tag_bytes = QuicPacket::access_data(data, offset, offset + 16)?; retry_tag = Some(QuicPacket::vec_u8_to_hex_string(retry_tag_bytes)); } } @@ -288,13 +299,12 @@ impl QuicPacket { }) } else { // Short Header - offset += 1; let mut max_dcid_len = 20; if data.len() < 1 + max_dcid_len { max_dcid_len = data.len() - 1; } // Parse DCID - let dcid_bytes = data[offset..offset + max_dcid_len].to_vec(); + let dcid_bytes = QuicPacket::access_data(data, offset, offset + max_dcid_len)?.to_vec(); offset += max_dcid_len; // Counts all bytes remaining let payload_bytes_count = Some((data.len() - offset) as u64);