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
};
16

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

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

            
28
use iterator::PacketIter;
29

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
242
        unsafe {
243
22
            let mut bpf_program: raw::bpf_program = mem::zeroed();
244
22
            let ret = raw::pcap_compile(
245
22
                self.handle.as_ptr(),
246
22
                &mut bpf_program,
247
22
                program.as_ptr(),
248
22
                optimize as libc::c_int,
249
22
                0,
250
22
            );
251
22
            self.check_err(ret != -1).and(Ok(BpfProgram(bpf_program)))
252
        }
253
22
    }
254

            
255
    /// Sets the filter on the capture using the given BPF program string. Internally this is
256
    /// compiled using `pcap_compile()`. `optimize` controls whether optimization on the resulting
257
    /// code is performed
258
    ///
259
    /// See <http://biot.com/capstats/bpf.html> for more information about this syntax.
260
4
    pub fn filter(&mut self, program: &str, optimize: bool) -> Result<(), Error> {
261
4
        let mut bpf_program = self.compile(program, optimize)?;
262
4
        let ret = unsafe { raw::pcap_setfilter(self.handle.as_ptr(), &mut bpf_program.0) };
263
4
        self.check_err(ret != -1)
264
4
    }
265

            
266
    /// Get capture statistics about this capture. The values represent packet statistics from the
267
    /// start of the run to the time of the call.
268
    ///
269
    /// See <https://www.tcpdump.org/manpages/pcap_stats.3pcap.html> for per-platform caveats about
270
    /// how packet statistics are calculated.
271
6
    pub fn stats(&mut self) -> Result<Stat, Error> {
272
6
        unsafe {
273
6
            let mut stats: raw::pcap_stat = mem::zeroed();
274
6
            self.check_err(raw::pcap_stats(self.handle.as_ptr(), &mut stats) != -1)
275
7
                .map(|_| Stat::new(stats.ps_recv, stats.ps_drop, stats.ps_ifdrop))
276
6
        }
277
6
    }
278
}
279

            
280
// Handler and its associated function let us create an extern "C" fn which dispatches to a normal
281
// Rust FnMut, which may be a closure with a captured environment. The *only* purpose of this
282
// generic parameter is to ensure that in Capture::pcap_loop that we pass the right function
283
// pointer and the right data pointer to pcap_loop.
284
struct Handler<F> {
285
    func: F,
286
    panic_payload: Option<Box<dyn Any + Send>>,
287
    handle: NonNull<raw::pcap_t>,
288
}
289

            
290
impl<F> Handler<F>
291
where
292
    F: FnMut(Packet),
293
{
294
12
    extern "C" fn callback(
295
12
        slf: *mut libc::c_uchar,
296
12
        header: *const raw::pcap_pkthdr,
297
12
        packet: *const libc::c_uchar,
298
12
    ) {
299
12
        unsafe {
300
12
            let packet = Packet::new(
301
12
                &*(header as *const PacketHeader),
302
12
                slice::from_raw_parts(packet, (*header).caplen as _),
303
12
            );
304
12

            
305
12
            let slf = slf as *mut Self;
306
12
            let func = &mut (*slf).func;
307
12
            let mut func = AssertUnwindSafe(func);
308
            // If our handler function panics, we need to prevent it from unwinding across the
309
            // FFI boundary. If the handler panics we catch the unwind here, break out of
310
            // pcap_loop, and resume the unwind outside.
311
12
            if let Err(e) = catch_unwind(move || func(packet)) {
312
4
                (*slf).panic_payload = Some(e);
313
4
                raw::pcap_breakloop((*slf).handle.as_ptr());
314
8
            }
315
        }
316
12
    }
317
}
318

            
319
impl<T: Activated> From<Capture<T>> for Capture<dyn Activated> {
320
14
    fn from(cap: Capture<T>) -> Capture<dyn Activated> {
321
14
        unsafe { mem::transmute(cap) }
322
14
    }
323
}
324

            
325
/// Abstraction for writing pcap savefiles, which can be read afterwards via `Capture::from_file()`.
326
pub struct Savefile {
327
    handle: NonNull<raw::pcap_dumper_t>,
328
}
329

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

            
335
impl Savefile {
336
    /// Write a packet to a capture file
337
404
    pub fn write(&mut self, packet: &Packet<'_>) {
338
404
        unsafe {
339
404
            raw::pcap_dump(
340
404
                self.handle.as_ptr() as _,
341
404
                &*(packet.header as *const PacketHeader as *const raw::pcap_pkthdr),
342
404
                packet.data.as_ptr(),
343
404
            );
344
404
        }
345
404
    }
346

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

            
353
2
        Ok(())
354
4
    }
