1
pub mod active;
2
pub mod dead;
3
pub mod iterator;
4
pub mod offline;
5

            
6
use 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))]
19
use std::os::unix::io::RawFd;
20

            
21
use crate::{
22
    capture::{Activated, Capture, PcapHandle},
23
    codec::PacketCodec,
24
    linktype::Linktype,
25
    packet::{Packet, PacketHeader},
26
    raw, Error,
27
};
28

            
29
use iterator::PacketIter;
30

            
31
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32
/// Packet statistics for a capture
33
pub 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

            
43
impl Stat {
44
4
    fn new(received: u32, dropped: u32, if_dropped: u32) -> Stat {
45
4
        Stat {
46
4
            received,
47
4
            dropped,
48
4
            if_dropped,
49
4
        }
50
4
    }
51
}
52

            
53
#[repr(u32)]
54
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
55
/// The direction of packets to be captured. Use with `Capture::direction`.
56
pub 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>`.
66
impl<T: Activated + ?Sized> Capture<T> {
67
    /// List the datalink types that this captured device supports.
68
4
    pub fn list_datalinks(&self) -> Result<Vec<Linktype>, Error> {
69
        unsafe {
70
4
            let mut links: *mut i32 = ptr::null_mut();
71
4
            let num = raw::pcap_list_datalinks(self.handle.as_ptr(), &mut links);
72
4
            let mut vec = vec![];
73
4
            if num > 0 {
74
2
                vec.extend(
75
2
                    slice::from_raw_parts(links, num as _)
76
2
                        .iter()
77
2
                        .cloned()
78
2
                        .map(Linktype),
79
                )
80
2
            }
81
4
            raw::pcap_free_datalinks(links);
82
4
            self.check_err(num > 0).and(Ok(vec))
83
        }
84
4
    }
85

            
86
    /// Set the datalink type for the current capture handle.
87
4
    pub fn set_datalink(&mut self, linktype: Linktype) -> Result<(), Error> {
88
4
        self.check_err(unsafe { raw::pcap_set_datalink(self.handle.as_ptr(), linktype.0) == 0 })
89
4
    }
90

            
91
    /// Get the current datalink type for this capture handle.
92
4
    pub fn get_datalink(&self) -> Linktype {
93
4
        unsafe { Linktype(raw::pcap_datalink(self.handle.as_ptr())) }
94
4
    }
95

            
96
    /// Create a `Savefile` context for recording captured packets using this `Capture`'s
97
    /// configurations.
98
10
    pub fn savefile<P: AsRef<Path>>(&self, path: P) -> Result<Savefile, Error> {
99
10
        let name = CString::new(path.as_ref().to_str().unwrap())?;
100
10
        let handle_opt = NonNull::<raw::pcap_dumper_t>::new(unsafe {
101
10
            raw::pcap_dump_open(self.handle.as_ptr(), name.as_ptr())
102
        });
103
10
        let handle = self
104
10
            .check_err(handle_opt.is_some())
105
14
            .map(|_| handle_opt.unwrap())?;
106
8
        Ok(Savefile::from(handle))
107
10
    }
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
4
    pub unsafe fn savefile_raw_fd(&self, fd: RawFd) -> Result<Savefile, Error> {
118
5
        open_raw_fd(fd, b'w').and_then(|file| {
119
2
            let handle_opt = NonNull::<raw::pcap_dumper_t>::new(raw::pcap_dump_fopen(
120
2
                self.handle.as_ptr(),
121
2
                file,
122
            ));
123
2
            let handle = self
124
2
                .check_err(handle_opt.is_some())
125
2
                .map(|_| handle_opt.unwrap())?;
126
2
            Ok(Savefile::from(handle))
127
2
        })
128
4
    }
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
6
    pub fn savefile_append<P: AsRef<Path>>(&self, path: P) -> Result<Savefile, Error> {
138
6
        let name = CString::new(path.as_ref().to_str().unwrap())?;
139
6
        let handle_opt = NonNull::<raw::pcap_dumper_t>::new(unsafe {
140
6
            raw::pcap_dump_open_append(self.handle.as_ptr(), name.as_ptr())
141
        });
142
6
        let handle = self
143
6
            .check_err(handle_opt.is_some())
144
8
            .map(|_| handle_opt.unwrap())?;
145
4
        Ok(Savefile::from(handle))
146
6
    }
147

            
148
    /// Set the direction of the capture
149
4
    pub fn direction(&self, direction: Direction) -> Result<(), Error> {
150
4
        self.check_err(unsafe {
151
4
            raw::pcap_setdirection(self.handle.as_ptr(), direction as u32 as _) == 0
152
        })
153
4
    }
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
    /// In high traffic situations, consider [`Self::dispatch()`] instead, which
166
    /// processes a whole batch of packets per call.
167
312
    pub fn next_packet(&mut self) -> Result<Packet<'_>, Error> {
168
        unsafe {
169
312
            let mut header: *mut raw::pcap_pkthdr = ptr::null_mut();
170
312
            let mut packet: *const libc::c_uchar = ptr::null();
171
312
            let retcode = raw::pcap_next_ex(self.handle.as_ptr(), &mut header, &mut packet);
172
312
            match retcode {
173
312
                i if i >= 1 => {
174
                    // packet was read without issue
175
286
                    Ok(Packet::new(
176
286
                        &*(&*header as *const raw::pcap_pkthdr as *const PacketHeader),
177
286
                        slice::from_raw_parts(packet, (*header).caplen as _),
178
286
                    ))
179
                }
180
                0 => {
181
                    // packets are being read from a live capture and the
182
                    // timeout expired
183
6
                    Err(Error::TimeoutExpired)
184
                }
185
                -1 => {
186
                    // an error occured while reading the packet
187
6
                    Err(self.get_err())
188
                }
189
                -2 => {
190
                    // packets are being read from a "savefile" and there are no
191
                    // more packets to read
192
14
                    Err(Error::NoMorePackets)
193
                }
194
                // GRCOV_EXCL_START
195
                _ => {
196
                    // libpcap only defines codes >=1, 0, -1, and -2
197
                    unreachable!()
198
                } // GRCOV_EXCL_STOP
199
            }
200
        }
201
312
    }
202

            
203
    /// Return an iterator that call [`Self::next_packet()`] forever. Require a [`PacketCodec`]
204
8
    pub fn iter<C: PacketCodec>(self, codec: C) -> PacketIter<T, C> {
205
8
        PacketIter::new(self, codec)
206
8
    }
207

            
208
12
    pub fn for_each<F>(&mut self, count: Option<usize>, handler: F) -> Result<(), Error>
209
12
    where
210
12
        F: FnMut(Packet),
211
    {
212
12
        let cnt = match count {
213
            // Actually passing 0 down to pcap_loop would mean read forever.
214
            // We interpret it as "read nothing", so we just succeed immediately.
215
2
            Some(0) => return Ok(()),
216
2
            Some(cnt) => cnt
217
2
                .try_into()
218
2
                .expect("count of packets to read cannot exceed c_int::MAX"),
219
8
            None => -1,
220
        };
221

            
222
10
        let mut handler = HandlerFn {
223
10
            func: AssertUnwindSafe(handler),
224
10
            panic_payload: None,
225
10
            handle: self.handle.clone(),
226
10
        };
227
10
        let return_code = unsafe {
228
10
            raw::pcap_loop(
229
10
                self.handle.as_ptr(),
230
10
                cnt,
231
10
                HandlerFn::<F>::callback,
232
10
                &mut handler as *mut HandlerFn<AssertUnwindSafe<F>> as *mut u8,
233
            )
234
        };
235
10
        if let Some(e) = handler.panic_payload {
236
4
            resume_unwind(e);
237
6
        }
238
6
        self.check_err(return_code == 0)
239
8
    }
240

            
241
    /// Process a batch of packets from the capture using `pcap_dispatch`.
242
    ///
243
    /// Unlike [`Self::next_packet()`], which performs a system call for every packet, this
244
    /// processes a whole buffer of packets in a single call, making packet loss less likely
245
    /// in high traffic situations. Packets are still dropped when the buffer fills up between
246
    /// calls, so a busy interface also wants a larger [`Capture::buffer_size()`].
247
    ///
248
    /// At most `count` packets are processed. `None` processes all the packets received in
249
    /// one buffer when reading a live capture, or all the packets in the file when reading
250
    /// a savefile.
251
    ///
252
    /// Unlike [`Self::for_each()`], this does not block until `count` packets have been
253
    /// processed: it returns the number of packets processed as soon as one buffer has been
254
    /// handled, which may be zero if there are no more packets to read. Note that on most
255
    /// platforms the read timeout only starts once the first packet arrives, so a quiet live
256
    /// capture can block instead of returning zero.
257
16
    pub fn dispatch<F>(&mut self, count: Option<usize>, handler: F) -> Result<usize, Error>
258
16
    where
259
16
        F: FnMut(Packet),
260
    {
261
16
        let cnt = match count {
262
            // What passing 0 down to pcap_dispatch means depends on the libpcap version.
263
            // We interpret it as "read nothing", so we just succeed immediately.
264
2
            Some(0) => return Ok(0),
265
6
            Some(cnt) => cnt
266
6
                .try_into()
267
6
                .expect("count of packets to read cannot exceed c_int::MAX"),
268
8
            None => -1,
269
        };
270

            
271
14
        let mut handler = HandlerFn {
272
14
            func: AssertUnwindSafe(handler),
273
14
            panic_payload: None,
274
14
            handle: self.handle.clone(),
275
14
        };
276
14
        let return_code = unsafe {
277
14
            raw::pcap_dispatch(
278
14
                self.handle.as_ptr(),
279
14
                cnt,
280
14
                HandlerFn::<F>::callback,
281
14
                &mut handler as *mut HandlerFn<AssertUnwindSafe<F>> as *mut u8,
282
            )
283
        };
284
14
        if let Some(e) = handler.panic_payload {
285
2
            resume_unwind(e);
286
12
        }
287
        // A successful pcap_dispatch returns the number of packets processed, not 0 like
288
        // pcap_loop.
289
12
        self.check_err(return_code >= 0)
290
12
            .and(Ok(return_code as usize))
291
14
    }
292

            
293
    /// Returns a thread-safe `BreakLoop` handle for calling pcap_breakloop() on an active capture.
294
    ///
295
    /// # Example
296
    ///
297
    /// ```no_run
