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

            
6
use std::{
7
    any::Any,
8
    convert::TryInto,
9
    ffi::CString,
10
    fmt, mem,
11
    panic::{catch_unwind, resume_unwind, AssertUnwindSafe},
12
    path::Path,
13
    ptr::{self, NonNull},
14
    slice,
15
    sync::{Arc, Weak},
16
};
17

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

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

            
29
use iterator::PacketIter;
30

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

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

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

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

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

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

            
96
    /// Create a `Savefile` context for recording captured packets using this `Capture`'s
97
    /// configurations.
98
10
    pub fn savefile<P: AsRef<Path>>(&self, path: P) -> Result<Savefile, Error> {
99
10
        let name = CString::new(path.as_ref().to_str().unwrap())?;
100
10
        let handle_opt = NonNull::<raw::pcap_dumper_t>::new(unsafe {
101
10
            raw::pcap_dump_open(self.handle.as_ptr(), name.as_ptr())
102
        });
103
10
        let handle = self
104
10
            .check_err(handle_opt.is_some())
105
14
            .map(|_| handle_opt.unwrap())?;
106
8
        Ok(Savefile::from(handle))
107
10
    }
108

            
109
    /// Create a `Savefile` context for recording captured packets using this `Capture`'s
110
    /// configurations. The output is written to a raw file descriptor which is opened in `"w"`
111
    /// mode.
112
    ///
113
    /// # Safety
114
    ///
115
    /// Unsafe, because the returned Savefile assumes it is the sole owner of the file descriptor.
116
    #[cfg(not(windows))]
117
4
    pub unsafe fn savefile_raw_fd(&self, fd: RawFd) -> Result<Savefile, Error> {
118
5
        open_raw_fd(fd, b'w').and_then(|file| {
119
2
            let handle_opt = NonNull::<raw::pcap_dumper_t>::new(raw::pcap_dump_fopen(
120
2
                self.handle.as_ptr(),
121
2
                file,
122
            ));
123
2
            let handle = self
124
2
                .check_err(handle_opt.is_some())
125
2
                .map(|_| handle_opt.unwrap())?;
126
2
            Ok(Savefile::from(handle))
127
2
        })
128
4
    }
129

            
130
    /// Reopen a `Savefile` context for recording captured packets using this `Capture`'s
131
    /// configurations. This is similar to `savefile()` but does not create the file if it
132
    /// does  not exist and, if it does already exist, and is a pcap file with the same
133
    /// byte order as the host opening the file, and has the same time stamp precision,
134
    /// link-layer header type,  and  snapshot length as p, it will write new packets
135
    /// at the end of the file.
136
    #[cfg(libpcap_1_7_2)]
137
6
    pub fn savefile_append<P: AsRef<Path>>(&self, path: P) -> Result<Savefile, Error> {
138
6
        let name = CString::new(path.as_ref().to_str().unwrap())?;
139
6
        let handle_opt = NonNull::<raw::pcap_dumper_t>::new(unsafe {
140
6
            raw::pcap_dump_open_append(self.handle.as_ptr(), name.as_ptr())
141
        });
142
6
        let handle = self
143
6
            .check_err(handle_opt.is_some())
144
8
            .map(|_| handle_opt.unwrap())?;
145
4
        Ok(Savefile::from(handle))
146
6
    }
147

            
148
    /// Set the direction of the capture
149
4
    pub fn direction(&self, direction: Direction) -> Result<(), Error> {
150
4
        self.check_err(unsafe {
151
4
            raw::pcap_setdirection(self.handle.as_ptr(), direction as u32 as _) == 0
152
        })
153
4
    }
154

            
155
    /// Blocks until a packet is returned from the capture handle or an error occurs.
156
    ///
157
    /// pcap captures packets and places them into a buffer which this function reads
158
    /// from.
159
    ///
160
    /// # Warning
161
    ///
162
    /// This buffer has a finite length, so if the buffer fills completely new
163
    /// packets will be discarded temporarily. This means that in realtime situations,
164
    /// you probably want to minimize the time between calls to next_packet() method.
165
312
    pub fn next_packet(&mut self) -> Result<Packet<'_>, Error> {
