]> code.octet-stream.net Git - netwatcher/commitdiff
List interfaces on Mac
authorThomas Karpiniec <tom.karpiniec@outlook.com>
Thu, 6 Jun 2024 10:10:45 +0000 (20:10 +1000)
committerThomas Karpiniec <tom.karpiniec@outlook.com>
Thu, 6 Jun 2024 10:10:45 +0000 (20:10 +1000)
Cargo.lock
Cargo.toml
src/imp_mac.rs [new file with mode: 0644]
src/lib.rs
src/util.rs [new file with mode: 0644]

index b7d6ccc69a6fb91e5b411f43115aa451d8d738ad..9af9c1be0906239b7b4002bb92c0fd66a4b26026 100644 (file)
@@ -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"
index 04dea3fa0cfb80e16680f97d7d6c3b8fc1912d78..2615972375a4f7a9077cd9780ec604fc6ad30ee5 100644 (file)
@@ -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 (file)
index 0000000..c4fc8d6
--- /dev/null
@@ -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<String>,
+    ips: Vec<IpAddr>,
+}
+
+pub(crate) fn list_interfaces() -> Result<HashMap<IfIndex, Interface>, 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);
+    }
+}
index af5b7aff2e083b82ad672f82494b4e4a8c86db98..9f82092e8d7dc9e5a74da68b09349b98347690cd 100644 (file)
@@ -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 (file)
index 0000000..bbc4e6c
--- /dev/null
@@ -0,0 +1,13 @@
+use crate::Error;
+use std::fmt::Write;
+
+pub(crate) fn format_mac(bytes: &[u8]) -> Result<String, Error> {
+    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)
+}