pcap_file/pcapng/blocks/
block_common.rs

1//! Common block types.
2
3use std::borrow::Cow;
4use std::io::{Result as IoResult, Write};
5
6use byteorder_slice::byteorder::WriteBytesExt;
7use byteorder_slice::result::ReadSlice;
8use byteorder_slice::{BigEndian, ByteOrder, LittleEndian};
9use derive_into_owned::IntoOwned;
10
11use super::enhanced_packet::EnhancedPacketBlock;
12use super::interface_description::InterfaceDescriptionBlock;
13use super::interface_statistics::InterfaceStatisticsBlock;
14use super::name_resolution::NameResolutionBlock;
15use super::packet::PacketBlock;
16use super::section_header::SectionHeaderBlock;
17use super::simple_packet::SimplePacketBlock;
18use super::systemd_journal_export::SystemdJournalExportBlock;
19use super::unknown::UnknownBlock;
20use crate::errors::PcapError;
21use crate::PcapResult;
22
23
24/// Section header block type
25pub const SECTION_HEADER_BLOCK: u32 = 0x0A0D0D0A;
26/// Interface description block type
27pub const INTERFACE_DESCRIPTION_BLOCK: u32 = 0x00000001;
28/// Packet block type
29pub const PACKET_BLOCK: u32 = 0x00000002;
30/// Simple packet block type
31pub const SIMPLE_PACKET_BLOCK: u32 = 0x00000003;
32/// Name resolution block type
33pub const NAME_RESOLUTION_BLOCK: u32 = 0x00000004;
34/// Interface statistic block type
35pub const INTERFACE_STATISTIC_BLOCK: u32 = 0x00000005;
36/// Enhanced packet block type
37pub const ENHANCED_PACKET_BLOCK: u32 = 0x00000006;
38/// Systemd journal export block type
39pub const SYSTEMD_JOURNAL_EXPORT_BLOCK: u32 = 0x00000009;
40
41//   0               1               2               3
42//   0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
43//  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
44//  |                          Block Type                           |
45//  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
46//  |                      Block Total Length                       |
47//  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48//  /                          Block Body                           /
49//  /          /* variable length, aligned to 32 bits */            /
50//  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
51//  |                      Block Total Length                       |
52//  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
53/// PcapNg Block
54#[derive(Clone, Debug)]
55pub struct RawBlock<'a> {
56    /// Type field
57    pub type_: u32,
58    /// Initial length field
59    pub initial_len: u32,
60    /// Body of the block
61    pub body: Cow<'a, [u8]>,
62    /// Trailer length field
63    pub trailer_len: u32,
64}
65
66impl<'a> RawBlock<'a> {
67    /// Parses a borrowed [`RawBlock`] from a slice.
68    pub fn from_slice<B: ByteOrder>(mut slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> {
69        if slice.len() < 12 {
70            return Err(PcapError::IncompleteBuffer);
71        }
72
73        let type_ = slice.read_u32::<B>().unwrap();
74
75        // Special case for the section header because we don't know the endianness yet
76        if type_ == SECTION_HEADER_BLOCK {
77            let initial_len = slice.read_u32::<BigEndian>().unwrap();
78
79            // Check the first field of the Section header to find the endianness
80            let mut tmp_slice = slice;
81            let magic = tmp_slice.read_u32::<BigEndian>().unwrap();
82            let res = match magic {
83                0x1A2B3C4D => inner_parse::<BigEndian>(slice, type_, initial_len),
84                0x4D3C2B1A => inner_parse::<LittleEndian>(slice, type_, initial_len.swap_bytes()),
85                _ => Err(PcapError::InvalidField("SectionHeaderBlock: invalid magic number")),
86            };
87
88            return res;
89        }
90        else {
91            let initial_len = slice.read_u32::<B>().map_err(|_| PcapError::IncompleteBuffer)?;
92            return inner_parse::<B>(slice, type_, initial_len);
93        };
94
95        // Section Header parsing
96        fn inner_parse<B: ByteOrder>(slice: &[u8], type_: u32, initial_len: u32) -> Result<(&[u8], RawBlock<'_>), PcapError> {
97            if (initial_len % 4) != 0 {
98                return Err(PcapError::InvalidField("Block: (initial_len % 4) != 0"));
99            }
100
101            if initial_len < 12 {
102                return Err(PcapError::InvalidField("Block: initial_len < 12"));
103            }
104
105            // Check if there is enough data for the body and the trailer_len
106            if slice.len() < initial_len as usize - 8 {
107                return Err(PcapError::IncompleteBuffer);
108            }
109
110            let body_len = initial_len - 12;
111            let body = &slice[..body_len as usize];
112
113            let mut rem = &slice[body_len as usize..];
114
115            let trailer_len = rem.read_u32::<B>().unwrap();
116
117            if initial_len != trailer_len {
118                return Err(PcapError::InvalidField("Block: initial_length != trailer_length"));
119            }
120
121            let block = RawBlock { type_, initial_len, body: Cow::Borrowed(body), trailer_len };
122
123            Ok((rem, block))
124        }
125    }
126
127    /// Writes a [`RawBlock`] to a writer.
128    ///
129    /// Uses the endianness of the header.
130    pub fn write_to<B: ByteOrder, W: Write>(&self, writer: &mut W) -> IoResult<usize> {
131        writer.write_u32::<B>(self.type_)?;
132        writer.write_u32::<B>(self.initial_len)?;
133        writer.write_all(&self.body[..])?;
134        writer.write_u32::<B>(self.trailer_len)?;
135
136        Ok(self.body.len() + 6)
137    }
138
139    /// Tries to convert a [`RawBlock`] into a [`Block`]
140    pub fn try_into_block<B: ByteOrder>(self) -> PcapResult<Block<'a>> {
141        Block::try_from_raw_block::<B>(self)
142    }
143}
144
145/// PcapNg parsed blocks
146#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
147pub enum Block<'a> {
148    /// Section Header block
149    SectionHeader(SectionHeaderBlock<'a>),
150    /// Interface Description block
151    InterfaceDescription(InterfaceDescriptionBlock<'a>),
152    /// Packet block
153    Packet(PacketBlock<'a>),
154    /// Simple packet block
155    SimplePacket(SimplePacketBlock<'a>),
156    /// Name Resolution block
157    NameResolution(NameResolutionBlock<'a>),
158    /// Interface statistics block
159    InterfaceStatistics(InterfaceStatisticsBlock<'a>),
160    /// Enhanced packet block
161    EnhancedPacket(EnhancedPacketBlock<'a>),
162    /// Systemd Journal Export block
163    SystemdJournalExport(SystemdJournalExportBlock<'a>),
164    /// Unknown block
165    Unknown(UnknownBlock<'a>),
166}
167
168impl<'a> Block<'a> {
169    /// Parses a [`Block`] from a slice
170    pub fn from_slice<B: ByteOrder>(slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> {
171        let (rem, raw_block) = RawBlock::from_slice::<B>(slice)?;
172        let block = Self::try_from_raw_block::<B>(raw_block)?;
173
174        Ok((rem, block))
175    }
176
177    /// Writes a [`Block`] to a writer.
178    pub fn write_to<B: ByteOrder, W: Write>(&self, writer: &mut W) -> IoResult<usize> {
179        return match self {
180            Self::SectionHeader(b) => inner_write_to::<B, _, W>(b, SECTION_HEADER_BLOCK, writer),
181            Self::InterfaceDescription(b) => inner_write_to::<B, _, W>(b, INTERFACE_DESCRIPTION_BLOCK, writer),
182            Self::Packet(b) => inner_write_to::<B, _, W>(b, PACKET_BLOCK, writer),
183            Self::SimplePacket(b) => inner_write_to::<B, _, W>(b, SIMPLE_PACKET_BLOCK, writer),
184            Self::NameResolution(b) => inner_write_to::<B, _, W>(b, NAME_RESOLUTION_BLOCK, writer),
185            Self::InterfaceStatistics(b) => inner_write_to::<B, _, W>(b, INTERFACE_STATISTIC_BLOCK, writer),
186            Self::EnhancedPacket(b) => inner_write_to::<B, _, W>(b, ENHANCED_PACKET_BLOCK, writer),
187            Self::SystemdJournalExport(b) => inner_write_to::<B, _, W>(b, SYSTEMD_JOURNAL_EXPORT_BLOCK, writer),
188            Self::Unknown(b) => inner_write_to::<B, _, W>(b, b.type_, writer),
189        };
190
191        fn inner_write_to<'a, B: ByteOrder, BL: PcapNgBlock<'a>, W: Write>(block: &BL, block_code: u32, writer: &mut W) -> IoResult<usize> {
192            // Fake write to compute the data length
193            let data_len = block.write_to::<B, _>(&mut std::io::sink()).unwrap();
194            let pad_len = (4 - (data_len % 4)) % 4;
195
196            let block_len = data_len + pad_len + 12;
197
198            writer.write_u32::<B>(block_code)?;
199            writer.write_u32::<B>(block_len as u32)?;
200            block.write_to::<B, _>(writer)?;
201            writer.write_all(&[0_u8; 3][..pad_len])?;
202            writer.write_u32::<B>(block_len as u32)?;
203
204            Ok(block_len)
205        }
206    }
207
208    /// Tries to create a [`Block`] from a [`RawBlock`].
209    ///
210    /// The RawBlock must be Borrowed.
211    pub fn try_from_raw_block<B: ByteOrder>(raw_block: RawBlock<'a>) -> Result<Block<'a>, PcapError> {
212        let body = match raw_block.body {
213            Cow::Borrowed(b) => b,
214            _ => panic!("The raw block is not borrowed"),
215        };
216
217        match raw_block.type_ {
218            SECTION_HEADER_BLOCK => {
219                let (_, block) = SectionHeaderBlock::from_slice::<BigEndian>(body)?;
220                Ok(Block::SectionHeader(block))
221            },
222            INTERFACE_DESCRIPTION_BLOCK => {
223                let (_, block) = InterfaceDescriptionBlock::from_slice::<B>(body)?;
224                Ok(Block::InterfaceDescription(block))
225            },
226            PACKET_BLOCK => {
227                let (_, block) = PacketBlock::from_slice::<B>(body)?;
228                Ok(Block::Packet(block))
229            },
230            SIMPLE_PACKET_BLOCK => {
231                let (_, block) = SimplePacketBlock::from_slice::<B>(body)?;
232                Ok(Block::SimplePacket(block))
233            },
234            NAME_RESOLUTION_BLOCK => {
235                let (_, block) = NameResolutionBlock::from_slice::<B>(body)?;
236                Ok(Block::NameResolution(block))
237            },
238            INTERFACE_STATISTIC_BLOCK => {
239                let (_, block) = InterfaceStatisticsBlock::from_slice::<B>(body)?;
240                Ok(Block::InterfaceStatistics(block))
241            },
242            ENHANCED_PACKET_BLOCK => {
243                let (_, block) = EnhancedPacketBlock::from_slice::<B>(body)?;
244                Ok(Block::EnhancedPacket(block))
245            },
246            SYSTEMD_JOURNAL_EXPORT_BLOCK => {
247                let (_, block) = SystemdJournalExportBlock::from_slice::<B>(body)?;
248                Ok(Block::SystemdJournalExport(block))
249            },
250            type_ => Ok(Block::Unknown(UnknownBlock::new(type_, raw_block.initial_len, body))),
251        }
252    }
253
254    /// Tries to downcasts the current block into an [`EnhancedPacketBlock`]
255    pub fn into_enhanced_packet(self) -> Option<EnhancedPacketBlock<'a>> {
256        match self {
257            Block::EnhancedPacket(a) => Some(a),
258            _ => None,
259        }
260    }
261
262    /// Tries to downcasts the current block into an [`InterfaceDescriptionBlock`]
263    pub fn into_interface_description(self) -> Option<InterfaceDescriptionBlock<'a>> {
264        match self {
265            Block::InterfaceDescription(a) => Some(a),
266            _ => None,
267        }
268    }
269
270    /// Tries to downcasts the current block into an [`InterfaceStatisticsBlock`]
271    pub fn into_interface_statistics(self) -> Option<InterfaceStatisticsBlock<'a>> {
272        match self {
273            Block::InterfaceStatistics(a) => Some(a),
274            _ => None,
275        }
276    }
277
278    /// Tries to downcast the current block into an [`NameResolutionBlock`], if possible
279    pub fn into_name_resolution(self) -> Option<NameResolutionBlock<'a>> {
280        match self {
281            Block::NameResolution(a) => Some(a),
282            _ => None,
283        }
284    }
285
286    /// Tries to downcast the current block into an [`PacketBlock`], if possible
287    pub fn into_packet(self) -> Option<PacketBlock<'a>> {
288        match self {
289            Block::Packet(a) => Some(a),
290            _ => None,
291        }
292    }
293
294    /// Tries to downcast the current block into an [`SectionHeaderBlock`], if possible
295    pub fn into_section_header(self) -> Option<SectionHeaderBlock<'a>> {
296        match self {
297            Block::SectionHeader(a) => Some(a),
298            _ => None,
299        }
300    }
301
302    /// Tries to downcast the current block into an [`SimplePacketBlock`], if possible
303    pub fn into_simple_packet(self) -> Option<SimplePacketBlock<'a>> {
304        match self {
305            Block::SimplePacket(a) => Some(a),
306            _ => None,
307        }
308    }
309
310    /// Tries to downcast the current block into an [`SystemdJournalExportBlock`], if possible
311    pub fn into_systemd_journal_export(self) -> Option<SystemdJournalExportBlock<'a>> {
312        match self {
313            Block::SystemdJournalExport(a) => Some(a),
314            _ => None,
315        }
316    }
317}
318
319
320/// Common interface for the PcapNg blocks
321pub trait PcapNgBlock<'a> {
322    /// Parse a new block from a slice
323    fn from_slice<B: ByteOrder>(slice: &'a [u8]) -> Result<(&[u8], Self), PcapError>
324    where
325        Self: std::marker::Sized;
326
327    /// Write the content of a block into a writer
328    fn write_to<B: ByteOrder, W: Write>(&self, writer: &mut W) -> IoResult<usize>;
329
330    /// Convert a block into the [`Block`] enumeration
331    fn into_block(self) -> Block<'a>;
332}