use std::borrow::Cow;
use std::error::Error;
use std::io::Write;
use byteorder_slice::ByteOrder;
use byteorder_slice::byteorder::{ReadBytesExt, WriteBytesExt};
use thiserror::Error;
use super::block_common::{Block, PcapNgBlock};
use crate::pcapng::blocks::opt_common::CommonOption;
use crate::pcapng::errors::{BlockContentParseError, PcapNgWriteError};
use crate::pcapng::{OptionEntryError, PcapNgState};
pub trait CustomPayloadCopiable<'a> {
const PEN: u32;
type FromSliceError: Error + Sync + Send + 'static;
type WriteToError: Error + Sync + Send + 'static;
fn from_slice(slice: &'a [u8]) -> Result<Option<Self>, Self::FromSliceError>
where
Self: Sized;
fn write_to<W: Write>(&self, writer: &mut W) -> Result<(), Self::WriteToError>;
fn to_bytes(&self) -> Result<Vec<u8>, CustomError>
where
Self: Sized,
{
let mut data = Vec::new();
self.write_to(&mut data).map_err(|e| CustomError {
pen: Self::PEN,
src: e.into(),
})?;
Ok(data)
}
}
pub trait CustomPayloadNonCopiable<'a> {
const PEN: u32;
type State;
type FromSliceError: Error + Sync + Send + 'static;
type WriteToError: Error + Sync + Send + 'static;
fn from_slice(state: &Self::State, slice: &'a [u8]) -> Result<Option<Self>, Self::FromSliceError>
where
Self: Sized;
fn write_to<W: Write>(&self, state: &Self::State, writer: &mut W) -> Result<(), Self::WriteToError>;
fn to_bytes(&self, state: &Self::State) -> Result<Vec<u8>, CustomError>
where
Self: Sized,
{
let mut data = Vec::new();
self.write_to(state, &mut data).map_err(|e| CustomError {
pen: Self::PEN,
src: e.into(),
})?;
Ok(data)
}
}
pub trait CustomBlockPayload<'a> {
fn into_custom_block_copiable(self) -> Result<CustomBlock<'a, true>, CustomError>
where
Self: Sized,
Self: CustomPayloadCopiable<'a>,
{
let data = self.to_bytes()?;
Ok(CustomBlock {
pen: Self::PEN,
payload: Cow::Owned(data),
})
}
fn into_custom_block_non_copiable(self, state: &Self::State) -> Result<CustomBlock<'a, false>, CustomError>
where
Self: Sized,
Self: CustomPayloadNonCopiable<'a>,
{
let data = self.to_bytes(state)?;
Ok(CustomBlock {
pen: Self::PEN,
payload: Cow::Owned(data),
})
}
}
pub trait CustomOptionPayload<'a> {
fn into_custom_binary_option_copiable(self) -> Result<CustomBinaryOption<'a, true>, CustomError>
where
Self: Sized,
Self: CustomPayloadCopiable<'a>,
{
let data = self.to_bytes()?;
Ok(CustomBinaryOption {
pen: Self::PEN,
value: Cow::Owned(data),
})
}
fn into_custom_binary_option_non_copiable(
self,
state: &Self::State,
) -> Result<CustomBinaryOption<'a, false>, CustomError>
where
Self: Sized,
Self: CustomPayloadNonCopiable<'a>,
{
let data = self.to_bytes(state)?;
Ok(CustomBinaryOption {
pen: Self::PEN,
value: Cow::Owned(data),
})
}
}
#[derive(Debug, Error)]
#[error("Error in custom conversion for PEN {pen:#X}")]
pub struct CustomError {
pub pen: u32,
#[source]
pub src: Box<dyn Error + Sync + Send + 'static>,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CustomBlock<'a, const COPIABLE: bool> {
pub pen: u32,
pub payload: Cow<'a, [u8]>,
}
impl<'a, const COPIABLE: bool> CustomBlock<'a, COPIABLE> {
pub fn into_owned(self) -> CustomBlock<'static, COPIABLE> {
CustomBlock {
pen: self.pen,
payload: Cow::Owned(self.payload.into_owned()),
}
}
}
impl<'a> CustomBlock<'a, true> {
pub fn interpret<T>(&'a self) -> Result<Option<T>, CustomError>
where
T: CustomPayloadCopiable<'a> + CustomBlockPayload<'a>,
{
if self.pen != T::PEN {
return Ok(None);
}
T::from_slice(&self.payload).map_err(|e| CustomError {
pen: T::PEN,
src: e.into(),
})
}
}
impl<'a> CustomBlock<'a, false> {
pub fn interpret<T>(&'a self, state: &T::State) -> Result<Option<T>, CustomError>
where
T: CustomPayloadNonCopiable<'a> + CustomBlockPayload<'a>,
{
if self.pen != T::PEN {
return Ok(None);
}
T::from_slice(state, &self.payload).map_err(|e| CustomError {
pen: T::PEN,
src: e.into(),
})
}
}
impl<'a, const COPIABLE: bool> PcapNgBlock<'a> for CustomBlock<'a, COPIABLE> {
fn from_slice<B: ByteOrder>(
_state: &PcapNgState,
mut slice: &'a [u8],
) -> Result<(&'a [u8], Self), BlockContentParseError>
where
Self: Sized,
{
if slice.len() < 4 {
return Err(BlockContentParseError::BlockContentTooSmall {
needed: 4,
actual: slice.len(),
});
}
let pen = slice.read_u32::<B>().unwrap();
Ok((
&[],
CustomBlock {
pen,
payload: Cow::Borrowed(slice),
},
))
}
fn write_to<B: ByteOrder, W: Write>(
&self,
_state: &PcapNgState,
writer: &mut W,
) -> Result<usize, PcapNgWriteError> {
writer.write_u32::<B>(self.pen)?;
writer.write_all(&self.payload)?;
Ok(4 + self.payload.len())
}
fn into_block(self) -> Block<'a> {
if COPIABLE {
Block::CustomCopiable(CustomBlock {
pen: self.pen,
payload: self.payload,
})
} else {
Block::CustomNonCopiable(CustomBlock {
pen: self.pen,
payload: self.payload,
})
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CustomBinaryOption<'a, const COPIABLE: bool> {
pub pen: u32,
pub value: Cow<'a, [u8]>,
}
impl<'a, const COPIABLE: bool> CustomBinaryOption<'a, COPIABLE> {
pub fn from_slice<B: ByteOrder>(mut src: &'a [u8]) -> Result<Self, OptionEntryError> {
let pen = src.read_u32::<B>().map_err(|_| OptionEntryError::WrongSize {
expected: 4,
actual: src.len(),
})?;
let opt = CustomBinaryOption {
pen,
value: Cow::Borrowed(src),
};
Ok(opt)
}
pub fn into_owned(self) -> CustomBinaryOption<'static, COPIABLE> {
CustomBinaryOption {
pen: self.pen,
value: Cow::Owned(self.value.into_owned()),
}
}
}
impl<'a> CustomBinaryOption<'a, true> {
pub fn interpret<T>(&'a self) -> Result<Option<T>, CustomError>
where
T: CustomPayloadCopiable<'a> + CustomOptionPayload<'a>,
{
if self.pen != T::PEN {
return Ok(None);
}
T::from_slice(&self.value).map_err(|e| CustomError {
pen: T::PEN,
src: e.into(),
})
}
pub fn into_common_option(self) -> CommonOption<'a> {
CommonOption::CustomBinaryCopiable(self)
}
}
impl<'a> CustomBinaryOption<'a, false> {
pub fn interpret<T>(&'a self, state: &T::State) -> Result<Option<T>, CustomError>
where
T: CustomPayloadNonCopiable<'a> + CustomOptionPayload<'a>,
{
if self.pen != T::PEN {
return Ok(None);
}
T::from_slice(state, &self.value).map_err(|e| CustomError {
pen: T::PEN,
src: e.into(),
})
}
pub fn into_common_option(self) -> CommonOption<'a> {
CommonOption::CustomBinaryNonCopiable(self)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct CustomUtf8Option<'a, const COPIABLE: bool> {
pub pen: u32,
pub value: Cow<'a, str>,
}
impl<'a, const COPIABLE: bool> CustomUtf8Option<'a, COPIABLE> {
pub fn from_slice<B: ByteOrder>(mut src: &'a [u8]) -> Result<Self, OptionEntryError> {
let pen = src.read_u32::<B>().map_err(|_| OptionEntryError::WrongSize {
expected: 4,
actual: src.len(),
})?;
let opt = CustomUtf8Option {
pen,
value: Cow::Borrowed(std::str::from_utf8(src)?),
};
Ok(opt)
}
pub fn into_owned(self) -> CustomUtf8Option<'static, COPIABLE> {
CustomUtf8Option {
pen: self.pen,
value: Cow::Owned(self.value.into_owned()),
}
}
}