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::{AssertUnwindSafe, catch_unwind, resume_unwind},
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
#[cfg(not(windows))]
22
use libc::FILE;
23

            
24
use crate::{
25
    Error,
26
    capture::{Activated, Capture, PcapHandle},
27
    codec::PacketCodec,
28
    linktype::Linktype,
29
    packet::{Packet, PacketHeader},
30
    path_to_cstring, raw,
31
};
32

            
33
use iterator::PacketIter;
34

            
35
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36
/// Packet statistics for a capture
37
pub struct Stat {
38
    /// Number of packets received
39
    pub received: u32,
40
    /// Number of packets dropped because there was no room in the operating system's buffer when
41
    /// they arrived, because packets weren't being read fast enough
42
    pub dropped: u32,
43
    /// Number of packets dropped by the network interface or its driver
44
    pub if_dropped: u32,
45
}
46

            
47
impl Stat {
48
4
    fn new(received: u32, dropped: u32, if_dropped: u32) -> Stat {
49
4
        Stat {
50
4
            received,
51
4
            dropped,
52
4
            if_dropped,
53
4
        }
54
4
    }
55
}
56

            
57
#[repr(u32)]
58
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
59
/// The direction of packets to be captured. Use with `Capture::direction`.
60
pub enum Direction {
61
    /// Capture packets received by or sent by the device. This is the default.
62
    InOut = raw::PCAP_D_INOUT,
63
    /// Only capture packets received by the device.
64
    In = raw::PCAP_D_IN,
65
    /// Only capture packets sent by the device.
66
    Out = raw::PCAP_D_OUT,
67
}
68

            
69
///# Activated captures include `Capture<Active>` and `Capture<Offline>`.
70
impl<T: Activated + ?Sized> Capture<T> {
71
    /// List the datalink types that this captured device supports.
72
4
    pub fn list_datalinks(&self) -> Result<Vec<Linktype>, Error> {
73
        unsafe {
74
4
            let mut links: *mut i32 = ptr::null_mut();
75
4
            let num = raw::pcap_list_datalinks(self.handle.as_ptr(), &mut links);
76
4
            let mut vec = vec![];
77
4
            if num > 0 {
78
2
                vec.extend(
79
2
                    slice::from_raw_parts(links, num as _)
80
2
                        .iter()
81
2
                        .cloned()
82
2
                        .map(Linktype),
83
                )
84
2
            }
85
4
            raw::pcap_free_datalinks(links);
86
4
            self.check_err(num > 0).and(Ok(vec))
87
        }
88
4
    }
89

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

            
95
    /// Get the current datalink type for this capture handle.
96
4
    pub fn get_datalink(&self) -> Linktype {
97
4
        unsafe { Linktype(raw::pcap_datalink(self.handle.as_ptr())) }
98
4
    }
99

            
100
    /// Get the snapshot length, that is the maximum number of bytes captured from each packet.
101
    ///
102
    /// For a `Capture<Offline>` this is the length the savefile was recorded with, except that a
103
    /// header claiming zero, or a length too large for an `i32`, is replaced with the largest
104
    /// length the link-layer type can produce.
105
8
    pub fn snaplen(&self) -> i32 {
106
8
        unsafe { raw::pcap_snapshot(self.handle.as_ptr()) }
107
8
    }
108

            
109
    /// Create a `Savefile` context for recording captured packets using this `Capture`'s
110
    /// configurations.
111
    ///
112
    /// On Windows a path outside ASCII needs `init(CharEncoding::Utf8)` first. The path always
113
    /// reaches libpcap as UTF-8, but until that call libpcap reads it in the local code page,
114
    /// which on most systems is not UTF-8: the name gets mangled and the file lands elsewhere.
115
26
    pub fn savefile<P: AsRef<Path>>(&self, path: P) -> Result<Savefile, Error> {
116
26
        let name = path_to_cstring(path.as_ref())?;
117
26
        let handle_opt = NonNull::<raw::pcap_dumper_t>::new(unsafe {
118
26
            raw::pcap_dump_open(self.handle.as_ptr(), name.as_ptr())
119
        });
120
26
        let handle = self
121
26
            .check_err(handle_opt.is_some())
122
38
            .map(|_| handle_opt.unwrap())?;
123
24
        Ok(Savefile::from(handle))
124
26
    }
125

            
126
    /// Create a `Savefile` context for recording captured packets using this `Capture`'s
127
    /// configurations. The output is written to a raw file descriptor which is opened in `"w"`
128
    /// mode.
129
    ///
130
    /// # Safety
131
    ///
132
    /// Unsafe, because the returned Savefile assumes it is the sole owner of the file descriptor.
133
    #[cfg(not(windows))]
134
4
    pub unsafe fn savefile_raw_fd(&self, fd: RawFd) -> Result<Savefile, Error> {
135
5
        unsafe { open_raw_fd(fd, b'w') }.and_then(|file| {
136
2
            let handle_opt = NonNull::<raw::pcap_dumper_t>::new(unsafe {
137
2
                raw::pcap_dump_fopen(self.handle.as_ptr(), file)
138
            });
139
2
            let handle = self
140
2
                .check_err(handle_opt.is_some())
141
2
                .map(|_| handle_opt.unwrap())?;
142
2
            Ok(Savefile::from(handle))
143
2
        })
144
4
    }
145

            
146
    /// Reopen a `Savefile` context for recording captured packets using this `Capture`'s
147
    /// configurations. This is similar to `savefile()` but does not create the file if it
148
    /// does  not exist and, if it does already exist, and is a pcap file with the same
149
    /// byte order as the host opening the file, and has the same timestamp precision,
150
    /// link-layer header type,  and  snapshot length as p, it will write new packets
151
    /// at the end of the file.
152
    ///
153
    /// On Windows a path outside ASCII needs `init(CharEncoding::Utf8)` first. The path always
154
    /// reaches libpcap as UTF-8, but until that call libpcap reads it in the local code page,
155
    /// which on most systems is not UTF-8: the name gets mangled and the file lands elsewhere.
156
    #[cfg(libpcap_1_7_2)]
157
6
    pub fn savefile_append<P: AsRef<Path>>(&self, path: P) -> Result<Savefile, Error> {
158
6
        let name = path_to_cstring(path.as_ref())?;
159
6
        let handle_opt = NonNull::<raw::pcap_dumper_t>::new(unsafe {
160
6
            raw::pcap_dump_open_append(self.handle.as_ptr(), name.as_ptr())
161
        });
162
6
        let handle = self
163
6
            .check_err(handle_opt.is_some())
164
8
            .map(|_| handle_opt.unwrap())?;
165
4
        Ok(Savefile::from(handle))
166
6
    }
167

            
168
    /// Set the direction of the capture
169
4
    pub fn direction(&self, direction: Direction) -> Result<(), Error> {
170
4
        self.check_err(unsafe {
171
4
            raw::pcap_setdirection(self.handle.as_ptr(), direction as u32 as _) == 0
172
        })
173
4
    }
174

            
175
    /// Blocks until a packet is returned from the capture handle or an error occurs.
176
    ///
177
    /// pcap captures packets and places them into a buffer which this function reads
178
    /// from.
179
    ///
180
    /// # Warning
181
    ///
182
    /// This buffer has a finite length, so if the buffer fills completely new
183
    /// packets will be discarded temporarily. This means that in realtime situations,
184
    /// you probably want to minimize the time between calls to next_packet() method.
185
    /// In high traffic situations, consider [`Self::dispatch()`] instead, which
186
    /// processes a whole batch of packets per call.
187
    ///
188
    /// A savefile that has run out gives [`Error::NoMorePackets`], and a capture stopped with
189
    /// [`BreakLoop::breakloop`] gives [`ErrorCode::Break`](crate::ErrorCode::Break). A savefile
190
    /// stopped rather than read to the end cannot be told from one that ended, as libpcap reports
191
    /// both alike.
192
332
    pub fn next_packet(&mut self) -> Result<Packet<'_>, Error> {
193
        unsafe {
194
332
            let mut header: *mut raw::pcap_pkthdr = ptr::null_mut();
195
332
            let mut packet: *const libc::c_uchar = ptr::null();
196
332
            let retcode = raw::pcap_next_ex(self.handle.as_ptr(), &mut header, &mut packet);
197
332
            match retcode {
198
332
                i if i >= 1 => {
199
                    // packet was read without issue
200
296
                    Ok(Packet::new(
201
296
                        &*(&*header as *const raw::pcap_pkthdr as *const PacketHeader),
202
296
                        slice::from_raw_parts(packet, (*header).caplen as _),
203
296
                    ))
204
                }
205
                0 => {
206
                    // packets are being read from a live capture and the
207
                    // timeout expired
208
8
                    Err(Error::TimeoutExpired)
209
                }
210
                -1 => {
211
                    // an error occured while reading the packet
212
8
                    Err(self.get_err())
213
                }
214
                -2 => {
215
                    // either a savefile ran out or the loop was stopped; libpcap answers -2 for
216
                    // both, and an interface has no end to reach
217
20
                    if self.reads_savefile() {
218
18
                        Err(Error::NoMorePackets)
219
                    } else {
220
2
                        Err(self.status_err(retcode))
221
                    }
222
                }
223
                // GRCOV_EXCL_START
224
                _ => {
225
                    // libpcap only defines codes >=1, 0, -1, and -2
226
                    unreachable!()
227
                } // GRCOV_EXCL_STOP
228
            }
229
        }
230
332
    }