298
    /// // Using an active capture
299
    /// use pcap::Device;
300
    ///
301
    /// let mut cap = Device::lookup().unwrap().unwrap().open().unwrap();
302
    ///
303
    /// let break_handle = cap.breakloop_handle();
304
    ///
305
    /// let capture_thread = std::thread::spawn(move || {
306
    ///     while let Ok(packet) = cap.next_packet() {
307
    ///         println!("received packet! {:?}", packet);
308
    ///     }
309
    /// });
310
    ///
311
    /// // Send break_handle to a separate thread (e.g. user input, signal handler, etc.)
312
    /// std::thread::spawn(move || {
313
    ///     std::thread::sleep(std::time::Duration::from_secs(1));
314
    ///     break_handle.breakloop();
315
    /// });
316
    ///
317
    /// capture_thread.join().unwrap();
318
    /// ```
319
2
    pub fn breakloop_handle(&mut self) -> BreakLoop {
320
2
        BreakLoop {
321
2
            handle: Arc::<PcapHandle>::downgrade(&self.handle),
322
2
        }
323
2
    }
324

            
325
    /// Compiles the string into a filter program using `pcap_compile`.
326
22
    pub fn compile(&self, program: &str, optimize: bool) -> Result<BpfProgram, Error> {
327
22
        let program = CString::new(program)?;
328

            
329
        unsafe {
330
22
            let mut bpf_program: raw::bpf_program = mem::zeroed();
331
22
            let ret = raw::pcap_compile(
332
22
                self.handle.as_ptr(),
333
22
                &mut bpf_program,
334
22
                program.as_ptr(),
335
22
                optimize as libc::c_int,
336
                0,
337
            );
338
22
            self.check_err(ret != -1).and(Ok(BpfProgram(bpf_program)))
339
        }
340
22
    }
