Skip to main content

stackforge_core/layer/quic/
mod.rs

1//! QUIC protocol layer implementation (RFC 9000).
2//!
3//! This module implements the "Lazy Zero-Copy View" architecture for QUIC:
4//! the [`QuicLayer`] holds a [`LayerIndex`] and reads fields directly from
5//! the underlying packet buffer on demand.
6//!
7//! # Sub-modules
8//!
9//! - [`varint`]  — QUIC variable-length integer encoding/decoding.
10//! - [`frames`]  — QUIC frame types and best-effort frame parser.
11//! - [`headers`] — QUIC long-header and short-header structures.
12//! - [`builder`] — [`QuicBuilder`] for constructing QUIC packets.
13
14pub mod builder;
15pub mod frames;
16pub mod headers;
17pub mod varint;
18
19pub use builder::{QuicBuilder, QuicInitialBuilder};
20pub use frames::{FrameType, QuicFrame, parse_frames};
21pub use headers::{QuicLongHeader, QuicPacketType, QuicShortHeader, packet_type};
22pub use varint::{decode as varint_decode, encode as varint_encode};
23
24use crate::layer::field::{FieldDesc, FieldError, FieldType, FieldValue};
25use crate::layer::{Layer, LayerIndex, LayerKind};
26
27/// Minimum size of a QUIC header (just the first byte).
28pub const QUIC_MIN_HEADER_LEN: usize = 1;
29
30/// Static field descriptors for the QUIC layer.
31///
32/// Fields common to all QUIC packet types plus long-header specific fields.
33/// Variable-length fields (conn IDs, length, `packet_number`) use placeholder
34/// offsets because their actual positions must be computed dynamically in
35/// `get_field()`.
36pub static QUIC_FIELDS: &[FieldDesc] = &[
37    FieldDesc::new("header_form", 0, 1, FieldType::U8),
38    FieldDesc::new("fixed_bit", 0, 1, FieldType::U8),
39    FieldDesc::new("packet_type", 0, 1, FieldType::U8),
40    FieldDesc::new("long_packet_type", 0, 1, FieldType::U8),
41    FieldDesc::new("packet_number_len", 0, 1, FieldType::U8),
42    FieldDesc::new("version", 1, 4, FieldType::U32),
43    // Variable-length fields (offsets are placeholders; actual access is dynamic)
44    FieldDesc::new("dst_conn_id_len", 5, 1, FieldType::U8),
45    FieldDesc::new("src_conn_id_len", 5, 1, FieldType::U8),
46    FieldDesc::new("dst_conn_id", 6, 0, FieldType::Bytes),
47    FieldDesc::new("src_conn_id", 6, 0, FieldType::Bytes),
48    FieldDesc::new("length", 6, 8, FieldType::U64),
49    FieldDesc::new("packet_number", 6, 4, FieldType::U32),
50];
51
52/// QUIC protocol layer — a lightweight view into a raw packet buffer.
53///
54/// Follows the same "Lazy Zero-Copy View" pattern as all other layers in
55/// stackforge-core: no field values are copied at parse time; they are read
56/// directly from the buffer when accessed.
57#[derive(Debug, Clone)]
58pub struct QuicLayer {
59    pub index: LayerIndex,
60}
61
62impl QuicLayer {
63    /// Create a new `QuicLayer` covering `buf[start..end]`.
64    #[must_use]
65    pub fn new(start: usize, end: usize) -> Self {
66        Self {
67            index: LayerIndex::new(LayerKind::Quic, start, end),
68        }
69    }
70
71    /// Create a `QuicLayer` from an existing `LayerIndex`.
72    #[must_use]
73    pub fn from_index(index: LayerIndex) -> Self {
74        Self { index }
75    }
76
77    // -------------------------------------------------------------------------
78    // Field readers
79    // -------------------------------------------------------------------------
80
81    /// Returns `true` if bit 7 of the first byte is set (long header).
82    #[must_use]
83    pub fn is_long_header(&self, buf: &[u8]) -> bool {
84        let slice = self.index.slice(buf);
85        !slice.is_empty() && slice[0] & 0x80 != 0
86    }
87
88    /// Returns the logical packet type, or `None` if the buffer is too short.
89    #[must_use]
90    pub fn packet_type(&self, buf: &[u8]) -> Option<QuicPacketType> {
91        let slice = self.index.slice(buf);
92        if slice.is_empty() {
93            return None;
94        }
95        let first = slice[0];
96        let version = if slice.len() >= 5 {
97            u32::from_be_bytes([slice[1], slice[2], slice[3], slice[4]])
98        } else {
99            0
100        };
101        Some(headers::packet_type(first, version))
102    }
103
104    /// Returns the QUIC version (bytes 1-4) for long-header packets, or `None`
105    /// if this is a short-header packet or the buffer is too short.
106    #[must_use]
107    pub fn version(&self, buf: &[u8]) -> Option<u32> {
108        if !self.is_long_header(buf) {
109            return None;
110        }
111        let slice = self.index.slice(buf);
112        if slice.len() < 5 {
113            return None;
114        }
115        Some(u32::from_be_bytes([slice[1], slice[2], slice[3], slice[4]]))
116    }
117
118    /// Returns a human-readable summary string.
119    #[must_use]
120    pub fn summary(&self, buf: &[u8]) -> String {
121        match self.packet_type(buf) {
122            Some(pt) => format!("QUIC {}", pt.name()),
123            None => "QUIC".to_string(),
124        }
125    }
126
127    /// Returns the header length in bytes.
128    ///
129    /// For long headers this parses the connection-ID lengths to compute the
130    /// actual header size.  For short headers (where the connection-ID length
131    /// is negotiated out-of-band) we return 1 as a safe minimum.
132    #[must_use]
133    pub fn header_len(&self, buf: &[u8]) -> usize {
134        let slice = self.index.slice(buf);
135        if slice.is_empty() {
136            return QUIC_MIN_HEADER_LEN;
137        }
138
139        if self.is_long_header(buf) {
140            match QuicLongHeader::parse(slice) {
141                Some(hdr) => hdr.header_len,
142                None => QUIC_MIN_HEADER_LEN,
143            }
144        } else {
145            // Short header: 1-byte first byte, variable connection ID (unknown here).
146            QUIC_MIN_HEADER_LEN
147        }
148    }
149
150    /// Returns the static field names exposed by this layer.
151    #[must_use]
152    pub fn field_names() -> &'static [&'static str] {
153        &[
154            "header_form",
155            "fixed_bit",
156            "packet_type",
157            "long_packet_type",
158            "packet_number_len",
159            "version",
160            "dst_conn_id_len",
161            "src_conn_id_len",
162            "dst_conn_id",
163            "src_conn_id",
164            "length",
165            "packet_number",
166        ]
167    }
168
169    /// Read a field value by name from the underlying buffer.
170    #[must_use]
171    pub fn get_field(&self, buf: &[u8], name: &str) -> Option<Result<FieldValue, FieldError>> {
172        let slice = self.index.slice(buf);
173        match name {
174            "header_form" => {
175                if slice.is_empty() {
176                    return Some(Err(FieldError::BufferTooShort {
177                        offset: self.index.start,
178                        need: 1,
179                        have: 0,
180                    }));
181                }
182                let v = (slice[0] >> 7) & 0x01;
183                Some(Ok(FieldValue::U8(v)))
184            },
185            "fixed_bit" => {
186                if slice.is_empty() {
187                    return Some(Err(FieldError::BufferTooShort {
188                        offset: self.index.start,
189                        need: 1,
190                        have: 0,
191                    }));
192                }
193                let v = (slice[0] >> 6) & 0x01;
194                Some(Ok(FieldValue::U8(v)))
195            },
196            "packet_type" => {
197                if slice.is_empty() {
198                    return Some(Err(FieldError::BufferTooShort {
199                        offset: self.index.start,
200                        need: 1,
201                        have: 0,
202                    }));
203                }
204                // For long headers: bits 5-4.  For short headers: 0 (no type field).
205                let v = if slice[0] & 0x80 != 0 {
206                    (slice[0] & 0x30) >> 4
207                } else {
208                    0
209                };
210                Some(Ok(FieldValue::U8(v)))
211            },
212            "version" => {
213                if slice.len() < 5 {
214                    return Some(Err(FieldError::BufferTooShort {
215                        offset: self.index.start + 1,
216                        need: 4,
217                        have: slice.len().saturating_sub(1),
218                    }));
219                }
220                let v = u32::from_be_bytes([slice[1], slice[2], slice[3], slice[4]]);
221                Some(Ok(FieldValue::U32(v)))
222            },
223            "long_packet_type" => {
224                if slice.is_empty() {
225                    return Some(Err(FieldError::BufferTooShort {
226                        offset: self.index.start,
227                        need: 1,
228                        have: 0,
229                    }));
230                }
231                // bits 5-4 of byte 0 (only meaningful for long-header packets)
232                let v = (slice[0] & 0x30) >> 4;
233                Some(Ok(FieldValue::U8(v)))
234            },
235            "packet_number_len" => {
236                if slice.is_empty() {
237                    return Some(Err(FieldError::BufferTooShort {
238                        offset: self.index.start,
239                        need: 1,
240                        have: 0,
241                    }));
242                }
243                // bits 1-0 of byte 0: encoded packet number length minus 1
244                let v = slice[0] & 0x03;
245                Some(Ok(FieldValue::U8(v)))
246            },
247            "dst_conn_id_len" => {
248                if slice.len() < 6 {
249                    return Some(Err(FieldError::BufferTooShort {
250                        offset: self.index.start + 5,
251                        need: 1,
252                        have: slice.len().saturating_sub(5),
253                    }));
254                }
255                if slice[0] & 0x80 == 0 {
256                    // Short header — no explicit DCIL byte
257                    return Some(Ok(FieldValue::U8(0)));
258                }
259                Some(Ok(FieldValue::U8(slice[5])))
260            },
261            "src_conn_id_len" => {
262                if slice[0] & 0x80 == 0 {
263                    // Short header — no SCID
264                    return Some(Ok(FieldValue::U8(0)));
265                }
266                match QuicLongHeader::parse(slice) {
267                    Some(hdr) => Some(Ok(FieldValue::U8(hdr.src_conn_id.len() as u8))),
268                    None => Some(Err(FieldError::BufferTooShort {
269                        offset: self.index.start,
270                        need: 7,
271                        have: slice.len(),
272                    })),
273                }
274            },
275            "dst_conn_id" => {
276                if slice[0] & 0x80 == 0 {
277                    // Short header — DCID not explicitly encoded
278                    return Some(Ok(FieldValue::Bytes(vec![])));
279                }
280                match QuicLongHeader::parse(slice) {
281                    Some(hdr) => Some(Ok(FieldValue::Bytes(hdr.dst_conn_id))),
282                    None => Some(Err(FieldError::BufferTooShort {
283                        offset: self.index.start,
284                        need: 7,
285                        have: slice.len(),
286                    })),
287                }
288            },
289            "src_conn_id" => {
290                if slice[0] & 0x80 == 0 {
291                    // Short header — no SCID
292                    return Some(Ok(FieldValue::Bytes(vec![])));
293                }
294                match QuicLongHeader::parse(slice) {
295                    Some(hdr) => Some(Ok(FieldValue::Bytes(hdr.src_conn_id))),
296                    None => Some(Err(FieldError::BufferTooShort {
297                        offset: self.index.start,
298                        need: 7,
299                        have: slice.len(),
300                    })),
301                }
302            },
303            "length" | "packet_number" => {
304                if slice[0] & 0x80 == 0 {
305                    // Short header — no length/packet_number fields in long-header sense
306                    return None;
307                }
308                let hdr = match QuicLongHeader::parse(slice) {
309                    Some(h) => h,
310                    None => {
311                        return Some(Err(FieldError::BufferTooShort {
312                            offset: self.index.start,
313                            need: 7,
314                            have: slice.len(),
315                        }));
316                    },
317                };
318                let mut pos = hdr.header_len; // after SCID
319
320                // For Initial packets only: skip token_length varint + token bytes.
321                if hdr.packet_type == headers::QuicPacketType::Initial {
322                    match varint::decode(&slice[pos..]) {
323                        Some((token_len, token_varint_bytes)) => {
324                            pos += token_varint_bytes + token_len as usize;
325                        },
326                        None => {
327                            return Some(Err(FieldError::BufferTooShort {
328                                offset: self.index.start + pos,
329                                need: 1,
330                                have: slice.len().saturating_sub(pos),
331                            }));
332                        },
333                    }
334                }
335
336                // Length varint
337                let (length_val, length_varint_bytes) = match varint::decode(&slice[pos..]) {
338                    Some(v) => v,
339                    None => {
340                        return Some(Err(FieldError::BufferTooShort {
341                            offset: self.index.start + pos,
342                            need: 1,
343                            have: slice.len().saturating_sub(pos),
344                        }));
345                    },
346                };
347
348                if name == "length" {
349                    return Some(Ok(FieldValue::U64(length_val)));
350                }
351
352                // packet_number: comes after the length varint
353                pos += length_varint_bytes;
354                let pn_len = (slice[0] & 0x03) as usize + 1;
355                if pos + pn_len > slice.len() {
356                    return Some(Err(FieldError::BufferTooShort {
357                        offset: self.index.start + pos,
358                        need: pn_len,
359                        have: slice.len().saturating_sub(pos),
360                    }));
361                }
362                let pn_bytes = &slice[pos..pos + pn_len];
363                let pn: u32 = match pn_len {
364                    1 => u32::from(pn_bytes[0]),
365                    2 => u32::from(u16::from_be_bytes([pn_bytes[0], pn_bytes[1]])),
366                    3 => {
367                        (u32::from(pn_bytes[0]) << 16)
368                            | (u32::from(pn_bytes[1]) << 8)
369                            | u32::from(pn_bytes[2])
370                    },
371                    4 => u32::from_be_bytes([pn_bytes[0], pn_bytes[1], pn_bytes[2], pn_bytes[3]]),
372                    _ => unreachable!(),
373                };
374                Some(Ok(FieldValue::U32(pn)))
375            },
376            _ => None,
377        }
378    }
379
380    /// Write a field value by name into the underlying (mutable) buffer.
381    pub fn set_field(
382        &self,
383        buf: &mut [u8],
384        name: &str,
385        value: FieldValue,
386    ) -> Option<Result<(), FieldError>> {
387        let start = self.index.start;
388        match name {
389            "header_form" => {
390                let v = match value {
391                    FieldValue::U8(v) => v,
392                    other => {
393                        return Some(Err(FieldError::InvalidValue(format!(
394                            "header_form: expected U8, got {other:?}"
395                        ))));
396                    },
397                };
398                if buf.len() <= start {
399                    return Some(Err(FieldError::BufferTooShort {
400                        offset: start,
401                        need: 1,
402                        have: 0,
403                    }));
404                }
405                if v != 0 {
406                    buf[start] |= 0x80;
407                } else {
408                    buf[start] &= !0x80;
409                }
410                Some(Ok(()))
411            },
412            "fixed_bit" => {
413                let v = match value {
414                    FieldValue::U8(v) => v,
415                    other => {
416                        return Some(Err(FieldError::InvalidValue(format!(
417                            "fixed_bit: expected U8, got {other:?}"
418                        ))));
419                    },
420                };
421                if buf.len() <= start {
422                    return Some(Err(FieldError::BufferTooShort {
423                        offset: start,
424                        need: 1,
425                        have: 0,
426                    }));
427                }
428                if v != 0 {
429                    buf[start] |= 0x40;
430                } else {
431                    buf[start] &= !0x40;
432                }
433                Some(Ok(()))
434            },
435            "packet_type" => {
436                let v = match value {
437                    FieldValue::U8(v) => v,
438                    other => {
439                        return Some(Err(FieldError::InvalidValue(format!(
440                            "packet_type: expected U8, got {other:?}"
441                        ))));
442                    },
443                };
444                if buf.len() <= start {
445                    return Some(Err(FieldError::BufferTooShort {
446                        offset: start,
447                        need: 1,
448                        have: 0,
449                    }));
450                }
451                // Only meaningful for long headers (bits 5-4).
452                buf[start] = (buf[start] & !0x30) | ((v & 0x03) << 4);
453                Some(Ok(()))
454            },
455            "version" => {
456                let v = match value {
457                    FieldValue::U32(v) => v,
458                    other => {
459                        return Some(Err(FieldError::InvalidValue(format!(
460                            "version: expected U32, got {other:?}"
461                        ))));
462                    },
463                };
464                if buf.len() < start + 5 {
465                    return Some(Err(FieldError::BufferTooShort {
466                        offset: start + 1,
467                        need: 4,
468                        have: buf.len().saturating_sub(start + 1),
469                    }));
470                }
471                buf[start + 1..start + 5].copy_from_slice(&v.to_be_bytes());
472                Some(Ok(()))
473            },
474            _ => None,
475        }
476    }
477}
478
479impl Layer for QuicLayer {
480    fn kind(&self) -> LayerKind {
481        LayerKind::Quic
482    }
483
484    fn summary(&self, data: &[u8]) -> String {
485        self.summary(data)
486    }
487
488    fn header_len(&self, data: &[u8]) -> usize {
489        self.header_len(data)
490    }
491
492    fn field_names(&self) -> &'static [&'static str] {
493        QuicLayer::field_names()
494    }
495
496    fn hashret(&self, _data: &[u8]) -> Vec<u8> {
497        vec![]
498    }
499}
500
501#[cfg(test)]
502mod tests {
503    use super::*;
504
505    /// Decode a hex string into bytes (panics on invalid input).
506    fn from_hex(s: &str) -> Vec<u8> {
507        (0..s.len())
508            .step_by(2)
509            .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
510            .collect()
511    }
512
513    fn make_initial_buf() -> Vec<u8> {
514        QuicBuilder::initial()
515            .version(1)
516            .dst_conn_id(vec![0x01, 0x02])
517            .src_conn_id(vec![])
518            .payload(vec![])
519            .build()
520    }
521
522    #[test]
523    fn test_quic_layer_new() {
524        let buf = make_initial_buf();
525        let layer = QuicLayer::new(0, buf.len());
526        assert!(layer.is_long_header(&buf));
527        assert_eq!(layer.packet_type(&buf), Some(QuicPacketType::Initial));
528        assert_eq!(layer.version(&buf), Some(1));
529    }
530
531    #[test]
532    fn test_summary_initial() {
533        let buf = make_initial_buf();
534        let layer = QuicLayer::new(0, buf.len());
535        let s = layer.summary(&buf);
536        assert!(s.contains("Initial"), "summary: {}", s);
537    }
538
539    #[test]
540    fn test_get_field_header_form() {
541        let buf = make_initial_buf();
542        let layer = QuicLayer::new(0, buf.len());
543        let val = layer.get_field(&buf, "header_form").unwrap().unwrap();
544        assert_eq!(val, FieldValue::U8(1));
545    }
546
547    #[test]
548    fn test_get_field_fixed_bit() {
549        let buf = make_initial_buf();
550        let layer = QuicLayer::new(0, buf.len());
551        let val = layer.get_field(&buf, "fixed_bit").unwrap().unwrap();
552        assert_eq!(val, FieldValue::U8(1));
553    }
554
555    #[test]
556    fn test_get_field_packet_type() {
557        let buf = make_initial_buf();
558        let layer = QuicLayer::new(0, buf.len());
559        let val = layer.get_field(&buf, "packet_type").unwrap().unwrap();
560        // Initial = 0x00
561        assert_eq!(val, FieldValue::U8(0));
562    }
563
564    #[test]
565    fn test_get_field_version() {
566        let buf = make_initial_buf();
567        let layer = QuicLayer::new(0, buf.len());
568        let val = layer.get_field(&buf, "version").unwrap().unwrap();
569        assert_eq!(val, FieldValue::U32(1));
570    }
571
572    #[test]
573    fn test_get_field_unknown() {
574        let buf = make_initial_buf();
575        let layer = QuicLayer::new(0, buf.len());
576        assert!(layer.get_field(&buf, "nonexistent").is_none());
577    }
578
579    #[test]
580    fn test_set_field_version() {
581        let mut buf = make_initial_buf();
582        let layer = QuicLayer::new(0, buf.len());
583        layer
584            .set_field(&mut buf, "version", FieldValue::U32(2))
585            .unwrap()
586            .unwrap();
587        assert_eq!(layer.version(&buf), Some(2));
588    }
589
590    #[test]
591    fn test_one_rtt_is_short_header() {
592        let buf = QuicBuilder::one_rtt().payload(vec![0x01]).build();
593        let layer = QuicLayer::new(0, buf.len());
594        assert!(!layer.is_long_header(&buf));
595        assert_eq!(layer.packet_type(&buf), Some(QuicPacketType::OneRtt));
596        assert_eq!(layer.version(&buf), None);
597    }
598
599    #[test]
600    fn test_layer_kind() {
601        let buf = make_initial_buf();
602        let layer = QuicLayer::new(0, buf.len());
603        assert_eq!(layer.kind(), LayerKind::Quic);
604    }
605
606    #[test]
607    fn test_header_len_long() {
608        let buf = make_initial_buf();
609        let layer = QuicLayer::new(0, buf.len());
610        // Long header with 2-byte DCID, 0-byte SCID, token varint, length varint
611        // header_len should be at minimum 6 (first+ver+DCIL+DCID+SCIL+SCID)
612        let h = layer.header_len(&buf);
613        assert!(h >= 6, "header_len={}", h);
614    }
615
616    // -------------------------------------------------------------------------
617    // New field tests
618    // -------------------------------------------------------------------------
619
620    #[test]
621    fn test_get_field_long_packet_type_initial() {
622        let buf = make_initial_buf();
623        let layer = QuicLayer::new(0, buf.len());
624        let val = layer.get_field(&buf, "long_packet_type").unwrap().unwrap();
625        assert_eq!(val, FieldValue::U8(0)); // 0 = Initial
626    }
627
628    #[test]
629    fn test_get_field_long_packet_type_handshake() {
630        let buf = QuicBuilder::handshake().dst_conn_id(vec![0x01]).build();
631        let layer = QuicLayer::new(0, buf.len());
632        let val = layer.get_field(&buf, "long_packet_type").unwrap().unwrap();
633        assert_eq!(val, FieldValue::U8(2)); // 2 = Handshake
634    }
635
636    #[test]
637    fn test_get_field_packet_number_len_zero() {
638        // Default packet_number=0 => pn_len=1, encoded as (pn_len-1)=0
639        let buf = make_initial_buf();
640        let layer = QuicLayer::new(0, buf.len());
641        let val = layer.get_field(&buf, "packet_number_len").unwrap().unwrap();
642        assert_eq!(val, FieldValue::U8(0));
643    }
644
645    #[test]
646    fn test_get_field_packet_number_len_one() {
647        // packet_number=0xFFFF => pn_len=2, encoded as 1
648        let buf = QuicBuilder::initial().packet_number(0xFFFF).build();
649        let layer = QuicLayer::new(0, buf.len());
650        let val = layer.get_field(&buf, "packet_number_len").unwrap().unwrap();
651        assert_eq!(val, FieldValue::U8(1));
652    }
653
654    #[test]
655    fn test_get_field_dst_conn_id_len() {
656        let buf = make_initial_buf();
657        let layer = QuicLayer::new(0, buf.len());
658        let val = layer.get_field(&buf, "dst_conn_id_len").unwrap().unwrap();
659        assert_eq!(val, FieldValue::U8(2)); // 2-byte DCID
660    }
661
662    #[test]
663    fn test_get_field_src_conn_id_len_zero() {
664        let buf = make_initial_buf();
665        let layer = QuicLayer::new(0, buf.len());
666        let val = layer.get_field(&buf, "src_conn_id_len").unwrap().unwrap();
667        assert_eq!(val, FieldValue::U8(0)); // 0-byte SCID
668    }
669
670    #[test]
671    fn test_get_field_src_conn_id_len_nonzero() {
672        let buf = QuicBuilder::initial()
673            .dst_conn_id(vec![0xAA])
674            .src_conn_id(vec![0xBB, 0xCC])
675            .build();
676        let layer = QuicLayer::new(0, buf.len());
677        let val = layer.get_field(&buf, "src_conn_id_len").unwrap().unwrap();
678        assert_eq!(val, FieldValue::U8(2));
679    }
680
681    #[test]
682    fn test_get_field_dst_conn_id() {
683        let dcid = vec![0x01, 0x02];
684        let buf = QuicBuilder::initial().dst_conn_id(dcid.clone()).build();
685        let layer = QuicLayer::new(0, buf.len());
686        let val = layer.get_field(&buf, "dst_conn_id").unwrap().unwrap();
687        assert_eq!(val, FieldValue::Bytes(dcid));
688    }
689
690    #[test]
691    fn test_get_field_src_conn_id() {
692        let scid = vec![0xAA, 0xBB];
693        let buf = QuicBuilder::initial().src_conn_id(scid.clone()).build();
694        let layer = QuicLayer::new(0, buf.len());
695        let val = layer.get_field(&buf, "src_conn_id").unwrap().unwrap();
696        assert_eq!(val, FieldValue::Bytes(scid));
697    }
698
699    #[test]
700    fn test_get_field_length_initial() {
701        // No payload, packet_number=0 (1 byte) => length = 1
702        let buf = QuicBuilder::initial()
703            .dst_conn_id(vec![0x01])
704            .payload(vec![])
705            .build();
706        let layer = QuicLayer::new(0, buf.len());
707        let val = layer.get_field(&buf, "length").unwrap().unwrap();
708        assert_eq!(val, FieldValue::U64(1)); // 1 byte pn + 0 payload
709    }
710
711    #[test]
712    fn test_get_field_packet_number_initial_zero() {
713        let buf = QuicBuilder::initial()
714            .dst_conn_id(vec![0x01])
715            .packet_number(0)
716            .build();
717        let layer = QuicLayer::new(0, buf.len());
718        let val = layer.get_field(&buf, "packet_number").unwrap().unwrap();
719        assert_eq!(val, FieldValue::U32(0));
720    }
721
722    #[test]
723    fn test_get_field_packet_number_initial_255() {
724        let buf = QuicBuilder::initial()
725            .dst_conn_id(vec![0x01])
726            .packet_number(0xFF)
727            .build();
728        let layer = QuicLayer::new(0, buf.len());
729        let val = layer.get_field(&buf, "packet_number").unwrap().unwrap();
730        assert_eq!(val, FieldValue::U32(0xFF));
731    }
732
733    #[test]
734    fn test_get_field_packet_number_initial_65535() {
735        let buf = QuicBuilder::initial()
736            .dst_conn_id(vec![0x01])
737            .packet_number(0xFFFF)
738            .build();
739        let layer = QuicLayer::new(0, buf.len());
740        let val = layer.get_field(&buf, "packet_number").unwrap().unwrap();
741        assert_eq!(val, FieldValue::U32(0xFFFF));
742    }
743
744    #[test]
745    fn test_get_field_packet_number_handshake() {
746        let buf = QuicBuilder::handshake()
747            .dst_conn_id(vec![0x01])
748            .packet_number(1)
749            .build();
750        let layer = QuicLayer::new(0, buf.len());
751        let val = layer.get_field(&buf, "packet_number").unwrap().unwrap();
752        assert_eq!(val, FieldValue::U32(1));
753    }
754
755    #[test]
756    fn test_uts_client_initial_fields() {
757        // From tests/uts/quic.uts — Client Initial Packet
758        let data = from_hex(
759            "c00000000108000102030405060705635f636964004103001c36a7ed78716be9711ba498b7ed868443bb2e0c514d4d848eadcc7a00d25ce9f9afa483978088de836be68c0b32a24595d7813ea5414a9199329a6d9f7f760dd8bb249bf3f53d9a77fbb7b395b8d66d7879a51fe59ef9601f79998eb3568e1fdc789f640acab3858a82ef2930fa5ce14b5b9ea0bdb29f4572da85aa3def39b7efafffa074b9267070d50b5d07842e49bba3bc787ff295d6ae3b514305f102afe5a047b3fb4c99eb92a274d244d60492c0e2e6e212cef0f9e3f62efd0955e71c768aa6bb3cd80bbb3755c8b7ebee32712f40f2245119487021b4b84e1565e3ca31967ac8604d4032170dec280aeefa095d08b3b7241ef6646a6c86e5c62ce08be099",
760        );
761        let layer = QuicLayer::new(0, data.len());
762
763        let dcid = layer.get_field(&data, "dst_conn_id").unwrap().unwrap();
764        assert_eq!(
765            dcid,
766            FieldValue::Bytes(vec![0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07])
767        );
768
769        let scid = layer.get_field(&data, "src_conn_id").unwrap().unwrap();
770        assert_eq!(scid, FieldValue::Bytes(b"c_cid".to_vec()));
771
772        let lpt = layer.get_field(&data, "long_packet_type").unwrap().unwrap();
773        assert_eq!(lpt, FieldValue::U8(0)); // Initial
774
775        let version = layer.get_field(&data, "version").unwrap().unwrap();
776        assert_eq!(version, FieldValue::U32(1));
777
778        let length = layer.get_field(&data, "length").unwrap().unwrap();
779        assert_eq!(length, FieldValue::U64(259));
780
781        let pn = layer.get_field(&data, "packet_number").unwrap().unwrap();
782        assert_eq!(pn, FieldValue::U32(0));
783    }
784
785    #[test]
786    fn test_uts_server_handshake_fields() {
787        // From tests/uts/quic.uts — Server Handshake Packet
788        let data = from_hex(
789            "e00000000105635f63696405735f63696440cf014420f919681c3f0f102a30f5e647a3399abf54bc8e80453134996ba33099056242f3b8e662bbfce42f3ef2b6ba87159147489f8479e849284e983fd905320a62fc7d67e9587797096ca60101d0b2685d8747811178133ad9172b7ff8ea83fd81a814bae27b953a97d57ebff4b4710dba8df82a6b49d7d7fa3d8179cbdb8683d4bfa832645401e5a56a76535f71c6fb3e616c241bb1f43bc147c296f591402997ed49aa0c55e31721d03e14114af2dc458ae03944de5126fe08d66a6ef3ba2ed1025f98fea6d6024998184687dc06",
790        );
791        let layer = QuicLayer::new(0, data.len());
792
793        let lpt = layer.get_field(&data, "long_packet_type").unwrap().unwrap();
794        assert_eq!(lpt, FieldValue::U8(2)); // Handshake
795
796        let dcid = layer.get_field(&data, "dst_conn_id").unwrap().unwrap();
797        assert_eq!(dcid, FieldValue::Bytes(b"c_cid".to_vec()));
798
799        let scid = layer.get_field(&data, "src_conn_id").unwrap().unwrap();
800        assert_eq!(scid, FieldValue::Bytes(b"s_cid".to_vec()));
801
802        let pn = layer.get_field(&data, "packet_number").unwrap().unwrap();
803        assert_eq!(pn, FieldValue::U32(1));
804    }
805
806    #[test]
807    fn test_uts_build_packet_fields() {
808        // From tests/uts/quic.uts — QUIC_Initial(DstConnID=..., SrcConnID=..., PacketNumber=0xFF)
809        let data: &[u8] = b"\xc0\x00\x00\x00\x01\x0fp\xa2\x8e@\x96\xc5}\xd0\xff\xb6\xc3\xd8\x1b\xcaR\x03\xf7\x10Q\x00\x00\xff";
810        let layer = QuicLayer::new(0, data.len());
811
812        let dcil = layer.get_field(data, "dst_conn_id_len").unwrap().unwrap();
813        assert_eq!(dcil, FieldValue::U8(15));
814
815        let scil = layer.get_field(data, "src_conn_id_len").unwrap().unwrap();
816        assert_eq!(scil, FieldValue::U8(3));
817
818        let pnl = layer.get_field(data, "packet_number_len").unwrap().unwrap();
819        assert_eq!(pnl, FieldValue::U8(0)); // 0 means 1 byte
820
821        let pn = layer.get_field(data, "packet_number").unwrap().unwrap();
822        assert_eq!(pn, FieldValue::U32(0xFF));
823    }
824}