use core::convert::TryInto;
use std::{ffi::OsString, os::windows::ffi::OsStringExt, ptr, slice};
use winapi::shared::{ntdef::ULONG, winerror::ERROR_SUCCESS, ws2def::AF_UNSPEC};
use winapi::um::{
iphlpapi::GetAdaptersAddresses,
iptypes::{IP_ADAPTER_ADDRESSES_LH, PIP_ADAPTER_ADDRESSES},
};
use crate::MacAddressError;
const GAA_FLAG_NONE: ULONG = 0x0000;
pub fn get_mac(name: Option<&str>) -> Result<Option<[u8; 6]>, MacAddressError> {
let mut adapters = get_adapters()?;
let mut ptr = adapters.as_mut_ptr() as PIP_ADAPTER_ADDRESSES;
loop {
if ptr.is_null() {
break;
}
let bytes = unsafe { convert_mac_bytes(ptr) };
if let Some(name) = name {
let adapter_name = unsafe { construct_string((*ptr).FriendlyName) };
if adapter_name == name {
return Ok(Some(bytes));
}
} else if bytes.iter().any(|&x| x != 0) {
return Ok(Some(bytes));
}
ptr = unsafe { (*ptr).Next };
}
Ok(None)
}
pub fn get_ifname(mac: &[u8; 6]) -> Result<Option<String>, MacAddressError> {
let mut adapters = get_adapters()?;
let mut ptr = adapters.as_mut_ptr() as PIP_ADAPTER_ADDRESSES;
loop {
if ptr.is_null() {
break;
}
let bytes = unsafe { convert_mac_bytes(ptr) };
if &bytes == mac {
let adapter_name = unsafe { construct_string((*ptr).FriendlyName) };
let adapter_name = adapter_name
.into_string()
.map_err(|_| MacAddressError::InternalError)?;
return Ok(Some(adapter_name));
}
ptr = unsafe { (*ptr).Next };
}
Ok(None)
}
pub(crate) unsafe fn convert_mac_bytes(ptr: *mut IP_ADAPTER_ADDRESSES_LH) -> [u8; 6] {
((*ptr).PhysicalAddress)[..6].try_into().unwrap()
}
pub(crate) fn get_adapters() -> Result<Vec<u8>, MacAddressError> {
let mut buf_len = 0;
unsafe {
GetAdaptersAddresses(
AF_UNSPEC as u32,
GAA_FLAG_NONE,
ptr::null_mut(),
ptr::null_mut(),
&mut buf_len,
);
}
let mut adapters_list = vec![0u8; buf_len as usize];
let adapter_addresses: PIP_ADAPTER_ADDRESSES = adapters_list.as_mut_ptr() as *mut _;
let result = unsafe {
GetAdaptersAddresses(
AF_UNSPEC as u32,
GAA_FLAG_NONE,
ptr::null_mut(),
adapter_addresses as *mut _,
&mut buf_len,
)
};
if result != ERROR_SUCCESS {
return Err(MacAddressError::InternalError);
}
Ok(adapters_list)
}
unsafe fn construct_string(ptr: *mut u16) -> OsString {
let slice = slice::from_raw_parts(ptr, get_null_position(ptr));
OsStringExt::from_wide(slice)
}
unsafe fn get_null_position(ptr: *mut u16) -> usize {
assert!(!ptr.is_null());
for i in 0.. {
if *ptr.offset(i) == 0 {
return i as usize;
}
}
unreachable!()
}