1
use std::mem;
2

            
3
use crate::{
4
    capture::{Active, Capture, Inactive},
5
    device::Device,
6
    raw, Error,
7
};
8

            
9
#[cfg(libpcap_1_5_0)]
10
use crate::capture::Precision;
11

            
12
impl Capture<Inactive> {
13
    /// Opens a capture handle for a device. You can pass a `Device` or an `&str` device
14
    /// name here. The handle is inactive, but can be activated via `.open()`.
15
    ///
16
    /// # Example
17
    /// ```
18
    /// use pcap::*;
19
    ///
20
    /// // Usage 1: Capture from a single owned device
21
    /// let dev: Device = pcap::Device::lookup()
22
    ///     .expect("device lookup failed")
23
    ///     .expect("no device available");
24
    /// let cap1 = Capture::from_device(dev);
25
    ///
26
    /// // Usage 2: Capture from an element of device list.
27
    /// let list: Vec<Device> = pcap::Device::list().unwrap();
28
    /// let cap2 = Capture::from_device(list[0].clone());
29
    ///
30
    /// // Usage 3: Capture from `&str` device name
31
    /// let cap3 = Capture::from_device("eth0");
32
    /// ```
33
6
    pub fn from_device<D: Into<Device>>(device: D) -> Result<Capture<Inactive>, Error> {
34
6
        let device: Device = device.into();
35
9
        Capture::new_raw(Some(&device.name), |name, err| unsafe {
36
6
            raw::pcap_create(name, err)
37
9
        })
38
6
    }
39

            
40
    /// Activates an inactive capture created from `Capture::from_device()` or returns an error.
41
6
    pub fn open(self) -> Result<Capture<Active>, Error> {
42
6
        unsafe {
43
6
            self.check_err(raw::pcap_activate(self.handle.as_ptr()) == 0)?;
44
4
            Ok(mem::transmute::<Capture<Inactive>, Capture<Active>>(self))
45
        }
46
6
    }
47

            
48
    /// Set the read timeout for the Capture. By default, this is 0, so it will block indefinitely.
49
2
    pub fn timeout(self, ms: i32) -> Capture<Inactive> {
50
2
        unsafe { raw::pcap_set_timeout(self.handle.as_ptr(), ms) };
51
2
        self
52
2
    }
53

            
54
    /// Set the time stamp type to be used by a capture device.
55
    #[cfg(libpcap_1_2_1)]
56
2
    pub fn tstamp_type(self, tstamp_type: TimestampType) -> Capture<Inactive> {
57
2
        unsafe { raw::pcap_set_tstamp_type(self.handle.as_ptr(), tstamp_type as _) };
58
2
        self
59
2
    }
60

            
61
    /// Set promiscuous mode on or off. By default, this is off.
62
2
    pub fn promisc(self, to: bool) -> Capture<Inactive> {
63
2
        unsafe { raw::pcap_set_promisc(self.handle.as_ptr(), to as _) };
64
2
        self
65
2
    }
66

            
67
    /// Set immediate mode on or off. By default, this is off.
68
    ///
69
    /// Note that in WinPcap immediate mode is set by passing a 0 argument to `min_to_copy`.
70
    /// Immediate mode will be unset if `min_to_copy` is later called with a non-zero argument.
71
    /// Immediate mode is unset by resetting `min_to_copy` to the WinPcap default possibly changing
72
    /// a previously set value. When using `min_to_copy`, it is best to avoid `immediate_mode`.
73
    #[cfg(any(libpcap_1_5_0, windows))]
74
4
    pub fn immediate_mode(self, to: bool) -> Capture<Inactive> {
75
4
        // Prior to 1.5.0 when `pcap_set_immediate_mode` was introduced, the necessary steps to set
76
4
        // immediate mode were more complicated, depended on the OS, and in some configurations had
77
4
        // to be set on an active capture. See
78
4
        // https://www.tcpdump.org/manpages/pcap_set_immediate_mode.3pcap.html. Since we do not
79
4
        // expect pre-1.5.0 version on unix systems in the wild, we simply ignore those cases.
80
4
        #[cfg(libpcap_1_5_0)]
81
4
        unsafe {
82
4
            raw::pcap_set_immediate_mode(self.handle.as_ptr(), to as _)
83
4
        };
84
4

            
85
4
        // In WinPcap we use `pcap_setmintocopy` as it does not have `pcap_set_immediate_mode`.
86
4
        #[cfg(all(windows, not(libpcap_1_5_0)))]
87
4
        unsafe {
88
4
            raw::pcap_setmintocopy(
89
4
                self.handle.as_ptr(),
90
4
                if to {
91
4
                    0
92
4
                } else {
93
4
                    raw::WINPCAP_MINTOCOPY_DEFAULT
94
4
                },
95
4
            )
96
4
        };
97
4

            
98
4
        self
99
4
    }
100

            
101
    /// Set want_pktap to true or false. The default is maintained by libpcap.
102
    #[cfg(all(libpcap_1_5_3, target_os = "macos"))]
103
    pub fn want_pktap(self, to: bool) -> Capture<Inactive> {
104
        unsafe { raw::pcap_set_want_pktap(self.handle.as_ptr(), to as _) };
105

            
106
        self
107
    }
108

            
109
    /// Set rfmon mode on or off. The default is maintained by pcap.
110
    #[cfg(not(windows))]
111
2
    pub fn rfmon(self, to: bool) -> Capture<Inactive> {
112
2
        unsafe { raw::pcap_set_rfmon(self.handle.as_ptr(), to as _) };
113
2
        self
114
2
    }
115

            
116
    /// Set the buffer size for incoming packet data.
117
    ///
118
    /// The default is 1000000. This should always be larger than the snaplen.
119
2
    pub fn buffer_size(self, to: i32) -> Capture<Inactive> {
120
2
        unsafe { raw::pcap_set_buffer_size(self.handle.as_ptr(), to) };
121
2
        self
122
2
    }
123

            
124
    /// Set the time stamp precision returned in captures.
125
    #[cfg(libpcap_1_5_0)]
126
2
    pub fn precision(self, precision: Precision) -> Capture<Inactive> {
127
2
        unsafe { raw::pcap_set_tstamp_precision(self.handle.as_ptr(), precision as _) };
128
2
        self
129
2
    }
130

            
131
    /// Set the snaplen size (the maximum length of a packet captured into the buffer).
132
    /// Useful if you only want certain headers, but not the entire packet.
133
    ///
134
    /// The default is 65535.
135
2
    pub fn snaplen(self, to: i32) -> Capture<Inactive> {
136
2
        unsafe { raw::pcap_set_snaplen(self.handle.as_ptr(), to) };
137
2
        self
138
2
    }
139
}
140

            
141
#[repr(i32)]
142
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
143
/// Timestamp types
144
///
145
/// Not all systems and interfaces will necessarily support all of these.
146
///
147
/// Note that time stamps synchronized with the system clock can go backwards, as the system clock
148
/// can go backwards.  If a clock is not in sync with the system clock, that could be because the
149
/// system clock isn't keeping accurate time, because the other clock isn't keeping accurate time,
150
/// or both.
151
///
152
/// Note that host-provided time stamps generally correspond to the time when the time-stamping
153
/// code sees the packet; this could be some unknown amount of time after the first or last bit of
154
/// the packet is received by the network adapter, due to batching of interrupts for packet
155
/// arrival, queueing delays, etc..
156
pub enum TimestampType {
157
    /// Timestamps are provided by the host machine, rather than by the capture device.
158
    ///
159
    /// The characteristics of the timestamp are unknown.
160
    Host = 0,
161
    /// A timestamp provided by the host machine that is low precision but relatively cheap to
162
    /// fetch.
163
    ///
164
    /// This is normally done using the system clock, so it's normally synchornized with times
165
    /// you'd fetch from system calls.
166
    HostLowPrec = 1,
167
    /// A timestamp provided by the host machine that is high precision. It might be more expensive
168
    /// to fetch.
169
    ///
170
    /// The timestamp might or might not be synchronized with the system clock, and might have
171
    /// problems with time stamps for packets received on different CPUs, depending on the
172
    /// platform.
173
    HostHighPrec = 2,
174
    /// The timestamp is a high-precision time stamp supplied by the capture device.
175
    ///
176
    /// The timestamp is synchronized with the system clock.
177
    Adapter = 3,
178
    /// The timestamp is a high-precision time stamp supplied by the capture device.
179
    ///
180
    /// The timestamp is not synchronized with the system clock.
181
    AdapterUnsynced = 4,
182
}
183

            
184
#[cfg(test)]
185
mod tests {
186
    use crate::{
187
        capture::testmod::test_capture,
188
        raw::testmod::{as_pcap_t, geterr_expect, RAWMTX},
189
    };
190

            
191
    use super::*;
192

            
193
    #[test]
194
    fn test_from_device() {
195
        let _m = RAWMTX.lock();
196

            
197
        let mut dummy: isize = 777;
198
        let pcap = as_pcap_t(&mut dummy);
199

            
200
        let ctx = raw::pcap_create_context();
201
        ctx.expect().return_once_st(move |_, _| pcap);
202

            
203
        let ctx = raw::pcap_close_context();
204
        ctx.expect()
205
            .withf_st(move |ptr| *ptr == pcap)
206
            .return_once(|_| {});
207

            
208
        let result = Capture::from_device("some_device");
209
        assert!(result.is_ok());
210
    }
211

            
212
    #[test]
213
    fn test_from_device_error() {
214
        let _m = RAWMTX.lock();
215

            
216
        let ctx = raw::pcap_create_context();
217
        ctx.expect().return_once_st(|_, _| std::ptr::null_mut());
218

            
219
        let result = Capture::from_device("some_device");
220
        assert!(result.is_err());
221
    }
222

            
223
    #[test]
224
    fn test_open() {
225
        let _m = RAWMTX.lock();
226

            
227
        let mut dummy: isize = 777;
228
        let pcap = as_pcap_t(&mut dummy);
229

            
230
        let test_capture = test_capture::<Inactive>(pcap);
231
        let capture = test_capture.capture;
232

            
233
        let ctx = raw::pcap_activate_context();
234
        ctx.expect()
235
            .withf_st(move |arg1| *arg1 == pcap)
236
            .return_once(|_| 0);
237

            
238
        let result = capture.open();
239
        assert!(result.is_ok());
240
    }
241

            
242
    #[test]
243
    fn test_open_error() {
244
        let _m = RAWMTX.lock();
245

            
246
        let mut dummy: isize = 777;
247
        let pcap = as_pcap_t(&mut dummy);
248

            
249
        let test_capture = test_capture::<Inactive>(pcap);
250
        let capture = test_capture.capture;
251

            
252
        let ctx = raw::pcap_activate_context();
253
        ctx.expect()
254
            .withf_st(move |arg1| *arg1 == pcap)
255
            .return_once(|_| -1);
256

            
257
        let _err = geterr_expect(pcap);
258

            
259
        let result = capture.open();
260
        assert!(result.is_err());
261
    }
262

            
263
    #[test]
264
    fn test_timeout() {
265
        let _m = RAWMTX.lock();
266

            
267
        let mut dummy: isize = 777;
268
        let pcap = as_pcap_t(&mut dummy);
269

            
270
        let test_capture = test_capture::<Inactive>(pcap);
271
        let capture = test_capture.capture;
272

            
273
        let ctx = raw::pcap_set_timeout_context();
274
        ctx.expect()
275
            .withf_st(move |arg1, _| *arg1 == pcap)
276
            .return_once(|_, _| 0);
277

            
278
        let _capture = capture.timeout(5);
279
    }
280

            
281
    #[test]
282
    #[cfg(libpcap_1_2_1)]
283
    fn test_timstamp_type() {
284
        let _m = RAWMTX.lock();
285

            
286
        let mut dummy: isize = 777;
287
        let pcap = as_pcap_t(&mut dummy);
288

            
289
        let test_capture = test_capture::<Inactive>(pcap);
290
        let capture = test_capture.capture;
291

            
292
        let ctx = raw::pcap_set_tstamp_type_context();
293
        ctx.expect()
294
            .withf_st(move |arg1, _| *arg1 == pcap)
295
            .return_once(|_, _| 0);
296

            
297
        let _capture = capture.tstamp_type(TimestampType::Host);
298

            
299
        // For code coverage of the derive line.
300
        assert_ne!(TimestampType::Host, TimestampType::HostLowPrec);
301
        assert_ne!(TimestampType::Host, TimestampType::HostHighPrec);
302
    }
303

            
304
    #[test]
305
    fn test_promisc() {
306
        let _m = RAWMTX.lock();
307

            
308
        let mut dummy: isize = 777;
309
        let pcap = as_pcap_t(&mut dummy);
310

            
311
        let test_capture = test_capture::<Inactive>(pcap);
312
        let capture = test_capture.capture;
313

            
314
        let ctx = raw::pcap_set_promisc_context();
315
        ctx.expect()
316
            .withf_st(move |arg1, _| *arg1 == pcap)
317
            .return_once(|_, _| 0);
318

            
319
        let _capture = capture.promisc(true);
320
    }
321

            
322
    #[cfg(libpcap_1_5_0)]
323
    struct ImmediateModeExpect(raw::__pcap_set_immediate_mode::Context);
324

            
325
    #[cfg(all(windows, not(libpcap_1_5_0)))]
326
    struct ImmediateModeExpect(raw::__pcap_setmintocopy::Context);
327

            
328
    #[cfg(any(libpcap_1_5_0, windows))]
329
    fn immediate_mode_expect(pcap: *mut raw::pcap_t) -> ImmediateModeExpect {
330
        #[cfg(libpcap_1_5_0)]
331
        {
332
            let ctx = raw::pcap_set_immediate_mode_context();
333
            ctx.checkpoint();
334
            ctx.expect()
335
                .withf_st(move |arg1, _| *arg1 == pcap)
336
                .return_once(|_, _| 0);
337
            ImmediateModeExpect(ctx)
338
        }
339
        #[cfg(all(windows, not(libpcap_1_5_0)))]
340
        {
341
            let ctx = raw::pcap_setmintocopy_context();
342
            ctx.checkpoint();
343
            ctx.expect()
344
                .withf_st(move |arg1, _| *arg1 == pcap)
345
                .return_once(|_, _| 0);
346
            ImmediateModeExpect(ctx)
347
        }
348
    }
349

            
350
    #[test]
351
    #[cfg(any(libpcap_1_5_0, windows))]
352
    fn test_immediate_mode() {
353
        let _m = RAWMTX.lock();
354

            
355
        let mut dummy: isize = 777;
356
        let pcap = as_pcap_t(&mut dummy);
357

            
358
        let test_capture = test_capture::<Inactive>(pcap);
359
        let capture = test_capture.capture;
360

            
361
        let _ctx = immediate_mode_expect(pcap);
362
        let capture = capture.immediate_mode(true);
363

            
364
        let _ctx = immediate_mode_expect(pcap);
365
        let _capture = capture.immediate_mode(false);
366
    }
367

            
368
    #[test]
369
    #[cfg(all(libpcap_1_5_3, target_os = "macos"))]
370
    fn test_want_pktap() {
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::<Inactive>(pcap);
377
        let capture = test_capture.capture;
378

            
379
        let ctx = raw::pcap_set_want_pktap_context();
380
        ctx.expect()
381
            .withf_st(move |arg1, _| *arg1 == pcap)
382
            .return_once(|_, _| 0);
383
        let _capture = capture.want_pktap(true);
384
    }
385

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

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

            
394
        let test_capture = test_capture::<Inactive>(pcap);
395
        let capture = test_capture.capture;
396

            
397
        let ctx = raw::pcap_set_rfmon_context();
398
        ctx.expect()
399
            .withf_st(move |arg1, _| *arg1 == pcap)
400
            .return_once(|_, _| 0);
401

            
402
        let _capture = capture.rfmon(true);
403
    }
