pub mod block;
pub mod iface;
use crate::block::{Block, BlockError, BlockReader, BlockType, FrameError, NameResolution};
use crate::iface::{InterfaceId, InterfaceInfo};
use bytes::Bytes;
use std::{
io::{Read, Seek},
time::SystemTime,
};
use thiserror::Error;
use tracing::*;
pub(crate) type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Debug, Error)]
pub enum Error {
#[error("Error while parsing a frame (fatal)")]
Frame(#[from] FrameError),
#[error("Error while parsing a {0:?} block (non-fatal)")]
Block(BlockType, #[source] BlockError),
#[error("IO error")]
IO(#[from] std::io::Error),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Packet {
pub timestamp: Option<SystemTime>,
pub interface: Option<InterfaceId>,
pub data: Bytes,
}
pub struct Capture<R> {
inner: BlockReader<R>,
current_section: u32,
interfaces: Vec<Option<InterfaceInfo>>,
resolved_names: Vec<NameResolution>,
}
impl<R> Capture<R> {
pub fn new(rdr: R) -> Capture<R> {
Capture {
inner: BlockReader::new(rdr),
current_section: 0,
interfaces: Vec::new(),
resolved_names: Vec::new(),
}
}
pub fn rewind(&mut self) -> Result<()>
where
R: Seek,
{
self.inner.rewind()?;
self.interfaces.clear();
self.resolved_names.clear();
Ok(())
}
pub fn lookup_interface(&self, interface_id: InterfaceId) -> Option<&InterfaceInfo> {
if interface_id.0 != self.current_section {
None
} else {
self.interfaces.get(interface_id.1 as usize)?.as_ref()
}
}
}
impl<R: Read> Iterator for Capture<R> {
type Item = Result<Packet>;
fn next(&mut self) -> Option<Self::Item> {
self.try_next().transpose()
}
}
impl<R: Read> Capture<R> {
fn try_next(&mut self) -> Result<Option<Packet>> {
loop {
let block = match self.inner.try_next() {
Ok(Some(block)) => block,
Ok(None) => return Ok(None),
Err(e) => {
if let Error::Block(block_type, _) = e {
self.handle_corrupt_block(block_type);
}
return Err(e);
}
};
self.handle_block(&block);
let Some((meta, data)) = block.into_pkt() else { continue };
let interface = meta.map(|(_, iface)| InterfaceId(self.current_section, iface));
let timestamp = meta.and_then(|(ts, iface)| {
let iface = self.interfaces.get(iface as usize)?.as_ref()?;
Some(iface.resolve_ts(ts))
});
return Ok(Some(Packet {
timestamp,
interface,
data,
}));
}
}
fn start_new_section(&mut self) {
self.interfaces.clear();
self.resolved_names.clear();
self.current_section += 1;
debug!("Starting new section (#{})", self.current_section);
}
fn handle_block(&mut self, block: &Block) {
match block {
Block::SectionHeader(_) => self.start_new_section(),
Block::InterfaceDescription(descr) => {
debug!("Defined a new interface: {:?}", descr);
if descr.snap_len.unwrap_or(0) > BlockReader::<R>::BUF_CAPACITY as u32 {
warn!(
"The max packet length for this interface is greater \
than the length of our buffer."
);
}
let iface = InterfaceInfo {
descr: descr.clone(),
stats: None,
};
debug!("Parsed: {iface:?}");
self.interfaces.push(Some(iface));
}
Block::NameResolution(x) => {
debug!("Defined a new resolved name: {x:?}");
self.resolved_names.push(x.clone());
}
Block::InterfaceStatistics(stats) => {
debug!("Got some interface statistics: {stats:?}");
match self
.interfaces
.get_mut(stats.interface_id as usize)
.and_then(|x| x.as_mut())
{
Some(x) => x.stats = Some(stats.clone()),
None => warn!("Saw statistics for an undefined interface"),
}
}
Block::EnhancedPacket(pkt) => trace!("Got a packet: {pkt:?}"),
Block::SimplePacket(pkt) => trace!("Got a packet: {pkt:?}"),
Block::ObsoletePacket(pkt) => trace!("Got a packet: {pkt:?}"),
Block::Unparsed(block_type) => {
warn!("{block_type:?} blocks are ignored")
}
}
}
fn handle_corrupt_block(&mut self, block_type: BlockType) {
use crate::block::BlockType as BT;
match block_type {
BT::SectionHeader => self.start_new_section(),
BT::InterfaceDescription => self.interfaces.push(None),
BT::NameResolution | BT::InterfaceStatistics => (),
BT::ObsoletePacket | BT::SimplePacket | BT::EnhancedPacket => (),
_ => (),
}
}
}