341

            
342
    /// Sets the filter on the capture using the given BPF program string. Internally this is
343
    /// compiled using `pcap_compile()`. `optimize` controls whether optimization on the resulting
344
    /// code is performed
345
    ///
346
    /// See <http://biot.com/capstats/bpf.html> for more information about this syntax.
347
4
    pub fn filter(&mut self, program: &str, optimize: bool) -> Result<(), Error> {
348
4
        let mut bpf_program = self.compile(program, optimize)?;
349
4
        let ret = unsafe { raw::pcap_setfilter(self.handle.as_ptr(), &mut bpf_program.0) };
350
4
        self.check_err(ret != -1)
351
4
    }
352

            
353
    /// Get capture statistics about this capture. The values represent packet statistics from the
354
    /// start of the run to the time of the call.
355
    ///
356
    /// See <https://www.tcpdump.org/manpages/pcap_stats.3pcap.html> for per-platform caveats about
357
    /// how packet statistics are calculated.
358
6
    pub fn stats(&mut self) -> Result<Stat, Error> {
359
        unsafe {
360
6
            let mut stats: raw::pcap_stat = mem::zeroed();
361
6
            self.check_err(raw::pcap_stats(self.handle.as_ptr(), &mut stats) != -1)
362
7
                .map(|_| Stat::new(stats.ps_recv, stats.ps_drop, stats.ps_ifdrop))
363
        }
364
6
    }
365
}
366

            
367
// Handler and its associated function let us create an extern "C" fn which dispatches to a normal
368
// Rust FnMut, which may be a closure with a captured environment. The *only* purpose of this
369
// generic parameter is to ensure that in Capture::pcap_loop that we pass the right function
370
// pointer and the right data pointer to pcap_loop.
371
struct HandlerFn<F> {
372
    func: F,
373
    panic_payload: Option<Box<dyn Any + Send>>,
374
    handle: Arc<PcapHandle>,
375
}
376

            
377
impl<F> HandlerFn<F>
378
where
379
    F: FnMut(Packet),
380
{
381
28
    extern "C" fn callback(
382
28
        slf: *mut libc::c_uchar,
383
28
        header: *const raw::pcap_pkthdr,
384
28
        packet: *const libc::c_uchar,
385
28
    ) {
386
        unsafe {
387
28
            let packet = Packet::new(
388
28
                &*(header as *const PacketHeader),
389
28
                slice::from_raw_parts(packet, (*header).caplen as _),
390
            );
391

            
392
28
            let slf = slf as *mut Self;
393
28
            let func = &mut (*slf).func;
394
28
            let mut func = AssertUnwindSafe(func);
395
            // If our handler function panics, we need to prevent it from unwinding across the
396
            // FFI boundary. If the handler panics we catch the unwind here, break out of
397
            // pcap_loop, and resume the unwind outside.
398
28
            if let Err(e) = catch_unwind(move || func(packet)) {
399
6
                (*slf).panic_payload = Some(e);
400
6
                raw::pcap_breakloop((*slf).handle.as_ptr());
401
22
            }
402
        }
403
28
    }
404
}
405

            
406
impl<T: Activated> From<Capture<T>> for Capture<dyn Activated> {
407
32
    fn from(cap: Capture<T>) -> Capture<dyn Activated> {
408
32
        unsafe { mem::transmute(cap) }
409
32
    }
410
}
411

            
412
/// BreakLoop can safely be sent to other threads such as signal handlers to abort
413
/// blocking capture loops such as `Capture::next_packet` and `Capture::for_each`.
414
///
415
/// See <https://www.tcpdump.org/manpages/pcap_breakloop.3pcap.html> for per-platform caveats about
416
/// how breakloop can wake up blocked threads.
417
pub struct BreakLoop {
418
    handle: Weak<PcapHandle>,
419
}
420

            
421
unsafe impl Send for BreakLoop {}
422
unsafe impl Sync for BreakLoop {}
423

            
424
impl BreakLoop {
425
    /// Calls `pcap_breakloop` to make the blocking loop of a pcap capture return.
426
    /// The call is a no-op if the handle is invalid.
427
    ///
428
    /// # Safety
429
    ///
430
    /// Can be called from any thread, but **must not** be used inside a
431
    /// signal handler unless the owning `Capture` is guaranteed to still
432
    /// be alive.
433
    ///
434
    /// The signal handler should defer the execution of `BreakLoop::breakloop()`
435
    /// to a thread instead for safety.
436
4
    pub fn breakloop(&self) {
437
4
        if let Some(handle) = self.handle.upgrade() {
438
2
            unsafe { raw::pcap_breakloop(handle.as_ptr()) };
439
2
        }
440
4
    }
441
}
442

            
443
/// Abstraction for writing pcap savefiles, which can be read afterwards via `Capture::from_file()`.
444
pub struct Savefile {
445
    handle: NonNull<raw::pcap_dumper_t>,
446
}
447

            
448
// Just like a Capture, a Savefile is safe to Send as it encapsulates the entire lifetime of
449
// `raw::pcap_dumper_t *`, but it is not safe to Sync as libpcap does not promise thread-safe access
450
// to the same `raw::pcap_dumper_t *` from multiple threads.
451
unsafe impl Send for Savefile {}
452

            
453
impl Savefile {
454
    /// Write a packet to a capture file
455
404
    pub fn write(&mut self, packet: &Packet<'_>) {
456
404
        unsafe {
457
404
            raw::pcap_dump(
458
404
                self.handle.as_ptr() as _,
459
404
                &*(packet.header as *const PacketHeader as *const raw::pcap_pkthdr),
460
404
                packet.data.as_ptr(),
461
404
            );
462
404
        }
463
404
    }
464

            
465
    /// Flushes all the packets that haven't been written to the savefile
466
4
    pub fn flush(&mut self) -> Result<(), Error> {
467
4
        if unsafe { raw::pcap_dump_flush(self.handle.as_ptr() as _) } != 0 {
468
2
            return Err(Error::ErrnoError(errno::errno()));
469
2
        }
470

            
471
2
        Ok(())
472
4
    }
473
}
474

            
475
impl From<NonNull<raw::pcap_dumper_t>> for Savefile {
476
19
    fn from(handle: NonNull<raw::pcap_dumper_t>) -> Self {
477
19
        Savefile { handle }
478
19
    }
479
}
480

            
481
impl Drop for Savefile {
482
21
    fn drop(&mut self) {
483
21
        unsafe { raw::pcap_dump_close(self.handle.as_ptr()) }
484
21
    }
485
}
486

            
487
#[repr(transparent)]
488
pub struct BpfInstruction(raw::bpf_insn);
489
#[repr(transparent)]
490
pub struct BpfProgram(raw::bpf_program);
491

            
492
impl BpfProgram {
493
    /// checks whether a filter matches a packet
494
6
    pub fn filter(&self, buf: &[u8]) -> bool {
495
6
        let header: raw::pcap_pkthdr = raw::pcap_pkthdr {
496
6
            ts: libc::timeval {
497
6
                tv_sec: 0,
498
6
                tv_usec: 0,
499
6
            },
500
6
            caplen: buf.len() as u32,
501
6
            len: buf.len() as u32,
502
6
        };
503
6
        unsafe { raw::pcap_offline_filter(&self.0, &header, buf.as_ptr()) > 0 }
504
6
    }
505

            
506
12
    pub fn get_instructions(&self) -> &[BpfInstruction] {
507
        unsafe {
508
12
            slice::from_raw_parts(
509
12
                self.0.bf_insns as *const BpfInstruction,
510
12
                self.0.bf_len as usize,
511
12
            )
512
        }
513
12
    }
514
}
515

            
516
impl Drop for BpfProgram {
517
29
    fn drop(&mut self) {
518
29
        unsafe { raw::pcap_freecode(&mut self.0) }
519
29
    }
520
}
521

            
522
impl fmt::Display for BpfInstruction {
523
2
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
524
2
        write!(
525
2
            f,
526
            "{} {} {} {}",
527
            self.0.code, self.0.jt, self.0.jf, self.0.k
528
        )
529
2
    }