166
        unsafe {
167
312
            let mut header: *mut raw::pcap_pkthdr = ptr::null_mut();
168
312
            let mut packet: *const libc::c_uchar = ptr::null();
169
312
            let retcode = raw::pcap_next_ex(self.handle.as_ptr(), &mut header, &mut packet);
170
312
            match retcode {
171
312
                i if i >= 1 => {
172
                    // packet was read without issue
173
286
                    Ok(Packet::new(
174
286
                        &*(&*header as *const raw::pcap_pkthdr as *const PacketHeader),
175
286
                        slice::from_raw_parts(packet, (*header).caplen as _),
176
286
                    ))
177
                }
178
                0 => {
179
                    // packets are being read from a live capture and the
180
                    // timeout expired
181
6
                    Err(Error::TimeoutExpired)
182
                }
183
                -1 => {
184
                    // an error occured while reading the packet
185
6
                    Err(self.get_err())
186
                }
187
                -2 => {
188
                    // packets are being read from a "savefile" and there are no
189
                    // more packets to read
190
14
                    Err(Error::NoMorePackets)
191
                }
192
                // GRCOV_EXCL_START
193
                _ => {
194
                    // libpcap only defines codes >=1, 0, -1, and -2
195
                    unreachable!()
196
                } // GRCOV_EXCL_STOP
197
            }
198
        }
199
312
    }
200

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

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

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

            
239
    /// Returns a thread-safe `BreakLoop` handle for calling pcap_breakloop() on an active capture.
240
    ///
241
    /// # Example
242
    ///
243
    /// ```no_run
244
    /// // Using an active capture
245
    /// use pcap::Device;
246
    ///
247
    /// let mut cap = Device::lookup().unwrap().unwrap().open().unwrap();
248
    ///
249
    /// let break_handle = cap.breakloop_handle();
250
    ///
251
    /// let capture_thread = std::thread::spawn(move || {
252
    ///     while let Ok(packet) = cap.next_packet() {
253
    ///         println!("received packet! {:?}", packet);
254
    ///     }
255
    /// });
256
    ///
257
    /// // Send break_handle to a separate thread (e.g. user input, signal handler, etc.)
258
    /// std::thread::spawn(move || {
259
    ///     std::thread::sleep(std::time::Duration::from_secs(1));
260
    ///     break_handle.breakloop();
261
    /// });
262
    ///
263
    /// capture_thread.join().unwrap();
264
    /// ```
265
2
    pub fn breakloop_handle(&mut self) -> BreakLoop {
266
2
        BreakLoop {
267
2
            handle: Arc::<PcapHandle>::downgrade(&self.handle),
268
2
        }
269
2
    }
270

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

            
275
        unsafe {
276
22
            let mut bpf_program: raw::bpf_program = mem::zeroed();
277
22
            let ret = raw::pcap_compile(
278
22
                self.handle.as_ptr(),
279
22
                &mut bpf_program,
280
22
                program.as_ptr(),
281
22
                optimize as libc::c_int,
282
                0,
283
            );
284
22
            self.check_err(ret != -1).and(Ok(BpfProgram(bpf_program)))
285
        }
286
22
    }
287

            
288
    /// Sets the filter on the capture using the given BPF program string. Internally this is
289
    /// compiled using `pcap_compile()`. `optimize` controls whether optimization on the resulting
290
    /// code is performed
291
    ///
292
    /// See <http://biot.com/capstats/bpf.html> for more information about this syntax.
293
4
    pub fn filter(&mut self, program: &str, optimize: bool) -> Result<(), Error> {
294
4
        let mut bpf_program = self.compile(program, optimize)?;
295
4
        let ret = unsafe { raw::pcap_setfilter(self.handle.as_ptr(), &mut bpf_program.0) };
296
4
        self.check_err(ret != -1)
297
4
    }
298

            
299
    /// Get capture statistics about this capture. The values represent packet statistics from the
300
    /// start of the run to the time of the call.
301
    ///
302
    /// See <https://www.tcpdump.org/manpages/pcap_stats.3pcap.html> for per-platform caveats about
303
    /// how packet statistics are calculated.
304
6
    pub fn stats(&mut self) -> Result<Stat, Error> {
305
        unsafe {
306
6
            let mut stats: raw::pcap_stat = mem::zeroed();
307
6
            self.check_err(raw::pcap_stats(self.handle.as_ptr(), &mut stats) != -1)
308
7
                .map(|_| Stat::new(stats.ps_recv, stats.ps_drop, stats.ps_ifdrop))
309
        }
310
6
    }
311
}
312

            
313
// Handler and its associated function let us create an extern "C" fn which dispatches to a normal
314
// Rust FnMut, which may be a closure with a captured environment. The *only* purpose of this
315
// generic parameter is to ensure that in Capture::pcap_loop that we pass the right function
316
// pointer and the right data pointer to pcap_loop.
317
struct HandlerFn<F> {
318
    func: F,
319
    panic_payload: Option<Box<dyn Any + Send>>,
320
    handle: Arc<PcapHandle>,
321
}
322

            
323
impl<F> HandlerFn<F>
324
where
325
    F: FnMut(Packet),
