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(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 rfmon mode on or off. The default is maintained by pcap.
102
    #[cfg(not(windows))]
103
2
    pub fn rfmon(self, to: bool) -> Capture<Inactive> {
104
2
        unsafe { raw::pcap_set_rfmon(self.handle.as_ptr(), to as _) };
105
2
        self
106
2
    }
107

            
108
    /// Set the buffer size for incoming packet data.
109
    ///
110
    /// The default is 1000000. This should always be larger than the snaplen.
111
2
    pub fn buffer_size(self, to: i32) -> Capture<Inactive> {
112
2
        unsafe { raw::pcap_set_buffer_size(self.handle.as_ptr(), to) };
113
2
        self
114
2
    }
115

            
116
    /// Set the time stamp precision returned in captures.
117
    #[cfg(libpcap_1_5_0)]
118
2
    pub fn precision(self, precision: Precision) -> Capture<Inactive> {
119
2
        unsafe { raw::pcap_set_tstamp_precision(self.handle.as_ptr(), precision as _) };
120
2
        self
121
2
    }
122

            
123
    /// Set the snaplen size (the maximum length of a packet captured into the buffer).
124
    /// Useful if you only want certain headers, but not the entire packet.
125
    ///
126
    /// The default is 65535.
127
2
    pub fn snaplen(self, to: i32) -> Capture<Inactive> {
128
2
        unsafe { raw::pcap_set_snaplen(self.handle.as_ptr(), to) };
129
2
        self
130
2
    }
131
}
132

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

            
176
#[cfg(test)]
177
mod tests {
178
    use crate::{
179
        capture::testmod::test_capture,
180
        raw::testmod::{as_pcap_t, geterr_expect, RAWMTX},
181
    };
182

            
183
    use super::*;
184

            
185
    #[test]
186
    fn test_from_device() {
187
        let _m = RAWMTX.lock();
188

            
189
        let mut dummy: isize = 777;
190
        let pcap = as_pcap_t(&mut dummy);
191

            
192
        let ctx = raw::pcap_create_context();
193
        ctx.expect().return_once_st(move |_, _| pcap);
194

            
195
        let ctx = raw::pcap_close_context();
196
        ctx.expect()
197
            .withf_st(move |ptr| *ptr == pcap)
198
            .return_once(|_| {});
199

            
200
        let result = Capture::from_device("some_device");
201
        assert!(result.is_ok());
202
    }
203

            
204
    #[test]
205
    fn test_from_device_error() {
206
        let _m = RAWMTX.lock();
207

            
208
        let ctx = raw::pcap_create_context();
209
        ctx.expect().return_once_st(|_, _| std::ptr::null_mut());
210

            
211
        let result = Capture::from_device("some_device");
212
        assert!(result.is_err());
213
    }
214

            
215
    #[test]
216
    fn test_open() {
217
        let _m = RAWMTX.lock();
218

            
219
        let mut dummy: isize = 777;
220
        let pcap = as_pcap_t(&mut dummy);
221

            
222
        let test_capture = test_capture::<Inactive>(pcap);
223
        let capture = test_capture.capture;
224

            
225
        let ctx = raw::pcap_activate_context();
226
        ctx.expect()
227
            .withf_st(move |arg1| *arg1 == pcap)
228
            .return_once(|_| 0);
229

            
230
        let result = capture.open();
231
        assert!(result.is_ok());
232
    }
233

            
234
    #[test]
235
    fn test_open_error() {
236
        let _m = RAWMTX.lock();
237

            
238
        let mut dummy: isize = 777;
239
        let pcap = as_pcap_t(&mut dummy);
240

            
241
        let test_capture = test_capture::<Inactive>(pcap);
242
        let capture = test_capture.capture;
243

            
244
        let ctx = raw::pcap_activate_context();
245
        ctx.expect()
246
            .withf_st(move |arg1| *arg1 == pcap)
247
            .return_once(|_| -1);
248

            
249
        let _err = geterr_expect(pcap);
250

            
251
        let result = capture.open();
252
        assert!(result.is_err());
253
    }
254

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

            
259
        let mut dummy: isize = 777;
260
        let pcap = as_pcap_t(&mut dummy);
261

            
262
        let test_capture = test_capture::<Inactive>(pcap);
263
        let capture = test_capture.capture;
264

            
265
        let ctx = raw::pcap_set_timeout_context();
266
        ctx.expect()
267
            .withf_st(move |arg1, _| *arg1 == pcap)
268
            .return_once(|_, _| 0);
269

            
270
        let _capture = capture.timeout(5);
271
    }
272

            
273
    #[test]
274
    #[cfg(libpcap_1_2_1)]
275
    fn test_timstamp_type() {
276
        let _m = RAWMTX.lock();
277

            
278
        let mut dummy: isize = 777;
279
        let pcap = as_pcap_t(&mut dummy);
280

            
281
        let test_capture = test_capture::<Inactive>(pcap);
282
        let capture = test_capture.capture;
283

            
284
        let ctx = raw::pcap_set_tstamp_type_context();
285
        ctx.expect()
286
            .withf_st(move |arg1, _| *arg1 == pcap)
287
            .return_once(|_, _| 0);
288

            
289
        let _capture = capture.tstamp_type(TimestampType::Host);
290

            
291
        // For code coverage of the derive line.
292
        assert_ne!(TimestampType::Host, TimestampType::HostLowPrec);
293
        assert_ne!(TimestampType::Host, TimestampType::HostHighPrec);
294
    }