530
}
531

            
532
unsafe impl Send for BpfProgram {}
533

            
534
#[cfg(not(windows))]
535
/// Open a raw file descriptor.
536
///
537
/// # Safety
538
///
539
/// Unsafe, because the returned FILE assumes it is the sole owner of the file descriptor.
540
18
pub unsafe fn open_raw_fd(fd: RawFd, mode: u8) -> Result<*mut libc::FILE, Error> {
541
18
    let mode = [mode, 0];
542
18
    libc::fdopen(fd, mode.as_ptr() as _)
543
18
        .as_mut()
544
21
        .map(|f| f as _)
545
18
        .ok_or(Error::InvalidRawFd)
546
18
}
547

            
548
// GRCOV_EXCL_START
549
#[cfg(test)]
550
mod testmod {
551
    use super::*;
552

            
553
    pub static TS: libc::timeval = libc::timeval {
554
        tv_sec: 5,
555
        tv_usec: 50,
556
    };
557
    pub static LEN: u32 = DATA.len() as u32;
558
    pub static CAPLEN: u32 = LEN;
559

            
560
    pub static mut PKTHDR: raw::pcap_pkthdr = raw::pcap_pkthdr {
561
        ts: TS,
562
        caplen: CAPLEN,
563
        len: LEN,
564
    };
565
    pub static PACKET_HEADER: PacketHeader = PacketHeader {
566
        ts: TS,
567
        caplen: CAPLEN,
568
        len: LEN,
569
    };
570

            
571
    pub static DATA: [u8; 4] = [4, 5, 6, 7];
572
    pub static PACKET: Packet = Packet {
573
        header: &PACKET_HEADER,
574
        data: &DATA,
575
    };
576

            
577
    pub struct NextExContext(raw::__pcap_next_ex::Context);
578
    pub fn next_ex_expect(pcap: *mut raw::pcap_t) -> NextExContext {
579
        let data_ptr: *const libc::c_uchar = DATA.as_ptr();
580
        #[allow(unused_unsafe)] // unsafe still needed to compile on MSRV
581
        let pkthdr_ptr: *mut raw::pcap_pkthdr = unsafe { std::ptr::addr_of_mut!(PKTHDR) };
582

            
583
        let ctx = raw::pcap_next_ex_context();
584
        ctx.checkpoint();
585
        ctx.expect()
586
            .withf_st(move |arg1, _, _| *arg1 == pcap)
587
            .return_once_st(move |_, arg2, arg3| {
588
                unsafe {
589
                    *arg2 = pkthdr_ptr;
590
                    *arg3 = data_ptr;
591
                }
592
                CAPLEN as i32
593
            });
594

            
595
        NextExContext(ctx)
596
    }
597
}
598
// GRCOV_EXCL_STOP
599

            
600
#[cfg(test)]
601
mod tests {
602
    use crate::{
603
        capture::{
604
            activated::testmod::{next_ex_expect, PACKET},
605
            testmod::test_capture,
606
            Active, Capture, Offline,
607
        },
608
        raw::testmod::{as_pcap_dumper_t, as_pcap_t, geterr_expect, RAWMTX},
609
    };
610

            
611
    use super::*;
612

            
613
    #[test]
614
    fn test_list_datalinks() {
615
        let _m = RAWMTX.lock();
616

            
617
        let mut value: isize = 777;
618
        let pcap = as_pcap_t(&mut value);
619

            
620
        let test_capture = test_capture::<Active>(pcap);
621
        let capture: Capture<dyn Activated> = test_capture.capture.into();
622

            
623
        let ctx = raw::pcap_list_datalinks_context();
624
        ctx.expect()
625
            .withf_st(move |arg1, _| *arg1 == pcap)
626
            .return_once_st(|_, _| 0);
627

            
628
        let ctx = raw::pcap_free_datalinks_context();
629
        ctx.expect().return_once(|_| {});
630

            
631
        let _err = geterr_expect(pcap);
632

            
633
        let result = capture.list_datalinks();
634
        assert!(result.is_err());
635

            
636
        let mut datalinks: [i32; 4] = [0, 1, 2, 3];
637
        let links: *mut i32 = datalinks.as_mut_ptr();
638
        let len = datalinks.len();
639

            
640
        let ctx = raw::pcap_list_datalinks_context();
641
        ctx.checkpoint();
642
        ctx.expect()
643
            .withf_st(move |arg1, _| *arg1 == pcap)
644
            .return_once_st(move |_, arg2| {
645
                unsafe { *arg2 = links };
646
                len as i32
647
            });
648

            
649
        let ctx = raw::pcap_free_datalinks_context();
650
        ctx.checkpoint();
651
        ctx.expect().return_once(|_| {});
652

            
653
        let pcap_datalinks = capture.list_datalinks().unwrap();
654
        assert_eq!(
655
            pcap_datalinks,
656
            datalinks.iter().cloned().map(Linktype).collect::<Vec<_>>()
657
        );
658
    }
659

            
660
    #[test]
661
    fn test_set_datalink() {
662
        let _m = RAWMTX.lock();
663

            
664
        let mut value: isize = 777;
665
        let pcap = as_pcap_t(&mut value);
666

            
667
        let test_capture = test_capture::<Active>(pcap);
668
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
669

            
670
        let ctx = raw::pcap_set_datalink_context();
671
        ctx.expect()
672
            .withf_st(move |arg1, _| *arg1 == pcap)
673
            .return_once(|_, _| 0);
674

            
675
        let result = capture.set_datalink(Linktype::ETHERNET);
676
        assert!(result.is_ok());
677

            
678
        let ctx = raw::pcap_set_datalink_context();
679
        ctx.checkpoint();
680
        ctx.expect()
681
            .withf_st(move |arg1, _| *arg1 == pcap)
682
            .return_once(|_, _| -1);
683

            
684
        let _err = geterr_expect(pcap);
685

            
686
        let result = capture.set_datalink(Linktype::ETHERNET);
687
        assert!(result.is_err());
688
    }
689

            
690
    #[test]
691
    fn test_get_datalink() {
692
        let _m = RAWMTX.lock();
693

            
694
        let mut value: isize = 777;
695
        let pcap = as_pcap_t(&mut value);
696

            
697
        let test_capture = test_capture::<Active>(pcap);
698
        let capture: Capture<dyn Activated> = test_capture.capture.into();
699

            
700
        let ctx = raw::pcap_datalink_context();
701
        ctx.expect()
702
            .withf_st(move |arg1| *arg1 == pcap)
703
            .return_once(|_| 1);
704

            
705
        let linktype = capture.get_datalink();
706
        assert_eq!(linktype, Linktype::ETHERNET);
707
    }
708

            
709
    #[test]
710
    fn unify_activated() {
711
        #![allow(dead_code)]
712
        fn test1() -> Capture<Active> {
713
            panic!();
714
        }
715

            
716
        fn test2() -> Capture<Offline> {
717
            panic!();
718
        }
719

            
720
        fn maybe(a: bool) -> Capture<dyn Activated> {
721
            if a {
722
                test1().into()
723
            } else {
724
                test2().into()
725
            }
726
        }
727

            
728
        fn also_maybe(a: &mut Capture<dyn Activated>) {
729
            a.filter("whatever filter string, this won't be run anyway", false)
730
                .unwrap();
731
        }
732
    }
733

            
734
    #[test]
735
    fn test_breakloop_capture_dropped() {
736
        let _m = RAWMTX.lock();
737

            
738
        let mut value: isize = 1234;
739
        let pcap = as_pcap_t(&mut value);
740

            
741
        let test_capture = test_capture::<Active>(pcap);
742
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
743

            
744
        let ctx = raw::pcap_breakloop_context();
745
        ctx.expect()
746
            .withf_st(move |h| *h == pcap)
747
            .return_const(())
748
            .times(1);
749

            
750
        let break_handle = capture.breakloop_handle();
751

            
752
        break_handle.breakloop();
753

            
754
        drop(capture);
755

            
756
        break_handle.breakloop(); // this call does not trigger mock after drop
757
    }
758

            
759
    #[test]
760
    fn test_savefile() {
761
        let _m = RAWMTX.lock();
762

            
763
        let mut value: isize = 777;
764
        let pcap = as_pcap_t(&mut value);
765

            
766
        let mut value: isize = 888;
767
        let pcap_dumper = as_pcap_dumper_t(&mut value);
768

            
769
        let test_capture = test_capture::<Offline>(pcap);
770
        let capture = test_capture.capture;
771

            
772
        let ctx = raw::pcap_dump_open_context();
773
        ctx.expect()
774
            .withf_st(move |arg1, _| *arg1 == pcap)
775
            .return_once_st(move |_, _| pcap_dumper);
776

            
777
        let ctx = raw::pcap_dump_close_context();
778
        ctx.expect()
779
            .withf_st(move |arg1| *arg1 == pcap_dumper)
780
            .return_once(|_| {});
781

            
782
        let result = capture.savefile("path/to/nowhere");
783
        assert!(result.is_ok());
784
    }
785

            
786
    #[test]
787
    #[cfg(libpcap_1_7_2)]
788
    fn test_savefile_append() {
789
        let _m = RAWMTX.lock();
790

            
791
        let mut value: isize = 777;
792
        let pcap = as_pcap_t(&mut value);
793

            
794
        let mut value: isize = 888;
795
        let pcap_dumper = as_pcap_dumper_t(&mut value);
796

            
797
        let test_capture = test_capture::<Offline>(pcap);
798
        let capture = test_capture.capture;
799

            
800
        let ctx = raw::pcap_dump_open_append_context();
801
        ctx.expect()
802
            .withf_st(move |arg1, _| *arg1 == pcap)
803
            .return_once_st(move |_, _| pcap_dumper);
804

            
805
        let ctx = raw::pcap_dump_close_context();
806
        ctx.expect()
807
            .withf_st(move |arg1| *arg1 == pcap_dumper)
808
            .return_once(|_| {});
809

            
810
        let result = capture.savefile_append("path/to/nowhere");
811
        assert!(result.is_ok());
812
    }
813

            
814
    #[test]
815
    fn test_savefile_error() {
816
        let _m = RAWMTX.lock();
817

            
818
        let mut value: isize = 777;
819
        let pcap = as_pcap_t(&mut value);
820

            
821
        let test_capture = test_capture::<Offline>(pcap);
822
        let capture = test_capture.capture;
823

            
824
        let ctx = raw::pcap_dump_open_context();
825
        ctx.expect()
826
            .withf_st(move |arg1, _| *arg1 == pcap)
827
            .return_once(|_, _| std::ptr::null_mut());
828

            
829
        let _err = geterr_expect(pcap);
830

            
831
        let result = capture.savefile("path/to/nowhere");
832
        assert!(result.is_err());
833
    }
834

            
835
    #[test]
836
    #[cfg(libpcap_1_7_2)]
837
    fn test_savefile_append_error() {
838
        let _m = RAWMTX.lock();
839

            
840
        let mut value: isize = 777;
841
        let pcap = as_pcap_t(&mut value);
842

            
843
        let test_capture = test_capture::<Offline>(pcap);
844
        let capture = test_capture.capture;
845

            
846
        let ctx = raw::pcap_dump_open_append_context();
847
        ctx.expect()
848
            .withf_st(move |arg1, _| *arg1 == pcap)
849
            .return_once(|_, _| std::ptr::null_mut());
850

            
851
        let _err = geterr_expect(pcap);
852

            
853
        let result = capture.savefile_append("path/to/nowhere");
854
        assert!(result.is_err());
855
    }
856

            
857
    #[test]
858
    fn test_savefile_ops() {
859
        let _m = RAWMTX.lock();
860

            
861
        let mut value: isize = 888;
862
        let pcap_dumper = as_pcap_dumper_t(&mut value);
863

            
864
        let ctx = raw::pcap_dump_close_context();
865
        ctx.expect()
866
            .withf_st(move |arg1| *arg1 == pcap_dumper)
867
            .return_once(|_| {});
868

            
869
        let mut savefile = Savefile {
870
            handle: NonNull::new(pcap_dumper).unwrap(),
871
        };
872

            
873
        let ctx = raw::pcap_dump_context();
874
        ctx.expect()
875
            .withf_st(move |arg1, _, _| *arg1 == pcap_dumper as _)
876
            .return_once(|_, _, _| {});
877

            
878
        savefile.write(&PACKET);
879

            
880
        let ctx = raw::pcap_dump_flush_context();
881
        ctx.expect()
882
            .withf_st(move |arg1| *arg1 == pcap_dumper)
883
            .return_once(|_| 0);
884

            
885
        let result = savefile.flush();
886
        assert!(result.is_ok());
887

            
888
        let ctx = raw::pcap_dump_flush_context();
889
        ctx.checkpoint();
890
        ctx.expect()
891
            .withf_st(move |arg1| *arg1 == pcap_dumper)
892
            .return_once(|_| -1);
893

            
894
        let result = savefile.flush();
895
        assert!(result.is_err());
896
    }
897

            
898
    #[test]
899
    fn test_direction() {
900
        let _m = RAWMTX.lock();
901

            
902
        let mut value: isize = 777;
903
        let pcap = as_pcap_t(&mut value);
904

            
905
        let test_capture = test_capture::<Active>(pcap);
906
        let capture = test_capture.capture;
907

            
908
        let ctx = raw::pcap_setdirection_context();
909
        ctx.expect()
910
            .withf_st(move |arg1, arg2| (*arg1 == pcap) && (*arg2 == raw::PCAP_D_OUT))
911
            .return_once(|_, _| 0);
912

            
913
        let result = capture.direction(Direction::Out);
914
        assert!(result.is_ok());
915

            
916
        let ctx = raw::pcap_setdirection_context();
917
        ctx.checkpoint();
918
        ctx.expect()
919
            .withf_st(move |arg1, arg2| (*arg1 == pcap) && (*arg2 == raw::PCAP_D_OUT))
920
            .return_once(|_, _| -1);
921

            
922
        let _err = geterr_expect(pcap);
923

            
924
        let result = capture.direction(Direction::Out);
925
        assert!(result.is_err());
926

            
927
        // For code coverage of the derive line.
928
        assert_ne!(Direction::In, Direction::InOut);
929
        assert_ne!(Direction::In, Direction::Out);
930
        assert_ne!(Direction::InOut, Direction::Out);
931
    }
932

            
933
    #[test]
934
    fn test_next_packet() {
935
        let _m = RAWMTX.lock();
936

            
937
        let mut value: isize = 777;
938
        let pcap = as_pcap_t(&mut value);
939

            
940
        let test_capture = test_capture::<Active>(pcap);
941
        let mut capture = test_capture.capture;
942

            
943
        let _nxt = next_ex_expect(pcap);
944

            
945
        let next_packet = capture.next_packet().unwrap();
946
        assert_eq!(next_packet, PACKET);
947
    }
948

            
949
    #[test]
950
    fn test_next_packet_timeout() {
951
        let _m = RAWMTX.lock();
952

            
953
        let mut value: isize = 777;
954
        let pcap = as_pcap_t(&mut value);
955

            
956
        let test_capture = test_capture::<Active>(pcap);
957
        let mut capture = test_capture.capture;
958

            
959
        let ctx = raw::pcap_next_ex_context();
960
        ctx.expect()
961
            .withf_st(move |arg1, _, _| *arg1 == pcap)
962
            .return_once_st(move |_, _, _| 0);
963

            
964
        let err = capture.next_packet().unwrap_err();
965
        assert_eq!(err, Error::TimeoutExpired);
966
    }
967

            
968
    #[test]
969
    fn test_next_packet_read_error() {
970
        let _m = RAWMTX.lock();
971

            
972
        let mut value: isize = 777;
973
        let pcap = as_pcap_t(&mut value);
974

            
975
        let test_capture = test_capture::<Active>(pcap);
976
        let mut capture = test_capture.capture;
977

            
978
        let ctx = raw::pcap_next_ex_context();
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.next_packet();
986
        assert!(result.is_err());
987
    }
988

            
989
    #[test]
990
    fn test_next_packet_no_more_packets() {
991
        let _m = RAWMTX.lock();
992

            
993
        let mut value: isize = 777;
994
        let pcap = as_pcap_t(&mut value);
995

            
996
        let test_capture = test_capture::<Offline>(pcap);
997
        let mut capture = test_capture.capture;
998

            
999
        let ctx = raw::pcap_next_ex_context();
        ctx.expect()
            .withf_st(move |arg1, _, _| *arg1 == pcap)
            .return_once_st(move |_, _, _| -2);
        let err = capture.next_packet().unwrap_err();
        assert_eq!(err, Error::NoMorePackets);
    }
    #[test]
    fn test_compile() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let capture = test_capture.capture;
        let ctx = raw::pcap_compile_context();
        ctx.expect()
            .withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
            .return_once(|_, _, _, _, _| -1);
        let _err = geterr_expect(pcap);
        let ctx = raw::pcap_freecode_context();
        ctx.expect().return_once(|_| {});
        let result = capture.compile("some bpf program", false);
        assert!(result.is_err());
        let ctx = raw::pcap_compile_context();
        ctx.checkpoint();
        ctx.expect()
            .withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
            .return_once(|_, _, _, _, _| 0);
        let ctx = raw::pcap_freecode_context();
        ctx.checkpoint();
        ctx.expect().return_once(|_| {});
        let result = capture.compile("some bpf program", false);
        assert!(result.is_ok());
    }
    #[test]
    fn test_filter() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture = test_capture.capture;
        let ctx = raw::pcap_compile_context();
        ctx.expect()
            .withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
            .return_once(|_, _, _, _, _| 0);
        let ctx = raw::pcap_setfilter_context();
        ctx.expect()
            .withf_st(move |arg1, _| *arg1 == pcap)
            .return_once(|_, _| -1);
        let _err = geterr_expect(pcap);
        let ctx = raw::pcap_freecode_context();
        ctx.expect().return_once(|_| {});
        let result = capture.filter("some bpf program", false);
        assert!(result.is_err());
        let ctx = raw::pcap_compile_context();
        ctx.checkpoint();
        ctx.expect()
            .withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
            .return_once(|_, _, _, _, _| 0);
        let ctx = raw::pcap_setfilter_context();
        ctx.checkpoint();
        ctx.expect()
            .withf_st(move |arg1, _| *arg1 == pcap)
            .return_once(|_, _| 0);
        let ctx = raw::pcap_freecode_context();
        ctx.checkpoint();
        ctx.expect().return_once(|_| {});
        let result = capture.compile("some bpf program", false);
        assert!(result.is_ok());
    }
    #[test]
    fn test_stats() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture = test_capture.capture;
        let stat = raw::pcap_stat {
            ps_recv: 1,
            ps_drop: 2,
            ps_ifdrop: 3,
        };
        let ctx = raw::pcap_stats_context();
        ctx.expect()
            .withf_st(move |arg1, _| *arg1 == pcap)
            .return_once_st(move |_, arg2| {
                unsafe { *arg2 = stat };
                0
            });
        let stats = capture.stats().unwrap();
        assert_eq!(stats, Stat::new(stat.ps_recv, stat.ps_drop, stat.ps_ifdrop));
        let ctx = raw::pcap_stats_context();
        ctx.checkpoint();
        ctx.expect()
            .withf_st(move |arg1, _| *arg1 == pcap)
            .return_once_st(move |_, _| -1);
        let _err = geterr_expect(pcap);
        let result = capture.stats();
        assert!(result.is_err());
    }
    #[test]
    fn test_bpf_instruction_display() {
        let instr = BpfInstruction(raw::bpf_insn {
            code: 1,
            jt: 2,
            jf: 3,
            k: 4,
        });
        assert_eq!(format!("{instr}"), "1 2 3 4");
    }
    #[test]
    fn read_packet_via_pcap_loop() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
        let ctx = raw::pcap_loop_context();
        ctx.expect()
            .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == -1)
            .return_once_st(move |_, _, func, data| {
                let header = raw::pcap_pkthdr {
                    ts: libc::timeval {
                        tv_sec: 0,
                        tv_usec: 0,
                    },
                    caplen: 0,
                    len: 0,
                };
                let packet_data = &[];
                func(data, &header, packet_data.as_ptr());
                0
            });
        let mut packets = 0;
        capture
            .for_each(None, |_| {
                packets += 1;
            })
            .unwrap();
        assert_eq!(packets, 1);
    }
    #[test]
    #[should_panic = "panic in callback"]
    fn panic_in_pcap_loop() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
        let ctx = raw::pcap_loop_context();
        ctx.expect()
            .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == -1)
            .return_once_st(move |_, _, func, data| {
                let header = raw::pcap_pkthdr {
                    ts: libc::timeval {
                        tv_sec: 0,
                        tv_usec: 0,
                    },
                    caplen: 0,
                    len: 0,
                };
                let packet_data = &[];
                func(data, &header, packet_data.as_ptr());
                0
            });
        let ctx = raw::pcap_breakloop_context();
        ctx.expect()
            .withf_st(move |arg1| *arg1 == pcap)
            .return_once_st(move |_| {});
        capture
            .for_each(None, |_| panic!("panic in callback"))
            .unwrap();
    }
    #[test]
    fn for_each_with_count() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
        let ctx = raw::pcap_loop_context();
        ctx.expect()
            .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == 2)
            .return_once_st(move |_, _, func, data| {
                let header = raw::pcap_pkthdr {
                    ts: libc::timeval {
                        tv_sec: 0,
                        tv_usec: 0,
                    },
                    caplen: 0,
                    len: 0,
                };
                let packet_data = &[];
                func(data, &header, packet_data.as_ptr());
                func(data, &header, packet_data.as_ptr());
                0
            });
        let mut packets = 0;
        capture
            .for_each(Some(2), |_| {
                packets += 1;
            })
            .unwrap();
        assert_eq!(packets, 2);
    }
    #[test]
    fn for_each_with_count_0() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
        let mut packets = 0;
        capture
            .for_each(Some(0), |_| {
                packets += 1;
            })
            .unwrap();
        assert_eq!(packets, 0);
    }
    #[test]
    fn read_packets_via_pcap_dispatch() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
        let ctx = raw::pcap_dispatch_context();
        ctx.expect()
            .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == -1)
            .return_once_st(move |_, _, func, data| {
                let header = raw::pcap_pkthdr {
                    ts: libc::timeval {
                        tv_sec: 0,
                        tv_usec: 0,
                    },
                    caplen: 0,
                    len: 0,
                };
                let packet_data = &[];
                func(data, &header, packet_data.as_ptr());
                func(data, &header, packet_data.as_ptr());
                2
            });
        let mut packets = 0;
        let processed = capture
            .dispatch(None, |_| {
                packets += 1;
            })
            .unwrap();
        assert_eq!(packets, 2);
        assert_eq!(processed, 2);
    }
    #[test]
    #[should_panic = "panic in callback"]
    fn panic_in_pcap_dispatch() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
        let ctx = raw::pcap_dispatch_context();
        ctx.expect()
            .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == -1)
            .return_once_st(move |_, _, func, data| {
                let header = raw::pcap_pkthdr {
                    ts: libc::timeval {
                        tv_sec: 0,
                        tv_usec: 0,
                    },
                    caplen: 0,
                    len: 0,
                };
                let packet_data = &[];
                func(data, &header, packet_data.as_ptr());
                -2
            });
        let ctx = raw::pcap_breakloop_context();
        ctx.expect()
            .withf_st(move |arg1| *arg1 == pcap)
            .return_once_st(move |_| {});
        capture
            .dispatch(None, |_| panic!("panic in callback"))
            .unwrap();
    }
    #[test]
    fn dispatch_with_count() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
        let ctx = raw::pcap_dispatch_context();
        ctx.expect()
            .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == 2)
            .return_once_st(move |_, _, func, data| {
                let header = raw::pcap_pkthdr {
                    ts: libc::timeval {
                        tv_sec: 0,
                        tv_usec: 0,
                    },
                    caplen: 0,
                    len: 0,
                };
                let packet_data = &[];
                func(data, &header, packet_data.as_ptr());
                func(data, &header, packet_data.as_ptr());
                2
            });
        let mut packets = 0;
        let processed = capture
            .dispatch(Some(2), |_| {
                packets += 1;
            })
            .unwrap();
        assert_eq!(packets, 2);
        assert_eq!(processed, 2);
    }
    #[test]
    fn dispatch_limited_by_packets() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
        let ctx = raw::pcap_dispatch_context();
        ctx.expect()
            .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == 5)
            .return_once_st(move |_, _, func, data| {
                let header = raw::pcap_pkthdr {
                    ts: libc::timeval {
                        tv_sec: 0,
                        tv_usec: 0,
                    },
                    caplen: 0,
                    len: 0,
                };
                let packet_data = &[];
                func(data, &header, packet_data.as_ptr());
                func(data, &header, packet_data.as_ptr());
                2
            });
        let mut packets = 0;
        let processed = capture
            .dispatch(Some(5), |_| {
                packets += 1;
            })
            .unwrap();
        assert_eq!(packets, 2);
        assert_eq!(processed, 2);
    }
    #[test]
    fn dispatch_limited_by_count() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
        let ctx = raw::pcap_dispatch_context();
        ctx.expect()
            .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == 1)
            .return_once_st(move |_, cnt, func, data| {
                let header = raw::pcap_pkthdr {
                    ts: libc::timeval {
                        tv_sec: 0,
                        tv_usec: 0,
                    },
                    caplen: 0,
                    len: 0,
                };
                let packet_data = &[];
                for _ in 0..cnt {
                    func(data, &header, packet_data.as_ptr());
                }
                cnt
            });
        let mut packets = 0;
        let processed = capture
            .dispatch(Some(1), |_| {
                packets += 1;
            })
            .unwrap();
        assert_eq!(packets, 1);
        assert_eq!(processed, 1);
    }
    #[test]
    fn dispatch_with_count_0() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
        // Packets are available, so the handler must stay unused.
        let ctx = raw::pcap_dispatch_context();
        ctx.expect()
            .withf_st(move |arg1, _, _, _| *arg1 == pcap)
            .return_once_st(move |_, _, func, data| {
                let header = raw::pcap_pkthdr {
                    ts: libc::timeval {
                        tv_sec: 0,
                        tv_usec: 0,
                    },
                    caplen: 0,
                    len: 0,
                };
                let packet_data = &[];
                func(data, &header, packet_data.as_ptr());
                func(data, &header, packet_data.as_ptr());
                2
            });
        let mut packets = 0;
        let processed = capture
            .dispatch(Some(0), |_| {
                packets += 1;
            })
            .unwrap();
        assert_eq!(packets, 0);
        assert_eq!(processed, 0);
    }
    #[test]
    fn dispatch_no_packets() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
        let ctx = raw::pcap_dispatch_context();
        ctx.expect()
            .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == -1)
            .return_once_st(move |_, _, _, _| 0);
        let mut packets = 0;
        let processed = capture
            .dispatch(None, |_| {
                packets += 1;
            })
            .unwrap();
        assert_eq!(packets, 0);
        assert_eq!(processed, 0);
    }
    #[test]
    fn dispatch_error() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
        let ctx = raw::pcap_dispatch_context();
        ctx.expect()
            .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == -1)
            .return_once_st(move |_, _, _, _| -1);
        let _err = geterr_expect(pcap);
        let result = capture.dispatch(None, |_| {});
        assert!(result.is_err());
    }
}