Skip to content

Commit 2207e18

Browse files
committed
Add a binding for pcap_loop
1 parent 7709be8 commit 2207e18

4 files changed

Lines changed: 106 additions & 4 deletions

File tree

‎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/capture/activated/mod.rs‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ pub mod iterator;
44
pub mod offline;
55

66
use std::{
7+
any::Any,
78
ffi::CString,
89
fmt, mem,
10+
panic::{catch_unwind, resume_unwind, AssertUnwindSafe},
911
path::Path,
1012
ptr::{self, NonNull},
1113
slice,
@@ -199,6 +201,28 @@ impl<T: Activated + ?Sized> Capture<T> {
199201
PacketIter::new(self, codec)
200202
}
201203

204+
pub fn for_each<F>(&mut self, handler: F)
205+
where
206+
F: FnMut(Packet),
207+
{
208+
let mut handler = Handler {
209+
func: AssertUnwindSafe(handler),
210+
panic_payload: None,
211+
handle: self.handle,
212+
};
213+
unsafe {
214+
raw::pcap_loop(
215+
self.handle.as_ptr(),
216+
-1,
217+
Handler::<F>::callback,
218+
&mut handler as *mut Handler<AssertUnwindSafe<F>> as *mut u8,
219+
)
220+
};
221+
if let Some(e) = handler.panic_payload {
222+
resume_unwind(e);
223+
}
224+
}
225+
202226
/// Compiles the string into a filter program using `pcap_compile`.
203227
pub fn compile(&self, program: &str, optimize: bool) -> Result<BpfProgram, Error> {
204228
let program = CString::new(program)?;
@@ -241,6 +265,45 @@ impl<T: Activated + ?Sized> Capture<T> {
241265
}
242266
}
243267

268+
// Handler and its associated function let us create an extern "C" fn which dispatches to a normal
269+
// Rust FnMut, which may be a closure with a captured environment. The *only* purpose of this
270+
// generic parameter is to ensure that in Capture::pcap_loop that we pass the right function
271+
// pointer and the right data pointer to pcap_loop.
272+
struct Handler<F> {
273+
func: F,
274+
panic_payload: Option<Box<dyn Any + Send>>,
275+
handle: NonNull<raw::pcap_t>,
276+
}
277+
278+
impl<F> Handler<F>
279+
where
280+
F: FnMut(Packet),
281+
{
282+
extern "C" fn callback(
283+
slf: *mut libc::c_uchar,
284+
header: *const raw::pcap_pkthdr,
285+
packet: *const libc::c_uchar,
286+
) {
287+
unsafe {
288+
let packet = Packet::new(
289+
&*(header as *const PacketHeader),
290+
slice::from_raw_parts(packet, (*header).caplen as _),
291+
);
292+
293+
let slf = slf as *mut Self;
294+
let func = &mut (*slf).func;
295+
let mut func = AssertUnwindSafe(func);
296+
// If our handler function panics, we need to prevent it from unwinding across the
297+
// FFI boundary. If the handler panics we catch the unwind here, break out of
298+
// pcap_loop, and resume the unwind outside.
299+
if let Err(e) = catch_unwind(move || func(packet)) {
300+
(*slf).panic_payload = Some(e);
301+
raw::pcap_breakloop((*slf).handle.as_ptr());
302+
}
303+
}
304+
}
305+
}
306+
244307
impl<T: Activated> From<Capture<T>> for Capture<dyn Activated> {
245308
fn from(cap: Capture<T>) -> Capture<dyn Activated> {
246309
unsafe { mem::transmute(cap) }

‎src/raw.rs‎

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

103+
// This is not Option<fn>, pcap functions do not check if the handler is null so it is wrong to
104+
// pass them Option::<fn>::None.
103105
pub type pcap_handler =
104-
Option<extern "C" fn(arg1: *mut c_uchar, arg2: *const pcap_pkthdr, arg3: *const c_uchar) -> ()>;
106+
extern "C" fn(arg1: *mut c_uchar, arg2: *const pcap_pkthdr, arg3: *const c_uchar) -> ();
105107

106108
#[cfg_attr(test, automock)]
107109
pub mod ffi {
@@ -124,8 +126,12 @@ pub mod ffi {
124126
pub fn pcap_open_offline(arg1: *const c_char, arg2: *mut c_char) -> *mut pcap_t;
125127
pub fn pcap_fopen_offline(arg1: *mut FILE, arg2: *mut c_char) -> *mut pcap_t;
126128
pub fn pcap_close(arg1: *mut pcap_t);
127-
// pub fn pcap_loop(arg1: *mut pcap_t, arg2: c_int,
128-
// arg3: pcap_handler, arg4: *mut c_uchar) -> c_int;
129+
pub fn pcap_loop(
130+
arg1: *mut pcap_t,
131+
arg2: c_int,
132+
arg3: pcap_handler,
133+
arg4: *mut c_uchar,
134+
) -> c_int;
129135
// pub fn pcap_dispatch(arg1: *mut pcap_t, arg2: c_int, arg3: pcap_handler,
130136
// arg4: *mut c_uchar)-> c_int;
131137
// pub fn pcap_next(arg1: *mut pcap_t, arg2: *mut pcap_pkthdr) -> *const c_uchar;
@@ -134,7 +140,7 @@ pub mod ffi {
134140
arg2: *mut *mut pcap_pkthdr,
135141
arg3: *mut *const c_uchar,
136142
) -> c_int;
137-
// pub fn pcap_breakloop(arg1: *mut pcap_t);
143+
pub fn pcap_breakloop(arg1: *mut pcap_t);
138144
pub fn pcap_stats(arg1: *mut pcap_t, arg2: *mut pcap_stat) -> c_int;
139145
pub fn pcap_setfilter(arg1: *mut pcap_t, arg2: *mut bpf_program) -> c_int;
140146
pub fn pcap_setdirection(arg1: *mut pcap_t, arg2: pcap_direction_t) -> c_int;

‎tests/capture/activated/mod.rs‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,13 @@ fn test_filter() {
136136
let result = capture.next_packet();
137137
assert!(result.is_ok());
138138
}
139+
140+
#[test]
141+
fn read_packet_via_pcap_loop() {
142+
let mut packets = 0;
143+
let mut capture = capture_from_test_file("packet_snaplen_65535.pcap");
144+
capture.for_each(|_| {
145+
packets += 1;
146+
});
147+
assert_eq!(packets, 1);
148+
}

0 commit comments

Comments
 (0)