231

            
232
    /// Return an iterator that call [`Self::next_packet()`] forever. Require a [`PacketCodec`]
233
8
    pub fn iter<C: PacketCodec>(self, codec: C) -> PacketIter<T, C> {
234
8
        PacketIter::new(self, codec)
235
8
    }
236

            
237
16
    pub fn for_each<F>(&mut self, count: Option<usize>, handler: F) -> Result<(), Error>
238
16
    where
239
16
        F: FnMut(Packet),
240
    {
241
16
        let cnt = match count {
242
            // Actually passing 0 down to pcap_loop would mean read forever.
243
            // We interpret it as "read nothing", so we just succeed immediately.
244
2
            Some(0) => return Ok(()),
245
2
            Some(cnt) => cnt
246
2
                .try_into()
247
2
                .expect("count of packets to read cannot exceed c_int::MAX"),
248
12
            None => -1,
249
        };
250

            
251
14
        let mut handler = HandlerFn {
252
14
            func: AssertUnwindSafe(handler),
253
14
            panic_payload: None,
254
14
            handle: self.handle.clone(),
255
14
        };
256
14
        let return_code = unsafe {
257
14
            raw::pcap_loop(
258
14
                self.handle.as_ptr(),
259
14
                cnt,
260
14
                HandlerFn::<F>::callback,
261
14
                &mut handler as *mut HandlerFn<AssertUnwindSafe<F>> as *mut u8,
262
            )
263
        };
264
14
        if let Some(e) = handler.panic_payload {
265
4
            resume_unwind(e);
266
10
        }
267
10
        if return_code < 0 {
268
4
            return Err(self.status_err(return_code));
269
6
        }
270

            
271
6
        Ok(())
272
12
    }
273

            
274
    /// Process a batch of packets from the capture using `pcap_dispatch`.
275
    ///
276
    /// Unlike [`Self::next_packet()`], which performs a system call for every packet, this
277
    /// processes a whole buffer of packets in a single call, making packet loss less likely
278
    /// in high traffic situations. Packets are still dropped when the buffer fills up between
