Skip to main content

wireforge_app/pcap/
ng_reader.rs

1//! pcapng file reader.
2
3use std::io::Read;
4use std::time::Duration;
5
6use super::types::{Endianness, LinkType, PcapNgBlock};
7use super::PacketCaptureReader;
8
9pub struct PcapNgReader<R: Read> {
10    reader: R,
11    link_type: Option<LinkType>,
12    byte_order: Endianness,
13}
14
15impl<R: Read> PcapNgReader<R> {
16    pub fn new(mut reader: R) -> Result<Self, std::io::Error> {
17        // Read first block to determine byte order
18        let mut hdr = [0u8; 8];
19        reader.read_exact(&mut hdr)?;
20        let block_type = u32::from_be_bytes([hdr[0], hdr[1], hdr[2], hdr[3]]);
21        let _block_len = u32::from_be_bytes([hdr[4], hdr[5], hdr[6], hdr[7]]);
22        // SHB has type 0x0A0D0D0A
23        let byte_order = if block_type == 0x0A0D0D0A { Endianness::Big } else { Endianness::Little };
24        Ok(Self { reader, link_type: None, byte_order })
25    }
26
27    pub fn link_type(&self) -> Option<LinkType> { self.link_type }
28}
29
30impl<R: Read> PacketCaptureReader for PcapNgReader<R> {
31    type Error = std::io::Error;
32
33    fn link_type(&self) -> Option<LinkType> { self.link_type }
34
35    fn next_packet(&mut self) -> Option<Result<(std::time::Duration, Vec<u8>), Self::Error>> {
36        loop {
37            match self.next()? {
38                Ok(PcapNgBlock::EnhancedPacket { timestamp, data }) => {
39                    return Some(Ok((timestamp, data)));
40                }
41                Ok(_) => continue, // skip non-packet blocks
42                Err(e) => return Some(Err(e)),
43            }
44        }
45    }
46}
47
48impl<R: Read> Iterator for PcapNgReader<R> {
49    type Item = Result<PcapNgBlock, std::io::Error>;
50
51    fn next(&mut self) -> Option<Self::Item> {
52        let mut hdr = [0u8; 8];
53        match self.reader.read_exact(&mut hdr) {
54            Ok(_) => {},
55            Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => return None,
56            Err(e) => return Some(Err(e)),
57        }
58        let block_type = read_u32(&hdr[0..4], self.byte_order);
59        let block_len = read_u32(&hdr[4..8], self.byte_order) as usize;
60        if block_len < 12 { return Some(Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "block too short"))); }
61        let body_len = block_len - 12;
62        let mut body = vec![0u8; body_len];
63        if let Err(e) = self.reader.read_exact(&mut body) { return Some(Err(e)); }
64        // Read trailing block length
65        let mut tail = [0u8; 4];
66        let _ = self.reader.read_exact(&mut tail);
67
68        let block = match block_type {
69            0x0A0D0D0A => PcapNgBlock::SectionHeader {
70                byte_order: self.byte_order,
71                version: read_u16(&body, self.byte_order),
72            },
73            0x00000001 => {
74                let lt = read_u16(&body[8..10], self.byte_order) as u32;
75                self.link_type = Some(LinkType::from(lt));
76                PcapNgBlock::InterfaceDescription {
77                    link_type: LinkType::from(lt),
78                    snap_len: read_u32(&body[12..16], self.byte_order),
79                }
80            }
81            0x00000006 => {
82                let _if_id = read_u32(&body[0..4], self.byte_order);
83                let ts_hi = read_u32(&body[4..8], self.byte_order) as u64;
84                let ts_lo = read_u32(&body[8..12], self.byte_order) as u64;
85                let cap_len = read_u32(&body[12..16], self.byte_order) as usize;
86                let ts = Duration::new(ts_hi, (ts_lo / 1000) as u32);
87                let data = body[20..20 + cap_len].to_vec();
88                PcapNgBlock::EnhancedPacket { timestamp: ts, data }
89            }
90            _ => PcapNgBlock::Unknown { block_type, data: body },
91        };
92        Some(Ok(block))
93    }
94}
95
96#[inline]
97fn read_u16(buf: &[u8], endian: Endianness) -> u16 {
98    match endian { Endianness::Big => u16::from_be_bytes([buf[0], buf[1]]), Endianness::Little => u16::from_le_bytes([buf[0], buf[1]]) }
99}
100#[inline]
101fn read_u32(buf: &[u8], endian: Endianness) -> u32 {
102    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]]) }
103}