pcap/capture/activated/
mod.rs

1pub mod active;
2pub mod dead;
3pub mod iterator;
4pub mod offline;
5
6use std::{
7    any::Any,
8    convert::TryInto,
9    ffi::CString,
10    fmt, mem,
11    panic::{catch_unwind, resume_unwind, AssertUnwindSafe},
12    path::Path,
13    ptr::{self, NonNull},
14    slice,
15    sync::{Arc, Weak},
16};
17
18#[cfg(not(windows))]
19use std::os::unix::io::RawFd;
20
21use crate::{
22    capture::{Activated, Capture, PcapHandle},
23    codec::PacketCodec,
24    linktype::Linktype,
25    packet::{Packet, PacketHeader},
26    raw, Error,
27};
28
29use iterator::PacketIter;
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32/// Packet statistics for a capture
33pub struct Stat {
34    /// Number of packets received
35    pub received: u32,
36    /// Number of packets dropped because there was no room in the operating system's buffer when
37    /// they arrived, because packets weren't being read fast enough
38    pub dropped: u32,
39    /// Number of packets dropped by the network interface or its driver
40    pub if_dropped: u32,
41}
42
43impl Stat {
44    fn new(received: u32, dropped: u32, if_dropped: u32) -> Stat {
45        Stat {
46            received,
47            dropped,
48            if_dropped,
49        }
50    }
51}
52
53#[repr(u32)]
54#[derive(Debug, PartialEq, Eq, Clone, Copy)]
55/// The direction of packets to be captured. Use with `Capture::direction`.
56pub enum Direction {
57    /// Capture packets received by or sent by the device. This is the default.
58    InOut = raw::PCAP_D_INOUT,
59    /// Only capture packets received by the device.
60    In = raw::PCAP_D_IN,
61    /// Only capture packets sent by the device.
62    Out = raw::PCAP_D_OUT,
63}
64
65///# Activated captures include `Capture<Active>` and `Capture<Offline>`.
66impl<T: Activated + ?Sized> Capture<T> {
67    /// List the datalink types that this captured device supports.
68    pub fn list_datalinks(&self) -> Result<Vec<Linktype>, Error> {
69        unsafe {
70            let mut links: *mut i32 = ptr::null_mut();
71            let num = raw::pcap_list_datalinks(self.handle.as_ptr(), &mut links);
72            let mut vec = vec![];
73            if num > 0 {
74                vec.extend(
75                    slice::from_raw_parts(links, num as _)
76                        .iter()
77                        .cloned()
78                        .map(Linktype),
79                )
80            }
81            raw::pcap_free_datalinks(links);
82            self.check_err(num > 0).and(Ok(vec))
83        }
84    }
85
86    /// Set the datalink type for the current capture handle.
87    pub fn set_datalink(&mut self, linktype: Linktype) -> Result<(), Error> {
88        self.check_err(unsafe { raw::pcap_set_datalink(self.handle.as_ptr(), linktype.0) == 0 })
89    }
90
91    /// Get the current datalink type for this capture handle.
92    pub fn get_datalink(&self) -> Linktype {
93        unsafe { Linktype(raw::pcap_datalink(self.handle.as_ptr())) }
94    }
95
96    /// Create a `Savefile` context for recording captured packets using this `Capture`'s
97    /// configurations.
98    pub fn savefile<P: AsRef<Path>>(&self, path: P) -> Result<Savefile, Error> {
99        let name = CString::new(path.as_ref().to_str().unwrap())?;
100        let handle_opt = NonNull::<raw::pcap_dumper_t>::new(unsafe {
101            raw::pcap_dump_open(self.handle.as_ptr(), name.as_ptr())
102        });
103        let handle = self
104            .check_err(handle_opt.is_some())
105            .map(|_| handle_opt.unwrap())?;
106        Ok(Savefile::from(handle))
107    }
108
109    /// Create a `Savefile` context for recording captured packets using this `Capture`'s
110    /// configurations. The output is written to a raw file descriptor which is opened in `"w"`
111    /// mode.
112    ///
113    /// # Safety
114    ///
115    /// Unsafe, because the returned Savefile assumes it is the sole owner of the file descriptor.
116    #[cfg(not(windows))]
117    pub unsafe fn savefile_raw_fd(&self, fd: RawFd) -> Result<Savefile, Error> {
118        open_raw_fd(fd, b'w').and_then(|file| {
119            let handle_opt = NonNull::<raw::pcap_dumper_t>::new(raw::pcap_dump_fopen(
120                self.handle.as_ptr(),
121                file,
122            ));
123            let handle = self
124                .check_err(handle_opt.is_some())
125                .map(|_| handle_opt.unwrap())?;
126            Ok(Savefile::from(handle))
127        })
128    }
129
130    /// Reopen a `Savefile` context for recording captured packets using this `Capture`'s
131    /// configurations. This is similar to `savefile()` but does not create the file if it
132    /// does  not exist and, if it does already exist, and is a pcap file with the same
133    /// byte order as the host opening the file, and has the same time stamp precision,
134    /// link-layer header type,  and  snapshot length as p, it will write new packets
135    /// at the end of the file.
136    #[cfg(libpcap_1_7_2)]
137    pub fn savefile_append<P: AsRef<Path>>(&self, path: P) -> Result<Savefile, Error> {
138        let name = CString::new(path.as_ref().to_str().unwrap())?;
139        let handle_opt = NonNull::<raw::pcap_dumper_t>::new(unsafe {
140            raw::pcap_dump_open_append(self.handle.as_ptr(), name.as_ptr())
141        });
142        let handle = self
143            .check_err(handle_opt.is_some())
144            .map(|_| handle_opt.unwrap())?;
145        Ok(Savefile::from(handle))
146    }
147
148    /// Set the direction of the capture
149    pub fn direction(&self, direction: Direction) -> Result<(), Error> {
150        self.check_err(unsafe {
151            raw::pcap_setdirection(self.handle.as_ptr(), direction as u32 as _) == 0
152        })
153    }
154
155    /// Blocks until a packet is returned from the capture handle or an error occurs.
156    ///
157    /// pcap captures packets and places them into a buffer which this function reads
158    /// from.
159    ///
160    /// # Warning
161    ///
162    /// This buffer has a finite length, so if the buffer fills completely new
163    /// packets will be discarded temporarily. This means that in realtime situations,
164    /// you probably want to minimize the time between calls to next_packet() method.
165    pub fn next_packet(&mut self) -> Result<Packet<'_>, Error> {
166        unsafe {
167            let mut header: *mut raw::pcap_pkthdr = ptr::null_mut();
168            let mut packet: *const libc::c_uchar = ptr::null();
169            let retcode = raw::pcap_next_ex(self.handle.as_ptr(), &mut header, &mut packet);
170            match retcode {
171                i if i >= 1 => {
172                    // packet was read without issue
173                    Ok(Packet::new(
174                        &*(&*header as *const raw::pcap_pkthdr as *const PacketHeader),
175                        slice::from_raw_parts(packet, (*header).caplen as _),
176                    ))
177                }
178                0 => {
179                    // packets are being read from a live capture and the
180                    // timeout expired
181                    Err(Error::TimeoutExpired)
182                }
183                -1 => {
184                    // an error occured while reading the packet
185                    Err(self.get_err())
186                }
187                -2 => {
188                    // packets are being read from a "savefile" and there are no
189                    // more packets to read
190                    Err(Error::NoMorePackets)
191                }
192                // GRCOV_EXCL_START
193                _ => {
194                    // libpcap only defines codes >=1, 0, -1, and -2
195                    unreachable!()
196                } // GRCOV_EXCL_STOP
197            }
198        }
199    }
200
201    /// Return an iterator that call [`Self::next_packet()`] forever. Require a [`PacketCodec`]
202    pub fn iter<C: PacketCodec>(self, codec: C) -> PacketIter<T, C> {
203        PacketIter::new(self, codec)
204    }
205
206    pub fn for_each<F>(&mut self, count: Option<usize>, handler: F) -> Result<(), Error>
207    where
208        F: FnMut(Packet),
209    {
210        let cnt = match count {
211            // Actually passing 0 down to pcap_loop would mean read forever.
212            // We interpret it as "read nothing", so we just succeed immediately.
213            Some(0) => return Ok(()),
214            Some(cnt) => cnt
215                .try_into()
216                .expect("count of packets to read cannot exceed c_int::MAX"),
217            None => -1,
218        };
219
220        let mut handler = HandlerFn {
221            func: AssertUnwindSafe(handler),
222            panic_payload: None,
223            handle: self.handle.clone(),
224        };
225        let return_code = unsafe {
226            raw::pcap_loop(
227                self.handle.as_ptr(),
228                cnt,
229                HandlerFn::<F>::callback,
230                &mut handler as *mut HandlerFn<AssertUnwindSafe<F>> as *mut u8,
231            )
232        };
233        if let Some(e) = handler.panic_payload {
234            resume_unwind(e);
235        }
236        self.check_err(return_code == 0)
237    }
238
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
271    /// Compiles the string into a filter program using `pcap_compile`.
272    pub fn compile(&self, program: &str, optimize: bool) -> Result<BpfProgram, Error> {
273        let program = CString::new(program)?;
274
275        unsafe {
276            let mut bpf_program: raw::bpf_program = mem::zeroed();
277            let ret = raw::pcap_compile(
278                self.handle.as_ptr(),
279                &mut bpf_program,
280                program.as_ptr(),
281                optimize as libc::c_int,
282                0,
283            );
284            self.check_err(ret != -1).and(Ok(BpfProgram(bpf_program)))
285        }
286    }
287
288    /// Sets the filter on the capture using the given BPF program string. Internally this is
289    /// compiled using `pcap_compile()`. `optimize` controls whether optimization on the resulting
290    /// code is performed
291    ///
292    /// See <http://biot.com/capstats/bpf.html> for more information about this syntax.
293    pub fn filter(&mut self, program: &str, optimize: bool) -> Result<(), Error> {
294        let mut bpf_program = self.compile(program, optimize)?;
295        let ret = unsafe { raw::pcap_setfilter(self.handle.as_ptr(), &mut bpf_program.0) };
296        self.check_err(ret != -1)
297    }
298
299    /// Get capture statistics about this capture. The values represent packet statistics from the
300    /// start of the run to the time of the call.
301    ///
302    /// See <https://www.tcpdump.org/manpages/pcap_stats.3pcap.html> for per-platform caveats about
303    /// how packet statistics are calculated.
304    pub fn stats(&mut self) -> Result<Stat, Error> {
305        unsafe {
306            let mut stats: raw::pcap_stat = mem::zeroed();
307            self.check_err(raw::pcap_stats(self.handle.as_ptr(), &mut stats) != -1)
308                .map(|_| Stat::new(stats.ps_recv, stats.ps_drop, stats.ps_ifdrop))
309        }
310    }
311}
312
313// Handler and its associated function let us create an extern "C" fn which dispatches to a normal
314// Rust FnMut, which may be a closure with a captured environment. The *only* purpose of this
315// generic parameter is to ensure that in Capture::pcap_loop that we pass the right function
316// pointer and the right data pointer to pcap_loop.
317struct HandlerFn<F> {
318    func: F,
319    panic_payload: Option<Box<dyn Any + Send>>,
320    handle: Arc<PcapHandle>,
321}
322
323impl<F> HandlerFn<F>
324where
325    F: FnMut(Packet),
326{
327    extern "C" fn callback(
328        slf: *mut libc::c_uchar,
329        header: *const raw::pcap_pkthdr,
330        packet: *const libc::c_uchar,
331    ) {
332        unsafe {
333            let packet = Packet::new(
334                &*(header as *const PacketHeader),
335                slice::from_raw_parts(packet, (*header).caplen as _),
336            );
337
338            let slf = slf as *mut Self;
339            let func = &mut (*slf).func;
340            let mut func = AssertUnwindSafe(func);
341            // If our handler function panics, we need to prevent it from unwinding across the
342            // FFI boundary. If the handler panics we catch the unwind here, break out of
343            // pcap_loop, and resume the unwind outside.
344            if let Err(e) = catch_unwind(move || func(packet)) {
345                (*slf).panic_payload = Some(e);
346                raw::pcap_breakloop((*slf).handle.as_ptr());
347            }
348        }
349    }
350}
351
352impl<T: Activated> From<Capture<T>> for Capture<dyn Activated> {
353    fn from(cap: Capture<T>) -> Capture<dyn Activated> {
354        unsafe { mem::transmute(cap) }
355    }
356}
357
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.
363pub struct BreakLoop {
364    handle: Weak<PcapHandle>,
365}
366
367unsafe impl Send for BreakLoop {}
368unsafe impl Sync for BreakLoop {}
369
370impl 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
389/// Abstraction for writing pcap savefiles, which can be read afterwards via `Capture::from_file()`.
390pub struct Savefile {
391    handle: NonNull<raw::pcap_dumper_t>,
392}
393
394// Just like a Capture, a Savefile is safe to Send as it encapsulates the entire lifetime of
395// `raw::pcap_dumper_t *`, but it is not safe to Sync as libpcap does not promise thread-safe access
396// to the same `raw::pcap_dumper_t *` from multiple threads.
397unsafe impl Send for Savefile {}
398
399impl Savefile {
400    /// Write a packet to a capture file
401    pub fn write(&mut self, packet: &Packet<'_>) {
402        unsafe {
403            raw::pcap_dump(
404                self.handle.as_ptr() as _,
405                &*(packet.header as *const PacketHeader as *const raw::pcap_pkthdr),
406                packet.data.as_ptr(),
407            );
408        }
409    }
410
411    /// Flushes all the packets that haven't been written to the savefile
412    pub fn flush(&mut self) -> Result<(), Error> {
413        if unsafe { raw::pcap_dump_flush(self.handle.as_ptr() as _) } != 0 {
414            return Err(Error::ErrnoError(errno::errno()));
415        }
416
417        Ok(())
418    }
419}
420
421impl From<NonNull<raw::pcap_dumper_t>> for Savefile {
422    fn from(handle: NonNull<raw::pcap_dumper_t>) -> Self {
423        Savefile { handle }
424    }
425}
426
427impl Drop for Savefile {
428    fn drop(&mut self) {
429        unsafe { raw::pcap_dump_close(self.handle.as_ptr()) }
430    }
431}
432
433#[repr(transparent)]
434pub struct BpfInstruction(raw::bpf_insn);
435#[repr(transparent)]
436pub struct BpfProgram(raw::bpf_program);
437
438impl BpfProgram {
439    /// checks whether a filter matches a packet
440    pub fn filter(&self, buf: &[u8]) -> bool {
441        let header: raw::pcap_pkthdr = raw::pcap_pkthdr {
442            ts: libc::timeval {
443                tv_sec: 0,
444                tv_usec: 0,
445            },
446            caplen: buf.len() as u32,
447            len: buf.len() as u32,
448        };
449        unsafe { raw::pcap_offline_filter(&self.0, &header, buf.as_ptr()) > 0 }
450    }
451
452    pub fn get_instructions(&self) -> &[BpfInstruction] {
453        unsafe {
454            slice::from_raw_parts(
455                self.0.bf_insns as *const BpfInstruction,
456                self.0.bf_len as usize,
457            )
458        }
459    }
460}
461
462impl Drop for BpfProgram {
463    fn drop(&mut self) {
464        unsafe { raw::pcap_freecode(&mut self.0) }
465    }
466}
467
468impl fmt::Display for BpfInstruction {
469    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470        write!(
471            f,
472            "{} {} {} {}",
473            self.0.code, self.0.jt, self.0.jf, self.0.k
474        )
475    }
476}
477
478unsafe impl Send for BpfProgram {}
479
480#[cfg(not(windows))]
481/// Open a raw file descriptor.
482///
483/// # Safety
484///
485/// Unsafe, because the returned FILE assumes it is the sole owner of the file descriptor.
486pub unsafe fn open_raw_fd(fd: RawFd, mode: u8) -> Result<*mut libc::FILE, Error> {
487    let mode = [mode, 0];
488    libc::fdopen(fd, mode.as_ptr() as _)
489        .as_mut()
490        .map(|f| f as _)
491        .ok_or(Error::InvalidRawFd)
492}
493
494// GRCOV_EXCL_START
495#[cfg(test)]
496mod testmod {
497    use super::*;
498
499    pub static TS: libc::timeval = libc::timeval {
500        tv_sec: 5,
501        tv_usec: 50,
502    };
503    pub static LEN: u32 = DATA.len() as u32;
504    pub static CAPLEN: u32 = LEN;
505
506    pub static mut PKTHDR: raw::pcap_pkthdr = raw::pcap_pkthdr {
507        ts: TS,
508        caplen: CAPLEN,
509        len: LEN,
510    };
511    pub static PACKET_HEADER: PacketHeader = PacketHeader {
512        ts: TS,
513        caplen: CAPLEN,
514        len: LEN,
515    };
516
517    pub static DATA: [u8; 4] = [4, 5, 6, 7];
518    pub static PACKET: Packet = Packet {
519        header: &PACKET_HEADER,
520        data: &DATA,
521    };
522
523    pub struct NextExContext(raw::__pcap_next_ex::Context);
524    pub fn next_ex_expect(pcap: *mut raw::pcap_t) -> NextExContext {
525        let data_ptr: *const libc::c_uchar = DATA.as_ptr();
526        #[allow(unused_unsafe)] // unsafe still needed to compile on MSRV
527        let pkthdr_ptr: *mut raw::pcap_pkthdr = unsafe { std::ptr::addr_of_mut!(PKTHDR) };
528
529        let ctx = raw::pcap_next_ex_context();
530        ctx.checkpoint();
531        ctx.expect()
532            .withf_st(move |arg1, _, _| *arg1 == pcap)
533            .return_once_st(move |_, arg2, arg3| {
534                unsafe {
535                    *arg2 = pkthdr_ptr;
536                    *arg3 = data_ptr;
537                }
538                CAPLEN as i32
539            });
540
541        NextExContext(ctx)
542    }
543}
544// GRCOV_EXCL_STOP
545
546#[cfg(test)]
547mod tests {
548    use crate::{
549        capture::{
550            activated::testmod::{next_ex_expect, PACKET},
551            testmod::test_capture,
552            Active, Capture, Offline,
553        },
554        raw::testmod::{as_pcap_dumper_t, as_pcap_t, geterr_expect, RAWMTX},
555    };
556
557    use super::*;
558
559    #[test]
560    fn test_list_datalinks() {
561        let _m = RAWMTX.lock();
562
563        let mut value: isize = 777;
564        let pcap = as_pcap_t(&mut value);
565
566        let test_capture = test_capture::<Active>(pcap);
567        let capture: Capture<dyn Activated> = test_capture.capture.into();
568
569        let ctx = raw::pcap_list_datalinks_context();
570        ctx.expect()
571            .withf_st(move |arg1, _| *arg1 == pcap)
572            .return_once_st(|_, _| 0);
573
574        let ctx = raw::pcap_free_datalinks_context();
575        ctx.expect().return_once(|_| {});
576
577        let _err = geterr_expect(pcap);
578
579        let result = capture.list_datalinks();
580        assert!(result.is_err());
581
582        let mut datalinks: [i32; 4] = [0, 1, 2, 3];
583        let links: *mut i32 = datalinks.as_mut_ptr();
584        let len = datalinks.len();
585
586        let ctx = raw::pcap_list_datalinks_context();
587        ctx.checkpoint();
588        ctx.expect()
589            .withf_st(move |arg1, _| *arg1 == pcap)
590            .return_once_st(move |_, arg2| {
591                unsafe { *arg2 = links };
592                len as i32
593            });
594
595        let ctx = raw::pcap_free_datalinks_context();
596        ctx.checkpoint();
597        ctx.expect().return_once(|_| {});
598
599        let pcap_datalinks = capture.list_datalinks().unwrap();
600        assert_eq!(
601            pcap_datalinks,
602            datalinks.iter().cloned().map(Linktype).collect::<Vec<_>>()
603        );
604    }
605
606    #[test]
607    fn test_set_datalink() {
608        let _m = RAWMTX.lock();
609
610        let mut value: isize = 777;
611        let pcap = as_pcap_t(&mut value);
612
613        let test_capture = test_capture::<Active>(pcap);
614        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
615
616        let ctx = raw::pcap_set_datalink_context();
617        ctx.expect()
618            .withf_st(move |arg1, _| *arg1 == pcap)
619            .return_once(|_, _| 0);
620
621        let result = capture.set_datalink(Linktype::ETHERNET);
622        assert!(result.is_ok());
623
624        let ctx = raw::pcap_set_datalink_context();
625        ctx.checkpoint();
626        ctx.expect()
627            .withf_st(move |arg1, _| *arg1 == pcap)
628            .return_once(|_, _| -1);
629
630        let _err = geterr_expect(pcap);
631
632        let result = capture.set_datalink(Linktype::ETHERNET);
633        assert!(result.is_err());
634    }
635
636    #[test]
637    fn test_get_datalink() {
638        let _m = RAWMTX.lock();
639
640        let mut value: isize = 777;
641        let pcap = as_pcap_t(&mut value);
642
643        let test_capture = test_capture::<Active>(pcap);
644        let capture: Capture<dyn Activated> = test_capture.capture.into();
645
646        let ctx = raw::pcap_datalink_context();
647        ctx.expect()
648            .withf_st(move |arg1| *arg1 == pcap)
649            .return_once(|_| 1);
650
651        let linktype = capture.get_datalink();
652        assert_eq!(linktype, Linktype::ETHERNET);
653    }
654
655    #[test]
656    fn unify_activated() {
657        #![allow(dead_code)]
658        fn test1() -> Capture<Active> {
659            panic!();
660        }
661
662        fn test2() -> Capture<Offline> {
663            panic!();
664        }
665
666        fn maybe(a: bool) -> Capture<dyn Activated> {
667            if a {
668                test1().into()
669            } else {
670                test2().into()
671            }
672        }
673
674        fn also_maybe(a: &mut Capture<dyn Activated>) {
675            a.filter("whatever filter string, this won't be run anyway", false)
676                .unwrap();
677        }
678    }
679
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
705    #[test]
706    fn test_savefile() {
707        let _m = RAWMTX.lock();
708
709        let mut value: isize = 777;
710        let pcap = as_pcap_t(&mut value);
711
712        let mut value: isize = 888;
713        let pcap_dumper = as_pcap_dumper_t(&mut value);
714
715        let test_capture = test_capture::<Offline>(pcap);
716        let capture = test_capture.capture;
717
718        let ctx = raw::pcap_dump_open_context();
719        ctx.expect()
720            .withf_st(move |arg1, _| *arg1 == pcap)
721            .return_once_st(move |_, _| pcap_dumper);
722
723        let ctx = raw::pcap_dump_close_context();
724        ctx.expect()
725            .withf_st(move |arg1| *arg1 == pcap_dumper)
726            .return_once(|_| {});
727
728        let result = capture.savefile("path/to/nowhere");
729        assert!(result.is_ok());
730    }
731
732    #[test]
733    #[cfg(libpcap_1_7_2)]
734    fn test_savefile_append() {
735        let _m = RAWMTX.lock();
736
737        let mut value: isize = 777;
738        let pcap = as_pcap_t(&mut value);
739
740        let mut value: isize = 888;
741        let pcap_dumper = as_pcap_dumper_t(&mut value);
742
743        let test_capture = test_capture::<Offline>(pcap);
744        let capture = test_capture.capture;
745
746        let ctx = raw::pcap_dump_open_append_context();
747        ctx.expect()
748            .withf_st(move |arg1, _| *arg1 == pcap)
749            .return_once_st(move |_, _| pcap_dumper);
750
751        let ctx = raw::pcap_dump_close_context();
752        ctx.expect()
753            .withf_st(move |arg1| *arg1 == pcap_dumper)
754            .return_once(|_| {});
755
756        let result = capture.savefile_append("path/to/nowhere");
757        assert!(result.is_ok());
758    }
759
760    #[test]
761    fn test_savefile_error() {
762        let _m = RAWMTX.lock();
763
764        let mut value: isize = 777;
765        let pcap = as_pcap_t(&mut value);
766
767        let test_capture = test_capture::<Offline>(pcap);
768        let capture = test_capture.capture;
769
770        let ctx = raw::pcap_dump_open_context();
771        ctx.expect()
772            .withf_st(move |arg1, _| *arg1 == pcap)
773            .return_once(|_, _| std::ptr::null_mut());
774
775        let _err = geterr_expect(pcap);
776
777        let result = capture.savefile("path/to/nowhere");
778        assert!(result.is_err());
779    }
780
781    #[test]
782    #[cfg(libpcap_1_7_2)]
783    fn test_savefile_append_error() {
784        let _m = RAWMTX.lock();
785
786        let mut value: isize = 777;
787        let pcap = as_pcap_t(&mut value);
788
789        let test_capture = test_capture::<Offline>(pcap);
790        let capture = test_capture.capture;
791
792        let ctx = raw::pcap_dump_open_append_context();
793        ctx.expect()
794            .withf_st(move |arg1, _| *arg1 == pcap)
795            .return_once(|_, _| std::ptr::null_mut());
796
797        let _err = geterr_expect(pcap);
798
799        let result = capture.savefile_append("path/to/nowhere");
800        assert!(result.is_err());
801    }
802
803    #[test]
804    fn test_savefile_ops() {
805        let _m = RAWMTX.lock();
806
807        let mut value: isize = 888;
808        let pcap_dumper = as_pcap_dumper_t(&mut value);
809
810        let ctx = raw::pcap_dump_close_context();
811        ctx.expect()
812            .withf_st(move |arg1| *arg1 == pcap_dumper)
813            .return_once(|_| {});
814
815        let mut savefile = Savefile {
816            handle: NonNull::new(pcap_dumper).unwrap(),
817        };
818
819        let ctx = raw::pcap_dump_context();
820        ctx.expect()
821            .withf_st(move |arg1, _, _| *arg1 == pcap_dumper as _)
822            .return_once(|_, _, _| {});
823
824        savefile.write(&PACKET);
825
826        let ctx = raw::pcap_dump_flush_context();
827        ctx.expect()
828            .withf_st(move |arg1| *arg1 == pcap_dumper)
829            .return_once(|_| 0);
830
831        let result = savefile.flush();
832        assert!(result.is_ok());
833
834        let ctx = raw::pcap_dump_flush_context();
835        ctx.checkpoint();
836        ctx.expect()
837            .withf_st(move |arg1| *arg1 == pcap_dumper)
838            .return_once(|_| -1);
839
840        let result = savefile.flush();
841        assert!(result.is_err());
842    }
843
844    #[test]
845    fn test_direction() {
846        let _m = RAWMTX.lock();
847
848        let mut value: isize = 777;
849        let pcap = as_pcap_t(&mut value);
850
851        let test_capture = test_capture::<Active>(pcap);
852        let capture = test_capture.capture;
853
854        let ctx = raw::pcap_setdirection_context();
855        ctx.expect()
856            .withf_st(move |arg1, arg2| (*arg1 == pcap) && (*arg2 == raw::PCAP_D_OUT))
857            .return_once(|_, _| 0);
858
859        let result = capture.direction(Direction::Out);
860        assert!(result.is_ok());
861
862        let ctx = raw::pcap_setdirection_context();
863        ctx.checkpoint();
864        ctx.expect()
865            .withf_st(move |arg1, arg2| (*arg1 == pcap) && (*arg2 == raw::PCAP_D_OUT))
866            .return_once(|_, _| -1);
867
868        let _err = geterr_expect(pcap);
869
870        let result = capture.direction(Direction::Out);
871        assert!(result.is_err());
872
873        // For code coverage of the derive line.
874        assert_ne!(Direction::In, Direction::InOut);
875        assert_ne!(Direction::In, Direction::Out);
876        assert_ne!(Direction::InOut, Direction::Out);
877    }
878
879    #[test]
880    fn test_next_packet() {
881        let _m = RAWMTX.lock();
882
883        let mut value: isize = 777;
884        let pcap = as_pcap_t(&mut value);
885
886        let test_capture = test_capture::<Active>(pcap);
887        let mut capture = test_capture.capture;
888
889        let _nxt = next_ex_expect(pcap);
890
891        let next_packet = capture.next_packet().unwrap();
892        assert_eq!(next_packet, PACKET);
893    }
894
895    #[test]
896    fn test_next_packet_timeout() {
897        let _m = RAWMTX.lock();
898
899        let mut value: isize = 777;
900        let pcap = as_pcap_t(&mut value);
901
902        let test_capture = test_capture::<Active>(pcap);
903        let mut capture = test_capture.capture;
904
905        let ctx = raw::pcap_next_ex_context();
906        ctx.expect()
907            .withf_st(move |arg1, _, _| *arg1 == pcap)
908            .return_once_st(move |_, _, _| 0);
909
910        let err = capture.next_packet().unwrap_err();
911        assert_eq!(err, Error::TimeoutExpired);
912    }
913
914    #[test]
915    fn test_next_packet_read_error() {
916        let _m = RAWMTX.lock();
917
918        let mut value: isize = 777;
919        let pcap = as_pcap_t(&mut value);
920
921        let test_capture = test_capture::<Active>(pcap);
922        let mut capture = test_capture.capture;
923
924        let ctx = raw::pcap_next_ex_context();
925        ctx.expect()
926            .withf_st(move |arg1, _, _| *arg1 == pcap)
927            .return_once_st(move |_, _, _| -1);
928
929        let _err = geterr_expect(pcap);
930
931        let result = capture.next_packet();
932        assert!(result.is_err());
933    }
934
935    #[test]
936    fn test_next_packet_no_more_packets() {
937        let _m = RAWMTX.lock();
938
939        let mut value: isize = 777;
940        let pcap = as_pcap_t(&mut value);
941
942        let test_capture = test_capture::<Offline>(pcap);
943        let mut capture = test_capture.capture;
944
945        let ctx = raw::pcap_next_ex_context();
946        ctx.expect()
947            .withf_st(move |arg1, _, _| *arg1 == pcap)
948            .return_once_st(move |_, _, _| -2);
949
950        let err = capture.next_packet().unwrap_err();
951        assert_eq!(err, Error::NoMorePackets);
952    }
953
954    #[test]
955    fn test_compile() {
956        let _m = RAWMTX.lock();
957
958        let mut value: isize = 777;
959        let pcap = as_pcap_t(&mut value);
960
961        let test_capture = test_capture::<Active>(pcap);
962        let capture = test_capture.capture;
963
964        let ctx = raw::pcap_compile_context();
965        ctx.expect()
966            .withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
967            .return_once(|_, _, _, _, _| -1);
968
969        let _err = geterr_expect(pcap);
970
971        let ctx = raw::pcap_freecode_context();
972        ctx.expect().return_once(|_| {});
973
974        let result = capture.compile("some bpf program", false);
975        assert!(result.is_err());
976
977        let ctx = raw::pcap_compile_context();
978        ctx.checkpoint();
979        ctx.expect()
980            .withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
981            .return_once(|_, _, _, _, _| 0);
982
983        let ctx = raw::pcap_freecode_context();
984        ctx.checkpoint();
985        ctx.expect().return_once(|_| {});
986
987        let result = capture.compile("some bpf program", false);
988        assert!(result.is_ok());
989    }
990
991    #[test]
992    fn test_filter() {
993        let _m = RAWMTX.lock();
994
995        let mut value: isize = 777;
996        let pcap = as_pcap_t(&mut value);
997
998        let test_capture = test_capture::<Active>(pcap);
999        let mut capture = test_capture.capture;
1000
1001        let ctx = raw::pcap_compile_context();
1002        ctx.expect()
1003            .withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
1004            .return_once(|_, _, _, _, _| 0);
1005
1006        let ctx = raw::pcap_setfilter_context();
1007        ctx.expect()
1008            .withf_st(move |arg1, _| *arg1 == pcap)
1009            .return_once(|_, _| -1);
1010
1011        let _err = geterr_expect(pcap);
1012
1013        let ctx = raw::pcap_freecode_context();
1014        ctx.expect().return_once(|_| {});
1015
1016        let result = capture.filter("some bpf program", false);
1017        assert!(result.is_err());
1018
1019        let ctx = raw::pcap_compile_context();
1020        ctx.checkpoint();
1021        ctx.expect()
1022            .withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
1023            .return_once(|_, _, _, _, _| 0);
1024
1025        let ctx = raw::pcap_setfilter_context();
1026        ctx.checkpoint();
1027        ctx.expect()
1028            .withf_st(move |arg1, _| *arg1 == pcap)
1029            .return_once(|_, _| 0);
1030
1031        let ctx = raw::pcap_freecode_context();
1032        ctx.checkpoint();
1033        ctx.expect().return_once(|_| {});
1034
1035        let result = capture.compile("some bpf program", false);
1036        assert!(result.is_ok());
1037    }
1038
1039    #[test]
1040    fn test_stats() {
1041        let _m = RAWMTX.lock();
1042
1043        let mut value: isize = 777;
1044        let pcap = as_pcap_t(&mut value);
1045
1046        let test_capture = test_capture::<Active>(pcap);
1047        let mut capture = test_capture.capture;
1048
1049        let stat = raw::pcap_stat {
1050            ps_recv: 1,
1051            ps_drop: 2,
1052            ps_ifdrop: 3,
1053        };
1054
1055        let ctx = raw::pcap_stats_context();
1056        ctx.expect()
1057            .withf_st(move |arg1, _| *arg1 == pcap)
1058            .return_once_st(move |_, arg2| {
1059                unsafe { *arg2 = stat };
1060                0
1061            });
1062
1063        let stats = capture.stats().unwrap();
1064        assert_eq!(stats, Stat::new(stat.ps_recv, stat.ps_drop, stat.ps_ifdrop));
1065
1066        let ctx = raw::pcap_stats_context();
1067        ctx.checkpoint();
1068        ctx.expect()
1069            .withf_st(move |arg1, _| *arg1 == pcap)
1070            .return_once_st(move |_, _| -1);
1071
1072        let _err = geterr_expect(pcap);
1073
1074        let result = capture.stats();
1075        assert!(result.is_err());
1076    }
1077
1078    #[test]
1079    fn test_bpf_instruction_display() {
1080        let instr = BpfInstruction(raw::bpf_insn {
1081            code: 1,
1082            jt: 2,
1083            jf: 3,
1084            k: 4,
1085        });
1086        assert_eq!(format!("{instr}"), "1 2 3 4");
1087    }
1088
1089    #[test]
1090    fn read_packet_via_pcap_loop() {
1091        let _m = RAWMTX.lock();
1092
1093        let mut value: isize = 777;
1094        let pcap = as_pcap_t(&mut value);
1095
1096        let test_capture = test_capture::<Active>(pcap);
1097        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
1098
1099        let ctx = raw::pcap_loop_context();
1100        ctx.expect()
1101            .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == -1)
1102            .return_once_st(move |_, _, func, data| {
1103                let header = raw::pcap_pkthdr {
1104                    ts: libc::timeval {
1105                        tv_sec: 0,
1106                        tv_usec: 0,
1107                    },
1108                    caplen: 0,
1109                    len: 0,
1110                };
1111                let packet_data = &[];
1112                func(data, &header, packet_data.as_ptr());
1113                0
1114            });
1115
1116        let mut packets = 0;
1117        capture
1118            .for_each(None, |_| {
1119                packets += 1;
1120            })
1121            .unwrap();
1122        assert_eq!(packets, 1);
1123    }
1124
1125    #[test]
1126    #[should_panic = "panic in callback"]
1127    fn panic_in_pcap_loop() {
1128        let _m = RAWMTX.lock();
1129
1130        let mut value: isize = 777;
1131        let pcap = as_pcap_t(&mut value);
1132
1133        let test_capture = test_capture::<Active>(pcap);
1134        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
1135
1136        let ctx = raw::pcap_loop_context();
1137        ctx.expect()
1138            .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == -1)
1139            .return_once_st(move |_, _, func, data| {
1140                let header = raw::pcap_pkthdr {
1141                    ts: libc::timeval {
1142                        tv_sec: 0,
1143                        tv_usec: 0,
1144                    },
1145                    caplen: 0,
1146                    len: 0,
1147                };
1148                let packet_data = &[];
1149                func(data, &header, packet_data.as_ptr());
1150                0
1151            });
1152
1153        let ctx = raw::pcap_breakloop_context();
1154        ctx.expect()
1155            .withf_st(move |arg1| *arg1 == pcap)
1156            .return_once_st(move |_| {});
1157
1158        capture
1159            .for_each(None, |_| panic!("panic in callback"))
1160            .unwrap();
1161    }
1162
1163    #[test]
1164    fn for_each_with_count() {
1165        let _m = RAWMTX.lock();
1166
1167        let mut value: isize = 777;
1168        let pcap = as_pcap_t(&mut value);
1169
1170        let test_capture = test_capture::<Active>(pcap);
1171        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
1172
1173        let ctx = raw::pcap_loop_context();
1174        ctx.expect()
1175            .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == 2)
1176            .return_once_st(move |_, _, func, data| {
1177                let header = raw::pcap_pkthdr {
1178                    ts: libc::timeval {
1179                        tv_sec: 0,
1180                        tv_usec: 0,
1181                    },
1182                    caplen: 0,
1183                    len: 0,
1184                };
1185                let packet_data = &[];
1186                func(data, &header, packet_data.as_ptr());
1187                func(data, &header, packet_data.as_ptr());
1188                0
1189            });
1190
1191        let mut packets = 0;
1192        capture
1193            .for_each(Some(2), |_| {
1194                packets += 1;
1195            })
1196            .unwrap();
1197        assert_eq!(packets, 2);
1198    }
1199
1200    #[test]
1201    fn for_each_with_count_0() {
1202        let _m = RAWMTX.lock();
1203
1204        let mut value: isize = 777;
1205        let pcap = as_pcap_t(&mut value);
1206
1207        let test_capture = test_capture::<Active>(pcap);
1208        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
1209
1210        let mut packets = 0;
1211        capture
1212            .for_each(Some(0), |_| {
1213                packets += 1;
1214            })
1215            .unwrap();
1216        assert_eq!(packets, 0);
1217    }
1218}