Skip to main content

stackforge_core/layer/tcp/
header.rs

1//! TCP header layer implementation.
2//!
3//! Provides zero-copy access to TCP header fields per RFC 793.
4//!
5//! # TCP Header Format
6//!
7//! ```text
8//!  0                   1                   2                   3
9//!  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
10//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
11//! |          Source Port          |       Destination Port        |
12//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
13//! |                        Sequence Number                        |
14//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
15//! |                    Acknowledgment Number                      |
16//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17//! |  Data |     |N|C|E|U|A|P|R|S|F|                               |
18//! | Offset| Res |S|W|C|R|C|S|S|Y|I|            Window             |
19//! |       |     | |R|E|G|K|H|T|N|N|                               |
20//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21//! |           Checksum            |         Urgent Pointer        |
22//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
23//! |                    Options                    |    Padding    |
24//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
25//! |                             data                              |
26//! +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
27//! ```
28
29use crate::layer::field::{Field, FieldDesc, FieldError, FieldType, FieldValue};
30use crate::layer::{Layer, LayerIndex, LayerKind};
31
32use super::flags::TcpFlags;
33use super::options::{TcpOptions, parse_options};
34use super::services;
35
36/// Minimum TCP header length (no options).
37pub const TCP_MIN_HEADER_LEN: usize = 20;
38
39/// Maximum TCP header length (with maximum options).
40pub const TCP_MAX_HEADER_LEN: usize = 60;
41
42/// Field offsets within the TCP header.
43pub mod offsets {
44    /// Source port (16 bits)
45    pub const SRC_PORT: usize = 0;
46    /// Destination port (16 bits)
47    pub const DST_PORT: usize = 2;
48    /// Sequence number (32 bits)
49    pub const SEQ: usize = 4;
50    /// Acknowledgment number (32 bits)
51    pub const ACK: usize = 8;
52    /// Data offset (4 bits) + Reserved (3 bits) + NS flag (1 bit)
53    pub const DATA_OFFSET: usize = 12;
54    /// Flags byte (8 bits: CWR, ECE, URG, ACK, PSH, RST, SYN, FIN)
55    pub const FLAGS: usize = 13;
56    /// Window size (16 bits)
57    pub const WINDOW: usize = 14;
58    /// Checksum (16 bits)
59    pub const CHECKSUM: usize = 16;
60    /// Urgent pointer (16 bits)
61    pub const URG_PTR: usize = 18;
62    /// Options start (if data offset > 5)
63    pub const OPTIONS: usize = 20;
64}
65
66/// Field descriptors for dynamic access.
67pub static FIELDS: &[FieldDesc] = &[
68    FieldDesc::new("sport", offsets::SRC_PORT, 2, FieldType::U16),
69    FieldDesc::new("dport", offsets::DST_PORT, 2, FieldType::U16),
70    FieldDesc::new("seq", offsets::SEQ, 4, FieldType::U32),
71    FieldDesc::new("ack", offsets::ACK, 4, FieldType::U32),
72    FieldDesc::new("dataofs", offsets::DATA_OFFSET, 1, FieldType::U8),
73    FieldDesc::new("reserved", offsets::DATA_OFFSET, 1, FieldType::U8),
74    FieldDesc::new("flags", offsets::FLAGS, 1, FieldType::U8),
75    FieldDesc::new("window", offsets::WINDOW, 2, FieldType::U16),
76    FieldDesc::new("chksum", offsets::CHECKSUM, 2, FieldType::U16),
77    FieldDesc::new("urgptr", offsets::URG_PTR, 2, FieldType::U16),
78];
79
80/// A view into a TCP packet header.
81#[derive(Debug, Clone)]
82pub struct TcpLayer {
83    pub index: LayerIndex,
84}
85
86impl TcpLayer {
87    /// Create a new TCP layer view with specified bounds.
88    #[inline]
89    #[must_use]
90    pub const fn new(start: usize, end: usize) -> Self {
91        Self {
92            index: LayerIndex::new(LayerKind::Tcp, start, end),
93        }
94    }
95
96    /// Create a layer at offset 0 with minimum header length.
97    #[inline]
98    #[must_use]
99    pub const fn at_start() -> Self {
100        Self::new(0, TCP_MIN_HEADER_LEN)
101    }
102
103    /// Create a layer at the specified offset with minimum header length.
104    #[inline]
105    #[must_use]
106    pub const fn at_offset(offset: usize) -> Self {
107        Self::new(offset, offset + TCP_MIN_HEADER_LEN)
108    }
109
110    /// Create a layer at offset, calculating actual header length from data offset.
111    pub fn at_offset_dynamic(buf: &[u8], offset: usize) -> Result<Self, FieldError> {
112        if buf.len() < offset + TCP_MIN_HEADER_LEN {
113            return Err(FieldError::BufferTooShort {
114                offset,
115                need: TCP_MIN_HEADER_LEN,
116                have: buf.len().saturating_sub(offset),
117            });
118        }
119
120        let data_offset = ((buf[offset + offsets::DATA_OFFSET] >> 4) & 0x0F) as usize;
121        let header_len = data_offset * 4;
122
123        if header_len < TCP_MIN_HEADER_LEN {
124            return Err(FieldError::InvalidValue(format!(
125                "Data offset {data_offset} is less than minimum (5)"
126            )));
127        }
128
129        if buf.len() < offset + header_len {
130            return Err(FieldError::BufferTooShort {
131                offset,
132                need: header_len,
133                have: buf.len().saturating_sub(offset),
134            });
135        }
136
137        Ok(Self::new(offset, offset + header_len))
138    }
139
140    /// Validate that the buffer contains a valid TCP header at the offset.
141    pub fn validate(buf: &[u8], offset: usize) -> Result<(), FieldError> {
142        if buf.len() < offset + TCP_MIN_HEADER_LEN {
143            return Err(FieldError::BufferTooShort {
144                offset,
145                need: TCP_MIN_HEADER_LEN,
146                have: buf.len().saturating_sub(offset),
147            });
148        }
149
150        let data_offset = ((buf[offset + offsets::DATA_OFFSET] >> 4) & 0x0F) as usize;
151        if data_offset < 5 {
152            return Err(FieldError::InvalidValue(format!(
153                "Data offset {data_offset} is less than minimum (5)"
154            )));
155        }
156
157        let header_len = data_offset * 4;
158        if buf.len() < offset + header_len {
159            return Err(FieldError::BufferTooShort {
160                offset,
161                need: header_len,
162                have: buf.len().saturating_sub(offset),
163            });
164        }
165
166        Ok(())
167    }
168
169    /// Calculate the actual header length from the buffer.
170    #[must_use]
171    pub fn calculate_header_len(&self, buf: &[u8]) -> usize {
172        self.data_offset(buf)
173            .map(|doff| (doff as usize) * 4)
174            .unwrap_or(TCP_MIN_HEADER_LEN)
175    }
176
177    /// Get the options length (header length - 20).
178    #[must_use]
179    pub fn options_len(&self, buf: &[u8]) -> usize {
180        self.calculate_header_len(buf)
181            .saturating_sub(TCP_MIN_HEADER_LEN)
182    }
183
184    // ========== Field Readers ==========
185
186    /// Read the source port.
187    #[inline]
188    pub fn src_port(&self, buf: &[u8]) -> Result<u16, FieldError> {
189        u16::read(buf, self.index.start + offsets::SRC_PORT)
190    }
191
192    /// Alias for `src_port` (Scapy compatibility).
193    #[inline]
194    pub fn sport(&self, buf: &[u8]) -> Result<u16, FieldError> {
195        self.src_port(buf)
196    }
197
198    /// Read the destination port.
199    #[inline]
200    pub fn dst_port(&self, buf: &[u8]) -> Result<u16, FieldError> {
201        u16::read(buf, self.index.start + offsets::DST_PORT)
202    }
203
204    /// Alias for `dst_port` (Scapy compatibility).
205    #[inline]
206    pub fn dport(&self, buf: &[u8]) -> Result<u16, FieldError> {
207        self.dst_port(buf)
208    }
209
210    /// Read the sequence number.
211    #[inline]
212    pub fn seq(&self, buf: &[u8]) -> Result<u32, FieldError> {
213        u32::read(buf, self.index.start + offsets::SEQ)
214    }
215
216    /// Read the acknowledgment number.
217    #[inline]
218    pub fn ack(&self, buf: &[u8]) -> Result<u32, FieldError> {
219        u32::read(buf, self.index.start + offsets::ACK)
220    }
221
222    /// Read the data offset (in 32-bit words).
223    #[inline]
224    pub fn data_offset(&self, buf: &[u8]) -> Result<u8, FieldError> {
225        let b = u8::read(buf, self.index.start + offsets::DATA_OFFSET)?;
226        Ok((b >> 4) & 0x0F)
227    }
228
229    /// Alias for `data_offset` (Scapy compatibility).
230    #[inline]
231    pub fn dataofs(&self, buf: &[u8]) -> Result<u8, FieldError> {
232        self.data_offset(buf)
233    }
234
235    /// Read the reserved bits (should be 0).
236    #[inline]
237    pub fn reserved(&self, buf: &[u8]) -> Result<u8, FieldError> {
238        let b = u8::read(buf, self.index.start + offsets::DATA_OFFSET)?;
239        Ok((b >> 1) & 0x07)
240    }
241
242    /// Read the flags as raw byte.
243    #[inline]
244    pub fn flags_raw(&self, buf: &[u8]) -> Result<u8, FieldError> {
245        u8::read(buf, self.index.start + offsets::FLAGS)
246    }
247
248    /// Read the flags as a structured type.
249    #[inline]
250    pub fn flags(&self, buf: &[u8]) -> Result<TcpFlags, FieldError> {
251        let hi = u8::read(buf, self.index.start + offsets::DATA_OFFSET)?;
252        let lo = u8::read(buf, self.index.start + offsets::FLAGS)?;
253        Ok(TcpFlags::from_bytes(hi, lo))
254    }
255
256    /// Read the window size.
257    #[inline]
258    pub fn window(&self, buf: &[u8]) -> Result<u16, FieldError> {
259        u16::read(buf, self.index.start + offsets::WINDOW)
260    }
261
262    /// Read the checksum.
263    #[inline]
264    pub fn checksum(&self, buf: &[u8]) -> Result<u16, FieldError> {
265        u16::read(buf, self.index.start + offsets::CHECKSUM)
266    }
267
268    /// Alias for checksum (Scapy compatibility).
269    #[inline]
270    pub fn chksum(&self, buf: &[u8]) -> Result<u16, FieldError> {
271        self.checksum(buf)
272    }
273
274    /// Read the urgent pointer.
275    #[inline]
276    pub fn urgent_ptr(&self, buf: &[u8]) -> Result<u16, FieldError> {
277        u16::read(buf, self.index.start + offsets::URG_PTR)
278    }
279
280    /// Alias for `urgent_ptr` (Scapy compatibility).
281    #[inline]
282    pub fn urgptr(&self, buf: &[u8]) -> Result<u16, FieldError> {
283        self.urgent_ptr(buf)
284    }
285
286    /// Read the options bytes (if any).
287    pub fn options_bytes<'a>(&self, buf: &'a [u8]) -> Result<&'a [u8], FieldError> {
288        let header_len = self.calculate_header_len(buf);
289        let opts_start = self.index.start + TCP_MIN_HEADER_LEN;
290        let opts_end = self.index.start + header_len;
291
292        if buf.len() < opts_end {
293            return Err(FieldError::BufferTooShort {
294                offset: opts_start,
295                need: header_len - TCP_MIN_HEADER_LEN,
296                have: buf.len().saturating_sub(opts_start),
297            });
298        }
299
300        Ok(&buf[opts_start..opts_end])
301    }
302
303    /// Parse and return the options.
304    pub fn options(&self, buf: &[u8]) -> Result<TcpOptions, FieldError> {
305        let opts_bytes = self.options_bytes(buf)?;
306        parse_options(opts_bytes)
307    }
308
309    // ========== Field Writers ==========
310
311    /// Set the source port.
312    #[inline]
313    pub fn set_src_port(&self, buf: &mut [u8], port: u16) -> Result<(), FieldError> {
314        port.write(buf, self.index.start + offsets::SRC_PORT)
315    }
316
317    /// Alias for `set_src_port` (Scapy compatibility).
318    #[inline]
319    pub fn set_sport(&self, buf: &mut [u8], port: u16) -> Result<(), FieldError> {
320        self.set_src_port(buf, port)
321    }
322
323    /// Set the destination port.
324    #[inline]
325    pub fn set_dst_port(&self, buf: &mut [u8], port: u16) -> Result<(), FieldError> {
326        port.write(buf, self.index.start + offsets::DST_PORT)
327    }
328
329    /// Alias for `set_dst_port` (Scapy compatibility).
330    #[inline]
331    pub fn set_dport(&self, buf: &mut [u8], port: u16) -> Result<(), FieldError> {
332        self.set_dst_port(buf, port)
333    }
334
335    /// Set the sequence number.
336    #[inline]
337    pub fn set_seq(&self, buf: &mut [u8], seq: u32) -> Result<(), FieldError> {
338        seq.write(buf, self.index.start + offsets::SEQ)
339    }
340
341    /// Set the acknowledgment number.
342    #[inline]
343    pub fn set_ack(&self, buf: &mut [u8], ack: u32) -> Result<(), FieldError> {
344        ack.write(buf, self.index.start + offsets::ACK)
345    }
346
347    /// Set the data offset (in 32-bit words).
348    #[inline]
349    pub fn set_data_offset(&self, buf: &mut [u8], offset: u8) -> Result<(), FieldError> {
350        let idx = self.index.start + offsets::DATA_OFFSET;
351        let current = u8::read(buf, idx)?;
352        let new_val = (current & 0x0F) | ((offset & 0x0F) << 4);
353        new_val.write(buf, idx)
354    }
355
356    /// Alias for `set_data_offset` (Scapy compatibility).
357    #[inline]
358    pub fn set_dataofs(&self, buf: &mut [u8], offset: u8) -> Result<(), FieldError> {
359        self.set_data_offset(buf, offset)
360    }
361
362    /// Set the reserved bits.
363    #[inline]
364    pub fn set_reserved(&self, buf: &mut [u8], reserved: u8) -> Result<(), FieldError> {
365        let idx = self.index.start + offsets::DATA_OFFSET;
366        let current = u8::read(buf, idx)?;
367        let new_val = (current & 0xF1) | ((reserved & 0x07) << 1);
368        new_val.write(buf, idx)
369    }
370
371    /// Set the flags from raw byte.
372    #[inline]
373    pub fn set_flags_raw(&self, buf: &mut [u8], flags: u8) -> Result<(), FieldError> {
374        flags.write(buf, self.index.start + offsets::FLAGS)
375    }
376
377    /// Set the flags from structured type.
378    #[inline]
379    pub fn set_flags(&self, buf: &mut [u8], flags: TcpFlags) -> Result<(), FieldError> {
380        // Set flags byte
381        flags
382            .to_byte()
383            .write(buf, self.index.start + offsets::FLAGS)?;
384
385        // Set NS bit in data offset byte
386        let idx = self.index.start + offsets::DATA_OFFSET;
387        let current = u8::read(buf, idx)?;
388        let new_val = (current & 0xFE) | flags.ns_bit();
389        new_val.write(buf, idx)
390    }
391
392    /// Set the window size.
393    #[inline]
394    pub fn set_window(&self, buf: &mut [u8], window: u16) -> Result<(), FieldError> {
395        window.write(buf, self.index.start + offsets::WINDOW)
396    }
397
398    /// Set the checksum.
399    #[inline]
400    pub fn set_checksum(&self, buf: &mut [u8], checksum: u16) -> Result<(), FieldError> {
401        checksum.write(buf, self.index.start + offsets::CHECKSUM)
402    }
403
404    /// Alias for `set_checksum` (Scapy compatibility).
405    #[inline]
406    pub fn set_chksum(&self, buf: &mut [u8], checksum: u16) -> Result<(), FieldError> {
407        self.set_checksum(buf, checksum)
408    }
409
410    /// Set the urgent pointer.
411    #[inline]
412    pub fn set_urgent_ptr(&self, buf: &mut [u8], urgptr: u16) -> Result<(), FieldError> {
413        urgptr.write(buf, self.index.start + offsets::URG_PTR)
414    }
415
416    /// Alias for `set_urgent_ptr` (Scapy compatibility).
417    #[inline]
418    pub fn set_urgptr(&self, buf: &mut [u8], urgptr: u16) -> Result<(), FieldError> {
419        self.set_urgent_ptr(buf, urgptr)
420    }
421
422    // ========== Dynamic Field Access ==========
423
424    /// Get a field value by name.
425    pub fn get_field(&self, buf: &[u8], name: &str) -> Option<Result<FieldValue, FieldError>> {
426        match name {
427            "sport" | "src_port" => Some(self.src_port(buf).map(FieldValue::U16)),
428            "dport" | "dst_port" => Some(self.dst_port(buf).map(FieldValue::U16)),
429            "seq" => Some(self.seq(buf).map(FieldValue::U32)),
430            "ack" => Some(self.ack(buf).map(FieldValue::U32)),
431            "dataofs" | "data_offset" => Some(self.data_offset(buf).map(FieldValue::U8)),
432            "reserved" => Some(self.reserved(buf).map(FieldValue::U8)),
433            "flags" => Some(self.flags_raw(buf).map(FieldValue::U8)),
434            "window" => Some(self.window(buf).map(FieldValue::U16)),
435            "chksum" | "checksum" => Some(self.checksum(buf).map(FieldValue::U16)),
436            "urgptr" | "urgent_ptr" => Some(self.urgent_ptr(buf).map(FieldValue::U16)),
437            _ => None,
438        }
439    }
440
441    /// Set a field value by name.
442    pub fn set_field(
443        &self,
444        buf: &mut [u8],
445        name: &str,
446        value: FieldValue,
447    ) -> Option<Result<(), FieldError>> {
448        match (name, value) {
449            ("sport" | "src_port", FieldValue::U16(v)) => Some(self.set_src_port(buf, v)),
450            ("dport" | "dst_port", FieldValue::U16(v)) => Some(self.set_dst_port(buf, v)),
451            ("seq", FieldValue::U32(v)) => Some(self.set_seq(buf, v)),
452            ("ack", FieldValue::U32(v)) => Some(self.set_ack(buf, v)),
453            ("dataofs" | "data_offset", FieldValue::U8(v)) => Some(self.set_data_offset(buf, v)),
454            ("reserved", FieldValue::U8(v)) => Some(self.set_reserved(buf, v)),
455            ("flags", FieldValue::U8(v)) => Some(self.set_flags_raw(buf, v)),
456            ("window", FieldValue::U16(v)) => Some(self.set_window(buf, v)),
457            ("chksum" | "checksum", FieldValue::U16(v)) => Some(self.set_checksum(buf, v)),
458            ("urgptr" | "urgent_ptr", FieldValue::U16(v)) => Some(self.set_urgent_ptr(buf, v)),
459            _ => None,
460        }
461    }
462
463    /// Get list of field names.
464    #[must_use]
465    pub fn field_names() -> &'static [&'static str] {
466        &[
467            "sport", "dport", "seq", "ack", "dataofs", "reserved", "flags", "window", "chksum",
468            "urgptr",
469        ]
470    }
471
472    // ========== Utility Methods ==========
473
474    /// Get the payload length from IP total length.
475    /// Note: TCP doesn't have its own length field; it relies on IP layer.
476    #[must_use]
477    pub fn payload_len(&self, buf: &[u8], ip_payload_len: usize) -> usize {
478        let header_len = self.calculate_header_len(buf);
479        ip_payload_len.saturating_sub(header_len)
480    }
481
482    /// Get a slice of the payload data.
483    #[must_use]
484    pub fn payload<'a>(&self, buf: &'a [u8]) -> &'a [u8] {
485        let header_len = self.calculate_header_len(buf);
486        let payload_start = self.index.start + header_len;
487
488        if payload_start > buf.len() {
489            return &[];
490        }
491
492        &buf[payload_start..]
493    }
494
495    /// Get the header bytes.
496    #[inline]
497    #[must_use]
498    pub fn header_bytes<'a>(&self, buf: &'a [u8]) -> &'a [u8] {
499        let header_len = self.calculate_header_len(buf);
500        let end = (self.index.start + header_len).min(buf.len());
501        &buf[self.index.start..end]
502    }
503
504    /// Get the source port service name.
505    pub fn src_service(&self, buf: &[u8]) -> &'static str {
506        self.src_port(buf)
507            .map(services::service_name)
508            .unwrap_or("unknown")
509    }
510
511    /// Get the destination port service name.
512    pub fn dst_service(&self, buf: &[u8]) -> &'static str {
513        self.dst_port(buf)
514            .map(services::service_name)
515            .unwrap_or("unknown")
516    }
517
518    /// Compute hash for packet matching (like Scapy's hashret).
519    #[must_use]
520    pub fn hashret(&self, buf: &[u8]) -> Vec<u8> {
521        let sport = self.src_port(buf).unwrap_or(0);
522        let dport = self.dst_port(buf).unwrap_or(0);
523
524        // XOR the ports
525        let xored = sport ^ dport;
526
527        // Return as bytes
528        xored.to_be_bytes().to_vec()
529    }
530
531    /// Check if this packet answers another (for `sr()` matching).
532    #[must_use]
533    pub fn answers(&self, buf: &[u8], other: &TcpLayer, other_buf: &[u8]) -> bool {
534        let self_flags = self.flags(buf).unwrap_or(TcpFlags::NONE);
535        let other_flags = other.flags(other_buf).unwrap_or(TcpFlags::NONE);
536
537        // RST packets don't get answers
538        if other_flags.rst {
539            return false;
540        }
541
542        // SYN packets without ACK don't answer anything
543        if self_flags.syn && !self_flags.ack {
544            return false;
545        }
546
547        // SYN+ACK answers SYN
548        if self_flags.syn && self_flags.ack && !other_flags.syn {
549            return false;
550        }
551
552        // Check ports
553        let self_sport = self.src_port(buf).unwrap_or(0);
554        let self_dport = self.dst_port(buf).unwrap_or(0);
555        let other_sport = other.src_port(other_buf).unwrap_or(0);
556        let other_dport = other.dst_port(other_buf).unwrap_or(0);
557
558        if self_sport != other_dport || self_dport != other_sport {
559            return false;
560        }
561
562        // Check sequence/ack numbers (with tolerance)
563        let self_seq = self.seq(buf).unwrap_or(0);
564        let self_ack = self.ack(buf).unwrap_or(0);
565        let other_seq = other.seq(other_buf).unwrap_or(0);
566        let other_ack = other.ack(other_buf).unwrap_or(0);
567
568        // For SYN packets without ACK, don't check ack value
569        if !(other_flags.syn && !other_flags.ack) {
570            let diff = other_ack.abs_diff(self_seq);
571            if diff > 2 {
572                return false;
573            }
574        }
575
576        // For RST packets without ACK, skip remaining checks
577        if self_flags.rst && !self_flags.ack {
578            return true;
579        }
580
581        // Check ack vs seq with payload length tolerance
582        let other_payload_len = other.payload(other_buf).len() as u32;
583        let diff = other_seq.abs_diff(self_ack);
584        if diff > 2 + other_payload_len {
585            return false;
586        }
587
588        true
589    }
590}
591
592impl Layer for TcpLayer {
593    fn kind(&self) -> LayerKind {
594        LayerKind::Tcp
595    }
596
597    fn summary(&self, buf: &[u8]) -> String {
598        let sport = self.src_port(buf).unwrap_or(0);
599        let dport = self.dst_port(buf).unwrap_or(0);
600        let flags = self.flags(buf).unwrap_or(TcpFlags::NONE);
601
602        format!("TCP {sport} > {dport} {flags}")
603    }
604
605    fn header_len(&self, buf: &[u8]) -> usize {
606        self.calculate_header_len(buf)
607    }
608
609    fn hashret(&self, buf: &[u8]) -> Vec<u8> {
610        self.hashret(buf)
611    }
612
613    fn answers(&self, buf: &[u8], other: &Self, other_buf: &[u8]) -> bool {
614        self.answers(buf, other, other_buf)
615    }
616
617    fn field_names(&self) -> &'static [&'static str] {
618        Self::field_names()
619    }
620}
621
622#[cfg(test)]
623mod tests {
624    use super::*;
625
626    fn sample_tcp_header() -> Vec<u8> {
627        vec![
628            0x00, 0x50, // Source port: 80
629            0x1F, 0x90, // Dest port: 8080
630            0x00, 0x00, 0x00, 0x01, // Seq: 1
631            0x00, 0x00, 0x00, 0x00, // Ack: 0
632            0x50, // Data offset: 5 (20 bytes), Reserved: 0, NS: 0
633            0x02, // Flags: SYN
634            0xFF, 0xFF, // Window: 65535
635            0x00, 0x00, // Checksum (to be computed)
636            0x00, 0x00, // Urgent pointer: 0
637        ]
638    }
639
640    #[test]
641    fn test_field_readers() {
642        let buf = sample_tcp_header();
643        let layer = TcpLayer::at_offset(0);
644
645        assert_eq!(layer.src_port(&buf).unwrap(), 80);
646        assert_eq!(layer.dst_port(&buf).unwrap(), 8080);
647        assert_eq!(layer.seq(&buf).unwrap(), 1);
648        assert_eq!(layer.ack(&buf).unwrap(), 0);
649        assert_eq!(layer.data_offset(&buf).unwrap(), 5);
650        assert_eq!(layer.window(&buf).unwrap(), 65535);
651        assert_eq!(layer.urgent_ptr(&buf).unwrap(), 0);
652
653        let flags = layer.flags(&buf).unwrap();
654        assert!(flags.syn);
655        assert!(!flags.ack);
656    }
657
658    #[test]
659    fn test_field_writers() {
660        let mut buf = sample_tcp_header();
661        let layer = TcpLayer::at_offset(0);
662
663        layer.set_src_port(&mut buf, 12345).unwrap();
664        assert_eq!(layer.src_port(&buf).unwrap(), 12345);
665
666        layer.set_dst_port(&mut buf, 443).unwrap();
667        assert_eq!(layer.dst_port(&buf).unwrap(), 443);
668
669        layer.set_seq(&mut buf, 0x12345678).unwrap();
670        assert_eq!(layer.seq(&buf).unwrap(), 0x12345678);
671
672        layer.set_ack(&mut buf, 0xABCDEF00).unwrap();
673        assert_eq!(layer.ack(&buf).unwrap(), 0xABCDEF00);
674
675        layer.set_flags(&mut buf, TcpFlags::SA).unwrap();
676        let flags = layer.flags(&buf).unwrap();
677        assert!(flags.syn);
678        assert!(flags.ack);
679    }
680
681    #[test]
682    fn test_flags() {
683        let mut buf = sample_tcp_header();
684        let layer = TcpLayer::at_offset(0);
685
686        // Test NS flag
687        let mut flags = TcpFlags::SA;
688        flags.ns = true;
689        layer.set_flags(&mut buf, flags).unwrap();
690
691        let read_flags = layer.flags(&buf).unwrap();
692        assert!(read_flags.syn);
693        assert!(read_flags.ack);
694        assert!(read_flags.ns);
695    }
696
697    #[test]
698    fn test_header_len() {
699        let buf = sample_tcp_header();
700        let layer = TcpLayer::at_offset(0);
701
702        assert_eq!(layer.calculate_header_len(&buf), 20);
703
704        // Test with options (data offset = 6 = 24 bytes)
705        let mut buf_with_opts = sample_tcp_header();
706        buf_with_opts[12] = 0x60; // Data offset = 6
707        buf_with_opts.extend_from_slice(&[0, 0, 0, 0]); // 4 bytes of options/padding
708
709        assert_eq!(layer.calculate_header_len(&buf_with_opts), 24);
710    }
711
712    #[test]
713    fn test_validate() {
714        let buf = sample_tcp_header();
715        assert!(TcpLayer::validate(&buf, 0).is_ok());
716
717        // Too short
718        let short = vec![0x00, 0x50];
719        assert!(TcpLayer::validate(&short, 0).is_err());
720
721        // Invalid data offset
722        let mut bad_doff = sample_tcp_header();
723        bad_doff[12] = 0x30; // Data offset = 3 (< minimum 5)
724        assert!(TcpLayer::validate(&bad_doff, 0).is_err());
725    }
726
727    #[test]
728    fn test_summary() {
729        let buf = sample_tcp_header();
730        let layer = TcpLayer::at_offset(0);
731
732        let summary = layer.summary(&buf);
733        assert!(summary.contains("80"));
734        assert!(summary.contains("8080"));
735        assert!(summary.contains("S")); // SYN flag
736    }
737
738    #[test]
739    fn test_at_offset_dynamic() {
740        let buf = sample_tcp_header();
741        let layer = TcpLayer::at_offset_dynamic(&buf, 0).unwrap();
742
743        assert_eq!(layer.index.start, 0);
744        assert_eq!(layer.index.end, 20);
745    }
746
747    #[test]
748    fn test_payload() {
749        let mut buf = sample_tcp_header();
750        buf.extend_from_slice(b"Hello, TCP!");
751
752        let layer = TcpLayer::at_offset(0);
753        let payload = layer.payload(&buf);
754
755        assert_eq!(payload, b"Hello, TCP!");
756    }
757}