Skip to content

Commit 3833e41

Browse files
committed
Added 'get_mac_address_by_ip' which does what it says
Also added ./example/lookup_by_ip.rs which correctly outputs the MacAddress on my windows machine. Linux implementation in the next commit.
1 parent f2416eb commit 3833e41

File tree

3 files changed

+111
-1
lines changed

3 files changed

+111
-1
lines changed

examples/lookup_by_ip.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
use std::{net::IpAddr, str::FromStr};
2+
3+
use mac_address::get_mac_address_by_ip;
4+
5+
// UDP 'connect' to a remote IP (Google's DNS) and
6+
// then see which local IP address we were bound to.
7+
//
8+
// NOTE: this is a nice portable way to use the routing
9+
// table and sends no actual packets
10+
fn lookup_default_adapter_ip() -> std::io::Result<IpAddr> {
11+
let udp_sock = std::net::UdpSocket::bind(("0.0.0.0", 0))?;
12+
udp_sock.connect((IpAddr::from_str("8.8.8.8").unwrap(), 53))?;
13+
Ok(udp_sock.local_addr()?.ip())
14+
}
15+
16+
fn main() -> std::io::Result<()> {
17+
// find a useful IP local address to test against
18+
let local_ip = lookup_default_adapter_ip()?;
19+
// find the MacAddress of the Adapter with this IP
20+
match get_mac_address_by_ip(&local_ip) {
21+
Ok(Some(ma)) => {
22+
println!("MAC addr = {}", ma);
23+
println!("bytes = {:?}", ma.bytes());
24+
}
25+
Ok(None) => println!("No MAC address found."),
26+
Err(e) => println!("{:?}", e),
27+
}
28+
Ok(())
29+
}

src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ mod os;
2121
mod os;
2222

2323
mod iter;
24+
use std::net::IpAddr;
25+
2426
pub use iter::MacAddressIterator;
2527

2628
/// Possible errors when attempting to retrieve a MAC address.
@@ -128,6 +130,13 @@ pub fn name_by_mac_address(mac: &MacAddress) -> Result<Option<String>, MacAddres
128130
os::get_ifname(&mac.bytes)
129131
}
130132

133+
/// Attempts to look up the local MAC address by matching the Apapter's IP Address
134+
pub fn get_mac_address_by_ip(ip: &IpAddr) -> Result<Option<MacAddress>, MacAddressError> {
135+
let bytes = os::get_mac_address_by_ip(ip)?;
136+
137+
Ok(bytes.map(|b| MacAddress { bytes: b }))
138+
}
139+
131140
impl MacAddress {
132141
/// Returns the array of MAC address bytes.
133142
pub fn bytes(self) -> [u8; 6] {

src/windows.rs

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,88 @@ use std::{
22
convert::{TryFrom, TryInto},
33
ffi::CStr,
44
ffi::OsString,
5+
net::IpAddr,
56
os::windows::ffi::OsStringExt,
67
ptr, slice,
78
};
8-
use winapi::shared::{ntdef::ULONG, winerror::ERROR_SUCCESS, ws2def::AF_UNSPEC};
9+
use winapi::shared::{
10+
ntdef::ULONG,
11+
winerror::ERROR_SUCCESS,
12+
ws2def::{AF_INET, AF_INET6, AF_UNSPEC, SOCKADDR_IN},
13+
ws2ipdef::SOCKADDR_IN6,
14+
};
915
use winapi::um::{iphlpapi::GetAdaptersAddresses, iptypes::IP_ADAPTER_ADDRESSES_LH};
1016

1117
use crate::MacAddressError;
1218

1319
const GAA_FLAG_NONE: ULONG = 0x0000;
1420

21+
/// Similar to get_mac(); walk this list of adapters and in each
22+
/// adapter, walk the list of UnicastIpAddresses and return the mac address
23+
/// of the first one that matches the given IP
24+
pub(crate) fn get_mac_address_by_ip(ip: &IpAddr) -> Result<Option<[u8; 6]>, MacAddressError> {
25+
let adapters = get_adapters()?;
26+
// Safety: We don't use the pointer after `adapters` is dropped
27+
let mut ptr = unsafe { adapters.ptr() };
28+
29+
// for each adapter on the machine
30+
while !ptr.is_null() {
31+
let bytes = unsafe { convert_mac_bytes(ptr) };
32+
33+
let mut ip_ptr = unsafe { (*ptr).FirstUnicastAddress };
34+
// for each IP on the adapter
35+
while !ip_ptr.is_null() {
36+
let sock_addr = unsafe { (*ip_ptr).Address.lpSockaddr };
37+
let adapter_ip = match unsafe { (*sock_addr).sa_family } as i32 {
38+
AF_INET => unsafe {
39+
let addr = (*(sock_addr as *mut SOCKADDR_IN)).sin_addr;
40+
Some(IpAddr::from([
41+
addr.S_un.S_un_b().s_b1,
42+
addr.S_un.S_un_b().s_b2,
43+
addr.S_un.S_un_b().s_b3,
44+
addr.S_un.S_un_b().s_b4,
45+
]))
46+
},
47+
AF_INET6 => unsafe {
48+
let addr = (*(sock_addr as *mut SOCKADDR_IN6)).sin6_addr;
49+
Some(IpAddr::from(addr.u.Byte().clone()))
50+
},
51+
_ => {
52+
// ignore unknown address families; only support IPv4/IPv6
53+
None
54+
}
55+
};
56+
if let Some(adapter_ip) = adapter_ip {
57+
if adapter_ip == *ip {
58+
return Ok(Some(bytes));
59+
}
60+
}
61+
// Otherwise check the next IP on the adapter
62+
#[cfg(target_pointer_width = "32")]
63+
{
64+
ip_ptr = unsafe { ip_ptr.read_unaligned().Next };
65+
}
66+
67+
#[cfg(not(target_pointer_width = "32"))]
68+
{
69+
ip_ptr = unsafe { (*ip_ptr).Next };
70+
}
71+
}
72+
73+
// Otherwise go to the next adapter
74+
#[cfg(target_pointer_width = "32")]
75+
{
76+
ptr = unsafe { ptr.read_unaligned().Next };
77+
}
78+
79+
#[cfg(not(target_pointer_width = "32"))]
80+
{
81+
ptr = unsafe { (*ptr).Next };
82+
}
83+
}
84+
// All of the calls succeeded, just didn't find it...
85+
Ok(None)
86+
}
1587
/// Uses bindings to the `Iphlpapi.h` Windows header to fetch the interface
1688
/// devices list with
1789
/// [GetAdaptersAddresses][https://msdn.microsoft.com/en-us/library/windows/desktop/aa365915(v=vs.85).aspx]

0 commit comments

Comments
 (0)