Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
capveg-netdebug committed Jan 11, 2024
2 parents 7fb63b0 + 3833e41 commit a6e4143
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 1 deletion.
9 changes: 9 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -128,6 +130,13 @@ pub fn name_by_mac_address(mac: &MacAddress) -> Result<Option<String>, 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<Option<MacAddress>, 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] {
Expand Down
74 changes: 73 additions & 1 deletion src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Option<[u8; 6]>, 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]
Expand Down

0 comments on commit a6e4143

Please sign in to comment.