1
//! pcap is a packet capture library available on Linux, Windows and Mac. This
2
//! crate supports creating and configuring capture contexts, sniffing packets,
3
//! sending packets to interfaces, listing devices, and recording packet captures
4
//! to pcap-format dump files.
5
//!
6
//! # Capturing packets
7
//! The easiest way to open an active capture handle and begin sniffing is to
8
//! use `.open()` on a `Device`. You can obtain the "default" device using
9
//! `Device::lookup()`, or you can obtain the device(s) you need via `Device::list()`.
10
//!
11
//! ```no_run
12
//! use pcap::Device;
13
//!
14
//! let mut cap = Device::lookup().unwrap().unwrap().open().unwrap();
15
//!
16
//! while let Ok(packet) = cap.next_packet() {
17
//!     println!("received packet! {:?}", packet);
18
//! }
19
//!
20
//! ```
21
//!
22
//! `Capture`'s `.next_packet()` will produce a `Packet` which can be dereferenced to access the
23
//! `&[u8]` packet contents.
24
//!
25
//! # Custom configuration
26
//!
27
//! You may want to configure the `timeout`, `snaplen` or other parameters for the capture
28
//! handle. In this case, use `Capture::from_device()` to obtain a `Capture<Inactive>`, and
29
//! proceed to configure the capture handle. When you're finished, run `.open()` on it to
30
//! turn it into a `Capture<Active>`.
31
//!
32
//! ```no_run
33
//! use pcap::{Device, Capture};
34
//!
35
//! let main_device = Device::lookup().unwrap().unwrap();
36
//! let mut cap = Capture::from_device(main_device).unwrap()
37
//!                   .promisc(true)
38
//!                   .snaplen(5000)
39
//!                   .open().unwrap();
40
//!
41
//! while let Ok(packet) = cap.next_packet() {
42
//!     println!("received packet! {:?}", packet);
43
//! }
44
//! ```
45
//!
46
//! # Abstracting over different capture types
47
//!
48
//! You can abstract over live captures (`Capture<Active>`) and file captures
49
//! (`Capture<Offline>`) using generics and the [`Activated`] trait, for example:
50
//!
51
//! ```
52
//! use pcap::{Activated, Capture};
53
//!
54
//! fn read_packets<T: Activated>(mut capture: Capture<T>) {
55
//!     while let Ok(packet) = capture.next_packet() {
56
//!         println!("received packet! {:?}", packet);
57
//!     }
58
//! }
59
//! ```
60

            
61
#![cfg_attr(docsrs, feature(doc_cfg))]
62

            
63
use std::ffi::{self, CStr, CString};
64
use std::fmt;
65
use std::path::Path;
66

            
67
use self::Error::*;
68

            
69
mod capture;
70
mod codec;
71
mod device;
72
mod linktype;
73
mod packet;
74

            
75
#[cfg(not(windows))]
76
pub use capture::activated::open_raw_fd;
77
pub use capture::{
78
    activated::{
79
        BpfInstruction, BpfProgram, BreakLoop, Direction, Savefile, Stat, iterator::PacketIter,
80
    },
81
    inactive::TimestampType,
82
    {Activated, Active, Capture, Dead, Inactive, Offline, Precision, State, Warning, WarningCode},
83
};
84
pub use codec::PacketCodec;
85
pub use device::{Address, ConnectionStatus, Device, DeviceFlags, IfFlags};
86
pub use linktype::Linktype;
87
pub use packet::{Packet, PacketHeader};
88

            
89
#[deprecated(note = "Renamed to TimestampType")]
90
/// An old name for `TimestampType`, kept around for backward-compatibility.
91
pub type TstampType = TimestampType;
92

            
93
mod raw;
94

            
95
#[cfg(windows)]
96
#[cfg_attr(docsrs, doc(cfg(windows)))]
97
pub mod sendqueue;
98

            
99
#[cfg(feature = "capture-stream")]
100
mod sink;
101
#[cfg(feature = "capture-stream")]
102
#[cfg_attr(docsrs, doc(cfg(feature = "capture-stream")))]
103
pub use sink::PacketSink;
104

            
105
#[cfg(feature = "capture-stream")]
106
mod stream;
107
#[cfg(feature = "capture-stream")]
108
#[cfg_attr(docsrs, doc(cfg(feature = "capture-stream")))]
109
pub use stream::PacketStream;
110

            
111
/// A list of libpcap's own error codes.
112
///
113
/// Anything else it fails with arrives as [`Error::PcapError`], with only the message to go on.
114
/// Not all of these can be reached through this crate, but libpcap defines them and so they are
115
/// all here.
116
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
117
#[non_exhaustive]
118
pub enum ErrorCode {
119
    /// The loop was terminated by `BreakLoop::breakloop`
120
    Break,
121
    /// The capture has not been activated
122
    NotActivated,
123
    /// The capture has already been activated
124
    AlreadyActivated,
125
    /// No such device exists
126
    NoSuchDevice,
127
    /// The device does not support monitor mode
128
    MonitorModeNotSupported,
129
    /// The operation is only supported in monitor mode
130
    NotInMonitorMode,
131
    /// No permission to open the device
132
    PermissionDenied,
133
    /// The interface is not up
134
    InterfaceNotUp,
135
    /// The device does not support setting the timestamp type
136
    CannotSetTimestampType,
137
    /// No permission to capture in promiscuous mode
138
    PromiscuousPermissionDenied,
139
    /// The device does not support the requested timestamp precision
140
    TimestampPrecisionNotSupported,
141
    /// The capture mechanism is not available
142
    CaptureNotSupported,
143
}
144

            
145
impl fmt::Display for ErrorCode {
146
13
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147
13
        f.write_str(match self {
148
2
            ErrorCode::Break => "the loop was terminated",
149
1
            ErrorCode::NotActivated => "the capture has not been activated",
150
1
            ErrorCode::AlreadyActivated => "the capture has already been activated",
151
1
            ErrorCode::NoSuchDevice => "no such device exists",
152
1
            ErrorCode::MonitorModeNotSupported => "the device does not support monitor mode",
153
1
            ErrorCode::NotInMonitorMode => "the operation is only supported in monitor mode",
154
1
            ErrorCode::PermissionDenied => "no permission to open the device",
155
1
            ErrorCode::InterfaceNotUp => "the interface is not up",
156
            ErrorCode::CannotSetTimestampType => {
157
1
                "the device does not support setting the timestamp type"
158
            }
159
            ErrorCode::PromiscuousPermissionDenied => {
160
1
                "no permission to capture in promiscuous mode"
161
            }
162
            ErrorCode::TimestampPrecisionNotSupported => {
163
1
                "the device does not support the requested timestamp precision"
164
            }
165
1
            ErrorCode::CaptureNotSupported => "the capture mechanism is not available",
166
        })
167
13
    }
