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,
11
    mem::{self, MaybeUninit},
12
    panic::{AssertUnwindSafe, catch_unwind, resume_unwind},
13
    path::Path,
14
    ptr::{self, NonNull},
15
    slice,
16
    sync::{Arc, Weak},
17
};
18

            
19
#[cfg(not(windows))]
20
use std::os::unix::io::RawFd;
21

            
22
#[cfg(not(windows))]
23
use libc::FILE;
24

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

            
34
use iterator::PacketIter;
35

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

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

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

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

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

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

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

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

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

            
147
    /// Reopen a `Savefile` context for recording captured packets using this `Capture`'s
148
    /// configurations. This is similar to `savefile()` but does not create the file if it
149
    /// does  not exist and, if it does already exist, and is a pcap file with the same
150
    /// byte order as the host opening the file, and has the same timestamp precision,
151
    /// link-layer header type,  and  snapshot length as p, it will write new packets
152
    /// at the end of the file.
153
    ///
154
    /// On Windows a path outside ASCII needs `init(CharEncoding::Utf8)` first. The path always
155
    /// reaches libpcap as UTF-8, but until that call libpcap reads it in the local code page,
156
    /// which on most systems is not UTF-8: the name gets mangled and the file lands elsewhere.
157
    #[cfg(libpcap_1_7_2)]
158
3
    pub fn savefile_append<P: AsRef<Path>>(&self, path: P) -> Result<Savefile, Error> {
159
        #[cfg(windows)]
160
        if !raw::has_dump_append() {
161
            return Err(Error::EntrypointNotFound("pcap_dump_open_append"));
162
        }
163

            
164
3
        let name = path_to_cstring(path.as_ref())?;
165
3
        let handle_opt = NonNull::<raw::pcap_dumper_t>::new(unsafe {
166
3
            raw::pcap_dump_open_append(self.handle.as_ptr(), name.as_ptr())
167
        });
168
3
        let handle = self
169
3
            .check_err(handle_opt.is_some())
170
3
            .map(|_| handle_opt.unwrap())?;
171
2
        Ok(Savefile { handle })
172
3
    }
173

            
174
    /// Set the direction of the capture
175
2
    pub fn direction(&self, direction: Direction) -> Result<(), Error> {
176
2
        self.check_err(unsafe {
177
2
            raw::pcap_setdirection(self.handle.as_ptr(), direction as u32 as _) == 0
178
        })
179
2
    }
180

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

            
239
    /// Return an iterator that call [`Self::next_packet()`] forever. Require a [`PacketCodec`]
240
4
    pub fn iter<C: PacketCodec>(self, codec: C) -> PacketIter<T, C> {
241
4
        PacketIter::new(self, codec)
242
4
    }
243

            
244
8
    pub fn for_each<F>(&mut self, count: Option<usize>, handler: F) -> Result<(), Error>
245
8
    where
246
8
        F: FnMut(Packet),
247
    {
248
8
        let cnt = match count {
249
            // Actually passing 0 down to pcap_loop would mean read forever.
250
            // We interpret it as "read nothing", so we just succeed immediately.
251
1
            Some(0) => return Ok(()),
252
1
            Some(cnt) => cnt
253
1
                .try_into()
254
1
                .expect("count of packets to read cannot exceed c_int::MAX"),
255
6
            None => -1,
256
        };
257

            
258
7
        let mut handler = HandlerFn {
259
7
            func: AssertUnwindSafe(handler),
260
7
            panic_payload: None,
261
7
            handle: self.handle.clone(),
262
7
        };
263
7
        let return_code = unsafe {
264
7
            raw::pcap_loop(
265
7
                self.handle.as_ptr(),
266
7
                cnt,
267
7
                HandlerFn::<F>::callback,
268
7
                &mut handler as *mut HandlerFn<AssertUnwindSafe<F>> as *mut u8,
269
            )
270
        };
271
7
        if let Some(e) = handler.panic_payload {
272
2
            resume_unwind(e);
273
5
        }
274
5
        if return_code < 0 {
275
2
            return Err(self.status_err(return_code));
276
3
        }
277

            
278
3
        Ok(())
279
6
    }