355
}
356

            
357
impl From<NonNull<raw::pcap_dumper_t>> for Savefile {
358
19
    fn from(handle: NonNull<raw::pcap_dumper_t>) -> Self {
359
19
        Savefile { handle }
360
19
    }
361
}
362

            
363
impl Drop for Savefile {
364
21
    fn drop(&mut self) {
365
21
        unsafe { raw::pcap_dump_close(self.handle.as_ptr()) }
366
21
    }
367
}
368

            
369
#[repr(transparent)]
370
pub struct BpfInstruction(raw::bpf_insn);
371
#[repr(transparent)]
372
pub struct BpfProgram(raw::bpf_program);
373

            
374
impl BpfProgram {
375
    /// checks whether a filter matches a packet
376
6
    pub fn filter(&self, buf: &[u8]) -> bool {
377
6
        let header: raw::pcap_pkthdr = raw::pcap_pkthdr {
378
6
            ts: libc::timeval {
379
6
                tv_sec: 0,
380
6
                tv_usec: 0,
381
6
            },
382
6
            caplen: buf.len() as u32,
383
6
            len: buf.len() as u32,
384
6
        };
385
6
        unsafe { raw::pcap_offline_filter(&self.0, &header, buf.as_ptr()) > 0 }
386
6
    }
387

            
388
12
    pub fn get_instructions(&self) -> &[BpfInstruction] {
389
12
        unsafe {
390
12
            slice::from_raw_parts(
391
12
                self.0.bf_insns as *const BpfInstruction,
392
12
                self.0.bf_len as usize,
393
12
            )
394
12
        }
395
12
    }
396
}
397

            
398
impl Drop for BpfProgram {
399
29
    fn drop(&mut self) {
400
29
        unsafe { raw::pcap_freecode(&mut self.0) }
401
29
    }
402
}
403

            
404
impl fmt::Display for BpfInstruction {
405
2
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
406
2
        write!(
407
2
            f,
408
2
            "{} {} {} {}",
409
2
            self.0.code, self.0.jt, self.0.jf, self.0.k
410
2
        )
411
2
    }
412
}
413

            
414
unsafe impl Send for BpfProgram {}
415

            
416
#[cfg(not(windows))]
417
/// Open a raw file descriptor.
418
///
419
/// # Safety
420
///
421
/// Unsafe, because the returned FILE assumes it is the sole owner of the file descriptor.
422
18
pub unsafe fn open_raw_fd(fd: RawFd, mode: u8) -> Result<*mut libc::FILE, Error> {
423
18
    let mode = [mode, 0];
424
18
    libc::fdopen(fd, mode.as_ptr() as _)
425
18
        .as_mut()
426
21
        .map(|f| f as _)
427
18
        .ok_or(Error::InvalidRawFd)
428
18
}
429

            
430
// GRCOV_EXCL_START
431
#[cfg(test)]
432
mod testmod {
433
    use super::*;
434

            
435
    pub static TS: libc::timeval = libc::timeval {
436
        tv_sec: 5,
437
        tv_usec: 50,
438
    };
439
    pub static LEN: u32 = DATA.len() as u32;
440
    pub static CAPLEN: u32 = LEN;
441

            
442
    pub static mut PKTHDR: raw::pcap_pkthdr = raw::pcap_pkthdr {
443
        ts: TS,
444
        caplen: CAPLEN,
445
        len: LEN,
446
    };
447
    pub static PACKET_HEADER: PacketHeader = PacketHeader {
448
        ts: TS,
449
        caplen: CAPLEN,
450
        len: LEN,
451
    };
452

            
453
    pub static DATA: [u8; 4] = [4, 5, 6, 7];
454
    pub static PACKET: Packet = Packet {
455
        header: &PACKET_HEADER,
456
        data: &DATA,
457
    };
458

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

            
465
        let ctx = raw::pcap_next_ex_context();
466
        ctx.checkpoint();
467
        ctx.expect()
468
            .withf_st(move |arg1, _, _| *arg1 == pcap)
469
            .return_once_st(move |_, arg2, arg3| {
470
                unsafe {
471
                    *arg2 = pkthdr_ptr;
472
                    *arg3 = data_ptr;
473
                }
474
                CAPLEN as i32
475
            });
476

            
477
        NextExContext(ctx)
478
    }
