1
use std::ffi::CString;
2
use std::mem;
3

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

            
11
#[cfg(libpcap_1_5_0)]
12
use crate::capture::Precision;
13

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

            
43
    /// Activates an inactive capture created from `Capture::from_device()` or returns an error.
44
    ///
45
    /// libpcap activates the capture but warns about it when it cannot honor every request,
46
    /// such as on a device with no promiscuous mode. The capture is usable, so a warning is not
47
    /// an error; [`Capture::warning`] is where it can be read.
48
10
    pub fn open(self) -> Result<Capture<Active>, Error> {
49
10
        let status = unsafe { raw::pcap_activate(self.handle.as_ptr()) };
50
10
        if status < 0 {
51
4
            return Err(self.status_err(status));
52
6
        }
53

            
54
6
        let mut capture = unsafe { mem::transmute::<Capture<Inactive>, Capture<Active>>(self) };
55

            
56
        // A warning leaves a message of its own, and no call between pcap_create and here
57
        // writes the error buffer, so its contents belong to this warning.
58
6
        if status > 0 {
59
2
            capture.warning = Some(unsafe {
60
2
                Warning::from_status(status, raw::pcap_geterr(capture.handle.as_ptr()))
61
2
            });
62
4
        }
63

            
64
6
        Ok(capture)
65
10
    }
66

            
67
    /// Set the read timeout for the Capture. By default, this is 0, so it will block indefinitely.
68
2
    pub fn timeout(self, ms: i32) -> Capture<Inactive> {
69
2
        unsafe { raw::pcap_set_timeout(self.handle.as_ptr(), ms) };
70
2
        self
71
2
    }
72

            
73
    /// Set the timestamp type to be used by a capture device.
74
    ///
75
    /// # Errors
76
    ///
77
    /// If the capture device does not support the timestamp type, an error will be returned.
78
    #[cfg(libpcap_1_2_1)]
79
4
    pub fn tstamp_type(self, tstamp_type: TimestampType) -> Result<Capture<Inactive>, Error> {
80
        // libpcap leaves the error buffer alone here. All it reports is whether the device
81
        // claims to support the type, so there is no message to pass on.
82
4
        if unsafe { raw::pcap_set_tstamp_type(self.handle.as_ptr(), tstamp_type as _) } != 0 {
83
2
            return Err(Error::UnsupportedTimestampType);
84
2
        }
85

            
86
2
        Ok(self)
87
4
    }
88

            
89
    /// Set promiscuous mode on or off. By default, this is off.
90
2
    pub fn promisc(self, to: bool) -> Capture<Inactive> {
91
2
        unsafe { raw::pcap_set_promisc(self.handle.as_ptr(), to as _) };
92
2
        self
93
2
    }
94

            
95
    /// Set immediate mode on or off. By default, this is off.
96
    ///
97
    /// Note that in WinPcap immediate mode is set by passing a 0 argument to `min_to_copy`.
98
    /// Immediate mode will be unset if `min_to_copy` is later called with a non-zero argument.
99
    /// Immediate mode is unset by resetting `min_to_copy` to the WinPcap default possibly changing
100
    /// a previously set value. When using `min_to_copy`, it is best to avoid `immediate_mode`.
101
    #[cfg(any(libpcap_1_5_0, windows))]
102
4
    pub fn immediate_mode(self, to: bool) -> Capture<Inactive> {
103
        // Prior to 1.5.0 when `pcap_set_immediate_mode` was introduced, the necessary steps to set
104
        // immediate mode were more complicated, depended on the OS, and in some configurations had
105
        // to be set on an active capture. See
106
        // https://www.tcpdump.org/manpages/pcap_set_immediate_mode.3pcap.html. Since we do not
107
        // expect pre-1.5.0 version on unix systems in the wild, we simply ignore those cases.
108
        #[cfg(libpcap_1_5_0)]
109
        unsafe {
110
4
            raw::pcap_set_immediate_mode(self.handle.as_ptr(), to as _)
111
        };
112

            
113
        // In WinPcap we use `pcap_setmintocopy` as it does not have `pcap_set_immediate_mode`.
114
        #[cfg(all(windows, not(libpcap_1_5_0)))]
115
        unsafe {
116
            raw::pcap_setmintocopy(
117
                self.handle.as_ptr(),
118
                if to {
119
                    0
120
                } else {
121
                    raw::WINPCAP_MINTOCOPY_DEFAULT
122
                },
123
            )
124
        };
125

            
126
4
        self
127
4
    }
