Skip to content

Instantly share code, notes, and snippets.

@ssrlive
Last active September 2, 2023 03:33
Show Gist options
  • Save ssrlive/4297e39057e6d9b90923ef9069d44ab8 to your computer and use it in GitHub Desktop.
Save ssrlive/4297e39057e6d9b90923ef9069d44ab8 to your computer and use it in GitHub Desktop.
GetInterfaceInfo function (iphlpapi.h)
//
// https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getinterfaceinfo#examples
//
// [target.'cfg(target_os="windows")'.dependencies]
// windows = { version = "0.51", features = [
// "Win32_NetworkManagement_IpHelper",
// "Win32_NetworkManagement_Ndis",
// "Win32_Networking_WinSock",
// "Win32_Foundation",
// ] }
use std::{
convert::TryInto,
mem,
ptr::{self, addr_of},
slice,
};
use windows::{
Win32::Foundation::*,
Win32::NetworkManagement::IpHelper::{GetInterfaceInfo, IP_ADAPTER_INDEX_MAP, IP_INTERFACE_INFO},
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let adapters = get_interface_info()?;
println!("Num adapters: {}", adapters.len());
for adapter in adapters {
println!("Adapter index: {}\tAdapter name: {}", adapter.index, adapter.name);
}
Ok(())
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct IpAdapterIndexMap {
pub index: usize,
pub name: String,
}
pub struct RawAllocator {
layout: std::alloc::Layout,
base_ptr: *mut u8,
}
impl RawAllocator {
pub fn new<T>(len: usize) -> Result<Self, Box<dyn std::error::Error>> {
let layout = std::alloc::Layout::from_size_align(len, mem::align_of::<T>())?;
use std::alloc::GlobalAlloc;
let base_ptr = unsafe { std::alloc::System.alloc(layout) };
Ok(Self { layout, base_ptr })
}
pub fn as_ptr<T>(&self) -> *mut T {
self.base_ptr.cast()
}
}
impl Drop for RawAllocator {
fn drop(&mut self) {
unsafe {
use std::alloc::GlobalAlloc;
std::alloc::System.dealloc(self.base_ptr, self.layout);
}
}
}
pub fn get_interface_info() -> Result<Vec<IpAdapterIndexMap>, Box<dyn std::error::Error>> {
unsafe {
// Perform the first call to know how many bytes to allocate
let mut raw_buf_len = 0;
let ret_val = GetInterfaceInfo(Some(ptr::null_mut()), &mut raw_buf_len);
if ret_val != ERROR_INSUFFICIENT_BUFFER.0 {
return Err(Box::new(std::io::Error::new(
std::io::ErrorKind::Other,
format!("GetInterfaceInfo: Expected to get the required buffer size, was {ret_val:?}"),
)));
}
// Allocate an appropriately sized *and aligned* buffer to store the result
let buf_len = raw_buf_len.try_into()?;
let allocator = RawAllocator::new::<IP_INTERFACE_INFO>(buf_len)?;
let ip_interface_info = allocator.as_ptr();
// Perform the second call to get the data
let ret_val = GetInterfaceInfo(Some(ip_interface_info), &mut raw_buf_len);
if ret_val != NO_ERROR.0 {
return Err(Box::new(std::io::Error::new(
std::io::ErrorKind::Other,
format!("GetInterfaceInfo: Could not get the data on the second call {ret_val:?}"),
)));
}
// Construct a pointer to the adapter array that preserves the provenance of the original pointer
let adapter_ptr = addr_of!((*ip_interface_info).Adapter);
let adapter_ptr = adapter_ptr.cast::<IP_ADAPTER_INDEX_MAP>();
// Combine the pointer and length into a Rust slice
let n_adapters = (*ip_interface_info).NumAdapters;
let n_adapters = n_adapters.try_into()?;
let adapters = slice::from_raw_parts(adapter_ptr, n_adapters);
let mut result = vec![];
for adapter in adapters {
let IP_ADAPTER_INDEX_MAP {
Index: index,
Name: name,
} = adapter;
// The fixed-size buffer contains data after the UTF-16 NUL character
let name_end = name.iter().position(|&c| c == 0).unwrap_or(name.len());
let name = String::from_utf16_lossy(&name[..name_end]);
result.push(IpAdapterIndexMap {
index: usize::try_from(*index)?,
name,
});
}
Ok(result)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment