rusty_pcap/pcap/
packet_header.rs

1//! Packet header representation and parsing for pcap files
2use std::{
3    io::{Cursor, Read, Write},
4    time::{SystemTime, SystemTimeError},
5};
6
7use crate::{
8    Version,
9    byte_order::{Endianness, ReadExt, WriteExt},
10    pcap::PcapParseError,
11};
12/// Represents the timestamp of a packet
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
14pub struct PacketTimestamp {
15    /// Seconds since epoch
16    pub seconds: u32,
17    /// If microseconds resolution - microseconds part
18    /// If nanoseconds resolution - nanoseconds part
19    pub usec: u32,
20}
21impl TryFrom<SystemTime> for PacketTimestamp {
22    type Error = SystemTimeError;
23
24    fn try_from(value: SystemTime) -> Result<Self, Self::Error> {
25        let duration_since_epoch = value.duration_since(SystemTime::UNIX_EPOCH)?;
26        Ok(Self {
27            seconds: duration_since_epoch.as_secs() as u32,
28            usec: duration_since_epoch.subsec_nanos(),
29        })
30    }
31}
32#[cfg(feature = "chrono")]
33mod _chrono_impl {
34    use chrono::{DateTime, NaiveDateTime};
35
36    use crate::pcap::file_header::MagicNumber;
37
38    use super::PacketTimestamp;
39    impl PacketTimestamp {
40        pub fn to_chrono_naive_datetime(&self, resolution: MagicNumber) -> Option<NaiveDateTime> {
41            match resolution {
42                MagicNumber::Microsecond => {
43                    DateTime::from_timestamp(self.seconds as i64, self.usec * 1000)
44                        .map(|x| x.naive_utc())
45                }
46                MagicNumber::Nanosecond => {
47                    DateTime::from_timestamp(self.seconds as i64, self.usec).map(|x| x.naive_utc())
48                }
49            }
50        }
51    }
52}
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub struct PacketHeader {
55    pub timestamp: PacketTimestamp,
56    /// The length of the packet data included in the file
57    pub include_len: u32,
58    /// The original length of the packet data
59    pub orig_len: u32,
60}
61
62impl PacketHeader {
63    pub fn new(timestamp: PacketTimestamp, incl_len: u32, orig_len: u32) -> Self {
64        Self {
65            timestamp,
66            include_len: incl_len,
67            orig_len,
68        }
69    }
70    /// Reads the packet header from the reader
71    /// Returns `Ok(Self)` on success, or `Err` if there was an error
72    /// reading the packet header
73    /// The endianness is used to determine how to read the bytes
74    #[inline(always)]
75    pub fn read<R: Read>(
76        reader: &mut R,
77        endianness: Endianness,
78        version: &Version,
79    ) -> Result<Self, PcapParseError> {
80        let mut header = [0u8; 16];
81        reader.read_exact(&mut header)?;
82        Self::parse_bytes(&header, endianness, version)
83    }
84    #[inline(always)]
85    pub fn parse_bytes(
86        bytes: &[u8; 16],
87        endianness: Endianness,
88        version: &Version,
89    ) -> Result<Self, PcapParseError> {
90        let mut cursor = Cursor::new(bytes);
91        let ts = cursor.read_u32(endianness)?;
92        let ts_usec = cursor.read_u32(endianness)?;
93        let (include_len, orig_len) = if version < &Version::PCAP_VERSION_2_3 {
94            let orig_len = cursor.read_u32(endianness)?;
95            let include_len = cursor.read_u32(endianness)?;
96            (include_len, orig_len)
97        } else {
98            let include_len = cursor.read_u32(endianness)?;
99            let orig_len = cursor.read_u32(endianness)?;
100            (include_len, orig_len)
101        };
102        Ok(Self {
103            timestamp: PacketTimestamp {
104                seconds: ts,
105                usec: ts_usec,
106            },
107            include_len,
108            orig_len,
109        })
110    }
111    pub fn write<W: Write>(
112        &self,
113        writer: &mut W,
114        endianness: Endianness,
115        version: &Version,
116    ) -> Result<(), std::io::Error> {
117        writer.write_u32(self.timestamp.seconds, endianness)?;
118        writer.write_u32(self.timestamp.usec, endianness)?;
119        if version < &Version::PCAP_VERSION_2_3 {
120            writer.write_u32(self.orig_len, endianness)?;
121            writer.write_u32(self.include_len, endianness)?;
122            return Ok(());
123        } else {
124            writer.write_u32(self.include_len, endianness)?;
125            writer.write_u32(self.orig_len, endianness)?;
126        }
127
128        Ok(())
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use std::io::Cursor;
135
136    use chrono::{TimeZone, Utc};
137
138    use crate::{
139        Version,
140        byte_order::Endianness,
141        pcap::packet_header::{PacketHeader, PacketTimestamp},
142    };
143
144    #[test]
145    fn write_test() -> anyhow::Result<()> {
146        let specific_datetime_utc = Utc.with_ymd_and_hms(2025, 11, 27, 10, 30, 0).unwrap();
147
148        let duration_since_epoch =
149            specific_datetime_utc.signed_duration_since(Utc.timestamp_opt(0, 0).unwrap());
150
151        let as_secs = duration_since_epoch.num_seconds() as u32;
152        let num_nanos = duration_since_epoch.subsec_nanos() as u32;
153        let mut target: [u8; 16] = [0; 16];
154        let header = PacketHeader {
155            timestamp: crate::pcap::packet_header::PacketTimestamp {
156                seconds: as_secs,
157                usec: num_nanos,
158            },
159            include_len: 100,
160            orig_len: 100,
161        };
162        {
163            let mut writer: Cursor<&mut [u8]> = Cursor::new(&mut target);
164            header.write(
165                &mut writer,
166                Endianness::BigEndian,
167                &Version::PCAP_VERSION_2_4,
168            )?;
169        }
170
171        let result =
172            PacketHeader::parse_bytes(&target, Endianness::BigEndian, &Version::PCAP_VERSION_2_4)?;
173        println!("{result:?}");
174        assert_eq!(result, header);
175        Ok(())
176    }
177
178    #[test]
179    fn test_len_order() {
180        let packet_header = PacketHeader {
181            timestamp: PacketTimestamp::default(),
182            include_len: 1500,
183            orig_len: 2000,
184        };
185        let mut buffer: [u8; 16] = [0; 16];
186        {
187            let mut writer: Cursor<&mut [u8]> = Cursor::new(&mut buffer);
188            packet_header
189                .write(
190                    &mut writer,
191                    Endianness::LittleEndian,
192                    &Version::PCAP_VERSION_2_4,
193                )
194                .unwrap();
195        }
196        let parsed_header = PacketHeader::parse_bytes(
197            &buffer,
198            Endianness::LittleEndian,
199            &Version::PCAP_VERSION_2_4,
200        )
201        .unwrap();
202        assert_eq!(parsed_header.include_len, 1500);
203        assert_eq!(parsed_header.orig_len, 2000);
204
205        let parsed_header_v2_2 = PacketHeader::parse_bytes(
206            &buffer,
207            Endianness::LittleEndian,
208            &Version { major: 2, minor: 2 },
209        )
210        .unwrap();
211        assert_eq!(parsed_header_v2_2.orig_len, 1500);
212        assert_eq!(parsed_header_v2_2.include_len, 2000);
213    }
214    #[test]
215    fn test_len_order_older() {
216        let packet_header = PacketHeader {
217            timestamp: PacketTimestamp::default(),
218            include_len: 1500,
219            orig_len: 2000,
220        };
221        let version = Version { major: 2, minor: 2 };
222        let mut buffer: [u8; 16] = [0; 16];
223        {
224            let mut writer: Cursor<&mut [u8]> = Cursor::new(&mut buffer);
225            packet_header
226                .write(&mut writer, Endianness::LittleEndian, &version)
227                .unwrap();
228        }
229        let parsed_header = PacketHeader::parse_bytes(
230            &buffer,
231            Endianness::LittleEndian,
232            &Version::PCAP_VERSION_2_4,
233        )
234        .unwrap();
235        assert_eq!(parsed_header.include_len, 2000);
236        assert_eq!(parsed_header.orig_len, 1500);
237
238        let parsed_header_v2_2 =
239            PacketHeader::parse_bytes(&buffer, Endianness::LittleEndian, &version).unwrap();
240        assert_eq!(parsed_header_v2_2.include_len, 1500);
241        assert_eq!(parsed_header_v2_2.orig_len, 2000);
242    }
243}