rusty_pcap/pcap_ng/
blocks.rs

1//! Block Types for pcap-ng files
2use std::io::Read;
3
4use crate::{
5    byte_order::{ByteOrder, Endianness, ReadExt, UnexpectedSize},
6    pcap_ng::PcapNgParseError,
7};
8
9mod enhanced_packet;
10mod generic;
11mod header;
12mod interface;
13mod name_resolution;
14mod simple_packet;
15pub use enhanced_packet::EnhancedPacket;
16pub use generic::GenericBlock;
17pub use header::{SHBOptionCodes, SectionHeaderBlock};
18pub use interface::{InterfaceDescriptionBlock, InterfaceOptionCodes};
19pub use name_resolution::NameResolutionBlock;
20pub use simple_packet::SimplePacket;
21pub trait Block {
22    /// Returns the block ID for this block type
23    fn block_id() -> u32
24    where
25        Self: Sized;
26    /// Returns the block ID in little-endian format
27    fn block_id_le() -> [u8; 4]
28    where
29        Self: Sized,
30    {
31        Self::block_id().to_le_bytes()
32    }
33    /// Returns the block ID in big-endian format
34    fn block_id_be() -> [u8; 4]
35    where
36        Self: Sized,
37    {
38        Self::block_id().to_be_bytes()
39    }
40    /// Minimum size of the block, including the header
41    ///
42    /// Should be at least 12 bytes for the header and footer.
43    fn minimum_size() -> usize {
44        12 // Default minimum size for a block header
45    }
46    /// Reads the block from the reader with the given header and byte order
47    ///
48    /// If byte_order is None, it will be determined from the block header.
49    ///
50    /// For SectionHeaderBlock, the byte order is determined from the first 4 bytes of the blocks content.
51    fn read_with_header<R: Read>(
52        reader: &mut R,
53        header: &BlockHeader,
54        byte_order: Option<Endianness>,
55    ) -> Result<Self, PcapNgParseError>
56    where
57        Self: Sized;
58}
59#[derive(Debug, Clone, Copy, PartialEq, Eq)]
60pub struct BlockHeader {
61    pub block_id: [u8; 4],
62    pub block_length: [u8; 4],
63}
64impl BlockHeader {
65    pub fn new(block_id: [u8; 4], block_length: [u8; 4]) -> Self {
66        Self {
67            block_id,
68            block_length,
69        }
70    }
71    /// Returns the block ID as a u32
72    pub fn block_id_as_u32(&self, endianness: impl ByteOrder) -> u32 {
73        endianness.u32_from_bytes(self.block_id)
74    }
75    /// Returns the block length as a u32
76    pub fn block_length_as_u32(&self, endianness: impl ByteOrder) -> u32 {
77        endianness.u32_from_bytes(self.block_length)
78    }
79    pub fn read<R: Read>(reader: &mut R) -> Result<Self, PcapNgParseError> {
80        let block_id = reader.read_bytes::<4>()?;
81        let block_length = reader.read_bytes::<4>()?;
82        Ok(Self::new(block_id, block_length))
83    }
84    pub fn parse_from_bytes(bytes: &[u8]) -> Result<Self, PcapNgParseError> {
85        if bytes.len() < 8 {
86            return Err(PcapNgParseError::UnexpectedSize(UnexpectedSize {
87                name: "BlockHeader",
88                expected: 8,
89                got: bytes.len(),
90            }));
91        }
92        let block_id = [bytes[0], bytes[1], bytes[2], bytes[3]];
93        let block_length = [bytes[4], bytes[5], bytes[6], bytes[7]];
94        Ok(Self::new(block_id, block_length))
95    }
96    /// Checks if the block ID matches the expected block ID for the given block type
97    pub(crate) fn matches_block_id<B: Block>(&self) -> Result<(), PcapNgParseError> {
98        if self.block_id != B::block_id_le() && self.block_id != B::block_id_be() {
99            return Err(PcapNgParseError::UnexpectedBlockId {
100                expected_be: B::block_id().to_be_bytes(),
101                expected_le: B::block_id().to_le_bytes(),
102                got: self.block_id,
103            });
104        }
105        Ok(())
106    }
107    /// Will panic if the block ID does not match the expected block ID for the given block type
108    pub(crate) fn endianness_from_block<B: Block>(&self) -> Option<Endianness> {
109        debug_assert_ne!(
110            B::block_id_be(),
111            B::block_id_le(),
112            "Unable to determine endianness for {}",
113            std::any::type_name::<B>()
114        );
115        if self.block_id == B::block_id_le() {
116            Some(Endianness::LittleEndian)
117        } else if self.block_id == B::block_id_be() {
118            Some(Endianness::BigEndian)
119        } else {
120            None
121        }
122    }
123}
124#[derive(Debug, Clone, PartialEq, Eq)]
125pub enum PcapNgBlock {
126    SectionHeader(SectionHeaderBlock),
127    InterfaceDescription(InterfaceDescriptionBlock),
128    SimplePacket(SimplePacket),
129    EnhancedPacket(EnhancedPacket),
130    NameResolution(NameResolutionBlock),
131    Generic(GenericBlock),
132}
133impl PcapNgBlock {
134    pub fn read<R: Read>(
135        reader: &mut R,
136        header: &BlockHeader,
137        byte_order: Endianness,
138    ) -> Result<Self, PcapNgParseError> {
139        let block_id = header.block_id_as_u32(byte_order);
140        match block_id {
141            168627466 => Ok(PcapNgBlock::SectionHeader(
142                SectionHeaderBlock::read_with_header(reader, header, Some(byte_order))?,
143            )),
144            1 => Ok(PcapNgBlock::InterfaceDescription(
145                InterfaceDescriptionBlock::read_with_header(reader, header, Some(byte_order))?,
146            )),
147            3 => Ok(PcapNgBlock::SimplePacket(SimplePacket::read_with_header(
148                reader,
149                header,
150                Some(byte_order),
151            )?)),
152            4 => Ok(PcapNgBlock::NameResolution(
153                NameResolutionBlock::read_with_header(reader, header, Some(byte_order))?,
154            )),
155            6 => Ok(PcapNgBlock::EnhancedPacket(
156                EnhancedPacket::read_with_header(reader, header, Some(byte_order))?,
157            )),
158
159            _ => Ok(PcapNgBlock::Generic(GenericBlock::read_with_header(
160                reader, header, byte_order,
161            )?)),
162        }
163    }
164}
165
166#[cfg(test)]
167mod tests {
168    use super::*;
169    #[cfg(debug_assertions)]
170    #[test]
171    #[should_panic]
172    fn endianness_from_block_panics() {
173        let block = BlockHeader {
174            block_id: SectionHeaderBlock::block_id_le(),
175            block_length: [0; 4],
176        };
177        block.endianness_from_block::<SectionHeaderBlock>();
178    }
179
180    #[test]
181    fn endianness_from_block() {
182        let block = BlockHeader {
183            block_id: InterfaceDescriptionBlock::block_id_le(),
184            block_length: [0; 4],
185        };
186        assert_eq!(
187            block.endianness_from_block::<InterfaceDescriptionBlock>(),
188            Some(Endianness::LittleEndian)
189        );
190        let block = BlockHeader {
191            block_id: InterfaceDescriptionBlock::block_id_be(),
192            block_length: [0; 4],
193        };
194        assert_eq!(
195            block.endianness_from_block::<InterfaceDescriptionBlock>(),
196            Some(Endianness::BigEndian)
197        );
198    }
199}