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
    ptr::{self, NonNull},
12
    sync::Arc,
13
};
14

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

            
18
use crate::{Error, raw};
19

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

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

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

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

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

            
41
impl Activated for Active {}
42

            
43
impl Activated for Offline {}
44

            
45
impl Activated for Dead {}
46

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

            
53
impl State for Inactive {}
54

            
55
impl State for Active {}
56

            
57
impl State for Offline {}
58

            
59
impl State for Dead {}
60

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

            
100
struct PcapHandle {
101
    handle: NonNull<raw::pcap_t>,
102
}
103

            
104
impl PcapHandle {
105
1014
    fn as_ptr(&self) -> *mut raw::pcap_t {
106
1014
        self.handle.as_ptr()
107
1014
    }
108
}
109

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

            
115
impl Drop for PcapHandle {
116
310
    fn drop(&mut self) {
117
310
        unsafe { raw::pcap_close(self.handle.as_ptr()) }
118
310
    }
119
}
120

            
121
unsafe impl<T: State + ?Sized> Send for Capture<T> {}
122

            
123
// `Capture` is not safe to implement Sync as the libpcap functions it uses are not promised to have
124
// thread-safe access to the same `raw::pcap_t *` from multiple threads.
125
#[allow(clippy::arc_with_non_send_sync)]
126
impl<T: State + ?Sized> From<NonNull<raw::pcap_t>> for Capture<T> {
127
310
    fn from(handle: NonNull<raw::pcap_t>) -> Self {
128
310
        Capture {
129
310
            nonblock: false,
130
310
            warning: None,
131
310
            handle: Arc::new(PcapHandle { handle }),
132
310
            _marker: PhantomData,
133
310
        }
134
310
    }
135
}
136

            
137
impl<T: State + ?Sized> Capture<T> {
138
58
    fn new_raw<F>(path: Option<CString>, func: F) -> Result<Capture<T>, Error>
139
58
    where
140
58
        F: FnOnce(*const libc::c_char, *mut libc::c_char) -> *mut raw::pcap_t,
141
    {
142
58
        Error::with_errbuf(|err| {
143
58
            let handle = match path {
144
8
                None => func(ptr::null(), err),
145
50
                Some(path) => func(path.as_ptr(), err),
146
            };
147
56
            Ok(Capture::from(
148
58
                NonNull::<raw::pcap_t>::new(handle).ok_or_else(|| unsafe { Error::new(err) })?,
149
            ))
150
58
        })
151
58
    }
152

            
153
24
    pub fn is_nonblock(&self) -> bool {
154
24
        self.nonblock
155
24
    }
156

            
157
22
    pub fn as_ptr(&self) -> *mut raw::pcap_t {
158
22
        self.handle.as_ptr()
159
22
    }
160

            
161
    /// Set the minumum amount of data received by the kernel in a single call.
162
    ///
163
    /// Note that this value is set to 0 when the capture is set to immediate mode. You should not
164
    /// call `min_to_copy` on captures in immediate mode if you want them to stay in immediate mode.
165
    #[cfg(windows)]
166
    pub fn min_to_copy(self, to: i32) -> Capture<T> {
167
        unsafe {
168
            raw::pcap_setmintocopy(self.handle.as_ptr(), to as _);
169
        }
170
        self
171
    }
172

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

            
190
82
    fn check_err(&self, success: bool) -> Result<(), Error> {
191
82
        if success { Ok(()) } else { Err(self.get_err()) }
192
82
    }
193

            
194
    /// Turn one of libpcap's status codes into an error, with the message it left behind.
195
12
    fn status_err(&self, status: libc::c_int) -> Error {
196
12
        unsafe { Error::from_status(status, raw::pcap_geterr(self.handle.as_ptr())) }
197
12
    }
198

            
199
36
    fn get_err(&self) -> Error {
200
36
        unsafe { Error::new(raw::pcap_geterr(self.handle.as_ptr())) }
201
36
    }
202

            
203
    /// Whether the capture is reading a savefile rather than an interface.
204
20
    fn reads_savefile(&self) -> bool {
205
20
        !unsafe { raw::pcap_file(self.handle.as_ptr()) }.is_null()
206
20
    }
207
}
208

            
209
#[repr(u32)]
210
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
211
/// Timestamp resolution types
212
///
213
/// Not all systems and interfaces will necessarily support all of these resolutions when doing
214
/// live captures; all of them can be requested when reading a safefile.
215
pub enum Precision {
216
    /// Use timestamps with microsecond precision. This is the default.
217
    Micro = 0,
218
    /// Use timestamps with nanosecond precision.
219
    Nano = 1,
220
}
221

            
222
/// What libpcap warned about while activating a capture.
223
///
224
/// A warning is not a failure. The capture works, but a requested option could not be applied.
225
/// [`Capture::warning`] reports the one an activation raised.
226
#[derive(Debug, PartialEq, Eq, Clone)]
227
pub struct Warning {
228
    /// The condition libpcap has a code of its own for
229
    pub code: WarningCode,
230
    /// The message libpcap left with it, empty if it left none
231
    pub message: String,
232
}
233

            
234
impl Warning {
235
    /// Read one of the warnings libpcap activates with, along with the message it left behind.
236
18
    unsafe fn from_status(status: libc::c_int, ptr: *const libc::c_char) -> Warning {
237
18
        let code = match status {
238
6
            raw::PCAP_WARNING_PROMISC_NOTSUP => WarningCode::PromiscuousModeNotSupported,
239
4
            raw::PCAP_WARNING_TSTAMP_TYPE_NOTSUP => WarningCode::TimestampTypeNotSupported,
240
8
            _ => WarningCode::Unspecified,
241
        };
242

            
243
18
        Warning {
244
18
            code,
245
18
            message: unsafe { Error::message(ptr) },
246
18
        }
247
18
    }
248
}
249

            
250
impl fmt::Display for Warning {
251
16
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252
16
        if self.message.is_empty() {
253
8
            write!(f, "libpcap warning: {}", self.code)
254
        } else {
255
8
            write!(f, "libpcap warning: {}", self.message)
256
        }
257
16
    }
