pcarp 2.0.0

A pure-Rust library for reading pcap-ng files
Documentation
use clap::Parser;
use pcarp::*;
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use std::time::*;
use tracing::*;

/// Dumps the packets from a pcapng file
#[derive(Parser)]
struct Opts {
    /// The pcapng file to read from
    pcap: PathBuf,
}

fn main() {
    let opts = Opts::parse();

    env_logger::init();

    let file = File::open(&opts.pcap).unwrap();
    let reader: Box<dyn Read> = match opts.pcap.extension().and_then(|x| x.to_str()) {
        Some("pcapng") => Box::new(file),
        Some("gz") => Box::new(flate2::read::GzDecoder::new(file)),
        Some("xz") => Box::new(xz2::read::XzDecoder::new(file)),
        Some(x) => {
            warn!("Didn't recognise file extension {}; assuming plain pcap", x);
            Box::new(file)
        }
        None => {
            warn!("No file extension; assuming plain pcap");
            Box::new(file)
        }
    };
    let pcap = Capture::new(reader);
    let start = Instant::now();
    for (n, pkt) in pcap.enumerate() {
        let pkt = match pkt {
            Ok(pkt) => pkt,
            Err(e) => {
                eprintln!("{e}");
                continue;
            }
        };
        let ts = pkt.timestamp.unwrap_or(SystemTime::UNIX_EPOCH);
        println!(
            "[{}] {:>5}  {}",
            humantime::format_rfc3339_nanos(ts),
            pkt.data.len(),
            sanitize(&pkt.data)
        );
        if n % 1000 == 0 {
            let nanos = start.elapsed().subsec_nanos();
            let bps = n as f64 * 1_000_000_000.0 / f64::from(nanos);
            info!("Read {} blocks at {} pps", n, bps);
        }
    }
}

fn sanitize(data: &[u8]) -> String {
    String::from_utf8_lossy(data).replace(|x: char| !x.is_ascii() || x.is_control(), ".")
}