use std::fs::File;
use std::io::{BufReader, Read};
use std::path::Path;
#[cfg(all(feature = "session", feature = "reassembler", feature = "extractors"))]
use crate::DatagramParser;
#[cfg(all(feature = "session", feature = "reassembler"))]
use crate::SessionParser;
#[cfg(all(feature = "session", feature = "reassembler", feature = "extractors"))]
use crate::datagram_driver::FlowDatagramDriver;
#[cfg(all(feature = "session", feature = "reassembler"))]
use crate::session::SessionEvent;
#[cfg(all(feature = "session", feature = "reassembler", feature = "extractors"))]
use crate::session_driver::FlowSessionDriver;
use crate::tracker::FlowEvents;
use crate::{FlowEvent, FlowExtractor, FlowTracker, Timestamp};
use pcap_file::pcap::PcapReader;
use crate::error::{Error, Module};
pub struct PcapFlowSource<R: Read> {
reader: PcapReader<R>,
speed_factor: Option<f64>,
}
impl PcapFlowSource<BufReader<File>> {
pub fn open(path: impl AsRef<Path>) -> crate::Result<Self> {
let file = File::open(path).map_err(|e| Error::io(Module::Pcap, e))?;
let reader = PcapReader::new(BufReader::new(file))
.map_err(|e| Error::parse_with(Module::Pcap, "invalid pcap header", e))?;
Ok(Self {
reader,
speed_factor: None,
})
}
}
impl<R: Read> PcapFlowSource<R> {
pub fn from_reader(reader: R) -> crate::Result<Self> {
Ok(Self {
reader: PcapReader::new(reader)
.map_err(|e| Error::parse_with(Module::Pcap, "invalid pcap header", e))?,
speed_factor: None,
})
}
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,
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(crate) 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: FlowSessionDriver::new(extractor, parser),
pending: std::collections::VecDeque::new(),
finished: false,
}
}
#[cfg(all(feature = "session", feature = "reassembler", feature = "extractors"))]
pub(crate) 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: FlowDatagramDriver::new(extractor, parser),
pending: std::collections::VecDeque::new(),
finished: false,
}
}
}
pub use crate::view::OwnedPacketView;
pub struct ViewIter<R: Read> {
reader: PcapReader<R>,
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 pkt = self.reader.next_packet()?;
match pkt {
Ok(p) => {
if let Some(factor) = self.speed_factor
&& factor.is_finite()
{
if let Some(prev) = self.prev_pcap_ts {
let dt = p.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(p.timestamp);
}
let ts = Timestamp::new(p.timestamp.as_secs() as u32, p.timestamp.subsec_nanos());
Some(Ok(OwnedPacketView::new(p.data.into_owned(), ts)))
}
Err(e) => Some(Err(Error::parse_with(
Module::Pcap,
"malformed pcap record",
e,
))),
}
}
}
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(crate) 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: FlowSessionDriver<E, 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", feature = "extractors"))]
pub(crate) 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: FlowDatagramDriver<E, P>,
pending: std::collections::VecDeque<SessionEvent<E::Key, P::Message>>,
finished: bool,
}
#[cfg(all(feature = "session", feature = "reassembler", feature = "extractors"))]
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));
}
}