Skip to main content

capfile/
reader.rs

1//! PCAP and PCAPNG readers
2//!
3//! This module provides readers for parsing PCAP (legacy) and PCAPNG (modern)
4//! packet capture file formats.
5//!
6//! # PCAP Reader
7//!
8//! The [`PcapReader`] reads legacy PCAP files. It provides an iterator-style
9//! API for reading packets one at a time.
10//!
11//! ```ignore
12//! use capfile::PcapReader;
13//!
14//! let mut reader = PcapReader::open("capture.pcap")?;
15//! while let Some(packet) = reader.next_packet()? {
16//!     println!("Packet: {} bytes at {}", packet.len(), packet.timestamp_ns());
17//! }
18//! ```
19//!
20//! # PCAPNG Reader
21//!
22//! The [`PcapngReader`] reads modern PCAPNG files, which support multiple
23//! interfaces and more metadata than PCAP.
24//!
25//! ```ignore
26//! use capfile::PcapngReader;
27//! use capfile::format::pcapng::Block;
28//!
29//! let mut reader = PcapngReader::open("capture.pcapng")?;
30//! for iface in reader.interfaces() {
31//!     println!("Interface: snap_len={}", iface.snap_len);
32//! }
33//! while let Some(block) = reader.next_block()? {
34//!     match block {
35//!         Block::EnhancedPacket(epb) => { /* ... */ }
36//!         _ => {}
37//!     }
38//! }
39//! ```
40
41pub mod mmap;
42
43use crate::error::Error;
44use crate::format::pcap::PcapHeader;
45use crate::format::pcapng as pcapng_mod;
46use crate::format::pcapng::Block;
47
48#[cfg(feature = "std")]
49use std::fs::File;
50#[cfg(feature = "std")]
51use std::io::{Read, Seek, SeekFrom};
52#[cfg(feature = "std")]
53use std::path::Path;
54
55/// Read a u32 from little-endian bytes safely
56fn read_u32(data: &[u8], offset: usize) -> Result<u32, Error> {
57    if data.len() < offset + 4 {
58        return Err(Error::truncated(offset + 4, data.len()));
59    }
60    Ok(u32::from_le_bytes([
61        data[offset],
62        data[offset + 1],
63        data[offset + 2],
64        data[offset + 3],
65    ]))
66}
67
68/// Read a u16 from little-endian bytes safely
69fn read_u16(data: &[u8], offset: usize) -> Result<u16, Error> {
70    if data.len() < offset + 2 {
71        return Err(Error::truncated(offset + 2, data.len()));
72    }
73    Ok(u16::from_le_bytes([data[offset], data[offset + 1]]))
74}
75
76/// Zero-copy reference to packet data
77#[derive(Debug, Clone)]
78pub struct PacketRef {
79    data: Vec<u8>,
80    timestamp_ns: u64,
81    original_len: u32,
82    captured_len: u32,
83}
84
85impl PacketRef {
86    /// Create a new packet reference
87    pub fn new(data: &[u8], timestamp_ns: u64, original_len: u32, captured_len: u32) -> Self {
88        Self {
89            data: data.to_vec(),
90            timestamp_ns,
91            original_len,
92            captured_len,
93        }
94    }
95
96    /// Get packet data
97    pub fn data(&self) -> &[u8] {
98        &self.data
99    }
100
101    /// Get timestamp in nanoseconds since epoch
102    pub fn timestamp_ns(&self) -> u64 {
103        self.timestamp_ns
104    }
105
106    /// Get captured length
107    pub fn captured_len(&self) -> u32 {
108        self.captured_len
109    }
110
111    /// Get original length
112    pub fn original_len(&self) -> u32 {
113        self.original_len
114    }
115
116    /// Get packet length
117    pub fn len(&self) -> usize {
118        self.data.len()
119    }
120
121    /// Check if packet is empty
122    pub fn is_empty(&self) -> bool {
123        self.data.is_empty()
124    }
125}
126
127/// PCAP reader
128pub struct PcapReader<R> {
129    reader: R,
130    header: PcapHeader,
131    is_nano: bool,
132}
133
134impl<R: Read + Seek> PcapReader<R> {
135    /// Open a pcap file from a reader
136    pub fn from_reader(reader: R) -> Result<Self, Error> {
137        let mut reader = reader;
138        let mut header_buf = [0u8; 24];
139        reader.read_exact(&mut header_buf)?;
140
141        let (header, _) = PcapHeader::parse(&header_buf)?;
142
143        Ok(Self {
144            reader,
145            header,
146            is_nano: header.is_nano(),
147        })
148    }
149
150    /// Get the pcap header
151    pub fn header(&self) -> PcapHeader {
152        self.header
153    }
154
155    /// Get link type
156    pub fn link_type(&self) -> u32 {
157        self.header.network
158    }
159
160    /// Read the next packet
161    pub fn next_packet(&mut self) -> Result<Option<PacketRef>, Error> {
162        let mut pkthdr_buf = [0u8; 16];
163        match self.reader.read_exact(&mut pkthdr_buf) {
164            Ok(()) => {}
165            Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(None),
166            Err(e) => return Err(Error::Io(e)),
167        }
168
169        let ts_sec = read_u32(&pkthdr_buf, 0)?;
170        let ts_usec = read_u32(&pkthdr_buf, 4)?;
171        let incl_len = read_u32(&pkthdr_buf, 8)?;
172        let orig_len = read_u32(&pkthdr_buf, 12)?;
173
174        let timestamp_ns = if self.is_nano {
175            (ts_sec as u64) * 1_000_000_000 + ts_usec as u64
176        } else {
177            (ts_sec as u64) * 1_000_000_000 + (ts_usec as u64) * 1000
178        };
179
180        let mut pkt_data = vec![0u8; incl_len as usize];
181        self.reader.read_exact(&mut pkt_data)?;
182
183        Ok(Some(PacketRef::new(
184            &pkt_data,
185            timestamp_ns,
186            orig_len,
187            incl_len,
188        )))
189    }
190}
191
192/// Iterator over packets in a pcap file
193pub struct PcapIterator<R> {
194    reader: PcapReader<R>,
195}
196
197impl<R: Read + Seek> Iterator for PcapIterator<R> {
198    type Item = Result<PacketRef, Error>;
199
200    fn next(&mut self) -> Option<Self::Item> {
201        self.reader.next_packet().transpose()
202    }
203}
204
205/// PCAPNG reader
206pub struct PcapngReader<R> {
207    reader: R,
208    interfaces: Vec<Interface>,
209}
210
211/// Interface information for pcapng
212pub struct Interface {
213    pub link_type: u16,
214    pub snap_len: u32,
215}
216
217impl<R: Read + Seek> PcapngReader<R> {
218    /// Open a pcapng file from a reader
219    pub fn from_reader(mut reader: R) -> Result<Self, Error> {
220        let mut interfaces = Vec::new();
221
222        // Read all blocks to find interfaces
223        loop {
224            // Read block type (4 bytes)
225            let mut type_buf = [0u8; 4];
226            if reader.read_exact(&mut type_buf).is_err() {
227                break;
228            }
229            let block_type = u32::from_le_bytes(type_buf);
230
231            // Read block length (4 bytes)
232            let mut len_buf = [0u8; 4];
233            if reader.read_exact(&mut len_buf).is_err() {
234                break;
235            }
236            let block_len = u32::from_le_bytes(len_buf) as usize;
237
238            if !(12..=1024 * 1024).contains(&block_len) {
239                break;
240            }
241
242            // Read block content.
243            // Total = type(4) + length1(4) + content + length2(4) = block_len
244            // So content = block_len - 12
245            let content_len = block_len - 12;
246            let mut block_data = vec![0u8; content_len];
247            if reader.read_exact(&mut block_data).is_err() {
248                break;
249            }
250
251            if block_type == pcapng_mod::block_type::IDB {
252                // Interface Description Block - link_type at offset 0, snap_len at offset 4
253                let link_type = read_u16(&block_data, 0)?;
254                let snap_len = read_u32(&block_data, 4)?;
255                interfaces.push(Interface {
256                    link_type,
257                    snap_len,
258                });
259            }
260
261            // Skip the trailer (4 bytes)
262            reader.seek(SeekFrom::Current(4))?;
263        }
264
265        reader.seek(SeekFrom::Start(0))?;
266
267        Ok(Self { reader, interfaces })
268    }
269
270    /// Get interfaces
271    pub fn interfaces(&self) -> &[Interface] {
272        &self.interfaces
273    }
274
275    /// Read the next block
276    pub fn next_block(&mut self) -> Result<Option<Block>, Error> {
277        // Read block type (4 bytes)
278        let mut type_buf = [0u8; 4];
279        match self.reader.read_exact(&mut type_buf) {
280            Ok(()) => {}
281            Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(None),
282            Err(e) => return Err(Error::Io(e)),
283        }
284        let _block_type = u32::from_le_bytes(type_buf);
285
286        // Read block length (4 bytes)
287        let mut len_buf = [0u8; 4];
288        match self.reader.read_exact(&mut len_buf) {
289            Ok(()) => {}
290            Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return Ok(None),
291            Err(e) => return Err(Error::Io(e)),
292        }
293
294        let block_len = u32::from_le_bytes(len_buf) as usize;
295
296        if block_len < 12 {
297            return Err(Error::parse(0, "Block too small"));
298        }
299
300        // Build full block data for parse_block:
301        // parse_block expects: type(4) + length(4) + content + trailing_length(4) = block_len
302        let mut full_block = vec![0u8; block_len];
303        full_block[0..4].copy_from_slice(&type_buf);
304        full_block[4..8].copy_from_slice(&len_buf);
305        // Read content into full_block[8..block_len-4]
306        if self
307            .reader
308            .read_exact(&mut full_block[8..block_len - 4])
309            .is_err()
310        {
311            return Ok(None);
312        }
313        // Read trailing length into full_block[block_len-4..block_len]
314        if self
315            .reader
316            .read_exact(&mut full_block[block_len - 4..block_len])
317            .is_err()
318        {
319            return Ok(None);
320        }
321
322        let offset = 0;
323        let result = pcapng_mod::parse_block(&full_block, offset);
324        match result {
325            Ok((block, _)) => Ok(Some(block)),
326            Err(_) => Ok(None),
327        }
328    }
329}
330
331#[cfg(feature = "std")]
332impl PcapReader<std::io::Cursor<Vec<u8>>> {
333    /// Create a reader from bytes (for embedded/WASM use)
334    pub fn from_bytes(data: &[u8]) -> Result<Self, Error> {
335        Self::from_reader(std::io::Cursor::new(data.to_vec()))
336    }
337}
338
339#[cfg(feature = "std")]
340impl PcapngReader<std::io::Cursor<Vec<u8>>> {
341    /// Create a reader from bytes (for embedded/WASM use)
342    pub fn from_bytes(data: &[u8]) -> Result<Self, Error> {
343        Self::from_reader(std::io::Cursor::new(data.to_vec()))
344    }
345}
346
347#[cfg(feature = "std")]
348impl PcapReader<File> {
349    /// Open a pcap file
350    pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
351        let file = File::open(path)?;
352        Self::from_reader(file)
353    }
354}
355
356#[cfg(feature = "std")]
357impl PcapngReader<File> {
358    /// Open a pcapng file
359    pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
360        let file = File::open(path)?;
361        Self::from_reader(file)
362    }
363}