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::{
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")]
89pub 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#[derive(Debug, PartialEq, Eq)]
106pub enum Error {
107 MalformedError(std::str::Utf8Error),
109 InvalidString,
111 PcapError(String),
113 InvalidLinktype,
115 TimeoutExpired,
117 NoMorePackets,
119 NonNonBlock,
121 InsufficientMemory,
123 InvalidInputString,
125 IoError(std::io::ErrorKind),
127 #[cfg(not(windows))]
128 InvalidRawFd,
130 ErrnoError(errno::Errno),
132 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
183impl 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
236pub 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}