From 846b618fe219c44dfc7b3a13aadace2198442c9c Mon Sep 17 00:00:00 2001 From: Thomas Karpiniec Date: Thu, 6 Jun 2024 20:10:45 +1000 Subject: [PATCH 1/1] List interfaces on Mac --- Cargo.lock | 53 +++++++++++++++++++++++++++++++++++++ Cargo.toml | 5 +++- src/imp_mac.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 2 ++ src/util.rs | 13 +++++++++ 5 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 src/imp_mac.rs create mode 100644 src/util.rs diff --git a/Cargo.lock b/Cargo.lock index b7d6ccc..9af9c1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,13 +2,66 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "netwatcher" version = "0.1.0" dependencies = [ + "nix", "windows", ] +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset", +] + [[package]] name = "proc-macro2" version = "1.0.84" diff --git a/Cargo.toml b/Cargo.toml index 04dea3f..2615972 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,10 @@ edition = "2021" [dependencies] -[dependencies.windows] +[target.'cfg(unix)'.dependencies] +nix = { version = "0.29.0", features = ["net"] } + +[target.'cfg(windows)'.dependencies.windows] version = "0.56.0" features = [ "Win32_NetworkManagement_IpHelper", diff --git a/src/imp_mac.rs b/src/imp_mac.rs new file mode 100644 index 0000000..c4fc8d6 --- /dev/null +++ b/src/imp_mac.rs @@ -0,0 +1,72 @@ +use std::{collections::HashMap, net::IpAddr}; + +use nix::{ifaddrs::getifaddrs, net::if_::if_nametoindex}; + +use crate::util::format_mac; +use crate::{Error, IfIndex, Interface}; + +struct CandidateInterface { + name: String, + index: u32, + hw_addr: Option, + ips: Vec, +} + +pub(crate) fn list_interfaces() -> Result, Error> { + let addrs = getifaddrs().map_err(|_| Error::Internal)?; + let mut candidates = HashMap::new(); + + for addr in addrs { + let index = if_nametoindex(addr.interface_name.as_str()).map_err(|_| Error::Internal)?; + let candidate = candidates + .entry(addr.interface_name.clone()) + .or_insert_with(|| CandidateInterface { + name: addr.interface_name.clone(), + index, + hw_addr: None, + ips: vec![], + }); + if let Some(a) = addr.address { + if let Some(a) = a.as_link_addr() { + if let Some(raw_addr) = a.addr() { + candidate.hw_addr = Some(format_mac(&raw_addr)?); + } + } + if let Some(a) = a.as_sockaddr_in() { + candidate.ips.push(IpAddr::V4(a.ip())); + } + if let Some(a) = a.as_sockaddr_in6() { + candidate.ips.push(IpAddr::V6(a.ip())); + } + } + } + + let ifs = candidates + .drain() + .flat_map(|(_, c)| { + c.hw_addr.map(|hw_addr| { + ( + c.index, + Interface { + index: c.index, + hw_addr, + name: c.name, + ips: c.ips, + }, + ) + }) + }) + .collect(); + Ok(ifs) +} + +#[cfg(test)] +mod test { + use super::list_interfaces; + + #[test] + fn list() { + let ifaces = list_interfaces().unwrap(); + println!("{:?}", ifaces); + } +} diff --git a/src/lib.rs b/src/lib.rs index af5b7af..9f82092 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,9 @@ use std::{ }; #[cfg_attr(windows, path = "imp_win.rs")] +#[cfg_attr(target_vendor = "apple", path = "imp_mac.rs")] mod imp; +mod util; type IfIndex = u32; diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..bbc4e6c --- /dev/null +++ b/src/util.rs @@ -0,0 +1,13 @@ +use crate::Error; +use std::fmt::Write; + +pub(crate) fn format_mac(bytes: &[u8]) -> Result { + let mut mac = String::with_capacity(bytes.len() * 3); + for i in 0..bytes.len() { + if i != 0 { + write!(mac, ":").map_err(|_| Error::Internal)?; + } + write!(mac, "{:02X}", bytes[i]).map_err(|_| Error::Internal)?; + } + Ok(mac) +} -- 2.39.5