479
}
480
// GRCOV_EXCL_STOP
481

            
482
#[cfg(test)]
483
mod tests {
484
    use crate::{
485
        capture::{
486
            activated::testmod::{next_ex_expect, PACKET},
487
            testmod::test_capture,
488
            Active, Capture, Offline,
489
        },
490
        raw::testmod::{as_pcap_dumper_t, as_pcap_t, geterr_expect, RAWMTX},
491
    };
492

            
493
    use super::*;
494

            
495
    #[test]
496
    fn test_list_datalinks() {
497
        let _m = RAWMTX.lock();
498

            
499
        let mut value: isize = 777;
500
        let pcap = as_pcap_t(&mut value);
501

            
502
        let test_capture = test_capture::<Active>(pcap);
503
        let capture: Capture<dyn Activated> = test_capture.capture.into();
504

            
505
        let ctx = raw::pcap_list_datalinks_context();
506
        ctx.expect()
507
            .withf_st(move |arg1, _| *arg1 == pcap)
508
            .return_once_st(|_, _| 0);
509

            
510
        let ctx = raw::pcap_free_datalinks_context();
511
        ctx.expect().return_once(|_| {});
512

            
513
        let _err = geterr_expect(pcap);
514

            
515
        let result = capture.list_datalinks();
516
        assert!(result.is_err());
517

            
518
        let mut datalinks: [i32; 4] = [0, 1, 2, 3];
519
        let links: *mut i32 = datalinks.as_mut_ptr();
520
        let len = datalinks.len();
521

            
522
        let ctx = raw::pcap_list_datalinks_context();
523
        ctx.checkpoint();
524
        ctx.expect()
525
            .withf_st(move |arg1, _| *arg1 == pcap)
526
            .return_once_st(move |_, arg2| {
527
                unsafe { *arg2 = links };
528
                len as i32
529
            });
530

            
531
        let ctx = raw::pcap_free_datalinks_context();
532
        ctx.checkpoint();
533
        ctx.expect().return_once(|_| {});
534

            
535
        let pcap_datalinks = capture.list_datalinks().unwrap();
536
        assert_eq!(
537
            pcap_datalinks,
538
            datalinks.iter().cloned().map(Linktype).collect::<Vec<_>>()
539
        );
540
    }
541

            
542
    #[test]
543
    fn test_set_datalink() {
544
        let _m = RAWMTX.lock();
545

            
546
        let mut value: isize = 777;
547
        let pcap = as_pcap_t(&mut value);
548

            
549
        let test_capture = test_capture::<Active>(pcap);
550
        let mut capture: Capture<dyn Activated> = test_capture.capture.into();
551

            
552
        let ctx = raw::pcap_set_datalink_context();
553
        ctx.expect()
554
            .withf_st(move |arg1, _| *arg1 == pcap)
555
            .return_once(|_, _| 0);
556

            
557
        let result = capture.set_datalink(Linktype::ETHERNET);
558
        assert!(result.is_ok());
559

            
560
        let ctx = raw::pcap_set_datalink_context();
561
        ctx.checkpoint();
562
        ctx.expect()
563
            .withf_st(move |arg1, _| *arg1 == pcap)
564
            .return_once(|_, _| -1);
565

            
566
        let _err = geterr_expect(pcap);
567

            
568
        let result = capture.set_datalink(Linktype::ETHERNET);
569
        assert!(result.is_err());
570
    }
571

            
572
    #[test]
573
    fn test_get_datalink() {
574
        let _m = RAWMTX.lock();
575

            
576
        let mut value: isize = 777;
577
        let pcap = as_pcap_t(&mut value);
578

            
579
        let test_capture = test_capture::<Active>(pcap);
580
        let capture: Capture<dyn Activated> = test_capture.capture.into();
581

            
582
        let ctx = raw::pcap_datalink_context();
583
        ctx.expect()
584
            .withf_st(move |arg1| *arg1 == pcap)
585
            .return_once(|_| 1);
586

            
587
        let linktype = capture.get_datalink();
588
        assert_eq!(linktype, Linktype::ETHERNET);
589
    }
590

            
591
    #[test]
592
    fn unify_activated() {
593
        #![allow(dead_code)]
594
        fn test1() -> Capture<Active> {
595
            panic!();
596
        }
597

            
598
        fn test2() -> Capture<Offline> {
599
            panic!();
600
        }
601

            
602
        fn maybe(a: bool) -> Capture<dyn Activated> {
603
            if a {
604
                test1().into()
605
            } else {
606
                test2().into()
607
            }
608
        }
609

            
610
        fn also_maybe(a: &mut Capture<dyn Activated>) {
611
            a.filter("whatever filter string, this won't be run anyway", false)
612
                .unwrap();
613
        }
614
    }
615

            
616
    #[test]
617
    fn test_savefile() {
618
        let _m = RAWMTX.lock();
619

            
620
        let mut value: isize = 777;
621
        let pcap = as_pcap_t(&mut value);
622

            
623
        let mut value: isize = 888;
624
        let pcap_dumper = as_pcap_dumper_t(&mut value);
625

            
626
        let test_capture = test_capture::<Offline>(pcap);
627
        let capture = test_capture.capture;
628

            
629
        let ctx = raw::pcap_dump_open_context();
630
        ctx.expect()
631
            .withf_st(move |arg1, _| *arg1 == pcap)
632
            .return_once_st(move |_, _| pcap_dumper);
633

            
634
        let ctx = raw::pcap_dump_close_context();
635
        ctx.expect()
636
            .withf_st(move |arg1| *arg1 == pcap_dumper)
637
            .return_once(|_| {});
638

            
639
        let result = capture.savefile("path/to/nowhere");
640
        assert!(result.is_ok());
641
    }
642

            
643
    #[test]
644
    #[cfg(libpcap_1_7_2)]
645
    fn test_savefile_append() {
646
        let _m = RAWMTX.lock();
647

            
648
        let mut value: isize = 777;
649
        let pcap = as_pcap_t(&mut value);
650

            
651
        let mut value: isize = 888;
652
        let pcap_dumper = as_pcap_dumper_t(&mut value);
653

            
654
        let test_capture = test_capture::<Offline>(pcap);
655
        let capture = test_capture.capture;
656

            
657
        let ctx = raw::pcap_dump_open_append_context();
658
        ctx.expect()
659
            .withf_st(move |arg1, _| *arg1 == pcap)
660
            .return_once_st(move |_, _| pcap_dumper);
661

            
662
        let ctx = raw::pcap_dump_close_context();
663
        ctx.expect()
664
            .withf_st(move |arg1| *arg1 == pcap_dumper)
665
            .return_once(|_| {});
666

            
667
        let result = capture.savefile_append("path/to/nowhere");
668
        assert!(result.is_ok());
669
    }
670

            
671
    #[test]
672
    fn test_savefile_error() {
673
        let _m = RAWMTX.lock();
674

            
675
        let mut value: isize = 777;
676
        let pcap = as_pcap_t(&mut value);
677

            
678
        let test_capture = test_capture::<Offline>(pcap);
679
        let capture = test_capture.capture;
680

            
681
        let ctx = raw::pcap_dump_open_context();
682
        ctx.expect()
683
            .withf_st(move |arg1, _| *arg1 == pcap)
684
            .return_once(|_, _| std::ptr::null_mut());
685

            
686
        let _err = geterr_expect(pcap);
687

            
688
        let result = capture.savefile("path/to/nowhere");
689
        assert!(result.is_err());
690
    }
691

            
692
    #[test]
693
    #[cfg(libpcap_1_7_2)]
694
    fn test_savefile_append_error() {
695
        let _m = RAWMTX.lock();
696

            
697
        let mut value: isize = 777;
698
        let pcap = as_pcap_t(&mut value);
699

            
700
        let test_capture = test_capture::<Offline>(pcap);
701
        let capture = test_capture.capture;
702

            
703
        let ctx = raw::pcap_dump_open_append_context();
704
        ctx.expect()
705
            .withf_st(move |arg1, _| *arg1 == pcap)
706
            .return_once(|_, _| std::ptr::null_mut());
707

            
708
        let _err = geterr_expect(pcap);
709

            
710
        let result = capture.savefile_append("path/to/nowhere");
711
        assert!(result.is_err());
712
    }
713

            
714
    #[test]
715
    fn test_savefile_ops() {
716
        let _m = RAWMTX.lock();
717

            
718
        let mut value: isize = 888;
719
        let pcap_dumper = as_pcap_dumper_t(&mut value);
720

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

            
726
        let mut savefile = Savefile {
727
            handle: NonNull::new(pcap_dumper).unwrap(),
728
        };
729

            
730
        let ctx = raw::pcap_dump_context();
731
        ctx.expect()
732
            .withf_st(move |arg1, _, _| *arg1 == pcap_dumper as _)
733
            .return_once(|_, _, _| {});
734

            
735
        savefile.write(&PACKET);
736

            
737
        let ctx = raw::pcap_dump_flush_context();
738
        ctx.expect()
739
            .withf_st(move |arg1| *arg1 == pcap_dumper)
740
            .return_once(|_| 0);
741

            
742
        let result = savefile.flush();
743
        assert!(result.is_ok());
744

            
745
        let ctx = raw::pcap_dump_flush_context();
746
        ctx.checkpoint();
747
        ctx.expect()
748
            .withf_st(move |arg1| *arg1 == pcap_dumper)
749
            .return_once(|_| -1);
750

            
751
        let result = savefile.flush();
752
        assert!(result.is_err());
753
    }
754

            
755
    #[test]
756
    fn test_direction() {
757
        let _m = RAWMTX.lock();
758

            
759
        let mut value: isize = 777;
760
        let pcap = as_pcap_t(&mut value);
761

            
762
        let test_capture = test_capture::<Active>(pcap);
763
        let capture = test_capture.capture;
764

            
765
        let ctx = raw::pcap_setdirection_context();
766
        ctx.expect()
767
            .withf_st(move |arg1, arg2| (*arg1 == pcap) && (*arg2 == raw::PCAP_D_OUT))
768
            .return_once(|_, _| 0);
769

            
770
        let result = capture.direction(Direction::Out);
771
        assert!(result.is_ok());
772

            
773
        let ctx = raw::pcap_setdirection_context();
774
        ctx.checkpoint();
775
        ctx.expect()
776
            .withf_st(move |arg1, arg2| (*arg1 == pcap) && (*arg2 == raw::PCAP_D_OUT))
777
            .return_once(|_, _| -1);
778

            
779
        let _err = geterr_expect(pcap);
780

            
781
        let result = capture.direction(Direction::Out);
782
        assert!(result.is_err());
783

            
784
        // For code coverage of the derive line.
785
        assert_ne!(Direction::In, Direction::InOut);
786
        assert_ne!(Direction::In, Direction::Out);
787
        assert_ne!(Direction::InOut, Direction::Out);
788
    }
789

            
790
    #[test]
791
    fn test_next_packet() {
792
        let _m = RAWMTX.lock();
793

            
794
        let mut value: isize = 777;
795
        let pcap = as_pcap_t(&mut value);
796

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

            
800
        let _nxt = next_ex_expect(pcap);
801

            
802
        let next_packet = capture.next_packet().unwrap();
803
        assert_eq!(next_packet, PACKET);
804
    }
805

            
806
    #[test]
807
    fn test_next_packet_timeout() {
808
        let _m = RAWMTX.lock();
809

            
810
        let mut value: isize = 777;
811
        let pcap = as_pcap_t(&mut value);
812

            
813
        let test_capture = test_capture::<Active>(pcap);
814
        let mut capture = test_capture.capture;
815

            
816
        let ctx = raw::pcap_next_ex_context();
817
        ctx.expect()
818
            .withf_st(move |arg1, _, _| *arg1 == pcap)
819
            .return_once_st(move |_, _, _| 0);
820

            
821
        let err = capture.next_packet().unwrap_err();
822
        assert_eq!(err, Error::TimeoutExpired);
823
    }
824

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

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

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

            
835
        let ctx = raw::pcap_next_ex_context();
836
        ctx.expect()
837
            .withf_st(move |arg1, _, _| *arg1 == pcap)
838
            .return_once_st(move |_, _, _| -1);
839

            
840
        let _err = geterr_expect(pcap);
841

            
842
        let result = capture.next_packet();
843
        assert!(result.is_err());
844
    }
