Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
+33
View File
@@ -0,0 +1,33 @@
use crate::{MacAddress, MacAddressError};
use nix::ifaddrs;
/// An iterator over all available MAC addresses on the system.
pub struct MacAddressIterator {
iter: std::iter::FilterMap<
ifaddrs::InterfaceAddressIterator,
fn(ifaddrs::InterfaceAddress) -> Option<MacAddress>,
>,
}
impl MacAddressIterator {
/// Creates a new `MacAddressIterator`.
pub fn new() -> Result<MacAddressIterator, MacAddressError> {
Ok(Self {
iter: ifaddrs::getifaddrs()?.filter_map(filter_macs),
})
}
}
fn filter_macs(intf: ifaddrs::InterfaceAddress) -> Option<MacAddress> {
intf.address?
.as_link_addr()
.and_then(|link| link.addr().map(MacAddress::new))
}
impl Iterator for MacAddressIterator {
type Item = MacAddress;
fn next(&mut self) -> Option<MacAddress> {
self.iter.next()
}
}
+17
View File
@@ -0,0 +1,17 @@
#[cfg(target_os = "windows")]
#[path = "windows.rs"]
mod internal;
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "android",
target_os = "illumos",
))]
#[path = "linux.rs"]
mod internal;
pub use internal::MacAddressIterator;
+47
View File
@@ -0,0 +1,47 @@
use crate::os;
use crate::{MacAddress, MacAddressError};
use winapi::um::iptypes::PIP_ADAPTER_ADDRESSES;
/// An iterator over all available MAC addresses on the system.
pub struct MacAddressIterator {
// So we don't UAF during iteration.
_buffer: os::AdaptersList,
ptr: PIP_ADAPTER_ADDRESSES,
}
impl MacAddressIterator {
/// Creates a new `MacAddressIterator`.
pub fn new() -> Result<MacAddressIterator, MacAddressError> {
let adapters = os::get_adapters()?;
let ptr = unsafe { adapters.ptr() };
Ok(Self {
_buffer: adapters,
ptr,
})
}
}
impl Iterator for MacAddressIterator {
type Item = MacAddress;
fn next(&mut self) -> Option<MacAddress> {
if self.ptr.is_null() {
None
} else {
let bytes = unsafe { os::convert_mac_bytes(self.ptr) };
#[cfg(target_pointer_width = "32")]
{
self.ptr = unsafe { self.ptr.read_unaligned().Next };
}
#[cfg(not(target_pointer_width = "32"))]
{
self.ptr = unsafe { (*self.ptr).Next };
}
Some(MacAddress::new(bytes))
}
}
}
+333
View File
@@ -0,0 +1,333 @@
//! `mac_address` provides a cross platform way to retrieve the MAC address of
//! network hardware. See [the Wikipedia
//! entry](https://en.wikipedia.org/wiki/MAC_address) for more information.
//!
//! Supported platforms: Linux, Windows, MacOS, FreeBSD, NetBSD
#![deny(missing_docs)]
#[cfg(target_os = "windows")]
#[path = "windows.rs"]
mod os;
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "android",
target_os = "illumos",
))]
#[path = "linux.rs"]
mod os;
mod iter;
pub use iter::MacAddressIterator;
/// Possible errors when attempting to retrieve a MAC address.
///
/// Eventually will expose more detailed error information.
#[derive(Debug)]
pub enum MacAddressError {
/// Signifies an internal API error has occurred.
InternalError,
}
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "android",
target_os = "illumos",
))]
impl From<nix::Error> for MacAddressError {
fn from(_: nix::Error) -> MacAddressError {
MacAddressError::InternalError
}
}
impl std::fmt::Display for MacAddressError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str(match self {
MacAddressError::InternalError => "Internal API error",
})
}
}
impl std::error::Error for MacAddressError {}
/// An error that may occur when parsing a MAC address string.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum MacParseError {
/// Parsing of the MAC address contained an invalid digit.
InvalidDigit,
/// The MAC address did not have the correct length.
InvalidLength,
}
impl std::fmt::Display for MacParseError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.write_str(match *self {
MacParseError::InvalidDigit => "invalid digit",
MacParseError::InvalidLength => "invalid length",
})
}
}
impl std::error::Error for MacParseError {}
impl From<core::num::ParseIntError> for MacParseError {
fn from(_: core::num::ParseIntError) -> Self {
MacParseError::InvalidDigit
}
}
/// Contains the individual bytes of the MAC address.
#[derive(Debug, Clone, Copy, PartialEq, Default, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(try_from = "std::borrow::Cow<'_, str>"))]
pub struct MacAddress {
bytes: [u8; 6],
}
impl MacAddress {
/// Creates a new `MacAddress` struct from the given bytes.
pub fn new(bytes: [u8; 6]) -> MacAddress {
MacAddress { bytes }
}
}
impl From<[u8; 6]> for MacAddress {
fn from(v: [u8; 6]) -> Self {
MacAddress::new(v)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for MacAddress {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.collect_str(self)
}
}
/// Calls the OS-specific function for retrieving the MAC address of the first
/// network device containing one, ignoring local-loopback.
pub fn get_mac_address() -> Result<Option<MacAddress>, MacAddressError> {
let bytes = os::get_mac(None)?;
Ok(bytes.map(|b| MacAddress { bytes: b }))
}
/// Attempts to look up the MAC address of an interface via the specified name.
/// **NOTE**: On Windows, this uses the `FriendlyName` field of the adapter, which
/// is the same name shown in the "Network Connections" Control Panel screen.
pub fn mac_address_by_name(name: &str) -> Result<Option<MacAddress>, MacAddressError> {
let bytes = os::get_mac(Some(name))?;
Ok(bytes.map(|b| MacAddress { bytes: b }))
}
/// Attempts to look up the interface name via MAC address.
pub fn name_by_mac_address(mac: &MacAddress) -> Result<Option<String>, MacAddressError> {
os::get_ifname(&mac.bytes)
}
impl MacAddress {
/// Returns the array of MAC address bytes.
pub fn bytes(self) -> [u8; 6] {
self.bytes
}
}
impl std::str::FromStr for MacAddress {
type Err = MacParseError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
let mut array = [0u8; 6];
// expect the `str` to be ASCII since it'll probably fail to parse
// anyway, this also asserts that each character in the string is only
// one byte in length which is necessary for the `match` below
if !input.is_ascii() {
// kind of hacky, but without `#[non_exhaustive]` on `MacParseError`
// adding a new variant is technically a breaking change, ugh...
return Err(MacParseError::InvalidLength);
}
match input.len() {
// MAC address with separators, e.g. 00:11:22:33:44:55
17 => {
array
.iter_mut()
.zip(input.split(|c| c == ':' || c == '-'))
.try_for_each::<_, Result<(), MacParseError>>(|(b, s)| {
*b = u8::from_str_radix(s, 16)?;
Ok(())
})?;
}
// MAC address without separators, e.g. 001122334455
12 => {
array
.iter_mut()
.zip((0..6).map(|i| &input[i * 2..=i * 2 + 1]))
.try_for_each::<_, Result<(), MacParseError>>(|(b, s)| {
*b = u8::from_str_radix(s, 16)?;
Ok(())
})?;
}
_ => return Err(MacParseError::InvalidLength),
}
Ok(MacAddress::new(array))
}
}
impl std::convert::TryFrom<&'_ str> for MacAddress {
type Error = MacParseError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
value.parse()
}
}
impl std::convert::TryFrom<std::borrow::Cow<'_, str>> for MacAddress {
type Error = MacParseError;
fn try_from(value: std::borrow::Cow<'_, str>) -> Result<Self, Self::Error> {
value.parse()
}
}
impl std::fmt::Display for MacAddress {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let _ = write!(
f,
"{:<02X}:{:<02X}:{:<02X}:{:<02X}:{:<02X}:{:<02X}",
self.bytes[0],
self.bytes[1],
self.bytes[2],
self.bytes[3],
self.bytes[4],
self.bytes[5]
);
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_str_colon() {
let string = "80:FA:5B:41:10:6B";
let address = string.parse::<MacAddress>().unwrap();
assert_eq!(address.bytes(), [128, 250, 91, 65, 16, 107]);
assert_eq!(&format!("{}", address), string);
}
#[test]
fn parse_str_hyphen() {
let string = "01-23-45-67-89-AB";
let address = string.parse::<MacAddress>().unwrap();
assert_eq!(address.bytes(), [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB]);
assert_eq!(format!("{}", address), string.replace('-', ":"));
}
#[test]
fn parse_str_no_sep() {
let string = "4827e24425d8";
let address = string.parse::<MacAddress>().unwrap();
assert_eq!(address.bytes(), [0x48, 0x27, 0xE2, 0x44, 0x25, 0xD8]);
}
#[test]
fn parse_invalid_length() {
let string = "80:FA:5B:41:10:6B:AC";
let address = string.parse::<MacAddress>().unwrap_err();
assert_eq!(MacParseError::InvalidLength, address);
let string = "80:FA:5B:41";
let address = string.parse::<MacAddress>().unwrap_err();
assert_eq!(MacParseError::InvalidLength, address);
let string = "80FA5B41";
let address = string.parse::<MacAddress>().unwrap_err();
assert_eq!(MacParseError::InvalidLength, address);
let string = "80:FÁ:5B:41:10:6B";
let address = string.parse::<MacAddress>().unwrap_err();
assert_eq!(MacParseError::InvalidLength, address);
}
#[test]
fn parse_invalid_digit() {
let string = "80:FA:ZZ:41:10:6B";
let address = string.parse::<MacAddress>().unwrap_err();
assert_eq!(MacParseError::InvalidDigit, address);
}
#[test]
fn parse_invalid_separator() {
let string = "80|FA|AA|41|10|6B";
let address = string.parse::<MacAddress>().unwrap_err();
assert_eq!(MacParseError::InvalidDigit, address);
}
#[cfg(feature = "serde")]
#[test]
fn serde_works() {
use serde::{Deserialize, Serialize};
use serde_test::{assert_tokens, Token};
let mac: MacAddress = "80:FA:5B:41:10:6B".parse().unwrap();
assert_tokens(&mac, &[Token::BorrowedStr("80:FA:5B:41:10:6B")]);
#[derive(Serialize, Deserialize)]
struct Test {
mac: MacAddress,
}
assert_eq!(
serde_json::to_string(&Test { mac }).unwrap(),
serde_json::to_string::<Test>(
&serde_json::from_str("{ \"mac\": \"80:FA:5B:41:10:6B\" }").unwrap()
)
.unwrap(),
);
}
#[cfg(feature = "serde")]
#[test]
fn serde_from_reader_works() {
use serde::{Deserialize, Serialize};
let mac: MacAddress = "80:FA:5B:41:10:6B".parse().unwrap();
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Test {
mac: MacAddress,
}
assert_eq!(
Test { mac },
serde_json::from_reader(std::io::Cursor::new(r#"{ "mac": "80:FA:5B:41:10:6B" }"#))
.unwrap(),
);
}
#[test]
fn convert() {
for mac in MacAddressIterator::new().unwrap() {
let name = name_by_mac_address(&mac).unwrap().unwrap();
let mac2 = mac_address_by_name(&name).unwrap().unwrap();
assert_eq!(mac, mac2);
}
}
}
+49
View File
@@ -0,0 +1,49 @@
#![allow(dead_code)]
use crate::MacAddressError;
use nix::ifaddrs::*;
/// Uses the `getifaddrs` call to retrieve a list of network interfaces on the
/// host device and returns the first MAC address listed that isn't
/// local-loopback or if a name was specified, that name.
pub fn get_mac(name: Option<&str>) -> Result<Option<[u8; 6]>, MacAddressError> {
let ifiter = getifaddrs()?;
for interface in ifiter {
if let Some(iface_address) = interface.address {
if let Some(link) = iface_address.as_link_addr() {
let bytes = link.addr();
if let Some(name) = name {
if interface.interface_name == name {
return Ok(bytes);
}
} else if let Some(bytes) = bytes {
if bytes.iter().any(|&x| x != 0) {
return Ok(Some(bytes));
}
}
}
}
}
Ok(None)
}
pub fn get_ifname(mac: &[u8; 6]) -> Result<Option<String>, MacAddressError> {
let ifiter = getifaddrs()?;
for interface in ifiter {
if let Some(iface_address) = interface.address {
if let Some(link) = iface_address.as_link_addr() {
let bytes = link.addr();
if bytes == Some(*mac) {
return Ok(Some(interface.interface_name));
}
}
}
}
Ok(None)
}
+239
View File
@@ -0,0 +1,239 @@
use std::{
convert::{TryFrom, TryInto},
ffi::CStr,
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};
use crate::MacAddressError;
const GAA_FLAG_NONE: ULONG = 0x0000;
/// 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]
/// then loops over the returned list until it finds a network device with a MAC
/// address, and returns it.
///
/// If it fails to find a device, it returns a `NoDevicesFound` error.
pub fn get_mac(name: Option<&str>) -> 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() };
loop {
// Break if we've gone through all devices
if ptr.is_null() {
break;
}
let bytes = unsafe { convert_mac_bytes(ptr) };
if let Some(name) = name {
#[cfg(not(target_pointer_width = "32"))]
let adapter_name = unsafe { construct_string((*ptr).FriendlyName) };
#[cfg(target_pointer_width = "32")]
let adapter_name = unsafe { construct_string(ptr.read_unaligned().FriendlyName) };
if adapter_name == name {
return Ok(Some(bytes));
} else {
#[cfg(not(target_pointer_width = "32"))]
let adapter_name = unsafe { CStr::from_ptr((*ptr).AdapterName) };
#[cfg(target_pointer_width = "32")]
let adapter_name = unsafe { CStr::from_ptr(ptr.read_unaligned().AdapterName) };
match adapter_name.to_str() {
Ok(s) if s == name => return Ok(Some(bytes)),
Ok(_) => {}
Err(_) => {
return Err(MacAddressError::InternalError);
}
}
}
} else if bytes.iter().any(|&x| x != 0) {
return Ok(Some(bytes));
}
// Otherwise go to the next device
#[cfg(target_pointer_width = "32")]
{
ptr = unsafe { ptr.read_unaligned().Next };
}
#[cfg(not(target_pointer_width = "32"))]
{
ptr = unsafe { (*ptr).Next };
}
}
Ok(None)
}
pub fn get_ifname(mac: &[u8; 6]) -> Result<Option<String>, MacAddressError> {
let adapters = get_adapters()?;
// Safety: We don't use the pointer after `adapters` is dropped
let mut ptr = unsafe { adapters.ptr() };
loop {
// Break if we've gone through all devices
if ptr.is_null() {
break;
}
let bytes = unsafe { convert_mac_bytes(ptr) };
if &bytes == mac {
#[cfg(not(target_pointer_width = "32"))]
let adapter_name = unsafe { construct_string((*ptr).FriendlyName) };
#[cfg(target_pointer_width = "32")]
let adapter_name = unsafe { construct_string(ptr.read_unaligned().FriendlyName) };
let adapter_name = adapter_name
.into_string()
.map_err(|_| MacAddressError::InternalError)?;
return Ok(Some(adapter_name));
}
// Otherwise go to the next device
#[cfg(target_pointer_width = "32")]
{
ptr = unsafe { ptr.read_unaligned().Next };
}
#[cfg(not(target_pointer_width = "32"))]
{
ptr = unsafe { (*ptr).Next };
}
}
Ok(None)
}
/// Copy over the 6 MAC address bytes to the buffer.
pub(crate) unsafe fn convert_mac_bytes(ptr: *mut IP_ADAPTER_ADDRESSES_LH) -> [u8; 6] {
#[cfg(target_pointer_width = "32")]
return ptr.read_unaligned().PhysicalAddress[..6]
.try_into()
.unwrap();
#[cfg(not(target_pointer_width = "32"))]
return ((*ptr).PhysicalAddress)[..6].try_into().unwrap();
}
pub(crate) struct AdaptersList {
ptr: *mut IP_ADAPTER_ADDRESSES_LH,
size: usize,
}
impl AdaptersList {
/// Safety: The pointer returned by this method MUST NOT be used after
/// `self` has gone out of scope. This pointer may also be null.
pub(crate) unsafe fn ptr(&self) -> *mut IP_ADAPTER_ADDRESSES_LH {
self.ptr
}
}
impl Drop for AdaptersList {
fn drop(&mut self) {
if !self.ptr.is_null() && self.size != 0 {
unsafe {
std::alloc::dealloc(
self.ptr as *mut u8,
std::alloc::Layout::from_size_align(
self.size,
core::mem::align_of::<IP_ADAPTER_ADDRESSES_LH>(),
)
.unwrap(),
)
};
}
}
}
pub(crate) fn get_adapters() -> Result<AdaptersList, MacAddressError> {
let mut buf_len = 0;
// This will get the number of bytes we need to allocate for all devices
unsafe {
GetAdaptersAddresses(
AF_UNSPEC as u32,
GAA_FLAG_NONE,
ptr::null_mut(),
ptr::null_mut(),
&mut buf_len,
);
}
if buf_len == 0 {
return Ok(AdaptersList {
ptr: ptr::null_mut(),
size: 0,
});
}
// Allocate `buf_len` bytes, and create a raw pointer to it with the correct alignment
// Safety:
let adapters_list: *mut IP_ADAPTER_ADDRESSES_LH = unsafe {
std::alloc::alloc(
std::alloc::Layout::from_size_align(
usize::try_from(buf_len).map_err(|_| MacAddressError::InternalError)?,
core::mem::align_of::<IP_ADAPTER_ADDRESSES_LH>(),
)
.unwrap(),
)
} as *mut IP_ADAPTER_ADDRESSES_LH;
// Get our list of adapters
let result = unsafe {
GetAdaptersAddresses(
// [IN] Family
AF_UNSPEC as u32,
// [IN] Flags
GAA_FLAG_NONE,
// [IN] Reserved
ptr::null_mut(),
// [INOUT] AdapterAddresses
adapters_list,
// [INOUT] SizePointer
&mut buf_len,
)
};
let adapters_list = AdaptersList {
ptr: adapters_list,
// Cast OK, we checked it above
size: buf_len as usize,
};
// Make sure we were successful
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!()
}