326
{
327
12
    extern "C" fn callback(
328
12
        slf: *mut libc::c_uchar,
329
12
        header: *const raw::pcap_pkthdr,
330
12
        packet: *const libc::c_uchar,
331
12
    ) {
332
        unsafe {
333
12
            let packet = Packet::new(
334
12
                &*(header as *const PacketHeader),
335
12
                slice::from_raw_parts(packet, (*header).caplen as _),
336
            );
337

            
338
12
            let slf = slf as *mut Self;
339
12
            let func = &mut (*slf).func;
340
12
            let mut func = AssertUnwindSafe(func);
341
            // If our handler function panics, we need to prevent it from unwinding across the
342
            // FFI boundary. If the handler panics we catch the unwind here, break out of
343
            // pcap_loop, and resume the unwind outside.
344
12
            if let Err(e) = catch_unwind(move || func(packet)) {
345
4
                (*slf).panic_payload = Some(e);
346
4
                raw::pcap_breakloop((*slf).handle.as_ptr());
347
8
            }
348
        }
349
12
    }
350
}
351

            
352
impl<T: Activated> From<Capture<T>> for Capture<dyn Activated> {
353
16
    fn from(cap: Capture<T>) -> Capture<dyn Activated> {
354
16
        unsafe { mem::transmute(cap) }
355
16
    }
356
}
357

            
358
/// BreakLoop can safely be sent to other threads such as signal handlers to abort
359
/// blocking capture loops such as `Capture::next_packet` and `Capture::for_each`.
360
///
361
/// See <https://www.tcpdump.org/manpages/pcap_breakloop.3pcap.html> for per-platform caveats about
362
/// how breakloop can wake up blocked threads.
363
pub struct BreakLoop {
364
    handle: Weak<PcapHandle>,
365
}
366

            
367
unsafe impl Send for BreakLoop {}
368
unsafe impl Sync for BreakLoop {}
369

            
370
impl BreakLoop {
371
    /// Calls `pcap_breakloop` to make the blocking loop of a pcap capture return.
372
    /// The call is a no-op if the handle is invalid.
373
    ///
374
    /// # Safety
375
    ///
376
    /// Can be called from any thread, but **must not** be used inside a
377
    /// signal handler unless the owning `Capture` is guaranteed to still
378
    /// be alive.
379
    ///
380
    /// The signal handler should defer the execution of `BreakLoop::breakloop()`
381
    /// to a thread instead for safety.
382
4
    pub fn breakloop(&self) {
383
4
        if let Some(handle) = self.handle.upgrade() {
384
2
            unsafe { raw::pcap_breakloop(handle.as_ptr()) };
385
2
        }
386
4
    }
387
}
388

            
389
/// Abstraction for writing pcap savefiles, which can be read afterwards via `Capture::from_file()`.
390
pub struct Savefile {
391
    handle: NonNull<raw::pcap_dumper_t>,
392
}
393

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

            
399
impl Savefile {
400
    /// Write a packet to a capture file
401
404
    pub fn write(&mut self, packet: &Packet<'_>) {
402
404
        unsafe {
403
404
            raw::pcap_dump(
404
404
                self.handle.as_ptr() as _,
405
404
                &*(packet.header as *const PacketHeader as *const raw::pcap_pkthdr),
406
404
                packet.data.as_ptr(),
407
404
            );
408
404
        }
409
404
    }
410

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

            
417
2
        Ok(())
418
4
    }
419
}
420

            
421
impl From<NonNull<raw::pcap_dumper_t>> for Savefile {
422
19
    fn from(handle: NonNull<raw::pcap_dumper_t>) -> Self {
423
19
        Savefile { handle }
424
19
    }
425
}
426

            
427
impl Drop for Savefile {
428
21
    fn drop(&mut self) {
429
21
        unsafe { raw::pcap_dump_close(self.handle.as_ptr()) }
430
21
    }
431
}
432

            
433
#[repr(transparent)]
434
pub struct BpfInstruction(raw::bpf_insn);
435
#[repr(transparent)]
436
pub struct BpfProgram(raw::bpf_program);
437

            
438
impl BpfProgram {
439
    /// checks whether a filter matches a packet
440
6
    pub fn filter(&self, buf: &[u8]) -> bool {
441
6
        let header: raw::pcap_pkthdr = raw::pcap_pkthdr {
442
6
            ts: libc::timeval {
443
6
                tv_sec: 0,
444
6
                tv_usec: 0,
445
6
            },
446
6
            caplen: buf.len() as u32,
447
6
            len: buf.len() as u32,
448
6
        };
449
6
        unsafe { raw::pcap_offline_filter(&self.0, &header, buf.as_ptr()) > 0 }
450
6
    }
451

            
452
12
    pub fn get_instructions(&self) -> &[BpfInstruction] {
453
        unsafe {
454
12
            slice::from_raw_parts(
455
12
                self.0.bf_insns as *const BpfInstruction,
456
12
                self.0.bf_len as usize,
457
12
            )
458
        }
459
12
    }
460
}
461

            
462
impl Drop for BpfProgram {
463
29
    fn drop(&mut self) {
464
29
        unsafe { raw::pcap_freecode(&mut self.0) }
465
29
    }
466
}
467

            
468
impl fmt::Display for BpfInstruction {
469
2
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470
2
        write!(
471
2
            f,
472
2
            "{} {} {} {}",
473
            self.0.code, self.0.jt, self.0.jf, self.0.k
474
        )
475
2
    }
