use core::{
iter::FusedIterator,
net::{Ipv4Addr, Ipv6Addr},
};
use windows::Win32::{NetworkManagement::IpHelper, Networking::WinSock};
trait WinAddr {
fn sockaddr(&self) -> &WinSock::SOCKADDR;
fn next(&self) -> Option<&dyn WinAddr>;
fn prefix_len(&self) -> Option<u8>;
}
macro_rules! impl_winaddr {
($ty:ty) => {
impl_winaddr!($ty, |_| None);
};
($ty:ty, $pfx:expr) => {
impl WinAddr for $ty {
fn sockaddr(&self) -> &WinSock::SOCKADDR {
unsafe { self.Address.lpSockaddr.as_ref() }.unwrap()
}
fn next(&self) -> Option<&dyn WinAddr> {
unsafe { (self.Next as *const dyn WinAddr).as_ref() }
}
fn prefix_len(&self) -> Option<u8> {
($pfx)(self)
}
}
};
}
impl_winaddr!(
IpHelper::IP_ADAPTER_UNICAST_ADDRESS_LH,
(|slf: &IpHelper::IP_ADAPTER_UNICAST_ADDRESS_LH| Some(slf.OnLinkPrefixLength))
);
impl_winaddr!(IpHelper::IP_ADAPTER_UNICAST_ADDRESS_XP);
impl_winaddr!(
IpHelper::IP_ADAPTER_PREFIX_XP,
|slf: &IpHelper::IP_ADAPTER_PREFIX_XP| Some(slf.PrefixLength as u8)
);
pub struct AddrIter<'a> {
item: Option<&'a dyn WinAddr>,
}
macro_rules! iter_fn {
($name:ident, $field:ident) => {
#[doc = concat!("Helper to iterate over the addresses in [`IpHelper::IP_ADAPTER_ADDRESSES_LH::", stringify!($field), "`].")]
pub fn $name(addrs: &IpHelper::IP_ADAPTER_ADDRESSES_LH) -> AddrIter<'_> {
AddrIter {
item: unsafe { (addrs.$field as *const dyn WinAddr).as_ref() },
}
}
};
}
iter_fn!(iter_unicast, FirstUnicastAddress);
iter_fn!(iter_prefixes, FirstPrefix);
impl Iterator for AddrIter<'_> {
type Item = ipnet::IpNet;
fn next(&mut self) -> Option<Self::Item> {
loop {
let ret = self.item?;
let sa = ret.sockaddr();
self.item = ret.next();
match sa.sa_family {
WinSock::AF_INET => {
let sa = unsafe {
core::mem::transmute::<&WinSock::SOCKADDR, &WinSock::SOCKADDR_IN>(sa)
};
let bytes = unsafe { sa.sin_addr.S_un.S_un_b };
let ip = Ipv4Addr::new(bytes.s_b1, bytes.s_b2, bytes.s_b3, bytes.s_b4);
break Some(
ipnet::IpNet::new(ip.into(), ret.prefix_len().unwrap_or(32)).unwrap(),
);
}
WinSock::AF_INET6 => {
let sa = unsafe {
core::mem::transmute::<&WinSock::SOCKADDR, &WinSock::SOCKADDR_IN6>(sa)
};
let ip = Ipv6Addr::from_octets(unsafe { sa.sin6_addr.u.Byte });
break Some(
ipnet::IpNet::new(ip.into(), ret.prefix_len().unwrap_or(128)).unwrap(),
);
}
_unknown => {
continue;
}
}
}
}
}
impl FusedIterator for AddrIter<'_> {}
#[cfg(test)]
mod test {
use super::*;
use crate::{Family, FamilyOrBoth, windows::InterfaceReport};
#[test]
fn iter_report() {
for family in [FamilyOrBoth::Both, Family::Ipv4.into(), Family::Ipv6.into()] {
let report = InterfaceReport::get(family).unwrap();
for elem in report.iter() {
println!(
"{}/{}:",
unsafe { elem.AdapterName.to_string() }.unwrap(),
unsafe { elem.FriendlyName.to_string() }.unwrap()
);
print!("\tprefixes: ",);
for pfx in iter_prefixes(elem) {
print!("{pfx}, ");
}
print!("\n\tunicast: ");
for pfx in iter_unicast(elem) {
print!("{pfx}, ");
}
println!();
println!();
}
println!();
println!();
}
}
}