Skip to main content

wireforge_app/pcap/
reader.rs

1//! pcap file reader.
2
3use std::io::Read;
4use std::time::Duration;
5
6use super::types::{Endianness, LinkType, PcapHeader};
7
8#[inline]
9fn read_u32(buf: &[u8], endian: Endianness) -> u32 {
10    match endian { Endianness::Big => u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]), Endianness::Little => u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]) }
11}
12
13pub struct PcapReader<R: Read> {
14    reader: R,
15    header: PcapHeader,
16    endian: Endianness,
17}
18
19impl<R: Read> PcapReader<R> {
20    pub fn new(mut reader: R) -> Result<Self, std::io::Error> {
21        let mut hdr_buf = [0u8; 24];
22        reader.read_exact(&mut hdr_buf)?;
23        let magic = u32::from_be_bytes([hdr_buf[0], hdr_buf[1], hdr_buf[2], hdr_buf[3]]);
24        let endian = match magic {
25            0xa1b2c3d4 => Endianness::Big,
26            0xd4c3b2a1 => Endianness::Little,
27            _ => return Err(std::io::Error::new(std::io::ErrorKind::InvalidData, format!("invalid magic: 0x{magic:08X}"))),
28        };
29        // Swap bytes if needed
30        let read_u32 = |off: usize| -> u32 {
31            let b = &hdr_buf[off..off+4];
32            match endian { Endianness::Big => u32::from_be_bytes([b[0],b[1],b[2],b[3]]), Endianness::Little => u32::from_le_bytes([b[0],b[1],b[2],b[3]]) }
33        };
34        let read_u16 = |off: usize| -> u16 {
35            let b = &hdr_buf[off..off+2];
36            match endian { Endianness::Big => u16::from_be_bytes([b[0],b[1]]), Endianness::Little => u16::from_le_bytes([b[0],b[1]]) }
37        };
38        Ok(Self {
39            reader,
40            endian,
41            header: PcapHeader {
42                magic_number: magic,
43                version_major: read_u16(4),
44                version_minor: read_u16(6),
45                thiszone: read_u32(8) as i32,
46                sigfigs: read_u32(12),
47                snaplen: read_u32(16),
48                network: read_u32(20),
49            },
50        })
51    }
52
53    pub fn header(&self) -> &PcapHeader { &self.header }
54    pub fn link_type(&self) -> LinkType { LinkType::from(self.header.network) }
55}
56
57impl<R: Read> Iterator for PcapReader<R> {
58    type Item = Result<(Duration, Vec<u8>), std::io::Error>;
59
60    fn next(&mut self) -> Option<Self::Item> {
61        let mut rec_hdr = [0u8; 16];
62        match self.reader.read_exact(&mut rec_hdr) {
63            Ok(_) => {},
64            Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return None,
65            Err(e) => return Some(Err(e)),
66        }
67        let ts_sec = read_u32(&rec_hdr[0..4], self.endian);
68        let ts_usec = read_u32(&rec_hdr[4..8], self.endian);
69        let incl_len = read_u32(&rec_hdr[8..12], self.endian) as usize;
70
71        let mut data = vec![0u8; incl_len];
72        if let Err(e) = self.reader.read_exact(&mut data) {
73            return Some(Err(e));
74        }
75        Some(Ok((Duration::new(ts_sec as u64, ts_usec * 1000), data)))
76    }
77}
78
79// Convenience: construct from in-memory bytes for testing.
80impl PcapReader<std::io::Cursor<Vec<u8>>> {
81    pub fn from_bytes(data: Vec<u8>) -> Result<Self, std::io::Error> {
82        Self::new(std::io::Cursor::new(data))
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89
90    fn make_pcap_file(packets: &[&[u8]]) -> Vec<u8> {
91        let mut data = Vec::new();
92        // global header
93        data.extend_from_slice(&0xa1b2c3d4u32.to_be_bytes());
94        data.extend_from_slice(&2u16.to_be_bytes());
95        data.extend_from_slice(&4u16.to_be_bytes());
96        data.extend_from_slice(&[0u8; 8]); // thiszone + sigfigs
97        data.extend_from_slice(&65535u32.to_be_bytes()); // snaplen
98        data.extend_from_slice(&1u32.to_be_bytes()); // network=Ethernet
99        for pkt in packets {
100            data.extend_from_slice(&0u32.to_be_bytes()); // ts_sec
101            data.extend_from_slice(&0u32.to_be_bytes()); // ts_usec
102            data.extend_from_slice(&(pkt.len() as u32).to_be_bytes()); // incl_len
103            data.extend_from_slice(&(pkt.len() as u32).to_be_bytes()); // orig_len
104            data.extend_from_slice(pkt);
105        }
106        data
107    }
108
109    #[test]
110    fn read_pcap_empty() {
111        let data = make_pcap_file(&[]);
112        let reader = PcapReader::from_bytes(data).unwrap();
113        assert_eq!(reader.link_type(), LinkType::Ethernet);
114        assert_eq!(reader.header().version_major, 2);
115    }
116
117    #[test]
118    fn read_pcap_with_packets() {
119        let packets: Vec<&[u8]> = vec![&[0xAA, 0xBB], &[0xCC, 0xDD, 0xEE]];
120        let data = make_pcap_file(&packets);
121        let reader = PcapReader::from_bytes(data).unwrap();
122        let results: Vec<_> = reader.collect();
123        assert_eq!(results.len(), 2);
124        assert_eq!(results[0].as_ref().unwrap().1, &[0xAA, 0xBB]);
125        assert_eq!(results[1].as_ref().unwrap().1, &[0xCC, 0xDD, 0xEE]);
126    }
127
128    #[test]
129    fn read_pcap_bad_magic() {
130        let mut data = vec![0u8; 24];
131        data[0..4].copy_from_slice(&0xDEADBEEFu32.to_be_bytes());
132        assert!(PcapReader::from_bytes(data).is_err());
133    }
134}