use std::fs::File;
use std::io::{BufRead, BufReader, Read};
use std::path::Path;
#[cfg(all(feature = "session", feature = "reassembler"))]
use crate::session::SessionEvent;
#[cfg(all(feature = "session", feature = "reassembler"))]
use crate::session::{DatagramDriver, SessionDriver, TemplateFactory};
use crate::tracker::FlowEvents;
#[cfg(all(feature = "session", feature = "reassembler"))]
use crate::{DatagramParser, SessionParser};
use crate::{FlowEvent, FlowExtractor, FlowTracker, Timestamp};
use super::reader::{CaptureFormat, CaptureReader};
pub struct PcapFlowSource<R: Read> {
reader: CaptureReader<R>,
speed_factor: Option<f64>,
}
impl PcapFlowSource<BufReader<File>> {
pub fn open(path: impl AsRef<Path>) -> crate::Result<Self> {
Ok(Self {
reader: CaptureReader::open(path)?,
speed_factor: None,
})
}
}
impl<R: BufRead> PcapFlowSource<R> {
pub fn from_reader(reader: R) -> crate::Result<Self> {
Ok(Self {
reader: CaptureReader::new(reader)?,
speed_factor: None,
})
}
}
impl<R: Read> PcapFlowSource<R> {
pub fn format(&self) -> CaptureFormat {
self.reader.format()
}
pub fn with_speed_factor(mut self, factor: f64) -> Self {
assert!(
factor > 0.0 && factor.is_finite() || factor == f64::INFINITY,
"speed_factor must be > 0 (got {factor})",
);
self.speed_factor = Some(factor);
self
}
pub fn views(self) -> ViewIter<R> {
ViewIter {
reader: self.reader,
unsupported: 0,
speed_factor: self.speed_factor,
prev_pcap_ts: None,
}
}
pub fn with_extractor<E: FlowExtractor>(self, extractor: E) -> EventIter<R, E>
where
E::Key: Clone,
{
EventIter {
views: self.views(),
tracker: FlowTracker::new(extractor),
pending: std::collections::VecDeque::new(),
sweep_done: false,
}
}
#[cfg(all(feature = "session", feature = "reassembler"))]
pub fn sessions<E, P>(self, extractor: E, parser: P) -> SessionIter<R, E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + Sync + 'static,
P: SessionParser + Clone + Send + Sync + 'static,
{
SessionIter {
views: self.views(),
driver: SessionDriver::new(extractor, TemplateFactory(parser)),
pending: std::collections::VecDeque::new(),
finished: false,
}
}
#[cfg(all(feature = "session", feature = "reassembler"))]
pub fn datagrams<E, P>(self, extractor: E, parser: P) -> DatagramIter<R, E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + Sync + 'static,
P: DatagramParser + Clone + Send + Sync + 'static,
{
DatagramIter {
views: self.views(),
driver: DatagramDriver::new(extractor, TemplateFactory(parser)),
pending: std::collections::VecDeque::new(),
finished: false,
}
}
}
pub use crate::view::OwnedPacketView;
pub struct ViewIter<R: Read> {
reader: CaptureReader<R>,
unsupported: u64,
speed_factor: Option<f64>,
prev_pcap_ts: Option<std::time::Duration>,
}
impl<R: Read> Iterator for ViewIter<R> {
type Item = crate::Result<OwnedPacketView>;
fn next(&mut self) -> Option<Self::Item> {
let (timestamp, frame, cut) = loop {
let p = match self.reader.next_packet()? {
Ok(p) => p,
Err(e) => return Some(Err(e)),
};
let timestamp = p.timestamp;
let cut = (p.original_len as usize).saturating_sub(p.data.len());
match p.into_ethernet() {
Ok(frame) => break (timestamp, frame, cut),
Err(_) => self.unsupported += 1,
}
};
if let Some(factor) = self.speed_factor
&& factor.is_finite()
{
if let Some(prev) = self.prev_pcap_ts {
let dt = timestamp.saturating_sub(prev);
let nanos = (dt.as_nanos() as f64 / factor) as u64;
if nanos > 0 {
std::thread::sleep(std::time::Duration::from_nanos(nanos));
}
}
self.prev_pcap_ts = Some(timestamp);
}
let ts = Timestamp::new(timestamp.as_secs() as u32, timestamp.subsec_nanos());
Some(Ok(
OwnedPacketView::new(frame, ts).with_snap_cut(u32::try_from(cut).unwrap_or(u32::MAX))
))
}
}
impl<R: Read> ViewIter<R> {
pub fn unsupported(&self) -> u64 {
self.unsupported
}
}
pub struct EventIter<R: Read, E: FlowExtractor>
where
E::Key: Clone,
{
views: ViewIter<R>,
tracker: FlowTracker<E, ()>,
pending: std::collections::VecDeque<FlowEvent<E::Key>>,
sweep_done: bool,
}
impl<R: Read, E: FlowExtractor> Iterator for EventIter<R, E>
where
E::Key: Clone,
{
type Item = crate::Result<FlowEvent<E::Key>>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(ev) = self.pending.pop_front() {
return Some(Ok(ev));
}
match self.views.next() {
Some(Ok(view)) => {
let evts: FlowEvents<E::Key> = self.tracker.track(&view);
for ev in evts {
self.pending.push_back(ev);
}
}
Some(Err(e)) => return Some(Err(e)),
None => {
if !self.sweep_done {
self.sweep_done = true;
for ev in self.tracker.sweep(Timestamp::MAX) {
self.pending.push_back(ev);
}
} else {
return None;
}
}
}
}
}
}
#[cfg(all(feature = "session", feature = "reassembler"))]
pub struct SessionIter<R: Read, E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + Sync + 'static,
P: SessionParser + Clone + Send + Sync + 'static,
{
views: ViewIter<R>,
driver: SessionDriver<E, TemplateFactory<P>>,
pending: std::collections::VecDeque<SessionEvent<E::Key, P::Message>>,
finished: bool,
}
#[cfg(all(feature = "session", feature = "reassembler"))]
impl<R: Read, E, P> Iterator for SessionIter<R, E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + Sync + 'static,
P: SessionParser + Clone + Send + Sync + 'static,
{
type Item = crate::Result<SessionEvent<E::Key, P::Message>>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(ev) = self.pending.pop_front() {
return Some(Ok(ev));
}
match self.views.next() {
Some(Ok(view)) => {
for ev in self.driver.track(&view) {
self.pending.push_back(ev);
}
}
Some(Err(e)) => return Some(Err(e)),
None => {
if self.finished {
return None;
}
self.finished = true;
for ev in self.driver.finish() {
self.pending.push_back(ev);
}
}
}
}
}
}
#[cfg(all(feature = "session", feature = "reassembler"))]
pub struct DatagramIter<R: Read, E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + Sync + 'static,
P: DatagramParser + Clone + Send + Sync + 'static,
{
views: ViewIter<R>,
driver: DatagramDriver<E, TemplateFactory<P>>,
pending: std::collections::VecDeque<SessionEvent<E::Key, P::Message>>,
finished: bool,
}
#[cfg(all(feature = "session", feature = "reassembler"))]
impl<R: Read, E, P> Iterator for DatagramIter<R, E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + Sync + 'static,
P: DatagramParser + Clone + Send + Sync + 'static,
{
type Item = crate::Result<SessionEvent<E::Key, P::Message>>;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(ev) = self.pending.pop_front() {
return Some(Ok(ev));
}
match self.views.next() {
Some(Ok(view)) => {
for ev in self.driver.track(&view) {
self.pending.push_back(ev);
}
}
Some(Err(e)) => return Some(Err(e)),
None => {
if self.finished {
return None;
}
self.finished = true;
for ev in self.driver.finish() {
self.pending.push_back(ev);
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn owned_view_converts_to_packet_view() {
let owned = OwnedPacketView::new(vec![1, 2, 3, 4], Timestamp::new(7, 42));
let pv: crate::PacketView<'_> = (&owned).into();
assert_eq!(pv.frame, &[1, 2, 3, 4]);
assert_eq!(pv.timestamp, Timestamp::new(7, 42));
}
}