use std::{
io::{self, Error, Result},
marker::PhantomData,
mem::MaybeUninit,
net::{IpAddr, Ipv4Addr, Ipv6Addr},
};
use smallvec_wrapper::{SmallVec, TinyVec};
use windows_sys::{
Win32::Foundation::{ERROR_BUFFER_OVERFLOW, NO_ERROR},
Win32::NetworkManagement::{IpHelper::*, Ndis::*},
Win32::Networking::WinSock::*,
};
use super::{
Address, IfAddr, IfNet, Ifv4Addr, Ifv4Net, Ifv6Addr, Ifv6Net, Interface, IpRoute, Ipv4Route,
Ipv6Route, MacAddr, Net, MAC_ADDRESS_SIZE,
};
pub(super) use gateway::*;
pub(super) use local_addr::*;
pub(super) use route::*;
#[path = "windows/local_addr.rs"]
mod local_addr;
#[path = "windows/gateway.rs"]
mod gateway;
#[path = "windows/route.rs"]
mod route;
bitflags::bitflags! {
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Flags: u32 {
const UP = 0x1;
const BROADCAST = 0x2;
const LOOPBACK = 0x4;
const POINTOPOINT = 0x8;
const MULTICAST = 0x10;
const RUNNING = 0x20;
}
}
struct Information {
buffer: Vec<MaybeUninit<IP_ADAPTER_ADDRESSES_LH>>,
}
const _: () = assert!(
core::mem::align_of::<MaybeUninit<IP_ADAPTER_ADDRESSES_LH>>()
== core::mem::align_of::<IP_ADAPTER_ADDRESSES_LH>()
);
const SLOT_SIZE: usize = core::mem::size_of::<IP_ADAPTER_ADDRESSES_LH>();
#[inline]
fn slots_for(size_bytes: u32) -> usize {
(size_bytes as usize + SLOT_SIZE - 1) / SLOT_SIZE
}
impl Information {
fn fetch() -> Result<Self> {
let mut size = 15000u32;
let mut buffer: Vec<MaybeUninit<IP_ADAPTER_ADDRESSES_LH>> = Vec::new();
buffer.resize_with(slots_for(size), MaybeUninit::uninit);
loop {
let result = unsafe {
GetAdaptersAddresses(
AF_UNSPEC as u32,
GAA_FLAG_INCLUDE_PREFIX,
std::ptr::null() as _,
buffer.as_mut_ptr() as *mut IP_ADAPTER_ADDRESSES_LH,
&mut size,
)
};
if result == NO_ERROR {
if size == 0 {
return Ok(Self { buffer: Vec::new() });
}
break;
}
if result != ERROR_BUFFER_OVERFLOW {
return Err(Error::last_os_error());
}
if (size as usize) <= buffer.len() * SLOT_SIZE {
return Err(Error::last_os_error());
}
buffer.resize_with(slots_for(size), MaybeUninit::uninit);
}
Ok(Self { buffer })
}
fn iter(&self) -> AdapterIter<'_> {
let head = if self.buffer.is_empty() {
std::ptr::null()
} else {
self.buffer.as_ptr() as *const IP_ADAPTER_ADDRESSES_LH
};
AdapterIter {
current: head,
_marker: PhantomData,
}
}
}
struct AdapterIter<'a> {
current: *const IP_ADAPTER_ADDRESSES_LH,
_marker: PhantomData<&'a IP_ADAPTER_ADDRESSES_LH>,
}
impl<'a> Iterator for AdapterIter<'a> {
type Item = &'a IP_ADAPTER_ADDRESSES_LH;
fn next(&mut self) -> Option<Self::Item> {
unsafe {
let curr = self.current.as_ref()?;
self.current = curr.Next;
Some(curr)
}
}
}
fn interface_name_fallback(index: u32) -> smol_str::SmolStr {
let mut name_buf = [0u8; 256];
let hname = unsafe { if_indextoname(index, name_buf.as_mut_ptr()) };
if hname.is_null() {
return smol_str::SmolStr::default();
}
unsafe {
std::ffi::CStr::from_ptr(hname as _)
.to_string_lossy()
.into()
}
}
fn adapter_index(adapter: &IP_ADAPTER_ADDRESSES_LH) -> u32 {
let mut index = 0u32;
let res = unsafe { ConvertInterfaceLuidToIndex(&adapter.Luid, &mut index) };
if res != NO_ERROR {
index = adapter.Ipv6IfIndex;
}
index
}
pub(super) fn interface_table(idx: Option<u32>) -> io::Result<TinyVec<Interface>> {
let info = Information::fetch()?;
let mut interfaces = TinyVec::new();
for adapter in info.iter() {
let index = adapter_index(adapter);
if let Some(idx) = idx {
if idx == index {
let name = match crate::utils::friendly_name(adapter.FriendlyName) {
Some(name) => name,
None => interface_name_fallback(index),
};
let mut flags = Flags::empty();
if adapter.OperStatus == IfOperStatusUp {
flags |= Flags::UP | Flags::RUNNING;
}
match adapter.IfType {
IF_TYPE_ETHERNET_CSMACD
| IF_TYPE_IEEE80211
| IF_TYPE_IEEE1394
| IF_TYPE_ISO88025_TOKENRING => {
flags |= Flags::BROADCAST | Flags::MULTICAST;
}
IF_TYPE_PPP | IF_TYPE_TUNNEL => {
flags |= Flags::POINTOPOINT | Flags::MULTICAST;
}
IF_TYPE_SOFTWARE_LOOPBACK => {
flags |= Flags::LOOPBACK | Flags::MULTICAST;
}
IF_TYPE_ATM => {
flags |= Flags::BROADCAST | Flags::POINTOPOINT | Flags::MULTICAST;
}
_ => {}
}
let mtu = if adapter.Mtu == 0xffffffff {
0
} else {
adapter.Mtu
};
let hardware_addr = if adapter.PhysicalAddressLength > 0 {
let mut buf = [0u8; MAC_ADDRESS_SIZE];
let max_addr_len = (adapter.PhysicalAddressLength as usize).min(MAC_ADDRESS_SIZE);
let addr = &adapter.PhysicalAddress[..max_addr_len];
buf[..max_addr_len].copy_from_slice(addr);
Some(MacAddr::from_raw(buf))
} else {
None
};
let interface = Interface {
index,
name,
flags,
mtu,
mac_addr: hardware_addr,
};
interfaces.push(interface);
break;
}
} else {
let name = match crate::utils::friendly_name(adapter.FriendlyName) {
Some(name) => name,
None => interface_name_fallback(index),
};
let mut flags = Flags::empty();
if adapter.OperStatus == IfOperStatusUp {
flags |= Flags::UP | Flags::RUNNING;
}
match adapter.IfType {
IF_TYPE_ETHERNET_CSMACD
| IF_TYPE_IEEE80211
| IF_TYPE_IEEE1394
| IF_TYPE_ISO88025_TOKENRING => {
flags |= Flags::BROADCAST | Flags::MULTICAST;
}
IF_TYPE_PPP | IF_TYPE_TUNNEL => {
flags |= Flags::POINTOPOINT | Flags::MULTICAST;
}
IF_TYPE_SOFTWARE_LOOPBACK => {
flags |= Flags::LOOPBACK | Flags::MULTICAST;
}
IF_TYPE_ATM => {
flags |= Flags::BROADCAST | Flags::POINTOPOINT | Flags::MULTICAST;
}
_ => {}
}
let mtu = if adapter.Mtu == 0xffffffff {
0
} else {
adapter.Mtu
};
let hardware_addr = if adapter.PhysicalAddressLength > 0 {
let mut buf = [0u8; MAC_ADDRESS_SIZE];
let max_addr_len = (adapter.PhysicalAddressLength as usize).min(MAC_ADDRESS_SIZE);
let addr = &adapter.PhysicalAddress[..max_addr_len];
buf[..max_addr_len].copy_from_slice(addr);
Some(MacAddr::from_raw(buf))
} else {
None
};
interfaces.push(Interface {
index,
name,
flags,
mtu,
mac_addr: hardware_addr,
});
}
}
Ok(interfaces)
}
pub(super) fn interface_ipv4_addresses<F>(idx: Option<u32>, f: F) -> io::Result<SmallVec<Ifv4Net>>
where
F: FnMut(&IpAddr) -> bool,
{
interface_addr_table(AF_INET, idx, f)
}
pub(super) fn interface_ipv6_addresses<F>(idx: Option<u32>, f: F) -> io::Result<SmallVec<Ifv6Net>>
where
F: FnMut(&IpAddr) -> bool,
{
interface_addr_table(AF_INET6, idx, f)
}
pub(super) fn interface_addresses<F>(idx: Option<u32>, f: F) -> io::Result<SmallVec<IfNet>>
where
F: FnMut(&IpAddr) -> bool,
{
interface_addr_table(AF_UNSPEC, idx, f)
}
pub(super) fn interface_addr_table<T, F>(
family: u16,
ifi: Option<u32>,
mut f: F,
) -> io::Result<SmallVec<T>>
where
T: Net,
F: FnMut(&IpAddr) -> bool,
{
let info = Information::fetch()?;
let mut addresses = SmallVec::new();
for adapter in info.iter() {
let index = adapter_index(adapter);
if let Some(ifi) = ifi {
if ifi == index {
unsafe {
let mut unicast = adapter.FirstUnicastAddress;
while let Some(addr) = unicast.as_ref() {
if let Some(ip) = sockaddr_to_ipaddr(family, addr.Address.lpSockaddr) {
if let Some(ip) = T::try_from_with_filter(index, ip, addr.OnLinkPrefixLength, &mut f)
{
addresses.push(ip);
}
}
unicast = addr.Next;
}
}
}
} else {
unsafe {
let mut unicast = adapter.FirstUnicastAddress;
while let Some(addr) = unicast.as_ref() {
if let Some(ip) = sockaddr_to_ipaddr(family, addr.Address.lpSockaddr) {
if let Some(ip) = T::try_from_with_filter(index, ip, addr.OnLinkPrefixLength, &mut f) {
addresses.push(ip);
}
}
unicast = addr.Next;
}
}
}
}
Ok(addresses)
}
pub(super) fn interface_multicast_ipv4_addresses<F>(
idx: Option<u32>,
mut f: F,
) -> io::Result<SmallVec<Ifv4Addr>>
where
F: FnMut(&Ipv4Addr) -> bool,
{
interface_multiaddr_table(AF_INET, idx, |addr| match addr {
IpAddr::V4(ip) => f(ip),
_ => false,
})
}
pub(super) fn interface_multicast_ipv6_addresses<F>(
idx: Option<u32>,
mut f: F,
) -> io::Result<SmallVec<Ifv6Addr>>
where
F: FnMut(&Ipv6Addr) -> bool,
{
interface_multiaddr_table(AF_INET6, idx, |addr| match addr {
IpAddr::V6(ip) => f(ip),
_ => false,
})
}
pub(super) fn interface_multicast_addresses<F>(
idx: Option<u32>,
f: F,
) -> io::Result<SmallVec<IfAddr>>
where
F: FnMut(&IpAddr) -> bool,
{
interface_multiaddr_table(AF_UNSPEC, idx, f)
}
pub(super) fn interface_multiaddr_table<T, F>(
family: u16,
ifi: Option<u32>,
mut f: F,
) -> io::Result<SmallVec<T>>
where
T: Address,
F: FnMut(&IpAddr) -> bool,
{
let info = Information::fetch()?;
let mut addresses = SmallVec::new();
for adapter in info.iter() {
let index = adapter_index(adapter);
if let Some(ifi) = ifi {
if ifi == index {
let mut multicast = adapter.FirstMulticastAddress;
unsafe {
while let Some(addr) = multicast.as_ref() {
if let Some(ip) = sockaddr_to_ipaddr(family, addr.Address.lpSockaddr) {
if let Some(ip) = T::try_from_with_filter(index, ip, &mut f) {
addresses.push(ip);
}
}
multicast = addr.Next;
}
}
}
} else {
let mut multicast = adapter.FirstMulticastAddress;
unsafe {
while let Some(addr) = multicast.as_ref() {
if let Some(ip) = sockaddr_to_ipaddr(family, addr.Address.lpSockaddr) {
if let Some(ip) = T::try_from_with_filter(index, ip, &mut f) {
addresses.push(ip);
}
}
multicast = addr.Next;
}
}
}
}
Ok(addresses)
}
fn sockaddr_to_ipaddr(family: u16, sockaddr: *const SOCKADDR) -> Option<IpAddr> {
if sockaddr.is_null() {
return None;
}
unsafe {
match (family, (*sockaddr).sa_family) {
(AF_INET, AF_INET) | (AF_UNSPEC, AF_INET) => {
let addr = sockaddr as *const SOCKADDR_IN;
if addr.is_null() {
return None;
}
let bytes = (*addr).sin_addr.S_un.S_addr.to_ne_bytes();
Some(IpAddr::V4(bytes.into()))
}
(AF_INET6, AF_INET6) | (AF_UNSPEC, AF_INET6) => {
let addr = sockaddr as *const SOCKADDR_IN6;
if addr.is_null() {
return None;
}
let bytes = (*addr).sin6_addr.u.Byte;
Some(IpAddr::V6(bytes.into()))
}
_ => None,
}
}
}