280

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

            
311
7
        let mut handler = HandlerFn {
312
7
            func: AssertUnwindSafe(handler),
313
7
            panic_payload: None,
314
7
            handle: self.handle.clone(),
315
7
        };
316
7
        let return_code = unsafe {
317
7
            raw::pcap_dispatch(
318
7
                self.handle.as_ptr(),
319
7
                cnt,
320
7
                HandlerFn::<F>::callback,
321
7
                &mut handler as *mut HandlerFn<AssertUnwindSafe<F>> as *mut u8,
322
            )
323
        };
324
7
        if let Some(e) = handler.panic_payload {
325
1
            resume_unwind(e);
326
6
        }
327
        // A successful pcap_dispatch returns the number of packets processed, not 0 like
328
        // pcap_loop.
329
6
        if return_code < 0 {
330
1
            return Err(self.status_err(return_code));
331
5
        }
332

            
333
5
        Ok(return_code as usize)
334
7
    }
335

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

            
368
    /// Compiles the string into a filter program using `pcap_compile`.
369
11
    pub fn compile(&self, program: &str, optimize: bool) -> Result<BpfProgram, Error> {
370
11
        let program = CString::new(program)?;
371

            
372
        unsafe {
373
11
            let mut bpf_program: raw::bpf_program = mem::zeroed();
374
11
            let ret = raw::pcap_compile(
375
11
                self.handle.as_ptr(),
376
11
                &mut bpf_program,
377
11
                program.as_ptr(),
378
11
                optimize as libc::c_int,
379
                0,
380
            );
381
11
            self.check_err(ret != -1).and(Ok(BpfProgram(bpf_program)))
382
        }
383
11
    }
384

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

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

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

            
420
impl<F> HandlerFn<F>
421
where
422
    F: FnMut(Packet),
