use std::ffi::{self, CStr};
use std::fmt;
use self::Error::*;
mod capture;
mod codec;
mod device;
mod linktype;
mod packet;
#[cfg(not(windows))]
pub use capture::activated::open_raw_fd;
pub use capture::{
activated::{iterator::PacketIter, BpfInstruction, BpfProgram, Direction, Savefile, Stat},
inactive::TimestampType,
{Activated, Active, Capture, Dead, Inactive, Offline, Precision, State},
};
pub use codec::PacketCodec;
pub use device::{Address, ConnectionStatus, Device, DeviceFlags, IfFlags};
pub use linktype::Linktype;
pub use packet::{Packet, PacketHeader};
#[deprecated(note = "Renamed to TimestampType")]
pub type TstampType = TimestampType;
mod raw;
#[cfg(windows)]
pub mod sendqueue;
#[cfg(feature = "capture-stream")]
mod stream;
#[cfg(feature = "capture-stream")]
pub use stream::PacketStream;
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
MalformedError(std::str::Utf8Error),
InvalidString,
PcapError(String),
InvalidLinktype,
TimeoutExpired,
NoMorePackets,
NonNonBlock,
InsufficientMemory,
InvalidInputString,
IoError(std::io::ErrorKind),
#[cfg(not(windows))]
InvalidRawFd,
ErrnoError(errno::Errno),
BufferOverflow,
}
impl Error {
unsafe fn new(ptr: *const libc::c_char) -> Error {
match cstr_to_string(ptr) {
Err(e) => e as Error,
Ok(string) => PcapError(string.unwrap_or_default()),
}
}
fn with_errbuf<T, F>(func: F) -> Result<T, Error>
where
F: FnOnce(*mut libc::c_char) -> Result<T, Error>,
{
let mut errbuf = [0i8; 256];
func(errbuf.as_mut_ptr() as _)
}
}
unsafe fn cstr_to_string(ptr: *const libc::c_char) -> Result<Option<String>, Error> {
let string = if ptr.is_null() {
None
} else {
Some(CStr::from_ptr(ptr as _).to_str()?.to_owned())
};
Ok(string)
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
MalformedError(ref e) => write!(f, "libpcap returned invalid UTF-8: {}", e),
InvalidString => write!(f, "libpcap returned a null string"),
PcapError(ref e) => write!(f, "libpcap error: {}", e),
InvalidLinktype => write!(f, "invalid or unknown linktype"),
TimeoutExpired => write!(f, "timeout expired while reading from a live capture"),
NonNonBlock => write!(f, "must be in non-blocking mode to function"),
NoMorePackets => write!(f, "no more packets to read from the file"),
InsufficientMemory => write!(f, "insufficient memory"),
InvalidInputString => write!(f, "invalid input string (internal null)"),
IoError(ref e) => write!(f, "io error occurred: {:?}", e),
#[cfg(not(windows))]
InvalidRawFd => write!(f, "invalid raw file descriptor provided"),
ErrnoError(ref e) => write!(f, "libpcap os errno: {}", e),
BufferOverflow => write!(f, "buffer size too large"),
}
}
}
impl std::error::Error for Error {
fn description(&self) -> &str {
match *self {
MalformedError(..) => "libpcap returned invalid UTF-8",
PcapError(..) => "libpcap FFI error",
InvalidString => "libpcap returned a null string",
InvalidLinktype => "invalid or unknown linktype",
TimeoutExpired => "timeout expired while reading from a live capture",
NonNonBlock => "must be in non-blocking mode to function",
NoMorePackets => "no more packets to read from the file",
InsufficientMemory => "insufficient memory",
InvalidInputString => "invalid input string (internal null)",
IoError(..) => "io error occurred",
#[cfg(not(windows))]
InvalidRawFd => "invalid raw file descriptor provided",
ErrnoError(..) => "internal error, providing errno",
BufferOverflow => "buffer size too large",
}
}
fn cause(&self) -> Option<&dyn std::error::Error> {
match *self {
MalformedError(ref e) => Some(e),
_ => None,
}
}
}
impl From<ffi::NulError> for Error {
fn from(_: ffi::NulError) -> Error {
InvalidInputString
}
}
impl From<std::str::Utf8Error> for Error {
fn from(obj: std::str::Utf8Error) -> Error {
MalformedError(obj)
}
}
impl From<std::io::Error> for Error {
fn from(obj: std::io::Error) -> Error {
obj.kind().into()
}
}
impl From<std::io::ErrorKind> for Error {
fn from(obj: std::io::ErrorKind) -> Error {
IoError(obj)
}
}
#[cfg(test)]
mod tests {
use std::error::Error as StdError;
use std::{ffi::CString, io};
use super::*;
#[test]
fn test_error_invalid_utf8() {
let bytes: [u8; 8] = [0x78, 0xfe, 0xe9, 0x89, 0x00, 0x00, 0xed, 0x4f];
let error = unsafe { Error::new(&bytes as *const _ as _) };
assert!(matches!(error, Error::MalformedError(_)));
}
#[test]
fn test_error_null() {
let error = unsafe { Error::new(std::ptr::null()) };
assert_eq!(error, Error::PcapError("".to_string()));
}
#[test]
#[allow(deprecated)]
fn test_errors() {
let mut errors: Vec<Error> = vec![];
let bytes: [u8; 8] = [0x78, 0xfe, 0xe9, 0x89, 0x00, 0x00, 0xed, 0x4f];
let cstr = unsafe { CStr::from_ptr(&bytes as *const _ as _) };
errors.push(cstr.to_str().unwrap_err().into());
errors.push(Error::InvalidString);
errors.push(Error::PcapError("git rekt".to_string()));
errors.push(Error::InvalidLinktype);
errors.push(Error::TimeoutExpired);
errors.push(Error::NoMorePackets);
errors.push(Error::NonNonBlock);
errors.push(Error::InsufficientMemory);
errors.push(CString::new(b"f\0oo".to_vec()).unwrap_err().into());
errors.push(io::Error::new(io::ErrorKind::Interrupted, "error").into());
#[cfg(not(windows))]
errors.push(Error::InvalidRawFd);
errors.push(Error::ErrnoError(errno::Errno(125)));
errors.push(Error::BufferOverflow);
for error in errors.iter() {
assert!(!error.to_string().is_empty());
assert!(!error.description().is_empty());
match error {
Error::MalformedError(_) => assert!(error.cause().is_some()),
_ => assert!(error.cause().is_none()),
}
}
}
}