128

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

            
134
        self
135
    }
136

            
137
    /// Set rfmon mode on or off. The default is maintained by pcap.
138
    #[cfg(not(windows))]
139
2
    pub fn rfmon(self, to: bool) -> Capture<Inactive> {
140
2
        unsafe { raw::pcap_set_rfmon(self.handle.as_ptr(), to as _) };
141
2
        self
142
2
    }
143

            
144
    /// Set the buffer size for incoming packet data.
145
    ///
146
    /// The default is 1000000. This should always be larger than the snaplen.
147
2
    pub fn buffer_size(self, to: i32) -> Capture<Inactive> {
148
2
        unsafe { raw::pcap_set_buffer_size(self.handle.as_ptr(), to) };
149
2
        self
150
2
    }
151

            
152
    /// Set the timestamp precision returned in captures.
153
    ///
154
    /// # Errors
155
    ///
156
    /// If the capture device does not support the timestamp precision, an error will be returned.
157
    #[cfg(libpcap_1_5_0)]
158
4
    pub fn precision(self, precision: Precision) -> Result<Capture<Inactive>, Error> {
159
        // libpcap leaves the error buffer alone here. All it reports is whether the device
160
        // claims to support the precision, so there is no message to pass on.
161
4
        if unsafe { raw::pcap_set_tstamp_precision(self.handle.as_ptr(), precision as _) } != 0 {
162
2
            return Err(Error::UnsupportedTimestampPrecision);
163
2
        }
164

            
165
2
        Ok(self)
166
4
    }
167

            
168
    /// Set the snaplen size (the maximum length of a packet captured into the buffer).
169
    /// Useful if you only want certain headers, but not the entire packet.
170
    ///
171
    /// The default is 65535.
172
2
    pub fn snaplen(self, to: i32) -> Capture<Inactive> {
173
2
        unsafe { raw::pcap_set_snaplen(self.handle.as_ptr(), to) };
174
2
        self
175
2
    }
