use crate::error::Error;
use crate::format::pcap::{PcapHeader, PCAP_MAGIC, PCAP_MAGIC_NANO};
#[cfg(feature = "std")]
use std::fs::File;
#[cfg(feature = "std")]
use std::io::Write;
#[cfg(feature = "std")]
use std::path::Path;
pub struct PcapWriter<W> {
writer: W,
link_type: u32,
is_nano: bool,
}
impl<W: Write> PcapWriter<W> {
pub fn new(mut writer: W, link_type: u32) -> Result<Self, Error> {
let header = PcapHeader {
magic: if is_nano_time() {
PCAP_MAGIC_NANO
} else {
PCAP_MAGIC
},
version_major: 2,
version_minor: 4,
thiszone: 0,
sigfigs: 0,
snaplen: 65535,
network: link_type,
};
let header_bytes: [u8; 24] = unsafe { core::mem::transmute(header) };
writer.write_all(&header_bytes)?;
let is_nano = is_nano_time();
Ok(Self {
writer,
link_type,
is_nano,
})
}
pub fn link_type(&self) -> u32 {
self.link_type
}
pub fn write_packet(
&mut self,
data: &[u8],
timestamp_ns: u64,
original_len: u32,
) -> Result<(), Error> {
let secs = (timestamp_ns / 1_000_000_000) as u32;
let frac = if self.is_nano {
(timestamp_ns % 1_000_000_000) as u32
} else {
((timestamp_ns % 1_000_000_000) / 1000) as u32
};
let header = [
secs.to_le_bytes(),
frac.to_le_bytes(),
(data.len() as u32).to_le_bytes(),
original_len.to_le_bytes(),
]
.concat();
self.writer.write_all(&header)?;
self.writer.write_all(data)?;
Ok(())
}
pub fn flush(&mut self) -> Result<(), Error> {
Ok(self.writer.flush()?)
}
}
fn is_nano_time() -> bool {
false
}
#[cfg(feature = "std")]
impl PcapWriter<std::fs::File> {
pub fn create<P: AsRef<Path>>(path: P, link_type: u32) -> Result<Self, Error> {
let file = File::create(path)?;
Self::new(file, link_type)
}
}
pub struct PcapngWriter<W> {
writer: W,
next_interface_id: u16,
}
impl<W: Write> PcapngWriter<W> {
pub fn new(mut writer: W) -> Result<Self, Error> {
let shb: Vec<u8> = vec![
0x0a, 0x0d, 0x0d, 0x0a, 0x1c, 0x00, 0x00, 0x00, 0x1a, 0x2b, 0x3c, 0x4d, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1c, 0x00, 0x00, 0x00,
];
writer.write_all(&shb)?;
Ok(Self {
writer,
next_interface_id: 0,
})
}
pub fn write_interface(&mut self, link_type: u16, snap_len: u32) -> Result<u16, Error> {
let interface_id = self.next_interface_id;
self.next_interface_id += 1;
let _block_len: u32 = 24;
let idb: Vec<u8> = vec![
0x01,
0x00,
0x00,
0x00,
0x18,
0x00,
0x00,
0x00,
link_type.to_le_bytes()[0],
link_type.to_le_bytes()[1],
0x00,
0x00,
snap_len.to_le_bytes()[0],
snap_len.to_le_bytes()[1],
snap_len.to_le_bytes()[2],
snap_len.to_le_bytes()[3],
0x00,
0x00,
0x00,
0x00,
0x18,
0x00,
0x00,
0x00,
];
self.writer.write_all(&idb)?;
Ok(interface_id)
}
pub fn write_packet(
&mut self,
interface_id: u16,
timestamp_ns: u64,
data: &[u8],
original_len: u32,
) -> Result<(), Error> {
let captured_len = data.len() as u32;
let padded_len = (captured_len + 3) & !3; let padding = padded_len - captured_len;
let timestamp_high = ((timestamp_ns >> 32) & 0xffffffff) as u32;
let timestamp_low = (timestamp_ns & 0xffffffff) as u32;
let block_len = 32 + padded_len;
let header: Vec<u8> = vec![
0x06,
0x00,
0x00,
0x00,
block_len.to_le_bytes()[0],
block_len.to_le_bytes()[1],
block_len.to_le_bytes()[2],
block_len.to_le_bytes()[3],
(interface_id as u32).to_le_bytes()[0],
(interface_id as u32).to_le_bytes()[1],
(interface_id as u32).to_le_bytes()[2],
(interface_id as u32).to_le_bytes()[3],
timestamp_high.to_le_bytes()[0],
timestamp_high.to_le_bytes()[1],
timestamp_high.to_le_bytes()[2],
timestamp_high.to_le_bytes()[3],
timestamp_low.to_le_bytes()[0],
timestamp_low.to_le_bytes()[1],
timestamp_low.to_le_bytes()[2],
timestamp_low.to_le_bytes()[3],
captured_len.to_le_bytes()[0],
captured_len.to_le_bytes()[1],
captured_len.to_le_bytes()[2],
captured_len.to_le_bytes()[3],
original_len.to_le_bytes()[0],
original_len.to_le_bytes()[1],
original_len.to_le_bytes()[2],
original_len.to_le_bytes()[3],
];
self.writer.write_all(&header)?;
self.writer.write_all(data)?;
self.writer.write_all(&vec![0u8; padding as usize])?;
let trailer = block_len.to_le_bytes();
self.writer.write_all(&trailer)?;
Ok(())
}
pub fn flush(&mut self) -> Result<(), Error> {
Ok(self.writer.flush()?)
}
}
#[cfg(feature = "std")]
impl PcapngWriter<std::fs::File> {
pub fn create<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
let file = File::create(path)?;
Self::new(file)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_pcap_writer_new() {
let mut buffer = Vec::new();
let mut writer = PcapWriter::new(&mut buffer, 1).unwrap();
let packet_data = vec![0xde, 0xad, 0xbe, 0xef];
writer.write_packet(&packet_data, 0, 4).unwrap();
assert_eq!(buffer.len(), 44);
assert_eq!(buffer[0..4], [0xd4, 0xc3, 0xb2, 0xa1]); }
#[test]
fn test_pcap_writer_timestamp() {
let mut buffer = Vec::new();
let mut writer = PcapWriter::new(&mut buffer, 1).unwrap();
let packet_data = vec![0xde, 0xad, 0xbe, 0xef];
writer.write_packet(&packet_data, 1_000_000_000, 4).unwrap();
let ts_sec = u32::from_le_bytes([buffer[24], buffer[25], buffer[26], buffer[27]]);
let ts_usec = u32::from_le_bytes([buffer[28], buffer[29], buffer[30], buffer[31]]);
assert_eq!(ts_sec, 1);
assert_eq!(ts_usec, 0); }
#[test]
fn test_pcap_round_trip() {
let mut buffer = Vec::new();
{
let mut writer = PcapWriter::new(&mut buffer, 1).unwrap();
let packet1 = vec![0xde, 0xad, 0xbe, 0xef];
let packet2 = vec![0xca, 0xfe, 0xba, 0xbe];
writer.write_packet(&packet1, 1000, 4).unwrap();
writer.write_packet(&packet2, 2000, 4).unwrap();
}
use crate::reader::PcapReader;
let mut reader = PcapReader::from_reader(Cursor::new(&buffer)).unwrap();
let pkt1 = reader.next_packet().unwrap().unwrap();
assert_eq!(pkt1.data(), &[0xde, 0xad, 0xbe, 0xef]);
let pkt2 = reader.next_packet().unwrap().unwrap();
assert_eq!(pkt2.data(), &[0xca, 0xfe, 0xba, 0xbe]);
assert!(reader.next_packet().unwrap().is_none());
}
#[test]
fn test_pcapng_writer_new() {
let mut buffer = Vec::new();
let _writer = PcapngWriter::new(&mut buffer).unwrap();
assert_eq!(buffer.len(), 28);
assert_eq!(buffer[0..4], [0x0a, 0x0d, 0x0d, 0x0a]);
}
#[test]
fn test_pcapng_write_interface() {
let mut buffer = Vec::new();
let mut writer = PcapngWriter::new(&mut buffer).unwrap();
let interface_id = writer.write_interface(1, 65535).unwrap();
assert_eq!(interface_id, 0);
assert_eq!(buffer.len(), 52);
}
#[test]
fn test_pcapng_write_packet() {
let mut buffer = Vec::new();
let mut writer = PcapngWriter::new(&mut buffer).unwrap();
let interface_id = writer.write_interface(1, 65535).unwrap();
let packet_data = vec![0xde, 0xad, 0xbe, 0xef];
writer
.write_packet(interface_id, 1000, &packet_data, 4)
.unwrap();
assert_eq!(buffer.len(), 88);
}
#[ignore]
#[test]
fn _test_pcapng_round_trip_disabled() {
let mut buffer = Vec::new();
{
let mut writer = PcapngWriter::new(&mut buffer).unwrap();
let interface_id = writer.write_interface(1, 65535).unwrap();
let packet1 = vec![0xde, 0xad, 0xbe, 0xef];
let packet2 = vec![0xca, 0xfe, 0xba, 0xbe];
writer
.write_packet(interface_id, 1000, &packet1, 4)
.unwrap();
writer
.write_packet(interface_id, 2000, &packet2, 4)
.unwrap();
}
use crate::format::pcapng::Block;
use crate::reader::PcapngReader;
let mut reader = PcapngReader::from_reader(Cursor::new(&buffer)).unwrap();
assert_eq!(reader.interfaces().len(), 1);
assert_eq!(reader.interfaces()[0].link_type, 1);
let mut found_packets = 0;
while let Some(block) = reader.next_block().unwrap() {
if let Block::EnhancedPacket(epb) = block {
found_packets += 1;
if found_packets == 1 {
assert_eq!(&epb.data, &[0xde, 0xad, 0xbe, 0xef]);
} else if found_packets == 2 {
assert_eq!(&epb.data, &[0xca, 0xfe, 0xba, 0xbe]);
}
}
}
assert_eq!(found_packets, 2);
}
}