pub mod active;
pub mod dead;
pub mod iterator;
pub mod offline;
use std::{
any::Any,
convert::TryInto,
ffi::CString,
fmt, mem,
panic::{catch_unwind, resume_unwind, AssertUnwindSafe},
path::Path,
ptr::{self, NonNull},
slice,
sync::{Arc, Weak},
};
#[cfg(not(windows))]
use std::os::unix::io::RawFd;
use crate::{
capture::{Activated, Capture, PcapHandle},
codec::PacketCodec,
linktype::Linktype,
packet::{Packet, PacketHeader},
raw, Error,
};
use iterator::PacketIter;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Stat {
pub received: u32,
pub dropped: u32,
pub if_dropped: u32,
}
impl Stat {
fn new(received: u32, dropped: u32, if_dropped: u32) -> Stat {
Stat {
received,
dropped,
if_dropped,
}
}
}
#[repr(u32)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Direction {
InOut = raw::PCAP_D_INOUT,
In = raw::PCAP_D_IN,
Out = raw::PCAP_D_OUT,
}
impl<T: Activated + ?Sized> Capture<T> {
pub fn list_datalinks(&self) -> Result<Vec<Linktype>, Error> {
unsafe {
let mut links: *mut i32 = ptr::null_mut();
let num = raw::pcap_list_datalinks(self.handle.as_ptr(), &mut links);
let mut vec = vec![];
if num > 0 {
vec.extend(
slice::from_raw_parts(links, num as _)
.iter()
.cloned()
.map(Linktype),
)
}
raw::pcap_free_datalinks(links);
self.check_err(num > 0).and(Ok(vec))
}
}
pub fn set_datalink(&mut self, linktype: Linktype) -> Result<(), Error> {
self.check_err(unsafe { raw::pcap_set_datalink(self.handle.as_ptr(), linktype.0) == 0 })
}
pub fn get_datalink(&self) -> Linktype {
unsafe { Linktype(raw::pcap_datalink(self.handle.as_ptr())) }
}
pub fn savefile<P: AsRef<Path>>(&self, path: P) -> Result<Savefile, Error> {
let name = CString::new(path.as_ref().to_str().unwrap())?;
let handle_opt = NonNull::<raw::pcap_dumper_t>::new(unsafe {
raw::pcap_dump_open(self.handle.as_ptr(), name.as_ptr())
});
let handle = self
.check_err(handle_opt.is_some())
.map(|_| handle_opt.unwrap())?;
Ok(Savefile::from(handle))
}
#[cfg(not(windows))]
pub unsafe fn savefile_raw_fd(&self, fd: RawFd) -> Result<Savefile, Error> {
open_raw_fd(fd, b'w').and_then(|file| {
let handle_opt = NonNull::<raw::pcap_dumper_t>::new(raw::pcap_dump_fopen(
self.handle.as_ptr(),
file,
));
let handle = self
.check_err(handle_opt.is_some())
.map(|_| handle_opt.unwrap())?;
Ok(Savefile::from(handle))
})
}
#[cfg(libpcap_1_7_2)]
pub fn savefile_append<P: AsRef<Path>>(&self, path: P) -> Result<Savefile, Error> {
let name = CString::new(path.as_ref().to_str().unwrap())?;
let handle_opt = NonNull::<raw::pcap_dumper_t>::new(unsafe {
raw::pcap_dump_open_append(self.handle.as_ptr(), name.as_ptr())
});
let handle = self
.check_err(handle_opt.is_some())
.map(|_| handle_opt.unwrap())?;
Ok(Savefile::from(handle))
}
pub fn direction(&self, direction: Direction) -> Result<(), Error> {
self.check_err(unsafe {
raw::pcap_setdirection(self.handle.as_ptr(), direction as u32 as _) == 0
})
}
pub fn next_packet(&mut self) -> Result<Packet<'_>, Error> {
unsafe {
let mut header: *mut raw::pcap_pkthdr = ptr::null_mut();
let mut packet: *const libc::c_uchar = ptr::null();
let retcode = raw::pcap_next_ex(self.handle.as_ptr(), &mut header, &mut packet);
match retcode {
i if i >= 1 => {
Ok(Packet::new(
&*(&*header as *const raw::pcap_pkthdr as *const PacketHeader),
slice::from_raw_parts(packet, (*header).caplen as _),
))
}
0 => {
Err(Error::TimeoutExpired)
}
-1 => {
Err(self.get_err())
}
-2 => {
Err(Error::NoMorePackets)
}
_ => {
unreachable!()
} }
}
}
pub fn iter<C: PacketCodec>(self, codec: C) -> PacketIter<T, C> {
PacketIter::new(self, codec)
}
pub fn for_each<F>(&mut self, count: Option<usize>, handler: F) -> Result<(), Error>
where
F: FnMut(Packet),
{
let cnt = match count {
Some(0) => return Ok(()),
Some(cnt) => cnt
.try_into()
.expect("count of packets to read cannot exceed c_int::MAX"),
None => -1,
};
let mut handler = HandlerFn {
func: AssertUnwindSafe(handler),
panic_payload: None,
handle: self.handle.clone(),
};
let return_code = unsafe {
raw::pcap_loop(
self.handle.as_ptr(),
cnt,
HandlerFn::<F>::callback,
&mut handler as *mut HandlerFn<AssertUnwindSafe<F>> as *mut u8,
)
};
if let Some(e) = handler.panic_payload {
resume_unwind(e);
}
self.check_err(return_code == 0)
}
pub fn breakloop_handle(&mut self) -> BreakLoop {
BreakLoop {
handle: Arc::<PcapHandle>::downgrade(&self.handle),
}
}
pub fn compile(&self, program: &str, optimize: bool) -> Result<BpfProgram, Error> {
let program = CString::new(program)?;
unsafe {
let mut bpf_program: raw::bpf_program = mem::zeroed();
let ret = raw::pcap_compile(
self.handle.as_ptr(),
&mut bpf_program,
program.as_ptr(),
optimize as libc::c_int,
0,
);
self.check_err(ret != -1).and(Ok(BpfProgram(bpf_program)))
}
}
pub fn filter(&mut self, program: &str, optimize: bool) -> Result<(), Error> {
let mut bpf_program = self.compile(program, optimize)?;
let ret = unsafe { raw::pcap_setfilter(self.handle.as_ptr(), &mut bpf_program.0) };
self.check_err(ret != -1)
}
pub fn stats(&mut self) -> Result<Stat, Error> {
unsafe {
let mut stats: raw::pcap_stat = mem::zeroed();
self.check_err(raw::pcap_stats(self.handle.as_ptr(), &mut stats) != -1)
.map(|_| Stat::new(stats.ps_recv, stats.ps_drop, stats.ps_ifdrop))
}
}
}
struct HandlerFn<F> {
func: F,
panic_payload: Option<Box<dyn Any + Send>>,
handle: Arc<PcapHandle>,
}
impl<F> HandlerFn<F>
where
F: FnMut(Packet),
{
extern "C" fn callback(
slf: *mut libc::c_uchar,
header: *const raw::pcap_pkthdr,
packet: *const libc::c_uchar,
) {
unsafe {
let packet = Packet::new(
&*(header as *const PacketHeader),
slice::from_raw_parts(packet, (*header).caplen as _),
);
let slf = slf as *mut Self;
let func = &mut (*slf).func;
let mut func = AssertUnwindSafe(func);
if let Err(e) = catch_unwind(move || func(packet)) {
(*slf).panic_payload = Some(e);
raw::pcap_breakloop((*slf).handle.as_ptr());
}
}
}
}
impl<T: Activated> From<Capture<T>> for Capture<dyn Activated> {
fn from(cap: Capture<T>) -> Capture<dyn Activated> {
unsafe { mem::transmute(cap) }
}
}
pub struct BreakLoop {
handle: Weak<PcapHandle>,
}
unsafe impl Send for BreakLoop {}
unsafe impl Sync for BreakLoop {}
impl BreakLoop {
pub fn breakloop(&self) {
if let Some(handle) = self.handle.upgrade() {
unsafe { raw::pcap_breakloop(handle.as_ptr()) };
}
}
}
pub struct Savefile {
handle: NonNull<raw::pcap_dumper_t>,
}
unsafe impl Send for Savefile {}
impl Savefile {
pub fn write(&mut self, packet: &Packet<'_>) {
unsafe {
raw::pcap_dump(
self.handle.as_ptr() as _,
&*(packet.header as *const PacketHeader as *const raw::pcap_pkthdr),
packet.data.as_ptr(),
);
}
}
pub fn flush(&mut self) -> Result<(), Error> {
if unsafe { raw::pcap_dump_flush(self.handle.as_ptr() as _) } != 0 {
return Err(Error::ErrnoError(errno::errno()));
}
Ok(())
}
}
impl From<NonNull<raw::pcap_dumper_t>> for Savefile {
fn from(handle: NonNull<raw::pcap_dumper_t>) -> Self {
Savefile { handle }
}
}
impl Drop for Savefile {
fn drop(&mut self) {
unsafe { raw::pcap_dump_close(self.handle.as_ptr()) }
}
}
#[repr(transparent)]
pub struct BpfInstruction(raw::bpf_insn);
#[repr(transparent)]
pub struct BpfProgram(raw::bpf_program);
impl BpfProgram {
pub fn filter(&self, buf: &[u8]) -> bool {
let header: raw::pcap_pkthdr = raw::pcap_pkthdr {
ts: libc::timeval {
tv_sec: 0,
tv_usec: 0,
},
caplen: buf.len() as u32,
len: buf.len() as u32,
};
unsafe { raw::pcap_offline_filter(&self.0, &header, buf.as_ptr()) > 0 }
}
pub fn get_instructions(&self) -> &[BpfInstruction] {
unsafe {
slice::from_raw_parts(
self.0.bf_insns as *const BpfInstruction,
self.0.bf_len as usize,
)
}
}
}
impl Drop for BpfProgram {
fn drop(&mut self) {
unsafe { raw::pcap_freecode(&mut self.0) }
}
}
impl fmt::Display for BpfInstruction {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} {} {} {}",
self.0.code, self.0.jt, self.0.jf, self.0.k
)
}
}
unsafe impl Send for BpfProgram {}
#[cfg(not(windows))]
pub unsafe fn open_raw_fd(fd: RawFd, mode: u8) -> Result<*mut libc::FILE, Error> {
let mode = [mode, 0];
libc::fdopen(fd, mode.as_ptr() as _)
.as_mut()
.map(|f| f as _)
.ok_or(Error::InvalidRawFd)
}
#[cfg(test)]
mod testmod {
use super::*;
pub static TS: libc::timeval = libc::timeval {
tv_sec: 5,
tv_usec: 50,
};
pub static LEN: u32 = DATA.len() as u32;
pub static CAPLEN: u32 = LEN;
pub static mut PKTHDR: raw::pcap_pkthdr = raw::pcap_pkthdr {
ts: TS,
caplen: CAPLEN,
len: LEN,
};
pub static PACKET_HEADER: PacketHeader = PacketHeader {
ts: TS,
caplen: CAPLEN,
len: LEN,
};
pub static DATA: [u8; 4] = [4, 5, 6, 7];
pub static PACKET: Packet = Packet {
header: &PACKET_HEADER,
data: &DATA,
};
pub struct NextExContext(raw::__pcap_next_ex::Context);
pub fn next_ex_expect(pcap: *mut raw::pcap_t) -> NextExContext {
let data_ptr: *const libc::c_uchar = DATA.as_ptr();
#[allow(unused_unsafe)] let pkthdr_ptr: *mut raw::pcap_pkthdr = unsafe { std::ptr::addr_of_mut!(PKTHDR) };
let ctx = raw::pcap_next_ex_context();
ctx.checkpoint();
ctx.expect()
.withf_st(move |arg1, _, _| *arg1 == pcap)
.return_once_st(move |_, arg2, arg3| {
unsafe {
*arg2 = pkthdr_ptr;
*arg3 = data_ptr;
}
CAPLEN as i32
});
NextExContext(ctx)
}
}
#[cfg(test)]
mod tests {
use crate::{
capture::{
activated::testmod::{next_ex_expect, PACKET},
testmod::test_capture,
Active, Capture, Offline,
},
raw::testmod::{as_pcap_dumper_t, as_pcap_t, geterr_expect, RAWMTX},
};
use super::*;
#[test]
fn test_list_datalinks() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Active>(pcap);
let capture: Capture<dyn Activated> = test_capture.capture.into();
let ctx = raw::pcap_list_datalinks_context();
ctx.expect()
.withf_st(move |arg1, _| *arg1 == pcap)
.return_once_st(|_, _| 0);
let ctx = raw::pcap_free_datalinks_context();
ctx.expect().return_once(|_| {});
let _err = geterr_expect(pcap);
let result = capture.list_datalinks();
assert!(result.is_err());
let mut datalinks: [i32; 4] = [0, 1, 2, 3];
let links: *mut i32 = datalinks.as_mut_ptr();
let len = datalinks.len();
let ctx = raw::pcap_list_datalinks_context();
ctx.checkpoint();
ctx.expect()
.withf_st(move |arg1, _| *arg1 == pcap)
.return_once_st(move |_, arg2| {
unsafe { *arg2 = links };
len as i32
});
let ctx = raw::pcap_free_datalinks_context();
ctx.checkpoint();
ctx.expect().return_once(|_| {});
let pcap_datalinks = capture.list_datalinks().unwrap();
assert_eq!(
pcap_datalinks,
datalinks.iter().cloned().map(Linktype).collect::<Vec<_>>()
);
}
#[test]
fn test_set_datalink() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Active>(pcap);
let mut capture: Capture<dyn Activated> = test_capture.capture.into();
let ctx = raw::pcap_set_datalink_context();
ctx.expect()
.withf_st(move |arg1, _| *arg1 == pcap)
.return_once(|_, _| 0);
let result = capture.set_datalink(Linktype::ETHERNET);
assert!(result.is_ok());
let ctx = raw::pcap_set_datalink_context();
ctx.checkpoint();
ctx.expect()
.withf_st(move |arg1, _| *arg1 == pcap)
.return_once(|_, _| -1);
let _err = geterr_expect(pcap);
let result = capture.set_datalink(Linktype::ETHERNET);
assert!(result.is_err());
}
#[test]
fn test_get_datalink() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Active>(pcap);
let capture: Capture<dyn Activated> = test_capture.capture.into();
let ctx = raw::pcap_datalink_context();
ctx.expect()
.withf_st(move |arg1| *arg1 == pcap)
.return_once(|_| 1);
let linktype = capture.get_datalink();
assert_eq!(linktype, Linktype::ETHERNET);
}
#[test]
fn unify_activated() {
#![allow(dead_code)]
fn test1() -> Capture<Active> {
panic!();
}
fn test2() -> Capture<Offline> {
panic!();
}
fn maybe(a: bool) -> Capture<dyn Activated> {
if a {
test1().into()
} else {
test2().into()
}
}
fn also_maybe(a: &mut Capture<dyn Activated>) {
a.filter("whatever filter string, this won't be run anyway", false)
.unwrap();
}
}
#[test]
fn test_breakloop_capture_dropped() {
let _m = RAWMTX.lock();
let mut value: isize = 1234;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Active>(pcap);
let mut capture: Capture<dyn Activated> = test_capture.capture.into();
let ctx = raw::pcap_breakloop_context();
ctx.expect()
.withf_st(move |h| *h == pcap)
.return_const(())
.times(1);
let break_handle = capture.breakloop_handle();
break_handle.breakloop();
drop(capture);
break_handle.breakloop(); }
#[test]
fn test_savefile() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let mut value: isize = 888;
let pcap_dumper = as_pcap_dumper_t(&mut value);
let test_capture = test_capture::<Offline>(pcap);
let capture = test_capture.capture;
let ctx = raw::pcap_dump_open_context();
ctx.expect()
.withf_st(move |arg1, _| *arg1 == pcap)
.return_once_st(move |_, _| pcap_dumper);
let ctx = raw::pcap_dump_close_context();
ctx.expect()
.withf_st(move |arg1| *arg1 == pcap_dumper)
.return_once(|_| {});
let result = capture.savefile("path/to/nowhere");
assert!(result.is_ok());
}
#[test]
#[cfg(libpcap_1_7_2)]
fn test_savefile_append() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let mut value: isize = 888;
let pcap_dumper = as_pcap_dumper_t(&mut value);
let test_capture = test_capture::<Offline>(pcap);
let capture = test_capture.capture;
let ctx = raw::pcap_dump_open_append_context();
ctx.expect()
.withf_st(move |arg1, _| *arg1 == pcap)
.return_once_st(move |_, _| pcap_dumper);
let ctx = raw::pcap_dump_close_context();
ctx.expect()
.withf_st(move |arg1| *arg1 == pcap_dumper)
.return_once(|_| {});
let result = capture.savefile_append("path/to/nowhere");
assert!(result.is_ok());
}
#[test]
fn test_savefile_error() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Offline>(pcap);
let capture = test_capture.capture;
let ctx = raw::pcap_dump_open_context();
ctx.expect()
.withf_st(move |arg1, _| *arg1 == pcap)
.return_once(|_, _| std::ptr::null_mut());
let _err = geterr_expect(pcap);
let result = capture.savefile("path/to/nowhere");
assert!(result.is_err());
}
#[test]
#[cfg(libpcap_1_7_2)]
fn test_savefile_append_error() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Offline>(pcap);
let capture = test_capture.capture;
let ctx = raw::pcap_dump_open_append_context();
ctx.expect()
.withf_st(move |arg1, _| *arg1 == pcap)
.return_once(|_, _| std::ptr::null_mut());
let _err = geterr_expect(pcap);
let result = capture.savefile_append("path/to/nowhere");
assert!(result.is_err());
}
#[test]
fn test_savefile_ops() {
let _m = RAWMTX.lock();
let mut value: isize = 888;
let pcap_dumper = as_pcap_dumper_t(&mut value);
let ctx = raw::pcap_dump_close_context();
ctx.expect()
.withf_st(move |arg1| *arg1 == pcap_dumper)
.return_once(|_| {});
let mut savefile = Savefile {
handle: NonNull::new(pcap_dumper).unwrap(),
};
let ctx = raw::pcap_dump_context();
ctx.expect()
.withf_st(move |arg1, _, _| *arg1 == pcap_dumper as _)
.return_once(|_, _, _| {});
savefile.write(&PACKET);
let ctx = raw::pcap_dump_flush_context();
ctx.expect()
.withf_st(move |arg1| *arg1 == pcap_dumper)
.return_once(|_| 0);
let result = savefile.flush();
assert!(result.is_ok());
let ctx = raw::pcap_dump_flush_context();
ctx.checkpoint();
ctx.expect()
.withf_st(move |arg1| *arg1 == pcap_dumper)
.return_once(|_| -1);
let result = savefile.flush();
assert!(result.is_err());
}
#[test]
fn test_direction() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Active>(pcap);
let capture = test_capture.capture;
let ctx = raw::pcap_setdirection_context();
ctx.expect()
.withf_st(move |arg1, arg2| (*arg1 == pcap) && (*arg2 == raw::PCAP_D_OUT))
.return_once(|_, _| 0);
let result = capture.direction(Direction::Out);
assert!(result.is_ok());
let ctx = raw::pcap_setdirection_context();
ctx.checkpoint();
ctx.expect()
.withf_st(move |arg1, arg2| (*arg1 == pcap) && (*arg2 == raw::PCAP_D_OUT))
.return_once(|_, _| -1);
let _err = geterr_expect(pcap);
let result = capture.direction(Direction::Out);
assert!(result.is_err());
assert_ne!(Direction::In, Direction::InOut);
assert_ne!(Direction::In, Direction::Out);
assert_ne!(Direction::InOut, Direction::Out);
}
#[test]
fn test_next_packet() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Active>(pcap);
let mut capture = test_capture.capture;
let _nxt = next_ex_expect(pcap);
let next_packet = capture.next_packet().unwrap();
assert_eq!(next_packet, PACKET);
}
#[test]
fn test_next_packet_timeout() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Active>(pcap);
let mut capture = test_capture.capture;
let ctx = raw::pcap_next_ex_context();
ctx.expect()
.withf_st(move |arg1, _, _| *arg1 == pcap)
.return_once_st(move |_, _, _| 0);
let err = capture.next_packet().unwrap_err();
assert_eq!(err, Error::TimeoutExpired);
}
#[test]
fn test_next_packet_read_error() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Active>(pcap);
let mut capture = test_capture.capture;
let ctx = raw::pcap_next_ex_context();
ctx.expect()
.withf_st(move |arg1, _, _| *arg1 == pcap)
.return_once_st(move |_, _, _| -1);
let _err = geterr_expect(pcap);
let result = capture.next_packet();
assert!(result.is_err());
}
#[test]
fn test_next_packet_no_more_packets() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Offline>(pcap);
let mut capture = test_capture.capture;
let ctx = raw::pcap_next_ex_context();
ctx.expect()
.withf_st(move |arg1, _, _| *arg1 == pcap)
.return_once_st(move |_, _, _| -2);
let err = capture.next_packet().unwrap_err();
assert_eq!(err, Error::NoMorePackets);
}
#[test]
fn test_compile() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Active>(pcap);
let capture = test_capture.capture;
let ctx = raw::pcap_compile_context();
ctx.expect()
.withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
.return_once(|_, _, _, _, _| -1);
let _err = geterr_expect(pcap);
let ctx = raw::pcap_freecode_context();
ctx.expect().return_once(|_| {});
let result = capture.compile("some bpf program", false);
assert!(result.is_err());
let ctx = raw::pcap_compile_context();
ctx.checkpoint();
ctx.expect()
.withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
.return_once(|_, _, _, _, _| 0);
let ctx = raw::pcap_freecode_context();
ctx.checkpoint();
ctx.expect().return_once(|_| {});
let result = capture.compile("some bpf program", false);
assert!(result.is_ok());
}
#[test]
fn test_filter() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Active>(pcap);
let mut capture = test_capture.capture;
let ctx = raw::pcap_compile_context();
ctx.expect()
.withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
.return_once(|_, _, _, _, _| 0);
let ctx = raw::pcap_setfilter_context();
ctx.expect()
.withf_st(move |arg1, _| *arg1 == pcap)
.return_once(|_, _| -1);
let _err = geterr_expect(pcap);
let ctx = raw::pcap_freecode_context();
ctx.expect().return_once(|_| {});
let result = capture.filter("some bpf program", false);
assert!(result.is_err());
let ctx = raw::pcap_compile_context();
ctx.checkpoint();
ctx.expect()
.withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
.return_once(|_, _, _, _, _| 0);
let ctx = raw::pcap_setfilter_context();
ctx.checkpoint();
ctx.expect()
.withf_st(move |arg1, _| *arg1 == pcap)
.return_once(|_, _| 0);
let ctx = raw::pcap_freecode_context();
ctx.checkpoint();
ctx.expect().return_once(|_| {});
let result = capture.compile("some bpf program", false);
assert!(result.is_ok());
}
#[test]
fn test_stats() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Active>(pcap);
let mut capture = test_capture.capture;
let stat = raw::pcap_stat {
ps_recv: 1,
ps_drop: 2,
ps_ifdrop: 3,
};
let ctx = raw::pcap_stats_context();
ctx.expect()
.withf_st(move |arg1, _| *arg1 == pcap)
.return_once_st(move |_, arg2| {
unsafe { *arg2 = stat };
0
});
let stats = capture.stats().unwrap();
assert_eq!(stats, Stat::new(stat.ps_recv, stat.ps_drop, stat.ps_ifdrop));
let ctx = raw::pcap_stats_context();
ctx.checkpoint();
ctx.expect()
.withf_st(move |arg1, _| *arg1 == pcap)
.return_once_st(move |_, _| -1);
let _err = geterr_expect(pcap);
let result = capture.stats();
assert!(result.is_err());
}
#[test]
fn test_bpf_instruction_display() {
let instr = BpfInstruction(raw::bpf_insn {
code: 1,
jt: 2,
jf: 3,
k: 4,
});
assert_eq!(format!("{instr}"), "1 2 3 4");
}
#[test]
fn read_packet_via_pcap_loop() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Active>(pcap);
let mut capture: Capture<dyn Activated> = test_capture.capture.into();
let ctx = raw::pcap_loop_context();
ctx.expect()
.withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == -1)
.return_once_st(move |_, _, func, data| {
let header = raw::pcap_pkthdr {
ts: libc::timeval {
tv_sec: 0,
tv_usec: 0,
},
caplen: 0,
len: 0,
};
let packet_data = &[];
func(data, &header, packet_data.as_ptr());
0
});
let mut packets = 0;
capture
.for_each(None, |_| {
packets += 1;
})
.unwrap();
assert_eq!(packets, 1);
}
#[test]
#[should_panic = "panic in callback"]
fn panic_in_pcap_loop() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Active>(pcap);
let mut capture: Capture<dyn Activated> = test_capture.capture.into();
let ctx = raw::pcap_loop_context();
ctx.expect()
.withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == -1)
.return_once_st(move |_, _, func, data| {
let header = raw::pcap_pkthdr {
ts: libc::timeval {
tv_sec: 0,
tv_usec: 0,
},
caplen: 0,
len: 0,
};
let packet_data = &[];
func(data, &header, packet_data.as_ptr());
0
});
let ctx = raw::pcap_breakloop_context();
ctx.expect()
.withf_st(move |arg1| *arg1 == pcap)
.return_once_st(move |_| {});
capture
.for_each(None, |_| panic!("panic in callback"))
.unwrap();
}
#[test]
fn for_each_with_count() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Active>(pcap);
let mut capture: Capture<dyn Activated> = test_capture.capture.into();
let ctx = raw::pcap_loop_context();
ctx.expect()
.withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == 2)
.return_once_st(move |_, _, func, data| {
let header = raw::pcap_pkthdr {
ts: libc::timeval {
tv_sec: 0,
tv_usec: 0,
},
caplen: 0,
len: 0,
};
let packet_data = &[];
func(data, &header, packet_data.as_ptr());
func(data, &header, packet_data.as_ptr());
0
});
let mut packets = 0;
capture
.for_each(Some(2), |_| {
packets += 1;
})
.unwrap();
assert_eq!(packets, 2);
}
#[test]
fn for_each_with_count_0() {
let _m = RAWMTX.lock();
let mut value: isize = 777;
let pcap = as_pcap_t(&mut value);
let test_capture = test_capture::<Active>(pcap);
let mut capture: Capture<dyn Activated> = test_capture.capture.into();
let mut packets = 0;
capture
.for_each(Some(0), |_| {
packets += 1;
})
.unwrap();
assert_eq!(packets, 0);
}
}