Skip to content

Commit

Permalink
feat(windows): group ip addresses to interfaces based on interface in…
Browse files Browse the repository at this point in the history
…dex (#55)
  • Loading branch information
scanban authored Apr 24, 2024
1 parent 962fdf8 commit bc89f7c
Showing 1 changed file with 140 additions and 91 deletions.
231 changes: 140 additions & 91 deletions src/target/windows.rs
Original file line number Diff line number Diff line change
@@ -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::{
Expand All @@ -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;
/// An alias for `IP_ADAPTER_ADDRESSES`
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;
Expand All @@ -44,6 +47,25 @@ 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<String>;

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<Vec<NetworkInterface>> {
// Allocate a 15 KB buffer to start with.
Expand Down Expand Up @@ -96,70 +118,48 @@ impl NetworkInterfaceConfig for NetworkInterface {
}?;

// iterate over the contained structs
let mut adapter_address_it = adapter_address.as_ptr();
let mut network_interfaces: Vec<NetworkInterface> = 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::<NetworkInterface>::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(&current_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)
Expand All @@ -184,39 +184,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<Ipv4Addr> {
let mut prefix_address = unsafe { (*adapter_address).FirstPrefix };
let mut prefix_index_v4 = 0;
let mut broadcast_index: Option<i32> = 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<String> {
let address_name = unsafe { (*adapter_address).FriendlyName };
fn make_adapter_address_name(adapter_address: &AdapterAddress) -> Result<String> {
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)?;
Expand All @@ -225,16 +221,16 @@ fn make_adapter_address_name(adapter_address: *const AdapterAddress) -> Result<S
}

/// Creates a `Ipv6Addr` from a `SOCKADDR_IN6`
fn make_ipv6_addr(sockaddr: &*mut SOCKADDR_IN6) -> Result<Ipv6Addr> {
let address_bytes = unsafe { (*(*sockaddr)).sin6_addr.u.Byte() };
fn make_ipv6_addr(sockaddr: &SOCKADDR_IN6) -> Result<Ipv6Addr> {
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
Expand All @@ -248,10 +244,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
}

Expand All @@ -260,9 +256,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<Ipv4Addr> {
fn make_ipv4_netmask(unicast_address: &IP_ADAPTER_UNICAST_ADDRESS) -> Netmask<Ipv4Addr> {
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);
}
Expand All @@ -279,12 +275,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<Ipv6Addr> {
fn make_ipv6_netmask(_sockaddr: &SOCKADDR_IN6) -> Netmask<Ipv6Addr> {
None
}

fn get_adapter_address_index(adapter_address: *const AdapterAddress) -> Result<u32> {
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<u32> {
let adapter_luid = &adapter_address.Luid as *const IF_LUID;

let index = &mut 0u32 as *mut u32;

Expand All @@ -297,6 +306,46 @@ fn get_adapter_address_index(adapter_address: *const AdapterAddress) -> Result<u
}
}

/// Trait for linked lists in Windows API structures iteration
trait IterableRawPointer {
type Pointer;
type Value;

/// Returns: pointer to the next element in the linked list
/// null at the end
fn next(&self) -> Self::Pointer;
}

/// Raw pointer container
struct RawPointerWrapper<'a, T>(*const T, PhantomData<&'a T>)
where
T: IterableRawPointer<Value = T, Pointer = *const T>;

impl<'a, T> RawPointerWrapper<'a, T>
where
T: IterableRawPointer<Value = T, Pointer = *const T>,
{
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<Value = T, Pointer = *const T>,
{
type Item = &'a T::Value;

fn next(&mut self) -> Option<Self::Item> {
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};
Expand Down

0 comments on commit bc89f7c

Please sign in to comment.