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

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

            
214
impl Error {
215
134
    unsafe fn message(ptr: *const libc::c_char) -> String {
216
134
        unsafe { cstr_to_string_lossy(ptr) }.unwrap_or_default()
217
134
    }
218

            
219
70
    unsafe fn new(ptr: *const libc::c_char) -> Error {
220
70
        let message = unsafe { Self::message(ptr) };
221

            
222
        // An interface going away is reported as PCAP_ERROR like every other failure, so the
223
        // message is all there is to go on. Windows adds the error code it got to the end,
224
        // which is why this matches the start rather than the whole string.
225
70
        if message.starts_with("The interface disappeared") {
226
4
            return InterfaceDisappeared;
227
66
        }
228

            
229
66
        PcapError(message)
230
70
    }
231

            
232
    /// Read one of the status codes libpcap returns, along with the message it left behind.
233
66
    unsafe fn from_status(status: libc::c_int, ptr: *const libc::c_char) -> Error {
234
        // A loop that was told to stop is the one case libpcap leaves no message for, so the
235
        // error buffer still holds the last thing that went wrong on the handle. Do not read it.
236
66
        if status == raw::PCAP_ERROR_BREAK {
237
12
            return PcapErrorCode(ErrorCode::Break, String::new());
238
54
        }
239

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

            
258
46
        PcapErrorCode(code, unsafe { Self::message(ptr) })
259
66
    }
260

            
261
90
    fn with_errbuf<T, F>(func: F) -> Result<T, Error>
262
90
    where
263
90
        F: FnOnce(*mut libc::c_char) -> Result<T, Error>,
264
    {
265
90
        let mut errbuf = [0i8; 256];
266
90
        func(errbuf.as_mut_ptr() as _)
267
90
    }
268
}
269

            
270
190
unsafe fn cstr<'a>(ptr: *const libc::c_char) -> Option<&'a CStr> {
271
261
    (!ptr.is_null()).then(|| unsafe { CStr::from_ptr(ptr as _) })
272
190
}
273

            
274
// Strict, for a string that goes back to libpcap: a device name that will not round-trip is worse
275
// than no name at all.
276
40
unsafe fn cstr_to_string(ptr: *const libc::c_char) -> Result<Option<String>, Error> {
277
40
    Ok(unsafe { cstr(ptr) }
278
40
        .map(CStr::to_str)
279
40
        .transpose()?
280
34
        .map(str::to_owned))
281
40
}
282

            
283
// Lossy, for a string that is only displayed: a message truncated mid-sequence at
284
// PCAP_ERRBUF_SIZE, or a description in the local Windows code page, is worth showing anyway.
285
150
unsafe fn cstr_to_string_lossy(ptr: *const libc::c_char) -> Option<String> {
286
205
    unsafe { cstr(ptr) }.map(|s| s.to_string_lossy().into_owned())
287
150
}
288

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

            
305
140
    Ok(CString::new(bytes)?)