845

            
846
    #[test]
847
    fn test_next_packet_no_more_packets() {
848
        let _m = RAWMTX.lock();
849

            
850
        let mut value: isize = 777;
851
        let pcap = as_pcap_t(&mut value);
852

            
853
        let test_capture = test_capture::<Offline>(pcap);
854
        let mut capture = test_capture.capture;
855

            
856
        let ctx = raw::pcap_next_ex_context();
857
        ctx.expect()
858
            .withf_st(move |arg1, _, _| *arg1 == pcap)
859
            .return_once_st(move |_, _, _| -2);
860

            
861
        let err = capture.next_packet().unwrap_err();
862
        assert_eq!(err, Error::NoMorePackets);
863
    }
864

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

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

            
872
        let test_capture = test_capture::<Active>(pcap);
873
        let capture = test_capture.capture;
874

            
875
        let ctx = raw::pcap_compile_context();
876
        ctx.expect()
877
            .withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
878
            .return_once(|_, _, _, _, _| -1);
879

            
880
        let _err = geterr_expect(pcap);
881

            
882
        let ctx = raw::pcap_freecode_context();
883
        ctx.expect().return_once(|_| {});
884

            
885
        let result = capture.compile("some bpf program", false);
886
        assert!(result.is_err());
887

            
888
        let ctx = raw::pcap_compile_context();