404

            
405
    #[test]
406
    fn test_buffer_size() {
407
        let _m = RAWMTX.lock();
408

            
409
        let mut dummy: isize = 777;
410
        let pcap = as_pcap_t(&mut dummy);
411

            
412
        let test_capture = test_capture::<Inactive>(pcap);
413
        let capture = test_capture.capture;
414

            
415
        let ctx = raw::pcap_set_buffer_size_context();
416
        ctx.expect()
417
            .withf_st(move |arg1, _| *arg1 == pcap)
418
            .return_once(|_, _| 0);
419

            
420
        let _capture = capture.buffer_size(10);
421
    }
422

            
423
    #[test]
424
    #[cfg(libpcap_1_5_0)]
425
    fn test_precision() {
426
        let _m = RAWMTX.lock();
427

            
428
        let mut dummy: isize = 777;
429
        let pcap = as_pcap_t(&mut dummy);
430

            
431
        let test_capture = test_capture::<Inactive>(pcap);
432
        let capture = test_capture.capture;
433

            
434
        let ctx = raw::pcap_set_tstamp_precision_context();
435
        ctx.expect()
436
            .withf_st(move |arg1, _| *arg1 == pcap)
437
            .return_once(|_, _| 0);
438

            
439
        let _capture = capture.precision(Precision::Nano);
440
    }
441

            
442
    #[test]
443
    fn test_snaplen() {
444
        let _m = RAWMTX.lock();
445

            
446
        let mut dummy: isize = 777;
447
        let pcap = as_pcap_t(&mut dummy);
448

            
449
        let test_capture = test_capture::<Inactive>(pcap);
450
        let capture = test_capture.capture;
451

            
452
        let ctx = raw::pcap_set_snaplen_context();
453
        ctx.expect()
454
            .withf_st(move |arg1, _| *arg1 == pcap)
455
            .return_once(|_, _| 0);
456

            
457
        let _capture = capture.snaplen(10);
458
    }
459
}