pcap_file/pcapng/blocks/
enhanced_packet.rs

1//! Enhanced Packet Block (EPB).
2
3use std::borrow::Cow;
4use std::io::{Result as IoResult, Write};
5use std::time::Duration;
6
7use byteorder_slice::byteorder::WriteBytesExt;
8use byteorder_slice::result::ReadSlice;
9use byteorder_slice::ByteOrder;
10use derive_into_owned::IntoOwned;
11
12use super::block_common::{Block, PcapNgBlock};
13use super::opt_common::{CustomBinaryOption, CustomUtf8Option, PcapNgOption, UnknownOption, WriteOptTo};
14use crate::errors::PcapError;
15
16
17/// An Enhanced Packet Block (EPB) is the standard container for storing the packets coming from the network.
18#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
19pub struct EnhancedPacketBlock<'a> {
20    /// It specifies the interface this packet comes from.
21    /// 
22    /// The correct interface will be the one whose Interface Description Block
23    /// (within the current Section of the file) is identified by the same number of this field.
24    pub interface_id: u32,
25
26    /// Number of units of time that have elapsed since 1970-01-01 00:00:00 UTC.
27    pub timestamp: Duration,
28
29    /// Actual length of the packet when it was transmitted on the network.
30    pub original_len: u32,
31
32    /// The data coming from the network, including link-layer headers.
33    pub data: Cow<'a, [u8]>,
34
35    /// Options
36    pub options: Vec<EnhancedPacketOption<'a>>,
37}
38
39impl<'a> PcapNgBlock<'a> for EnhancedPacketBlock<'a> {
40    fn from_slice<B: ByteOrder>(mut slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> {
41        if slice.len() < 20 {
42            return Err(PcapError::InvalidField("EnhancedPacketBlock: block length length < 20"));
43        }
44
45        let interface_id = slice.read_u32::<B>().unwrap();
46        let timestamp_high = slice.read_u32::<B>().unwrap() as u64;
47        let timestamp_low = slice.read_u32::<B>().unwrap() as u64;
48        let timestamp = (timestamp_high << 32) + timestamp_low;
49        let captured_len = slice.read_u32::<B>().unwrap();
50        let original_len = slice.read_u32::<B>().unwrap();
51
52        let pad_len = (4 - (captured_len as usize % 4)) % 4;
53        let tot_len = captured_len as usize + pad_len;
54
55        if slice.len() < tot_len {
56            return Err(PcapError::InvalidField("EnhancedPacketBlock: captured_len + padding > block length"));
57        }
58
59        let data = &slice[..captured_len as usize];
60        slice = &slice[tot_len..];
61
62        let (slice, options) = EnhancedPacketOption::opts_from_slice::<B>(slice)?;
63        let block = EnhancedPacketBlock {
64            interface_id,
65            timestamp: Duration::from_nanos(timestamp),
66            original_len,
67            data: Cow::Borrowed(data),
68            options,
69        };
70
71        Ok((slice, block))
72    }
73
74    fn write_to<B: ByteOrder, W: Write>(&self, writer: &mut W) -> IoResult<usize> {
75        let pad_len = (4 - (&self.data.len() % 4)) % 4;
76
77        writer.write_u32::<B>(self.interface_id)?;
78
79        let timestamp = self.timestamp.as_nanos();
80        let timestamp_high = (timestamp >> 32) as u32;
81        writer.write_u32::<B>(timestamp_high)?;
82        let timestamp_low = (timestamp & 0xFFFFFFFF) as u32;
83        writer.write_u32::<B>(timestamp_low)?;
84
85        writer.write_u32::<B>(self.data.len() as u32)?;
86        writer.write_u32::<B>(self.original_len)?;
87        writer.write_all(&self.data)?;
88        writer.write_all(&[0_u8; 3][..pad_len])?;
89
90        let opt_len = EnhancedPacketOption::write_opts_to::<B, W>(&self.options, writer)?;
91
92        Ok(20 + &self.data.len() + pad_len + opt_len)
93    }
94
95    fn into_block(self) -> Block<'a> {
96        Block::EnhancedPacket(self)
97    }
98}
99
100/// The Enhanced Packet Block (EPB) options
101#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
102pub enum EnhancedPacketOption<'a> {
103    /// Comment associated with the current block
104    Comment(Cow<'a, str>),
105
106    /// 32-bit flags word containing link-layer information.
107    Flags(u32),
108
109    /// Contains a hash of the packet.
110    Hash(Cow<'a, [u8]>),
111
112    /// 64-bit integer value specifying the number of packets lost
113    /// (by the interface and the operating system) between this packet and the preceding one for
114    /// the same interface or, for the first packet for an interface, between this packet
115    /// and the start of the capture process.
116    DropCount(u64),
117
118    /// Custom option containing binary octets in the Custom Data portion
119    CustomBinary(CustomBinaryOption<'a>),
120
121    /// Custom option containing a UTF-8 string in the Custom Data portion
122    CustomUtf8(CustomUtf8Option<'a>),
123
124    /// Unknown option
125    Unknown(UnknownOption<'a>),
126}
127
128impl<'a> PcapNgOption<'a> for EnhancedPacketOption<'a> {
129    fn from_slice<B: ByteOrder>(code: u16, length: u16, mut slice: &'a [u8]) -> Result<Self, PcapError> {
130        let opt = match code {
131            1 => EnhancedPacketOption::Comment(Cow::Borrowed(std::str::from_utf8(slice)?)),
132            2 => {
133                if slice.len() != 4 {
134                    return Err(PcapError::InvalidField("EnhancedPacketOption: Flags length != 4"));
135                }
136                EnhancedPacketOption::Flags(slice.read_u32::<B>().map_err(|_| PcapError::IncompleteBuffer)?)
137            },
138            3 => EnhancedPacketOption::Hash(Cow::Borrowed(slice)),
139            4 => {
140                if slice.len() != 8 {
141                    return Err(PcapError::InvalidField("EnhancedPacketOption: DropCount length != 8"));
142                }
143                EnhancedPacketOption::DropCount(slice.read_u64::<B>().map_err(|_| PcapError::IncompleteBuffer)?)
144            },
145
146            2988 | 19372 => EnhancedPacketOption::CustomUtf8(CustomUtf8Option::from_slice::<B>(code, slice)?),
147            2989 | 19373 => EnhancedPacketOption::CustomBinary(CustomBinaryOption::from_slice::<B>(code, slice)?),
148
149            _ => EnhancedPacketOption::Unknown(UnknownOption::new(code, length, slice)),
150        };
151
152        Ok(opt)
153    }
154
155    fn write_to<B: ByteOrder, W: Write>(&self, writer: &mut W) -> IoResult<usize> {
156        match self {
157            EnhancedPacketOption::Comment(a) => a.write_opt_to::<B, W>(1, writer),
158            EnhancedPacketOption::Flags(a) => a.write_opt_to::<B, W>(2, writer),
159            EnhancedPacketOption::Hash(a) => a.write_opt_to::<B, W>(3, writer),
160            EnhancedPacketOption::DropCount(a) => a.write_opt_to::<B, W>(4, writer),
161            EnhancedPacketOption::CustomBinary(a) => a.write_opt_to::<B, W>(a.code, writer),
162            EnhancedPacketOption::CustomUtf8(a) => a.write_opt_to::<B, W>(a.code, writer),
163            EnhancedPacketOption::Unknown(a) => a.write_opt_to::<B, W>(a.code, writer),
164        }
165    }
166}