423
{
424
14
    extern "C" fn callback(
425
14
        slf: *mut libc::c_uchar,
426
14
        header: *const raw::pcap_pkthdr,
427
14
        packet: *const libc::c_uchar,
428
14
    ) {
429
        unsafe {
430
14
            let mut converted = MaybeUninit::uninit();
431
14
            let header = PacketHeader::from_raw(header, &mut converted);
432
14
            let packet = Packet::new(header, slice::from_raw_parts(packet, header.caplen as _));
433

            
434
14
            let slf = slf as *mut Self;
435
14
            let func = &mut (*slf).func;
436
14
            let mut func = AssertUnwindSafe(func);
437
            // If our handler function panics, we need to prevent it from unwinding across the
438
            // FFI boundary. If the handler panics we catch the unwind here, break out of
439
            // pcap_loop, and resume the unwind outside.
440
14
            if let Err(e) = catch_unwind(move || func(packet)) {
441
3
                (*slf).panic_payload = Some(e);
442
3
                raw::pcap_breakloop((*slf).handle.as_ptr());
443
11
            }
444
        }
445
14
    }
446
}
447

            
448
impl<T: Activated> From<Capture<T>> for Capture<dyn Activated> {
449
18
    fn from(cap: Capture<T>) -> Capture<dyn Activated> {
450
18
        unsafe { mem::transmute(cap) }
451
18
    }
452
}
453

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

            
463
unsafe impl Send for BreakLoop {}
464
unsafe impl Sync for BreakLoop {}
465

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

            
485
/// Abstraction for writing pcap savefiles, which can be read afterwards via `Capture::from_file()`.
486
pub struct Savefile {
487
    handle: NonNull<raw::pcap_dumper_t>,
488
}
489

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

            
495
impl Savefile {
496
    /// Write a packet to a capture file
497
    ///
498
    /// At most `packet.data.len()` bytes are written. A larger `caplen` in the header is capped
499
    /// to that, and `len` is raised to the number of bytes written if it is smaller.
500
422
    pub fn write(&mut self, packet: &Packet<'_>) {
501
422
        let caplen = packet
502
422
            .header
503
422
            .caplen
504
422
            .min(u32::try_from(packet.data.len()).unwrap_or(u32::MAX));
505
422
        let header = PacketHeader {
506
422
            ts: packet.header.ts,
507
422
            caplen,
508
422
            len: packet.header.len.max(caplen),
509
422
        };
510
422
        let mut converted = MaybeUninit::uninit();
511

            
512
422
        unsafe {
513
422
            raw::pcap_dump(
514
422
                self.handle.as_ptr() as _,
515
422
                header.as_raw(&mut converted),
516
422
                packet.data.as_ptr(),
517
422
            );
518
422
        }
519
422
    }
520

            
521
    /// Flushes all the packets that haven't been written to the savefile
522
5
    pub fn flush(&mut self) -> Result<(), Error> {
523
5
        if unsafe { raw::pcap_dump_flush(self.handle.as_ptr() as _) } != 0 {
524
1
            return Err(Error::ErrnoError(errno::errno()));
525
4
        }
526

            
527
4
        Ok(())
528
5
    }
529

            
530
    /// Get the current offset of the savefile, that is the number of bytes written so far,
531
    /// including any that are still buffered
532
11
    pub fn offset(&self) -> Result<u64, Error> {
533
        // Prior to 1.9.0 when `pcap_dump_ftell64` was introduced, the offset was only reported as
534
        // a `long`. Where that is a 32-bit type, as it is on Windows, the call fails once the
535
        // savefile has grown past 2 GB.
536
        #[cfg(windows)]
537
        if !raw::has_dump_ftell64() {
538
            return Err(Error::EntrypointNotFound("pcap_dump_ftell64"));
539
        }
540

            
541
        #[cfg(libpcap_1_9_0)]
542
11
        let offset = unsafe { raw::pcap_dump_ftell64(self.handle.as_ptr()) };
543

            
544
        #[cfg(not(libpcap_1_9_0))]
545
        let offset = unsafe { raw::pcap_dump_ftell(self.handle.as_ptr()) };
546

            
547
11
        if offset < 0 {
548
1
            return Err(Error::ErrnoError(errno::errno()));
549
10
        }
550

            
551
10
        Ok(offset as u64)
552
11
    }
553

            
554
    /// Get the `FILE *` the savefile is being written to
555
    ///
556
    /// This is not available on Windows, where wpcap may be linked against a different C runtime
557
    /// than its caller and the `FILE *` would belong to the wrong one.
558
    ///
559
    /// # Safety
560
    ///
561
    /// The caller must ensure that the `Savefile` outlives the returned `FILE *` since it is
562
    /// closed when the `Savefile` is dropped.
563
    #[cfg(not(windows))]
564
4
    pub unsafe fn file(&self) -> *mut FILE {
565
4
        unsafe { raw::pcap_dump_file(self.handle.as_ptr()) }
566
4
    }
567
}
568

            
569
impl Drop for Savefile {
570
43
    fn drop(&mut self) {
571
43
        unsafe { raw::pcap_dump_close(self.handle.as_ptr()) }
572
43
    }
573
}
574

            
575
#[repr(transparent)]
576
pub struct BpfInstruction(raw::bpf_insn);
577
#[repr(transparent)]
578
pub struct BpfProgram(raw::bpf_program);
579

            
580
impl BpfProgram {
581
    /// checks whether a filter matches a packet
582
6
    pub fn filter(&self, buf: &[u8]) -> bool {
583
6
        let header = PacketHeader {
584
6
            ts: libc::timeval {
585
6
                tv_sec: 0,
586
6
                tv_usec: 0,
587
6
            },
588
6
            caplen: buf.len() as u32,
589
6
            len: buf.len() as u32,
590
6
        };
591
6
        let mut converted = MaybeUninit::uninit();
592
        unsafe {
593
6
            raw::pcap_offline_filter(&self.0, header.as_raw(&mut converted), buf.as_ptr()) > 0
594
        }
595
6
    }
596

            
597
12
    pub fn get_instructions(&self) -> &[BpfInstruction] {
598
        unsafe {
599
12
            slice::from_raw_parts(
600
12
                self.0.bf_insns as *const BpfInstruction,
601
12
                self.0.bf_len as usize,
602
12
            )
603
        }
604
12
    }
605
}
606

            
607
impl Drop for BpfProgram {
608
25
    fn drop(&mut self) {
609
25
        unsafe { raw::pcap_freecode(&mut self.0) }
610
25
    }
611
}
612

            
613
impl fmt::Display for BpfInstruction {
614
1
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
615
1
        write!(
616
1
            f,
617
            "{} {} {} {}",
618
            self.0.code, self.0.jt, self.0.jf, self.0.k
619
        )
620
1
    }
