use std::path::{Path, PathBuf};
use std::time::Duration;
use pktbaffle::codegen::LinkType;
use pktbaffle::{compile, Target};
use crate::error::{Error, Result};
use crate::file::FileCapture;
use crate::live::{self, Live};
use crate::packet::Packet;
pub(crate) enum FilterSpec {
String(String),
Program(pktbaffle::bpf::Program),
}
enum Source {
Live(String),
File(PathBuf),
}
pub struct CaptureBuilder {
source: Source,
filter: Option<FilterSpec>,
snaplen: u32,
promiscuous: bool,
buffer_timeout: Duration,
}
impl CaptureBuilder {
fn new_live(iface: &str) -> Self {
Self {
source: Source::Live(iface.to_owned()),
filter: None,
snaplen: 65535,
promiscuous: false,
buffer_timeout: Duration::from_millis(100),
}
}
fn new_file(path: PathBuf) -> Self {
Self {
source: Source::File(path),
filter: None,
snaplen: 65535,
promiscuous: false,
buffer_timeout: Duration::from_millis(100),
}
}
pub fn filter<'a>(mut self, expr: impl Into<Option<&'a str>>) -> Self {
self.filter = expr.into().map(|s| FilterSpec::String(s.to_owned()));
self
}
pub fn filter_program(mut self, program: pktbaffle::bpf::Program) -> Self {
self.filter = Some(FilterSpec::Program(program));
self
}
pub fn snaplen(mut self, n: u32) -> Self {
self.snaplen = n;
self
}
pub fn promiscuous(mut self, on: bool) -> Self {
self.promiscuous = on;
self
}
pub fn buffer_timeout(mut self, d: Duration) -> Self {
self.buffer_timeout = d;
self
}
pub fn open(self) -> Result<Capture> {
match self.source {
Source::Live(iface) => {
let link_type = live::query_link_type(&iface)?;
let prog = compile_filter(self.filter, link_type)?;
let live = Live::open(&iface, prog.as_ref(), self.snaplen, self.promiscuous)?;
Ok(Capture {
inner: Inner::Live(live),
})
}
Source::File(path) => {
let fc = FileCapture::open(&path, self.filter)?;
Ok(Capture {
inner: Inner::File(fc),
})
}
}
}
}
enum Inner {
Live(Live),
File(FileCapture),
}
pub struct Capture {
inner: Inner,
}
impl Capture {
pub fn live(iface: &str) -> CaptureBuilder {
CaptureBuilder::new_live(iface)
}
pub fn from_file(path: impl AsRef<Path>) -> CaptureBuilder {
CaptureBuilder::new_file(path.as_ref().to_owned())
}
pub fn link_type(&self) -> LinkType {
match &self.inner {
Inner::Live(l) => l.link_type(),
Inner::File(f) => f.link_type(),
}
}
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Result<Option<Packet>> {
match &mut self.inner {
Inner::Live(l) => l.next_packet().map(Some),
Inner::File(f) => f.next_packet(),
}
}
}
pub(crate) fn compile_filter(
spec: Option<FilterSpec>,
link: LinkType,
) -> Result<Option<pktbaffle::bpf::Program>> {
match spec {
None => Ok(None),
Some(FilterSpec::Program(p)) => Ok(Some(p)),
Some(FilterSpec::String(s)) => {
let prog = compile(&s, link, Target::Classic)?;
match prog {
pktbaffle::Program::Classic(p) => Ok(Some(p)),
pktbaffle::Program::Extended(_) => Err(Error::Platform(
"unexpected eBPF program for live capture".into(),
)),
}
}
}
}