889
        ctx.checkpoint();
890
        ctx.expect()
891
            .withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
892
            .return_once(|_, _, _, _, _| 0);
893

            
894
        let ctx = raw::pcap_freecode_context();
895
        ctx.checkpoint();
896
        ctx.expect().return_once(|_| {});
897

            
898
        let result = capture.compile("some bpf program", false);
899
        assert!(result.is_ok());
900
    }
901

            
902
    #[test]
903
    fn test_filter() {
904
        let _m = RAWMTX.lock();
905

            
906
        let mut value: isize = 777;
907
        let pcap = as_pcap_t(&mut value);
908

            
909
        let test_capture = test_capture::<Active>(pcap);
910
        let mut capture = test_capture.capture;
911

            
912
        let ctx = raw::pcap_compile_context();
913
        ctx.expect()
914
            .withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
915
            .return_once(|_, _, _, _, _| 0);
916

            
917
        let ctx = raw::pcap_setfilter_context();
918
        ctx.expect()
919
            .withf_st(move |arg1, _| *arg1 == pcap)
920
            .return_once(|_, _| -1);
921

            
922
        let _err = geterr_expect(pcap);
923

            
924
        let ctx = raw::pcap_freecode_context();
925
        ctx.expect().return_once(|_| {});
926

            
927
        let result = capture.filter("some bpf program", false);
928
        assert!(result.is_err());
929

            
930
        let ctx = raw::pcap_compile_context();
931
        ctx.checkpoint();
932
        ctx.expect()
933
            .withf_st(move |arg1, _, _, _, _| *arg1 == pcap)
934
            .return_once(|_, _, _, _, _| 0);
935

            
936
        let ctx = raw::pcap_setfilter_context();
937
        ctx.checkpoint();
938
        ctx.expect()
939
            .withf_st(move |arg1, _| *arg1 == pcap)
940
            .return_once(|_, _| 0);
941

            
942
        let ctx = raw::pcap_freecode_context();
943
        ctx.checkpoint();
944
        ctx.expect().return_once(|_| {});
945

            
946
        let result = capture.compile("some bpf program", false);
947
        assert!(result.is_ok());
948
    }
