From c2998eb000f7b88e8e61c0baff6cefe0621ba1dc Mon Sep 17 00:00:00 2001 From: staslock Date: Tue, 23 Apr 2024 18:17:26 +0200 Subject: [PATCH] feat(windows): group ip addresses to interfaces based on interface index On Linux, all ip addresses of each interface are stored in a single NetworkInterface structure. On windows each IP address was stored in a distinct NetworkInterface structure. With this change, windows output structure is now the same as on Linux - one NetworkInterface structure for each network interface. --- src/target/windows.rs | 228 +++++++++++++++++++++++++----------------- 1 file changed, 138 insertions(+), 90 deletions(-) diff --git a/src/target/windows.rs b/src/target/windows.rs index 4c68228..993cc62 100644 --- a/src/target/windows.rs +++ b/src/target/windows.rs @@ -1,8 +1,11 @@ use std::ffi::c_void; +use std::fmt::Pointer; use std::mem::size_of; use std::net::{Ipv4Addr, Ipv6Addr}; use std::ptr::null_mut; use std::slice::from_raw_parts; +use std::iter::Iterator; +use std::marker::PhantomData; use libc::{free, malloc, wchar_t, wcslen}; use winapi::{ @@ -16,18 +19,18 @@ use winapi::{ winerror, }, um::{ - iptypes::{IP_ADAPTER_ADDRESSES_LH, IP_ADAPTER_UNICAST_ADDRESS_LH}, + iptypes::{IP_ADAPTER_ADDRESSES, IP_ADAPTER_UNICAST_ADDRESS, IP_ADAPTER_PREFIX}, iphlpapi::GetAdaptersAddresses, }, }; use crate::utils::hex::HexSlice; use crate::utils::ffialloc::FFIAlloc; -use crate::{Error, NetworkInterface, NetworkInterfaceConfig, Result}; +use crate::{Addr, Error, NetworkInterface, NetworkInterfaceConfig, Result, V4IfAddr, V6IfAddr}; use crate::interface::Netmask; /// An alias for `IP_ADAPTER_ADDRESSES_LH` -type AdapterAddress = IP_ADAPTER_ADDRESSES_LH; +type AdapterAddress = IP_ADAPTER_ADDRESSES; /// A constant to store `winapi::shared::ws2def::AF_INET` casted as `u16` const AF_INET: u16 = winapi::shared::ws2def::AF_INET as u16; @@ -44,6 +47,24 @@ const GET_ADAPTERS_ADDRESSES_FAMILY: u32 = AF_UNSPEC as u32; /// A constant to store `winapi::um::iptypes::GAA_FLAG_INCLUDE_PREFIX` const GET_ADAPTERS_ADDRESSES_FLAGS: ULONG = winapi::um::iptypes::GAA_FLAG_INCLUDE_PREFIX; +type MacAddress = Option; +macro_rules! iterable_raw_pointer { + ($t: ty, $n: ident) => { + impl IterableRawPointer for $t { + type Pointer = *const $t; + type Value = $t; + + fn next(&self) -> Self::Pointer { + self.$n + } + } + }; +} + +iterable_raw_pointer!(IP_ADAPTER_ADDRESSES, Next); +iterable_raw_pointer!(IP_ADAPTER_UNICAST_ADDRESS, Next); +iterable_raw_pointer!(IP_ADAPTER_PREFIX, Next); + impl NetworkInterfaceConfig for NetworkInterface { fn show() -> Result> { // Allocate a 15 KB buffer to start with. @@ -96,70 +117,48 @@ impl NetworkInterfaceConfig for NetworkInterface { }?; // iterate over the contained structs - let mut adapter_address_it = adapter_address.as_ptr(); - let mut network_interfaces: Vec = Vec::new(); - - while !adapter_address_it.is_null() { - let address_name = make_adapter_address_name(adapter_address_it)?; - - let index = get_adapter_address_index(adapter_address_it)?; - - // see https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses#examples - let mac_addr_len = unsafe { (*adapter_address_it).PhysicalAddressLength } as _; - let mac_addr = match mac_addr_len { - 0 => None, - len => Some(format!( - "{}", - HexSlice::new(unsafe { &(*adapter_address_it).PhysicalAddress[..len] }) - )), + let mut network_interfaces = Vec::::new(); + + for adapter_address in RawPointerWrapper::new(adapter_address.as_ptr()) { + let name = make_adapter_address_name(adapter_address)?; + let index = get_adapter_address_index(adapter_address)?; + let mac_addr = make_mac_address(adapter_address); + let mut network_interface = NetworkInterface { + name, + addr: Vec::new(), + mac_addr, + index, }; - // Find interface addresses - let mut current_unicast_address = unsafe { (*adapter_address_it).FirstUnicastAddress }; - - while !current_unicast_address.is_null() { - let address = unsafe { (*current_unicast_address).Address }; - - match unsafe { (*address.lpSockaddr).sa_family } { - AF_INET => { - let sockaddr: *mut SOCKADDR_IN = address.lpSockaddr as *mut SOCKADDR_IN; - let addr = make_ipv4_addr(&sockaddr); - let netmask = make_ipv4_netmask(¤t_unicast_address); - let network_interface = NetworkInterface::new_afinet( - &address_name, - addr, - netmask, - lookup_ipv4_broadcast_addr(adapter_address_it, &sockaddr), - index, - ) - .with_mac_addr(mac_addr.clone()); - - network_interfaces.push(network_interface); - } - AF_INET6 => { - let sockaddr: *mut SOCKADDR_IN6 = address.lpSockaddr as *mut SOCKADDR_IN6; - let addr = make_ipv6_addr(&sockaddr)?; - let netmask = make_ipv6_netmask(&sockaddr); - let network_interface = NetworkInterface::new_afinet6( - &address_name, - addr, - netmask, - None, - index, - ) - .with_mac_addr(mac_addr.clone()); - - network_interfaces.push(network_interface); - } - _ => {} - } - - if !current_unicast_address.is_null() { - current_unicast_address = unsafe { (*current_unicast_address).Next }; - } + for current_unicast_address in + RawPointerWrapper::new(adapter_address.FirstUnicastAddress) + { + let address = current_unicast_address.Address; + + network_interface + .addr + .push(match unsafe { (*address.lpSockaddr).sa_family } { + AF_INET => { + let sockaddr = &unsafe { *(address.lpSockaddr as *const SOCKADDR_IN) }; + Addr::V4(V4IfAddr { + ip: make_ipv4_addr(sockaddr), + broadcast: lookup_ipv4_broadcast_addr(adapter_address, sockaddr), + netmask: make_ipv4_netmask(current_unicast_address), + }) + } + AF_INET6 => { + let sockaddr = &unsafe { *(address.lpSockaddr as *const SOCKADDR_IN6) }; + Addr::V6(V6IfAddr { + ip: make_ipv6_addr(sockaddr)?, + broadcast: None, + netmask: make_ipv6_netmask(sockaddr), + }) + } + _ => continue, + }); } - adapter_address_it = unsafe { (*adapter_address_it).Next }; + network_interfaces.push(network_interface); } Ok(network_interfaces) @@ -184,39 +183,35 @@ impl NetworkInterfaceConfig for NetworkInterface { // be the same, so we search for the unicast address in the prefix list, and // then the broadcast address is next in list. fn lookup_ipv4_broadcast_addr( - adapter_address: *const IP_ADAPTER_ADDRESSES_LH, - unicast_ip: &*mut SOCKADDR_IN, + adapter_address: &IP_ADAPTER_ADDRESSES, + unicast_ip: &SOCKADDR_IN, ) -> Option { - let mut prefix_address = unsafe { (*adapter_address).FirstPrefix }; let mut prefix_index_v4 = 0; let mut broadcast_index: Option = None; // Find adapter - while !prefix_address.is_null() { - let address = unsafe { (*prefix_address).Address }; + for prefix_address in RawPointerWrapper::new(adapter_address.FirstPrefix) { + let address = prefix_address.Address; if unsafe { (*address.lpSockaddr).sa_family } == AF_INET { + let sockaddr = &unsafe { *(address.lpSockaddr as *const SOCKADDR_IN) }; + if let Some(broadcast_index) = broadcast_index { if prefix_index_v4 == broadcast_index { - let sockaddr: *mut SOCKADDR_IN = address.lpSockaddr as *mut SOCKADDR_IN; - return Some(make_ipv4_addr(&sockaddr)); + return Some(make_ipv4_addr(sockaddr)); } - } else if prefix_index_v4 % 3 == 1 - && ipv4_addr_equal(&(address.lpSockaddr as *mut SOCKADDR_IN), unicast_ip) - { + } else if prefix_index_v4 % 3 == 1 && ipv4_addr_equal(sockaddr, unicast_ip) { broadcast_index = Some(prefix_index_v4 + 1); } prefix_index_v4 += 1; } - - prefix_address = unsafe { (*prefix_address).Next }; } None } /// Retrieves the network interface name -fn make_adapter_address_name(adapter_address: *const AdapterAddress) -> Result { - let address_name = unsafe { (*adapter_address).FriendlyName }; +fn make_adapter_address_name(adapter_address: &AdapterAddress) -> Result { + let address_name = adapter_address.FriendlyName; let address_name_length = unsafe { wcslen(address_name as *const wchar_t) }; let byte_slice = unsafe { from_raw_parts(address_name, address_name_length) }; let string = String::from_utf16(byte_slice).map_err(Error::from)?; @@ -225,16 +220,16 @@ fn make_adapter_address_name(adapter_address: *const AdapterAddress) -> Result Result { - let address_bytes = unsafe { (*(*sockaddr)).sin6_addr.u.Byte() }; +fn make_ipv6_addr(sockaddr: &SOCKADDR_IN6) -> Result { + let address_bytes = unsafe { sockaddr.sin6_addr.u.Byte() }; let ip = Ipv6Addr::from(*address_bytes); Ok(ip) } /// Creates a `Ipv4Addr` from a `SOCKADDR_IN` -fn make_ipv4_addr(sockaddr: &*mut SOCKADDR_IN) -> Ipv4Addr { - let address = unsafe { (*(*sockaddr)).sin_addr.S_un.S_addr() }; +fn make_ipv4_addr(sockaddr: &SOCKADDR_IN) -> Ipv4Addr { + let address = unsafe { sockaddr.sin_addr.S_un.S_addr() }; if cfg!(target_endian = "little") { // due to a difference on how bytes are arranged on a @@ -248,10 +243,10 @@ fn make_ipv4_addr(sockaddr: &*mut SOCKADDR_IN) -> Ipv4Addr { Ipv4Addr::from(*address) } -/// Compare 2 ipv4 addresses. Caller must ensure pointers are non-null. -fn ipv4_addr_equal(sockaddr1: &*mut SOCKADDR_IN, sockaddr2: &*mut SOCKADDR_IN) -> bool { - let address1 = unsafe { (*(*sockaddr1)).sin_addr.S_un.S_addr() }; - let address2 = unsafe { (*(*sockaddr2)).sin_addr.S_un.S_addr() }; +/// Compare 2 ipv4 addresses. +fn ipv4_addr_equal(sockaddr1: &SOCKADDR_IN, sockaddr2: &SOCKADDR_IN) -> bool { + let address1 = unsafe { sockaddr1.sin_addr.S_un.S_addr() }; + let address2 = unsafe { sockaddr2.sin_addr.S_un.S_addr() }; address1 == address2 } @@ -260,9 +255,9 @@ fn ipv4_addr_equal(sockaddr1: &*mut SOCKADDR_IN, sockaddr2: &*mut SOCKADDR_IN) - /// /// An implementation of `GetIpAddrTable` to get all available network interfaces would be required /// in order to support previous versions of Windows. -fn make_ipv4_netmask(unicast_address: &*mut IP_ADAPTER_UNICAST_ADDRESS_LH) -> Netmask { +fn make_ipv4_netmask(unicast_address: &IP_ADAPTER_UNICAST_ADDRESS) -> Netmask { let mut mask: c_ulong = 0; - let on_link_prefix_length = unsafe { (*(*unicast_address)).OnLinkPrefixLength }; + let on_link_prefix_length = unicast_address.OnLinkPrefixLength; unsafe { ConvertLengthToIpv4Mask(on_link_prefix_length as u32, &mut mask as *mut c_ulong); } @@ -279,12 +274,25 @@ fn make_ipv4_netmask(unicast_address: &*mut IP_ADAPTER_UNICAST_ADDRESS_LH) -> Ne Some(Ipv4Addr::from(mask)) } -fn make_ipv6_netmask(_sockaddr: &*mut SOCKADDR_IN6) -> Netmask { +fn make_ipv6_netmask(_sockaddr: &SOCKADDR_IN6) -> Netmask { None } -fn get_adapter_address_index(adapter_address: *const AdapterAddress) -> Result { - let adapter_luid = &unsafe { (*adapter_address).Luid } as *const IF_LUID; +/// Creates MacAddress from AdapterAddress +fn make_mac_address(adapter_address: &AdapterAddress) -> MacAddress { + // see https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses#examples + let mac_addr_len = adapter_address.PhysicalAddressLength as usize; + match mac_addr_len { + 0 => None, + len => Some(format!( + "{}", + HexSlice::new(&adapter_address.PhysicalAddress[..len]) + )), + } +} + +fn get_adapter_address_index(adapter_address: &AdapterAddress) -> Result { + let adapter_luid = &adapter_address.Luid as *const IF_LUID; let index = &mut 0u32 as *mut u32; @@ -297,6 +305,46 @@ fn get_adapter_address_index(adapter_address: *const AdapterAddress) -> Result Self::Pointer; +} + +/// Raw pointer container +struct RawPointerWrapper<'a, T>(*const T, PhantomData<&'a T>) +where + T: IterableRawPointer; + +impl<'a, T> RawPointerWrapper<'a, T> +where + T: IterableRawPointer, +{ + fn new(ptr: *const T) -> RawPointerWrapper<'a, T> { + Self(ptr, PhantomData) + } +} + +/// Iterator implementation for RawPointer +impl<'a, T> Iterator for RawPointerWrapper<'a, T> +where + T: IterableRawPointer, +{ + type Item = &'a T::Value; + + fn next(&mut self) -> Option { + let ret = unsafe { self.0.as_ref() }; + if let Some(v) = ret { + self.0 = v.next(); + } + ret + } +} + #[cfg(test)] mod tests { use std::{process::Command, cmp::min};