1use compact_str::CompactString;
8use smallvec::SmallVec;
9
10use super::{FieldValue, ParseContext, ParseResult, Protocol};
11use crate::schema::{DataKind, FieldDescriptor};
12
13mod version {
15 pub const VERSION_NEGOTIATION: u32 = 0x00000000;
16 pub const QUIC_V1: u32 = 0x00000001;
17 pub const QUIC_V2: u32 = 0x6b3343cf;
18 pub const DRAFT_29: u32 = 0xff00001d;
19 pub const DRAFT_32: u32 = 0xff000020;
20 pub const DRAFT_34: u32 = 0xff000022;
21}
22
23mod long_packet_type {
25 pub const INITIAL: u8 = 0x0;
26 pub const ZERO_RTT: u8 = 0x1;
27 pub const HANDSHAKE: u8 = 0x2;
28 pub const RETRY: u8 = 0x3;
29}
30
31#[derive(Debug, Clone, Copy)]
33pub struct QuicProtocol;
34
35impl Protocol for QuicProtocol {
36 fn name(&self) -> &'static str {
37 "quic"
38 }
39
40 fn display_name(&self) -> &'static str {
41 "QUIC"
42 }
43
44 fn can_parse(&self, context: &ParseContext) -> Option<u32> {
45 let parent = context.parent_protocol;
47 if parent != Some("udp") {
48 return None;
49 }
50
51 let src_port = context.hint("src_port");
53 let dst_port = context.hint("dst_port");
54
55 match (src_port, dst_port) {
56 (Some(443), _) | (_, Some(443)) => Some(40),
57 (Some(8443), _) | (_, Some(8443)) => Some(40),
58 _ => None,
59 }
60 }
61
62 fn parse<'a>(&self, data: &'a [u8], _context: &ParseContext) -> ParseResult<'a> {
63 let mut fields = SmallVec::new();
64
65 if data.is_empty() {
66 return ParseResult::error("QUIC packet empty".to_string(), data);
67 }
68
69 let first_byte = data[0];
70
71 let is_long_header = (first_byte & 0x80) != 0;
73
74 if is_long_header {
75 fields.push(("header_form", FieldValue::Str("long")));
76 parse_long_header(data, &mut fields)
77 } else {
78 fields.push(("header_form", FieldValue::Str("short")));
79 parse_short_header(data, &mut fields)
80 }
81 }
82
83 fn schema_fields(&self) -> Vec<FieldDescriptor> {
84 vec![
85 FieldDescriptor::new("quic.header_form", DataKind::String).set_nullable(true),
86 FieldDescriptor::new("quic.long_packet_type", DataKind::String).set_nullable(true),
87 FieldDescriptor::new("quic.version", DataKind::UInt32).set_nullable(true),
88 FieldDescriptor::new("quic.version_name", DataKind::String).set_nullable(true),
89 FieldDescriptor::new("quic.dcid_length", DataKind::UInt8).set_nullable(true),
90 FieldDescriptor::new("quic.dcid", DataKind::String).set_nullable(true),
91 FieldDescriptor::new("quic.scid_length", DataKind::UInt8).set_nullable(true),
92 FieldDescriptor::new("quic.scid", DataKind::String).set_nullable(true),
93 FieldDescriptor::new("quic.token_length", DataKind::UInt32).set_nullable(true),
94 FieldDescriptor::new("quic.packet_length", DataKind::UInt32).set_nullable(true),
95 FieldDescriptor::new("quic.spin_bit", DataKind::Bool).set_nullable(true),
96 FieldDescriptor::new("quic.key_phase", DataKind::Bool).set_nullable(true),
97 FieldDescriptor::new("quic.sni", DataKind::String).set_nullable(true),
98 ]
99 }
100
101 fn child_protocols(&self) -> &[&'static str] {
102 &[]
103 }
104
105 fn dependencies(&self) -> &'static [&'static str] {
106 &["udp"]
107 }
108}
109
110fn parse_long_header<'a>(
112 data: &'a [u8],
113 fields: &mut SmallVec<[(&'static str, FieldValue<'a>); 16]>,
114) -> ParseResult<'a> {
115 if data.len() < 6 {
129 return ParseResult::error("QUIC long header too short".to_string(), data);
130 }
131
132 let first_byte = data[0];
133
134 let fixed_bit = (first_byte & 0x40) != 0;
136 if !fixed_bit {
137 return ParseResult::error("QUIC fixed bit not set".to_string(), data);
138 }
139
140 let packet_type = (first_byte >> 4) & 0x03;
142 let packet_type_name = match packet_type {
143 long_packet_type::INITIAL => "Initial",
144 long_packet_type::ZERO_RTT => "0-RTT",
145 long_packet_type::HANDSHAKE => "Handshake",
146 long_packet_type::RETRY => "Retry",
147 _ => "Unknown",
148 };
149 fields.push(("long_packet_type", FieldValue::Str(packet_type_name)));
150
151 let quic_version = u32::from_be_bytes([data[1], data[2], data[3], data[4]]);
153 fields.push(("version", FieldValue::UInt32(quic_version)));
154 fields.push((
155 "version_name",
156 FieldValue::OwnedString(CompactString::new(format_version(quic_version))),
157 ));
158
159 let dcid_len = data[5] as usize;
161 fields.push(("dcid_length", FieldValue::UInt8(dcid_len as u8)));
162
163 let mut offset = 6;
164
165 if offset + dcid_len > data.len() {
167 return ParseResult::partial(
168 fields.clone(),
169 &data[offset..],
170 "QUIC DCID truncated".to_string(),
171 );
172 }
173 if dcid_len > 0 {
174 let dcid = &data[offset..offset + dcid_len];
175 fields.push((
176 "dcid",
177 FieldValue::OwnedString(CompactString::new(hex_encode(dcid))),
178 ));
179 }
180 offset += dcid_len;
181
182 if offset >= data.len() {
184 return ParseResult::partial(
185 fields.clone(),
186 &data[offset..],
187 "QUIC SCID length missing".to_string(),
188 );
189 }
190 let scid_len = data[offset] as usize;
191 fields.push(("scid_length", FieldValue::UInt8(scid_len as u8)));
192 offset += 1;
193
194 if offset + scid_len > data.len() {
196 return ParseResult::partial(
197 fields.clone(),
198 &data[offset..],
199 "QUIC SCID truncated".to_string(),
200 );
201 }
202 if scid_len > 0 {
203 let scid = &data[offset..offset + scid_len];
204 fields.push((
205 "scid",
206 FieldValue::OwnedString(CompactString::new(hex_encode(scid))),
207 ));
208 }
209 offset += scid_len;
210
211 if packet_type == long_packet_type::INITIAL && offset < data.len() {
213 parse_initial_packet(&data[offset..], fields);
214 }
215
216 ParseResult::success(fields.clone(), &[], SmallVec::new())
217}
218
219fn parse_short_header<'a>(
221 data: &'a [u8],
222 fields: &mut SmallVec<[(&'static str, FieldValue<'a>); 16]>,
223) -> ParseResult<'a> {
224 if data.is_empty() {
234 return ParseResult::error("QUIC short header empty".to_string(), data);
235 }
236
237 let first_byte = data[0];
238
239 let fixed_bit = (first_byte & 0x40) != 0;
241 if !fixed_bit {
242 return ParseResult::error("QUIC fixed bit not set".to_string(), data);
243 }
244
245 let spin_bit = (first_byte & 0x20) != 0;
247 fields.push(("spin_bit", FieldValue::Bool(spin_bit)));
248
249 let key_phase = (first_byte & 0x04) != 0;
251 fields.push(("key_phase", FieldValue::Bool(key_phase)));
252
253 ParseResult::success(fields.clone(), &[], SmallVec::new())
257}
258
259fn parse_initial_packet<'a>(
261 data: &[u8],
262 fields: &mut SmallVec<[(&'static str, FieldValue<'a>); 16]>,
263) {
264 if data.is_empty() {
272 return;
273 }
274
275 let (token_len, consumed) = match parse_varint(data) {
277 Some(v) => v,
278 None => return,
279 };
280
281 fields.push(("token_length", FieldValue::UInt32(token_len as u32)));
282
283 let mut offset = consumed;
284
285 if offset + token_len > data.len() {
287 return;
288 }
289 offset += token_len;
290
291 if offset >= data.len() {
293 return;
294 }
295 let (packet_len, consumed) = match parse_varint(&data[offset..]) {
296 Some(v) => v,
297 None => return,
298 };
299
300 fields.push(("packet_length", FieldValue::UInt32(packet_len as u32)));
301 offset += consumed;
302
303 if offset < data.len() {
310 try_extract_sni(&data[offset..], fields);
311 }
312}
313
314fn try_extract_sni<'a>(data: &[u8], fields: &mut SmallVec<[(&'static str, FieldValue<'a>); 16]>) {
317 for i in 0..data.len().saturating_sub(10) {
322 if data[i] == 0x00 && data[i + 1] == 0x00 {
324 if i + 4 > data.len() {
326 continue;
327 }
328 let ext_len = u16::from_be_bytes([data[i + 2], data[i + 3]]) as usize;
329 if ext_len == 0 || ext_len > 256 || i + 4 + ext_len > data.len() {
330 continue;
331 }
332
333 if i + 6 > data.len() {
335 continue;
336 }
337 let list_len = u16::from_be_bytes([data[i + 4], data[i + 5]]) as usize;
338 if list_len == 0 || list_len > ext_len {
339 continue;
340 }
341
342 if i + 7 > data.len() || data[i + 6] != 0x00 {
344 continue;
345 }
346
347 if i + 9 > data.len() {
349 continue;
350 }
351 let name_len = u16::from_be_bytes([data[i + 7], data[i + 8]]) as usize;
352 if name_len == 0 || name_len > 255 || i + 9 + name_len > data.len() {
353 continue;
354 }
355
356 if let Ok(hostname) = std::str::from_utf8(&data[i + 9..i + 9 + name_len]) {
358 if hostname
360 .chars()
361 .all(|c| c.is_ascii_alphanumeric() || c == '.' || c == '-')
362 && hostname.contains('.')
363 {
364 fields.push(("sni", FieldValue::OwnedString(CompactString::new(hostname))));
365 return;
366 }
367 }
368 }
369 }
370}
371
372fn parse_varint(data: &[u8]) -> Option<(usize, usize)> {
375 if data.is_empty() {
376 return None;
377 }
378
379 let first = data[0];
380 let prefix = first >> 6;
381
382 match prefix {
383 0 => {
384 Some(((first & 0x3F) as usize, 1))
386 }
387 1 => {
388 if data.len() < 2 {
390 return None;
391 }
392 let value = (((first & 0x3F) as usize) << 8) | (data[1] as usize);
393 Some((value, 2))
394 }
395 2 => {
396 if data.len() < 4 {
398 return None;
399 }
400 let value = (((first & 0x3F) as usize) << 24)
401 | ((data[1] as usize) << 16)
402 | ((data[2] as usize) << 8)
403 | (data[3] as usize);
404 Some((value, 4))
405 }
406 3 => {
407 if data.len() < 8 {
409 return None;
410 }
411 let value = (((first & 0x3F) as usize) << 56)
412 | ((data[1] as usize) << 48)
413 | ((data[2] as usize) << 40)
414 | ((data[3] as usize) << 32)
415 | ((data[4] as usize) << 24)
416 | ((data[5] as usize) << 16)
417 | ((data[6] as usize) << 8)
418 | (data[7] as usize);
419 Some((value, 8))
420 }
421 _ => None,
422 }
423}
424
425fn format_version(ver: u32) -> String {
427 match ver {
428 version::VERSION_NEGOTIATION => "Version Negotiation".to_string(),
429 version::QUIC_V1 => "QUIC v1".to_string(),
430 version::QUIC_V2 => "QUIC v2".to_string(),
431 version::DRAFT_29 => "Draft-29".to_string(),
432 version::DRAFT_32 => "Draft-32".to_string(),
433 version::DRAFT_34 => "Draft-34".to_string(),
434 v if (v & 0xff000000) == 0xff000000 => {
435 format!("Draft-{}", v & 0xff)
436 }
437 _ => format!("0x{ver:08x}"),
438 }
439}
440
441fn hex_encode(data: &[u8]) -> String {
443 data.iter().map(|b| format!("{b:02x}")).collect()
444}
445
446#[cfg(test)]
447mod tests {
448 use super::*;
449
450 fn create_quic_initial(dcid: &[u8], scid: &[u8], version: u32) -> Vec<u8> {
452 let mut packet = Vec::new();
453
454 packet.push(0xC0 | 0x00);
456
457 packet.extend_from_slice(&version.to_be_bytes());
459
460 packet.push(dcid.len() as u8);
462 packet.extend_from_slice(dcid);
463
464 packet.push(scid.len() as u8);
466 packet.extend_from_slice(scid);
467
468 packet.push(0x00);
470
471 packet.push(0x40); packet.push(0x64); packet.extend(std::iter::repeat(0u8).take(100));
477
478 packet
479 }
480
481 fn create_quic_handshake(dcid: &[u8], scid: &[u8]) -> Vec<u8> {
483 let mut packet = Vec::new();
484
485 packet.push(0xC0 | 0x20);
487
488 packet.extend_from_slice(&version::QUIC_V1.to_be_bytes());
490
491 packet.push(dcid.len() as u8);
493 packet.extend_from_slice(dcid);
494
495 packet.push(scid.len() as u8);
497 packet.extend_from_slice(scid);
498
499 packet.push(0x40);
501 packet.push(0x32); packet.extend(std::iter::repeat(0u8).take(50));
505
506 packet
507 }
508
509 fn create_quic_short(dcid: &[u8], spin: bool, key_phase: bool) -> Vec<u8> {
511 let mut packet = Vec::new();
512
513 let mut first = 0x40;
515 if spin {
516 first |= 0x20;
517 }
518 if key_phase {
519 first |= 0x04;
520 }
521 packet.push(first);
522
523 packet.extend_from_slice(dcid);
525
526 packet.extend(std::iter::repeat(0u8).take(20));
528
529 packet
530 }
531
532 #[test]
533 fn test_can_parse_quic() {
534 let parser = QuicProtocol;
535
536 let ctx1 = ParseContext::new(1);
538 assert!(parser.can_parse(&ctx1).is_none());
539
540 let mut ctx2 = ParseContext::new(1);
542 ctx2.parent_protocol = Some("udp");
543 ctx2.insert_hint("dst_port", 80);
544 assert!(parser.can_parse(&ctx2).is_none());
545
546 let mut ctx3 = ParseContext::new(1);
548 ctx3.parent_protocol = Some("udp");
549 ctx3.insert_hint("dst_port", 443);
550 assert!(parser.can_parse(&ctx3).is_some());
551 }
552
553 #[test]
554 fn test_quic_detection_long_header() {
555 let dcid = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
556 let scid = [0x11, 0x12, 0x13, 0x14];
557 let packet = create_quic_initial(&dcid, &scid, version::QUIC_V1);
558
559 let parser = QuicProtocol;
560 let mut context = ParseContext::new(1);
561 context.parent_protocol = Some("udp");
562 context.insert_hint("dst_port", 443);
563
564 let result = parser.parse(&packet, &context);
565
566 assert!(result.is_ok());
567 assert_eq!(result.get("header_form"), Some(&FieldValue::Str("long")));
568 }
569
570 #[test]
571 fn test_quic_detection_short_header() {
572 let dcid = [0x01, 0x02, 0x03, 0x04];
573 let packet = create_quic_short(&dcid, false, false);
574
575 let parser = QuicProtocol;
576 let mut context = ParseContext::new(1);
577 context.parent_protocol = Some("udp");
578 context.insert_hint("dst_port", 443);
579
580 let result = parser.parse(&packet, &context);
581
582 assert!(result.is_ok());
583 assert_eq!(result.get("header_form"), Some(&FieldValue::Str("short")));
584 }
585
586 #[test]
587 fn test_quic_version_parsing() {
588 let dcid = [0x01, 0x02, 0x03, 0x04];
589 let scid = [0x11, 0x12];
590 let packet = create_quic_initial(&dcid, &scid, version::QUIC_V1);
591
592 let parser = QuicProtocol;
593 let context = ParseContext::new(1);
594
595 let result = parser.parse(&packet, &context);
596
597 assert!(result.is_ok());
598 assert_eq!(
599 result.get("version"),
600 Some(&FieldValue::UInt32(version::QUIC_V1))
601 );
602 assert_eq!(
603 result.get("version_name"),
604 Some(&FieldValue::OwnedString(CompactString::new("QUIC v1")))
605 );
606 }
607
608 #[test]
609 fn test_quic_initial_packet_parsing() {
610 let dcid = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
611 let scid = [0x11, 0x12, 0x13, 0x14];
612 let packet = create_quic_initial(&dcid, &scid, version::QUIC_V1);
613
614 let parser = QuicProtocol;
615 let context = ParseContext::new(1);
616
617 let result = parser.parse(&packet, &context);
618
619 assert!(result.is_ok());
620 assert_eq!(
621 result.get("long_packet_type"),
622 Some(&FieldValue::Str("Initial"))
623 );
624 }
625
626 #[test]
627 fn test_quic_handshake_packet_parsing() {
628 let dcid = [0x01, 0x02, 0x03, 0x04];
629 let scid = [0x11, 0x12];
630 let packet = create_quic_handshake(&dcid, &scid);
631
632 let parser = QuicProtocol;
633 let context = ParseContext::new(1);
634
635 let result = parser.parse(&packet, &context);
636
637 assert!(result.is_ok());
638 assert_eq!(
639 result.get("long_packet_type"),
640 Some(&FieldValue::Str("Handshake"))
641 );
642 }
643
644 #[test]
645 fn test_quic_dcid_extraction() {
646 let dcid = [0xaa, 0xbb, 0xcc, 0xdd];
647 let scid = [0x11, 0x22];
648 let packet = create_quic_initial(&dcid, &scid, version::QUIC_V1);
649
650 let parser = QuicProtocol;
651 let context = ParseContext::new(1);
652
653 let result = parser.parse(&packet, &context);
654
655 assert!(result.is_ok());
656 assert_eq!(result.get("dcid_length"), Some(&FieldValue::UInt8(4)));
657 assert_eq!(
658 result.get("dcid"),
659 Some(&FieldValue::OwnedString(CompactString::new("aabbccdd")))
660 );
661 }
662
663 #[test]
664 fn test_quic_scid_extraction() {
665 let dcid = [0x01, 0x02];
666 let scid = [0xee, 0xff, 0x00, 0x11];
667 let packet = create_quic_initial(&dcid, &scid, version::QUIC_V1);
668
669 let parser = QuicProtocol;
670 let context = ParseContext::new(1);
671
672 let result = parser.parse(&packet, &context);
673
674 assert!(result.is_ok());
675 assert_eq!(result.get("scid_length"), Some(&FieldValue::UInt8(4)));
676 assert_eq!(
677 result.get("scid"),
678 Some(&FieldValue::OwnedString(CompactString::new("eeff0011")))
679 );
680 }
681
682 #[test]
683 fn test_quic_version_negotiation_detection() {
684 let dcid = [0x01, 0x02, 0x03, 0x04];
685 let scid = [0x11, 0x12];
686 let packet = create_quic_initial(&dcid, &scid, version::VERSION_NEGOTIATION);
687
688 let parser = QuicProtocol;
689 let context = ParseContext::new(1);
690
691 let result = parser.parse(&packet, &context);
692
693 assert!(result.is_ok());
694 assert_eq!(
695 result.get("version"),
696 Some(&FieldValue::UInt32(version::VERSION_NEGOTIATION))
697 );
698 assert_eq!(
699 result.get("version_name"),
700 Some(&FieldValue::OwnedString(CompactString::new(
701 "Version Negotiation"
702 )))
703 );
704 }
705
706 #[test]
707 fn test_quic_unknown_version_handling() {
708 let dcid = [0x01, 0x02];
709 let scid = [0x11, 0x12];
710 let unknown_version = 0xdeadbeef;
711 let packet = create_quic_initial(&dcid, &scid, unknown_version);
712
713 let parser = QuicProtocol;
714 let context = ParseContext::new(1);
715
716 let result = parser.parse(&packet, &context);
717
718 assert!(result.is_ok());
719 assert_eq!(
720 result.get("version"),
721 Some(&FieldValue::UInt32(unknown_version))
722 );
723 assert_eq!(
724 result.get("version_name"),
725 Some(&FieldValue::OwnedString(CompactString::new("0xdeadbeef")))
726 );
727 }
728
729 #[test]
730 fn test_quic_short_header_spin_bit() {
731 let dcid = [0x01, 0x02, 0x03, 0x04];
732 let packet = create_quic_short(&dcid, true, false);
733
734 let parser = QuicProtocol;
735 let context = ParseContext::new(1);
736
737 let result = parser.parse(&packet, &context);
738
739 assert!(result.is_ok());
740 assert_eq!(result.get("spin_bit"), Some(&FieldValue::Bool(true)));
741 assert_eq!(result.get("key_phase"), Some(&FieldValue::Bool(false)));
742 }
743
744 #[test]
745 fn test_quic_short_header_key_phase() {
746 let dcid = [0x01, 0x02, 0x03, 0x04];
747 let packet = create_quic_short(&dcid, false, true);
748
749 let parser = QuicProtocol;
750 let context = ParseContext::new(1);
751
752 let result = parser.parse(&packet, &context);
753
754 assert!(result.is_ok());
755 assert_eq!(result.get("spin_bit"), Some(&FieldValue::Bool(false)));
756 assert_eq!(result.get("key_phase"), Some(&FieldValue::Bool(true)));
757 }
758
759 #[test]
760 fn test_quic_schema_fields() {
761 let parser = QuicProtocol;
762 let fields = parser.schema_fields();
763
764 assert!(!fields.is_empty());
765
766 let field_names: Vec<&str> = fields.iter().map(|f| f.name).collect();
767 assert!(field_names.contains(&"quic.header_form"));
768 assert!(field_names.contains(&"quic.version"));
769 assert!(field_names.contains(&"quic.dcid"));
770 assert!(field_names.contains(&"quic.scid"));
771 assert!(field_names.contains(&"quic.sni"));
772 }
773
774 #[test]
775 fn test_varint_parsing() {
776 assert_eq!(parse_varint(&[0x25]), Some((0x25, 1)));
778 assert_eq!(parse_varint(&[0x00]), Some((0, 1)));
779
780 assert_eq!(parse_varint(&[0x40, 0x19]), Some((0x19, 2)));
782 assert_eq!(parse_varint(&[0x7f, 0xff]), Some((0x3fff, 2)));
783
784 assert_eq!(parse_varint(&[0x80, 0x00, 0x00, 0x01]), Some((0x01, 4)));
786
787 assert_eq!(parse_varint(&[]), None);
789 }
790
791 #[test]
792 fn test_version_formatting() {
793 assert_eq!(format_version(version::QUIC_V1), "QUIC v1");
794 assert_eq!(format_version(version::QUIC_V2), "QUIC v2");
795 assert_eq!(
796 format_version(version::VERSION_NEGOTIATION),
797 "Version Negotiation"
798 );
799 assert_eq!(format_version(0xff00001d), "Draft-29");
800 assert_eq!(format_version(0x12345678), "0x12345678");
801 }
802}