1
pub mod activated;
2
pub mod inactive;
3
#[cfg(all(not(windows), feature = "capture-stream"))]
4
#[cfg_attr(docsrs, doc(cfg(all(not(windows), feature = "capture-stream"))))]
5
pub mod selectable;
6

            
7
use std::{
8
    ffi::CString,
9
    fmt,
10
    marker::PhantomData,
11
    mem::MaybeUninit,
12
    ptr::{self, NonNull},
13
    sync::Arc,
14
};
15

            
16
#[cfg(windows)]
17
use windows_sys::Win32::Foundation::HANDLE;
18

            
19
use crate::{Error, packet::PacketHeader, raw};
20

            
21
/// Phantom type representing an inactive capture handle.
22
pub enum Inactive {}
23

            
24
/// Phantom type representing an active capture handle.
25
pub enum Active {}
26

            
27
/// Phantom type representing an offline capture handle, from a pcap dump file.
28
/// Implements `Activated` because it behaves nearly the same as a live handle.
29
pub enum Offline {}
30

            
31
/// Phantom type representing a dead capture handle.  This can be use to create
32
/// new save files that are not generated from an active capture.
33
/// Implements `Activated` because it behaves nearly the same as a live handle.
34
pub enum Dead {}
35

            
36
/// `Capture`s can be in different states at different times, and in these states they
37
/// may or may not have particular capabilities. This trait is implemented by phantom
38
/// types which allows us to punt these invariants to the type system to avoid runtime
39
/// errors.
40
pub trait Activated: State {}
41

            
42
impl Activated for Active {}
43

            
44
impl Activated for Offline {}
45

            
46
impl Activated for Dead {}
47

            
48
/// `Capture`s can be in different states at different times, and in these states they
49
/// may or may not have particular capabilities. This trait is implemented by phantom
50
/// types which allows us to punt these invariants to the type system to avoid runtime
51
/// errors.
52
pub trait State {}
53

            
54
impl State for Inactive {}
55

            
56
impl State for Active {}
57

            
58
impl State for Offline {}
59

            
60
impl State for Dead {}
61

            
62
/// This is a pcap capture handle which is an abstraction over the `pcap_t` provided by pcap.
63
/// There are many ways to instantiate and interact with a pcap handle, so phantom types are
64
/// used to express these behaviors.
65
///
66
/// **`Capture<Inactive>`** is created via `Capture::from_device()`. This handle is inactive,
67
/// so you cannot (yet) obtain packets from it. However, you can configure things like the
68
/// buffer size, snaplen, timeout, and promiscuity before you activate it.
69
///
70
/// **`Capture<Active>`** is created by calling `.open()` on a `Capture<Inactive>`. This
71
/// activates the capture handle, allowing you to get packets with `.next_packet()` or apply filters
72
/// with `.filter()`.
73
///
74
/// **`Capture<Offline>`** is created via `Capture::from_file()`. This allows you to read a
75
/// pcap format dump file as if you were opening an interface -- very useful for testing or
76
/// analysis.
77
///
78
/// **`Capture<Dead>`** is created via `Capture::dead()`. This allows you to create a pcap
79
/// format dump file without needing an active capture.
80
///
81
/// # Example:
82
///
83
/// ```no_run
84
/// # use pcap::{Capture, Device};
85
/// let mut cap = Capture::from_device(Device::lookup().unwrap().unwrap()) // open the "default" interface
86
///               .unwrap() // assume the device exists and we are authorized to open it
87
///               .open() // activate the handle
88
///               .unwrap(); // assume activation worked
89
///
90
/// while let Ok(packet) = cap.next_packet() {
91
///     println!("received packet! {:?}", packet);
92
/// }
93
/// ```
94
pub struct Capture<T: State + ?Sized> {
95
    nonblock: bool,
96
    warning: Option<Warning>,
97
    #[cfg(windows)]
98
    min_to_copy: Option<i32>,
99
    // Holds the header that `next_packet` converts when libpcap lays it out differently from
100
    // `PacketHeader`.
101
    header: MaybeUninit<PacketHeader>,
102
    handle: Arc<PcapHandle>,
103
    _marker: PhantomData<T>,
104
}
105

            
106
struct PcapHandle {
107
    handle: NonNull<raw::pcap_t>,
108
}
109

            
110
impl PcapHandle {
111
700
    fn as_ptr(&self) -> *mut raw::pcap_t {
112
700
        self.handle.as_ptr()
113
700
    }
114
}
115

            
116
// `PcapHandle` is safe to Send as it encapsulates the entire lifetime of `raw::pcap_t *`
117
// `PcapHandle` is only Sync under special circumstances when used in thread-safe functions such as
118
// the `pcap_breakloop` function. The Sync correctness is left to the wrapping structure to provide.
119
unsafe impl Send for PcapHandle {}
120

            
121
impl Drop for PcapHandle {
122
194
    fn drop(&mut self) {
123
194
        unsafe { raw::pcap_close(self.handle.as_ptr()) }
124
194
    }
125
}
126

            
127
unsafe impl<T: State + ?Sized> Send for Capture<T> {}
128

            
129
impl<T: State + ?Sized> Capture<T> {
130
    // `Capture` is not safe to implement Sync as the libpcap functions it uses are not promised to
131
    // have thread-safe access to the same `raw::pcap_t *` from multiple threads.
132
    #[allow(clippy::arc_with_non_send_sync)]
133
194
    fn from_handle(handle: NonNull<raw::pcap_t>) -> Self {
134
194
        Capture {
135
194
            nonblock: false,
136
194
            warning: None,
137
194
            #[cfg(windows)]
138
194
            min_to_copy: None,
139
194
            header: MaybeUninit::uninit(),
140
194
            handle: Arc::new(PcapHandle { handle }),
141
194
            _marker: PhantomData,
142
194
        }
143
194
    }
144

            
145
31
    fn new_raw<F>(path: Option<CString>, func: F) -> Result<Capture<T>, Error>
146
31
    where
147
31
        F: FnOnce(&raw::Library, *const libc::c_char, *mut libc::c_char) -> *mut raw::pcap_t,
148
    {
149
31
        let library = raw::require_library()?;
150

            
151
31
        Error::with_errbuf(|err| {
152
31
            let handle = match path {
153
6
                None => func(&library, ptr::null(), err),
154
25
                Some(path) => func(&library, path.as_ptr(), err),
155
            };
156
30
            Ok(Capture::from_handle(
157
31
                NonNull::<raw::pcap_t>::new(handle).ok_or_else(|| unsafe { Error::new(err) })?,
158
            ))
159
31
        })
160
31
    }
161

            
162
12
    pub fn is_nonblock(&self) -> bool {
163
12
        self.nonblock
164
12
    }
165

            
166
11
    pub fn as_ptr(&self) -> *mut raw::pcap_t {
167
11
        self.handle.as_ptr()
168
11
    }
169

            
170
    /// Set the minumum amount of data received by the kernel in a single call.
171
    ///
172
    /// Note that this value is set to 0 when the capture is set to immediate mode. You should not
173
    /// call `min_to_copy` on captures in immediate mode if you want them to stay in immediate mode.
174
    #[cfg(windows)]
175
    pub fn min_to_copy(self, to: i32) -> Capture<T> {
176
        unsafe {
177
            raw::pcap_setmintocopy(self.handle.as_ptr(), to as _);
178
        }
179
        self
180
    }
181

            
182
    /// Get handle to the Capture context's internal Win32 event semaphore.
183
    ///
184
    /// Setting this event will cause a blocking capture call to unblock and return.
185
    ///
186
    /// # Example
187
    /// The _winevt_ example demonstrates how to use the event semaphore to send command requests
188
    /// to a capture loop running in a separate thread.
189
    ///
190
    /// # Safety
191
    ///
192
    /// The caller must ensure that the `Capture` context outlives the returned `HANDLE` since it is
193
    /// a kernel object owned by the `Capture`'s pcap context.
194
    #[cfg(windows)]
195
    pub unsafe fn get_event(&self) -> HANDLE {
196
        unsafe { raw::pcap_getevent(self.handle.as_ptr()) }
197
    }
198

            
199
41
    fn check_err(&self, success: bool) -> Result<(), Error> {
200
41
        if success { Ok(()) } else { Err(self.get_err()) }
201
41
    }
202

            
203
    /// Turn one of libpcap's status codes into an error, with the message it left behind.
204
6
    fn status_err(&self, status: libc::c_int) -> Error {
205
6
        unsafe { Error::from_status(status, raw::pcap_geterr(self.handle.as_ptr())) }
206
6
    }
207

            
208
18
    fn get_err(&self) -> Error {
209
18
        unsafe { Error::new(raw::pcap_geterr(self.handle.as_ptr())) }
210
18
    }
211

            
212
    /// Whether the capture is reading a savefile rather than an interface.
213
10
    fn reads_savefile(&self) -> bool {
214
10
        !unsafe { raw::pcap_file(self.handle.as_ptr()) }.is_null()
215
10
    }
216
}
217

            
218
#[repr(u32)]
219
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
220
/// Timestamp resolution types
221
///
222
/// Not all systems and interfaces will necessarily support all of these resolutions when doing
223
/// live captures; all of them can be requested when reading a safefile.
224
pub enum Precision {
225
    /// Use timestamps with microsecond precision. This is the default.
226
    Micro = 0,
227
    /// Use timestamps with nanosecond precision.
228
    Nano = 1,
229
}
230

            
231
/// What libpcap warned about while activating a capture.
232
///
233
/// A warning is not a failure. The capture works, but a requested option could not be applied.
234
/// [`Capture::warning`] reports the one an activation raised.
235
#[derive(Debug, PartialEq, Eq, Clone)]
236
pub struct Warning {
237
    /// The condition libpcap has a code of its own for
238
    pub code: WarningCode,
239
    /// The message libpcap left with it, empty if it left none
240
    pub message: String,
241
}
242

            
243
impl Warning {
244
    /// Read one of the warnings libpcap activates with, along with the message it left behind.
245
9
    unsafe fn from_status(status: libc::c_int, ptr: *const libc::c_char) -> Warning {
246
9
        let code = match status {
247
3
            raw::PCAP_WARNING_PROMISC_NOTSUP => WarningCode::PromiscuousModeNotSupported,
248
2
            raw::PCAP_WARNING_TSTAMP_TYPE_NOTSUP => WarningCode::TimestampTypeNotSupported,
249
4
            _ => WarningCode::Unspecified,
250
        };
251

            
252
9
        Warning {
253
9
            code,
254
9
            message: unsafe { Error::message(ptr) },
255
9
        }
256
9
    }
257
}
258

            
259
impl fmt::Display for Warning {
260
8
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261
8
        if self.message.is_empty() {
262
4
            write!(f, "libpcap warning: {}", self.code)
263
        } else {
264
4
            write!(f, "libpcap warning: {}", self.message)
265
        }
266
8
    }