476
}
477

            
478
unsafe impl Send for BpfProgram {}
479

            
480
#[cfg(not(windows))]
481
/// Open a raw file descriptor.
482
///
483
/// # Safety
484
///
485
/// Unsafe, because the returned FILE assumes it is the sole owner of the file descriptor.
486
18
pub unsafe fn open_raw_fd(fd: RawFd, mode: u8) -> Result<*mut libc::FILE, Error> {
487
18
    let mode = [mode, 0];
488
18
    libc::fdopen(fd, mode.as_ptr() as _)
489
18
        .as_mut()
490
21
        .map(|f| f as _)
491
18
        .ok_or(Error::InvalidRawFd)
492
18
}
493

            
494
// GRCOV_EXCL_START
495
#[cfg(test)]
496
mod testmod {
497
    use super::*;
498

            
499
    pub static TS: libc::timeval = libc::timeval {
500
        tv_sec: 5,
501
        tv_usec: 50,
502
    };
503
    pub static LEN: u32 = DATA.len() as u32;
504
    pub static CAPLEN: u32 = LEN;
505

            
506
    pub static mut PKTHDR: raw::pcap_pkthdr = raw::pcap_pkthdr {
507
        ts: TS,
508
        caplen: CAPLEN,
509
        len: LEN,
510
    };
511
    pub static PACKET_HEADER: PacketHeader = PacketHeader {
512
        ts: TS,
513
        caplen: CAPLEN,
514
        len: LEN,
515
    };
516

            
517
    pub static DATA: [u8; 4] = [4, 5, 6, 7];
518
    pub static PACKET: Packet = Packet {
519
        header: &PACKET_HEADER,
520
        data: &DATA,
521
    };
522

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

            
529
        let ctx = raw::pcap_next_ex_context();
530
        ctx.checkpoint();
531
        ctx.expect()
532
            .withf_st(move |arg1, _, _| *arg1 == pcap)
533
            .return_once_st(move |_, arg2, arg3| {
534
                unsafe {
535
                    *arg2 = pkthdr_ptr;
536
                    *arg3 = data_ptr;
537
                }
538
                CAPLEN as i32
539
            });
540

            
541
        NextExContext(ctx)
542
    }