279
    /// calls, so a busy interface also wants a larger [`Capture::buffer_size()`].
280
    ///
281
    /// At most `count` packets are processed. `None` processes all the packets received in
282
    /// one buffer when reading a live capture, or all the packets in the file when reading
283
    /// a savefile.
284
    ///
285
    /// Unlike [`Self::for_each()`], this does not block until `count` packets have been
286
    /// processed: it returns the number of packets processed as soon as one buffer has been
287
    /// handled, which may be zero if there are no more packets to read. Note that on most
288
    /// platforms the read timeout only starts once the first packet arrives, so a quiet live
289
    /// capture can block instead of returning zero.
290
16
    pub fn dispatch<F>(&mut self, count: Option<usize>, handler: F) -> Result<usize, Error>
291
16
    where
292
16
        F: FnMut(Packet),
293
    {
294
16
        let cnt = match count {
295
            // What passing 0 down to pcap_dispatch means depends on the libpcap version.
296
            // We interpret it as "read nothing", so we just succeed immediately.
297
2
            Some(0) => return Ok(0),
298
6
            Some(cnt) => cnt
299
6
                .try_into()
300
6
                .expect("count of packets to read cannot exceed c_int::MAX"),
301
8
            None => -1,
302
        };
303

            
304
14
        let mut handler = HandlerFn {
305
14
            func: AssertUnwindSafe(handler),
306
14
            panic_payload: None,
307
14
            handle: self.handle.clone(),
308
14
        };
309
14
        let return_code = unsafe {
310
14
            raw::pcap_dispatch(
311
14
                self.handle.as_ptr(),
312
14
                cnt,
313
14
                HandlerFn::<F>::callback,
314
14
                &mut handler as *mut HandlerFn<AssertUnwindSafe<F>> as *mut u8,
315
            )
316
        };
317
14
        if let Some(e) = handler.panic_payload {
318
2
            resume_unwind(e);
319
12
        }
320
        // A successful pcap_dispatch returns the number of packets processed, not 0 like
321
        // pcap_loop.
322
12
        if return_code < 0 {
323
2
            return Err(self.status_err(return_code));
324
10
        }
325

            
326
10
        Ok(return_code as usize)
327
14
    }
328

            
329
    /// Returns a thread-safe `BreakLoop` handle for calling pcap_breakloop() on an active capture.
330
    ///
331
    /// # Example
332
    ///
333
    /// ```no_run
334
    /// // Using an active capture
335
    /// use pcap::Device;
336
    ///
337
    /// let mut cap = Device::lookup().unwrap().unwrap().open().unwrap();
338
    ///
339
    /// let break_handle = cap.breakloop_handle();
340
    ///
341
    /// let capture_thread = std::thread::spawn(move || {
342
    ///     while let Ok(packet) = cap.next_packet() {
343
    ///         println!("received packet! {:?}", packet);
344
    ///     }
345
    /// });
346
    ///
347
    /// // Send break_handle to a separate thread (e.g. user input, signal handler, etc.)
348
    /// std::thread::spawn(move || {
349
    ///     std::thread::sleep(std::time::Duration::from_secs(1));
350
    ///     break_handle.breakloop();
351
    /// });
352
    ///
353
    /// capture_thread.join().unwrap();
354
    /// ```
355
4
    pub fn breakloop_handle(&mut self) -> BreakLoop {
356
4
        BreakLoop {
357
4
            handle: Arc::<PcapHandle>::downgrade(&self.handle),
358
4
        }
359
4
    }
360

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

            
365
        unsafe {
366
22
            let mut bpf_program: raw::bpf_program = mem::zeroed();
367
22
            let ret = raw::pcap_compile(
368
22
                self.handle.as_ptr(),
369
22
                &mut bpf_program,
370
22
                program.as_ptr(),
371
22
                optimize as libc::c_int,
372
                0,
373
            );
374
22
            self.check_err(ret != -1).and(Ok(BpfProgram(bpf_program)))
375
        }
376
22
    }
377

            
378
    /// Sets the filter on the capture using the given BPF program string. Internally this is
379
    /// compiled using `pcap_compile()`. `optimize` controls whether optimization on the resulting
380
    /// code is performed
381
    ///
382
    /// See <http://biot.com/capstats/bpf.html> for more information about this syntax.
383
4
    pub fn filter(&mut self, program: &str, optimize: bool) -> Result<(), Error> {
384
4
        let mut bpf_program = self.compile(program, optimize)?;
385
4
        let ret = unsafe { raw::pcap_setfilter(self.handle.as_ptr(), &mut bpf_program.0) };
386
4
        self.check_err(ret != -1)
387
4
    }
388

            
389
    /// Get capture statistics about this capture. The values represent packet statistics from the
390
    /// start of the run to the time of the call.
391
    ///
392
    /// See <https://www.tcpdump.org/manpages/pcap_stats.3pcap.html> for per-platform caveats about
393
    /// how packet statistics are calculated.
394
6
    pub fn stats(&mut self) -> Result<Stat, Error> {
395
        unsafe {
396
6
            let mut stats: raw::pcap_stat = mem::zeroed();
397
6
            self.check_err(raw::pcap_stats(self.handle.as_ptr(), &mut stats) != -1)
398
7
                .map(|_| Stat::new(stats.ps_recv, stats.ps_drop, stats.ps_ifdrop))
399
        }
400
6
    }
401
}
402

            
403
// Handler and its associated function let us create an extern "C" fn which dispatches to a normal
404
// Rust FnMut, which may be a closure with a captured environment. The *only* purpose of this
405
// generic parameter is to ensure that in Capture::pcap_loop that we pass the right function
406
// pointer and the right data pointer to pcap_loop.
407
struct HandlerFn<F> {
408
    func: F,
409
    panic_payload: Option<Box<dyn Any + Send>>,
410
    handle: Arc<PcapHandle>,
411
}
412

            
413
impl<F> HandlerFn<F>
414
where
415
    F: FnMut(Packet),
