//! pcap is a packet capture library available on Linux, Windows and Mac. This //! crate supports creating and configuring capture contexts, sniffing packets, //! sending packets to interfaces, listing devices, and recording packet captures //! to pcap-format dump files. //! //! # Capturing packets //! The easiest way to open an active capture handle and begin sniffing is to //! use `.open()` on a `Device`. You can obtain the "default" device using //! `Device::lookup()`, or you can obtain the device(s) you need via `Device::list()`. //! //! ```ignore //! use pcap::Device; //! //! fn main() { //! let mut cap = Device::lookup().unwrap().open().unwrap(); //! //! while let Ok(packet) = cap.next() { //! println!("received packet! {:?}", packet); //! } //! } //! ``` //! //! `Capture`'s `.next()` will produce a `Packet` which can be dereferenced to access the //! `&[u8]` packet contents. //! //! # Custom configuration //! //! You may want to configure the `timeout`, `snaplen` or other parameters for the capture //! handle. In this case, use `Capture::from_device()` to obtain a `Capture`, and //! proceed to configure the capture handle. When you're finished, run `.open()` on it to //! turn it into a `Capture`. //! //! ```ignore //! use pcap::{Device,Capture}; //! //! fn main() { //! let main_device = Device::lookup().unwrap(); //! let mut cap = Capture::from_device(main_device).unwrap() //! .promisc(true) //! .snaplen(5000) //! .open().unwrap(); //! //! while let Ok(packet) = cap.next() { //! println!("received packet! {:?}", packet); //! } //! } //! ``` use unique::Unique; use std::borrow::Borrow; use std::marker::PhantomData; use std::ptr; use std::ffi::{self, CString, CStr}; use std::path::Path; use std::slice; use std::ops::Deref; use std::mem; use std::fmt; #[cfg(feature = "capture-stream")] use std::io; #[cfg(not(windows))] use std::os::unix::io::{RawFd, AsRawFd}; use self::Error::*; mod raw; mod unique; #[cfg(feature = "capture-stream")] pub mod stream; /// An error received from pcap #[derive(Debug, PartialEq)] pub enum Error { MalformedError(std::str::Utf8Error), InvalidString, PcapError(String), InvalidLinktype, TimeoutExpired, NoMorePackets, NonNonBlock, InsufficientMemory, InvalidInputString, IoError(std::io::ErrorKind), #[cfg(not(windows))] InvalidRawFd, } impl Error { 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()), } } } 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"), } } } 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", } } fn cause(&self) -> Option<&dyn std::error::Error> { match *self { MalformedError(ref e) => Some(e), _ => None, } } } impl From for Error { fn from(_: ffi::NulError) -> Error { InvalidInputString } } impl From for Error { fn from(obj: std::str::Utf8Error) -> Error { MalformedError(obj) } } impl From for Error { fn from(obj: std::io::Error) -> Error { IoError(obj.kind()) } } impl From for Error { fn from(obj: std::io::ErrorKind) -> Error { IoError(obj) } } #[derive(Debug, Clone)] /// A network device name and (potentially) pcap's description of it. pub struct Device { pub name: String, pub desc: Option, } impl Device { fn new(name: String, desc: Option) -> Device { Device { name, desc } } /// Opens a `Capture` on this device. pub fn open(self) -> Result, Error> { Capture::from_device(self)?.open() } /// Returns the default Device suitable for captures according to pcap_lookupdev, /// or an error from pcap. pub fn lookup() -> Result { with_errbuf(|err| unsafe { cstr_to_string(raw::pcap_lookupdev(err)) ? .map(|name| Device::new(name, None)) .ok_or_else(|| Error::new(err)) }) } /// Returns a vector of `Device`s known by pcap via pcap_findalldevs. pub fn list() -> Result, Error> { with_errbuf(|err| unsafe { let mut dev_buf: *mut raw::pcap_if_t = ptr::null_mut(); if raw::pcap_findalldevs(&mut dev_buf, err) != 0 { return Err(Error::new(err)); } let result = (|| { let mut devices = vec![]; let mut cur = dev_buf; while !cur.is_null() { let dev = &*cur; devices.push(Device::new(cstr_to_string(dev.name)?.ok_or(InvalidString)?, cstr_to_string(dev.description)?)); cur = dev.next; } Ok(devices) })(); raw::pcap_freealldevs(dev_buf); result }) } } impl<'a> Into for &'a str { fn into(self) -> Device { Device::new(self.into(), None) } } /// This is a datalink link type. /// /// As an example, `Linktype(1)` is ethernet. A full list of linktypes is available /// [here](http://www.tcpdump.org/linktypes.html). #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub struct Linktype(pub i32); impl Linktype { /// Gets the name of the link type, such as EN10MB pub fn get_name(&self) -> Result { cstr_to_string(unsafe { raw::pcap_datalink_val_to_name(self.0) }) ? .ok_or(InvalidLinktype) } /// Gets the description of a link type. pub fn get_description(&self) -> Result { cstr_to_string(unsafe { raw::pcap_datalink_val_to_description(self.0) }) ? .ok_or(InvalidLinktype) } } /// Represents a packet returned from pcap. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Packet<'a> { pub header: &'a PacketHeader, pub data: &'a [u8], } impl<'a> Packet<'a> { #[doc(hidden)] pub fn new(header: &'a PacketHeader, data: &'a [u8]) -> Packet<'a> { Packet { header, data } } } impl<'b> Deref for Packet<'b> { type Target = [u8]; fn deref(&self) -> &[u8] { self.data } } #[repr(C)] #[derive(Copy, Clone)] /// Represents a packet header provided by pcap, including the timeval, caplen and len. pub struct PacketHeader { pub ts: libc::timeval, pub caplen: u32, pub len: u32, } impl fmt::Debug for PacketHeader { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "PacketHeader {{ ts: {}.{:06}, caplen: {}, len: {} }}", self.ts.tv_sec, self.ts.tv_usec, self.caplen, self.len) } } impl PartialEq for PacketHeader { fn eq(&self, rhs: &PacketHeader) -> bool { self.ts.tv_sec == rhs.ts.tv_sec && self.ts.tv_usec == rhs.ts.tv_usec && self.caplen == rhs.caplen && self.len == rhs.len } } impl Eq for PacketHeader {} #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Stat { pub received: u32, pub dropped: u32, pub if_dropped: u32, } impl Stat { fn new(received: u32, dropped: u32, if_dropped: u32) -> Stat { Stat { received, dropped, if_dropped } } } #[repr(u32)] #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Precision { Micro = 0, Nano = 1, } /// Phantom type representing an inactive capture handle. pub enum Inactive {} /// Phantom type representing an active capture handle. 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. 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. pub enum Dead {} pub unsafe trait Activated: State {} unsafe impl Activated for Active {} unsafe impl Activated for Offline {} unsafe impl Activated for Dead {} /// `Capture`s can be in different states at different times, and in these states they /// may or may not have particular capabilities. This trait is implemented by phantom /// types which allows us to punt these invariants to the type system to avoid runtime /// errors. pub unsafe trait State {} unsafe impl State for Inactive {} unsafe impl State for Active {} unsafe impl State for Offline {} unsafe impl State for Dead {} /// This is a pcap capture handle which is an abstraction over the `pcap_t` provided by pcap. /// There are many ways to instantiate and interact with a pcap handle, so phantom types are /// used to express these behaviors. /// /// **`Capture`** is created via `Capture::from_device()`. This handle is inactive, /// so you cannot (yet) obtain packets from it. However, you can configure things like the /// buffer size, snaplen, timeout, and promiscuity before you activate it. /// /// **`Capture`** is created by calling `.open()` on a `Capture`. This /// activates the capture handle, allowing you to get packets with `.next()` or apply filters /// with `.filter()`. /// /// **`Capture`** is created via `Capture::from_file()`. This allows you to read a /// pcap format dump file as if you were opening an interface -- very useful for testing or /// analysis. /// /// **`Capture`** is created via `Capture::dead()`. This allows you to create a pcap /// format dump file without needing an active capture. /// /// # Example: /// /// ```ignore /// let cap = Capture::from_device(Device::lookup().unwrap()) // open the "default" interface /// .unwrap() // assume the device exists and we are authorized to open it /// .open() // activate the handle /// .unwrap(); // assume activation worked /// /// while let Ok(packet) = cap.next() { /// println!("received packet! {:?}", packet); /// } /// ``` pub struct Capture { nonblock: bool, handle: Unique, _marker: PhantomData, } impl Capture { fn new(handle: *mut raw::pcap_t) -> Capture { unsafe { Capture { nonblock: false, handle: Unique::new(handle), _marker: PhantomData, } } } fn new_raw(path: Option<&str>, func: F) -> Result, Error> where F: FnOnce(*const libc::c_char, *mut libc::c_char) -> *mut raw::pcap_t { with_errbuf(|err| { let handle = match path { None => func(ptr::null(), err), Some(path) => { let path = CString::new(path)?; func(path.as_ptr(), err) } }; unsafe { handle.as_mut() }.map(|h| Capture::new(h)).ok_or_else(|| Error::new(err)) }) } /// Set the minumum amount of data received by the kernel in a single call. /// /// Note that this value is set to 0 when the capture is set to immediate mode. You should not /// call `min_to_copy` on captures in immediate mode if you want them to stay in immediate mode. #[cfg(windows)] pub fn min_to_copy(self, to: i32) -> Capture { unsafe { raw::pcap_setmintocopy(*self.handle, to as _); } self } #[inline] fn check_err(&self, success: bool) -> Result<(), Error> { if success { Ok(()) } else { Err(Error::new(unsafe { raw::pcap_geterr(*self.handle) })) } } } 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 { raw::pcap_open_offline(path, err) }) } /// Opens an offline capture handle from a pcap dump file, given a path. /// Takes an additional precision argument specifying the time stamp precision desired. #[cfg(libpcap_1_5_0)] pub fn from_file_with_precision>(path: P, precision: Precision) -> Result, Error> { Capture::new_raw(path.as_ref().to_str(), |path, err| unsafe { raw::pcap_open_offline_with_tstamp_precision(path, precision as _, err) }) } /// Opens an offline capture handle from a pcap dump file, given a file descriptor. #[cfg(not(windows))] pub fn from_raw_fd(fd: RawFd) -> Result, Error> { open_raw_fd(fd, b'r') .and_then(|file| Capture::new_raw(None, |_, err| unsafe { raw::pcap_fopen_offline(file, err) })) } /// Opens an offline capture handle from a pcap dump file, given a file descriptor. /// Takes an additional precision argument specifying the time stamp precision desired. #[cfg(all(not(windows), libpcap_1_5_0))] pub fn from_raw_fd_with_precision(fd: RawFd, precision: Precision) -> Result, Error> { open_raw_fd(fd, b'r') .and_then(|file| Capture::new_raw(None, |_, err| unsafe { raw::pcap_fopen_offline_with_tstamp_precision(file, precision as _, err) })) } } #[repr(i32)] #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum TimestampType { Host = 0, HostLowPrec = 1, HostHighPrec = 2, Adapter = 3, AdapterUnsynced = 4, } #[deprecated(note = "Renamed to TimestampType")] pub type TstampType = TimestampType; #[repr(u32)] #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Direction { InOut = raw::PCAP_D_INOUT, In = raw::PCAP_D_IN, Out = raw::PCAP_D_OUT, } impl Capture { /// Opens a capture handle for a device. You can pass a `Device` or an `&str` device /// name here. The handle is inactive, but can be activated via `.open()`. /// /// # Example /// ``` /// use pcap::*; /// /// // Usage 1: Capture from a single owned device /// let dev: Device = pcap::Device::lookup().unwrap(); /// let cap1 = Capture::from_device(dev); /// /// // Usage 2: Capture from an element of device list. /// let list: Vec = pcap::Device::list().unwrap(); /// let cap2 = Capture::from_device(list[0].clone()); /// /// // Usage 3: Capture from `&str` device name /// let cap3 = Capture::from_device("eth0"); /// ``` pub fn from_device>(device: D) -> Result, Error> { let device: Device = device.into(); Capture::new_raw(Some(&device.name), |name, err| unsafe { raw::pcap_create(name, err) }) } /// Activates an inactive capture created from `Capture::from_device()` or returns /// an error. pub fn open(self) -> Result, Error> { unsafe { self.check_err(raw::pcap_activate(*self.handle) == 0)?; Ok(mem::transmute(self)) } } /// Set the read timeout for the Capture. By default, this is 0, so it will block /// indefinitely. pub fn timeout(self, ms: i32) -> Capture { unsafe { raw::pcap_set_timeout(*self.handle, ms) }; self } /// Set the time stamp type to be used by a capture device. #[cfg(libpcap_1_2_1)] pub fn tstamp_type(self, tstamp_type: TimestampType) -> Capture { unsafe { raw::pcap_set_tstamp_type(*self.handle, tstamp_type as _) }; self } /// Set promiscuous mode on or off. By default, this is off. pub fn promisc(self, to: bool) -> Capture { unsafe { raw::pcap_set_promisc(*self.handle, to as _) }; self } /// Set immediate mode on or off. By default, this is off. /// /// Note that in WinPcap immediate mode is set by passing a 0 argument to `min_to_copy`. /// Immediate mode will be unset if `min_to_copy` is later called with a non-zero argument. /// Immediate mode is unset by resetting `min_to_copy` to the WinPcap default possibly changing /// a previously set value. When using `min_to_copy`, it is best to avoid `immediate_mode`. #[cfg(any(libpcap_1_5_0, windows))] pub fn immediate_mode(self, to: bool) -> Capture { // Prior to 1.5.0 when `pcap_set_immediate_mode` was introduced, the necessary steps to set // immediate mode were more complicated, depended on the OS, and in some configurations had // to be set on an active capture. See // https://www.tcpdump.org/manpages/pcap_set_immediate_mode.3pcap.html. Since we do not // expect pre-1.5.0 version on unix systems in the wild, we simply ignore those cases. #[cfg(libpcap_1_5_0)] unsafe { raw::pcap_set_immediate_mode(*self.handle, to as _) }; // In WinPcap we use `pcap_setmintocopy` as it does not have `pcap_set_immediate_mode`. #[cfg(all(windows, not(libpcap_1_5_0)))] unsafe { raw::pcap_setmintocopy(*self.handle, if to { 0 } else { raw::WINPCAP_MINTOCOPY_DEFAULT }) }; self } /// Set rfmon mode on or off. The default is maintained by pcap. #[cfg(not(windows))] pub fn rfmon(self, to: bool) -> Capture { unsafe { raw::pcap_set_rfmon(*self.handle, to as _) }; self } /// Set the buffer size for incoming packet data. /// /// The default is 1000000. This should always be larger than the snaplen. pub fn buffer_size(self, to: i32) -> Capture { unsafe { raw::pcap_set_buffer_size(*self.handle, to) }; self } /// Set the time stamp precision returned in captures. #[cfg(libpcap_1_5_0)] pub fn precision(self, precision: Precision) -> Capture { unsafe { raw::pcap_set_tstamp_precision(*self.handle, precision as _) }; self } /// Set the snaplen size (the maximum length of a packet captured into the buffer). /// Useful if you only want certain headers, but not the entire packet. /// /// The default is 65535. pub fn snaplen(self, to: i32) -> Capture { unsafe { raw::pcap_set_snaplen(*self.handle, to) }; self } } ///# Activated captures include `Capture` and `Capture`. impl Capture { /// List the datalink types that this captured device supports. pub fn list_datalinks(&self) -> Result, Error> { unsafe { let mut links: *mut i32 = ptr::null_mut(); let num = raw::pcap_list_datalinks(*self.handle, &mut links); let mut vec = vec![]; if num > 0 { vec.extend(slice::from_raw_parts(links, num as _).iter().cloned().map(Linktype)) } raw::pcap_free_datalinks(links); self.check_err(num > 0).and(Ok(vec)) } } /// Set the datalink type for the current capture handle. pub fn set_datalink(&mut self, linktype: Linktype) -> Result<(), Error> { self.check_err(unsafe { raw::pcap_set_datalink(*self.handle, linktype.0) == 0 }) } /// Get the current datalink type for this capture handle. pub fn get_datalink(&self) -> Linktype { unsafe { Linktype(raw::pcap_datalink(*self.handle)) } } /// Create a `Savefile` context for recording captured packets using this `Capture`'s /// configurations. pub fn savefile>(&self, path: P) -> Result { let name = CString::new(path.as_ref().to_str().unwrap())?; let handle = unsafe { raw::pcap_dump_open(*self.handle, name.as_ptr()) }; self.check_err(!handle.is_null()).map(|_| Savefile::new(handle)) } /// Create a `Savefile` context for recording captured packets using this `Capture`'s /// configurations. The output is written to a raw file descriptor which is opened /// in `"w"` mode. #[cfg(not(windows))] pub fn savefile_raw_fd(&self, fd: RawFd) -> Result { open_raw_fd(fd, b'w') .and_then(|file| { let handle = unsafe { raw::pcap_dump_fopen(*self.handle, file) }; self.check_err(!handle.is_null()).map(|_| Savefile::new(handle)) }) } /// Reopen a `Savefile` context for recording captured packets using this `Capture`'s /// configurations. This is similar to `savefile()` but does not create the file if it /// does not exist and, if it does already exist, and is a pcap file with the same /// byte order as the host opening the file, and has the same time stamp precision, /// link-layer header type, and snapshot length as p, it will write new packets /// at the end of the file. #[cfg(libpcap_1_7_2)] pub fn savefile_append>(&self, path: P) -> Result { let name = CString::new(path.as_ref().to_str().unwrap())?; let handle = unsafe { raw::pcap_dump_open_append(*self.handle, name.as_ptr()) }; self.check_err(!handle.is_null()).map(|_| Savefile::new(handle)) } /// Set the direction of the capture pub fn direction(&self, direction: Direction) -> Result<(), Error> { self.check_err(unsafe { raw::pcap_setdirection(*self.handle, direction as u32 as _) == 0 }) } /// Blocks until a packet is returned from the capture handle or an error occurs. /// /// pcap captures packets and places them into a buffer which this function reads /// from. This buffer has a finite length, so if the buffer fills completely new /// packets will be discarded temporarily. This means that in realtime situations, /// you probably want to minimize the time between calls of this next() method. #[allow(clippy::should_implement_trait)] pub fn next(&mut self) -> Result { unsafe { let mut header: *mut raw::pcap_pkthdr = ptr::null_mut(); let mut packet: *const libc::c_uchar = ptr::null(); let retcode = raw::pcap_next_ex(*self.handle, &mut header, &mut packet); self.check_err(retcode != -1)?; // -1 => an error occured while reading the packet match retcode { i if i >= 1 => { // packet was read without issue Ok(Packet::new(&*(&*header as *const raw::pcap_pkthdr as *const PacketHeader), slice::from_raw_parts(packet, (*header).caplen as _))) } 0 => { // packets are being read from a live capture and the // timeout expired Err(TimeoutExpired) } -2 => { // packets are being read from a "savefile" and there are no // more packets to read Err(NoMorePackets) } _ => { // libpcap only defines codes >=1, 0, -1, and -2 unreachable!() } } } } #[cfg(feature = "capture-stream")] fn next_noblock<'a>(&'a mut self, cx: &mut core::task::Context, fd: &mut tokio::io::PollEvented) -> Result, Error> { if let futures::task::Poll::Pending = fd.poll_read_ready(cx, mio::Ready::readable()) { Err(IoError(io::ErrorKind::WouldBlock)) } else { match self.next() { Ok(p) => Ok(p), Err(TimeoutExpired) => { fd.clear_read_ready(cx, mio::Ready::readable())?; Err(IoError(io::ErrorKind::WouldBlock)) } Err(e) => Err(e) } } } #[cfg(feature = "capture-stream")] pub fn stream(self, codec: C) -> Result, Error> { if !self.nonblock { return Err(NonNonBlock); } unsafe { let fd = raw::pcap_get_selectable_fd(*self.handle); stream::PacketStream::new(self, fd, codec) } } /// Adds a filter to the capture using the given BPF program string. Internally /// this is compiled using `pcap_compile()`. /// /// See http://biot.com/capstats/bpf.html for more information about this syntax. pub fn filter(&mut self, program: &str) -> Result<(), Error> { let program = CString::new(program)?; unsafe { let mut bpf_program: raw::bpf_program = mem::zeroed(); let ret = raw::pcap_compile(*self.handle, &mut bpf_program, program.as_ptr(), 0, 0); self.check_err(ret != -1)?; let ret = raw::pcap_setfilter(*self.handle, &mut bpf_program); raw::pcap_freecode(&mut bpf_program); self.check_err(ret != -1) } } pub fn stats(&mut self) -> Result { unsafe { let mut stats: raw::pcap_stat = mem::zeroed(); self.check_err(raw::pcap_stats(*self.handle, &mut stats) != -1) .map(|_| Stat::new(stats.ps_recv, stats.ps_drop, stats.ps_ifdrop)) } } } impl Capture { /// Sends a packet over this capture handle's interface. pub fn sendpacket>(&mut self, buf: B) -> Result<(), Error> { let buf = buf.borrow(); self.check_err(unsafe { raw::pcap_sendpacket(*self.handle, buf.as_ptr() as _, buf.len() as _) == 0 }) } pub fn setnonblock(mut self) -> Result, Error> { with_errbuf(|err| unsafe { if raw::pcap_setnonblock(*self.handle, 1, err) != 0 { return Err(Error::new(err)); } self.nonblock = true; Ok(self) }) } } impl Capture { /// Creates a "fake" capture handle for the given link type. pub fn dead(linktype: Linktype) -> Result, Error> { unsafe { raw::pcap_open_dead(linktype.0, 65535).as_mut() } .map(|h| Capture::new(h)) .ok_or(InsufficientMemory) } } #[cfg(not(windows))] impl AsRawFd for Capture { fn as_raw_fd(&self) -> RawFd { unsafe { let fd = raw::pcap_fileno(*self.handle); match fd { -1 => { panic!("Unable to get file descriptor for live capture"); } fd => fd, } } } } impl Drop for Capture { fn drop(&mut self) { unsafe { raw::pcap_close(*self.handle) } } } impl From> for Capture { fn from(cap: Capture) -> Capture { unsafe { mem::transmute(cap) } } } /// Abstraction for writing pcap savefiles, which can be read afterwards via `Capture::from_file()`. pub struct Savefile { handle: Unique, } impl Savefile { pub fn write(&mut self, packet: &Packet) { unsafe { raw::pcap_dump(*self.handle as _, &*(packet.header as *const PacketHeader as *const raw::pcap_pkthdr), packet.data.as_ptr()); } } } impl Savefile { fn new(handle: *mut raw::pcap_dumper_t) -> Savefile { unsafe { Savefile { handle: Unique::new(handle) } } } } impl Drop for Savefile { fn drop(&mut self) { unsafe { raw::pcap_dump_close(*self.handle) } } } #[cfg(not(windows))] pub fn open_raw_fd(fd: RawFd, mode: u8) -> Result<*mut libc::FILE, Error> { let mode = vec![mode, 0]; unsafe { libc::fdopen(fd, mode.as_ptr() as _).as_mut() }.map(|f| f as _).ok_or(InvalidRawFd) } #[inline] fn cstr_to_string(ptr: *const libc::c_char) -> Result, Error> { let string = if ptr.is_null() { None } else { Some(unsafe { CStr::from_ptr(ptr as _) }.to_str()?.to_owned()) }; Ok(string) } #[inline] fn with_errbuf(func: F) -> Result where F: FnOnce(*mut libc::c_char) -> Result { let mut errbuf = [0i8; 256]; func(errbuf.as_mut_ptr() as _) } #[test] fn test_struct_size() { use std::mem::size_of; assert_eq!(size_of::(), size_of::()); }