Skip to content

Commit 09075c3

Browse files
committed
Add a binding for pcap_loop
1 parent 74a04de commit 09075c3

File tree

4 files changed

+105
-4
lines changed

4 files changed

+105
-4
lines changed

examples/loop.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
fn main() {
2+
// get the default Device
3+
let device = pcap::Device::lookup()
4+
.expect("device lookup failed")
5+
.expect("no device available");
6+
println!("Using device {}", device.name);
7+
8+
// Setup Capture
9+
let mut cap = pcap::Capture::from_device(device)
10+
.unwrap()
11+
.immediate_mode(true)
12+
.open()
13+
.unwrap();
14+
15+
let mut count = 0;
16+
cap.for_each(|packet| {
17+
println!("Got {:?}", packet.header);
18+
count += 1;
19+
if count > 100 {
20+
panic!("ow");
21+
}
22+
});
23+
}

src/lib.rs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
//! ```
6060
6161
use bitflags::bitflags;
62+
use std::any::Any;
6263
use std::borrow::Borrow;
6364
use std::convert::TryFrom;
6465
use std::ffi::{self, CStr, CString};
@@ -69,6 +70,7 @@ use std::net::IpAddr;
6970
use std::ops::Deref;
7071
#[cfg(not(windows))]
7172
use std::os::unix::io::{AsRawFd, RawFd};
73+
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
7274
use std::path::Path;
7375
use std::ptr::{self, NonNull};
7476
use std::slice;
@@ -835,7 +837,68 @@ impl<T: State + ?Sized> From<NonNull<raw::pcap_t>> for Capture<T> {
835837
}
836838
}
837839

840+
// Handler and its associated function let us create an extern "C" fn which dispatches to a normal
841+
// Rust FnMut, which may be a closure with a captured environment. The *only* purpose of this
842+
// generic parameter is to ensure that in Capture::pcap_loop that we pass the right function
843+
// pointer and the right data pointer to pcap_loop.
844+
struct Handler<F> {
845+
func: F,
846+
panic_payload: Option<Box<dyn Any + Send>>,
847+
handle: NonNull<raw::pcap_t>,
848+
}
849+
850+
impl<F> Handler<F>
851+
where
852+
F: FnMut(Packet),
853+
{
854+
extern "C" fn callback(
855+
slf: *mut libc::c_uchar,
856+
header: *const raw::pcap_pkthdr,
857+
packet: *const libc::c_uchar,
858+
) {
859+
unsafe {
860+
let packet = Packet::new(
861+
&*(header as *const PacketHeader),
862+
slice::from_raw_parts(packet, (*header).caplen as _),
863+
);
864+
865+
let slf = slf as *mut Self;
866+
let func = &mut (*slf).func;
867+
let mut func = AssertUnwindSafe(func);
868+
// If our handler function panics, we need to prevent it from unwinding across the
869+
// FFI boundary. If the handler panics we catch the unwind here, break out of
870+
// pcap_loop, and resume the unwind outside.
871+
if let Err(e) = catch_unwind(move || func(packet)) {
872+
(*slf).panic_payload = Some(e);
873+
raw::pcap_breakloop((*slf).handle.as_ptr());
874+
}
875+
}
876+
}
877+
}
878+
838879
impl<T: State + ?Sized> Capture<T> {
880+
pub fn for_each<F>(&mut self, handler: F)
881+
where
882+
F: FnMut(Packet),
883+
{
884+
let mut handler = Handler {
885+
func: AssertUnwindSafe(handler),
886+
panic_payload: None,
887+
handle: self.handle,
888+
};
889+
unsafe {
890+
raw::pcap_loop(
891+
self.handle.as_ptr(),
892+
-1,
893+
Handler::<F>::callback,
894+
ptr::addr_of_mut!(handler).cast(),
895+
)
896+
};
897+
if let Some(e) = handler.panic_payload {
898+
resume_unwind(e);
899+
}
900+
}
901+
839902
fn new_raw<F>(path: Option<&str>, func: F) -> Result<Capture<T>, Error>
840903
where
841904
F: FnOnce(*const libc::c_char, *mut libc::c_char) -> *mut raw::pcap_t,

src/raw.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,9 @@ pub struct pcap_send_queue {
9999
pub buffer: *mut c_char,
100100
}
101101

102+
// This is not Option<fn>, pcap functions do not check if the handler is null.
102103
pub type pcap_handler =
103-
Option<extern "C" fn(arg1: *mut c_uchar, arg2: *const pcap_pkthdr, arg3: *const c_uchar) -> ()>;
104+
extern "C" fn(user: *mut c_uchar, h: *const pcap_pkthdr, bytes: *const c_uchar) -> ();
104105

105106
extern "C" {
106107
// [OBSOLETE] pub fn pcap_lookupdev(arg1: *mut c_char) -> *mut c_char;
@@ -119,8 +120,12 @@ extern "C" {
119120
pub fn pcap_open_offline(arg1: *const c_char, arg2: *mut c_char) -> *mut pcap_t;
120121
pub fn pcap_fopen_offline(arg1: *mut FILE, arg2: *mut c_char) -> *mut pcap_t;
121122
pub fn pcap_close(arg1: *mut pcap_t);
122-
// pub fn pcap_loop(arg1: *mut pcap_t, arg2: c_int,
123-
// arg3: pcap_handler, arg4: *mut c_uchar) -> c_int;
123+
pub fn pcap_loop(
124+
arg1: *mut pcap_t,
125+
arg2: c_int,
126+
arg3: pcap_handler,
127+
arg4: *mut c_uchar,
128+
) -> c_int;
124129
// pub fn pcap_dispatch(arg1: *mut pcap_t, arg2: c_int, arg3: pcap_handler,
125130
// arg4: *mut c_uchar)-> c_int;
126131
// pub fn pcap_next(arg1: *mut pcap_t, arg2: *mut pcap_pkthdr) -> *const c_uchar;
@@ -129,7 +134,7 @@ extern "C" {
129134
arg2: *mut *mut pcap_pkthdr,
130135
arg3: *mut *const c_uchar,
131136
) -> c_int;
132-
// pub fn pcap_breakloop(arg1: *mut pcap_t);
137+
pub fn pcap_breakloop(arg1: *mut pcap_t);
133138
pub fn pcap_stats(arg1: *mut pcap_t, arg2: *mut pcap_stat) -> c_int;
134139
pub fn pcap_setfilter(arg1: *mut pcap_t, arg2: *mut bpf_program) -> c_int;
135140
pub fn pcap_setdirection(arg1: *mut pcap_t, arg2: pcap_direction_t) -> c_int;

tests/lib.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ fn read_packet_with_full_data() {
3131
assert_eq!(capture.next_packet().unwrap().len(), 98);
3232
}
3333

34+
#[test]
35+
fn read_packet_via_pcap_loop() {
36+
let mut packets = 0;
37+
let mut capture = capture_from_test_file("packet_snaplen_65535.pcap");
38+
capture.pcap_loop(|_| {
39+
packets += 1;
40+
});
41+
assert_eq!(packets, 1);
42+
}
43+
3444
#[test]
3545
fn read_packet_with_truncated_data() {
3646
let mut capture = capture_from_test_file("packet_snaplen_20.pcap");

0 commit comments

Comments
 (0)