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