use crate::{
common::{
BigEndianWriter,
Endianness,
LittleEndianWriter,
PcapResult,
},
header::PcapHeader,
packet::{
write_packet,
PcapPacket,
},
reader::PcapReader,
};
use std::{
fs::File,
io::{
BufWriter,
Seek,
Write,
},
path::Path,
};
#[derive(Debug)]
pub struct PcapWriter<W>
where
W: Write,
{
header: PcapHeader,
writer: W,
}
impl<W> PcapWriter<W>
where
W: Write,
{
pub fn new(writer: W) -> PcapResult<Self> {
let endianness = if cfg!(target_endian = "big") {
Endianness::Big
} else {
Endianness::Little
};
let header = PcapHeader {
endianness,
..Default::default()
};
Self::new_with_header(writer, header)
}
pub fn new_with_header(mut writer: W, header: PcapHeader) -> PcapResult<Self> {
header.write(&mut writer)?;
Ok(Self { header, writer })
}
pub fn open_with_header(writer: W, header: PcapHeader) -> Self {
Self { header, writer }
}
pub fn into_writer(self) -> W {
self.writer
}
pub fn header(&self) -> PcapHeader {
self.header
}
pub fn write_packet(&mut self, packet: &PcapPacket) -> PcapResult<u32> {
match self.header.endianness {
Endianness::Big => packet.write(BigEndianWriter::from(&mut self.writer)),
Endianness::Little => packet.write(LittleEndianWriter::from(&mut self.writer)),
}
}
pub fn write_packet_data(
&mut self,
ts_sec: u32,
ts_frac: u32,
incl_len: u32,
orig_len: u32,
data: impl AsRef<[u8]>,
) -> PcapResult<u32> {
match self.header.endianness {
Endianness::Big => write_packet(
BigEndianWriter::from(&mut self.writer),
ts_sec,
ts_frac,
incl_len,
orig_len,
data,
),
Endianness::Little => write_packet(
LittleEndianWriter::from(&mut self.writer),
ts_sec,
ts_frac,
incl_len,
orig_len,
data,
),
}
}
pub fn flush(&mut self) -> PcapResult<()> {
self.writer.flush().map_err(Into::into)
}
}
pub struct PcapFileWriter {
writer: PcapWriter<BufWriter<File>>,
packet_count: u64,
len: u64,
}
impl PcapFileWriter {
pub fn new(path: impl AsRef<Path>) -> PcapResult<Self> {
let file = File::create(path.as_ref())?;
let writer = PcapWriter::new(BufWriter::new(file))?;
Ok(Self {
writer,
packet_count: 0,
len: PcapHeader::LEN.into(),
})
}
pub fn new_with_header(path: impl AsRef<Path>, header: PcapHeader) -> PcapResult<Self> {
let file = File::create(path.as_ref())?;
let writer = PcapWriter::new_with_header(BufWriter::new(file), header)?;
Ok(Self {
writer,
packet_count: 0,
len: PcapHeader::LEN.into(),
})
}
pub fn open(path: impl AsRef<Path>, create: bool) -> PcapResult<PcapFileWriter> {
let mut file = File::options()
.read(true)
.append(true)
.create(create)
.open(path.as_ref())?;
let mut len = file.metadata().map(|md| md.len())?;
let mut packet_count = 0;
let writer = match len > 0 {
true => {
file.rewind()?;
let mut reader = PcapReader::new(&mut file)?;
let header = reader.header();
let mut data = vec![];
while let Some(res) = reader.next_with(data) {
let packet = res?;
packet_count += 1;
data = packet.data;
}
let writer = BufWriter::new(file);
PcapWriter::open_with_header(writer, header)
}
false => {
len += PcapHeader::LEN as u64;
PcapWriter::new(BufWriter::new(file))?
}
};
Ok(Self {
writer,
packet_count,
len,
})
}
pub fn packet_count(&self) -> u64 {
self.packet_count
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> u64 {
self.len
}
pub fn write_packet(&mut self, packet: &PcapPacket) -> PcapResult<()> {
let packet_len = self.writer.write_packet(packet)?;
self.len += u64::from(packet_len);
self.packet_count += 1;
Ok(())
}
pub fn write_packet_data(
&mut self,
ts_sec: u32,
ts_frac: u32,
incl_len: u32,
orig_len: u32,
data: impl AsRef<[u8]>,
) -> PcapResult<()> {
let packet_len = self
.writer
.write_packet_data(ts_sec, ts_frac, incl_len, orig_len, data)?;
self.len += u64::from(packet_len);
self.packet_count += 1;
Ok(())
}
pub fn flush(&mut self) -> PcapResult<()> {
self.writer.flush()
}
pub fn close(mut self) -> PcapResult<()> {
self.flush()?;
let file = self
.writer
.into_writer()
.into_inner()
.map_err(|e| e.into_error())?;
file.sync_all()?;
Ok(())
}
}