diff --git a/src/capture/activated/active.rs b/src/capture/activated/active.rs index 7bfa4131..0790fe7f 100644 --- a/src/capture/activated/active.rs +++ b/src/capture/activated/active.rs @@ -84,7 +84,7 @@ mod tests { .return_once(|_, _, _| 0); let result = capture.sendpacket(buffer); - assert!(result.is_ok()); + result.unwrap(); let ctx = pcap_sendpacket_context(); ctx.checkpoint(); @@ -95,7 +95,7 @@ mod tests { let _err = geterr_expect(pcap); let result = capture.sendpacket(buffer); - assert!(result.is_err()); + result.unwrap_err(); } #[test] @@ -135,7 +135,7 @@ mod tests { .return_once(|_, _, _| -1); let result = capture.setnonblock(); - assert!(result.is_err()); + result.unwrap_err(); } #[test] diff --git a/src/capture/activated/dead.rs b/src/capture/activated/dead.rs index 5efbbf06..53bbebca 100644 --- a/src/capture/activated/dead.rs +++ b/src/capture/activated/dead.rs @@ -58,7 +58,7 @@ mod tests { .return_once(|_| {}); let result = Capture::dead(Linktype::ETHERNET); - assert!(result.is_ok()); + result.unwrap(); } #[test] @@ -80,6 +80,6 @@ mod tests { .return_once(|_| {}); let result = Capture::dead_with_precision(Linktype::ETHERNET, Precision::Nano); - assert!(result.is_ok()); + result.unwrap(); } } diff --git a/src/capture/activated/mod.rs b/src/capture/activated/mod.rs index 9375a973..516289da 100644 --- a/src/capture/activated/mod.rs +++ b/src/capture/activated/mod.rs @@ -323,6 +323,7 @@ impl From> for Capture { } /// Abstraction for writing pcap savefiles, which can be read afterwards via `Capture::from_file()`. +#[derive(Debug)] pub struct Savefile { handle: NonNull, } @@ -369,6 +370,7 @@ impl Drop for Savefile { #[repr(transparent)] pub struct BpfInstruction(raw::bpf_insn); #[repr(transparent)] +#[derive(Debug)] pub struct BpfProgram(raw::bpf_program); impl BpfProgram { @@ -513,7 +515,7 @@ mod tests { let _err = geterr_expect(pcap); let result = capture.list_datalinks(); - assert!(result.is_err()); + result.unwrap_err(); let mut datalinks: [i32; 4] = [0, 1, 2, 3]; let links: *mut i32 = datalinks.as_mut_ptr(); @@ -555,7 +557,7 @@ mod tests { .return_once(|_, _| 0); let result = capture.set_datalink(Linktype::ETHERNET); - assert!(result.is_ok()); + result.unwrap(); let ctx = raw::pcap_set_datalink_context(); ctx.checkpoint(); @@ -566,7 +568,7 @@ mod tests { let _err = geterr_expect(pcap); let result = capture.set_datalink(Linktype::ETHERNET); - assert!(result.is_err()); + result.unwrap_err(); } #[test] @@ -637,7 +639,7 @@ mod tests { .return_once(|_| {}); let result = capture.savefile("path/to/nowhere"); - assert!(result.is_ok()); + result.unwrap(); } #[test] @@ -665,7 +667,7 @@ mod tests { .return_once(|_| {}); let result = capture.savefile_append("path/to/nowhere"); - assert!(result.is_ok()); + result.unwrap(); } #[test] @@ -686,7 +688,7 @@ mod tests { let _err = geterr_expect(pcap); let result = capture.savefile("path/to/nowhere"); - assert!(result.is_err()); + result.unwrap_err(); } #[test] @@ -708,7 +710,7 @@ mod tests { let _err = geterr_expect(pcap); let result = capture.savefile_append("path/to/nowhere"); - assert!(result.is_err()); + result.unwrap_err(); } #[test] @@ -740,7 +742,7 @@ mod tests { .return_once(|_| 0); let result = savefile.flush(); - assert!(result.is_ok()); + result.unwrap(); let ctx = raw::pcap_dump_flush_context(); ctx.checkpoint(); @@ -749,7 +751,7 @@ mod tests { .return_once(|_| -1); let result = savefile.flush(); - assert!(result.is_err()); + result.unwrap_err(); } #[test] @@ -768,7 +770,7 @@ mod tests { .return_once(|_, _| 0); let result = capture.direction(Direction::Out); - assert!(result.is_ok()); + result.unwrap(); let ctx = raw::pcap_setdirection_context(); ctx.checkpoint(); @@ -779,7 +781,7 @@ mod tests { let _err = geterr_expect(pcap); let result = capture.direction(Direction::Out); - assert!(result.is_err()); + result.unwrap_err(); // For code coverage of the derive line. assert_ne!(Direction::In, Direction::InOut); @@ -840,7 +842,7 @@ mod tests { let _err = geterr_expect(pcap); let result = capture.next_packet(); - assert!(result.is_err()); + result.unwrap_err(); } #[test] @@ -883,7 +885,7 @@ mod tests { ctx.expect().return_once(|_| {}); let result = capture.compile("some bpf program", false); - assert!(result.is_err()); + result.unwrap_err(); let ctx = raw::pcap_compile_context(); ctx.checkpoint(); @@ -896,7 +898,7 @@ mod tests { ctx.expect().return_once(|_| {}); let result = capture.compile("some bpf program", false); - assert!(result.is_ok()); + result.unwrap(); } #[test] @@ -925,7 +927,7 @@ mod tests { ctx.expect().return_once(|_| {}); let result = capture.filter("some bpf program", false); - assert!(result.is_err()); + result.unwrap_err(); let ctx = raw::pcap_compile_context(); ctx.checkpoint(); @@ -944,7 +946,7 @@ mod tests { ctx.expect().return_once(|_| {}); let result = capture.compile("some bpf program", false); - assert!(result.is_ok()); + result.unwrap(); } #[test] @@ -983,7 +985,7 @@ mod tests { let _err = geterr_expect(pcap); let result = capture.stats(); - assert!(result.is_err()); + result.unwrap_err(); } #[test] diff --git a/src/capture/activated/offline.rs b/src/capture/activated/offline.rs index 950d3894..e2661b07 100644 --- a/src/capture/activated/offline.rs +++ b/src/capture/activated/offline.rs @@ -1,23 +1,20 @@ -use std::path::Path; - #[cfg(not(windows))] use std::os::unix::io::RawFd; +use std::path::Path; +#[cfg(not(windows))] +use crate::capture::activated::open_raw_fd; +#[cfg(libpcap_1_5_0)] +use crate::capture::Precision; use crate::{ capture::{Capture, Offline}, raw, Error, }; -#[cfg(libpcap_1_5_0)] -use crate::capture::Precision; - -#[cfg(not(windows))] -use crate::capture::activated::open_raw_fd; - impl Capture { /// Opens an offline capture handle from a pcap dump file, given a path. pub fn from_file>(path: P) -> Result, Error> { - Capture::new_raw(path.as_ref().to_str(), |path, err| unsafe { + Capture::new_raw_with_path(path.as_ref(), |path, err| unsafe { raw::pcap_open_offline(path, err) }) } @@ -29,7 +26,7 @@ impl Capture { path: P, precision: Precision, ) -> Result, Error> { - Capture::new_raw(path.as_ref().to_str(), |path, err| unsafe { + Capture::new_raw_with_path(path.as_ref(), |path, err| unsafe { raw::pcap_open_offline_with_tstamp_precision(path, precision as _, err) }) } @@ -42,7 +39,7 @@ impl Capture { #[cfg(not(windows))] pub unsafe fn from_raw_fd(fd: RawFd) -> Result, Error> { open_raw_fd(fd, b'r') - .and_then(|file| Capture::new_raw(None, |_, err| raw::pcap_fopen_offline(file, err))) + .and_then(|file| Capture::new_raw(|err| raw::pcap_fopen_offline(file, err))) } /// Opens an offline capture handle from a pcap dump file, given a file descriptor. Takes an @@ -57,7 +54,7 @@ impl Capture { precision: Precision, ) -> Result, Error> { open_raw_fd(fd, b'r').and_then(|file| { - Capture::new_raw(None, |_, err| { + Capture::new_raw(|err| { raw::pcap_fopen_offline_with_tstamp_precision(file, precision as _, err) }) }) @@ -84,13 +81,12 @@ mod tests { #[cfg(libpcap_1_5_0)] use mockall::predicate; + use super::*; use crate::{ capture::testmod::test_capture, raw::testmod::{as_pcap_t, RAWMTX}, }; - use super::*; - #[test] fn test_from_file() { let _m = RAWMTX.lock(); @@ -107,7 +103,7 @@ mod tests { .return_once(|_| {}); let result = Capture::from_file("path/to/nowhere"); - assert!(result.is_ok()); + result.unwrap(); } #[test] @@ -129,7 +125,7 @@ mod tests { .return_once(|_| {}); let result = Capture::from_file_with_precision("path/to/nowhere", Precision::Nano); - assert!(result.is_ok()); + result.unwrap(); } #[test] diff --git a/src/capture/inactive.rs b/src/capture/inactive.rs index e2a94cae..d79bda42 100644 --- a/src/capture/inactive.rs +++ b/src/capture/inactive.rs @@ -1,4 +1,4 @@ -use std::mem; +use std::{mem, path::Path}; use crate::{ capture::{Active, Capture, Inactive}, @@ -32,7 +32,7 @@ impl Capture { /// ``` pub fn from_device>(device: D) -> Result, Error> { let device: Device = device.into(); - Capture::new_raw(Some(&device.name), |name, err| unsafe { + Capture::new_raw_with_path(Path::new(&device.name), |name, err| unsafe { raw::pcap_create(name, err) }) } @@ -206,7 +206,7 @@ mod tests { .return_once(|_| {}); let result = Capture::from_device("some_device"); - assert!(result.is_ok()); + result.unwrap(); } #[test] @@ -217,7 +217,7 @@ mod tests { ctx.expect().return_once_st(|_, _| std::ptr::null_mut()); let result = Capture::from_device("some_device"); - assert!(result.is_err()); + result.unwrap_err(); } #[test] @@ -236,7 +236,7 @@ mod tests { .return_once(|_| 0); let result = capture.open(); - assert!(result.is_ok()); + result.unwrap(); } #[test] @@ -257,7 +257,7 @@ mod tests { let _err = geterr_expect(pcap); let result = capture.open(); - assert!(result.is_err()); + result.unwrap_err(); } #[test] diff --git a/src/capture/mod.rs b/src/capture/mod.rs index 83f50566..26b0296f 100644 --- a/src/capture/mod.rs +++ b/src/capture/mod.rs @@ -4,11 +4,7 @@ pub mod inactive; #[cfg_attr(docsrs, doc(cfg(all(not(windows), feature = "capture-stream"))))] pub mod selectable; -use std::{ - ffi::CString, - marker::PhantomData, - ptr::{self, NonNull}, -}; +use std::{marker::PhantomData, path::Path, ptr::NonNull}; #[cfg(windows)] use windows_sys::Win32::Foundation::HANDLE; @@ -16,18 +12,22 @@ use windows_sys::Win32::Foundation::HANDLE; use crate::{raw, Error}; /// Phantom type representing an inactive capture handle. +#[derive(Debug)] pub enum Inactive {} /// Phantom type representing an active capture handle. +#[derive(Debug)] pub enum Active {} /// Phantom type representing an offline capture handle, from a pcap dump file. /// Implements `Activated` because it behaves nearly the same as a live handle. +#[derive(Debug)] pub enum Offline {} /// Phantom type representing a dead capture handle. This can be use to create /// new save files that are not generated from an active capture. /// Implements `Activated` because it behaves nearly the same as a live handle. +#[derive(Debug)] pub enum Dead {} /// `Capture`s can be in different states at different times, and in these states they @@ -88,6 +88,7 @@ impl State for Dead {} /// println!("received packet! {:?}", packet); /// } /// ``` +#[derive(Debug)] pub struct Capture { nonblock: bool, handle: NonNull, @@ -110,18 +111,42 @@ impl From> for Capture { } impl Capture { - fn new_raw(path: Option<&str>, func: F) -> Result, Error> + fn new_raw_with_path(path: &Path, func: F) -> Result, Error> where F: FnOnce(*const libc::c_char, *mut libc::c_char) -> *mut raw::pcap_t, { Error::with_errbuf(|err| { - let handle = match path { - None => func(ptr::null(), err), - Some(path) => { - let path = CString::new(path)?; - func(path.as_ptr(), err) - } - }; + #[cfg(unix)] + use std::os::unix::ffi::OsStrExt; + #[cfg(unix)] + let path = crate::bytes_to_cstr(path.as_os_str().as_bytes())?; + + #[cfg(windows)] + use std::os::windows::ffi::OsStrExt; + #[cfg(windows)] + let path: Vec = path + .as_os_str() + .encode_wide() + .chain(Some(0)) + .flat_map(u16::to_ne_bytes) + .collect(); + #[cfg(windows)] + let path: Vec = unsafe { std::mem::transmute(path) }; + + let handle = func(path.as_ptr(), err); + Ok(Capture::from( + NonNull::::new(handle).ok_or_else(|| unsafe { Error::new(err) })?, + )) + }) + } + + #[cfg(not(windows))] + fn new_raw(func: F) -> Result, Error> + where + F: FnOnce(*mut libc::c_char) -> *mut raw::pcap_t, + { + Error::with_errbuf(|err| { + let handle = func(err); Ok(Capture::from( NonNull::::new(handle).ok_or_else(|| unsafe { Error::new(err) })?, )) @@ -229,13 +254,12 @@ pub mod testmod { #[cfg(test)] mod tests { + use super::*; use crate::{ capture::testmod::test_capture, raw::testmod::{as_pcap_t, RAWMTX}, }; - use super::*; - #[test] fn test_capture_getters() { let _m = RAWMTX.lock(); diff --git a/src/capture/selectable.rs b/src/capture/selectable.rs index 55b0fe9d..a652c216 100644 --- a/src/capture/selectable.rs +++ b/src/capture/selectable.rs @@ -6,6 +6,7 @@ use crate::{ }; /// Newtype [`Capture`] wrapper that exposes `pcap_get_selectable_fd()`. +#[derive(Debug)] pub struct SelectableCapture { inner: Capture, fd: RawFd, @@ -76,6 +77,6 @@ mod tests { .return_once(|_| -1); let result = SelectableCapture::new(capture); - assert!(result.is_err()); + result.unwrap_err(); } } diff --git a/src/codec.rs b/src/codec.rs index c2acabba..b7913d99 100644 --- a/src/codec.rs +++ b/src/codec.rs @@ -18,6 +18,7 @@ pub mod testmod { use super::*; + #[derive(Debug)] pub struct Codec; #[derive(Debug, PartialEq, Eq)] diff --git a/src/device.rs b/src/device.rs index 561b11fd..72b883b9 100644 --- a/src/device.rs +++ b/src/device.rs @@ -570,7 +570,7 @@ mod tests { ctx.checkpoint(); let result = Device::lookup(); - assert!(result.is_err()); + result.unwrap_err(); } #[test] @@ -630,7 +630,7 @@ mod tests { ctx.checkpoint(); let result = Device::list(); - assert!(result.is_err()); + result.unwrap_err(); } #[test] diff --git a/src/lib.rs b/src/lib.rs index 0a283ff1..70805048 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,6 @@ //! while let Ok(packet) = cap.next_packet() { //! println!("received packet! {:?}", packet); //! } -//! //! ``` //! //! `Capture`'s `.next_packet()` will produce a `Packet` which can be dereferenced to access the @@ -30,13 +29,18 @@ //! turn it into a `Capture`. //! //! ```no_run -//! use pcap::{Device, Capture}; +//! use pcap::{ +//! Capture, +//! Device, +//! }; //! //! let main_device = Device::lookup().unwrap().unwrap(); -//! let mut cap = Capture::from_device(main_device).unwrap() -//! .promisc(true) -//! .snaplen(5000) -//! .open().unwrap(); +//! let mut cap = Capture::from_device(main_device) +//! .unwrap() +//! .promisc(true) +//! .snaplen(5000) +//! .open() +//! .unwrap(); //! //! while let Ok(packet) = cap.next_packet() { //! println!("received packet! {:?}", packet); @@ -49,7 +53,10 @@ //! (`Capture`) using generics and the [`Activated`] trait, for example: //! //! ``` -//! use pcap::{Activated, Capture}; +//! use pcap::{ +//! Activated, +//! Capture, +//! }; //! //! fn read_packets(mut capture: Capture) { //! while let Ok(packet) = capture.next_packet() { @@ -60,8 +67,11 @@ #![cfg_attr(docsrs, feature(doc_cfg))] -use std::ffi::{self, CStr}; -use std::fmt; +use std::{ + borrow::Cow, + ffi::{self, CStr, CString}, + fmt, +}; use self::Error::*; @@ -76,7 +86,7 @@ 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}, + Activated, Active, Capture, Dead, Inactive, Offline, Precision, State, }; pub use codec::PacketCodec; pub use device::{Address, ConnectionStatus, Device, DeviceFlags, IfFlags}; @@ -157,6 +167,23 @@ unsafe fn cstr_to_string(ptr: *const libc::c_char) -> Result, Err Ok(string) } +// for 1.69.0 +// pub(crate) fn bytes_to_cstr(bytes: &[u8]) -> Cow { +// match CStr::from_bytes_until_nul(&bytes) { +// Ok(cstr) => Cow::Borrowed(cstr), +// Err(_) => Cow::Owned(CString::new(bytes).unwrap()), +// } +// } + +#[allow(dead_code)] +pub(crate) fn bytes_to_cstr(bytes: &[u8]) -> Result, Error> { + if let Some(nul) = bytes.iter().position(|&b| b == 0) { + Ok(Cow::Borrowed(CStr::from_bytes_with_nul(&bytes[..=nul])?)) + } else { + Ok(Cow::Owned(CString::new(bytes)?)) + } +} + impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { @@ -213,6 +240,18 @@ impl From for Error { } } +impl From for Error { + fn from(_: ffi::FromBytesWithNulError) -> Error { + InvalidInputString + } +} + +impl From for Error { + fn from(_: ffi::FromVecWithNulError) -> Error { + InvalidInputString + } +} + impl From for Error { fn from(obj: std::str::Utf8Error) -> Error { MalformedError(obj) @@ -241,14 +280,13 @@ pub const fn packet_header_size() -> usize { #[cfg(test)] mod tests { - use std::error::Error as StdError; - use std::{ffi::CString, io}; + use std::{error::Error as StdError, ffi::CString, io}; use super::*; #[test] fn test_error_invalid_utf8() { - let bytes: [u8; 8] = [0x78, 0xfe, 0xe9, 0x89, 0x00, 0x00, 0xed, 0x4f]; + 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(_))); } @@ -264,7 +302,7 @@ mod tests { fn test_errors() { let mut errors: Vec = vec![]; - let bytes: [u8; 8] = [0x78, 0xfe, 0xe9, 0x89, 0x00, 0x00, 0xed, 0x4f]; + 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()); @@ -299,4 +337,23 @@ mod tests { std::mem::size_of::() ); } + + #[test] + fn test_bytes_to_cstr() { + let bytes_with_nul = b"hello world\0"; + let cstr = bytes_to_cstr(bytes_with_nul).unwrap(); + assert_eq!(cstr.to_bytes_with_nul(), bytes_with_nul); + + let bytes = b"hello world"; + let cstr = bytes_to_cstr(bytes).unwrap(); + assert_eq!(cstr.to_bytes_with_nul(), bytes_with_nul); + + let bytes = b"hello\0world"; + let cstr = bytes_to_cstr(bytes).unwrap(); + assert_eq!(cstr.to_bytes_with_nul(), b"hello\0"); + + let bytes = b""; + let cstr = bytes_to_cstr(bytes).unwrap(); + assert_eq!(cstr.to_bytes_with_nul(), b"\0"); + } } diff --git a/src/raw.rs b/src/raw.rs index 0e180368..7c359dde 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -18,7 +18,7 @@ pub const PCAP_IF_CONNECTION_STATUS_DISCONNECTED: u32 = 0x00000020; pub const PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE: u32 = 0x00000030; #[repr(C)] -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub struct bpf_program { pub bf_len: c_uint, pub bf_insns: *mut bpf_insn, diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 2488baac..98b37097 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -49,6 +49,6 @@ mod tests { assert!(!capture.is_nonblock()); let result = capture.stream(Codec); - assert!(result.is_err()); + result.unwrap_err(); } } diff --git a/src/stream/unix.rs b/src/stream/unix.rs index 538ae9de..f423421e 100644 --- a/src/stream/unix.rs +++ b/src/stream/unix.rs @@ -16,6 +16,7 @@ use crate::{ }; /// Implement Stream for async use of pcap +#[derive(Debug)] pub struct PacketStream { inner: AsyncFd>, codec: C, diff --git a/src/stream/windows.rs b/src/stream/windows.rs index 11656c8f..2e905991 100644 --- a/src/stream/windows.rs +++ b/src/stream/windows.rs @@ -16,6 +16,7 @@ use crate::{ }; /// Implement Stream for async use of pcap +#[derive(Debug)] pub struct PacketStream { event_handle: EventHandle, capture: Capture, @@ -68,11 +69,13 @@ impl futures::Stream for PacketStream