#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![cfg_attr(feature = "clippy", allow(redundant_closure_call))]
#[cfg(feature = "tokio")]
extern crate futures;
extern crate libc;
extern crate libloading;
#[cfg(feature = "tokio")]
extern crate mio;
#[cfg(feature = "tokio")]
extern crate tokio_core;
use unique::Unique;
use std::borrow::Borrow;
use std::ffi::{self, CStr, CString};
use std::fmt;
#[cfg(feature = "tokio")]
use std::io;
use std::marker::PhantomData;
use std::mem;
use std::ops::Deref;
#[cfg(not(windows))]
use std::os::unix::io::{AsRawFd, RawFd};
use std::path::Path;
use std::ptr;
use std::slice;
use self::Error::*;
use libloading::{Library, Symbol};
mod raw;
#[cfg(feature = "tokio")]
pub mod tokio;
mod unique;
#[cfg(windows)]
fn load_dll() -> Result<libloading::Library, Box<dyn std::error::Error>> {
match libloading::Library::new("wpcap.dll") {
Ok(lib) => Ok(lib),
Err(err) => Err(Box::new(err)),
}
}
#[cfg(target_os = "macos")]
fn load_dll() -> Result<libloading::Library, Box<dyn std::error::Error>> {
match libloading::Library::new("libpcap.dylib") {
Ok(lib) => Ok(lib),
Err(err) => Err(Box::new(err)),
}
}
#[cfg(not(any(windows,target_os = "macos")))]
fn load_dll() -> Result<libloading::Library, Box<dyn std::error::Error>> {
match libloading::Library::new("libpcap.so") {
Ok(lib) => Ok(lib),
Err(_err) => match libloading::Library::new("libpcap.so.1") {
Ok(lib) => Ok(lib),
Err(_err) => match libloading::Library::new("/usr/lib/x86_64-linux-gnu/libpcap.so") {
Ok(lib) => Ok(lib),
Err(err) => Err(Box::new(err)),
},
},
}
}
pub unsafe fn load_pcap_library() -> Result<(), Box<dyn std::error::Error>> {
let dll = load_dll()?;
let on_heap = Box::new(dll);
let leaked_dll: &'static mut libloading::Library = Box::leak(on_heap);
raw::pcap_create_symbol = Some(*leaked_dll.get(b"pcap_create")?);
raw::pcap_lookupdev_symbol = Some(*leaked_dll.get(b"pcap_lookupdev")?);
raw::pcap_set_snaplen_symbol = Some(*leaked_dll.get(b"pcap_set_snaplen")?);
raw::pcap_set_promisc_symbol = Some(*leaked_dll.get(b"pcap_set_promisc")?);
raw::pcap_set_timeout_symbol = Some(*leaked_dll.get(b"pcap_set_timeout")?);
raw::pcap_set_buffer_size_symbol = Some(*leaked_dll.get(b"pcap_set_buffer_size")?);
raw::pcap_activate_symbol = Some(*leaked_dll.get(b"pcap_activate")?);
raw::pcap_open_dead_symbol = Some(*leaked_dll.get(b"pcap_open_dead")?);
raw::pcap_open_offline_symbol = Some(*leaked_dll.get(b"pcap_open_offline")?);
raw::pcap_close_symbol = Some(*leaked_dll.get(b"pcap_close")?);
raw::pcap_next_ex_symbol = Some(*leaked_dll.get(b"pcap_next_ex")?);
raw::pcap_stats_symbol = Some(*leaked_dll.get(b"pcap_stats")?);
raw::pcap_setfilter_symbol = Some(*leaked_dll.get(b"pcap_setfilter")?);
raw::pcap_setdirection_symbol = Some(*leaked_dll.get(b"pcap_setdirection")?);
raw::pcap_setnonblock_symbol = Some(*leaked_dll.get(b"pcap_setnonblock")?);
raw::pcap_sendpacket_symbol = Some(*leaked_dll.get(b"pcap_sendpacket")?);
raw::pcap_geterr_symbol = Some(*leaked_dll.get(b"pcap_geterr")?);
raw::pcap_compile_symbol = Some(*leaked_dll.get(b"pcap_compile")?);
raw::pcap_freecode_symbol = Some(*leaked_dll.get(b"pcap_freecode")?);
raw::pcap_datalink_symbol = Some(*leaked_dll.get(b"pcap_datalink")?);
raw::pcap_list_datalinks_symbol = Some(*leaked_dll.get(b"pcap_list_datalinks")?);
raw::pcap_set_datalink_symbol = Some(*leaked_dll.get(b"pcap_set_datalink")?);
raw::pcap_free_datalinks_symbol = Some(*leaked_dll.get(b"pcap_free_datalinks")?);
raw::pcap_datalink_val_to_name_symbol = Some(*leaked_dll.get(b"pcap_datalink_val_to_name")?);
raw::pcap_datalink_val_to_description_symbol = Some(*leaked_dll.get(b"pcap_datalink_val_to_description")?);
raw::pcap_fileno_symbol = Some(*leaked_dll.get(b"pcap_fileno")?);
raw::pcap_dump_open_symbol = Some(*leaked_dll.get(b"pcap_dump_open")?);
raw::pcap_dump_close_symbol = Some(*leaked_dll.get(b"pcap_dump_close")?);
raw::pcap_dump_symbol = Some(*leaked_dll.get(b"pcap_dump")?);
raw::pcap_findalldevs_symbol = Some(*leaked_dll.get(b"pcap_findalldevs")?);
raw::pcap_freealldevs_symbol = Some(*leaked_dll.get(b"pcap_freealldevs")?);
load_fopen_offline_precision_feature(leaked_dll);
load_pcap_savefile_append_feature(leaked_dll);
load_non_windows(leaked_dll);
Ok(())
}
#[cfg(not(windows))]
fn load_non_windows(leaked_dll: &mut Library) -> Result<(), Box<dyn std::error::Error>> {
unsafe {
raw::pcap_set_tstamp_type_symbol = Some(*leaked_dll.get(b"pcap_set_tstamp_type")?);
raw::pcap_set_tstamp_precision_symbol = Some(*leaked_dll.get(b"pcap_set_tstamp_precision")?);
raw::pcap_set_rfmon_symbol = Some(*leaked_dll.get(b"pcap_set_rfmon")?);
raw::pcap_fopen_offline_symbol = Some(*leaked_dll.get(b"pcap_fopen_offline")?);
raw::pcap_dump_fopen_symbol = Some(*leaked_dll.get(b"pcap_dump_fopen")?);
}
Ok(())
}
#[cfg(windows)]
fn load_non_windows(leaked_dll: &mut Library) {}
#[cfg(feature = "pcap-fopen-offline-precision")]
fn load_fopen_offline_precision_feature(leaked_dll: &mut Library) -> Result<(), Box<dyn std::error::Error>> {
unsafe {
raw::pcap_fopen_offline_with_tstamp_precision_symbol =
Some(*leaked_dll.get(b"pcap_fopen_offline_with_tstamp_precision")?);
raw::pcap_open_dead_with_tstamp_precision_symbol =
Some(*leaked_dll.get(b"pcap_open_dead_with_tstamp_precision")?);
raw::pcap_open_offline_with_tstamp_precision_symbol =
Some(*leaked_dll.get(b"pcap_open_offline_with_tstamp_precision")?);
}
Ok(())
}
#[cfg(not(feature = "pcap-fopen-offline-precision"))]
fn load_fopen_offline_precision_feature(leaked_dll: &mut Library) {}
#[cfg(feature = "pcap-savefile-append")]
fn load_pcap_savefile_append_feature(leaked_dll: &mut Library) -> Result<(), Box<dyn std::error::Error>> {
unsafe {
raw::pcap_dump_open_append_symbol = Some(*leaked_dll.get(b"pcap_dump_open_append")?);
}
Ok(())
}
#[cfg(not(feature = "pcap-savefile-append"))]
fn load_pcap_savefile_append_feature(leaked_dll: &mut Library) {}
#[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<&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 {
IoError(obj.kind())
}
}
impl From<std::io::ErrorKind> for Error {
fn from(obj: std::io::ErrorKind) -> Error {
IoError(obj)
}
}
#[derive(Debug)]
pub struct Device {
pub name: String,
pub desc: Option<String>,
}
impl Device {
fn new(name: String, desc: Option<String>) -> Device {
Device { name, desc }
}
pub fn open(self) -> Result<Capture<Active>, Error> {
Capture::from_device(self)?.open()
}
pub fn lookup() -> Result<Device, Error> {
with_errbuf(|err| unsafe {
cstr_to_string(raw::pcap_lookupdev_symbol.unwrap()(err))?
.map(|name| Device::new(name, None))
.ok_or_else(|| Error::new(err))
})
}
pub fn list() -> Result<Vec<Device>, Error> {
with_errbuf(|err| unsafe {
let mut dev_buf: *mut raw::pcap_if_t = ptr::null_mut();
if raw::pcap_findalldevs_symbol.unwrap()(&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_symbol.unwrap()(dev_buf);
result
})
}
}
impl<'a> Into<Device> for &'a str {
fn into(self) -> Device {
Device::new(self.into(), None)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct Linktype(pub i32);
impl Linktype {
pub fn get_name(&self) -> Result<String, Error> {
cstr_to_string(unsafe { raw::pcap_datalink_val_to_name_symbol.unwrap()(self.0) })?.ok_or(InvalidLinktype)
}
pub fn get_description(&self) -> Result<String, Error> {
cstr_to_string(unsafe { raw::pcap_datalink_val_to_description_symbol.unwrap()(self.0) })?.ok_or(InvalidLinktype)
}
}
#[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)]
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,
}
pub enum Inactive {}
pub enum Active {}
pub enum Offline {}
pub enum Dead {}
pub unsafe trait Activated: State {}
unsafe impl Activated for Active {}
unsafe impl Activated for Offline {}
unsafe impl Activated for Dead {}
pub unsafe trait State {}
unsafe impl State for Inactive {}
unsafe impl State for Active {}
unsafe impl State for Offline {}
unsafe impl State for Dead {}
pub struct Capture<T: State + ?Sized> {
nonblock: bool,
handle: Unique<raw::pcap_t>,
_marker: PhantomData<T>,
}
impl<T: State + ?Sized> Capture<T> {
fn new(handle: *mut raw::pcap_t) -> Capture<T> {
unsafe { Capture { nonblock: false, handle: Unique::new(handle), _marker: PhantomData } }
}
fn new_raw<F>(path: Option<&str>, func: F) -> Result<Capture<T>, 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))
})
}
#[inline]
fn check_err(&self, success: bool) -> Result<(), Error> {
if success {
Ok(())
} else {
Err(Error::new(unsafe { raw::pcap_geterr_symbol.unwrap()(*self.handle) }))
}
}
}
impl Capture<Offline> {
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Capture<Offline>, Error> {
Capture::new_raw(path.as_ref().to_str(), |path, err| unsafe {
raw::pcap_open_offline_symbol.unwrap()(path, err)
})
}
#[cfg(feature = "pcap-fopen-offline-precision")]
pub fn from_file_with_precision<P: AsRef<Path>>(path: P, precision: Precision) -> Result<Capture<Offline>, Error> {
Capture::new_raw(path.as_ref().to_str(), |path, err| unsafe {
raw::pcap_open_offline_with_tstamp_precision_symbol.unwrap()(path, precision as _, err)
})
}
#[cfg(not(windows))]
pub fn from_raw_fd(fd: RawFd) -> Result<Capture<Offline>, Error> {
open_raw_fd(fd, b'r').and_then(|file| {
Capture::new_raw(None, |_, err| unsafe { raw::pcap_fopen_offline_symbol.unwrap()(file, err) })
})
}
#[cfg(all(not(windows), feature = "pcap-fopen-offline-precision"))]
pub fn from_raw_fd_with_precision(fd: RawFd, precision: Precision) -> Result<Capture<Offline>, Error> {
open_raw_fd(fd, b'r').and_then(|file| {
Capture::new_raw(None, |_, err| unsafe {
raw::pcap_fopen_offline_with_tstamp_precision_symbol.unwrap()(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<Inactive> {
pub fn from_device<D: Into<Device>>(device: D) -> Result<Capture<Inactive>, Error> {
let device: Device = device.into();
Capture::new_raw(Some(&device.name), |name, err| unsafe { raw::pcap_create_symbol.unwrap()(name, err) })
}
pub fn open(self) -> Result<Capture<Active>, Error> {
unsafe {
self.check_err(raw::pcap_activate_symbol.unwrap()(*self.handle) == 0)?;
Ok(mem::transmute(self))
}
}
pub fn timeout(self, ms: i32) -> Capture<Inactive> {
unsafe { raw::pcap_set_timeout_symbol.unwrap()(*self.handle, ms) };
self
}
#[cfg(not(windows))]
pub fn tstamp_type(self, tstamp_type: TimestampType) -> Capture<Inactive> {
unsafe { raw::pcap_set_tstamp_type_symbol.unwrap()(*self.handle, tstamp_type as _) };
self
}
pub fn promisc(self, to: bool) -> Capture<Inactive> {
unsafe { raw::pcap_set_promisc_symbol.unwrap()(*self.handle, to as _) };
self
}
#[cfg(not(windows))]
pub fn rfmon(self, to: bool) -> Capture<Inactive> {
unsafe { raw::pcap_set_rfmon_symbol.unwrap()(*self.handle, to as _) };
self
}
pub fn buffer_size(self, to: i32) -> Capture<Inactive> {
unsafe { raw::pcap_set_buffer_size_symbol.unwrap()(*self.handle, to) };
self
}
#[cfg(not(windows))]
pub fn precision(self, precision: Precision) -> Capture<Inactive> {
unsafe { raw::pcap_set_tstamp_precision_symbol.unwrap()(*self.handle, precision as _) };
self
}
pub fn snaplen(self, to: i32) -> Capture<Inactive> {
unsafe { raw::pcap_set_snaplen_symbol.unwrap()(*self.handle, to) };
self
}
}
impl<T: Activated + ?Sized> Capture<T> {
pub fn list_datalinks(&self) -> Result<Vec<Linktype>, Error> {
unsafe {
let mut links: *mut i32 = ptr::null_mut();
let num = raw::pcap_list_datalinks_symbol.unwrap()(*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_symbol.unwrap()(links);
self.check_err(num > 0).and(Ok(vec))
}
}
pub fn set_datalink(&mut self, linktype: Linktype) -> Result<(), Error> {
self.check_err(unsafe { raw::pcap_set_datalink_symbol.unwrap()(*self.handle, linktype.0) == 0 })
}
pub fn get_datalink(&self) -> Linktype {
unsafe { Linktype(raw::pcap_datalink_symbol.unwrap()(*self.handle)) }
}
pub fn savefile<P: AsRef<Path>>(&self, path: P) -> Result<Savefile, Error> {
let name = CString::new(path.as_ref().to_str().unwrap())?;
let handle = unsafe { raw::pcap_dump_open_symbol.unwrap()(*self.handle, name.as_ptr()) };
self.check_err(!handle.is_null()).map(|_| Savefile::new(handle))
}
#[cfg(not(windows))]
pub fn savefile_raw_fd(&self, fd: RawFd) -> Result<Savefile, Error> {
open_raw_fd(fd, b'w').and_then(|file| {
let handle = unsafe { raw::pcap_dump_fopen_symbol.unwrap()(*self.handle, file) };
self.check_err(!handle.is_null()).map(|_| Savefile::new(handle))
})
}
#[cfg(feature = "pcap-savefile-append")]
pub fn savefile_append<P: AsRef<Path>>(&self, path: P) -> Result<Savefile, Error> {
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))
}
pub fn direction(&self, direction: Direction) -> Result<(), Error> {
self.check_err(unsafe { raw::pcap_setdirection_symbol.unwrap()(*self.handle, direction as u32 as _) == 0 })
}
#[cfg_attr(feature = "clippy", allow(should_implement_trait))]
pub fn next(&mut self) -> Result<Packet, Error> {
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_symbol.unwrap()(*self.handle, &mut header, &mut packet);
self.check_err(retcode != -1)?;
match retcode {
i if i >= 1 => {
Ok(Packet::new(mem::transmute(&*header), slice::from_raw_parts(packet, (&*header).caplen as _)))
}
0 => {
Err(TimeoutExpired)
}
-2 => {
Err(NoMorePackets)
}
_ => {
unreachable!()
}
}
}
}
#[cfg(feature = "tokio")]
fn next_noblock<'a>(
&'a mut self,
fd: &mut tokio_core::reactor::PollEvented<tokio::SelectableFd>,
) -> Result<Packet<'a>, Error> {
if let futures::Async::NotReady = fd.poll_read() {
return Err(IoError(io::ErrorKind::WouldBlock));
} else {
return match self.next() {
Ok(p) => Ok(p),
Err(TimeoutExpired) => {
fd.need_read();
Err(IoError(io::ErrorKind::WouldBlock))
}
Err(e) => Err(e),
};
}
}
#[cfg(feature = "tokio")]
pub fn stream<C: tokio::PacketCodec>(
self,
handle: &tokio_core::reactor::Handle,
codec: C,
) -> Result<tokio::PacketStream<T, C>, Error> {
if !self.nonblock {
return Err(NonNonBlock);
}
unsafe {
let fd = raw::pcap_get_selectable_fd(*self.handle);
tokio::PacketStream::new(self, fd, handle, codec)
}
}
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_symbol.unwrap()(*self.handle, &mut bpf_program, program.as_ptr(), 0, 0);
self.check_err(ret != -1)?;
let ret = raw::pcap_setfilter_symbol.unwrap()(*self.handle, &mut bpf_program);
raw::pcap_freecode_symbol.unwrap()(&mut bpf_program);
self.check_err(ret != -1)
}
}
pub fn stats(&mut self) -> Result<Stat, Error> {
unsafe {
let mut stats: raw::pcap_stat = mem::zeroed();
self.check_err(raw::pcap_stats_symbol.unwrap()(*self.handle, &mut stats) != -1)
.map(|_| Stat::new(stats.ps_recv, stats.ps_drop, stats.ps_ifdrop))
}
}
}
impl Capture<Active> {
pub fn sendpacket<B: Borrow<[u8]>>(&mut self, buf: B) -> Result<(), Error> {
let buf = buf.borrow();
self.check_err(unsafe {
raw::pcap_sendpacket_symbol.unwrap()(*self.handle, buf.as_ptr() as _, buf.len() as _) == 0
})
}
pub fn setnonblock(mut self) -> Result<Capture<Active>, Error> {
with_errbuf(|err| unsafe {
if raw::pcap_setnonblock_symbol.unwrap()(*self.handle, 1, err) != 0 {
return Err(Error::new(err));
}
self.nonblock = true;
Ok(self)
})
}
}
impl Capture<Dead> {
pub fn dead(linktype: Linktype) -> Result<Capture<Dead>, Error> {
unsafe { raw::pcap_open_dead_symbol.unwrap()(linktype.0, 65535).as_mut() }
.map(|h| Capture::new(h))
.ok_or(InsufficientMemory)
}
}
#[cfg(not(windows))]
impl AsRawFd for Capture<Active> {
fn as_raw_fd(&self) -> RawFd {
unsafe {
let fd = raw::pcap_fileno_symbol.unwrap()(*self.handle);
match fd {
-1 => {
panic!("Unable to get file descriptor for live capture");
}
fd => fd,
}
}
}
}
impl<T: State + ?Sized> Drop for Capture<T> {
fn drop(&mut self) {
unsafe { raw::pcap_close_symbol.unwrap()(*self.handle) }
}
}
impl<T: Activated> From<Capture<T>> for Capture<Activated> {
fn from(cap: Capture<T>) -> Capture<Activated> {
unsafe { mem::transmute(cap) }
}
}
pub struct Savefile {
handle: Unique<raw::pcap_dumper_t>,
}
impl Savefile {
pub fn write(&mut self, packet: &Packet) {
unsafe {
raw::pcap_dump_symbol.unwrap()(
*self.handle as _,
mem::transmute::<_, &raw::pcap_pkthdr>(packet.header),
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_symbol.unwrap()(*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<Option<String>, 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<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 _)
}
#[test]
fn test_struct_size() {
use std::mem::size_of;
assert_eq!(size_of::<PacketHeader>(), size_of::<raw::pcap_pkthdr>());
}