use std::collections::{HashMap, VecDeque};
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
use ahash::RandomState;
use flowscope::tracker::FlowEvents;
use flowscope::{
BufferedReassembler, BufferedReassemblerFactory, DatagramParser, FlowEvent, FlowExtractor,
FlowSide, FlowTracker, FlowTrackerConfig, L4Proto, Orientation, PacketView, Reassembler,
ReassemblerFactory, SessionParser, SessionParserFactory, Timestamp,
};
use futures_core::Stream;
use crate::async_adapters::datagram_stream::{convert_event, peek_udp_payload};
use crate::async_adapters::session_event::SessionEvent;
use crate::async_adapters::session_stream::{build_reassembler_factory, process_session_event};
use crate::error::Error;
use crate::pcap_source::AsyncPcapSource;
pub struct PcapFlowStream<E>
where
E: FlowExtractor,
{
source: AsyncPcapSource,
tracker: FlowTracker<E, ()>,
pending: VecDeque<FlowEvent<E::Key>>,
eof: bool,
}
impl<E> PcapFlowStream<E>
where
E: FlowExtractor,
E::Key: Clone + Send + 'static,
{
pub(crate) fn new(source: AsyncPcapSource, extractor: E) -> Self {
Self {
source,
tracker: FlowTracker::new(extractor),
pending: VecDeque::new(),
eof: false,
}
}
pub fn with_config(mut self, config: FlowTrackerConfig) -> Self {
self.tracker.set_config(config);
self
}
pub fn with_idle_timeout_fn<G>(mut self, f: G) -> Self
where
G: Fn(&E::Key, Option<flowscope::L4Proto>) -> Option<Duration> + Send + Sync + 'static,
{
self.tracker.set_idle_timeout_fn(f);
self
}
pub fn tracker(&self) -> &FlowTracker<E, ()> {
&self.tracker
}
pub fn tracker_stats(&self) -> &flowscope::FlowTrackerStats {
self.tracker.stats()
}
pub fn active_flows(&self) -> usize {
self.tracker.flows().count()
}
pub fn packets_read(&self) -> u64 {
self.source.packets_yielded()
}
}
impl<E> Stream for PcapFlowStream<E>
where
E: FlowExtractor + Unpin,
E::Key: Clone + Unpin,
{
type Item = Result<FlowEvent<E::Key>, Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.get_mut();
loop {
if let Some(evt) = this.pending.pop_front() {
return Poll::Ready(Some(Ok(evt)));
}
if this.eof {
for ev in this.tracker.sweep(Timestamp::MAX) {
this.pending.push_back(ev);
}
if let Some(evt) = this.pending.pop_front() {
return Poll::Ready(Some(Ok(evt)));
}
return Poll::Ready(None);
}
match Pin::new(&mut this.source).poll_next(cx) {
Poll::Ready(Some(Ok(owned))) => {
let view = PacketView::new(&owned.data, owned.timestamp);
let evts: FlowEvents<E::Key> = this.tracker.track(view);
for ev in evts {
this.pending.push_back(ev);
}
}
Poll::Ready(Some(Err(e))) => return Poll::Ready(Some(Err(e))),
Poll::Ready(None) => {
this.eof = true;
}
Poll::Pending => return Poll::Pending,
}
}
}
}
impl AsyncPcapSource {
pub fn flow_events<E>(self, extractor: E) -> PcapFlowStream<E>
where
E: FlowExtractor,
E::Key: Clone + Send + 'static,
{
PcapFlowStream::new(self, extractor)
}
pub fn sessions<E, P>(self, extractor: E, parser: P) -> PcapSessionStream<E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + 'static,
P: SessionParser + Clone + Send + Sync,
{
PcapSessionStream::new(self, FlowTracker::new(extractor), parser)
}
pub fn datagrams<E, P>(self, extractor: E, parser: P) -> PcapDatagramStream<E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + 'static,
P: DatagramParser + Clone + Send + Sync,
{
PcapDatagramStream::new(self, FlowTracker::new(extractor), parser)
}
}
impl<E> PcapFlowStream<E>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + 'static,
{
pub fn session_stream<P>(self, parser: P) -> PcapSessionStream<E, P>
where
E::Key: std::hash::Hash + Eq,
P: SessionParser + Clone + Send + Sync,
{
PcapSessionStream::new(self.source, self.tracker, parser)
}
pub fn datagram_stream<P>(self, parser: P) -> PcapDatagramStream<E, P>
where
E::Key: std::hash::Hash + Eq,
P: DatagramParser + Clone + Send + Sync,
{
PcapDatagramStream::new(self.source, self.tracker, parser)
}
}
struct CloneSeed<P>(P);
impl<K, P> SessionParserFactory<K> for CloneSeed<P>
where
P: SessionParser + Clone,
{
type Parser = P;
fn new_parser(&mut self, _key: &K) -> P {
self.0.clone()
}
}
pub struct PcapSessionStream<E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + 'static,
P: SessionParser + Clone + Send + Sync,
{
source: AsyncPcapSource,
tracker: FlowTracker<E, ()>,
parser_factory: CloneSeed<P>,
parsers: HashMap<E::Key, P, RandomState>,
reassembler_factory: BufferedReassemblerFactory,
reassemblers: HashMap<(E::Key, FlowSide), BufferedReassembler, RandomState>,
pending: VecDeque<SessionEvent<E::Key, <P as SessionParser>::Message>>,
finished: bool,
}
impl<E, P> PcapSessionStream<E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + 'static,
P: SessionParser + Clone + Send + Sync,
{
pub(crate) fn new(source: AsyncPcapSource, tracker: FlowTracker<E, ()>, parser: P) -> Self {
let reassembler_factory = build_reassembler_factory(tracker.config());
Self {
source,
tracker,
parser_factory: CloneSeed(parser),
parsers: HashMap::with_hasher(RandomState::new()),
reassembler_factory,
reassemblers: HashMap::with_hasher(RandomState::new()),
pending: VecDeque::new(),
finished: false,
}
}
pub fn tracker(&self) -> &FlowTracker<E, ()> {
&self.tracker
}
pub fn tracker_stats(&self) -> &flowscope::FlowTrackerStats {
self.tracker.stats()
}
pub fn active_flows(&self) -> usize {
self.tracker.flows().count()
}
pub fn packets_read(&self) -> u64 {
self.source.packets_yielded()
}
}
impl<E, P> Stream for PcapSessionStream<E, P>
where
E: FlowExtractor + Unpin,
E::Key: std::hash::Hash + Eq + Clone + Send + Unpin + 'static,
P: SessionParser + Clone + Send + Sync + Unpin,
<P as SessionParser>::Message: Unpin,
{
type Item = Result<SessionEvent<E::Key, <P as SessionParser>::Message>, Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.get_mut();
loop {
if let Some(ev) = this.pending.pop_front() {
return Poll::Ready(Some(Ok(ev)));
}
if this.finished {
return Poll::Ready(None);
}
match Pin::new(&mut this.source).poll_next(cx) {
Poll::Ready(Some(Ok(owned))) => {
let view = PacketView::new(&owned.data, owned.timestamp);
let view_ts = view.timestamp;
let parsers = &mut this.parsers;
let parser_factory = &mut this.parser_factory;
let reassemblers = &mut this.reassemblers;
let reassembler_factory = &mut this.reassembler_factory;
let pending = &mut this.pending;
let evts = this
.tracker
.track_with_payload(view, |key, side, seq, payload| {
if payload.is_empty() {
return;
}
reassemblers
.entry((key.clone(), side))
.or_insert_with(|| reassembler_factory.new_reassembler(key, side))
.segment(seq, payload, view_ts);
});
for ev in evts {
process_session_event::<E::Key, CloneSeed<P>>(
ev,
parsers,
parser_factory,
reassemblers,
pending,
);
}
}
Poll::Ready(Some(Err(e))) => return Poll::Ready(Some(Err(e))),
Poll::Ready(None) => {
let now = Timestamp::MAX;
let sweep_events: Vec<_> = this.tracker.sweep(now).into_iter().collect();
let mut scratch = Vec::new();
for (key, parser) in this.parsers.iter_mut() {
let parser_kind = parser.parser_kind();
let orientation = this
.tracker
.get(key)
.map(|e| e.initiator_orientation())
.unwrap_or_default();
scratch.clear();
parser.on_tick(now, &mut scratch);
for m in scratch.drain(..) {
this.pending.push_back(SessionEvent::Application {
key: key.clone(),
side: FlowSide::Initiator,
orientation,
message: m,
ts: now,
parser_kind,
});
}
}
for ev in sweep_events {
process_session_event::<E::Key, CloneSeed<P>>(
ev,
&mut this.parsers,
&mut this.parser_factory,
&mut this.reassemblers,
&mut this.pending,
);
}
this.finished = true;
}
Poll::Pending => return Poll::Pending,
}
}
}
}
pub struct PcapDatagramStream<E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + 'static,
P: DatagramParser + Clone + Send + Sync,
{
source: AsyncPcapSource,
tracker: FlowTracker<E, ()>,
factory: P,
parsers: HashMap<E::Key, P, RandomState>,
pending: VecDeque<SessionEvent<E::Key, <P as DatagramParser>::Message>>,
finished: bool,
}
impl<E, P> PcapDatagramStream<E, P>
where
E: FlowExtractor,
E::Key: std::hash::Hash + Eq + Clone + Send + 'static,
P: DatagramParser + Clone + Send + Sync,
{
pub(crate) fn new(source: AsyncPcapSource, tracker: FlowTracker<E, ()>, parser: P) -> Self {
Self {
source,
tracker,
factory: parser,
parsers: HashMap::with_hasher(RandomState::new()),
pending: VecDeque::new(),
finished: false,
}
}
pub fn tracker(&self) -> &FlowTracker<E, ()> {
&self.tracker
}
pub fn tracker_stats(&self) -> &flowscope::FlowTrackerStats {
self.tracker.stats()
}
pub fn active_flows(&self) -> usize {
self.tracker.flows().count()
}
pub fn packets_read(&self) -> u64 {
self.source.packets_yielded()
}
}
impl<E, P> Stream for PcapDatagramStream<E, P>
where
E: FlowExtractor + Unpin,
E::Key: std::hash::Hash + Eq + Clone + Send + Unpin + 'static,
P: DatagramParser + Clone + Send + Sync + Unpin,
<P as DatagramParser>::Message: Unpin,
{
type Item = Result<SessionEvent<E::Key, <P as DatagramParser>::Message>, Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.get_mut();
loop {
if let Some(ev) = this.pending.pop_front() {
return Poll::Ready(Some(Ok(ev)));
}
if this.finished {
return Poll::Ready(None);
}
match Pin::new(&mut this.source).poll_next(cx) {
Poll::Ready(Some(Ok(owned))) => {
let view = PacketView::new(&owned.data, owned.timestamp);
let view_ts = view.timestamp;
let frame: &[u8] = &owned.data;
let extracted = this.tracker.extractor().extract(view);
for ev in this.tracker.track(view) {
convert_event(ev, &mut this.parsers, &mut this.pending);
}
if let Some(extracted) = extracted
&& extracted.l4 == Some(L4Proto::Udp)
&& let Some(payload) = peek_udp_payload(frame)
{
let key = &extracted.key;
let side = match extracted.orientation {
Orientation::Forward => FlowSide::Initiator,
Orientation::Reverse => FlowSide::Responder,
};
let parser = this
.parsers
.entry(key.clone())
.or_insert_with(|| this.factory.clone());
let parser_kind = parser.parser_kind();
let mut messages = Vec::new();
parser.parse(payload, side, view_ts, &mut messages);
for message in messages {
this.pending.push_back(SessionEvent::Application {
key: key.clone(),
side,
orientation: extracted.orientation,
message,
ts: view_ts,
parser_kind,
});
}
}
}
Poll::Ready(Some(Err(e))) => return Poll::Ready(Some(Err(e))),
Poll::Ready(None) => {
let now = Timestamp::MAX;
let sweep_events: Vec<_> = this.tracker.sweep(now).into_iter().collect();
let mut scratch = Vec::new();
for (key, parser) in this.parsers.iter_mut() {
let parser_kind = parser.parser_kind();
let orientation = this
.tracker
.get(key)
.map(|e| e.initiator_orientation())
.unwrap_or_default();
scratch.clear();
parser.on_tick(now, &mut scratch);
for m in scratch.drain(..) {
this.pending.push_back(SessionEvent::Application {
key: key.clone(),
side: FlowSide::Initiator,
orientation,
message: m,
ts: now,
parser_kind,
});
}
}
for ev in sweep_events {
convert_event(ev, &mut this.parsers, &mut this.pending);
}
this.finished = true;
}
Poll::Pending => return Poll::Pending,
}
}
}
}