306
140
}
307

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

            
339
// Using description is deprecated. Remove in next version.
340
impl std::error::Error for Error {
341
36
    fn description(&self) -> &str {
342
36
        match *self {
343
2
            MalformedError(..) => "libpcap returned invalid UTF-8",
344
2
            PcapError(..) => "libpcap FFI error",
345
4
            PcapErrorCode(..) => "libpcap FFI error with a status code",
346
2
            InvalidString => "libpcap returned a null string",
347
2
            InvalidLinktype => "invalid or unknown linktype",
348
2
            UnsupportedTimestampType => "unsupported timestamp type",
349
2
            UnsupportedTimestampPrecision => "unsupported timestamp precision",
350
2
            TimeoutExpired => "timeout expired while reading from a live capture",
351
2
            NonNonBlock => "must be in non-blocking mode to function",
352
2
            NoMorePackets => "no more packets to read from the file",
353
2
            InterfaceDisappeared => "the interface being captured from went away",
354
2
            InsufficientMemory => "insufficient memory",
355
2
            InvalidInputString => "invalid input string (internal null)",
356
2
            IoError(..) => "io error occurred",
357
            #[cfg(not(windows))]
358
2
            InvalidRawFd => "invalid raw file descriptor provided",
359
            #[cfg(windows)]
360
            InvalidPath => "invalid path (not valid UTF-8)",
361
2
            ErrnoError(..) => "internal error, providing errno",
362
2
            BufferOverflow => "buffer size too large",
363
        }
364
36
    }
365

            
366
36
    fn cause(&self) -> Option<&dyn std::error::Error> {
367
36
        match *self {
368
2
            MalformedError(ref e) => Some(e),
369
34
            _ => None,
370
        }
371
36
    }
372
}
373

            
374
impl From<ffi::NulError> for Error {
375
2
    fn from(_: ffi::NulError) -> Error {
376
2
        InvalidInputString
377
2
    }
378
}
379

            
380
impl From<std::str::Utf8Error> for Error {
381
8
    fn from(obj: std::str::Utf8Error) -> Error {
382
8
        MalformedError(obj)
383
8
    }
384
}
385

            
386
impl From<std::io::Error> for Error {
387
2
    fn from(obj: std::io::Error) -> Error {
388
2
        obj.kind().into()
389
2
    }
390
}
391

            
392
impl From<std::io::ErrorKind> for Error {
393
2
    fn from(obj: std::io::ErrorKind) -> Error {
394
2
        IoError(obj)
395
2
    }
396
}
397

            
398
/// Return size of a commonly used packet header.
399
///
400
/// On Windows this packet header is implicitly added to send queues, so this size must be known
401
/// if an application needs to precalculate the exact send queue buffer size.
402
2
pub const fn packet_header_size() -> usize {
403
2
    std::mem::size_of::<raw::pcap_pkthdr>()
404
2
}
405

            
406
#[cfg(libpcap_1_10_0)]
407
#[repr(u32)]
408
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
409
/// The character encoding libpcap uses for strings. Use with `init`.
410
pub enum CharEncoding {
411
    /// Strings are in the local character encoding. On UN*X that is taken to be UTF-8, on Windows
412
    /// it is the local ANSI code page. This is the default.
413
    Local = raw::PCAP_CHAR_ENC_LOCAL,
414
    /// Strings are in UTF-8.
415
    Utf8 = raw::PCAP_CHAR_ENC_UTF_8,
416
}
417

            
418
/// Initialize the library, choosing the character encoding it uses for the strings it is given
419
/// and the strings it returns.
420
///
421
/// This is optional, but it has to come before any other libpcap call, and a second call asking
422
/// for a different encoding fails. Without it strings are in the local character encoding.
423
///
424
/// On Windows this is what makes a path outside ASCII work, since the local code page is
425
/// usually not UTF-8.
426
#[cfg(libpcap_1_10_0)]
427
4
pub fn init(encoding: CharEncoding) -> Result<(), Error> {
428
6
    Error::with_errbuf(|err| {
429
4
        if unsafe { raw::pcap_init(encoding as _, err) } != 0 {
430
2
            return Err(unsafe { Error::new(err) });
431
2
        }
432
2
        Ok(())
433
4
    })
434
4
}
435

            
436
#[cfg(test)]
437
mod tests {
438
    use std::error::Error as StdError;
439
    use std::{ffi::CString, io};
440

            
441
    #[cfg(libpcap_1_10_0)]
442
    use crate::raw::testmod::RAWMTX;
443

            
444
    use super::*;
445

            
446
    #[test]
447
    fn test_error_invalid_utf8() {
448
        let bytes: [u8; 8] = [0x78, 0xfe, 0xe9, 0x89, 0x00, 0x00, 0xed, 0x4f];
449
        let error = unsafe { Error::new(&bytes as *const _ as _) };
450
        // The message is kept, with the bytes that are not valid UTF-8 replaced.
451
        assert_eq!(error, Error::PcapError("x\u{fffd}\u{fffd}".to_string()));
452
    }
453

            
454
    #[test]
455
    fn test_error_null() {
456
        let error = unsafe { Error::new(std::ptr::null()) };
457
        assert_eq!(error, Error::PcapError("".to_string()));
458
    }
459

            
460
    #[test]
461
    fn test_error_interface_disappeared() {
462
        let message = CString::new("The interface disappeared").unwrap();
463
        let error = unsafe { Error::new(message.as_ptr()) };
464
        assert_eq!(error, Error::InterfaceDisappeared);
465

            
466
        // Windows puts the error code it got on the end of the message.
467
        let message = CString::new(
468
            "The interface disappeared (error code ERROR_DEVICE_REMOVED/STATUS_DEVICE_REMOVED)",
469
        )
470
        .unwrap();
471
        let error = unsafe { Error::new(message.as_ptr()) };
472
        assert_eq!(error, Error::InterfaceDisappeared);
473

            
474
        // Another message about the interface is still a plain one.
475
        let message = CString::new("The interface went down").unwrap();
476
        let error = unsafe { Error::new(message.as_ptr()) };
477
        assert_eq!(
478
            error,
479
            Error::PcapError("The interface went down".to_string())
480
        );
481
    }
482

            
483
    #[test]
484
    fn test_error_from_status() {
485
        let message = CString::new("no dice").unwrap();
486
        let cases = [
487
            (raw::PCAP_ERROR_BREAK, ErrorCode::Break),
488
            (raw::PCAP_ERROR_NOT_ACTIVATED, ErrorCode::NotActivated),
489
            (raw::PCAP_ERROR_ACTIVATED, ErrorCode::AlreadyActivated),
490
            (raw::PCAP_ERROR_NO_SUCH_DEVICE, ErrorCode::NoSuchDevice),
491
            (
492
                raw::PCAP_ERROR_RFMON_NOTSUP,
493
                ErrorCode::MonitorModeNotSupported,
494
            ),
495
            (raw::PCAP_ERROR_NOT_RFMON, ErrorCode::NotInMonitorMode),
496
            (raw::PCAP_ERROR_PERM_DENIED, ErrorCode::PermissionDenied),
497
            (raw::PCAP_ERROR_IFACE_NOT_UP, ErrorCode::InterfaceNotUp),
498
            (
499
                raw::PCAP_ERROR_CANTSET_TSTAMP_TYPE,
500
                ErrorCode::CannotSetTimestampType,
501
            ),
502
            (
503
                raw::PCAP_ERROR_PROMISC_PERM_DENIED,
504
                ErrorCode::PromiscuousPermissionDenied,
505
            ),
506
            (
507
                raw::PCAP_ERROR_TSTAMP_PRECISION_NOTSUP,
508
                ErrorCode::TimestampPrecisionNotSupported,
509
            ),
510
            (
511
                raw::PCAP_ERROR_CAPTURE_NOTSUP,
512
                ErrorCode::CaptureNotSupported,
513
            ),
514
        ];
515

            
516
        for (status, code) in cases {
517
            // A stopped loop is the one code that leaves the error buffer alone, so it is also
518
            // the one whose message is not read.
519
            let kept = match code {
520
                ErrorCode::Break => String::new(),
521
                _ => "no dice".to_string(),
522
            };
523
            let error = unsafe { Error::from_status(status, message.as_ptr()) };
524
            assert_eq!(error, Error::PcapErrorCode(code, kept));
525

            
526
            // With no message left behind, each code has to describe itself.
527
            let error = unsafe { Error::from_status(status, std::ptr::null()) };
528
            assert_eq!(error, Error::PcapErrorCode(code, String::new()));
529
            assert!(error.to_string().len() > "libpcap error: ".len());
530
        }
531

            
532
        // The generic code, and one libpcap has yet to define, leave only the message.
533
        for status in [raw::PCAP_ERROR, -99] {
534
            let error = unsafe { Error::from_status(status, message.as_ptr()) };
535
            assert_eq!(error, Error::PcapError("no dice".to_string()));
536
        }
537
    }
538

            
539
    #[test]
540
    #[allow(deprecated)]
541
    fn test_errors() {
542
        let mut errors: Vec<Error> = vec![];
543

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

            
547
        errors.push(cstr.to_str().unwrap_err().into());
548
        errors.push(Error::InvalidString);
549
        errors.push(Error::PcapError("git rekt".to_string()));
550
        errors.push(Error::InvalidLinktype);
551
        errors.push(Error::UnsupportedTimestampType);
552
        errors.push(Error::UnsupportedTimestampPrecision);
553
        errors.push(Error::TimeoutExpired);
554
        errors.push(Error::NoMorePackets);
555
        errors.push(Error::InterfaceDisappeared);
556
        errors.push(Error::PcapErrorCode(
557
            ErrorCode::PermissionDenied,
558
            "nope".to_string(),
559
        ));
560
        // A code libpcap leaves no message with still has to describe itself.
561
        errors.push(Error::PcapErrorCode(ErrorCode::Break, String::new()));
562
        errors.push(Error::NonNonBlock);
563
        errors.push(Error::InsufficientMemory);
564
        errors.push(CString::new(b"f\0oo".to_vec()).unwrap_err().into());
565
        errors.push(io::Error::new(io::ErrorKind::Interrupted, "error").into());
566
        #[cfg(not(windows))]
567
        errors.push(Error::InvalidRawFd);
568
        #[cfg(windows)]
569
        errors.push(Error::InvalidPath);
570
        errors.push(Error::ErrnoError(errno::Errno(125)));
571
        errors.push(Error::BufferOverflow);
572

            
573
        for error in errors.iter() {
574
            assert!(!error.to_string().is_empty());
575
            assert!(!error.description().is_empty());
576
            match error {
577
                Error::MalformedError(_) => assert!(error.cause().is_some()),
578
                _ => assert!(error.cause().is_none()),
579
            }
580
        }
581
    }
582

            
583
    #[test]
584
    fn test_packet_size() {
585
        assert_eq!(
586
            packet_header_size(),
587
            std::mem::size_of::<raw::pcap_pkthdr>()
588
        );
589
    }
590

            
591
    #[test]
592
    #[cfg(windows)]
593
    fn test_path_to_cstring_not_utf8() {
594
        use std::ffi::OsString;
595
        use std::os::windows::ffi::OsStringExt;
596

            
597
        // An unpaired surrogate, which a Windows path may hold and UTF-8 cannot express.
598
        let name = OsString::from_wide(&[0xd800]);
599
        let result = path_to_cstring(Path::new(&name));
600
        assert_eq!(result.unwrap_err(), Error::InvalidPath);
601
    }
602

            
603
    #[test]
604
    #[cfg(libpcap_1_10_0)]
605
    fn test_init() {
606
        let _m = RAWMTX.lock();
607

            
608
        let ctx = raw::pcap_init_context();
609
        ctx.expect()
610
            .withf_st(|arg1, _| *arg1 == raw::PCAP_CHAR_ENC_UTF_8)
611
            .return_once(|_, _| 0);
612

            
613
        let result = init(CharEncoding::Utf8);
614
        assert!(result.is_ok());
615

            
616
        let ctx = raw::pcap_init_context();
617
        ctx.checkpoint();
618
        ctx.expect()
619
            .withf_st(|arg1, _| *arg1 == raw::PCAP_CHAR_ENC_LOCAL)
620
            .return_once(|_, _| -1);
621

            
622
        let result = init(CharEncoding::Local);
623
        assert!(result.is_err());
624
    }
625
}