iris_core/protocols/stream/quic/
parser.rs1use crate::protocols::stream::quic::crypto::calc_init_keys;
6use crate::protocols::stream::quic::frame::QuicFrame;
7use crate::protocols::stream::quic::header::{
8 LongHeaderPacketType, QuicLongHeader, QuicShortHeader,
9};
10use crate::protocols::stream::quic::{QuicError, QuicPacket};
11use crate::protocols::stream::tls::Tls;
12use crate::protocols::stream::{
13 ConnParsable, L4Pdu, ParseResult, ParsingState, ProbeResult, Session, SessionData,
14};
15use byteorder::{BigEndian, ByteOrder};
16use std::collections::HashSet;
17use tls_parser::parse_tls_message_handshake;
18
19use super::QuicConn;
20
21#[derive(Debug)]
22pub struct QuicParser {
23 sessions: Vec<QuicConn>,
28}
29
30impl Default for QuicParser {
31 fn default() -> Self {
32 QuicParser {
33 sessions: vec![QuicConn::new()],
34 }
35 }
36}
37
38impl ConnParsable for QuicParser {
39 fn parse(&mut self, pdu: &L4Pdu) -> ParseResult {
40 let offset = pdu.offset();
41 let length = pdu.length();
42 if length == 0 {
43 return ParseResult::Skipped;
44 }
45
46 if let Ok(data) = (pdu.mbuf_ref()).get_data_slice(offset, length) {
47 if !self.sessions.is_empty() {
48 return self.sessions[0].parse_packet(data, pdu.dir);
49 }
50 ParseResult::Skipped
51 } else {
52 log::warn!("Malformed packet on parse");
53 ParseResult::Skipped
54 }
55 }
56
57 fn probe(&self, pdu: &L4Pdu) -> ProbeResult {
58 if pdu.length() < 5 {
59 return ProbeResult::Unsure;
60 }
61
62 let offset = pdu.offset();
63 let length = pdu.length();
64
65 if let Ok(data) = (pdu.mbuf).get_data_slice(offset, length) {
66 if (data[0] & 0x40) == 0 {
68 return ProbeResult::NotForUs;
69 }
70
71 if (data[0] & 0x80) != 0 {
72 if data.len() < 6 {
74 return ProbeResult::Unsure;
75 }
76
77 let version = ((data[1] as u32) << 24)
79 | ((data[2] as u32) << 16)
80 | ((data[3] as u32) << 8)
81 | (data[4] as u32);
82 match QuicVersion::from_u32(version) {
83 QuicVersion::Unknown => ProbeResult::NotForUs,
84 _ => ProbeResult::Certain,
85 }
86 } else {
87 ProbeResult::Unsure
88 }
89 } else {
90 log::warn!("Malformed packet");
91 ProbeResult::Error
92 }
93 }
94
95 fn remove_session(&mut self, session_id: usize) -> Option<Session> {
96 self.sessions.pop().map(|quic| Session {
97 data: SessionData::Quic(Box::new(quic)),
98 id: session_id,
99 })
100 }
101
102 fn drain_sessions(&mut self) -> Vec<Session> {
103 self.sessions
104 .drain(..)
105 .map(|quic| Session {
106 data: SessionData::Quic(Box::new(quic)),
107 id: 0,
108 })
109 .collect()
110 }
111
112 fn session_parsed_state(&self) -> ParsingState {
113 ParsingState::Parsing
114 }
115
116 fn body_offset(&mut self) -> Option<usize> {
118 None
119 }
120}
121
122#[derive(Debug, PartialEq, Eq, Hash)]
124#[repr(u32)]
125pub enum QuicVersion {
126 ReservedNegotiation = 0x00000000,
127 Rfc9000 = 0x00000001, Rfc9369 = 0x6b3343cf, Draft27 = 0xff00001b, Draft28 = 0xff00001c, Draft29 = 0xff00001d, Mvfst27 = 0xfaceb002, Unknown,
134}
135
136impl QuicVersion {
137 pub fn from_u32(version: u32) -> Self {
138 match version {
139 0x00000000 => QuicVersion::ReservedNegotiation,
140 0x00000001 => QuicVersion::Rfc9000,
141 0x6b3343cf => QuicVersion::Rfc9369,
142 0xff00001b => QuicVersion::Draft27,
143 0xff00001c => QuicVersion::Draft28,
144 0xff00001d => QuicVersion::Draft29,
145 0xfaceb002 => QuicVersion::Mvfst27,
146 _ => QuicVersion::Unknown,
147 }
148 }
149}
150
151impl QuicPacket {
152 pub fn vec_u8_to_hex_string(vec: &[u8]) -> String {
154 vec.iter()
155 .map(|&byte| format!("{:02x}", byte))
156 .collect::<Vec<String>>()
157 .join("")
158 }
159
160 pub fn get_var_len(a: u8) -> Result<usize, QuicError> {
163 let two_msb = a >> 6;
164 match two_msb {
165 0b00 => Ok(1),
166 0b01 => Ok(2),
167 0b10 => Ok(4),
168 0b11 => Ok(8),
169 _ => Err(QuicError::UnsupportedVarLen),
170 }
171 }
172
173 pub fn slice_to_u64(data: &[u8]) -> Result<u64, QuicError> {
175 if data.len() > 8 {
176 return Err(QuicError::UnsupportedVarLen);
177 }
178
179 let mut result: u64 = 0;
180 for &byte in data {
181 result = (result << 8) | u64::from(byte);
182 }
183 result &= !(0b11 << ((data.len() * 8) - 2)); Ok(result)
185 }
186
187 pub fn access_data(data: &[u8], start: usize, end: usize) -> Result<&[u8], QuicError> {
188 if end < start {
189 return Err(QuicError::InvalidDataIndices);
190 }
191 if data.len() < end {
192 return Err(QuicError::PacketTooShort);
193 }
194 Ok(&data[start..end])
195 }
196
197 pub fn parse_from(
199 conn: &mut QuicConn,
200 data: &[u8],
201 mut offset: usize,
202 dir: bool,
203 ) -> Result<(QuicPacket, usize), QuicError> {
204 let packet_header_byte = QuicPacket::access_data(data, offset, offset + 1)?[0];
205 offset += 1;
206 if (packet_header_byte & 0x40) == 0 {
208 return Err(QuicError::FixedBitNotSet);
209 }
210 if (packet_header_byte & 0x80) != 0 {
212 let packet_type = LongHeaderPacketType::from_u8((packet_header_byte & 0x30) >> 4)?;
215 let type_specific = packet_header_byte & 0x0F; let version_bytes = QuicPacket::access_data(data, offset, offset + 4)?;
218 let version = ((version_bytes[0] as u32) << 24)
219 | ((version_bytes[1] as u32) << 16)
220 | ((version_bytes[2] as u32) << 8)
221 | (version_bytes[3] as u32);
222 if QuicVersion::from_u32(version) == QuicVersion::Unknown {
223 return Err(QuicError::UnknownVersion);
224 }
225 offset += 4;
226 let dcid_len = QuicPacket::access_data(data, offset, offset + 1)?[0];
228 offset += 1;
229 let dcid_bytes = QuicPacket::access_data(data, offset, offset + dcid_len as usize)?;
230 let dcid = QuicPacket::vec_u8_to_hex_string(dcid_bytes);
231 if dcid_len > 0 && !conn.cids.contains(&dcid) {
232 conn.cids.insert(dcid.clone());
233 }
234 offset += dcid_len as usize;
235 let scid_len = QuicPacket::access_data(data, offset, offset + 1)?[0];
237 offset += 1;
238 let scid_bytes = QuicPacket::access_data(data, offset, offset + scid_len as usize)?;
239 let scid = QuicPacket::vec_u8_to_hex_string(scid_bytes);
240 if scid_len > 0 && !conn.cids.contains(&scid) {
241 conn.cids.insert(scid.clone());
242 }
243 offset += scid_len as usize;
244
245 let token_len;
246 let token;
247 let packet_len;
248 let retry_tag;
249 let decrypted_payload;
250 match packet_type {
252 LongHeaderPacketType::Initial => {
253 retry_tag = None;
254 let token_len_len = QuicPacket::get_var_len(
256 QuicPacket::access_data(data, offset, offset + 1)?[0],
257 )?;
258 let token_len_bytes =
259 QuicPacket::access_data(data, offset, offset + token_len_len)?;
260 token_len = Some(QuicPacket::slice_to_u64(token_len_bytes)?);
261 offset += token_len_len;
262 let token_bytes = QuicPacket::access_data(
263 data,
264 offset,
265 offset + token_len.unwrap() as usize,
266 )?;
267 token = Some(QuicPacket::vec_u8_to_hex_string(token_bytes));
268 offset += token_len.unwrap() as usize;
269 let packet_len_len = QuicPacket::get_var_len(
271 QuicPacket::access_data(data, offset, offset + 1)?[0],
272 )?;
273 let packet_len_bytes =
274 QuicPacket::access_data(data, offset, offset + packet_len_len)?;
275 packet_len = Some(QuicPacket::slice_to_u64(packet_len_bytes)?);
276 offset += packet_len_len;
277 if conn.client_opener.is_none() {
278 let [client_opener, server_opener] = calc_init_keys(dcid_bytes, version)?;
280 conn.client_opener = Some(client_opener);
281 conn.server_opener = Some(server_opener);
282 }
283 let sample_len = conn.client_opener.as_ref().unwrap().sample_len();
285 let hp_sample =
286 QuicPacket::access_data(data, offset + 4, offset + 4 + sample_len)?;
287 let mask = if dir {
288 conn.client_opener.as_ref().unwrap().new_mask(hp_sample)?
289 } else {
290 conn.server_opener.as_ref().unwrap().new_mask(hp_sample)?
291 };
292 let unprotected_header = packet_header_byte ^ (mask[0] & 0b00001111);
294 if (unprotected_header >> 2) & 0b00000011 != 0 {
295 return Err(QuicError::FailedHeaderProtection);
296 }
297 let packet_num_len = ((unprotected_header & 0b00000011) + 1) as usize;
299 let packet_number_bytes =
300 QuicPacket::access_data(data, offset, offset + packet_num_len)?;
301 let mut packet_number = vec![0; 4 - packet_num_len];
302 for i in 0..packet_num_len {
303 packet_number.push(packet_number_bytes[i] ^ mask[i + 1]);
304 }
305
306 let initial_packet_number_bytes = &packet_number[4 - packet_num_len..];
307 let packet_number_int = BigEndian::read_i32(&packet_number);
308 offset += packet_num_len;
309 let tag_len = conn.client_opener.as_ref().unwrap().alg().tag_len();
311 if (packet_len.unwrap() as usize) < (tag_len + packet_num_len) {
312 return Err(QuicError::PacketTooShort);
313 }
314 let cipher_text_len = packet_len.unwrap() as usize - tag_len - packet_num_len;
315 let mut encrypted_payload =
316 QuicPacket::access_data(data, offset, offset + cipher_text_len)?.to_vec();
317 offset += cipher_text_len;
318 let tag = QuicPacket::access_data(data, offset, offset + tag_len)?;
320 offset += tag_len;
321 let mut ad = Vec::new();
323 ad.append(&mut [unprotected_header].to_vec());
324 ad.append(&mut version_bytes.to_vec());
325 ad.append(&mut [dcid_len].to_vec());
326 ad.append(&mut dcid_bytes.to_vec());
327 ad.append(&mut [scid_len].to_vec());
328 ad.append(&mut scid_bytes.to_vec());
329 ad.append(&mut token_len_bytes.to_vec());
330 ad.append(&mut token_bytes.to_vec());
331 ad.append(&mut packet_len_bytes.to_vec());
332 ad.append(&mut initial_packet_number_bytes.to_vec());
333 if dir {
335 decrypted_payload =
336 Some(conn.client_opener.as_ref().unwrap().open_with_u64_counter(
337 packet_number_int as u64,
338 &ad,
339 &mut encrypted_payload,
340 tag,
341 )?);
342 } else {
343 decrypted_payload =
344 Some(conn.server_opener.as_ref().unwrap().open_with_u64_counter(
345 packet_number_int as u64,
346 &ad,
347 &mut encrypted_payload,
348 tag,
349 )?);
350 }
351 }
352 LongHeaderPacketType::ZeroRTT | LongHeaderPacketType::Handshake => {
353 token_len = None;
354 token = None;
355 retry_tag = None;
356 decrypted_payload = None;
357 let packet_len_len = QuicPacket::get_var_len(
359 QuicPacket::access_data(data, offset, offset + 1)?[0],
360 )?;
361 packet_len = Some(QuicPacket::slice_to_u64(QuicPacket::access_data(
362 data,
363 offset,
364 offset + packet_len_len,
365 )?)?);
366 offset += packet_len_len;
367 offset += packet_len.unwrap() as usize;
368 }
369 LongHeaderPacketType::Retry => {
370 packet_len = None;
371 decrypted_payload = None;
372 if data.len() > (offset + 16) {
373 token_len = Some((data.len() - offset - 16) as u64);
374 } else {
375 return Err(QuicError::PacketTooShort);
376 }
377 let token_bytes = QuicPacket::access_data(
379 data,
380 offset,
381 offset + token_len.unwrap() as usize,
382 )?;
383 token = Some(QuicPacket::vec_u8_to_hex_string(token_bytes));
384 offset += token_len.unwrap() as usize;
385 let retry_tag_bytes = QuicPacket::access_data(data, offset, offset + 16)?;
387 retry_tag = Some(QuicPacket::vec_u8_to_hex_string(retry_tag_bytes));
388 offset += 16;
389 }
390 }
391
392 let mut frames: Option<Vec<QuicFrame>> = None;
393 let crypto_buffer: &mut Vec<u8> = if dir {
395 conn.client_buffer.as_mut()
396 } else {
397 conn.server_buffer.as_mut()
398 };
399 if let Some(frame_bytes) = decrypted_payload {
401 let (q_frames, mut crypto_bytes) =
404 QuicFrame::parse_frames(&frame_bytes, crypto_buffer.len())?;
405 frames = Some(q_frames);
406 if !crypto_bytes.is_empty() {
407 crypto_buffer.append(&mut crypto_bytes);
408 match parse_tls_message_handshake(crypto_buffer) {
413 Ok((_, msg)) => {
414 conn.tls.parse_message_level(&msg, dir);
415 crypto_buffer.clear();
416 }
417 Err(_) => return Err(QuicError::TlsParseFail),
418 }
419 }
420 }
421
422 Ok((
423 QuicPacket {
424 payload_bytes_count: packet_len,
425 short_header: None,
426 long_header: Some(QuicLongHeader {
427 packet_type,
428 type_specific,
429 version,
430 dcid_len,
431 dcid,
432 scid_len,
433 scid,
434 token_len,
435 token,
436 retry_tag,
437 }),
438 frames,
439 },
440 offset,
441 ))
442 } else {
443 let mut dcid_len = 20;
445 if data.len() < 1 + dcid_len {
446 dcid_len = data.len() - 1;
447 }
448 let dcid_hex = QuicPacket::vec_u8_to_hex_string(QuicPacket::access_data(
450 data,
451 offset,
452 offset + dcid_len,
453 )?);
454 let mut dcid = None;
455 for cid in &conn.cids {
456 if dcid_hex.starts_with(cid) {
457 dcid_len = cid.chars().count() / 2;
458 dcid = Some(cid.clone());
459 }
460 }
461 offset += dcid_len;
462 let payload_bytes_count = (data.len() - offset) as u64;
464 offset += payload_bytes_count as usize;
465 Ok((
466 QuicPacket {
467 short_header: Some(QuicShortHeader { dcid }),
468 long_header: None,
469 payload_bytes_count: Some(payload_bytes_count),
470 frames: None,
471 },
472 offset,
473 ))
474 }
475 }
476}
477
478impl QuicConn {
479 pub(crate) fn new() -> QuicConn {
480 QuicConn {
481 packets: Vec::new(),
482 cids: HashSet::new(),
483 tls: Tls::new(),
484 client_opener: None,
485 server_opener: None,
486 client_buffer: Vec::new(),
487 server_buffer: Vec::new(),
488 }
489 }
490
491 fn parse_packet(&mut self, data: &[u8], direction: bool) -> ParseResult {
492 let mut offset = 0;
493 while data.len() > offset {
497 if let Ok((quic, off)) = QuicPacket::parse_from(self, data, offset, direction) {
498 self.packets.push(quic);
499 offset = off;
500 } else {
501 return ParseResult::Skipped;
502 }
503 }
504 if self
505 .packets
506 .last()
507 .is_some_and(|p| p.short_header.is_some())
508 {
509 return ParseResult::HeadersDone(0);
510 }
511 ParseResult::Continue(0)
512 }
513}