pcap/
lib.rs

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
63use std::ffi::{self, CStr};
64use std::fmt;
65
66use self::Error::*;
67
68mod capture;
69mod codec;
70mod device;
71mod linktype;
72mod packet;
73
74#[cfg(not(windows))]
75pub use capture::activated::open_raw_fd;
76pub use capture::{
77    activated::{
78        iterator::PacketIter, BpfInstruction, BpfProgram, BreakLoop, Direction, Savefile, Stat,
79    },
80    inactive::TimestampType,
81    {Activated, Active, Capture, Dead, Inactive, Offline, Precision, State},
82};
83pub use codec::PacketCodec;
84pub use device::{Address, ConnectionStatus, Device, DeviceFlags, IfFlags};
85pub use linktype::Linktype;
86pub use packet::{Packet, PacketHeader};
87
88#[deprecated(note = "Renamed to TimestampType")]
89/// An old name for `TimestampType`, kept around for backward-compatibility.
90pub type TstampType = TimestampType;
91
92mod raw;
93
94#[cfg(windows)]
95#[cfg_attr(docsrs, doc(cfg(windows)))]
96pub mod sendqueue;
97
98#[cfg(feature = "capture-stream")]
99mod stream;
100#[cfg(feature = "capture-stream")]
101#[cfg_attr(docsrs, doc(cfg(feature = "capture-stream")))]
102pub use stream::PacketStream;
103
104/// An error received from pcap
105#[derive(Debug, PartialEq, Eq)]
106pub 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
136impl Error {
137    unsafe fn new(ptr: *const libc::c_char) -> Error {
138        match cstr_to_string(ptr) {
139            Err(e) => e as Error,
140            Ok(string) => PcapError(string.unwrap_or_default()),
141        }
142    }
143
144    fn with_errbuf<T, F>(func: F) -> Result<T, Error>
145    where
146        F: FnOnce(*mut libc::c_char) -> Result<T, Error>,
147    {
148        let mut errbuf = [0i8; 256];
149        func(errbuf.as_mut_ptr() as _)
150    }
151}
152
153unsafe fn cstr_to_string(ptr: *const libc::c_char) -> Result<Option<String>, Error> {
154    let string = if ptr.is_null() {
155        None
156    } else {
157        Some(CStr::from_ptr(ptr as _).to_str()?.to_owned())
158    };
159    Ok(string)
160}
161
162impl fmt::Display for Error {
163    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164        match *self {
165            MalformedError(ref e) => write!(f, "libpcap returned invalid UTF-8: {e}"),
166            InvalidString => write!(f, "libpcap returned a null string"),
167            PcapError(ref e) => write!(f, "libpcap error: {e}"),
168            InvalidLinktype => write!(f, "invalid or unknown linktype"),
169            TimeoutExpired => write!(f, "timeout expired while reading from a live capture"),
170            NonNonBlock => write!(f, "must be in non-blocking mode to function"),
171            NoMorePackets => write!(f, "no more packets to read from the file"),
172            InsufficientMemory => write!(f, "insufficient memory"),
173            InvalidInputString => write!(f, "invalid input string (internal null)"),
174            IoError(ref e) => write!(f, "io error occurred: {e:?}"),
175            #[cfg(not(windows))]
176            InvalidRawFd => write!(f, "invalid raw file descriptor provided"),
177            ErrnoError(ref e) => write!(f, "libpcap os errno: {e}"),
178            BufferOverflow => write!(f, "buffer size too large"),
179        }
180    }
181}
182
183// Using description is deprecated. Remove in next version.
184impl std::error::Error for Error {
185    fn description(&self) -> &str {
186        match *self {
187            MalformedError(..) => "libpcap returned invalid UTF-8",
188            PcapError(..) => "libpcap FFI error",
189            InvalidString => "libpcap returned a null string",
190            InvalidLinktype => "invalid or unknown linktype",
191            TimeoutExpired => "timeout expired while reading from a live capture",
192            NonNonBlock => "must be in non-blocking mode to function",
193            NoMorePackets => "no more packets to read from the file",
194            InsufficientMemory => "insufficient memory",
195            InvalidInputString => "invalid input string (internal null)",
196            IoError(..) => "io error occurred",
197            #[cfg(not(windows))]
198            InvalidRawFd => "invalid raw file descriptor provided",
199            ErrnoError(..) => "internal error, providing errno",
200            BufferOverflow => "buffer size too large",
201        }
202    }
203
204    fn cause(&self) -> Option<&dyn std::error::Error> {
205        match *self {
206            MalformedError(ref e) => Some(e),
207            _ => None,
208        }
209    }
210}
211
212impl From<ffi::NulError> for Error {
213    fn from(_: ffi::NulError) -> Error {
214        InvalidInputString
215    }
216}
217
218impl From<std::str::Utf8Error> for Error {
219    fn from(obj: std::str::Utf8Error) -> Error {
220        MalformedError(obj)
221    }
222}
223
224impl From<std::io::Error> for Error {
225    fn from(obj: std::io::Error) -> Error {
226        obj.kind().into()
227    }
228}
229
230impl From<std::io::ErrorKind> for Error {
231    fn from(obj: std::io::ErrorKind) -> Error {
232        IoError(obj)
233    }
234}
235
236/// Return size of a commonly used packet header.
237///
238/// On Windows this packet header is implicitly added to send queues, so this size must be known
239/// if an application needs to precalculate the exact send queue buffer size.
240pub const fn packet_header_size() -> usize {
241    std::mem::size_of::<raw::pcap_pkthdr>()
242}
243
244#[cfg(test)]
245mod tests {
246    use std::error::Error as StdError;
247    use std::{ffi::CString, io};
248
249    use super::*;
250
251    #[test]
252    fn test_error_invalid_utf8() {
253        let bytes: [u8; 8] = [0x78, 0xfe, 0xe9, 0x89, 0x00, 0x00, 0xed, 0x4f];
254        let error = unsafe { Error::new(&bytes as *const _ as _) };
255        assert!(matches!(error, Error::MalformedError(_)));
256    }
257
258    #[test]
259    fn test_error_null() {
260        let error = unsafe { Error::new(std::ptr::null()) };
261        assert_eq!(error, Error::PcapError("".to_string()));
262    }
263
264    #[test]
265    #[allow(deprecated)]
266    fn test_errors() {
267        let mut errors: Vec<Error> = vec![];
268
269        let bytes: [u8; 8] = [0x78, 0xfe, 0xe9, 0x89, 0x00, 0x00, 0xed, 0x4f];
270        let cstr = unsafe { CStr::from_ptr(&bytes as *const _ as _) };
271
272        errors.push(cstr.to_str().unwrap_err().into());
273        errors.push(Error::InvalidString);
274        errors.push(Error::PcapError("git rekt".to_string()));
275        errors.push(Error::InvalidLinktype);
276        errors.push(Error::TimeoutExpired);
277        errors.push(Error::NoMorePackets);
278        errors.push(Error::NonNonBlock);
279        errors.push(Error::InsufficientMemory);
280        errors.push(CString::new(b"f\0oo".to_vec()).unwrap_err().into());
281        errors.push(io::Error::new(io::ErrorKind::Interrupted, "error").into());
282        #[cfg(not(windows))]
283        errors.push(Error::InvalidRawFd);
284        errors.push(Error::ErrnoError(errno::Errno(125)));
285        errors.push(Error::BufferOverflow);
286
287        for error in errors.iter() {
288            assert!(!error.to_string().is_empty());
289            assert!(!error.description().is_empty());
290            match error {
291                Error::MalformedError(_) => assert!(error.cause().is_some()),
292                _ => assert!(error.cause().is_none()),
293            }
294        }
295    }
296
297    #[test]
298    fn test_packet_size() {
299        assert_eq!(
300            packet_header_size(),
301            std::mem::size_of::<raw::pcap_pkthdr>()
302        );
303    }
304}