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
        let pkthdr_ptr: *mut raw::pcap_pkthdr = unsafe { std::ptr::addr_of_mut!(PKTHDR) };
463

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

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

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

            
492
    use super::*;
493

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

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

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

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

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

            
512
        let _err = geterr_expect(pcap);
513

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

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

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

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

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

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

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

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

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

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

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

            
565
        let _err = geterr_expect(pcap);
566

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            
685
        let _err = geterr_expect(pcap);
686

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

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

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

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

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

            
707
        let _err = geterr_expect(pcap);
708

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

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

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

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

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

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

            
734
        savefile.write(&PACKET);
735

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

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

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

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

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

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

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

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

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

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

            
778
        let _err = geterr_expect(pcap);
779

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

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

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

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

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

            
799
        let _nxt = next_ex_expect(pcap);
800

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

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

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

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

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

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

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

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

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

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

            
839
        let _err = geterr_expect(pcap);
840

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

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

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

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

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

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

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

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

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

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

            
879
        let _err = geterr_expect(pcap);
880

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

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

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

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

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

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

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

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

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

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

            
921
        let _err = geterr_expect(pcap);
922

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

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

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

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

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

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

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

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

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

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

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

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

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

            
982
        let _err = geterr_expect(pcap);
983

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

            
988
    #[test]
989
    fn test_bpf_instruction_display() {
990
        let instr = BpfInstruction(raw::bpf_insn {
991
            code: 1,
992
            jt: 2,
993
            jf: 3,
994
            k: 4,
995
        });
996
        assert_eq!(format!("{}", instr), "1 2 3 4");
997
    }
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);
    }
}