543
}
544
// GRCOV_EXCL_STOP
545

            
546
#[cfg(test)]
547
mod tests {
548
    use crate::{
549
        capture::{
550
            activated::testmod::{next_ex_expect, PACKET},
551
            testmod::test_capture,
552
            Active, Capture, Offline,
553
        },
554
        raw::testmod::{as_pcap_dumper_t, as_pcap_t, geterr_expect, RAWMTX},
555
    };
556

            
557
    use super::*;
558

            
559
    #[test]
560
    fn test_list_datalinks() {
561
        let _m = RAWMTX.lock();
562

            
563
        let mut value: isize = 777;
564
        let pcap = as_pcap_t(&mut value);
565

            
566
        let test_capture = test_capture::<Active>(pcap);
567
        let capture: Capture<dyn Activated> = test_capture.capture.into();
568

            
569
        let ctx = raw::pcap_list_datalinks_context();
570
        ctx.expect()
571
            .withf_st(move |arg1, _| *arg1 == pcap)
572
            .return_once_st(|_, _| 0);
573

            
574
        let ctx = raw::pcap_free_datalinks_context();
575
        ctx.expect().return_once(|_| {});
576

            
577
        let _err = geterr_expect(pcap);
578

            
579
        let result = capture.list_datalinks();
580
        assert!(result.is_err());
581

            
582
        let mut datalinks: [i32; 4] = [0, 1, 2, 3];
583
        let links: *mut i32 = datalinks.as_mut_ptr();
584
        let len = datalinks.len();
585

            
586
        let ctx = raw::pcap_list_datalinks_context();
587
        ctx.checkpoint();
588
        ctx.expect()
589
            .withf_st(move |arg1, _| *arg1 == pcap)
590
            .return_once_st(move |_, arg2| {
591
                unsafe { *arg2 = links };
592
                len as i32
593
            });
594

            
595
        let ctx = raw::pcap_free_datalinks_context();
596
        ctx.checkpoint();
597
        ctx.expect().return_once(|_| {});
598

            
599
        let pcap_datalinks = capture.list_datalinks().unwrap();
600
        assert_eq!(
601
            pcap_datalinks,
602
            datalinks.iter().cloned().map(Linktype).collect::<Vec<_>>()
603
        );
604
    }
605

            
606
    #[test]
607
    fn test_set_datalink() {
608
        let _m = RAWMTX.lock();
609

            
610
        let mut value: isize = 777;
611
        let pcap = as_pcap_t(&mut value);
612

            
613
        let test_capture = test_capture::<Active>(pcap);
614
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
615

            
616
        let ctx = raw::pcap_set_datalink_context();
617
        ctx.expect()
618
            .withf_st(move |arg1, _| *arg1 == pcap)
619
            .return_once(|_, _| 0);
620

            
621
        let result = capture.set_datalink(Linktype::ETHERNET);
622
        assert!(result.is_ok());
623

            
624
        let ctx = raw::pcap_set_datalink_context();
625
        ctx.checkpoint();
626
        ctx.expect()
627
            .withf_st(move |arg1, _| *arg1 == pcap)
628
            .return_once(|_, _| -1);
629

            
630
        let _err = geterr_expect(pcap);
631

            
632
        let result = capture.set_datalink(Linktype::ETHERNET);
633
        assert!(result.is_err());
634
    }
635

            
636
    #[test]
637
    fn test_get_datalink() {
638
        let _m = RAWMTX.lock();
639

            
640
        let mut value: isize = 777;
641
        let pcap = as_pcap_t(&mut value);
642

            
643
        let test_capture = test_capture::<Active>(pcap);
644
        let capture: Capture<dyn Activated> = test_capture.capture.into();
645

            
646
        let ctx = raw::pcap_datalink_context();
647
        ctx.expect()
648
            .withf_st(move |arg1| *arg1 == pcap)
649
            .return_once(|_| 1);
650

            
651
        let linktype = capture.get_datalink();
652
        assert_eq!(linktype, Linktype::ETHERNET);
653
    }
654

            
655
    #[test]
656
    fn unify_activated() {
657
        #![allow(dead_code)]
658
        fn test1() -> Capture<Active> {
659
            panic!();
660
        }
661

            
662
        fn test2() -> Capture<Offline> {
663
            panic!();
664
        }
665

            
666
        fn maybe(a: bool) -> Capture<dyn Activated> {
667
            if a {
668
                test1().into()
669
            } else {
670
                test2().into()
671
            }
672
        }
673

            
674
        fn also_maybe(a: &mut Capture<dyn Activated>) {
675
            a.filter("whatever filter string, this won't be run anyway", false)
676
                .unwrap();
677
        }
678
    }
679

            
680
    #[test]
681
    fn test_breakloop_capture_dropped() {
682
        let _m = RAWMTX.lock();
683

            
684
        let mut value: isize = 1234;
685
        let pcap = as_pcap_t(&mut value);
686

            
687
        let test_capture = test_capture::<Active>(pcap);
688
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
689

            
690
        let ctx = raw::pcap_breakloop_context();
691
        ctx.expect()
692
            .withf_st(move |h| *h == pcap)
693
            .return_const(())
694
            .times(1);
695

            
696
        let break_handle = capture.breakloop_handle();
697

            
698
        break_handle.breakloop();
699

            
700
        drop(capture);
701

            
702
        break_handle.breakloop(); // this call does not trigger mock after drop
703
    }
704

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

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

            
712
        let mut value: isize = 888;
713
        let pcap_dumper = as_pcap_dumper_t(&mut value);
714

            
715
        let test_capture = test_capture::<Offline>(pcap);
716
        let capture = test_capture.capture;
717

            
718
        let ctx = raw::pcap_dump_open_context();
719
        ctx.expect()
720
            .withf_st(move |arg1, _| *arg1 == pcap)
721
            .return_once_st(move |_, _| pcap_dumper);
722

            
723
        let ctx = raw::pcap_dump_close_context();
724
        ctx.expect()
725
            .withf_st(move |arg1| *arg1 == pcap_dumper)
726
            .return_once(|_| {});
727

            
728
        let result = capture.savefile("path/to/nowhere");
729
        assert!(result.is_ok());
730
    }
731

            
732
    #[test]
733
    #[cfg(libpcap_1_7_2)]
