use std::time::{SystemTime, UNIX_EPOCH};
pub const LINKTYPE_USER0: u32 = 147;
pub struct PcapSink<W> {
writer: W,
}
impl<W: std::io::Write> PcapSink<W> {
pub fn new(mut writer: W) -> std::io::Result<Self> {
writer.write_all(&global_header())?;
Ok(Self { writer })
}
pub fn log_packet(&mut self, path_code: u16, pkt: &[u8]) -> std::io::Result<()> {
let (sec, usec) = now_parts();
self.write_record(path_code, sec, usec, pkt)
}
fn write_record(
&mut self,
path_code: u16,
ts_sec: u32,
ts_usec: u32,
pkt: &[u8],
) -> std::io::Result<()> {
let incl_len: u32 = 4u32.saturating_add(pkt.len() as u32);
self.writer.write_all(&ts_sec.to_le_bytes())?;
self.writer.write_all(&ts_usec.to_le_bytes())?;
self.writer.write_all(&incl_len.to_le_bytes())?;
self.writer.write_all(&incl_len.to_le_bytes())?;
self.writer.write_all(&record_preamble(path_code))?;
self.writer.write_all(pkt)?;
Ok(())
}
pub fn flush(&mut self) -> std::io::Result<()> {
self.writer.flush()
}
#[cfg(test)]
fn into_inner(self) -> W {
self.writer
}
}
fn global_header() -> [u8; 24] {
let mut h = [0u8; 24];
h[0..4].copy_from_slice(&0xA1B2_C3D4u32.to_le_bytes()); h[4..6].copy_from_slice(&2u16.to_le_bytes()); h[6..8].copy_from_slice(&4u16.to_le_bytes()); h[8..12].copy_from_slice(&0i32.to_le_bytes()); h[12..16].copy_from_slice(&0u32.to_le_bytes()); h[16..20].copy_from_slice(&65535u32.to_le_bytes()); h[20..24].copy_from_slice(&LINKTYPE_USER0.to_le_bytes()); h
}
fn record_preamble(path_code: u16) -> [u8; 4] {
let p = path_code.to_le_bytes();
[p[0], p[1], 0, 0]
}
fn now_parts() -> (u32, u32) {
match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(d) => (d.as_secs() as u32, d.subsec_micros()),
Err(_) => (0, 0),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn global_header_is_exact() {
assert_eq!(
global_header(),
[
0xD4, 0xC3, 0xB2, 0xA1, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x93, 0x00, 0x00, 0x00, ]
);
}
#[test]
fn record_preamble_encodes_path_le() {
assert_eq!(record_preamble(1), [0x01, 0x00, 0x00, 0x00]);
assert_eq!(record_preamble(0x0102), [0x02, 0x01, 0x00, 0x00]);
}
#[test]
fn write_record_layout() {
let mut sink = PcapSink::new(Vec::<u8>::new()).expect("global header");
sink.write_record(1, 0x1122_3344, 0x0005_5AA5, &[0xAB, 0xCD, 0xEF])
.expect("write record");
let buf = sink.into_inner();
let rec = &buf[24..];
assert_eq!(
rec,
&[
0x44, 0x33, 0x22, 0x11, 0xA5, 0x5A, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xAB, 0xCD, 0xEF, ]
);
}
#[test]
fn new_writes_global_header() {
let mut buf = Vec::<u8>::new();
let _sink = PcapSink::new(&mut buf).expect("global header");
assert_eq!(buf, global_header());
}
}