@@ -12,13 +12,14 @@ use std::{
1212 path:: Path ,
1313 ptr:: { self , NonNull } ,
1414 slice,
15+ sync:: { Arc , Weak } ,
1516} ;
1617
1718#[ cfg( not( windows) ) ]
1819use std:: os:: unix:: io:: RawFd ;
1920
2021use crate :: {
21- capture:: { Activated , Capture } ,
22+ capture:: { Activated , Capture , PcapHandle } ,
2223 codec:: PacketCodec ,
2324 linktype:: Linktype ,
2425 packet:: { Packet , PacketHeader } ,
@@ -216,17 +217,17 @@ impl<T: Activated + ?Sized> Capture<T> {
216217 None => -1 ,
217218 } ;
218219
219- let mut handler = Handler {
220+ let mut handler = HandlerFn {
220221 func : AssertUnwindSafe ( handler) ,
221222 panic_payload : None ,
222- handle : self . handle ,
223+ handle : self . handle . clone ( ) ,
223224 } ;
224225 let return_code = unsafe {
225226 raw:: pcap_loop (
226227 self . handle . as_ptr ( ) ,
227228 cnt,
228- Handler :: < F > :: callback,
229- & mut handler as * mut Handler < AssertUnwindSafe < F > > as * mut u8 ,
229+ HandlerFn :: < F > :: callback,
230+ & mut handler as * mut HandlerFn < AssertUnwindSafe < F > > as * mut u8 ,
230231 )
231232 } ;
232233 if let Some ( e) = handler. panic_payload {
@@ -235,6 +236,38 @@ impl<T: Activated + ?Sized> Capture<T> {
235236 self . check_err ( return_code == 0 )
236237 }
237238
239+ /// Returns a thread-safe `BreakLoop` handle for calling pcap_breakloop() on an active capture.
240+ ///
241+ /// # Example
242+ ///
243+ /// ```no_run
244+ /// // Using an active capture
245+ /// use pcap::Device;
246+ ///
247+ /// let mut cap = Device::lookup().unwrap().unwrap().open().unwrap();
248+ ///
249+ /// let break_handle = cap.breakloop_handle();
250+ ///
251+ /// let capture_thread = std::thread::spawn(move || {
252+ /// while let Ok(packet) = cap.next_packet() {
253+ /// println!("received packet! {:?}", packet);
254+ /// }
255+ /// });
256+ ///
257+ /// // Send break_handle to a separate thread (e.g. user input, signal handler, etc.)
258+ /// std::thread::spawn(move || {
259+ /// std::thread::sleep(std::time::Duration::from_secs(1));
260+ /// break_handle.breakloop();
261+ /// });
262+ ///
263+ /// capture_thread.join().unwrap();
264+ /// ```
265+ pub fn breakloop_handle ( & mut self ) -> BreakLoop {
266+ BreakLoop {
267+ handle : Arc :: < PcapHandle > :: downgrade ( & self . handle ) ,
268+ }
269+ }
270+
238271 /// Compiles the string into a filter program using `pcap_compile`.
239272 pub fn compile ( & self , program : & str , optimize : bool ) -> Result < BpfProgram , Error > {
240273 let program = CString :: new ( program) ?;
@@ -281,13 +314,13 @@ impl<T: Activated + ?Sized> Capture<T> {
281314// Rust FnMut, which may be a closure with a captured environment. The *only* purpose of this
282315// generic parameter is to ensure that in Capture::pcap_loop that we pass the right function
283316// pointer and the right data pointer to pcap_loop.
284- struct Handler < F > {
317+ struct HandlerFn < F > {
285318 func : F ,
286319 panic_payload : Option < Box < dyn Any + Send > > ,
287- handle : NonNull < raw :: pcap_t > ,
320+ handle : Arc < PcapHandle > ,
288321}
289322
290- impl < F > Handler < F >
323+ impl < F > HandlerFn < F >
291324where
292325 F : FnMut ( Packet ) ,
293326{
@@ -322,6 +355,37 @@ impl<T: Activated> From<Capture<T>> for Capture<dyn Activated> {
322355 }
323356}
324357
358+ /// BreakLoop can safely be sent to other threads such as signal handlers to abort
359+ /// blocking capture loops such as `Capture::next_packet` and `Capture::for_each`.
360+ ///
361+ /// See <https://www.tcpdump.org/manpages/pcap_breakloop.3pcap.html> for per-platform caveats about
362+ /// how breakloop can wake up blocked threads.
363+ pub struct BreakLoop {
364+ handle : Weak < PcapHandle > ,
365+ }
366+
367+ unsafe impl Send for BreakLoop { }
368+ unsafe impl Sync for BreakLoop { }
369+
370+ impl BreakLoop {
371+ /// Calls `pcap_breakloop` to make the blocking loop of a pcap capture return.
372+ /// The call is a no-op if the handle is invalid.
373+ ///
374+ /// # Safety
375+ ///
376+ /// Can be called from any thread, but **must not** be used inside a
377+ /// signal handler unless the owning `Capture` is guaranteed to still
378+ /// be alive.
379+ ///
380+ /// The signal handler should defer the execution of `BreakLoop::breakloop()`
381+ /// to a thread instead for safety.
382+ pub fn breakloop ( & self ) {
383+ if let Some ( handle) = self . handle . upgrade ( ) {
384+ unsafe { raw:: pcap_breakloop ( handle. as_ptr ( ) ) } ;
385+ }
386+ }
387+ }
388+
325389/// Abstraction for writing pcap savefiles, which can be read afterwards via `Capture::from_file()`.
326390pub struct Savefile {
327391 handle : NonNull < raw:: pcap_dumper_t > ,
@@ -613,6 +677,31 @@ mod tests {
613677 }
614678 }
615679
680+ #[ test]
681+ fn test_breakloop_capture_dropped ( ) {
682+ let _m = RAWMTX . lock ( ) ;
683+
684+ let mut value: isize = 1234 ;
685+ let pcap = as_pcap_t ( & mut value) ;
686+
687+ let test_capture = test_capture :: < Active > ( pcap) ;
688+ let mut capture: Capture < dyn Activated > = test_capture. capture . into ( ) ;
689+
690+ let ctx = raw:: pcap_breakloop_context ( ) ;
691+ ctx. expect ( )
692+ . withf_st ( move |h| * h == pcap)
693+ . return_const ( ( ) )
694+ . times ( 1 ) ;
695+
696+ let break_handle = capture. breakloop_handle ( ) ;
697+
698+ break_handle. breakloop ( ) ;
699+
700+ drop ( capture) ;
701+
702+ break_handle. breakloop ( ) ; // this call does not trigger mock after drop
703+ }
704+
616705 #[ test]
617706 fn test_savefile ( ) {
618707 let _m = RAWMTX . lock ( ) ;
0 commit comments