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