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