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