416
{
417
28
    extern "C" fn callback(
418
28
        slf: *mut libc::c_uchar,
419
28
        header: *const raw::pcap_pkthdr,
420
28
        packet: *const libc::c_uchar,
421
28
    ) {
422
        unsafe {
423
28
            let packet = Packet::new(
424
28
                &*(header as *const PacketHeader),
425
28
                slice::from_raw_parts(packet, (*header).caplen as _),
426
            );
427

            
428
28
            let slf = slf as *mut Self;
429
28
            let func = &mut (*slf).func;
430
28
            let mut func = AssertUnwindSafe(func);
431
            // If our handler function panics, we need to prevent it from unwinding across the
432
            // FFI boundary. If the handler panics we catch the unwind here, break out of
433
            // pcap_loop, and resume the unwind outside.
434
28
            if let Err(e) = catch_unwind(move || func(packet)) {
435
6
                (*slf).panic_payload = Some(e);
436
6
                raw::pcap_breakloop((*slf).handle.as_ptr());
437
22
            }
438
        }
439
28
    }
440
}
441

            
442
impl<T: Activated> From<Capture<T>> for Capture<dyn Activated> {
443
36
    fn from(cap: Capture<T>) -> Capture<dyn Activated> {
444
36
        unsafe { mem::transmute(cap) }
445
36
    }
446
}
447

            
448
/// BreakLoop can safely be sent to other threads such as signal handlers to abort
449
/// blocking capture loops such as `Capture::next_packet` and `Capture::for_each`.
450
///
451
/// See <https://www.tcpdump.org/manpages/pcap_breakloop.3pcap.html> for per-platform caveats about
452
/// how breakloop can wake up blocked threads.
453
pub struct BreakLoop {
454
    handle: Weak<PcapHandle>,
455
}
456

            
457
unsafe impl Send for BreakLoop {}
458
unsafe impl Sync for BreakLoop {}
459

            
460
impl BreakLoop {
461
    /// Calls `pcap_breakloop` to make the blocking loop of a pcap capture return.
462
    /// The call is a no-op if the handle is invalid.
463
    ///
464
    /// # Safety
465
    ///
466
    /// Can be called from any thread, but **must not** be used inside a
467
    /// signal handler unless the owning `Capture` is guaranteed to still
468
    /// be alive.
469
    ///
470
    /// The signal handler should defer the execution of `BreakLoop::breakloop()`
471
    /// to a thread instead for safety.
472
8
    pub fn breakloop(&self) {
473
8
        if let Some(handle) = self.handle.upgrade() {
474
6
            unsafe { raw::pcap_breakloop(handle.as_ptr()) };
475
6
        }
476
8
    }
477
}
478

            
479
/// Abstraction for writing pcap savefiles, which can be read afterwards via `Capture::from_file()`.
480
pub struct Savefile {
481
    handle: NonNull<raw::pcap_dumper_t>,
482
}
483

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

            
489
impl Savefile {
490
    /// Write a packet to a capture file
491
    ///
492
    /// At most `packet.data.len()` bytes are written. A larger `caplen` in the header is capped
493
    /// to that, and `len` is raised to the number of bytes written if it is smaller.
494
564
    pub fn write(&mut self, packet: &Packet<'_>) {
495
564
        let caplen = packet
496
564
            .header
497
564
            .caplen
498
564
            .min(u32::try_from(packet.data.len()).unwrap_or(u32::MAX));
499
564
        let header = raw::pcap_pkthdr {
500
564
            ts: packet.header.ts,
501
564
            caplen,
502
564
            len: packet.header.len.max(caplen),
503
564
        };
504

            
505
564
        unsafe {
506
564
            raw::pcap_dump(self.handle.as_ptr() as _, &header, packet.data.as_ptr());
507
564
        }
508
564
    }
509

            
510
    /// Flushes all the packets that haven't been written to the savefile
511
8
    pub fn flush(&mut self) -> Result<(), Error> {
512
8
        if unsafe { raw::pcap_dump_flush(self.handle.as_ptr() as _) } != 0 {
513
2
            return Err(Error::ErrnoError(errno::errno()));
514
6
        }
515

            
516
6
        Ok(())
517
8
    }
518

            
519
    /// Get the current offset of the savefile, that is the number of bytes written so far,
520
    /// including any that are still buffered
521
16
    pub fn offset(&self) -> Result<u64, Error> {
522
        // Prior to 1.9.0 when `pcap_dump_ftell64` was introduced, the offset was only reported as
523
        // a `long`. Where that is a 32-bit type, as it is on Windows, the call fails once the
524
        // savefile has grown past 2 GB.
525
        #[cfg(libpcap_1_9_0)]
526
16
        let offset = unsafe { raw::pcap_dump_ftell64(self.handle.as_ptr()) };
527

            
528
        #[cfg(not(libpcap_1_9_0))]
529
        let offset = unsafe { raw::pcap_dump_ftell(self.handle.as_ptr()) };
530

            
531
16
        if offset < 0 {
532
2
            return Err(Error::ErrnoError(errno::errno()));
533
14
        }
534

            
535
14
        Ok(offset as u64)
536
16
    }
537

            
538
    /// Get the `FILE *` the savefile is being written to
539
    ///
540
    /// This is not available on Windows, where wpcap may be linked against a different C runtime
541
    /// than its caller and the `FILE *` would belong to the wrong one.
542
    ///
543
    /// # Safety
544
    ///
545
    /// The caller must ensure that the `Savefile` outlives the returned `FILE *` since it is
546
    /// closed when the `Savefile` is dropped.
547
    #[cfg(not(windows))]
548
6
    pub unsafe fn file(&self) -> *mut FILE {
549
6
        unsafe { raw::pcap_dump_file(self.handle.as_ptr()) }
550
6
    }
551
}
552

            
553
impl From<NonNull<raw::pcap_dumper_t>> for Savefile {
554
56
    fn from(handle: NonNull<raw::pcap_dumper_t>) -> Self {
555
56
        Savefile { handle }
556
56
    }
557
}
558

            
559
impl Drop for Savefile {
560
60
    fn drop(&mut self) {
561
60
        unsafe { raw::pcap_dump_close(self.handle.as_ptr()) }
562
60
    }
563
}
564

            
565
#[repr(transparent)]
566
pub struct BpfInstruction(raw::bpf_insn);
567
#[repr(transparent)]
568
pub struct BpfProgram(raw::bpf_program);
569

            
570
impl BpfProgram {
571
    /// checks whether a filter matches a packet
572
8
    pub fn filter(&self, buf: &[u8]) -> bool {
573
8
        let header: raw::pcap_pkthdr = raw::pcap_pkthdr {
574
8
            ts: libc::timeval {
575
8
                tv_sec: 0,
576
8
                tv_usec: 0,
577
8
            },
578
8
            caplen: buf.len() as u32,
579
8
            len: buf.len() as u32,
580
8
        };
581
8
        unsafe { raw::pcap_offline_filter(&self.0, &header, buf.as_ptr()) > 0 }
582
8
    }
583

            
584
16
    pub fn get_instructions(&self) -> &[BpfInstruction] {
585
        unsafe {
586
16
            slice::from_raw_parts(
587
16
                self.0.bf_insns as *const BpfInstruction,
588
16
                self.0.bf_len as usize,
589
16
            )
590
        }
591
16
    }
592
}
593

            
594
impl Drop for BpfProgram {
595
36
    fn drop(&mut self) {
596
36
        unsafe { raw::pcap_freecode(&mut self.0) }
597
36
    }
598
}
599

            
600
impl fmt::Display for BpfInstruction {
601
2
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
602
2
        write!(
603
2
            f,
604
            "{} {} {} {}",
605
            self.0.code, self.0.jt, self.0.jf, self.0.k
606
        )
607
2
    }
