#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#[cfg(any(doc, feature = "async-std"))]
pub mod async_std;
#[cfg(any(
doc,
target_os = "dragonfly",
target_os = "freebsd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
))]
pub mod bpf;
#[cfg(any(doc, all(feature = "mio", not(target_os = "windows"))))]
pub mod mio;
#[cfg(any(doc, feature = "smol"))]
pub mod smol;
#[cfg(any(doc, feature = "tokio"))]
pub mod tokio;
pub mod filter;
#[cfg(target_os = "linux")]
pub mod linux;
#[cfg(any(doc, all(target_os = "windows", feature = "npcap")))]
pub mod npcap;
#[cfg(any(doc, target_os = "windows"))]
pub mod pktmon;
#[cfg(any(doc, not(target_os = "windows"), feature = "npcap"))]
mod sniffer;
mod utils;
#[cfg(any(doc, not(target_os = "windows"), feature = "npcap"))]
pub use sniffer::Sniffer;
#[cfg(any(doc, target_os = "linux"))]
use std::array;
use std::ffi::{CStr, OsStr};
use std::io;
#[cfg(not(target_os = "windows"))]
use std::os::unix::ffi::OsStrExt;
#[cfg(target_os = "windows")]
use windows_sys::Win32::NetworkManagement::IpHelper::MAX_ADAPTER_NAME;
#[cfg(not(target_os = "windows"))]
const INTERNAL_MAX_INTERFACE_NAME_LEN: usize = libc::IF_NAMESIZE - 1;
#[cfg(target_os = "windows")]
const INTERNAL_MAX_INTERFACE_NAME_LEN: usize = MAX_ADAPTER_NAME as usize - 1;
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Interface {
name: [u8; Self::MAX_INTERFACE_NAME_LEN + 1],
}
impl Interface {
pub const MAX_INTERFACE_NAME_LEN: usize = INTERNAL_MAX_INTERFACE_NAME_LEN;
#[inline]
pub fn new(if_name: impl AsRef<OsStr>) -> io::Result<Self> {
Self::new_inner(if_name)
}
#[cfg(target_os = "windows")]
#[inline]
fn new_inner(if_name: impl AsRef<OsStr>) -> io::Result<Self> {
let s = if_name.as_ref().as_encoded_bytes();
Self::new_raw(s)
}
#[cfg(not(target_os = "windows"))]
#[inline]
fn new_inner(if_name: impl AsRef<OsStr>) -> io::Result<Self> {
let s = if_name.as_ref().as_bytes();
Self::new_raw(s)
}
pub fn new_raw(if_name: &[u8]) -> io::Result<Self> {
if if_name.len() > Self::MAX_INTERFACE_NAME_LEN || if_name.contains(&0x00) {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
"malformed interface name",
));
}
let mut name = [0u8; Self::MAX_INTERFACE_NAME_LEN + 1];
name[..if_name.len()].copy_from_slice(if_name);
let interface = Interface { name };
#[cfg(not(target_os = "windows"))]
interface.index()?;
Ok(interface)
}
pub fn find_all() -> io::Result<Vec<Self>> {
todo!()
}
#[inline]
#[cfg(not(target_os = "windows"))]
pub fn from_index(if_index: u32) -> io::Result<Self> {
let mut name = [0u8; Self::MAX_INTERFACE_NAME_LEN + 1];
match unsafe { libc::if_indextoname(if_index, name.as_mut_ptr() as *mut i8) } {
ptr if ptr.is_null() => Err(io::Error::last_os_error()),
_ => Ok(Self { name }),
}
}
#[inline]
#[cfg(not(target_os = "windows"))]
pub fn index(&self) -> io::Result<u32> {
match unsafe { libc::if_nametoindex(self.name.as_ptr() as *const i8) } {
0 => Err(io::Error::last_os_error()),
i => Ok(i),
}
}
pub fn name(&self) -> &OsStr {
let end = self.name.partition_point(|&c| c != 0);
unsafe { OsStr::from_encoded_bytes_unchecked(&self.name[..end]) }
}
pub fn name_cstr(&self) -> &CStr {
let end = self.name.partition_point(|&c| c != 0);
CStr::from_bytes_with_nul(&self.name[..=end]).unwrap()
}
pub fn name_raw(&self) -> [u8; Self::MAX_INTERFACE_NAME_LEN + 1] {
self.name.clone()
}
#[cfg(any(doc, target_os = "linux"))]
pub fn arp_type(&self) -> io::Result<u16> {
use std::ptr;
let mut ifr = libc::ifreq {
ifr_name: array::from_fn(|i| self.name[i] as i8),
ifr_ifru: libc::__c_anonymous_ifr_ifru {
ifru_hwaddr: libc::sockaddr {
sa_family: 0,
sa_data: [0; 14],
},
},
};
let fd = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
if fd < 0 {
return Err(io::Error::last_os_error());
}
if unsafe { libc::ioctl(fd, libc::SIOCGIFHWADDR, ptr::addr_of_mut!(ifr)) } < 0 {
let err = io::Error::last_os_error();
unsafe {
libc::close(fd);
}
return Err(err);
}
unsafe {
libc::close(fd);
Ok(ifr.ifr_ifru.ifru_hwaddr.sa_family)
}
}
#[cfg(target_os = "linux")]
#[inline]
pub fn datalink_type(&self) -> io::Result<u16> {
match self.arp_type()? {
libc::ARPHRD_ETHER => todo!(), _ => todo!(),
}
}
}