use std::borrow::Cow;
use std::io::Write;
use std::time::Duration;
use byteorder_slice::ByteOrder;
use byteorder_slice::byteorder::WriteBytesExt;
use byteorder_slice::result::ReadSlice;
use derive_into_owned::IntoOwned;
use super::block_common::{Block, PcapNgBlock};
use super::opt_common::{CommonOption, PcapNgOption, WriteOpt};
use crate::pcapng::errors::{BlockContentParseError, OptionEntryError, PcapNgWriteError};
use crate::pcapng::{ContentValidationError, PcapNgState};
#[derive(Clone, Debug, Default, IntoOwned, Eq, PartialEq)]
pub struct EnhancedPacketBlock<'a> {
pub interface_id: u32,
pub timestamp: Duration,
pub original_len: u32,
pub data: Cow<'a, [u8]>,
pub options: Vec<EnhancedPacketOption<'a>>,
}
impl<'a> PcapNgBlock<'a> for EnhancedPacketBlock<'a> {
fn from_slice<B: ByteOrder>(
state: &PcapNgState,
mut slice: &'a [u8],
) -> Result<(&'a [u8], Self), BlockContentParseError> {
if slice.len() < 20 {
return Err(BlockContentParseError::BlockContentTooSmall {
needed: 20,
actual: slice.len(),
});
}
let interface_id = slice.read_u32::<B>().expect("slice length checked above");
if (interface_id as usize) >= state.interfaces.len() {
return Err(ContentValidationError::InvalidInterfaceId(interface_id).into());
}
let timestamp_high = slice.read_u32::<B>().expect("slice length checked above");
let timestamp_low = slice.read_u32::<B>().expect("slice length checked above");
let timestamp = state.decode_timestamp(interface_id, timestamp_high, timestamp_low)?;
let captured_len = slice.read_u32::<B>().expect("slice length checked above");
let original_len = slice.read_u32::<B>().expect("slice length checked above");
if original_len < captured_len {
return Err(ContentValidationError::InvalidOriginalLen(original_len, captured_len as usize).into());
}
let pad_len = (4 - (captured_len as usize % 4)) % 4;
let tot_len = captured_len as usize + pad_len;
if slice.len() < tot_len {
return Err(BlockContentParseError::BlockContentTooSmall {
needed: tot_len,
actual: slice.len(),
});
}
let data = &slice[..captured_len as usize];
slice = &slice[tot_len..];
let (slice, options) = EnhancedPacketOption::opts_from_slice::<B>(state, Some(interface_id), slice)?;
let block = EnhancedPacketBlock {
interface_id,
timestamp,
original_len,
data: Cow::Borrowed(data),
options,
};
Ok((slice, block))
}
fn write_to<B: ByteOrder, W: Write>(&self, state: &PcapNgState, writer: &mut W) -> Result<usize, PcapNgWriteError> {
if (self.interface_id as usize) >= state.interfaces.len() {
return Err(PcapNgWriteError::validation_error(
"EnhancedPacketBlock.interface_id",
crate::pcapng::ContentValidationError::InvalidInterfaceId(self.interface_id),
));
}
if (self.original_len as usize) < self.data.len() {
return Err(PcapNgWriteError::validation_error(
"EnhancedPacketBlock.original_len",
crate::pcapng::ContentValidationError::InvalidOriginalLen(self.original_len, self.data.len()),
));
}
let (timestamp_high, timestamp_low) = state
.encode_timestamp(self.interface_id, self.timestamp)
.map_err(|source| PcapNgWriteError::validation_error("EnhancedPacketBlock.timestamp", source))?;
let pad_len = (4 - (&self.data.len() % 4)) % 4;
writer.write_u32::<B>(self.interface_id)?;
writer.write_u32::<B>(timestamp_high)?;
writer.write_u32::<B>(timestamp_low)?;
writer.write_u32::<B>(self.data.len() as u32)?;
writer.write_u32::<B>(self.original_len)?;
writer.write_all(&self.data)?;
writer.write_all(&[0_u8; 3][..pad_len])?;
let opt_len =
EnhancedPacketOption::write_opts_to::<B, W>(&self.options, state, Some(self.interface_id), writer)?;
Ok(20 + &self.data.len() + pad_len + opt_len)
}
fn into_block(self) -> Block<'a> {
Block::EnhancedPacket(self)
}
}
#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
pub enum EnhancedPacketOption<'a> {
Flags(u32),
Hash(Cow<'a, [u8]>),
DropCount(u64),
Common(CommonOption<'a>),
}
impl EnhancedPacketOption<'_> {
const FLAGS: u16 = 2;
const HASH: u16 = 3;
const DROP_COUNT: u16 = 4;
}
impl<'a> PcapNgOption<'a> for EnhancedPacketOption<'a> {
fn from_slice<B: ByteOrder>(
_state: &PcapNgState,
_interface_id: Option<u32>,
code: u16,
mut slice: &'a [u8],
) -> Result<Self, OptionEntryError> {
let opt = match code {
Self::FLAGS => {
if slice.len() != 4 {
return Err(OptionEntryError::WrongSize {
expected: 4,
actual: slice.len(),
});
}
EnhancedPacketOption::Flags(slice.read_u32::<B>().expect("slice length checked above"))
}
Self::HASH => EnhancedPacketOption::Hash(Cow::Borrowed(slice)),
Self::DROP_COUNT => {
if slice.len() != 8 {
return Err(OptionEntryError::WrongSize {
expected: 8,
actual: slice.len(),
});
}
EnhancedPacketOption::DropCount(slice.read_u64::<B>().expect("slice length checked above"))
}
_ => EnhancedPacketOption::Common(CommonOption::new::<B>(code, slice)?),
};
Ok(opt)
}
fn write_to<B: ByteOrder, W: Write>(
&self,
_state: &PcapNgState,
_interface_id: Option<u32>,
writer: &mut W,
) -> Result<usize, PcapNgWriteError> {
match self {
EnhancedPacketOption::Flags(a) => a.write_opt::<B, W>(2, writer),
EnhancedPacketOption::Hash(a) => a.write_opt::<B, W>(3, writer),
EnhancedPacketOption::DropCount(a) => a.write_opt::<B, W>(4, writer),
EnhancedPacketOption::Common(a) => a.write_opt::<B, W>(a.code(), writer),
}
}
fn code_name(code: u16) -> &'static str {
match code {
Self::FLAGS => "Flags",
Self::HASH => "Hash",
Self::DROP_COUNT => "Drop Count",
_ => CommonOption::code_name(code),
}
}
}