295

            
296
    #[test]
297
    fn test_promisc() {
298
        let _m = RAWMTX.lock();
299

            
300
        let mut dummy: isize = 777;
301
        let pcap = as_pcap_t(&mut dummy);
302

            
303
        let test_capture = test_capture::<Inactive>(pcap);
304
        let capture = test_capture.capture;
305

            
306
        let ctx = raw::pcap_set_promisc_context();
307
        ctx.expect()
308
            .withf_st(move |arg1, _| *arg1 == pcap)
309
            .return_once(|_, _| 0);
310

            
311
        let _capture = capture.promisc(true);
312
    }
313

            
314
    #[cfg(libpcap_1_5_0)]
315
    struct ImmediateModeExpect(raw::__pcap_set_immediate_mode::Context);
316

            
317
    #[cfg(all(windows, not(libpcap_1_5_0)))]
318
    struct ImmediateModeExpect(raw::__pcap_setmintocopy::Context);
319

            
320
    #[cfg(any(libpcap_1_5_0, windows))]
321
    fn immediate_mode_expect(pcap: *mut raw::pcap_t) -> ImmediateModeExpect {
322
        #[cfg(libpcap_1_5_0)]
323
        {
324
            let ctx = raw::pcap_set_immediate_mode_context();
325
            ctx.checkpoint();
326
            ctx.expect()
327
                .withf_st(move |arg1, _| *arg1 == pcap)
328
                .return_once(|_, _| 0);
329
            ImmediateModeExpect(ctx)
330
        }
331
        #[cfg(all(windows, not(libpcap_1_5_0)))]
332
        {
333
            let ctx = raw::pcap_setmintocopy_context();
334
            ctx.checkpoint();
335
            ctx.expect()
336
                .withf_st(move |arg1, _| *arg1 == pcap)
337
                .return_once(|_, _| 0);
338
            ImmediateModeExpect(ctx)
339
        }
340
    }
341

            
342
    #[test]
343
    #[cfg(any(libpcap_1_5_0, windows))]
344
    fn test_immediate_mode() {
345
        let _m = RAWMTX.lock();
346

            
347
        let mut dummy: isize = 777;
348
        let pcap = as_pcap_t(&mut dummy);
349

            
350
        let test_capture = test_capture::<Inactive>(pcap);
351
        let capture = test_capture.capture;
352

            
353
        let _ctx = immediate_mode_expect(pcap);
354
        let capture = capture.immediate_mode(true);
355

            
356
        let _ctx = immediate_mode_expect(pcap);
357
        let _capture = capture.immediate_mode(false);
358
    }
359

            
360
    #[test]
361
    #[cfg(not(windows))]
362
    fn test_rfmon() {
363
        let _m = RAWMTX.lock();
364

            
365
        let mut dummy: isize = 777;
366
        let pcap = as_pcap_t(&mut dummy);
367

            
368
        let test_capture = test_capture::<Inactive>(pcap);
369
        let capture = test_capture.capture;
370

            
371
        let ctx = raw::pcap_set_rfmon_context();
372
        ctx.expect()
373
            .withf_st(move |arg1, _| *arg1 == pcap)
374
            .return_once(|_, _| 0);
375

            
376
        let _capture = capture.rfmon(true);
377
    }
378

            
379
    #[test]
380
    fn test_buffer_size() {
381
        let _m = RAWMTX.lock();
382

            
383
        let mut dummy: isize = 777;
384
        let pcap = as_pcap_t(&mut dummy);
385

            
386
        let test_capture = test_capture::<Inactive>(pcap);
387
        let capture = test_capture.capture;
388

            
389
        let ctx = raw::pcap_set_buffer_size_context();
390
        ctx.expect()
391
            .withf_st(move |arg1, _| *arg1 == pcap)
392
            .return_once(|_, _| 0);
393

            
394
        let _capture = capture.buffer_size(10);
395
    }
396

            
397
    #[test]
398
    #[cfg(libpcap_1_5_0)]
399
    fn test_precision() {
400
        let _m = RAWMTX.lock();
401

            
402
        let mut dummy: isize = 777;
403
        let pcap = as_pcap_t(&mut dummy);
404

            
405
        let test_capture = test_capture::<Inactive>(pcap);
406
        let capture = test_capture.capture;
407

            
408
        let ctx = raw::pcap_set_tstamp_precision_context();
409
        ctx.expect()
410
            .withf_st(move |arg1, _| *arg1 == pcap)
411
            .return_once(|_, _| 0);
412

            
413
        let _capture = capture.precision(Precision::Nano);
414
    }
415

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

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

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

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

            
431
        let _capture = capture.snaplen(10);
432
    }
433
}