267
}
268

            
269
/// A list of libpcap's own warning codes.
270
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
271
#[non_exhaustive]
272
pub enum WarningCode {
273
    /// Promiscuous mode was asked for but the device does not have it
274
    PromiscuousModeNotSupported,
275
    /// The device does not support the timestamp type that was set
276
    TimestampTypeNotSupported,
277
    /// Something libpcap has no code of its own for
278
    Unspecified,
279
}
280

            
281
impl fmt::Display for WarningCode {
282
8
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283
8
        f.write_str(match self {
284
            WarningCode::PromiscuousModeNotSupported => {
285
2
                "the device does not support promiscuous mode"
286
            }
287
            WarningCode::TimestampTypeNotSupported => {
288
2
                "the device does not support the timestamp type"
289
            }
290
4
            WarningCode::Unspecified => "the capture was activated with a warning",
291
        })
292
8
    }
293
}
294

            
295
// GRCOV_EXCL_START
296
#[cfg(test)]
297
pub mod testmod {
298
    use raw::testmod::RAWMTX;
299

            
300
    use super::*;
301
    use crate::packet::{self, Layout};
302

            
303
    pub struct TestCapture<T: State + ?Sized> {
304
        pub capture: Capture<T>,
305
        _close_ctx: raw::__pcap_close::Context,
306
        _layout_ctx: packet::__layout::Context,
307
    }
308

            
309
    pub fn test_capture<T: State + ?Sized>(pcap: *mut raw::pcap_t) -> TestCapture<T> {
310
        // Lock must be acquired by caller.
311
        assert!(RAWMTX.try_lock().is_err());
312

            
313
        let ctx = raw::pcap_close_context();
314
        ctx.checkpoint();
315
        ctx.expect()
316
            .withf_st(move |ptr| *ptr == pcap)
317
            .return_once(|_| {});
318

            
319
        let layout_ctx = packet::layout_context();
320
        layout_ctx.expect().times(..).return_const(Layout::Native);
321

            
322
        TestCapture {
323
            capture: Capture::<T>::from_handle(NonNull::new(pcap).unwrap()),
324
            _close_ctx: ctx,
325
            _layout_ctx: layout_ctx,
326
        }
327
    }
328
}
329
// GRCOV_EXCL_STOP
330

            
331
#[cfg(test)]
332
mod tests {
333
    use crate::{
334
        capture::testmod::test_capture,
335
        raw::testmod::{RAWMTX, as_pcap_t},
336
    };
337

            
338
    use super::*;
339

            
340
    #[test]
341
    fn test_warning_from_status() {
342
        let message = CString::new("not quite").unwrap();
343
        let cases = [
344
            (
345
                raw::PCAP_WARNING_PROMISC_NOTSUP,
346
                WarningCode::PromiscuousModeNotSupported,
347
            ),
348
            (
349
                raw::PCAP_WARNING_TSTAMP_TYPE_NOTSUP,
350
                WarningCode::TimestampTypeNotSupported,
351
            ),
352
            (raw::PCAP_WARNING, WarningCode::Unspecified),
353
            // A code libpcap has yet to define still warns.
354
            (99, WarningCode::Unspecified),
355
        ];
356

            
357
        for (status, code) in cases {
358
            let warning = unsafe { Warning::from_status(status, message.as_ptr()) };
359
            assert_eq!(warning.code, code);
360
            assert_eq!(warning.to_string(), "libpcap warning: not quite");
361

            
362
            // With no message left behind, the code has to describe itself.
363
            let warning = unsafe { Warning::from_status(status, ptr::null()) };
364
            assert!(warning.message.is_empty());
365
            assert_eq!(warning.to_string(), format!("libpcap warning: {code}"));
366
        }
367
    }
368

            
369
    #[test]
370
    fn test_capture_getters() {
371
        let _m = RAWMTX.lock();
372

            
373
        let mut dummy: isize = 777;
374
        let pcap = as_pcap_t(&mut dummy);
375

            
376
        let test_capture = test_capture::<Active>(pcap);
377
        let capture = test_capture.capture;
378

            
379
        assert!(!capture.is_nonblock());
380
        assert_eq!(capture.as_ptr(), capture.handle.as_ptr());
381
    }
382

            
383
    #[test]
384
    #[cfg(windows)]
385
    fn test_min_to_copy() {
386
        let _m = RAWMTX.lock();
387

            
388
        let mut dummy: isize = 777;
389
        let pcap = as_pcap_t(&mut dummy);
390

            
391
        let test_capture = test_capture::<Active>(pcap);
392
        let capture = test_capture.capture;
393

            
394
        let ctx = raw::pcap_setmintocopy_context();
395
        ctx.expect()
396
            .withf_st(move |arg1, _| *arg1 == pcap)
397
            .return_once(|_, _| 0);
398

            
399
        let _capture = capture.min_to_copy(5);
400
    }
401

            
402
    #[test]
403
    #[cfg(windows)]
404
    fn test_get_event() {
405
        let _m = RAWMTX.lock();
406

            
407
        let mut dummy: isize = 777;
408
        let pcap = as_pcap_t(&mut dummy);
409

            
410
        let test_capture = test_capture::<Active>(pcap);
411
        let capture = test_capture.capture;
412

            
413
        let ctx = raw::pcap_getevent_context();
414
        ctx.expect()
415
            .withf_st(move |arg1| *arg1 == pcap)
416
            .return_once(|_| 5 as HANDLE);
417

            
418
        let handle = unsafe { capture.get_event() };
419
        assert_eq!(handle, 5 as HANDLE);
420
    }
421

            
422
    #[test]
423
    fn test_precision() {
424
        assert_ne!(Precision::Micro, Precision::Nano);
425
    }
426
}