621
}
622

            
623
unsafe impl Send for BpfProgram {}
624

            
625
#[cfg(not(windows))]
626
/// Open a raw file descriptor.
627
///
628
/// # Safety
629
///
630
/// Unsafe, because the returned FILE assumes it is the sole owner of the file descriptor.
631
18
pub unsafe fn open_raw_fd(fd: RawFd, mode: u8) -> Result<*mut libc::FILE, Error> {
632
18
    let mode = [mode, 0];
633
18
    unsafe { libc::fdopen(fd, mode.as_ptr() as _).as_mut() }
634
18
        .map(|f| f as _)
635
18
        .ok_or(Error::InvalidRawFd)
636
18
}
637

            
638
// GRCOV_EXCL_START
639
#[cfg(test)]
640
pub mod testmod {
641
    use super::*;
642

            
643
    pub static TS: libc::timeval = libc::timeval {
644
        tv_sec: 5,
645
        tv_usec: 50,
646
    };
647
    pub static LEN: u32 = DATA.len() as u32;
648
    pub static CAPLEN: u32 = LEN;
649

            
650
    pub static mut PKTHDR: raw::pcap_pkthdr = raw::pcap_pkthdr {
651
        ts: TS,
652
        caplen: CAPLEN,
653
        len: LEN,
654
    };
655
    pub static PACKET_HEADER: PacketHeader = PacketHeader {
656
        ts: TS,
657
        caplen: CAPLEN,
658
        len: LEN,
659
    };
660

            
661
    pub static DATA: [u8; 4] = [4, 5, 6, 7];
662
    pub static PACKET: Packet = Packet {
663
        header: &PACKET_HEADER,
664
        data: &DATA,
665
    };
666

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

            
673
        let ctx = raw::pcap_next_ex_context();
674
        ctx.checkpoint();
675
        ctx.expect()
676
            .withf_st(move |arg1, _, _| *arg1 == pcap)
677
            .return_once_st(move |_, arg2, arg3| {
678
                unsafe {
679
                    *arg2 = pkthdr_ptr;
680
                    *arg3 = data_ptr;
681
                }
682
                CAPLEN as i32
683
            });
684

            
685
        NextExContext(ctx)
686
    }