734
    fn test_savefile_append() {
735
        let _m = RAWMTX.lock();
736

            
737
        let mut value: isize = 777;
738
        let pcap = as_pcap_t(&mut value);
739

            
740
        let mut value: isize = 888;
741
        let pcap_dumper = as_pcap_dumper_t(&mut value);
742

            
743
        let test_capture = test_capture::<Offline>(pcap);
744
        let capture = test_capture.capture;
745

            
746
        let ctx = raw::pcap_dump_open_append_context();
747
        ctx.expect()
748
            .withf_st(move |arg1, _| *arg1 == pcap)
749
            .return_once_st(move |_, _| pcap_dumper);
750

            
751
        let ctx = raw::pcap_dump_close_context();
752
        ctx.expect()
753
            .withf_st(move |arg1| *arg1 == pcap_dumper)
754
            .return_once(|_| {});
755

            
756
        let result = capture.savefile_append("path/to/nowhere");
757
        assert!(result.is_ok());
758
    }
759

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

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

            
767
        let test_capture = test_capture::<Offline>(pcap);
768
        let capture = test_capture.capture;
769

            
770
        let ctx = raw::pcap_dump_open_context();
771
        ctx.expect()
772
            .withf_st(move |arg1, _| *arg1 == pcap)
773
            .return_once(|_, _| std::ptr::null_mut());
774

            
775
        let _err = geterr_expect(pcap);
776

            
777
        let result = capture.savefile("path/to/nowhere");
778
        assert!(result.is_err());
779
    }
780

            
781
    #[test]
782
    #[cfg(libpcap_1_7_2)]
783
    fn test_savefile_append_error() {
784
        let _m = RAWMTX.lock();
785

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

            
789
        let test_capture = test_capture::<Offline>(pcap);
790
        let capture = test_capture.capture;
791

            
792
        let ctx = raw::pcap_dump_open_append_context();
793
        ctx.expect()
794
            .withf_st(move |arg1, _| *arg1 == pcap)
795
            .return_once(|_, _| std::ptr::null_mut());
796

            
797
        let _err = geterr_expect(pcap);
798

            
799
        let result = capture.savefile_append("path/to/nowhere");
800
        assert!(result.is_err());
801
    }
802

            
803
    #[test]
804
    fn test_savefile_ops() {
805
        let _m = RAWMTX.lock();
806

            
807
        let mut value: isize = 888;
808
        let pcap_dumper = as_pcap_dumper_t(&mut value);
809

            
810
        let ctx = raw::pcap_dump_close_context();
811
        ctx.expect()
812
            .withf_st(move |arg1| *arg1 == pcap_dumper)
813
            .return_once(|_| {});
814

            
815
        let mut savefile = Savefile {
816
            handle: NonNull::new(pcap_dumper).unwrap(),
817
        };
818

            
819
        let ctx = raw::pcap_dump_context();
820
        ctx.expect()
821
            .withf_st(move |arg1, _, _| *arg1 == pcap_dumper as _)
822
            .return_once(|_, _, _| {});
823

            
824
        savefile.write(&PACKET);
825

            
826
        let ctx = raw::pcap_dump_flush_context();
827
        ctx.expect()
828
            .withf_st(move |arg1| *arg1 == pcap_dumper)
829
            .return_once(|_| 0);
830

            
831
        let result = savefile.flush();
832
        assert!(result.is_ok());
833

            
834
        let ctx = raw::pcap_dump_flush_context();
835
        ctx.checkpoint();
836
        ctx.expect()
837
            .withf_st(move |arg1| *arg1 == pcap_dumper)
838
            .return_once(|_| -1);
839

            
840
        let result = savefile.flush();
841
        assert!(result.is_err());
842
    }
843

            
844
    #[test]
845
    fn test_direction() {
846
        let _m = RAWMTX.lock();
847

            
848
        let mut value: isize = 777;
849
        let pcap = as_pcap_t(&mut value);
850

            
851
        let test_capture = test_capture::<Active>(pcap);
852
        let capture = test_capture.capture;
853

            
854
        let ctx = raw::pcap_setdirection_context();
855
        ctx.expect()
856
            .withf_st(move |arg1, arg2| (*arg1 == pcap) && (*arg2 == raw::PCAP_D_OUT))
857
            .return_once(|_, _| 0);
858

            
859
        let result = capture.direction(Direction::Out);
860
        assert!(result.is_ok());
861

            
862
        let ctx = raw::pcap_setdirection_context();
863
        ctx.checkpoint();
864
        ctx.expect()
865
            .withf_st(move |arg1, arg2| (*arg1 == pcap) && (*arg2 == raw::PCAP_D_OUT))
866
            .return_once(|_, _| -1);
867

            
868
        let _err = geterr_expect(pcap);
869

            
870
        let result = capture.direction(Direction::Out);
871
        assert!(result.is_err());
872

            
873
        // For code coverage of the derive line.
874
        assert_ne!(Direction::In, Direction::InOut);
875
        assert_ne!(Direction::In, Direction::Out);
876
        assert_ne!(Direction::InOut, Direction::Out);
877
    }
