#![allow(clippy::cast_lossless)]
use std::borrow::Cow;
use std::io::Result as IoResult;
use std::io::Write;
use byteorder::{ReadBytesExt, WriteBytesExt};
use derive_into_owned::IntoOwned;
use byteorder::ByteOrder;
#[cfg(feature = "tokio")]
use tokio::io::AsyncWrite;
#[cfg(feature = "tokio")]
use tokio_byteorder::{AsyncReadBytesExt, AsyncWriteBytesExt};
#[cfg(feature = "tokio")]
use super::block_common::AsyncPcapNgBlock;
use super::block_common::{Block, PcapNgBlock};
#[cfg(feature = "tokio")]
use super::opt_common::{AsyncPcapNgOption, AsyncWriteOptTo};
use super::opt_common::{CustomBinaryOption, CustomUtf8Option, PcapNgOption, UnknownOption, WriteOptTo};
use crate::errors::PcapError;
use crate::DataLink;
#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
pub struct InterfaceDescriptionBlock<'a> {
pub linktype: DataLink,
pub snaplen: u32,
pub options: Vec<InterfaceDescriptionOption<'a>>,
pub ts_divide: Option<u64>,
pub ts_offset: Option<u64>,
}
fn ts_resol_to_divide(resol: u8) -> u64 {
match resol & 0x80 {
0 => 10_u64.pow(resol.into()),
_ => 2_u64.pow((resol & 0b01111111).into()),
}
}
impl<'a> PcapNgBlock<'a> for InterfaceDescriptionBlock<'a> {
fn from_slice<B: ByteOrder>(mut slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> {
if slice.len() < 8 {
return Err(PcapError::InvalidField("InterfaceDescriptionBlock: block length < 8"));
}
let linktype = (ReadBytesExt::read_u16::<B>(&mut slice).unwrap() as u32).into();
let reserved = ReadBytesExt::read_u16::<B>(&mut slice).unwrap();
if reserved != 0 {
return Err(PcapError::InvalidField("InterfaceDescriptionBlock: reserved != 0"));
}
let snaplen = ReadBytesExt::read_u32::<B>(&mut slice).unwrap();
let (slice, options) = InterfaceDescriptionOption::opts_from_slice::<B>(slice)?;
let mut ts_divide = None;
if let Some(option) = options.iter().find(|o| matches!(o, InterfaceDescriptionOption::IfTsResol(_))) {
if let InterfaceDescriptionOption::IfTsResol(resol) = option {
ts_divide = Some(ts_resol_to_divide(*resol));
}
}
let mut ts_offset = None;
if let Some(option) = options.iter().find(|o| matches!(o, InterfaceDescriptionOption::IfTsOffset(_))) {
if let InterfaceDescriptionOption::IfTsOffset(offset) = option {
ts_offset = Some(*offset);
}
}
let block = InterfaceDescriptionBlock { linktype, snaplen, options, ts_divide, ts_offset };
Ok((slice, block))
}
fn write_to<B: ByteOrder, W: Write>(&self, writer: &mut W) -> IoResult<usize> {
writer.write_u16::<B>(u32::from(self.linktype) as u16)?;
writer.write_u16::<B>(0)?;
writer.write_u32::<B>(self.snaplen)?;
let opt_len = InterfaceDescriptionOption::write_opts_to::<B, W>(&self.options, writer)?;
Ok(8 + opt_len)
}
fn into_block(self) -> Block<'a> {
Block::InterfaceDescription(self)
}
}
#[cfg(feature = "tokio")]
#[async_trait::async_trait]
impl<'a> AsyncPcapNgBlock<'a> for InterfaceDescriptionBlock<'a> {
async fn async_from_slice<B: ByteOrder + Send>(mut slice: &'a [u8]) -> Result<(&'a [u8], Self), PcapError> {
if slice.len() < 8 {
return Err(PcapError::InvalidField("InterfaceDescriptionBlock: block length < 8"));
}
let linktype = (AsyncReadBytesExt::read_u16::<B>(&mut slice).await.unwrap() as u32).into();
let reserved = AsyncReadBytesExt::read_u16::<B>(&mut slice).await.unwrap();
if reserved != 0 {
return Err(PcapError::InvalidField("InterfaceDescriptionBlock: reserved != 0"));
}
let snaplen = AsyncReadBytesExt::read_u32::<B>(&mut slice).await.unwrap();
let (slice, options) = InterfaceDescriptionOption::async_opts_from_slice::<B>(slice).await?;
let mut ts_divide = None;
if let Some(option) = options.iter().find(|o| matches!(o, InterfaceDescriptionOption::IfTsResol(_))) {
if let InterfaceDescriptionOption::IfTsResol(resol) = option {
ts_divide = Some(ts_resol_to_divide(*resol));
}
}
let mut ts_offset = None;
if let Some(option) = options.iter().find(|o| matches!(o, InterfaceDescriptionOption::IfTsOffset(_))) {
if let InterfaceDescriptionOption::IfTsOffset(offset) = option {
ts_offset = Some(*offset);
}
}
let block = InterfaceDescriptionBlock { linktype, snaplen, options, ts_divide, ts_offset };
Ok((slice, block))
}
async fn async_write_to<B: ByteOrder, W: AsyncWrite + Unpin + Send>(&self, writer: &mut W) -> IoResult<usize> {
writer.write_u16::<B>(u32::from(self.linktype) as u16).await?;
writer.write_u16::<B>(0).await?;
writer.write_u32::<B>(self.snaplen).await?;
let opt_len = InterfaceDescriptionOption::async_write_opts_to::<B, W>(&self.options, writer).await?;
Ok(8 + opt_len)
}
}
impl InterfaceDescriptionBlock<'static> {
pub fn new(linktype: DataLink, snaplen: u32) -> Self {
Self { linktype, snaplen, options: vec![], ts_divide: None, ts_offset: None }
}
}
#[derive(Clone, Debug, IntoOwned, Eq, PartialEq)]
pub enum InterfaceDescriptionOption<'a> {
Comment(Cow<'a, str>),
IfName(Cow<'a, str>),
IfDescription(Cow<'a, str>),
IfIpv4Addr(Cow<'a, [u8]>),
IfIpv6Addr(Cow<'a, [u8]>),
IfMacAddr(Cow<'a, [u8]>),
IfEuIAddr(u64),
IfSpeed(u64),
IfTsResol(u8),
IfTzone(u32),
IfFilter(Cow<'a, [u8]>),
IfOs(Cow<'a, str>),
IfFcsLen(u8),
IfTsOffset(u64),
IfHardware(Cow<'a, str>),
CustomBinary(CustomBinaryOption<'a>),
CustomUtf8(CustomUtf8Option<'a>),
Unknown(UnknownOption<'a>),
}
impl<'a> PcapNgOption<'a> for InterfaceDescriptionOption<'a> {
fn from_slice<B: ByteOrder>(code: u16, length: u16, mut slice: &'a [u8]) -> Result<Self, PcapError> {
let opt = match code {
1 => InterfaceDescriptionOption::Comment(Cow::Borrowed(std::str::from_utf8(slice)?)),
2 => InterfaceDescriptionOption::IfName(Cow::Borrowed(std::str::from_utf8(slice)?)),
3 => InterfaceDescriptionOption::IfDescription(Cow::Borrowed(std::str::from_utf8(slice)?)),
4 => {
if slice.len() != 8 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfIpv4Addr length != 8"));
}
InterfaceDescriptionOption::IfIpv4Addr(Cow::Borrowed(slice))
},
5 => {
if slice.len() != 17 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfIpv6Addr length != 17"));
}
InterfaceDescriptionOption::IfIpv6Addr(Cow::Borrowed(slice))
},
6 => {
if slice.len() != 6 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfMacAddr length != 6"));
}
InterfaceDescriptionOption::IfMacAddr(Cow::Borrowed(slice))
},
7 => {
if slice.len() != 8 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfEuIAddr length != 8"));
}
InterfaceDescriptionOption::IfEuIAddr(ReadBytesExt::read_u64::<B>(&mut slice).map_err(|_| PcapError::IncompleteBuffer)?)
},
8 => {
if slice.len() != 8 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfSpeed length != 8"));
}
InterfaceDescriptionOption::IfSpeed(ReadBytesExt::read_u64::<B>(&mut slice).map_err(|_| PcapError::IncompleteBuffer)?)
},
9 => {
if slice.len() != 1 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfTsResol length != 1"));
}
InterfaceDescriptionOption::IfTsResol(ReadBytesExt::read_u8(&mut slice).map_err(|_| PcapError::IncompleteBuffer)?)
},
10 => {
if slice.len() != 1 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfTzone length != 1"));
}
InterfaceDescriptionOption::IfTzone(ReadBytesExt::read_u32::<B>(&mut slice).map_err(|_| PcapError::IncompleteBuffer)?)
},
11 => {
if slice.is_empty() {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfFilter is empty"));
}
InterfaceDescriptionOption::IfFilter(Cow::Borrowed(slice))
},
12 => InterfaceDescriptionOption::IfOs(Cow::Borrowed(std::str::from_utf8(slice)?)),
13 => {
if slice.len() != 1 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfFcsLen length != 1"));
}
InterfaceDescriptionOption::IfFcsLen(ReadBytesExt::read_u8(&mut slice).map_err(|_| PcapError::IncompleteBuffer)?)
},
14 => {
if slice.len() != 8 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfTsOffset length != 8"));
}
InterfaceDescriptionOption::IfTsOffset(ReadBytesExt::read_u64::<B>(&mut slice).map_err(|_| PcapError::IncompleteBuffer)?)
},
15 => InterfaceDescriptionOption::IfHardware(Cow::Borrowed(std::str::from_utf8(slice)?)),
2988 | 19372 => InterfaceDescriptionOption::CustomUtf8(CustomUtf8Option::from_slice::<B>(code, slice)?),
2989 | 19373 => InterfaceDescriptionOption::CustomBinary(CustomBinaryOption::from_slice::<B>(code, slice)?),
_ => InterfaceDescriptionOption::Unknown(UnknownOption::new(code, length, slice)),
};
Ok(opt)
}
fn write_to<B: ByteOrder, W: Write>(&self, writer: &mut W) -> IoResult<usize> {
match self {
InterfaceDescriptionOption::Comment(a) => a.write_opt_to::<B, W>(1, writer),
InterfaceDescriptionOption::IfName(a) => a.write_opt_to::<B, W>(2, writer),
InterfaceDescriptionOption::IfDescription(a) => a.write_opt_to::<B, W>(3, writer),
InterfaceDescriptionOption::IfIpv4Addr(a) => a.write_opt_to::<B, W>(4, writer),
InterfaceDescriptionOption::IfIpv6Addr(a) => a.write_opt_to::<B, W>(5, writer),
InterfaceDescriptionOption::IfMacAddr(a) => a.write_opt_to::<B, W>(6, writer),
InterfaceDescriptionOption::IfEuIAddr(a) => a.write_opt_to::<B, W>(7, writer),
InterfaceDescriptionOption::IfSpeed(a) => a.write_opt_to::<B, W>(8, writer),
InterfaceDescriptionOption::IfTsResol(a) => a.write_opt_to::<B, W>(9, writer),
InterfaceDescriptionOption::IfTzone(a) => a.write_opt_to::<B, W>(10, writer),
InterfaceDescriptionOption::IfFilter(a) => a.write_opt_to::<B, W>(11, writer),
InterfaceDescriptionOption::IfOs(a) => a.write_opt_to::<B, W>(12, writer),
InterfaceDescriptionOption::IfFcsLen(a) => a.write_opt_to::<B, W>(13, writer),
InterfaceDescriptionOption::IfTsOffset(a) => a.write_opt_to::<B, W>(14, writer),
InterfaceDescriptionOption::IfHardware(a) => a.write_opt_to::<B, W>(15, writer),
InterfaceDescriptionOption::CustomBinary(a) => a.write_opt_to::<B, W>(a.code, writer),
InterfaceDescriptionOption::CustomUtf8(a) => a.write_opt_to::<B, W>(a.code, writer),
InterfaceDescriptionOption::Unknown(a) => a.write_opt_to::<B, W>(a.code, writer),
}
}
}
#[cfg(feature = "tokio")]
#[async_trait::async_trait]
impl<'a> AsyncPcapNgOption<'a> for InterfaceDescriptionOption<'a> {
async fn async_from_slice<B: ByteOrder + Send>(code: u16, length: u16, mut slice: &'a [u8]) -> Result<Self, PcapError> {
let opt = match code {
1 => InterfaceDescriptionOption::Comment(Cow::Borrowed(std::str::from_utf8(slice)?)),
2 => InterfaceDescriptionOption::IfName(Cow::Borrowed(std::str::from_utf8(slice)?)),
3 => InterfaceDescriptionOption::IfDescription(Cow::Borrowed(std::str::from_utf8(slice)?)),
4 => {
if slice.len() != 8 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfIpv4Addr length != 8"));
}
InterfaceDescriptionOption::IfIpv4Addr(Cow::Borrowed(slice))
},
5 => {
if slice.len() != 17 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfIpv6Addr length != 17"));
}
InterfaceDescriptionOption::IfIpv6Addr(Cow::Borrowed(slice))
},
6 => {
if slice.len() != 6 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfMacAddr length != 6"));
}
InterfaceDescriptionOption::IfMacAddr(Cow::Borrowed(slice))
},
7 => {
if slice.len() != 8 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfEuIAddr length != 8"));
}
InterfaceDescriptionOption::IfEuIAddr(
AsyncReadBytesExt::read_u64::<B>(&mut slice).await.map_err(|_| PcapError::IncompleteBuffer)?,
)
},
8 => {
if slice.len() != 8 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfSpeed length != 8"));
}
InterfaceDescriptionOption::IfSpeed(
AsyncReadBytesExt::read_u64::<B>(&mut slice).await.map_err(|_| PcapError::IncompleteBuffer)?,
)
},
9 => {
if slice.len() != 1 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfTsResol length != 1"));
}
InterfaceDescriptionOption::IfTsResol(
AsyncReadBytesExt::read_u8(&mut slice).await.map_err(|_| PcapError::IncompleteBuffer)?,
)
},
10 => {
if slice.len() != 1 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfTzone length != 1"));
}
InterfaceDescriptionOption::IfTzone(
AsyncReadBytesExt::read_u32::<B>(&mut slice).await.map_err(|_| PcapError::IncompleteBuffer)?,
)
},
11 => {
if slice.is_empty() {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfFilter is empty"));
}
InterfaceDescriptionOption::IfFilter(Cow::Borrowed(slice))
},
12 => InterfaceDescriptionOption::IfOs(Cow::Borrowed(std::str::from_utf8(slice)?)),
13 => {
if slice.len() != 1 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfFcsLen length != 1"));
}
InterfaceDescriptionOption::IfFcsLen(AsyncReadBytesExt::read_u8(&mut slice).await.map_err(|_| PcapError::IncompleteBuffer)?)
},
14 => {
if slice.len() != 8 {
return Err(PcapError::InvalidField("InterfaceDescriptionOption: IfTsOffset length != 8"));
}
InterfaceDescriptionOption::IfTsOffset(
AsyncReadBytesExt::read_u64::<B>(&mut slice).await.map_err(|_| PcapError::IncompleteBuffer)?,
)
},
15 => InterfaceDescriptionOption::IfHardware(Cow::Borrowed(std::str::from_utf8(slice)?)),
2988 | 19372 => InterfaceDescriptionOption::CustomUtf8(CustomUtf8Option::async_from_slice::<B>(code, slice).await?),
2989 | 19373 => InterfaceDescriptionOption::CustomBinary(CustomBinaryOption::async_from_slice::<B>(code, slice).await?),
_ => InterfaceDescriptionOption::Unknown(UnknownOption::new(code, length, slice)),
};
Ok(opt)
}
async fn async_write_to<B: ByteOrder, W: AsyncWrite + Unpin + Send>(&self, writer: &mut W) -> IoResult<usize> {
match self {
InterfaceDescriptionOption::Comment(a) => a.async_write_opt_to::<B, W>(1, writer).await,
InterfaceDescriptionOption::IfName(a) => a.async_write_opt_to::<B, W>(2, writer).await,
InterfaceDescriptionOption::IfDescription(a) => a.async_write_opt_to::<B, W>(3, writer).await,
InterfaceDescriptionOption::IfIpv4Addr(a) => a.async_write_opt_to::<B, W>(4, writer).await,
InterfaceDescriptionOption::IfIpv6Addr(a) => a.async_write_opt_to::<B, W>(5, writer).await,
InterfaceDescriptionOption::IfMacAddr(a) => a.async_write_opt_to::<B, W>(6, writer).await,
InterfaceDescriptionOption::IfEuIAddr(a) => a.async_write_opt_to::<B, W>(7, writer).await,
InterfaceDescriptionOption::IfSpeed(a) => a.async_write_opt_to::<B, W>(8, writer).await,
InterfaceDescriptionOption::IfTsResol(a) => a.async_write_opt_to::<B, W>(9, writer).await,
InterfaceDescriptionOption::IfTzone(a) => a.async_write_opt_to::<B, W>(10, writer).await,
InterfaceDescriptionOption::IfFilter(a) => a.async_write_opt_to::<B, W>(11, writer).await,
InterfaceDescriptionOption::IfOs(a) => a.async_write_opt_to::<B, W>(12, writer).await,
InterfaceDescriptionOption::IfFcsLen(a) => a.async_write_opt_to::<B, W>(13, writer).await,
InterfaceDescriptionOption::IfTsOffset(a) => a.async_write_opt_to::<B, W>(14, writer).await,
InterfaceDescriptionOption::IfHardware(a) => a.async_write_opt_to::<B, W>(15, writer).await,
InterfaceDescriptionOption::CustomBinary(a) => a.async_write_opt_to::<B, W>(a.code, writer).await,
InterfaceDescriptionOption::CustomUtf8(a) => a.async_write_opt_to::<B, W>(a.code, writer).await,
InterfaceDescriptionOption::Unknown(a) => a.async_write_opt_to::<B, W>(a.code, writer).await,
}
}
}