Skip to main content

stackforge_core/pcap/
reader.rs

1//! PCAP file reader with streaming support.
2
3use std::fs::File;
4use std::io::{BufReader, Read};
5use std::path::Path;
6
7use bytes::Bytes;
8use pcap_file::pcap::PcapReader as PcapFileReader;
9
10use crate::error::{PacketError, Result};
11use crate::packet::Packet;
12
13use super::{CapturedPacket, LinkType, PcapMetadata};
14
15/// Read all packets from a PCAP file into memory.
16///
17/// This is the simple Scapy-like API. For large files, use [`PcapIterator`] instead.
18pub fn rdpcap(path: impl AsRef<Path>) -> Result<Vec<CapturedPacket>> {
19    let iter = PcapIterator::open(path)?;
20    iter.collect()
21}
22
23/// Streaming iterator over packets in a PCAP file.
24///
25/// Reads packets one at a time, suitable for gigabyte-sized captures.
26/// Implements `Iterator<Item = Result<CapturedPacket>>`.
27pub struct PcapIterator<R: Read> {
28    inner: PcapFileReader<R>,
29    link_type: LinkType,
30}
31
32impl PcapIterator<BufReader<File>> {
33    /// Open a PCAP file for streaming iteration.
34    pub fn open(path: impl AsRef<Path>) -> Result<Self> {
35        let file = File::open(path.as_ref()).map_err(|e| {
36            PacketError::Io(format!("failed to open {}: {}", path.as_ref().display(), e))
37        })?;
38        let reader = BufReader::new(file);
39        Self::from_reader(reader)
40    }
41}
42
43impl<R: Read> PcapIterator<R> {
44    /// Create a `PcapIterator` from any reader.
45    pub fn from_reader(reader: R) -> Result<Self> {
46        let pcap_reader = PcapFileReader::new(reader)
47            .map_err(|e| PacketError::Io(format!("invalid PCAP: {}", e)))?;
48        let link_type = LinkType(u32::from(pcap_reader.header().datalink));
49        Ok(Self {
50            inner: pcap_reader,
51            link_type,
52        })
53    }
54
55    /// Returns the link-layer type from the PCAP global header.
56    pub fn link_type(&self) -> LinkType {
57        self.link_type
58    }
59}
60
61impl<R: Read> Iterator for PcapIterator<R> {
62    type Item = Result<CapturedPacket>;
63
64    fn next(&mut self) -> Option<Self::Item> {
65        match self.inner.next_packet() {
66            Some(Ok(pcap_pkt)) => {
67                let ts = pcap_pkt.timestamp;
68                let data = Bytes::copy_from_slice(&pcap_pkt.data);
69                let mut pkt = Packet::from_bytes(data);
70                // Auto-parse; ignore parse errors (store as unparsed)
71                let _ = pkt.parse();
72                Some(Ok(CapturedPacket {
73                    packet: pkt,
74                    metadata: PcapMetadata {
75                        timestamp: ts,
76                        orig_len: pcap_pkt.orig_len,
77                    },
78                }))
79            },
80            Some(Err(e)) => Some(Err(PacketError::Io(format!("PCAP read error: {}", e)))),
81            None => None,
82        }
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use std::io::Cursor;
90    use std::time::Duration;
91
92    use pcap_file::pcap::{PcapHeader, PcapPacket, PcapWriter as PcapFileWriter};
93
94    fn sample_ethernet_packet() -> Vec<u8> {
95        // Minimal Ethernet frame: dst(6) + src(6) + type(2) + payload(4)
96        vec![
97            0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // dst: broadcast
98            0x00, 0x11, 0x22, 0x33, 0x44, 0x55, // src
99            0x08, 0x00, // EtherType: IPv4
100            0x00, 0x00, 0x00, 0x00, // dummy payload
101        ]
102    }
103
104    fn create_test_pcap(packets: &[(Duration, &[u8])]) -> Vec<u8> {
105        let mut buf = Vec::new();
106        let header = PcapHeader::default();
107        let mut writer = PcapFileWriter::with_header(Cursor::new(&mut buf), header).unwrap();
108        for (ts, data) in packets {
109            let pkt = PcapPacket::new(*ts, data.len() as u32, data);
110            writer.write_packet(&pkt).unwrap();
111        }
112        drop(writer);
113        buf
114    }
115
116    #[test]
117    fn test_pcap_iterator_from_reader() {
118        let eth = sample_ethernet_packet();
119        let pcap_data = create_test_pcap(&[
120            (Duration::from_secs(1), &eth),
121            (Duration::from_secs(2), &eth),
122        ]);
123        let iter = PcapIterator::from_reader(Cursor::new(pcap_data)).unwrap();
124        let packets: Vec<_> = iter.collect::<std::result::Result<Vec<_>, _>>().unwrap();
125        assert_eq!(packets.len(), 2);
126        assert_eq!(packets[0].metadata.timestamp, Duration::from_secs(1));
127        assert_eq!(packets[1].metadata.timestamp, Duration::from_secs(2));
128    }
129
130    #[test]
131    fn test_pcap_iterator_link_type() {
132        let pcap_data = create_test_pcap(&[]);
133        let iter = PcapIterator::from_reader(Cursor::new(pcap_data)).unwrap();
134        assert_eq!(iter.link_type(), LinkType::ETHERNET);
135    }
136
137    #[test]
138    fn test_pcap_iterator_empty() {
139        let pcap_data = create_test_pcap(&[]);
140        let iter = PcapIterator::from_reader(Cursor::new(pcap_data)).unwrap();
141        let packets: Vec<_> = iter.collect::<std::result::Result<Vec<_>, _>>().unwrap();
142        assert!(packets.is_empty());
143    }
144
145    #[test]
146    fn test_pcap_iterator_metadata() {
147        let eth = sample_ethernet_packet();
148        let pcap_data = create_test_pcap(&[(Duration::from_millis(1500), &eth)]);
149        let iter = PcapIterator::from_reader(Cursor::new(pcap_data)).unwrap();
150        let packets: Vec<_> = iter.collect::<std::result::Result<Vec<_>, _>>().unwrap();
151        assert_eq!(packets.len(), 1);
152        assert_eq!(packets[0].metadata.orig_len, eth.len() as u32);
153        assert_eq!(packets[0].packet.len(), eth.len());
154    }
155
156    #[test]
157    fn test_pcap_iterator_is_lazy() {
158        let eth = sample_ethernet_packet();
159        let pcap_data = create_test_pcap(&[
160            (Duration::from_secs(1), &eth),
161            (Duration::from_secs(2), &eth),
162            (Duration::from_secs(3), &eth),
163        ]);
164        let mut iter = PcapIterator::from_reader(Cursor::new(pcap_data)).unwrap();
165        // Only read one packet
166        let first = iter.next().unwrap().unwrap();
167        assert_eq!(first.metadata.timestamp, Duration::from_secs(1));
168        // Iterator still has more
169        let second = iter.next().unwrap().unwrap();
170        assert_eq!(second.metadata.timestamp, Duration::from_secs(2));
171    }
172}