176
}
177

            
178
#[repr(i32)]
179
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
180
/// Timestamp types
181
///
182
/// Not all systems and interfaces will necessarily support all of these. They are described in
183
/// more detail in [pcap-tstamp(7)](https://www.tcpdump.org/manpages/pcap-tstamp.7.html).
184
///
185
/// Note that timestamps synchronized with the system clock can go backwards, as the system clock
186
/// can go backwards.  If a clock is not in sync with the system clock, that could be because the
187
/// system clock isn't keeping accurate time, because the other clock isn't keeping accurate time,
188
/// or both.
189
///
190
/// Note that host-provided timestamps generally correspond to the time when the timestamping
191
/// code sees the packet; this could be some unknown amount of time after the first or last bit of
192
/// the packet is received by the network adapter, due to batching of interrupts for packet
193
/// arrival, queueing delays, etc..
194
pub enum TimestampType {
195
    /// Timestamps are provided by the host machine, rather than by the capture device.
196
    ///
197
    /// The characteristics of the timestamp are unknown.
198
    Host = 0,
199
    /// A timestamp provided by the host machine that is low precision but relatively cheap to
200
    /// fetch.
201
    ///
202
    /// It is taken from the system clock, so it is synchronized with the times you would fetch
203
    /// from system calls.
204
    HostLowPrec = 1,
205
    /// A timestamp provided by the host machine that is high precision. It might be more expensive
206
    /// to fetch than [`TimestampType::HostLowPrec`].
207
    ///
208
    /// From libpcap 1.10.0 on it is synchronized with the system clock. Before that it might or
209
    /// might not have been.
210
    HostHighPrec = 2,
211
    /// A high-precision timestamp supplied by the capture device.
212
    ///
213
    /// The timestamp is synchronized with the system clock.
214
    Adapter = 3,
215
    /// A high-precision timestamp supplied by the capture device.
216
    ///
217
    /// The timestamp is not synchronized with the system clock.
218
    AdapterUnsynced = 4,
219
    /// A timestamp provided by the host machine that is high precision. It might be more expensive
220
    /// to fetch than [`TimestampType::HostLowPrec`].
221
    ///
222
    /// The timestamp is not synchronized with the system clock.
223
    #[cfg(libpcap_1_10_0)]
224
    HostHighPrecUnsynced = 5,
225
}
226

            
227
#[cfg(test)]
228
mod tests {
229
    use crate::{
230
        capture::testmod::test_capture,
231
        raw::testmod::{RAWMTX, as_pcap_t, geterr_expect},
232
    };
233

            
234
    use super::*;
235

            
236
    #[test]
237
    fn test_from_device() {
238
        let _m = RAWMTX.lock();
239

            
240
        let mut dummy: isize = 777;
241
        let pcap = as_pcap_t(&mut dummy);
242

            
243
        let ctx = raw::pcap_create_context();
244
        ctx.expect().return_once_st(move |_, _| pcap);
245

            
246
        let ctx = raw::pcap_close_context();
247
        ctx.expect()
248
            .withf_st(move |ptr| *ptr == pcap)
249
            .return_once(|_| {});
250

            
251
        let result = Capture::from_device("some_device");
252
        assert!(result.is_ok());
253
    }
254

            
255
    #[test]
256
    fn test_from_device_error() {
257
        let _m = RAWMTX.lock();
258

            
259
        let ctx = raw::pcap_create_context();
260
        ctx.expect().return_once_st(|_, _| std::ptr::null_mut());
261

            
262
        let result = Capture::from_device("some_device");
263
        assert!(result.is_err());
264
    }
265

            
266
    #[test]
267
    fn test_open() {
268
        let _m = RAWMTX.lock();
269

            
270
        let mut dummy: isize = 777;
271
        let pcap = as_pcap_t(&mut dummy);
272

            
273
        let test_capture = test_capture::<Inactive>(pcap);
274
        let capture = test_capture.capture;
275

            
276
        let ctx = raw::pcap_activate_context();
277
        ctx.expect()
278
            .withf_st(move |arg1| *arg1 == pcap)
279
            .return_once(|_| 0);
280

            
281
        let capture = capture.open().unwrap();
282
        assert_eq!(capture.warning(), None);
283
    }
284

            
285
    #[test]
286
    fn test_open_warning() {
287
        let _m = RAWMTX.lock();
288

            
289
        let mut dummy: isize = 777;
290
        let pcap = as_pcap_t(&mut dummy);
291

            
292
        let test_capture = test_capture::<Inactive>(pcap);
293
        let capture = test_capture.capture;
294

            
295
        // A device with no promiscuous mode is activated all the same.
296
        let ctx = raw::pcap_activate_context();
297
        ctx.expect()
298
            .withf_st(move |arg1| *arg1 == pcap)
299
            .return_once(|_| raw::PCAP_WARNING_PROMISC_NOTSUP);
300

            
301
        let _err = geterr_expect(pcap);
302

            
303
        let capture = capture.open().unwrap();
304
        assert_eq!(
305
            capture.warning(),
306
            Some(&Warning {
307
                code: crate::WarningCode::PromiscuousModeNotSupported,
308
                message: "oh oh".to_string(),
309
            })
310
        );
311
    }
312

            
313
    #[test]
314
    fn test_open_status_error() {
315
        let _m = RAWMTX.lock();
316

            
317
        let mut dummy: isize = 777;
318
        let pcap = as_pcap_t(&mut dummy);
319

            
320
        let test_capture = test_capture::<Inactive>(pcap);
321
        let capture = test_capture.capture;
322

            
323
        let ctx = raw::pcap_activate_context();
324
        ctx.expect()
325
            .withf_st(move |arg1| *arg1 == pcap)
326
            .return_once(|_| raw::PCAP_ERROR_PERM_DENIED);
327

            
328
        let _err = geterr_expect(pcap);
329

            
330
        let error = capture.open().err().unwrap();
331
        assert_eq!(
332
            error,
333
            Error::PcapErrorCode(crate::ErrorCode::PermissionDenied, "oh oh".to_string())
334
        );
335
    }
336

            
337
    #[test]
338
    fn test_open_error() {
339
        let _m = RAWMTX.lock();
340

            
341
        let mut dummy: isize = 777;
342
        let pcap = as_pcap_t(&mut dummy);
343

            
344
        let test_capture = test_capture::<Inactive>(pcap);
345
        let capture = test_capture.capture;
346

            
347
        let ctx = raw::pcap_activate_context();
348
        ctx.expect()
349
            .withf_st(move |arg1| *arg1 == pcap)
350
            .return_once(|_| -1);
351

            
352
        let _err = geterr_expect(pcap);
353

            
354
        let result = capture.open();
355
        assert!(result.is_err());
356
    }
357

            
358
    #[test]
359
    fn test_timeout() {
360
        let _m = RAWMTX.lock();
361

            
362
        let mut dummy: isize = 777;
363
        let pcap = as_pcap_t(&mut dummy);
364

            
365
        let test_capture = test_capture::<Inactive>(pcap);
366
        let capture = test_capture.capture;
367

            
368
        let ctx = raw::pcap_set_timeout_context();
369
        ctx.expect()
370
            .withf_st(move |arg1, _| *arg1 == pcap)
371
            .return_once(|_, _| 0);
372

            
373
        let _capture = capture.timeout(5);
374
    }
375

            
376
    #[test]
377
    #[cfg(libpcap_1_2_1)]
378
    fn test_timstamp_type() {
379
        let _m = RAWMTX.lock();
380

            
381
        let mut dummy: isize = 777;
382
        let pcap = as_pcap_t(&mut dummy);
383

            
384
        let test_capture = test_capture::<Inactive>(pcap);
385
        let capture = test_capture.capture;
386

            
387
        let ctx = raw::pcap_set_tstamp_type_context();
388
        ctx.expect()
389
            .withf_st(move |arg1, _| *arg1 == pcap)
390
            .return_once(|_, _| 0);
391

            
392
        let capture = capture.tstamp_type(TimestampType::Host).unwrap();
393

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

            
400
        assert_eq!(
401
            capture.tstamp_type(TimestampType::Adapter).err().unwrap(),
402
            Error::UnsupportedTimestampType
403
        );
404

            
405
        // For code coverage of the derive line.
406
        assert_ne!(TimestampType::Host, TimestampType::HostLowPrec);
407
        assert_ne!(TimestampType::Host, TimestampType::HostHighPrec);
408
        #[cfg(libpcap_1_10_0)]
409
        assert_ne!(
410
            TimestampType::HostHighPrec,
411
            TimestampType::HostHighPrecUnsynced
412
        );
413
    }
414

            
415
    #[test]
416
    fn test_promisc() {
417
        let _m = RAWMTX.lock();
418

            
419
        let mut dummy: isize = 777;
420
        let pcap = as_pcap_t(&mut dummy);
421

            
422
        let test_capture = test_capture::<Inactive>(pcap);
423
        let capture = test_capture.capture;
424

            
425
        let ctx = raw::pcap_set_promisc_context();
426
        ctx.expect()
427
            .withf_st(move |arg1, _| *arg1 == pcap)
428
            .return_once(|_, _| 0);
429

            
430
        let _capture = capture.promisc(true);
431
    }
432

            
433
    #[cfg(libpcap_1_5_0)]
434
    struct ImmediateModeExpect(raw::__pcap_set_immediate_mode::Context);
435

            
436
    #[cfg(all(windows, not(libpcap_1_5_0)))]
437
    struct ImmediateModeExpect(raw::__pcap_setmintocopy::Context);
438

            
439
    #[cfg(any(libpcap_1_5_0, windows))]
440
    fn immediate_mode_expect(pcap: *mut raw::pcap_t) -> ImmediateModeExpect {
441
        #[cfg(libpcap_1_5_0)]
442
        {
443
            let ctx = raw::pcap_set_immediate_mode_context();
444
            ctx.checkpoint();
445
            ctx.expect()
446
                .withf_st(move |arg1, _| *arg1 == pcap)
447
                .return_once(|_, _| 0);
448
            ImmediateModeExpect(ctx)
449
        }
450
        #[cfg(all(windows, not(libpcap_1_5_0)))]
451
        {
452
            let ctx = raw::pcap_setmintocopy_context();
453
            ctx.checkpoint();
454
            ctx.expect()
455
                .withf_st(move |arg1, _| *arg1 == pcap)
456
                .return_once(|_, _| 0);
457
            ImmediateModeExpect(ctx)
458
        }
459
    }
460

            
461
    #[test]
462
    #[cfg(any(libpcap_1_5_0, windows))]
463
    fn test_immediate_mode() {
464
        let _m = RAWMTX.lock();
465

            
466
        let mut dummy: isize = 777;
467
        let pcap = as_pcap_t(&mut dummy);
468

            
469
        let test_capture = test_capture::<Inactive>(pcap);
470
        let capture = test_capture.capture;
471

            
472
        let _ctx = immediate_mode_expect(pcap);
473
        let capture = capture.immediate_mode(true);
474

            
475
        let _ctx = immediate_mode_expect(pcap);
476
        let _capture = capture.immediate_mode(false);
477
    }
478

            
479
    #[test]
480
    #[cfg(all(libpcap_1_5_3, target_os = "macos"))]
481
    fn test_want_pktap() {
482
        let _m = RAWMTX.lock();
483

            
484
        let mut dummy: isize = 777;
485
        let pcap = as_pcap_t(&mut dummy);
486

            
487
        let test_capture = test_capture::<Inactive>(pcap);
488
        let capture = test_capture.capture;
489

            
490
        let ctx = raw::pcap_set_want_pktap_context();
491
        ctx.expect()
492
            .withf_st(move |arg1, _| *arg1 == pcap)
493
            .return_once(|_, _| 0);
494
        let _capture = capture.want_pktap(true);
495
    }
496

            
497
    #[test]
498
    #[cfg(not(windows))]
499
    fn test_rfmon() {
500
        let _m = RAWMTX.lock();
501

            
502
        let mut dummy: isize = 777;
503
        let pcap = as_pcap_t(&mut dummy);
504

            
505
        let test_capture = test_capture::<Inactive>(pcap);
506
        let capture = test_capture.capture;
507

            
508
        let ctx = raw::pcap_set_rfmon_context();
509
        ctx.expect()
510
            .withf_st(move |arg1, _| *arg1 == pcap)
511
            .return_once(|_, _| 0);
512

            
513
        let _capture = capture.rfmon(true);
514
    }
515

            
516
    #[test]
517
    fn test_buffer_size() {
518
        let _m = RAWMTX.lock();
519

            
520
        let mut dummy: isize = 777;
521
        let pcap = as_pcap_t(&mut dummy);
522

            
523
        let test_capture = test_capture::<Inactive>(pcap);
524
        let capture = test_capture.capture;
525

            
526
        let ctx = raw::pcap_set_buffer_size_context();
527
        ctx.expect()
528
            .withf_st(move |arg1, _| *arg1 == pcap)
529
            .return_once(|_, _| 0);
530

            
531
        let _capture = capture.buffer_size(10);
532
    }
533

            
534
    #[test]
535
    #[cfg(libpcap_1_5_0)]
536
    fn test_precision() {
537
        let _m = RAWMTX.lock();
538

            
539
        let mut dummy: isize = 777;
540
        let pcap = as_pcap_t(&mut dummy);
541

            
542
        let test_capture = test_capture::<Inactive>(pcap);
543
        let capture = test_capture.capture;
544

            
545
        let ctx = raw::pcap_set_tstamp_precision_context();
546
        ctx.expect()
547
            .withf_st(move |arg1, _| *arg1 == pcap)
548
            .return_once(|_, _| 0);
549

            
550
        let capture = capture.precision(Precision::Nano).unwrap();
551

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

            
558
        assert_eq!(
559
            capture.precision(Precision::Nano).err().unwrap(),
560
            Error::UnsupportedTimestampPrecision
561
        );
562
    }
563

            
564
    #[test]
565
    fn test_snaplen() {
566
        let _m = RAWMTX.lock();
567

            
568
        let mut dummy: isize = 777;
569
        let pcap = as_pcap_t(&mut dummy);
570

            
571
        let test_capture = test_capture::<Inactive>(pcap);
572
        let capture = test_capture.capture;
573

            
574
        let ctx = raw::pcap_set_snaplen_context();
575
        ctx.expect()
576
            .withf_st(move |arg1, _| *arg1 == pcap)
577
            .return_once(|_, _| 0);
578

            
579
        let _capture = capture.snaplen(10);
580
    }
581
}