878

            
879
    #[test]
880
    fn test_next_packet() {
881
        let _m = RAWMTX.lock();
882

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

            
886
        let test_capture = test_capture::<Active>(pcap);
887
        let mut capture = test_capture.capture;
888

            
889
        let _nxt = next_ex_expect(pcap);
890

            
891
        let next_packet = capture.next_packet().unwrap();
892
        assert_eq!(next_packet, PACKET);
893
    }
894

            
895
    #[test]
896
    fn test_next_packet_timeout() {
897
        let _m = RAWMTX.lock();
898

            
899
        let mut value: isize = 777;
900
        let pcap = as_pcap_t(&mut value);
901

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

            
905
        let ctx = raw::pcap_next_ex_context();
906
        ctx.expect()
907
            .withf_st(move |arg1, _, _| *arg1 == pcap)
908
            .return_once_st(move |_, _, _| 0);
909

            
910
        let err = capture.next_packet().unwrap_err();
911
        assert_eq!(err, Error::TimeoutExpired);
912
    }
913

            
914
    #[test]
915
    fn test_next_packet_read_error() {
916
        let _m = RAWMTX.lock();
917

            
918
        let mut value: isize = 777;
919
        let pcap = as_pcap_t(&mut value);
920

            
921
        let test_capture = test_capture::<Active>(pcap);
922
        let mut capture = test_capture.capture;
923

            
924
        let ctx = raw::pcap_next_ex_context();
925
        ctx.expect()
926
            .withf_st(move |arg1, _, _| *arg1 == pcap)
927
            .return_once_st(move |_, _, _| -1);
928

            
929
        let _err = geterr_expect(pcap);
930

            
931
        let result = capture.next_packet();
932
        assert!(result.is_err());
933
    }
934

            
935
    #[test]
936
    fn test_next_packet_no_more_packets() {
937
        let _m = RAWMTX.lock();
938

            
939
        let mut value: isize = 777;
940
        let pcap = as_pcap_t(&mut value);
941

            
942
        let test_capture = test_capture::<Offline>(pcap);
943
        let mut capture = test_capture.capture;
944

            
945
        let ctx = raw::pcap_next_ex_context();
946
        ctx.expect()
947
            .withf_st(move |arg1, _, _| *arg1 == pcap)
948
            .return_once_st(move |_, _, _| -2);
949

            
950
        let err = capture.next_packet().unwrap_err();
951
        assert_eq!(err, Error::NoMorePackets);
952
    }
953

            
954
    #[test]
955
    fn test_compile() {
956
        let _m = RAWMTX.lock();
957

            
958
        let mut value: isize = 777;
959
        let pcap = as_pcap_t(&mut value);
960

            
961
        let test_capture = test_capture::<Active>(pcap);
962
        let capture = test_capture.capture;
963

            
964
        let ctx = raw::pcap_compile_context();
965
        ctx.expect()
966
            .withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
967
            .return_once(|_, _, _, _, _| -1);
968

            
969
        let _err = geterr_expect(pcap);
970

            
971
        let ctx = raw::pcap_freecode_context();
972
        ctx.expect().return_once(|_| {});
973

            
974
        let result = capture.compile("some bpf program", false);
975
        assert!(result.is_err());
976

            
977
        let ctx = raw::pcap_compile_context();
978
        ctx.checkpoint();
979
        ctx.expect()
980
            .withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
981
            .return_once(|_, _, _, _, _| 0);
982

            
983
        let ctx = raw::pcap_freecode_context();
984
        ctx.checkpoint();
985
        ctx.expect().return_once(|_| {});
986

            
987
        let result = capture.compile("some bpf program", false);
988
        assert!(result.is_ok());
989
    }
990

            
991
    #[test]