949

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

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

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

            
960
        let stat = raw::pcap_stat {
961
            ps_recv: 1,
962
            ps_drop: 2,
963
            ps_ifdrop: 3,
964
        };
965

            
966
        let ctx = raw::pcap_stats_context();
967
        ctx.expect()
968
            .withf_st(move |arg1, _| *arg1 == pcap)
969
            .return_once_st(move |_, arg2| {
970
                unsafe { *arg2 = stat };
971
                0
972
            });
973

            
974
        let stats = capture.stats().unwrap();
975
        assert_eq!(stats, Stat::new(stat.ps_recv, stat.ps_drop, stat.ps_ifdrop));
976

            
977
        let ctx = raw::pcap_stats_context();
978
        ctx.checkpoint();
979
        ctx.expect()
980
            .withf_st(move |arg1, _| *arg1 == pcap)
981
            .return_once_st(move |_, _| -1);
982

            
983
        let _err = geterr_expect(pcap);
984

            
985
        let result = capture.stats();
986
        assert!(result.is_err());
987
    }
988

            
989
    #[test]
990
    fn test_bpf_instruction_display() {
991
        let instr = BpfInstruction(raw::bpf_insn {
992
            code: 1,
993
            jt: 2,
994
            jf: 3,
995
            k: 4,
996
        });
997
        assert_eq!(format!("{}", instr), "1 2 3 4");
998
    }
999

            
    #[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);
    }
}