168
}
169

            
170
/// An error received from pcap
171
#[derive(Debug, PartialEq, Eq)]
172
#[non_exhaustive]
173
pub enum Error {
174
    /// The underlying library returned invalid UTF-8
175
    MalformedError(std::str::Utf8Error),
176
    /// The underlying library returned a null string
177
    InvalidString,
178
    /// The unerlying library returned an error
179
    PcapError(String),
180
    /// The underlying library returned an error it has a code for, along with the message it
181
    /// left behind. The message is sometimes the more useful of the two.
182
    PcapErrorCode(ErrorCode, String),
183
    /// The linktype was invalid or unknown
184
    InvalidLinktype,
185
    /// The capture device does not support the timestamp type
186
    UnsupportedTimestampType,
187
    /// The capture device does not support the timestamp precision
188
    UnsupportedTimestampPrecision,
189
    /// The timeout expired while reading from a live capture
190
    TimeoutExpired,
191
    /// No more packets to read from the file
192
    NoMorePackets,
193
    /// The interface being captured from went away
194
    InterfaceDisappeared,
195
    /// Must be in non-blocking mode to function
196
    NonNonBlock,
197
    /// There is not sufficent memory to create a dead capture
198
    InsufficientMemory,
199
    /// An invalid input string (internal null)
200
    InvalidInputString,
201
    /// An IO error occurred
202
    IoError(std::io::ErrorKind),
203
    #[cfg(not(windows))]
204
    /// An invalid raw file descriptor was provided
205
    InvalidRawFd,
206
    #[cfg(windows)]
207
    /// A path that libpcap cannot be given because it is not valid UTF-8
208
    InvalidPath,
209
    /// Errno error
210
    ErrnoError(errno::Errno),
211
    #[cfg(windows)]
212
    /// The capture library could not be loaded
213
    LibraryNotFound,
214
    #[cfg(windows)]
215
    /// The capture library does not have an entrypoint the call needs
216
    EntrypointNotFound(&'static str),
217
    /// Buffer size overflows capacity
218
    BufferOverflow,
219
}
220

            
221
impl Error {
222
70
    unsafe fn message(ptr: *const libc::c_char) -> String {
223
70
        unsafe { cstr_to_string_lossy(ptr) }.unwrap_or_default()
224
70
    }
225

            
226
38
    unsafe fn new(ptr: *const libc::c_char) -> Error {
227
38
        let message = unsafe { Self::message(ptr) };
228

            
229
        // An interface going away is reported as PCAP_ERROR like every other failure, so the
230
        // message is all there is to go on. Windows adds the error code it got to the end,
231
        // which is why this matches the start rather than the whole string.
232
38
        if message.starts_with("The interface disappeared") {
233
2
            return InterfaceDisappeared;
234
36
        }
235

            
236
36
        PcapError(message)
237
38
    }
238

            
239
    /// Read one of the status codes libpcap returns, along with the message it left behind.
240
34
    unsafe fn from_status(status: libc::c_int, ptr: *const libc::c_char) -> Error {
241
        // A loop that was told to stop is the one case libpcap leaves no message for, so the
242
        // error buffer still holds the last thing that went wrong on the handle. Do not read it.
243
34
        if status == raw::PCAP_ERROR_BREAK {
244
7
            return PcapErrorCode(ErrorCode::Break, String::new());
245
27
        }
246

            
247
27
        let code = match status {
248
2
            raw::PCAP_ERROR_NOT_ACTIVATED => ErrorCode::NotActivated,
249
2
            raw::PCAP_ERROR_ACTIVATED => ErrorCode::AlreadyActivated,
250
2
            raw::PCAP_ERROR_NO_SUCH_DEVICE => ErrorCode::NoSuchDevice,
251
2
            raw::PCAP_ERROR_RFMON_NOTSUP => ErrorCode::MonitorModeNotSupported,
252
2
            raw::PCAP_ERROR_NOT_RFMON => ErrorCode::NotInMonitorMode,
253
3
            raw::PCAP_ERROR_PERM_DENIED => ErrorCode::PermissionDenied,
254
2
            raw::PCAP_ERROR_IFACE_NOT_UP => ErrorCode::InterfaceNotUp,
255
2
            raw::PCAP_ERROR_CANTSET_TSTAMP_TYPE => ErrorCode::CannotSetTimestampType,
256
2
            raw::PCAP_ERROR_PROMISC_PERM_DENIED => ErrorCode::PromiscuousPermissionDenied,
257
2
            raw::PCAP_ERROR_TSTAMP_PRECISION_NOTSUP => ErrorCode::TimestampPrecisionNotSupported,
258
2
            raw::PCAP_ERROR_CAPTURE_NOTSUP => ErrorCode::CaptureNotSupported,
259
            // PCAP_ERROR is what libpcap uses when it has no more specific code, and a code it
260
            // gained after this was written would land here too. Either way the message is all
261
            // that tells the conditions apart.
262
4
            _ => return unsafe { Self::new(ptr) },
263
        };
264

            
265
23
        PcapErrorCode(code, unsafe { Self::message(ptr) })
266
34
    }
267

            
268
47
    fn with_errbuf<T, F>(func: F) -> Result<T, Error>
269
47
    where
270
47
        F: FnOnce(*mut libc::c_char) -> Result<T, Error>,
271
    {
272
47
        let mut errbuf = [0i8; 256];
273
47
        func(errbuf.as_mut_ptr() as _)
274
47
    }
275
}
276

            
277
101
unsafe fn cstr<'a>(ptr: *const libc::c_char) -> Option<&'a CStr> {
278
101
    (!ptr.is_null()).then(|| unsafe { CStr::from_ptr(ptr as _) })
279
101
}
280

            
281
// Strict, for a string that goes back to libpcap: a device name that will not round-trip is worse
282
// than no name at all.
283
23
unsafe fn cstr_to_string(ptr: *const libc::c_char) -> Result<Option<String>, Error> {
284
23
    Ok(unsafe { cstr(ptr) }
285
23
        .map(CStr::to_str)
286
23
        .transpose()?
287
20
        .map(str::to_owned))
288
23
}
289

            
290
// Lossy, for a string that is only displayed: a message truncated mid-sequence at
291
// PCAP_ERRBUF_SIZE, or a description in the local Windows code page, is worth showing anyway.
292
78
unsafe fn cstr_to_string_lossy(ptr: *const libc::c_char) -> Option<String> {
293
78
    unsafe { cstr(ptr) }.map(|s| s.to_string_lossy().into_owned())
294
78
}
295

            
296
102
fn path_to_cstring(path: &Path) -> Result<CString, Error> {
297
    #[cfg(not(windows))]
298
102
    let bytes = {
299
        use std::os::unix::ffi::OsStrExt;
300
102
        path.as_os_str().as_bytes()
301
    };
302
    // libpcap has no entrypoints taking wide strings. It reads the path in the local code page,
303
    // or in UTF-8 once pcap_init has been asked for that, so give it the UTF-8 form. A path that
304
    // is not valid UTF-8 holds an unpaired surrogate, which has no form libpcap would accept.
305
    #[cfg(windows)]
306
    let bytes = path
307
        .as_os_str()
308
        .to_str()
309
        .ok_or(Error::InvalidPath)?
310
        .as_bytes();
311

            
312
102
    Ok(CString::new(bytes)?)
313
102
}
314

            
315
impl fmt::Display for Error {
316
30
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317
14
        match *self {
318
1
            MalformedError(ref e) => write!(f, "libpcap returned invalid UTF-8: {e}"),
319
1
            InvalidString => write!(f, "libpcap returned a null string"),
320
1
            PcapError(ref e) => write!(f, "libpcap error: {e}"),
321
            // The code is what a caller matches on; the message is what libpcap writes for a
322
            // reader, and it usually says the same thing at more length. Only fall back to
323
            // describing the code when there is no message, as for a loop that was told to stop.
324
14
            PcapErrorCode(code, ref e) if e.is_empty() => write!(f, "libpcap error: {code}"),
325
1
            PcapErrorCode(_, ref e) => write!(f, "libpcap error: {e}"),
326
1
            InvalidLinktype => write!(f, "invalid or unknown linktype"),
327
1
            UnsupportedTimestampType => write!(f, "unsupported timestamp type"),
328
1
            UnsupportedTimestampPrecision => write!(f, "unsupported timestamp precision"),
329
1
            TimeoutExpired => write!(f, "timeout expired while reading from a live capture"),
330
1
            NonNonBlock => write!(f, "must be in non-blocking mode to function"),
331
1
            NoMorePackets => write!(f, "no more packets to read from the file"),
332
1
            InterfaceDisappeared => write!(f, "the interface being captured from went away"),
333
1
            InsufficientMemory => write!(f, "insufficient memory"),
334
1
            InvalidInputString => write!(f, "invalid input string (internal null)"),
335
1
            IoError(ref e) => write!(f, "io error occurred: {e:?}"),
336
            #[cfg(not(windows))]
337
1
            InvalidRawFd => write!(f, "invalid raw file descriptor provided"),
338
            #[cfg(windows)]
339
            InvalidPath => write!(f, "invalid path (not valid UTF-8)"),
340
1
            ErrnoError(ref e) => write!(f, "libpcap os errno: {e}"),
341
            #[cfg(windows)]
342
            LibraryNotFound => write!(f, "could not load wpcap.dll, Npcap may not be installed"),
343
            #[cfg(windows)]
344
            EntrypointNotFound(name) => {
345
                write!(
346
                    f,
347
                    "the installed wpcap.dll does not export {name}, upgrade to a newer Npcap"
348
                )
349
            }
350
1
            BufferOverflow => write!(f, "buffer size too large"),
351
        }
352
30
    }
353
}
354

            
355
// Using description is deprecated. Remove in next version.
356
impl std::error::Error for Error {
357
18
    fn description(&self) -> &str {
358
18
        match *self {
359
1
            MalformedError(..) => "libpcap returned invalid UTF-8",
360
1
            PcapError(..) => "libpcap FFI error",
361
2
            PcapErrorCode(..) => "libpcap FFI error with a status code",
362
1
            InvalidString => "libpcap returned a null string",
363
1
            InvalidLinktype => "invalid or unknown linktype",
364
1
            UnsupportedTimestampType => "unsupported timestamp type",
365
1
            UnsupportedTimestampPrecision => "unsupported timestamp precision",
366
1
            TimeoutExpired => "timeout expired while reading from a live capture",
367
1
            NonNonBlock => "must be in non-blocking mode to function",
368
1
            NoMorePackets => "no more packets to read from the file",
369
1
            InterfaceDisappeared => "the interface being captured from went away",
370
1
            InsufficientMemory => "insufficient memory",
371
1
            InvalidInputString => "invalid input string (internal null)",
372
1
            IoError(..) => "io error occurred",
373
            #[cfg(not(windows))]
374
1
            InvalidRawFd => "invalid raw file descriptor provided",
375
            #[cfg(windows)]
376
            InvalidPath => "invalid path (not valid UTF-8)",
377
1
            ErrnoError(..) => "internal error, providing errno",
378
            #[cfg(windows)]
379
            LibraryNotFound => "could not load wpcap.dll",
380
            #[cfg(windows)]
381
            EntrypointNotFound(..) => "wpcap.dll is missing an entrypoint",
382
1
            BufferOverflow => "buffer size too large",
383
        }
384
18
    }
385

            
386
18
    fn cause(&self) -> Option<&dyn std::error::Error> {
387
18
        match *self {
388
1
            MalformedError(ref e) => Some(e),
389
17
            _ => None,
390
        }
391
18
    }
392
}
393

            
394
impl From<ffi::NulError> for Error {
395
1
    fn from(_: ffi::NulError) -> Error {
396
1
        InvalidInputString
397
1
    }
398
}
399

            
400
impl From<std::str::Utf8Error> for Error {
401
4
    fn from(obj: std::str::Utf8Error) -> Error {
402
4
        MalformedError(obj)
403
4
    }
404
}
405

            
406
impl From<std::io::Error> for Error {
407
1
    fn from(obj: std::io::Error) -> Error {
408
1
        obj.kind().into()
409
1
    }
410
}
411

            
412
impl From<std::io::ErrorKind> for Error {
413
1
    fn from(obj: std::io::ErrorKind) -> Error {
414
1
        IoError(obj)
415
1
    }
416
}
417

            
418
/// Return size of a commonly used packet header.
419
///
420
/// On Windows this packet header is implicitly added to send queues, so this size must be known
421
/// if an application needs to precalculate the exact send queue buffer size.
422
1
pub const fn packet_header_size() -> usize {
423
1
    std::mem::size_of::<raw::pcap_pkthdr>()
424
1
}
425

            
426
#[cfg(libpcap_1_10_0)]
427
#[repr(u32)]
428
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
429
/// The character encoding libpcap uses for strings. Use with `init`.
430
pub enum CharEncoding {
431
    /// Strings are in the local character encoding. On UN*X that is taken to be UTF-8, on Windows
432
    /// it is the local ANSI code page. This is the default.
433
    Local = raw::PCAP_CHAR_ENC_LOCAL,
434
    /// Strings are in UTF-8.
435
    Utf8 = raw::PCAP_CHAR_ENC_UTF_8,
436
}
437

            
438
/// Initialize the library, choosing the character encoding it uses for the strings it is given
439
/// and the strings it returns.
440
///
441
/// This is optional, but it has to come before any other libpcap call, and a second call asking
442
/// for a different encoding fails. Without it strings are in the local character encoding.
443
///
444
/// On Windows this is what makes a path outside ASCII work, since the local code page is
445
/// usually not UTF-8.
446
#[cfg(libpcap_1_10_0)]
447
2
pub fn init(encoding: CharEncoding) -> Result<(), Error> {
448
2
    let library = raw::require_library()?;
449

            
450
    #[cfg(windows)]
451
    if !raw::has_init() {
452
        return Err(Error::EntrypointNotFound("pcap_init"));
453
    }
454

            
455
2
    Error::with_errbuf(|err| {
456
2
        if unsafe { raw::pcap_init(&library, encoding as _, err) } != 0 {
457
1
            return Err(unsafe { Error::new(err) });
458
1
        }
459
1
        Ok(())
460
2
    })
461
2
}
462

            
463
#[cfg(test)]
464
mod tests {
465
    use std::error::Error as StdError;
466
    use std::{ffi::CString, io};
467

            
468
    #[cfg(libpcap_1_10_0)]
469
    use crate::raw::testmod::RAWMTX;
470

            
471
    use super::*;
472

            
473
    #[test]
474
    fn test_error_invalid_utf8() {
475
        let bytes: [u8; 8] = [0x78, 0xfe, 0xe9, 0x89, 0x00, 0x00, 0xed, 0x4f];
476
        let error = unsafe { Error::new(&bytes as *const _ as _) };
477
        // The message is kept, with the bytes that are not valid UTF-8 replaced.
478
        assert_eq!(error, Error::PcapError("x\u{fffd}\u{fffd}".to_string()));
479
    }
480

            
481
    #[test]
482
    fn test_error_null() {
483
        let error = unsafe { Error::new(std::ptr::null()) };
484
        assert_eq!(error, Error::PcapError("".to_string()));
485
    }
486

            
487
    #[test]
488
    fn test_error_interface_disappeared() {
489
        let message = CString::new("The interface disappeared").unwrap();
490
        let error = unsafe { Error::new(message.as_ptr()) };
491
        assert_eq!(error, Error::InterfaceDisappeared);
492

            
493
        // Windows puts the error code it got on the end of the message.
494
        let message = CString::new(
495
            "The interface disappeared (error code ERROR_DEVICE_REMOVED/STATUS_DEVICE_REMOVED)",
496
        )
497
        .unwrap();
498
        let error = unsafe { Error::new(message.as_ptr()) };
499
        assert_eq!(error, Error::InterfaceDisappeared);
500

            
501
        // Another message about the interface is still a plain one.
502
        let message = CString::new("The interface went down").unwrap();
503
        let error = unsafe { Error::new(message.as_ptr()) };
504
        assert_eq!(
505
            error,
506
            Error::PcapError("The interface went down".to_string())
507
        );
508
    }
509

            
510
    #[test]
511
    fn test_error_from_status() {
512
        let message = CString::new("no dice").unwrap();
513
        let cases = [
514
            (raw::PCAP_ERROR_BREAK, ErrorCode::Break),
515
            (raw::PCAP_ERROR_NOT_ACTIVATED, ErrorCode::NotActivated),
516
            (raw::PCAP_ERROR_ACTIVATED, ErrorCode::AlreadyActivated),
517
            (raw::PCAP_ERROR_NO_SUCH_DEVICE, ErrorCode::NoSuchDevice),
518
            (
519
                raw::PCAP_ERROR_RFMON_NOTSUP,
520
                ErrorCode::MonitorModeNotSupported,
521
            ),
522
            (raw::PCAP_ERROR_NOT_RFMON, ErrorCode::NotInMonitorMode),
523
            (raw::PCAP_ERROR_PERM_DENIED, ErrorCode::PermissionDenied),
524
            (raw::PCAP_ERROR_IFACE_NOT_UP, ErrorCode::InterfaceNotUp),
525
            (
526
                raw::PCAP_ERROR_CANTSET_TSTAMP_TYPE,
527
                ErrorCode::CannotSetTimestampType,
528
            ),
529
            (
530
                raw::PCAP_ERROR_PROMISC_PERM_DENIED,
531
                ErrorCode::PromiscuousPermissionDenied,
532
            ),
533
            (
534
                raw::PCAP_ERROR_TSTAMP_PRECISION_NOTSUP,
535
                ErrorCode::TimestampPrecisionNotSupported,
536
            ),
537
            (
538
                raw::PCAP_ERROR_CAPTURE_NOTSUP,
539
                ErrorCode::CaptureNotSupported,
540
            ),
541
        ];
542

            
543
        for (status, code) in cases {
544
            // A stopped loop is the one code that leaves the error buffer alone, so it is also
545
            // the one whose message is not read.
546
            let kept = match code {
547
                ErrorCode::Break => String::new(),
548
                _ => "no dice".to_string(),
549
            };
550
            let error = unsafe { Error::from_status(status, message.as_ptr()) };
551
            assert_eq!(error, Error::PcapErrorCode(code, kept));
552

            
553
            // With no message left behind, each code has to describe itself.
554
            let error = unsafe { Error::from_status(status, std::ptr::null()) };
555
            assert_eq!(error, Error::PcapErrorCode(code, String::new()));
556
            assert!(error.to_string().len() > "libpcap error: ".len());
557
        }
558

            
559
        // The generic code, and one libpcap has yet to define, leave only the message.
560
        for status in [raw::PCAP_ERROR, -99] {
561
            let error = unsafe { Error::from_status(status, message.as_ptr()) };
562
            assert_eq!(error, Error::PcapError("no dice".to_string()));
563
        }
564
    }
565

            
566
    #[test]
567
    #[allow(deprecated)]
568
    fn test_errors() {
569
        let mut errors: Vec<Error> = vec![];
570

            
571
        let bytes: [u8; 8] = [0x78, 0xfe, 0xe9, 0x89, 0x00, 0x00, 0xed, 0x4f];
572
        let cstr = unsafe { CStr::from_ptr(&bytes as *const _ as _) };
573

            
574
        errors.push(cstr.to_str().unwrap_err().into());
575
        errors.push(Error::InvalidString);
576
        errors.push(Error::PcapError("git rekt".to_string()));
577
        errors.push(Error::InvalidLinktype);
578
        errors.push(Error::UnsupportedTimestampType);
579
        errors.push(Error::UnsupportedTimestampPrecision);
580
        errors.push(Error::TimeoutExpired);
581
        errors.push(Error::NoMorePackets);
582
        errors.push(Error::InterfaceDisappeared);
583
        errors.push(Error::PcapErrorCode(
584
            ErrorCode::PermissionDenied,
585
            "nope".to_string(),
586
        ));
587
        // A code libpcap leaves no message with still has to describe itself.
588
        errors.push(Error::PcapErrorCode(ErrorCode::Break, String::new()));
589
        errors.push(Error::NonNonBlock);
590
        errors.push(Error::InsufficientMemory);
591
        errors.push(CString::new(b"f\0oo".to_vec()).unwrap_err().into());
592
        errors.push(io::Error::new(io::ErrorKind::Interrupted, "error").into());
593
        #[cfg(not(windows))]
594
        errors.push(Error::InvalidRawFd);
595
        #[cfg(windows)]
596
        errors.push(Error::InvalidPath);
597
        errors.push(Error::ErrnoError(errno::Errno(125)));
598
        #[cfg(windows)]
599
        errors.push(Error::LibraryNotFound);
600
        #[cfg(windows)]
601
        errors.push(Error::EntrypointNotFound("pcap_set_immediate_mode"));
602
        errors.push(Error::BufferOverflow);
603

            
604
        for error in errors.iter() {
605
            assert!(!error.to_string().is_empty());
606
            assert!(!error.description().is_empty());
607
            match error {
608
                Error::MalformedError(_) => assert!(error.cause().is_some()),
609
                _ => assert!(error.cause().is_none()),
610
            }
611
        }
612
    }
613

            
614
    #[test]
615
    fn test_packet_size() {
616
        assert_eq!(
617
            packet_header_size(),
618
            std::mem::size_of::<raw::pcap_pkthdr>()
619
        );
620
    }
621

            
622
    #[test]
623
    #[cfg(windows)]
624
    fn test_path_to_cstring_not_utf8() {
625
        use std::ffi::OsString;
626
        use std::os::windows::ffi::OsStringExt;
627

            
628
        // An unpaired surrogate, which a Windows path may hold and UTF-8 cannot express.
629
        let name = OsString::from_wide(&[0xd800]);
630
        let result = path_to_cstring(Path::new(&name));
631
        assert_eq!(result.unwrap_err(), Error::InvalidPath);
632
    }
633

            
634
    #[test]
635
    #[cfg(libpcap_1_10_0)]
636
    fn test_init() {
637
        let _m = RAWMTX.lock();
638

            
639
        #[cfg(windows)]
640
        let has = raw::has_init_context();
641
        #[cfg(windows)]
642
        has.expect().times(..).return_const(true);
643

            
644
        let ctx = raw::pcap_init_context();
645
        ctx.expect()
646
            .withf_st(|_, arg1, _| *arg1 == raw::PCAP_CHAR_ENC_UTF_8)
647
            .return_once(|_, _, _| 0);
648

            
649
        let result = init(CharEncoding::Utf8);
650
        assert!(result.is_ok());
651

            
652
        let ctx = raw::pcap_init_context();
653
        ctx.checkpoint();
654
        ctx.expect()
655
            .withf_st(|_, arg1, _| *arg1 == raw::PCAP_CHAR_ENC_LOCAL)
656
            .return_once(|_, _, _| -1);
657

            
658
        let result = init(CharEncoding::Local);
659
        assert!(result.is_err());
660
    }
661
}