use std::{
ffi::CStr,
io::{Error, Result},
net::IpAddr,
ptr, slice,
};
use windows::Win32::{
Foundation::NO_ERROR,
NetworkManagement::{
IpHelper::{
if_indextoname, FreeMibTable, GetBestInterfaceEx, GetIpInterfaceTable,
MIB_IPINTERFACE_ROW, MIB_IPINTERFACE_TABLE,
},
Ndis::IF_MAX_STRING_SIZE,
},
Networking::WinSock::{
AF_INET, AF_INET6, IN6_ADDR, IN6_ADDR_0, IN_ADDR, IN_ADDR_0, SOCKADDR, SOCKADDR_IN,
SOCKADDR_IN6, SOCKADDR_INET,
},
};
use crate::default_err;
struct MibTablePtr(*mut MIB_IPINTERFACE_TABLE);
impl MibTablePtr {
fn mut_ptr_ptr(&mut self) -> *mut *mut MIB_IPINTERFACE_TABLE {
ptr::from_mut(&mut self.0)
}
}
impl Default for MibTablePtr {
fn default() -> Self {
Self(ptr::null_mut())
}
}
impl Drop for MibTablePtr {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe {
FreeMibTable(self.0.cast());
}
}
}
}
pub fn interface_and_mtu_impl(remote: IpAddr) -> Result<(String, usize)> {
let dst = match remote {
IpAddr::V4(ip) => {
SOCKADDR_INET {
Ipv4: SOCKADDR_IN {
sin_family: AF_INET,
sin_addr: IN_ADDR {
S_un: IN_ADDR_0 {
S_addr: u32::to_be(ip.into()),
},
},
..Default::default()
},
}
}
IpAddr::V6(ip) => {
SOCKADDR_INET {
Ipv6: SOCKADDR_IN6 {
sin6_family: AF_INET6,
sin6_addr: IN6_ADDR {
u: IN6_ADDR_0 { Byte: ip.octets() },
},
..Default::default()
},
}
}
};
let mut idx = 0;
let res = unsafe {
GetBestInterfaceEx(
ptr::from_ref(&dst).cast::<SOCKADDR>(),
ptr::from_mut(&mut idx),
)
};
if res != 0 {
return Err(Error::last_os_error());
}
let mut if_table = MibTablePtr::default();
let family = if remote.is_ipv4() { AF_INET } else { AF_INET6 };
if unsafe { GetIpInterfaceTable(family, if_table.mut_ptr_ptr()) } != NO_ERROR {
return Err(Error::last_os_error());
}
let ifaces = unsafe {
slice::from_raw_parts::<MIB_IPINTERFACE_ROW>(
&(*if_table.0).Table[0],
(*if_table.0).NumEntries as usize,
)
};
for iface in ifaces {
if iface.InterfaceIndex == idx {
let mtu: usize = iface.NlMtu.try_into().map_err(|_| default_err())?;
let mut interfacename = [0u8; IF_MAX_STRING_SIZE as usize];
if unsafe { if_indextoname(iface.InterfaceIndex, &mut interfacename).is_null() } {
return Err(default_err());
}
let name = CStr::from_bytes_until_nul(interfacename.as_ref())
.map_err(|_| default_err())?
.to_str()
.map_err(Error::other)?
.to_string();
return Ok((name, mtu));
}
}
Err(default_err())
}