608
}
609

            
610
unsafe impl Send for BpfProgram {}
611

            
612
#[cfg(not(windows))]
613
/// Open a raw file descriptor.
614
///
615
/// # Safety
616
///
617
/// Unsafe, because the returned FILE assumes it is the sole owner of the file descriptor.
618
24
pub unsafe fn open_raw_fd(fd: RawFd, mode: u8) -> Result<*mut libc::FILE, Error> {
619
24
    let mode = [mode, 0];
620
24
    unsafe { libc::fdopen(fd, mode.as_ptr() as _).as_mut() }
621
27
        .map(|f| f as _)
622
24
        .ok_or(Error::InvalidRawFd)
623
24
}
624

            
625
// GRCOV_EXCL_START
626
#[cfg(test)]
627
pub mod testmod {
628
    use super::*;
629

            
630
    pub static TS: libc::timeval = libc::timeval {
631
        tv_sec: 5,
632
        tv_usec: 50,
633
    };
634
    pub static LEN: u32 = DATA.len() as u32;
635
    pub static CAPLEN: u32 = LEN;
636

            
637
    pub static mut PKTHDR: raw::pcap_pkthdr = raw::pcap_pkthdr {
638
        ts: TS,
639
        caplen: CAPLEN,
640
        len: LEN,
641
    };
642
    pub static PACKET_HEADER: PacketHeader = PacketHeader {
643
        ts: TS,
644
        caplen: CAPLEN,
645
        len: LEN,
646
    };
647

            
648
    pub static DATA: [u8; 4] = [4, 5, 6, 7];
649
    pub static PACKET: Packet = Packet {
650
        header: &PACKET_HEADER,
651
        data: &DATA,
652
    };
653

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

            
660
        let ctx = raw::pcap_next_ex_context();
661
        ctx.checkpoint();
662
        ctx.expect()
663
            .withf_st(move |arg1, _, _| *arg1 == pcap)
664
            .return_once_st(move |_, arg2, arg3| {
665
                unsafe {
666
                    *arg2 = pkthdr_ptr;
667
                    *arg3 = data_ptr;
668
                }
669
                CAPLEN as i32
670
            });
671

            
672
        NextExContext(ctx)
673
    }
