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};
64
use std::fmt;
65

            
66
use self::Error::*;
67

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

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

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

            
92
mod raw;
93

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

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

            
104
/// An error received from pcap
105
#[derive(Debug, PartialEq, Eq)]
106
pub enum Error {
107
    /// The underlying library returned invalid UTF-8
108
    MalformedError(std::str::Utf8Error),
109
    /// The underlying library returned a null string
110
    InvalidString,
111
    /// The unerlying library returned an error
112
    PcapError(String),
113
    /// The linktype was invalid or unknown
114
    InvalidLinktype,
115
    /// The timeout expired while reading from a live capture
116
    TimeoutExpired,
117
    /// No more packets to read from the file
118
    NoMorePackets,
119
    /// Must be in non-blocking mode to function
120
    NonNonBlock,
121
    /// There is not sufficent memory to create a dead capture
122
    InsufficientMemory,
123
    /// An invalid input string (internal null)
124
    InvalidInputString,
125
    /// An IO error occurred
126
    IoError(std::io::ErrorKind),
127
    #[cfg(not(windows))]
128
    /// An invalid raw file descriptor was provided
129
    InvalidRawFd,
130
    /// Errno error
131
    ErrnoError(errno::Errno),
132
    /// Buffer size overflows capacity
133
    BufferOverflow,
134
}
135

            
136
impl Error {
137
49
    unsafe fn new(ptr: *const libc::c_char) -> Error {
138
49
        if ptr.is_null() {
139
2
            return PcapError(String::new());
140
47
        }
141

            
142
        // libpcap truncates its messages at PCAP_ERRBUF_SIZE without regard for character
143
        // boundaries, so one quoting a long path can end in the middle of a UTF-8 sequence.
144
        // Take such a message lossily rather than lose it. Strings that are not error messages
145
        // still go through cstr_to_string, which rejects the malformed ones.
146
47
        PcapError(
147
47
            unsafe { CStr::from_ptr(ptr as _) }
148
47
                .to_string_lossy()
149
47
                .into_owned(),
150
47
        )
151
49
    }
152

            
153
54
    fn with_errbuf<T, F>(func: F) -> Result<T, Error>
154
54
    where
155
54
        F: FnOnce(*mut libc::c_char) -> Result<T, Error>,
156
    {
157
54
        let mut errbuf = [0i8; 256];
158
54
        func(errbuf.as_mut_ptr() as _)
159
54
    }
160
}
161

            
162
27
unsafe fn cstr_to_string(ptr: *const libc::c_char) -> Result<Option<String>, Error> {
163
27
    let string = if ptr.is_null() {
164
2
        None
165
    } else {
166
25
        Some(unsafe { CStr::from_ptr(ptr as _) }.to_str()?.to_owned())
167
    };
168
27
    Ok(string)
169
27
}
170

            
171
impl fmt::Display for Error {
172
26
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173
26
        match *self {
174
2
            MalformedError(ref e) => write!(f, "libpcap returned invalid UTF-8: {e}"),
175
2
            InvalidString => write!(f, "libpcap returned a null string"),
176
2
            PcapError(ref e) => write!(f, "libpcap error: {e}"),
177
2
            InvalidLinktype => write!(f, "invalid or unknown linktype"),
178
2
            TimeoutExpired => write!(f, "timeout expired while reading from a live capture"),
179
2
            NonNonBlock => write!(f, "must be in non-blocking mode to function"),
180
2
            NoMorePackets => write!(f, "no more packets to read from the file"),
181
2
            InsufficientMemory => write!(f, "insufficient memory"),
182
2
            InvalidInputString => write!(f, "invalid input string (internal null)"),
183
2
            IoError(ref e) => write!(f, "io error occurred: {e:?}"),
184
            #[cfg(not(windows))]
185
2
            InvalidRawFd => write!(f, "invalid raw file descriptor provided"),
186
2
            ErrnoError(ref e) => write!(f, "libpcap os errno: {e}"),
187
2
            BufferOverflow => write!(f, "buffer size too large"),
188
        }
189
26
    }
