use std::io::Write;
use byteorder::{BigEndian, ByteOrder, LittleEndian};
#[cfg(feature = "tokio")]
use tokio::io::AsyncWrite;
#[cfg(feature = "tokio")]
use super::blocks::block_common::AsyncPcapNgBlock;
use super::blocks::block_common::{Block, PcapNgBlock};
use super::blocks::interface_description::InterfaceDescriptionBlock;
use super::blocks::section_header::SectionHeaderBlock;
use super::blocks::SECTION_HEADER_BLOCK;
use super::RawBlock;
use crate::{Endianness, PcapError, PcapResult};
pub struct PcapNgWriter<W> {
section: SectionHeaderBlock<'static>,
interfaces: Vec<InterfaceDescriptionBlock<'static>>,
writer: W,
}
impl<W> PcapNgWriter<W> {
pub fn into_inner(self) -> W {
self.writer
}
pub fn get_ref(&self) -> &W {
&self.writer
}
pub fn get_mut(&mut self) -> &mut W {
&mut self.writer
}
pub fn section(&self) -> &SectionHeaderBlock<'static> {
&self.section
}
pub fn interfaces(&self) -> &[InterfaceDescriptionBlock<'static>] {
&self.interfaces
}
}
impl<W: Write> PcapNgWriter<W> {
pub fn new(writer: W) -> PcapResult<Self> {
Self::with_endianness(writer, Endianness::native())
}
pub fn with_endianness(writer: W, endianness: Endianness) -> PcapResult<Self> {
let section = SectionHeaderBlock { endianness, ..Default::default() };
Self::with_section_header(writer, section)
}
pub fn with_section_header(mut writer: W, section: SectionHeaderBlock<'static>) -> PcapResult<Self> {
match section.endianness {
Endianness::Big => section.clone().into_block().write_to::<BigEndian, _>(&mut writer).map_err(PcapError::IoError)?,
Endianness::Little => section.clone().into_block().write_to::<LittleEndian, _>(&mut writer).map_err(PcapError::IoError)?,
};
Ok(Self { section, interfaces: vec![], writer })
}
pub fn write_block(&mut self, block: &Block) -> PcapResult<usize> {
match block {
Block::SectionHeader(a) => {
self.section = a.clone().into_owned();
self.interfaces.clear();
},
Block::InterfaceDescription(a) => {
self.interfaces.push(a.clone().into_owned());
},
Block::InterfaceStatistics(a) => {
if a.interface_id as usize >= self.interfaces.len() {
return Err(PcapError::InvalidInterfaceId(a.interface_id));
}
},
Block::EnhancedPacket(a) => {
if a.interface_id as usize >= self.interfaces.len() {
return Err(PcapError::InvalidInterfaceId(a.interface_id));
}
},
_ => (),
}
match self.section.endianness {
Endianness::Big => block.write_to::<BigEndian, _>(&mut self.writer).map_err(PcapError::IoError),
Endianness::Little => block.write_to::<LittleEndian, _>(&mut self.writer).map_err(PcapError::IoError),
}
}
pub fn write_pcapng_block<'a, B: PcapNgBlock<'a>>(&mut self, block: B) -> PcapResult<usize> {
self.write_block(&block.into_block())
}
pub fn write_raw_block(&mut self, block: &RawBlock) -> PcapResult<usize> {
return match self.section.endianness {
Endianness::Big => inner::<BigEndian, _>(&mut self.section, block, &mut self.writer),
Endianness::Little => inner::<LittleEndian, _>(&mut self.section, block, &mut self.writer),
};
fn inner<B: ByteOrder, W: Write>(section: &mut SectionHeaderBlock, block: &RawBlock, writer: &mut W) -> PcapResult<usize> {
if block.type_ == SECTION_HEADER_BLOCK {
*section = block.clone().try_into_block::<B>()?.into_owned().into_section_header().unwrap();
}
block.write_to::<B, _>(writer).map_err(PcapError::IoError)
}
}
}
#[cfg(feature = "tokio")]
impl<W: AsyncWrite + Unpin + Send> PcapNgWriter<W> {
pub async fn async_new(writer: W) -> PcapResult<Self> {
Self::async_with_endianness(writer, Endianness::native()).await
}
pub async fn async_with_endianness(writer: W, endianness: Endianness) -> PcapResult<Self> {
let section = SectionHeaderBlock { endianness, ..Default::default() };
Self::async_with_section_header(writer, section).await
}
pub async fn async_with_section_header(mut writer: W, section: SectionHeaderBlock<'static>) -> PcapResult<Self> {
match section.endianness {
Endianness::Big => section
.clone()
.into_block()
.async_write_to::<BigEndian, _>(&mut writer)
.await
.map_err(PcapError::IoError)?,
Endianness::Little => section
.clone()
.into_block()
.async_write_to::<LittleEndian, _>(&mut writer)
.await
.map_err(PcapError::IoError)?,
};
Ok(Self { section, interfaces: vec![], writer })
}
pub async fn async_write_block(&mut self, block: &Block<'_>) -> PcapResult<usize> {
match block {
Block::SectionHeader(a) => {
self.section = a.clone().into_owned();
self.interfaces.clear();
},
Block::InterfaceDescription(a) => {
self.interfaces.push(a.clone().into_owned());
},
Block::InterfaceStatistics(a) => {
if a.interface_id as usize >= self.interfaces.len() {
return Err(PcapError::InvalidInterfaceId(a.interface_id));
}
},
Block::EnhancedPacket(a) => {
if a.interface_id as usize >= self.interfaces.len() {
return Err(PcapError::InvalidInterfaceId(a.interface_id));
}
},
_ => (),
}
match self.section.endianness {
Endianness::Big => block.async_write_to::<BigEndian, _>(&mut self.writer).await.map_err(PcapError::IoError),
Endianness::Little => block.async_write_to::<LittleEndian, _>(&mut self.writer).await.map_err(PcapError::IoError),
}
}
pub async fn async_write_pcapng_block<'a, B: AsyncPcapNgBlock<'a> + PcapNgBlock<'a>>(&mut self, block: B) -> PcapResult<usize> {
self.async_write_block(&block.into_block()).await
}
pub async fn async_write_raw_block(&mut self, block: &RawBlock<'_>) -> PcapResult<usize> {
return match self.section.endianness {
Endianness::Big => inner::<BigEndian, _>(&mut self.section, block, &mut self.writer).await,
Endianness::Little => inner::<LittleEndian, _>(&mut self.section, block, &mut self.writer).await,
};
async fn inner<B: ByteOrder + Send, W: AsyncWrite + Unpin>(
section: &mut SectionHeaderBlock<'_>,
block: &RawBlock<'_>,
writer: &mut W,
) -> PcapResult<usize> {
if block.type_ == SECTION_HEADER_BLOCK {
*section = block.clone().async_try_into_block::<B>().await?.into_owned().into_section_header().unwrap();
}
block.async_write_to::<B, _>(writer).await.map_err(PcapError::IoError)
}
}
}