258
}
259

            
260
/// A list of libpcap's own warning codes.
261
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
262
#[non_exhaustive]
263
pub enum WarningCode {
264
    /// Promiscuous mode was asked for but the device does not have it
265
    PromiscuousModeNotSupported,
266
    /// The device does not support the timestamp type that was set
267
    TimestampTypeNotSupported,
268
    /// Something libpcap has no code of its own for
269
    Unspecified,
270
}
271

            
272
impl fmt::Display for WarningCode {
273
16
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274
16
        f.write_str(match self {
275
            WarningCode::PromiscuousModeNotSupported => {
276
4
                "the device does not support promiscuous mode"
277
            }
278
            WarningCode::TimestampTypeNotSupported => {
279
4
                "the device does not support the timestamp type"
280
            }
281
8
            WarningCode::Unspecified => "the capture was activated with a warning",
282
        })
283
16
    }
284
}
285

            
286
// GRCOV_EXCL_START
287
#[cfg(test)]
288
pub mod testmod {
289
    use raw::testmod::RAWMTX;
290

            
291
    use super::*;
292

            
293
    pub struct TestCapture<T: State + ?Sized> {
294
        pub capture: Capture<T>,
295
        _close_ctx: raw::__pcap_close::Context,
296
    }
297

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

            
302
        let ctx = raw::pcap_close_context();
303
        ctx.checkpoint();
304
        ctx.expect()
305
            .withf_st(move |ptr| *ptr == pcap)
306
            .return_once(|_| {});
307

            
308
        TestCapture {
309
            capture: Capture::<T>::from(NonNull::new(pcap).unwrap()),
310
            _close_ctx: ctx,
311
        }
312
    }
313
}
314
// GRCOV_EXCL_STOP
315

            
316
#[cfg(test)]
317
mod tests {
318
    use crate::{
319
        capture::testmod::test_capture,
320
        raw::testmod::{RAWMTX, as_pcap_t},
321
    };
322

            
323
    use super::*;
324

            
325
    #[test]
326
    fn test_warning_from_status() {
327
        let message = CString::new("not quite").unwrap();
328
        let cases = [
329
            (
330
                raw::PCAP_WARNING_PROMISC_NOTSUP,
331
                WarningCode::PromiscuousModeNotSupported,
332
            ),
333
            (
334
                raw::PCAP_WARNING_TSTAMP_TYPE_NOTSUP,
335
                WarningCode::TimestampTypeNotSupported,
336
            ),
337
            (raw::PCAP_WARNING, WarningCode::Unspecified),
338
            // A code libpcap has yet to define still warns.
339
            (99, WarningCode::Unspecified),
340
        ];
341

            
342
        for (status, code) in cases {
343
            let warning = unsafe { Warning::from_status(status, message.as_ptr()) };
344
            assert_eq!(warning.code, code);
345
            assert_eq!(warning.to_string(), "libpcap warning: not quite");
346

            
347
            // With no message left behind, the code has to describe itself.
348
            let warning = unsafe { Warning::from_status(status, ptr::null()) };
349
            assert!(warning.message.is_empty());
350
            assert_eq!(warning.to_string(), format!("libpcap warning: {code}"));
351
        }
352
    }
353

            
354
    #[test]
355
    fn test_capture_getters() {
356
        let _m = RAWMTX.lock();
357

            
358
        let mut dummy: isize = 777;
359
        let pcap = as_pcap_t(&mut dummy);
360

            
361
        let test_capture = test_capture::<Active>(pcap);
362
        let capture = test_capture.capture;
363

            
364
        assert!(!capture.is_nonblock());
365
        assert_eq!(capture.as_ptr(), capture.handle.as_ptr());
366
    }
367

            
368
    #[test]
369
    #[cfg(windows)]
370
    fn test_min_to_copy() {
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
        let ctx = raw::pcap_setmintocopy_context();
380
        ctx.expect()
381
            .withf_st(move |arg1, _| *arg1 == pcap)
382
            .return_once(|_, _| 0);
383

            
384
        let _capture = capture.min_to_copy(5);
385
    }
386

            
387
    #[test]
388
    #[cfg(windows)]
389
    fn test_get_event() {
390
        let _m = RAWMTX.lock();
391

            
392
        let mut dummy: isize = 777;
393
        let pcap = as_pcap_t(&mut dummy);
394

            
395
        let test_capture = test_capture::<Active>(pcap);
396
        let capture = test_capture.capture;
397

            
398
        let ctx = raw::pcap_getevent_context();
399
        ctx.expect()
400
            .withf_st(move |arg1| *arg1 == pcap)
401
            .return_once(|_| 5 as HANDLE);
402

            
403
        let handle = unsafe { capture.get_event() };
404
        assert_eq!(handle, 5 as HANDLE);
405
    }
406

            
407
    #[test]
408
    fn test_precision() {
409
        assert_ne!(Precision::Micro, Precision::Nano);
410
    }
411
}