190
}
191

            
192
// Using description is deprecated. Remove in next version.
193
impl std::error::Error for Error {
194
26
    fn description(&self) -> &str {
195
26
        match *self {
196
2
            MalformedError(..) => "libpcap returned invalid UTF-8",
197
2
            PcapError(..) => "libpcap FFI error",
198
2
            InvalidString => "libpcap returned a null string",
199
2
            InvalidLinktype => "invalid or unknown linktype",
200
2
            TimeoutExpired => "timeout expired while reading from a live capture",
201
2
            NonNonBlock => "must be in non-blocking mode to function",
202
2
            NoMorePackets => "no more packets to read from the file",
203
2
            InsufficientMemory => "insufficient memory",
204
2
            InvalidInputString => "invalid input string (internal null)",
205
2
            IoError(..) => "io error occurred",
206
            #[cfg(not(windows))]
207
2
            InvalidRawFd => "invalid raw file descriptor provided",
208
2
            ErrnoError(..) => "internal error, providing errno",
209
2
            BufferOverflow => "buffer size too large",
210
        }
211
26
    }
212

            
213
26
    fn cause(&self) -> Option<&dyn std::error::Error> {
214
26
        match *self {
215
2
            MalformedError(ref e) => Some(e),
216
24
            _ => None,
217
        }
218
26
    }
219
}
220

            
221
impl From<ffi::NulError> for Error {
222
2
    fn from(_: ffi::NulError) -> Error {
223
2
        InvalidInputString
224
2
    }
225
}
226

            
227
impl From<std::str::Utf8Error> for Error {
228
2
    fn from(obj: std::str::Utf8Error) -> Error {
229
2
        MalformedError(obj)
230
2
    }
231
}
232

            
233
impl From<std::io::Error> for Error {
234
2
    fn from(obj: std::io::Error) -> Error {
235
2
        obj.kind().into()
236
2
    }
237
}
238

            
239
impl From<std::io::ErrorKind> for Error {
240
2
    fn from(obj: std::io::ErrorKind) -> Error {
241
2
        IoError(obj)
242
2
    }
243
}
244

            
245
/// Return size of a commonly used packet header.
246
///
247
/// On Windows this packet header is implicitly added to send queues, so this size must be known
248
/// if an application needs to precalculate the exact send queue buffer size.
249
2
pub const fn packet_header_size() -> usize {
250
2
    std::mem::size_of::<raw::pcap_pkthdr>()
251
2
}
252

            
253
#[cfg(test)]
254
mod tests {
255
    use std::error::Error as StdError;
256
    use std::{ffi::CString, io};
257

            
258
    use super::*;
259

            
260
    #[test]
261
    fn test_error_invalid_utf8() {
262
        let bytes: [u8; 8] = [0x78, 0xfe, 0xe9, 0x89, 0x00, 0x00, 0xed, 0x4f];
263
        let error = unsafe { Error::new(&bytes as *const _ as _) };
264
        // The message is kept, with the bytes that are not valid UTF-8 replaced.
265
        assert_eq!(error, Error::PcapError("x\u{fffd}\u{fffd}".to_string()));
266
    }
267

            
268
    #[test]
269
    fn test_error_null() {
270
        let error = unsafe { Error::new(std::ptr::null()) };
271
        assert_eq!(error, Error::PcapError("".to_string()));
272
    }
273

            
274
    #[test]
275
    #[allow(deprecated)]
276
    fn test_errors() {
277
        let mut errors: Vec<Error> = vec![];
278

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

            
282
        errors.push(cstr.to_str().unwrap_err().into());
283
        errors.push(Error::InvalidString);
284
        errors.push(Error::PcapError("git rekt".to_string()));
285
        errors.push(Error::InvalidLinktype);
286
        errors.push(Error::TimeoutExpired);
287
        errors.push(Error::NoMorePackets);
288
        errors.push(Error::NonNonBlock);
289
        errors.push(Error::InsufficientMemory);
290
        errors.push(CString::new(b"f\0oo".to_vec()).unwrap_err().into());
291
        errors.push(io::Error::new(io::ErrorKind::Interrupted, "error").into());
292
        #[cfg(not(windows))]
293
        errors.push(Error::InvalidRawFd);
294
        errors.push(Error::ErrnoError(errno::Errno(125)));
295
        errors.push(Error::BufferOverflow);
296

            
297
        for error in errors.iter() {
298
            assert!(!error.to_string().is_empty());
299
            assert!(!error.description().is_empty());
300
            match error {
301
                Error::MalformedError(_) => assert!(error.cause().is_some()),
302
                _ => assert!(error.cause().is_none()),
303
            }
304
        }
305
    }
306

            
307
    #[test]
308
    fn test_packet_size() {
309
        assert_eq!(
310
            packet_header_size(),
311
            std::mem::size_of::<raw::pcap_pkthdr>()
312
        );
313
    }
314
}