diff --git a/src/lib.rs b/src/lib.rs
index e5b56d5..c9c7bad 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -21,6 +21,8 @@ mod os;
mod os;
mod iter;
+use std::net::IpAddr;
+
pub use iter::MacAddressIterator;
/// Possible errors when attempting to retrieve a MAC address.
@@ -128,6 +130,13 @@ pub fn name_by_mac_address(mac: &MacAddress) -> Result, MacAddres
os::get_ifname(&mac.bytes)
}
+/// Attempts to look up the local MAC address by matching the Apapter's IP Address
+pub fn get_mac_address_by_ip(ip: &IpAddr) -> Result , MacAddressError> {
+ let bytes = os::get_mac_address_by_ip(ip)?;
+
+ Ok(bytes.map(|b| MacAddress { bytes: b }))
+}
+
impl MacAddress {
/// Returns the array of MAC address bytes.
pub fn bytes(self) -> [u8; 6] {
diff --git a/src/windows.rs b/src/windows.rs
index 521517e..7faaa74 100644
--- a/src/windows.rs
+++ b/src/windows.rs
@@ -2,16 +2,88 @@ use std::{
convert::{TryFrom, TryInto},
ffi::CStr,
ffi::OsString,
+ net::IpAddr,
os::windows::ffi::OsStringExt,
ptr, slice,
};
-use winapi::shared::{ntdef::ULONG, winerror::ERROR_SUCCESS, ws2def::AF_UNSPEC};
+use winapi::shared::{
+ ntdef::ULONG,
+ winerror::ERROR_SUCCESS,
+ ws2def::{AF_INET, AF_INET6, AF_UNSPEC, SOCKADDR_IN},
+ ws2ipdef::SOCKADDR_IN6,
+};
use winapi::um::{iphlpapi::GetAdaptersAddresses, iptypes::IP_ADAPTER_ADDRESSES_LH};
use crate::MacAddressError;
const GAA_FLAG_NONE: ULONG = 0x0000;
+/// Similar to get_mac(); walk this list of adapters and in each
+/// adapter, walk the list of UnicastIpAddresses and return the mac address
+/// of the first one that matches the given IP
+pub(crate) fn get_mac_address_by_ip(ip: &IpAddr) -> Result , MacAddressError> {
+ let adapters = get_adapters()?;
+ // Safety: We don't use the pointer after `adapters` is dropped
+ let mut ptr = unsafe { adapters.ptr() };
+
+ // for each adapter on the machine
+ while !ptr.is_null() {
+ let bytes = unsafe { convert_mac_bytes(ptr) };
+
+ let mut ip_ptr = unsafe { (*ptr).FirstUnicastAddress };
+ // for each IP on the adapter
+ while !ip_ptr.is_null() {
+ let sock_addr = unsafe { (*ip_ptr).Address.lpSockaddr };
+ let adapter_ip = match unsafe { (*sock_addr).sa_family } as i32 {
+ AF_INET => unsafe {
+ let addr = (*(sock_addr as *mut SOCKADDR_IN)).sin_addr;
+ Some(IpAddr::from([
+ addr.S_un.S_un_b().s_b1,
+ addr.S_un.S_un_b().s_b2,
+ addr.S_un.S_un_b().s_b3,
+ addr.S_un.S_un_b().s_b4,
+ ]))
+ },
+ AF_INET6 => unsafe {
+ let addr = (*(sock_addr as *mut SOCKADDR_IN6)).sin6_addr;
+ Some(IpAddr::from(addr.u.Byte().clone()))
+ },
+ _ => {
+ // ignore unknown address families; only support IPv4/IPv6
+ None
+ }
+ };
+ if let Some(adapter_ip) = adapter_ip {
+ if adapter_ip == *ip {
+ return Ok(Some(bytes));
+ }
+ }
+ // Otherwise check the next IP on the adapter
+ #[cfg(target_pointer_width = "32")]
+ {
+ ip_ptr = unsafe { ip_ptr.read_unaligned().Next };
+ }
+
+ #[cfg(not(target_pointer_width = "32"))]
+ {
+ ip_ptr = unsafe { (*ip_ptr).Next };
+ }
+ }
+
+ // Otherwise go to the next adapter
+ #[cfg(target_pointer_width = "32")]
+ {
+ ptr = unsafe { ptr.read_unaligned().Next };
+ }
+
+ #[cfg(not(target_pointer_width = "32"))]
+ {
+ ptr = unsafe { (*ptr).Next };
+ }
+ }
+ // All of the calls succeeded, just didn't find it...
+ Ok(None)
+}
/// Uses bindings to the `Iphlpapi.h` Windows header to fetch the interface
/// devices list with
/// [GetAdaptersAddresses][https://msdn.microsoft.com/en-us/library/windows/desktop/aa365915(v=vs.85).aspx]