diff --git a/src/network/utils.rs b/src/network/utils.rs index 40356e6..cd25041 100644 --- a/src/network/utils.rs +++ b/src/network/utils.rs @@ -11,9 +11,12 @@ use std::process::Command; /// # Rückgabe /// - `bool`: True wenn die Adresse erreichbar ist, False wenn nicht pub fn is_reachable(address: &str) -> bool { - let addr = &*if let Some(parts) = address.split('/').nth(0) { - if let Ok(ip) = parts.parse::() { - let next_ip = Ipv4Addr::from(u32::from(ip) + 1); + let addr_string = if let Some((ip_str, mask_str)) = address.split_once('/') { + if ip_str.parse::().is_ok() + && mask_str.parse::().ok().filter(|m| *m <= 32).is_some() + { + let ip = ip_str.parse::().unwrap(); + let next_ip = Ipv4Addr::from(u32::from(ip).saturating_add(1)); next_ip.to_string() } else { address.to_string() @@ -21,6 +24,7 @@ pub fn is_reachable(address: &str) -> bool { } else { address.to_string() }; + let addr = addr_string.as_str(); #[cfg(target_os = "linux")] { @@ -30,6 +34,9 @@ pub fn is_reachable(address: &str) -> bool { .output(); if let Ok(out) = output { + let stdout = String::from_utf8_lossy(&out.stdout); + log("network_utils", &*stdout, LogLevel::Debug); + out.status.success() } else { log::log("network_utils", &*format!("Couldn't reach address {}!", addr), LogLevel::Error); @@ -224,54 +231,98 @@ pub fn get_ip_from_mac(mac: &str, network: &str) -> Option { /// # Rückgabe /// - Option: Die MAC-Adresse des Geräts oder None wenn nicht gefunden pub fn get_mac_from_ip(ip: &str) -> Option { + // Hilfsfunktionen zur MAC-Normalisierung + let normalize_mac = |m: &str| -> String { + m.chars() + .filter(|c| c.is_ascii_hexdigit()) + .flat_map(|c| c.to_lowercase()) + .collect::() + }; + let canonicalize_mac = |hex_no_sep: &str| -> String { + hex_no_sep + .as_bytes() + .chunks(2) + .map(std::str::from_utf8) + .filter_map(Result::ok) + .collect::>() + .join(":") + }; + #[cfg(target_os = "linux")] { - let output = Command::new("ip") - .args(["neigh", "show", ip]) + // 1) Ziel kurz anpingen, damit ein ARP-Eintrag entsteht + let _ = Command::new("ping") + .args(["-c", "1", "-W", "1", ip]) .output(); - match output { - Ok(out) => { - if !out.status.success() { - return None; - } - let stdout = String::from_utf8_lossy(&out.stdout); - // Format: 192.168.1.1 dev eth0 lladdr 00:11:22:33:44:55 REACHABLE - for line in stdout.lines() { - let parts: Vec<&str> = line.split_whitespace().collect(); - if parts.len() >= 4 && parts[0] == ip && parts[2] == "lladdr" { - return Some(parts[3].to_string()); + // 2) Bis zu drei Versuche, ARP/Neigh einzulesen (kleine Wartezeit) + for _ in 0..3 { + let output = Command::new("ip") + .args(["neigh", "show", ip]) + .output(); + + if let Ok(out) = output { + if out.status.success() { + let stdout = String::from_utf8_lossy(&out.stdout); + // Beispiel: 192.168.1.1 dev eth0 lladdr 00:11:22:33:44:55 REACHABLE + for line in stdout.lines() { + let parts: Vec<&str> = line.split_whitespace().collect(); + if parts.get(0).copied() == Some(ip) { + if let Some(idx) = parts.iter().position(|p| *p == "lladdr") { + if let Some(mac_tok) = parts.get(idx + 1) { + let seen_norm = normalize_mac(mac_tok); + if seen_norm.len() == 12 { + return Some(canonicalize_mac(&seen_norm)); + } + } + } + } } } - None } - Err(_) => None + + std::thread::sleep(std::time::Duration::from_millis(100)); } + + None } #[cfg(target_os = "windows")] { - let output = Command::new("arp") - .args(["-a", ip]) + // 1) Ziel kurz anpingen, damit ein ARP-Eintrag entsteht + let _ = Command::new("ping") + .args(["-n", "1", "-w", "500", ip]) .output(); - match output { - Ok(out) => { - if !out.status.success() { - return None; - } - let stdout = String::from_utf8_lossy(&out.stdout); - for line in stdout.lines() { - let parts: Vec<&str> = line.split_whitespace().collect(); - // Format: 192.168.1.1 00-11-22-33-44-55 dynamic - if parts.len() >= 2 && parts[0] == ip { - return Some(parts[1].replace('-', ":")); + // 2) Bis zu drei Versuche, ARP einzulesen (kleine Wartezeit) + for _ in 0..3 { + // Hinweis: `arp -a` auf Windows zeigt die gesamte Tabelle; mit IP filtert es i. d. R. auf Interface, + // daher filtern wir inhaltlich auf die Zeile mit der Ziel-IP. + let output = Command::new("arp") + .args(["-a"]) + .output(); + + if let Ok(out) = output { + if out.status.success() { + let stdout = String::from_utf8_lossy(&out.stdout); + for line in stdout.lines() { + let cols: Vec<&str> = line.split_whitespace().collect(); + // Typisch: "192.168.1.1 00-11-22-33-44-55 dynamic" + if cols.get(0).copied() == Some(ip) && cols.len() >= 2 { + let mac_colon = cols[1].replace('-', ":"); + let seen_norm = normalize_mac(&mac_colon); + if seen_norm.len() == 12 { + return Some(canonicalize_mac(&seen_norm)); + } + } } } - None } - Err(_) => None + + std::thread::sleep(std::time::Duration::from_millis(100)); } + + None } #[cfg(not(any(target_os = "linux", target_os = "windows")))] @@ -280,6 +331,7 @@ pub fn get_mac_from_ip(ip: &str) -> Option { None } } + /// Sendet ein Wake-on-LAN Magic Packet an eine MAC-Adresse /// /// # Parameter