992
    fn test_filter() {
993
        let _m = RAWMTX.lock();
994

            
995
        let mut value: isize = 777;
996
        let pcap = as_pcap_t(&mut value);
997

            
998
        let test_capture = test_capture::<Active>(pcap);
999
        let mut capture = test_capture.capture;
        let ctx = raw::pcap_compile_context();
        ctx.expect()
            .withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
            .return_once(|_, _, _, _, _| 0);
        let ctx = raw::pcap_setfilter_context();
        ctx.expect()
            .withf_st(move |arg1, _| *arg1 == pcap)
            .return_once(|_, _| -1);
        let _err = geterr_expect(pcap);
        let ctx = raw::pcap_freecode_context();
        ctx.expect().return_once(|_| {});
        let result = capture.filter("some bpf program", false);
        assert!(result.is_err());
        let ctx = raw::pcap_compile_context();
        ctx.checkpoint();
        ctx.expect()
            .withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
            .return_once(|_, _, _, _, _| 0);
        let ctx = raw::pcap_setfilter_context();
        ctx.checkpoint();
        ctx.expect()
            .withf_st(move |arg1, _| *arg1 == pcap)
            .return_once(|_, _| 0);
        let ctx = raw::pcap_freecode_context();
        ctx.checkpoint();
        ctx.expect().return_once(|_| {});
        let result = capture.compile("some bpf program", false);
        assert!(result.is_ok());
    }
    #[test]
    fn test_stats() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture = test_capture.capture;
        let stat = raw::pcap_stat {
            ps_recv: 1,
            ps_drop: 2,
            ps_ifdrop: 3,
        };
        let ctx = raw::pcap_stats_context();
        ctx.expect()
            .withf_st(move |arg1, _| *arg1 == pcap)
            .return_once_st(move |_, arg2| {
                unsafe { *arg2 = stat };
                0
            });
        let stats = capture.stats().unwrap();
        assert_eq!(stats, Stat::new(stat.ps_recv, stat.ps_drop, stat.ps_ifdrop));
        let ctx = raw::pcap_stats_context();
        ctx.checkpoint();
        ctx.expect()
            .withf_st(move |arg1, _| *arg1 == pcap)
            .return_once_st(move |_, _| -1);
        let _err = geterr_expect(pcap);
        let result = capture.stats();
        assert!(result.is_err());
    }
    #[test]
    fn test_bpf_instruction_display() {
        let instr = BpfInstruction(raw::bpf_insn {
            code: 1,
            jt: 2,
            jf: 3,
            k: 4,
        });
        assert_eq!(format!("{instr}"), "1 2 3 4");
    }
    #[test]
    fn read_packet_via_pcap_loop() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
        let ctx = raw::pcap_loop_context();
        ctx.expect()
            .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == -1)
            .return_once_st(move |_, _, func, data| {
                let header = raw::pcap_pkthdr {
                    ts: libc::timeval {
                        tv_sec: 0,
                        tv_usec: 0,
                    },
                    caplen: 0,
                    len: 0,
                };
                let packet_data = &[];
                func(data, &header, packet_data.as_ptr());
                0
            });
        let mut packets = 0;
        capture
            .for_each(None, |_| {
                packets += 1;
            })
            .unwrap();
        assert_eq!(packets, 1);
    }
    #[test]
    #[should_panic = "panic in callback"]
    fn panic_in_pcap_loop() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
        let ctx = raw::pcap_loop_context();
        ctx.expect()
            .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == -1)
            .return_once_st(move |_, _, func, data| {
                let header = raw::pcap_pkthdr {
                    ts: libc::timeval {
                        tv_sec: 0,
                        tv_usec: 0,
                    },
                    caplen: 0,
                    len: 0,
                };
                let packet_data = &[];
                func(data, &header, packet_data.as_ptr());
                0
            });
        let ctx = raw::pcap_breakloop_context();
        ctx.expect()
            .withf_st(move |arg1| *arg1 == pcap)
            .return_once_st(move |_| {});
        capture
            .for_each(None, |_| panic!("panic in callback"))
            .unwrap();
    }
    #[test]
    fn for_each_with_count() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
        let ctx = raw::pcap_loop_context();
        ctx.expect()
            .withf_st(move |arg1, cnt, _, _| *arg1 == pcap && *cnt == 2)
            .return_once_st(move |_, _, func, data| {
                let header = raw::pcap_pkthdr {
                    ts: libc::timeval {
                        tv_sec: 0,
                        tv_usec: 0,
                    },
                    caplen: 0,
                    len: 0,
                };
                let packet_data = &[];
                func(data, &header, packet_data.as_ptr());
                func(data, &header, packet_data.as_ptr());
                0
            });
        let mut packets = 0;
        capture
            .for_each(Some(2), |_| {
                packets += 1;
            })
            .unwrap();
        assert_eq!(packets, 2);
    }
    #[test]
    fn for_each_with_count_0() {
        let _m = RAWMTX.lock();
        let mut value: isize = 777;
        let pcap = as_pcap_t(&mut value);
        let test_capture = test_capture::<Active>(pcap);
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
        let mut packets = 0;
        capture
            .for_each(Some(0), |_| {
                packets += 1;
            })
            .unwrap();
        assert_eq!(packets, 0);
    }
}