674
}
675
// GRCOV_EXCL_STOP
676

            
677
#[cfg(test)]
678
mod tests {
679
    use crate::{
680
        capture::{
681
            Active, Capture, Offline,
682
            activated::testmod::{DATA, PACKET, TS, next_ex_expect},
683
            testmod::test_capture,
684
        },
685
        raw::testmod::{RAWMTX, as_file, as_pcap_dumper_t, as_pcap_t, geterr_expect},
686
    };
687

            
688
    use super::*;
689

            
690
    #[test]
691
    fn test_list_datalinks() {
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_list_datalinks_context();
701
        ctx.expect()
702
            .withf_st(move |arg1, _| *arg1 == pcap)
703
            .return_once_st(|_, _| 0);
704

            
705
        let ctx = raw::pcap_free_datalinks_context();
706
        ctx.expect().return_once(|_| {});
707

            
708
        let _err = geterr_expect(pcap);
709

            
710
        let result = capture.list_datalinks();
711
        assert!(result.is_err());
712

            
713
        let mut datalinks: [i32; 4] = [0, 1, 2, 3];
714
        let links: *mut i32 = datalinks.as_mut_ptr();
715
        let len = datalinks.len();
716

            
717
        let ctx = raw::pcap_list_datalinks_context();
718
        ctx.checkpoint();
719
        ctx.expect()
720
            .withf_st(move |arg1, _| *arg1 == pcap)
721
            .return_once_st(move |_, arg2| {
722
                unsafe { *arg2 = links };
723
                len as i32
724
            });
725

            
726
        let ctx = raw::pcap_free_datalinks_context();
727
        ctx.checkpoint();
728
        ctx.expect().return_once(|_| {});
729

            
730
        let pcap_datalinks = capture.list_datalinks().unwrap();
731
        assert_eq!(
732
            pcap_datalinks,
733
            datalinks.iter().cloned().map(Linktype).collect::<Vec<_>>()
734
        );
735
    }
736

            
737
    #[test]
738
    fn test_set_datalink() {
739
        let _m = RAWMTX.lock();
740

            
741
        let mut value: isize = 777;
742
        let pcap = as_pcap_t(&mut value);
743

            
744
        let test_capture = test_capture::<Active>(pcap);
745
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
746

            
747
        let ctx = raw::pcap_set_datalink_context();
748
        ctx.expect()
749
            .withf_st(move |arg1, _| *arg1 == pcap)
750
            .return_once(|_, _| 0);
751

            
752
        let result = capture.set_datalink(Linktype::ETHERNET);
753
        assert!(result.is_ok());
754

            
755
        let ctx = raw::pcap_set_datalink_context();
756
        ctx.checkpoint();
757
        ctx.expect()
758
            .withf_st(move |arg1, _| *arg1 == pcap)
759
            .return_once(|_, _| -1);
760

            
761
        let _err = geterr_expect(pcap);
762

            
763
        let result = capture.set_datalink(Linktype::ETHERNET);
764
        assert!(result.is_err());
765
    }
766

            
767
    #[test]
768
    fn test_get_datalink() {
769
        let _m = RAWMTX.lock();
770

            
771
        let mut value: isize = 777;
772
        let pcap = as_pcap_t(&mut value);
773

            
774
        let test_capture = test_capture::<Active>(pcap);
775
        let capture: Capture<dyn Activated> = test_capture.capture.into();
776

            
777
        let ctx = raw::pcap_datalink_context();
778
        ctx.expect()
779
            .withf_st(move |arg1| *arg1 == pcap)
780
            .return_once(|_| 1);
781

            
782
        let linktype = capture.get_datalink();
783
        assert_eq!(linktype, Linktype::ETHERNET);
784
    }
785

            
786
    #[test]
787
    fn test_snaplen() {
788
        let _m = RAWMTX.lock();
789

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

            
793
        let test_capture = test_capture::<Active>(pcap);
794
        let capture: Capture<dyn Activated> = test_capture.capture.into();
795

            
796
        let ctx = raw::pcap_snapshot_context();
797
        ctx.expect()
798
            .withf_st(move |arg1| *arg1 == pcap)
799
            .return_once(|_| 65535);
800

            
801
        assert_eq!(capture.snaplen(), 65535);
802
    }
803

            
804
    #[test]
805
    fn unify_activated() {
806
        #![allow(dead_code)]
807
        fn test1() -> Capture<Active> {
808
            panic!();
809
        }
810

            
811
        fn test2() -> Capture<Offline> {
812
            panic!();
813
        }
814

            
815
        fn maybe(a: bool) -> Capture<dyn Activated> {
816
            if a { test1().into() } else { test2().into() }
817
        }
818

            
819
        fn also_maybe(a: &mut Capture<dyn Activated>) {
820
            a.filter("whatever filter string, this won't be run anyway", false)
821
                .unwrap();
822
        }
823
    }
824

            
825
    #[test]
826
    fn test_breakloop_capture_dropped() {
827
        let _m = RAWMTX.lock();
828

            
829
        let mut value: isize = 1234;
830
        let pcap = as_pcap_t(&mut value);
831

            
832
        let test_capture = test_capture::<Active>(pcap);
833
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
834

            
835
        let ctx = raw::pcap_breakloop_context();
836
        ctx.expect()
837
            .withf_st(move |h| *h == pcap)
838
            .return_const(())
839
            .times(1);
840

            
841
        let break_handle = capture.breakloop_handle();
842

            
843
        break_handle.breakloop();
844

            
845
        drop(capture);
846

            
847
        break_handle.breakloop(); // this call does not trigger mock after drop
848
    }
849

            
850
    #[test]
851
    fn test_savefile() {
852
        let _m = RAWMTX.lock();
853

            
854
        let mut value: isize = 777;
855
        let pcap = as_pcap_t(&mut value);
856

            
857
        let mut value: isize = 888;
858
        let pcap_dumper = as_pcap_dumper_t(&mut value);
859

            
860
        let test_capture = test_capture::<Offline>(pcap);
861
        let capture = test_capture.capture;
862

            
863
        let ctx = raw::pcap_dump_open_context();
864
        ctx.expect()
865
            .withf_st(move |arg1, _| *arg1 == pcap)
866
            .return_once_st(move |_, _| pcap_dumper);
867

            
868
        let ctx = raw::pcap_dump_close_context();
869
        ctx.expect()
870
            .withf_st(move |arg1| *arg1 == pcap_dumper)
871
            .return_once(|_| {});
872

            
873
        let result = capture.savefile("path/to/nowhere");
874
        assert!(result.is_ok());
875
    }
876

            
877
    #[test]
878
    #[cfg(libpcap_1_7_2)]
879
    fn test_savefile_append() {
880
        let _m = RAWMTX.lock();
881

            
882
        let mut value: isize = 777;
883
        let pcap = as_pcap_t(&mut value);
884

            
885
        let mut value: isize = 888;
886
        let pcap_dumper = as_pcap_dumper_t(&mut value);
887

            
888
        let test_capture = test_capture::<Offline>(pcap);
889
        let capture = test_capture.capture;
890

            
891
        let ctx = raw::pcap_dump_open_append_context();
892
        ctx.expect()
893
            .withf_st(move |arg1, _| *arg1 == pcap)
894
            .return_once_st(move |_, _| pcap_dumper);
895

            
896
        let ctx = raw::pcap_dump_close_context();
897
        ctx.expect()
898
            .withf_st(move |arg1| *arg1 == pcap_dumper)
899
            .return_once(|_| {});
900

            
901
        let result = capture.savefile_append("path/to/nowhere");
902
        assert!(result.is_ok());
903
    }
904

            
905
    #[test]
906
    fn test_savefile_error() {
907
        let _m = RAWMTX.lock();
908

            
909
        let mut value: isize = 777;
910
        let pcap = as_pcap_t(&mut value);
911

            
912
        let test_capture = test_capture::<Offline>(pcap);
913
        let capture = test_capture.capture;
914

            
915
        let ctx = raw::pcap_dump_open_context();
916
        ctx.expect()
917
            .withf_st(move |arg1, _| *arg1 == pcap)
918
            .return_once(|_, _| std::ptr::null_mut());
919

            
920
        let _err = geterr_expect(pcap);
921

            
922
        let result = capture.savefile("path/to/nowhere");
923
        assert!(result.is_err());
924
    }
925

            
926
    #[test]
927
    #[cfg(libpcap_1_7_2)]
928
    fn test_savefile_append_error() {
929
        let _m = RAWMTX.lock();
930

            
931
        let mut value: isize = 777;
932
        let pcap = as_pcap_t(&mut value);
933

            
934
        let test_capture = test_capture::<Offline>(pcap);
935
        let capture = test_capture.capture;
936

            
937
        let ctx = raw::pcap_dump_open_append_context();
938
        ctx.expect()
939
            .withf_st(move |arg1, _| *arg1 == pcap)
940
            .return_once(|_, _| std::ptr::null_mut());
941

            
942
        let _err = geterr_expect(pcap);
943

            
944
        let result = capture.savefile_append("path/to/nowhere");
945
        assert!(result.is_err());
946
    }
947

            
948
    #[cfg(libpcap_1_9_0)]
949
    struct DumpFtellExpect(raw::__pcap_dump_ftell64::Context);
950

            
951
    #[cfg(not(libpcap_1_9_0))]
952
    struct DumpFtellExpect(raw::__pcap_dump_ftell::Context);
953

            
954
    fn dump_ftell_expect(pcap_dumper: *mut raw::pcap_dumper_t, offset: i64) -> DumpFtellExpect {
955
        // Lock must be acquired by caller.
956
        assert!(RAWMTX.try_lock().is_err());
957

            
958
        #[cfg(libpcap_1_9_0)]
959
        {
960
            let ctx = raw::pcap_dump_ftell64_context();
961
            ctx.checkpoint();
962
            ctx.expect()
963
                .withf_st(move |arg1| *arg1 == pcap_dumper)
964
                .return_once(move |_| offset);
965
            DumpFtellExpect(ctx)
966
        }
967
        #[cfg(not(libpcap_1_9_0))]
968
        {
969
            let ctx = raw::pcap_dump_ftell_context();
970
            ctx.checkpoint();
971
            ctx.expect()
972
                .withf_st(move |arg1| *arg1 == pcap_dumper)
973
                .return_once(move |_| offset as _);
974
            DumpFtellExpect(ctx)
975
        }
976
    }
977

            
978
    #[test]
979
    fn test_savefile_ops() {
980
        let _m = RAWMTX.lock();
981

            
982
        let mut value: isize = 888;
983
        let pcap_dumper = as_pcap_dumper_t(&mut value);
984

            
985
        let ctx = raw::pcap_dump_close_context();
986
        ctx.expect()
987
            .withf_st(move |arg1| *arg1 == pcap_dumper)
988
            .return_once(|_| {});
989

            
990
        let mut savefile = Savefile {
991
            handle: NonNull::new(pcap_dumper).unwrap(),
992
        };
993

            
994
        let ctx = raw::pcap_dump_context();
995
        ctx.expect()
996
            .withf_st(move |arg1, _, _| *arg1 == pcap_dumper as _)
997
            .return_once(|_, _, _| {});
998

            
999
        savefile.write(&PACKET);
        let ctx = raw::pcap_dump_flush_context();
        ctx.expect()
            .withf_st(move |arg1| *arg1 == pcap_dumper)
            .return_once(|_| 0);
        let result = savefile.flush();
        assert!(result.is_ok());
        let ctx = raw::pcap_dump_flush_context();
        ctx.checkpoint();
        ctx.expect()
            .withf_st(move |arg1| *arg1 == pcap_dumper)
            .return_once(|_| -1);
        let result = savefile.flush();
        assert!(result.is_err());
        let _ctx = dump_ftell_expect(pcap_dumper, 6144);
        let result = savefile.offset();
        assert_eq!(result.unwrap(), 6144);
        let _ctx = dump_ftell_expect(pcap_dumper, -1);
        let result = savefile.offset();
        assert!(result.is_err());
        #[cfg(not(windows))]
        {
            let mut dummy: isize = 999;
            let file = &mut dummy as *mut isize as *mut FILE;
            let ctx = raw::pcap_dump_file_context();
            ctx.expect()
                .withf_st(move |arg1| *arg1 == pcap_dumper)
                .return_once_st(move |_| file);
            assert_eq!(unsafe { savefile.file() }, file);
        }
    }
    // A caplen past the end of the data, and a len shorter than what is written, are covered by
    // the savefile tests. What is left to check here is that a truncated packet, which is what a
    // caplen within the data means, still goes through with the header it came with.
    #[test]
    fn test_savefile_write_truncated() {
        let _m = RAWMTX.lock();
        let mut value: isize = 888;
        let pcap_dumper = as_pcap_dumper_t(&mut value);
        let ctx = raw::pcap_dump_close_context();
        ctx.expect()
            .withf_st(move |arg1| *arg1 == pcap_dumper)
            .return_once(|_| {});
        let mut savefile = Savefile {
            handle: NonNull::new(pcap_dumper).unwrap(),
        };
        let header = PacketHeader {
            ts: TS,
            caplen: 2,
            len: 60,
        };
        let ctx = raw::pcap_dump_context();
        ctx.expect()
            .withf_st(move |arg1, arg2, arg3| {
                let header = unsafe { &**arg2 };
                *arg1 == pcap_dumper as _
                    && *arg3 == DATA.as_ptr()
                    && header.caplen == 2
                    && header.len == 60
                    && header.ts.tv_sec == TS.tv_sec
                    && header.ts.tv_usec == TS.tv_usec
            })
            .return_once(|_, _, _| {});
        savefile.write(&Packet::new(&header, &DATA));
    }
    #[test]
    fn test_direction() {
        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_setdirection_context();
        ctx.expect()
            .withf_st(move |arg1, arg2| (*arg1 == pcap) && (*arg2 == raw::PCAP_D_OUT))
            .return_once(|_, _| 0);
        let result = capture.direction(Direction::Out);
        assert!(result.is_ok());
        let ctx = raw::pcap_setdirection_context();
        ctx.checkpoint();
        ctx.expect()
            .withf_st(move |arg1, arg2| (*arg1 == pcap) && (*arg2 == raw::PCAP_D_OUT))
            .return_once(|_, _| -1);
        let _err = geterr_expect(pcap);
        let result = capture.direction(Direction::Out);
        assert!(result.is_err());
        // For code coverage of the derive line.
        assert_ne!(Direction::In, Direction::InOut);
        assert_ne!(Direction::In, Direction::Out);
        assert_ne!(Direction::InOut, Direction::Out);
    }
    #[test]
    fn test_next_packet() {
        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 _nxt = next_ex_expect(pcap);
        let next_packet = capture.next_packet().unwrap();
        assert_eq!(next_packet, PACKET);
    }
    #[test]
    fn test_next_packet_timeout() {
        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_next_ex_context();
        ctx.expect()
            .withf_st(move |arg1, _, _| *arg1 == pcap)
            .return_once_st(move |_, _, _| 0);
        let err = capture.next_packet().unwrap_err();
        assert_eq!(err, Error::TimeoutExpired);
    }
    #[test]
    fn test_next_packet_read_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 = test_capture.capture;
        let ctx = raw::pcap_next_ex_context();
        ctx.expect()
            .withf_st(move |arg1, _, _| *arg1 == pcap)
            .return_once_st(move |_, _, _| -1);
        let _err = geterr_expect(pcap);
        let result = capture.next_packet();
        assert!(result.is_err());
    }
    #[test]
    fn test_next_packet_no_more_packets() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Offline>(pcap);
        let mut capture = test_capture.capture;
        let ctx = raw::pcap_next_ex_context();
        ctx.expect()
            .withf_st(move |arg1, _, _| *arg1 == pcap)
            .return_once_st(move |_, _, _| -2);
        let mut value: isize = 888;
        let file = as_file(&mut value);
        let ctx = raw::pcap_file_context();
        ctx.expect()
            .withf_st(move |arg1| *arg1 == pcap)
            .return_once_st(move |_| file);
        let err = capture.next_packet().unwrap_err();
        assert_eq!(err, Error::NoMorePackets);
    }
    #[test]
    fn test_next_packet_broken_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 = test_capture.capture;
        let ctx = raw::pcap_next_ex_context();
        ctx.expect()
            .withf_st(move |arg1, _, _| *arg1 == pcap)
            .return_once_st(move |_, _, _| -2);
        // An interface reads no savefile, so the -2 came from pcap_breakloop.
        let ctx = raw::pcap_file_context();
        ctx.expect()
            .withf_st(move |arg1| *arg1 == pcap)
            .return_once_st(|_| ptr::null_mut());
        // The message left over from an earlier failure is not the reason the loop stopped.
        let _err = geterr_expect(pcap);
        let err = capture.next_packet().unwrap_err();
        assert_eq!(
            err,
            Error::PcapErrorCode(crate::ErrorCode::Break, String::new())
        );
    }
    #[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]
    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 |_| {});
        // Catch the unwind here instead of letting it leave the test. mockall skips its
        // checkpoint while a thread is panicking, so the expectations above would stay
        // registered for whichever test runs next to drop on the wrong thread.
        let panic = catch_unwind(AssertUnwindSafe(|| {
            capture
                .for_each(None, |_| panic!("panic in callback"))
                .unwrap()
        }))
        .unwrap_err();
        assert_eq!(*panic.downcast_ref::<&str>().unwrap(), "panic in callback");
    }
    #[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 loop_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_loop_context();
        ctx.expect()
            .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == -1)
            .return_once_st(move |_, _, _, _| raw::PCAP_ERROR_BREAK);
        // The message left over from an earlier failure is not the reason the loop stopped.
        let _err = geterr_expect(pcap);
        let result = capture.for_each(None, |_| {});
        assert_eq!(
            result.unwrap_err(),
            Error::PcapErrorCode(crate::ErrorCode::Break, String::new())
        );
    }
    #[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]
    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 |_| {});
        // Catch the unwind here instead of letting it leave the test. mockall skips its
        // checkpoint while a thread is panicking, so the expectations above would stay
        // registered for whichever test runs next to drop on the wrong thread.
        let panic = catch_unwind(AssertUnwindSafe(|| {
            capture
                .dispatch(None, |_| panic!("panic in callback"))
                .unwrap()
        }))
        .unwrap_err();
        assert_eq!(*panic.downcast_ref::<&str>().unwrap(), "panic in callback");
    }
    #[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());
    }
}