use bitflags::bitflags;
use std::{
ffi::{OsStr, OsString, c_void},
io, mem,
net::{IpAddr, Ipv4Addr, Ipv6Addr},
ops::Deref,
os::windows::{ffi::OsStringExt, io::RawHandle},
ptr,
sync::Mutex,
};
use windows_sys::Win32::{
Foundation::{ERROR_BUFFER_OVERFLOW, ERROR_SUCCESS, NO_ERROR},
NetworkManagement::{
IpHelper::{
CancelMibChangeNotify2, ConvertInterfaceAliasToLuid, ConvertInterfaceLuidToAlias,
ConvertInterfaceLuidToGuid, ConvertInterfaceLuidToIndex, FreeMibTable,
GAA_FLAG_INCLUDE_ALL_COMPARTMENTS, GAA_FLAG_INCLUDE_ALL_INTERFACES,
GAA_FLAG_INCLUDE_GATEWAYS, GAA_FLAG_INCLUDE_PREFIX,
GAA_FLAG_INCLUDE_TUNNEL_BINDINGORDER, GAA_FLAG_INCLUDE_WINS_INFO,
GAA_FLAG_SKIP_ANYCAST, GAA_FLAG_SKIP_DNS_SERVER, GAA_FLAG_SKIP_MULTICAST,
GetAdaptersAddresses, GetIpForwardEntry2, GetIpForwardTable2, GetIpInterfaceEntry,
GetUnicastIpAddressTable, IP_ADAPTER_ADDRESSES_LH, IP_ADDRESS_PREFIX,
MIB_IPFORWARD_ROW2, MIB_IPFORWARD_TABLE2, MIB_IPINTERFACE_ROW,
MIB_UNICASTIPADDRESS_ROW, MIB_UNICASTIPADDRESS_TABLE, MibAddInstance,
MibDeleteInstance, MibInitialNotification, MibParameterNotification,
NotifyIpInterfaceChange, NotifyRouteChange2, SetIpInterfaceEntry,
},
Ndis::{IF_MAX_STRING_SIZE, NET_LUID_LH},
},
Networking::WinSock::{AF_INET, AF_INET6, AF_UNSPEC, SOCKADDR_INET},
};
use windows_sys::core::GUID;
use crate::util::string_to_null_terminated_utf16;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AddressFamily {
Inet = AF_INET as isize,
Inet6 = AF_INET6 as isize,
}
impl TryFrom<u16> for AddressFamily {
type Error = ();
fn try_from(value: u16) -> Result<Self, Self::Error> {
match value {
AF_INET => Ok(AddressFamily::Inet),
AF_INET6 => Ok(AddressFamily::Inet6),
_ => Err(()),
}
}
}
#[repr(transparent)]
pub struct Luid(u64);
const _: () = {
assert!(mem::size_of::<Luid>() == mem::size_of::<NET_LUID_LH>());
assert!(mem::align_of::<Luid>() == mem::align_of::<NET_LUID_LH>());
};
impl AsRef<Luid> for NET_LUID_LH {
fn as_ref(&self) -> &Luid {
unsafe { &*(self as *const NET_LUID_LH as *const Luid) }
}
}
impl AsRef<NET_LUID_LH> for Luid {
fn as_ref(&self) -> &NET_LUID_LH {
unsafe { &*(self as *const Luid as *const NET_LUID_LH) }
}
}
impl Deref for Luid {
type Target = u64;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<NET_LUID_LH> for Luid {
fn from(luid: NET_LUID_LH) -> Self {
Luid(unsafe { luid.Value })
}
}
impl From<u64> for Luid {
fn from(value: u64) -> Self {
Luid(value)
}
}
impl From<Luid> for u64 {
fn from(luid: Luid) -> Self {
luid.0
}
}
impl From<Luid> for NET_LUID_LH {
fn from(luid: Luid) -> Self {
NET_LUID_LH { Value: luid.0 }
}
}
pub struct Guid(GUID);
impl From<GUID> for Guid {
fn from(guid: GUID) -> Self {
Guid(guid)
}
}
impl From<u128> for Guid {
fn from(guid: u128) -> Self {
Guid(GUID::from_u128(guid))
}
}
impl From<Guid> for GUID {
fn from(value: Guid) -> Self {
value.0
}
}
const _: () = {
assert!(mem::size_of::<Guid>() == mem::size_of::<GUID>());
assert!(mem::align_of::<Guid>() == mem::align_of::<GUID>());
assert!(mem::size_of::<Guid>() == mem::size_of::<u128>());
};
pub fn get_unicast_ip_address_table(
family: Option<AddressFamily>,
) -> io::Result<Vec<UnicastIpAddressRow>> {
let mut table: *mut MIB_UNICASTIPADDRESS_TABLE = ptr::null_mut();
let family = family.map(|f| f as u16).unwrap_or(AF_UNSPEC);
let result = unsafe { GetUnicastIpAddressTable(family, &mut table) };
if result != NO_ERROR {
return Err(io::Error::from_raw_os_error(result as i32));
}
debug_assert_ne!(table, ptr::null_mut());
let num_entries = usize::try_from(unsafe { (*table).NumEntries }).unwrap();
let entries = unsafe { std::slice::from_raw_parts((*table).Table.as_ptr(), num_entries) }
.iter()
.map(|&raw_entry| UnicastIpAddressRow { raw_entry })
.collect();
unsafe {
FreeMibTable(table as *mut _);
}
Ok(entries)
}
pub fn get_ip_forward_table(family: Option<AddressFamily>) -> io::Result<Vec<RouteRow>> {
let mut table: *mut MIB_IPFORWARD_TABLE2 = ptr::null_mut();
let family = family.map(|f| f as u16).unwrap_or(AF_UNSPEC);
let result = unsafe { GetIpForwardTable2(family, &mut table) };
if result != NO_ERROR {
return Err(io::Error::from_raw_os_error(result as i32));
}
debug_assert_ne!(table, ptr::null_mut());
let num_entries = usize::try_from(unsafe { (*table).NumEntries }).unwrap();
let entries = unsafe { std::slice::from_raw_parts((*table).Table.as_ptr(), num_entries) }
.iter()
.map(|&row| RouteRow { row })
.collect();
unsafe {
FreeMibTable(table as *mut _);
}
Ok(entries)
}
#[repr(transparent)]
pub struct UnicastIpAddressRow {
raw_entry: MIB_UNICASTIPADDRESS_ROW,
}
impl UnicastIpAddressRow {
pub fn interface_index(&self) -> u32 {
self.raw_entry.InterfaceIndex
}
pub fn family(&self) -> AddressFamily {
unsafe { AddressFamily::try_from(self.raw_entry.Address.si_family) }
.expect("invalid address family")
}
pub fn interface_luid(&self) -> Luid {
Luid::from(self.raw_entry.InterfaceLuid)
}
pub fn address(&self) -> IpAddr {
match self.family() {
AddressFamily::Inet => {
let addr_bytes = unsafe { self.raw_entry.Address.Ipv4.sin_addr.S_un.S_addr };
let ipv4 = Ipv4Addr::from(addr_bytes.to_ne_bytes());
IpAddr::V4(ipv4)
}
AddressFamily::Inet6 => {
let addr_bytes = unsafe { self.raw_entry.Address.Ipv6.sin6_addr.u.Byte };
let ipv6 = Ipv6Addr::from(addr_bytes);
IpAddr::V6(ipv6)
}
}
}
pub fn prefix_length(&self) -> u8 {
match self.raw_entry.OnLinkPrefixLength {
prefix @ 0..=128 => prefix,
_ if self.family() == AddressFamily::Inet => 32,
_ if self.family() == AddressFamily::Inet6 => 128,
_ => panic!("invalid prefix length"),
}
}
pub fn as_raw(&self) -> &MIB_UNICASTIPADDRESS_ROW {
&self.raw_entry
}
}
pub enum InterfaceIdentifier {
Luid(Luid),
Index(u32),
}
impl<T: Into<Luid>> From<T> for InterfaceIdentifier {
fn from(luid: T) -> Self {
InterfaceIdentifier::Luid(luid.into())
}
}
pub fn get_ip_interface_entry(
id: impl Into<InterfaceIdentifier>,
family: AddressFamily,
) -> io::Result<IpInterfaceRow> {
match id.into() {
InterfaceIdentifier::Luid(luid) => IpInterfaceRow::get_by_luid(luid, family),
InterfaceIdentifier::Index(index) => IpInterfaceRow::get_by_index(index, family),
}
}
pub fn set_ip_interface_entry(interface: impl AsRef<MIB_IPINTERFACE_ROW>) -> io::Result<()> {
let interface = interface.as_ref();
let status = unsafe { SetIpInterfaceEntry(interface as *const _ as *mut _) };
if status != 0 {
return Err(io::Error::from_raw_os_error(status as i32));
}
Ok(())
}
pub fn get_ip_forward_entry(
interface: impl Into<InterfaceIdentifier>,
destination_prefix: (IpAddr, u8),
next_hop: IpAddr,
) -> io::Result<RouteRow> {
match interface.into() {
InterfaceIdentifier::Luid(luid) => {
RouteRow::get_by_destination_and_interface(destination_prefix, luid, next_hop)
}
InterfaceIdentifier::Index(index) => {
RouteRow::get_by_destination_and_index(destination_prefix, index, next_hop)
}
}
}
#[repr(transparent)]
pub struct IpInterfaceRow {
row: MIB_IPINTERFACE_ROW,
}
impl IpInterfaceRow {
pub fn new(row: &MIB_IPINTERFACE_ROW) -> &Self {
let pprows = row as *const MIB_IPINTERFACE_ROW;
unsafe { &*(pprows.cast()) }
}
fn get_by_index(index: u32, family: AddressFamily) -> io::Result<Self> {
Self::get_inner(|| MIB_IPINTERFACE_ROW {
Family: family as u16,
InterfaceIndex: index,
..Default::default()
})
}
fn get_by_luid(luid: impl Into<Luid>, family: AddressFamily) -> io::Result<Self> {
Self::get_inner(|| MIB_IPINTERFACE_ROW {
Family: family as u16,
InterfaceLuid: NET_LUID_LH::from(luid.into()),
..Default::default()
})
}
fn get_inner(make_row: impl FnOnce() -> MIB_IPINTERFACE_ROW) -> io::Result<Self> {
let mut row = make_row();
let status = unsafe { GetIpInterfaceEntry(&mut row) };
if status != 0 {
return Err(io::Error::from_raw_os_error(status as i32));
}
Ok(IpInterfaceRow { row })
}
pub fn interface_luid(&self) -> Luid {
Luid::from(self.row.InterfaceLuid)
}
pub fn family(&self) -> AddressFamily {
AddressFamily::try_from(self.row.Family).expect("invalid address family")
}
pub fn metric(&self) -> u32 {
self.row.Metric
}
pub fn automatic_metric(&self) -> bool {
self.row.UseAutomaticMetric
}
pub fn mtu(&self) -> u32 {
self.row.NlMtu
}
pub fn modify(self) -> IpInterfaceRowModifier {
IpInterfaceRowModifier { row: self.row }
}
pub fn as_raw(&self) -> &MIB_IPINTERFACE_ROW {
&self.row
}
pub fn as_raw_mut(&mut self) -> &mut MIB_IPINTERFACE_ROW {
&mut self.row
}
}
impl<'a> From<&'a MIB_IPINTERFACE_ROW> for &'a IpInterfaceRow {
fn from(row: &'a MIB_IPINTERFACE_ROW) -> Self {
IpInterfaceRow::new(row)
}
}
impl AsRef<MIB_IPINTERFACE_ROW> for IpInterfaceRow {
fn as_ref(&self) -> &MIB_IPINTERFACE_ROW {
&self.row
}
}
pub struct IpInterfaceRowModifier {
row: MIB_IPINTERFACE_ROW,
}
impl IpInterfaceRowModifier {
pub fn set_metric(mut self, metric: u32) -> Self {
self.row.Metric = metric;
self.row.UseAutomaticMetric = false;
self
}
pub fn set_automatic_metric(mut self, enabled: bool) -> Self {
self.row.UseAutomaticMetric = enabled;
self
}
pub fn set_mtu(mut self, mtu: u32) -> Self {
self.row.NlMtu = mtu;
self
}
pub fn as_raw_mut(&mut self) -> &mut MIB_IPINTERFACE_ROW {
&mut self.row
}
pub fn save(mut self) -> io::Result<()> {
let prev_prefix_len = self.row.SitePrefixLength;
self.row.SitePrefixLength = 0;
let result = set_ip_interface_entry(&self);
self.row.SitePrefixLength = prev_prefix_len;
result
}
}
impl AsRef<MIB_IPINTERFACE_ROW> for IpInterfaceRowModifier {
fn as_ref(&self) -> &MIB_IPINTERFACE_ROW {
&self.row
}
}
#[repr(transparent)]
pub struct RouteRow {
row: MIB_IPFORWARD_ROW2,
}
impl RouteRow {
pub fn new(row: &MIB_IPFORWARD_ROW2) -> &Self {
let pprows = row as *const MIB_IPFORWARD_ROW2;
unsafe { &*(pprows.cast()) }
}
fn get_by_destination_and_interface(
destination_prefix: (IpAddr, u8),
interface_luid: impl Into<Luid>,
next_hop: IpAddr,
) -> io::Result<Self> {
let interface_luid = interface_luid.into();
Self::get_inner(|| MIB_IPFORWARD_ROW2 {
InterfaceLuid: NET_LUID_LH::from(interface_luid),
DestinationPrefix: IP_ADDRESS_PREFIX {
Prefix: Self::prefix_from_ip(destination_prefix.0),
PrefixLength: destination_prefix.1,
},
NextHop: Self::prefix_from_ip(next_hop),
..Default::default()
})
}
fn get_by_destination_and_index(
destination_prefix: (IpAddr, u8),
interface_index: u32,
next_hop: IpAddr,
) -> io::Result<Self> {
Self::get_inner(|| MIB_IPFORWARD_ROW2 {
InterfaceIndex: interface_index,
DestinationPrefix: IP_ADDRESS_PREFIX {
Prefix: Self::prefix_from_ip(destination_prefix.0),
PrefixLength: destination_prefix.1,
},
NextHop: Self::prefix_from_ip(next_hop),
..Default::default()
})
}
fn prefix_from_ip(ip: IpAddr) -> SOCKADDR_INET {
let mut addr = SOCKADDR_INET::default();
match ip {
IpAddr::V4(ipv4) => {
addr.si_family = AF_INET;
addr.Ipv4.sin_addr.S_un.S_addr = u32::from_be_bytes(ipv4.octets());
}
IpAddr::V6(ipv6) => {
addr.si_family = AF_INET6;
addr.Ipv6.sin6_addr.u.Byte = ipv6.octets();
}
}
addr
}
fn get_inner(make_row: impl FnOnce() -> MIB_IPFORWARD_ROW2) -> io::Result<Self> {
let mut row = make_row();
let status = unsafe { GetIpForwardEntry2(&mut row) };
if status != 0 {
return Err(io::Error::from_raw_os_error(status as i32));
}
Ok(RouteRow { row })
}
pub fn destination_prefix(&self) -> (IpAddr, u8) {
let dest = unsafe {
match self.row.DestinationPrefix.Prefix.si_family {
AF_INET => {
let addr = self.row.DestinationPrefix.Prefix.Ipv4.sin_addr.S_un.S_addr;
IpAddr::V4(Ipv4Addr::from(u32::from_be(addr)))
}
AF_INET6 => {
let addr = self.row.DestinationPrefix.Prefix.Ipv6.sin6_addr.u.Byte;
IpAddr::V6(Ipv6Addr::from(addr))
}
_ => unreachable!("invalid address family"),
}
};
(dest, self.prefix_length())
}
fn prefix_length(&self) -> u8 {
self.row.DestinationPrefix.PrefixLength
}
pub fn next_hop(&self) -> IpAddr {
unsafe {
match self.row.NextHop.si_family {
AF_INET => {
let addr = self.row.NextHop.Ipv4.sin_addr.S_un.S_addr;
IpAddr::V4(Ipv4Addr::from(u32::from_be(addr)))
}
AF_INET6 => {
let addr = self.row.NextHop.Ipv6.sin6_addr.u.Byte;
IpAddr::V6(Ipv6Addr::from(addr))
}
_ => unreachable!("invalid address family"),
}
}
}
pub fn interface_index(&self) -> u32 {
self.row.InterfaceIndex
}
pub fn interface_luid(&self) -> Luid {
Luid::from(self.row.InterfaceLuid)
}
pub fn metric(&self) -> u32 {
self.row.Metric
}
pub fn immortal(&self) -> bool {
self.row.Immortal
}
pub fn age(&self) -> u32 {
self.row.Age
}
pub fn as_raw(&self) -> &MIB_IPFORWARD_ROW2 {
&self.row
}
}
impl<'a> From<&'a MIB_IPFORWARD_ROW2> for &'a RouteRow {
fn from(raw: &'a MIB_IPFORWARD_ROW2) -> Self {
RouteRow::new(raw)
}
}
impl AsRef<MIB_IPFORWARD_ROW2> for RouteRow {
fn as_ref(&self) -> &MIB_IPFORWARD_ROW2 {
&self.row
}
}
pub enum NotificationType<'a, T> {
ParameterNotification(&'a T),
AddInstance(&'a T),
DeleteInstance(&'a T),
InitialNotification,
}
impl<T> From<&NotificationType<'_, T>> for i32 {
fn from(nt: &NotificationType<'_, T>) -> Self {
match nt {
NotificationType::ParameterNotification(_) => MibParameterNotification,
NotificationType::AddInstance(_) => MibAddInstance,
NotificationType::DeleteInstance(_) => MibDeleteInstance,
NotificationType::InitialNotification => MibInitialNotification,
}
}
}
pub trait NotificationCb<T>: FnMut(NotificationType<'_, T>) + Send {}
impl<T, F: FnMut(NotificationType<'_, T>) + Send> NotificationCb<T> for F {}
pub fn notify_ip_interface_change(
family: Option<AddressFamily>,
callback: impl NotificationCb<IpInterfaceRow> + 'static,
initial_notification: bool,
) -> io::Result<Box<NotifyCallbackHandle<IpInterfaceRow>>> {
let mut context = Box::new(NotifyCallbackHandle {
callback: Mutex::new(Box::new(callback)),
handle: ptr::null_mut(),
});
let status = unsafe {
NotifyIpInterfaceChange(
family.map(|f| f as u16).unwrap_or(AF_UNSPEC),
Some(notify_callback::<MIB_IPINTERFACE_ROW, IpInterfaceRow>),
&mut *context.as_mut() as *mut _ as *mut _,
initial_notification,
(&mut context.handle) as *mut _,
)
};
if status != ERROR_SUCCESS {
return Err(io::Error::from_raw_os_error(status as i32));
}
Ok(context)
}
pub struct NotifyCallbackHandle<T> {
callback: Mutex<Box<dyn NotificationCb<T>>>,
handle: RawHandle,
}
unsafe impl<T> Send for NotifyCallbackHandle<T> {}
impl<T> Drop for NotifyCallbackHandle<T> {
fn drop(&mut self) {
unsafe { CancelMibChangeNotify2(self.handle) };
}
}
#[allow(non_upper_case_globals)]
unsafe extern "system" fn notify_callback<'a, UnderlyingType: 'a, WrappedType: 'a>(
context: *const c_void,
row: *const UnderlyingType,
notification_type: i32,
) where
&'a WrappedType: From<&'a UnderlyingType>,
{
if context.is_null() {
return;
}
let context = unsafe { &*(context as *mut NotifyCallbackHandle<WrappedType>) };
let mut callback = context.callback.lock().unwrap();
if notification_type == MibInitialNotification {
(callback)(NotificationType::InitialNotification);
return;
}
let raw = unsafe { &*row };
let converted: &WrappedType = <&WrappedType>::from(raw);
match notification_type {
MibParameterNotification => {
(callback)(NotificationType::ParameterNotification(converted));
}
MibAddInstance => {
(callback)(NotificationType::AddInstance(converted));
}
MibDeleteInstance => {
(callback)(NotificationType::DeleteInstance(converted));
}
other => unreachable!("invalid notification type: {other}"),
}
}
pub fn notify_route_change(
family: Option<AddressFamily>,
callback: impl NotificationCb<RouteRow> + 'static,
initial_notification: bool,
) -> io::Result<Box<NotifyCallbackHandle<RouteRow>>> {
let mut context = Box::new(NotifyCallbackHandle {
callback: Mutex::new(Box::new(callback)),
handle: ptr::null_mut(),
});
let status = unsafe {
NotifyRouteChange2(
family.map(|f| f as u16).unwrap_or(AF_UNSPEC),
Some(notify_callback::<MIB_IPFORWARD_ROW2, RouteRow>),
&mut *context.as_mut() as *mut _ as *mut _,
initial_notification,
(&mut context.handle) as *mut _,
)
};
if status != ERROR_SUCCESS {
return Err(io::Error::from_raw_os_error(status as i32));
}
Ok(context)
}
pub fn convert_interface_luid_to_index(luid: impl Into<Luid>) -> io::Result<u32> {
let mut index = 0u32;
let luid = luid.into();
let status = unsafe { ConvertInterfaceLuidToIndex(luid.as_ref(), &mut index) };
if status != NO_ERROR {
return Err(io::Error::from_raw_os_error(status as i32));
}
Ok(index)
}
pub fn convert_interface_luid_to_guid(luid: impl Into<Luid>) -> io::Result<Guid> {
let mut guid = GUID::default();
let luid = luid.into();
let status = unsafe { ConvertInterfaceLuidToGuid(luid.as_ref(), &mut guid) };
if status != NO_ERROR {
return Err(io::Error::from_raw_os_error(status as i32));
}
Ok(guid.into())
}
pub fn convert_interface_alias_to_luid<T: AsRef<OsStr>>(alias: T) -> io::Result<Luid> {
let alias_wide: Vec<u16> = string_to_null_terminated_utf16(alias);
let mut luid = NET_LUID_LH::default();
let status = unsafe { ConvertInterfaceAliasToLuid(alias_wide.as_ptr(), &mut luid) };
if status != NO_ERROR {
return Err(io::Error::from_raw_os_error(status as i32));
}
Ok(luid.into())
}
pub fn convert_interface_luid_to_alias(luid: impl Into<Luid>) -> io::Result<OsString> {
let mut buffer = [0u16; IF_MAX_STRING_SIZE as usize + 1];
let luid = luid.into();
let status =
unsafe { ConvertInterfaceLuidToAlias(luid.as_ref(), buffer.as_mut_ptr(), buffer.len()) };
if status != NO_ERROR {
return Err(io::Error::from_raw_os_error(status as i32));
}
let nul = buffer.iter().position(|&c| c == 0u16).unwrap();
Ok(OsString::from_wide(&buffer[0..nul]))
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AdapterAddressFlags: u32 {
const INCLUDE_PREFIX = GAA_FLAG_INCLUDE_PREFIX;
const SKIP_ANYCAST = GAA_FLAG_SKIP_ANYCAST;
const SKIP_MULTICAST = GAA_FLAG_SKIP_MULTICAST;
const SKIP_DNS_SERVER = GAA_FLAG_SKIP_DNS_SERVER;
const INCLUDE_WINS_INFO = GAA_FLAG_INCLUDE_WINS_INFO;
const INCLUDE_GATEWAYS = GAA_FLAG_INCLUDE_GATEWAYS;
const INCLUDE_ALL_INTERFACES = GAA_FLAG_INCLUDE_ALL_INTERFACES;
const INCLUDE_ALL_COMPARTMENTS = GAA_FLAG_INCLUDE_ALL_COMPARTMENTS;
const INCLUDE_TUNNEL_BINDINGORDER = GAA_FLAG_INCLUDE_TUNNEL_BINDINGORDER;
}
}
pub fn get_adapters_addresses(
family: Option<AddressFamily>,
flags: AdapterAddressFlags,
) -> io::Result<AdapterAddressTable> {
let family = family.map(|f| f as u16).unwrap_or(AF_UNSPEC);
let mut size: u32 = 15 * 1024;
let mut buffer = vec![0u8; size as usize];
loop {
let result = unsafe {
GetAdaptersAddresses(
u32::from(family),
flags.bits(),
ptr::null_mut(),
buffer.as_mut_ptr() as *mut _,
&mut size,
)
};
if result == ERROR_BUFFER_OVERFLOW {
buffer.resize(size as usize, 0);
continue;
}
if result != NO_ERROR {
return Err(io::Error::from_raw_os_error(result as i32));
}
break;
}
buffer.truncate(size as usize);
Ok(AdapterAddressTable { buffer })
}
pub struct AdapterAddressTable {
buffer: Vec<u8>,
}
impl AdapterAddressTable {
pub fn iter(&self) -> AdapterAddressIterator<'_> {
AdapterAddressIterator {
current: if self.buffer.is_empty() {
ptr::null()
} else {
self.buffer.as_ptr() as *const IP_ADAPTER_ADDRESSES_LH
},
_phantom: std::marker::PhantomData,
}
}
}
impl<'a> IntoIterator for &'a AdapterAddressTable {
type Item = AdapterAddress<'a>;
type IntoIter = AdapterAddressIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
pub struct AdapterAddressIterator<'a> {
current: *const IP_ADAPTER_ADDRESSES_LH,
_phantom: std::marker::PhantomData<&'a IP_ADAPTER_ADDRESSES_LH>,
}
impl<'a> Iterator for AdapterAddressIterator<'a> {
type Item = AdapterAddress<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.current.is_null() {
return None;
}
let adapter = unsafe { &*self.current };
let result = AdapterAddress { raw: adapter };
self.current = adapter.Next;
Some(result)
}
}
pub struct AdapterAddress<'a> {
raw: &'a IP_ADAPTER_ADDRESSES_LH,
}
impl<'a> AdapterAddress<'a> {
pub fn adapter_name(&self) -> &str {
unsafe {
std::ffi::CStr::from_ptr(self.raw.AdapterName as *const i8)
.to_str()
.unwrap_or("<invalid>")
}
}
pub fn friendly_name(&self) -> OsString {
unsafe { crate::util::osstring_from_wide(self.raw.FriendlyName) }
}
pub fn description(&self) -> OsString {
unsafe { crate::util::osstring_from_wide(self.raw.Description) }
}
pub fn interface_luid(&self) -> Luid {
self.raw.Luid.into()
}
pub fn mtu(&self) -> u32 {
self.raw.Mtu
}
pub fn as_raw(&self) -> &IP_ADAPTER_ADDRESSES_LH {
self.raw
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_unicast_table() {
let table = get_unicast_ip_address_table(None).expect("Failed to get IP address table");
for address in table {
println!(
"Interface: {}, Family: {:?}",
address.interface_index(),
address.family()
);
}
}
}