687
}
688
// GRCOV_EXCL_STOP
689

            
690
#[cfg(test)]
691
mod tests {
692
    use crate::{
693
        capture::{
694
            Active, Capture, Offline,
695
            activated::testmod::{DATA, PACKET, TS, next_ex_expect},
696
            testmod::test_capture,
697
        },
698
        packet::{self, Layout},
699
        raw::testmod::{RAWMTX, as_file, as_pcap_dumper_t, as_pcap_t, geterr_expect},
700
    };
701

            
702
    use super::*;
703

            
704
    #[test]
705
    fn test_list_datalinks() {
706
        let _m = RAWMTX.lock();
707

            
708
        let mut value: isize = 777;
709
        let pcap = as_pcap_t(&mut value);
710

            
711
        let test_capture = test_capture::<Active>(pcap);
712
        let capture: Capture<dyn Activated> = test_capture.capture.into();
713

            
714
        let ctx = raw::pcap_list_datalinks_context();
715
        ctx.expect()
716
            .withf_st(move |arg1, _| *arg1 == pcap)
717
            .return_once_st(|_, _| 0);
718

            
719
        let ctx = raw::pcap_free_datalinks_context();
720
        ctx.expect().return_once(|_| {});
721

            
722
        let _err = geterr_expect(pcap);
723

            
724
        let result = capture.list_datalinks();
725
        assert!(result.is_err());
726

            
727
        let mut datalinks: [i32; 4] = [0, 1, 2, 3];
728
        let links: *mut i32 = datalinks.as_mut_ptr();
729
        let len = datalinks.len();
730

            
731
        let ctx = raw::pcap_list_datalinks_context();
732
        ctx.checkpoint();
733
        ctx.expect()
734
            .withf_st(move |arg1, _| *arg1 == pcap)
735
            .return_once_st(move |_, arg2| {
736
                unsafe { *arg2 = links };
737
                len as i32
738
            });
739

            
740
        let ctx = raw::pcap_free_datalinks_context();
741
        ctx.checkpoint();
742
        ctx.expect().return_once(|_| {});
743

            
744
        let pcap_datalinks = capture.list_datalinks().unwrap();
745
        assert_eq!(
746
            pcap_datalinks,
747
            datalinks.iter().cloned().map(Linktype).collect::<Vec<_>>()
748
        );
749
    }
750

            
751
    #[test]
752
    fn test_set_datalink() {
753
        let _m = RAWMTX.lock();
754

            
755
        let mut value: isize = 777;
756
        let pcap = as_pcap_t(&mut value);
757

            
758
        let test_capture = test_capture::<Active>(pcap);
759
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
760

            
761
        let ctx = raw::pcap_set_datalink_context();
762
        ctx.expect()
763
            .withf_st(move |arg1, _| *arg1 == pcap)
764
            .return_once(|_, _| 0);
765

            
766
        let result = capture.set_datalink(Linktype::ETHERNET);
767
        assert!(result.is_ok());
768

            
769
        let ctx = raw::pcap_set_datalink_context();
770
        ctx.checkpoint();
771
        ctx.expect()
772
            .withf_st(move |arg1, _| *arg1 == pcap)
773
            .return_once(|_, _| -1);
774

            
775
        let _err = geterr_expect(pcap);
776

            
777
        let result = capture.set_datalink(Linktype::ETHERNET);
778
        assert!(result.is_err());
779
    }
780

            
781
    #[test]
782
    fn test_get_datalink() {
783
        let _m = RAWMTX.lock();
784

            
785
        let mut value: isize = 777;
786
        let pcap = as_pcap_t(&mut value);
787

            
788
        let test_capture = test_capture::<Active>(pcap);
789
        let capture: Capture<dyn Activated> = test_capture.capture.into();
790

            
791
        let ctx = raw::pcap_datalink_context();
792
        ctx.expect()
793
            .withf_st(move |arg1| *arg1 == pcap)
794
            .return_once(|_| 1);
795

            
796
        let linktype = capture.get_datalink();
797
        assert_eq!(linktype, Linktype::ETHERNET);
798
    }
799

            
800
    #[test]
801
    fn test_snaplen() {
802
        let _m = RAWMTX.lock();
803

            
804
        let mut value: isize = 777;
805
        let pcap = as_pcap_t(&mut value);
806

            
807
        let test_capture = test_capture::<Active>(pcap);
808
        let capture: Capture<dyn Activated> = test_capture.capture.into();
809

            
810
        let ctx = raw::pcap_snapshot_context();
811
        ctx.expect()
812
            .withf_st(move |arg1| *arg1 == pcap)
813
            .return_once(|_| 65535);
814

            
815
        assert_eq!(capture.snaplen(), 65535);
816
    }
817

            
818
    #[test]
819
    fn unify_activated() {
820
        #![allow(dead_code)]
821
        fn test1() -> Capture<Active> {
822
            panic!();
823
        }
824

            
825
        fn test2() -> Capture<Offline> {
826
            panic!();
827
        }
828

            
829
        fn maybe(a: bool) -> Capture<dyn Activated> {
830
            if a { test1().into() } else { test2().into() }
831
        }
832

            
833
        fn also_maybe(a: &mut Capture<dyn Activated>) {
834
            a.filter("whatever filter string, this won't be run anyway", false)
835
                .unwrap();
836
        }
837
    }
838

            
839
    #[test]
840
    fn test_breakloop_capture_dropped() {
841
        let _m = RAWMTX.lock();
842

            
843
        let mut value: isize = 1234;
844
        let pcap = as_pcap_t(&mut value);
845

            
846
        let test_capture = test_capture::<Active>(pcap);
847
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
848

            
849
        let ctx = raw::pcap_breakloop_context();
850
        ctx.expect()
851
            .withf_st(move |h| *h == pcap)
852
            .return_const(())
853
            .times(1);
854

            
855
        let break_handle = capture.breakloop_handle();
856

            
857
        break_handle.breakloop();
858

            
859
        drop(capture);
860

            
861
        break_handle.breakloop(); // this call does not trigger mock after drop
862
    }
863

            
864
    #[test]
865
    fn test_savefile() {
866
        let _m = RAWMTX.lock();
867

            
868
        let mut value: isize = 777;
869
        let pcap = as_pcap_t(&mut value);
870

            
871
        let mut value: isize = 888;
872
        let pcap_dumper = as_pcap_dumper_t(&mut value);
873

            
874
        let test_capture = test_capture::<Offline>(pcap);
875
        let capture = test_capture.capture;
876

            
877
        let ctx = raw::pcap_dump_open_context();
878
        ctx.expect()
879
            .withf_st(move |arg1, _| *arg1 == pcap)
880
            .return_once_st(move |_, _| pcap_dumper);
881

            
882
        let ctx = raw::pcap_dump_close_context();
883
        ctx.expect()
884
            .withf_st(move |arg1| *arg1 == pcap_dumper)
885
            .return_once(|_| {});
886

            
887
        let result = capture.savefile("path/to/nowhere");
888
        assert!(result.is_ok());
889
    }
890

            
891
    #[test]
892
    #[cfg(libpcap_1_7_2)]
893
    fn test_savefile_append() {
894
        let _m = RAWMTX.lock();
895

            
896
        let mut value: isize = 777;
897
        let pcap = as_pcap_t(&mut value);
898

            
899
        let mut value: isize = 888;
900
        let pcap_dumper = as_pcap_dumper_t(&mut value);
901

            
902
        let test_capture = test_capture::<Offline>(pcap);
903
        let capture = test_capture.capture;
904

            
905
        #[cfg(windows)]
906
        let ctx = raw::has_dump_append_context();
907
        #[cfg(windows)]
908
        ctx.expect().return_once(|| true);
909

            
910
        let ctx = raw::pcap_dump_open_append_context();
911
        ctx.expect()
912
            .withf_st(move |arg1, _| *arg1 == pcap)
913
            .return_once_st(move |_, _| pcap_dumper);
914

            
915
        let ctx = raw::pcap_dump_close_context();
916
        ctx.expect()
917
            .withf_st(move |arg1| *arg1 == pcap_dumper)
918
            .return_once(|_| {});
919

            
920
        let result = capture.savefile_append("path/to/nowhere");
921
        assert!(result.is_ok());
922
    }
923

            
924
    #[test]
925
    #[cfg(all(windows, libpcap_1_7_2))]
926
    fn test_savefile_append_missing() {
927
        let _m = RAWMTX.lock();
928

            
929
        let mut value: isize = 777;
930
        let pcap = as_pcap_t(&mut value);
931

            
932
        let test_capture = test_capture::<Offline>(pcap);
933
        let capture = test_capture.capture;
934

            
935
        let ctx = raw::has_dump_append_context();
936
        ctx.expect().return_once(|| false);
937

            
938
        let result = capture.savefile_append("path/to/nowhere");
939
        assert!(matches!(result, Err(Error::EntrypointNotFound(_))));
940
    }
941

            
942
    #[test]
943
    fn test_savefile_error() {
944
        let _m = RAWMTX.lock();
945

            
946
        let mut value: isize = 777;
947
        let pcap = as_pcap_t(&mut value);
948

            
949
        let test_capture = test_capture::<Offline>(pcap);
950
        let capture = test_capture.capture;
951

            
952
        let ctx = raw::pcap_dump_open_context();
953
        ctx.expect()
954
            .withf_st(move |arg1, _| *arg1 == pcap)
955
            .return_once(|_, _| std::ptr::null_mut());
956

            
957
        let _err = geterr_expect(pcap);
958

            
959
        let result = capture.savefile("path/to/nowhere");
960
        assert!(result.is_err());
961
    }
962

            
963
    #[test]
964
    #[cfg(libpcap_1_7_2)]
965
    fn test_savefile_append_error() {
966
        let _m = RAWMTX.lock();
967

            
968
        let mut value: isize = 777;
969
        let pcap = as_pcap_t(&mut value);
970

            
971
        let test_capture = test_capture::<Offline>(pcap);
972
        let capture = test_capture.capture;
973

            
974
        #[cfg(windows)]
975
        let has_ctx = raw::has_dump_append_context();
976
        #[cfg(windows)]
977
        has_ctx.expect().return_once(|| true);
978

            
979
        let ctx = raw::pcap_dump_open_append_context();
980
        ctx.expect()
981
            .withf_st(move |arg1, _| *arg1 == pcap)
982
            .return_once(|_, _| std::ptr::null_mut());
983

            
984
        let _err = geterr_expect(pcap);
985

            
986
        let result = capture.savefile_append("path/to/nowhere");
987
        assert!(result.is_err());
988
    }
989

            
990
    #[cfg(libpcap_1_9_0)]
991
    struct DumpFtellExpect(raw::__pcap_dump_ftell64::Context);
992

            
993
    #[cfg(not(libpcap_1_9_0))]
994
    struct DumpFtellExpect(raw::__pcap_dump_ftell::Context);
995

            
996
    fn dump_ftell_expect(pcap_dumper: *mut raw::pcap_dumper_t, offset: i64) -> DumpFtellExpect {
997
        // Lock must be acquired by caller.
998
        assert!(RAWMTX.try_lock().is_err());
999

            
        #[cfg(libpcap_1_9_0)]
        {
            let ctx = raw::pcap_dump_ftell64_context();
            ctx.checkpoint();
            ctx.expect()
                .withf_st(move |arg1| *arg1 == pcap_dumper)
                .return_once(move |_| offset);
            DumpFtellExpect(ctx)
        }
        #[cfg(not(libpcap_1_9_0))]
        {
            let ctx = raw::pcap_dump_ftell_context();
            ctx.checkpoint();
            ctx.expect()
                .withf_st(move |arg1| *arg1 == pcap_dumper)
                .return_once(move |_| offset as _);
            DumpFtellExpect(ctx)
        }
    }
    #[test]
    fn test_savefile_ops() {
        let _m = RAWMTX.lock();
        #[cfg(windows)]
        let has_ftell64 = raw::has_dump_ftell64_context();
        #[cfg(windows)]
        has_ftell64.expect().times(..).return_const(true);
        let layout = packet::layout_context();
        layout.expect().times(..).return_const(Layout::Native);
        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 ctx = raw::pcap_dump_context();
        ctx.expect()
            .withf_st(move |arg1, _, _| *arg1 == pcap_dumper as _)
            .return_once(|_, _, _| {});
        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 layout = packet::layout_context();
        layout.expect().times(..).return_const(Layout::Native);
        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());
    }
}