Ref: Optimiert IP-Adressprüfung und verbessert MAC-Ermittlung durch zusätzliche Validierung und Mehrfachversuche

This commit is contained in:
2025-08-21 16:24:32 +02:00
parent 04d0fd5532
commit 14876c7e1e

View File

@@ -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::<Ipv4Addr>() {
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::<Ipv4Addr>().is_ok()
&& mask_str.parse::<u8>().ok().filter(|m| *m <= 32).is_some()
{
let ip = ip_str.parse::<Ipv4Addr>().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,55 +231,99 @@ pub fn get_ip_from_mac(mac: &str, network: &str) -> Option<String> {
/// # Rückgabe
/// - Option<String>: Die MAC-Adresse des Geräts oder None wenn nicht gefunden
pub fn get_mac_from_ip(ip: &str) -> Option<String> {
// 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::<String>()
};
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::<Vec<&str>>()
.join(":")
};
#[cfg(target_os = "linux")]
{
// 1) Ziel kurz anpingen, damit ein ARP-Eintrag entsteht
let _ = Command::new("ping")
.args(["-c", "1", "-W", "1", ip])
.output();
// 2) Bis zu drei Versuche, ARP/Neigh einzulesen (kleine Wartezeit)
for _ in 0..3 {
let output = Command::new("ip")
.args(["neigh", "show", ip])
.output();
match output {
Ok(out) => {
if !out.status.success() {
return None;
}
if let Ok(out) = output {
if out.status.success() {
let stdout = String::from_utf8_lossy(&out.stdout);
// Format: 192.168.1.1 dev eth0 lladdr 00:11:22:33:44:55 REACHABLE
// 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.len() >= 4 && parts[0] == ip && parts[2] == "lladdr" {
return Some(parts[3].to_string());
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));
}
}
}
}
}
}
}
std::thread::sleep(std::time::Duration::from_millis(100));
}
None
}
Err(_) => 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;
}
// 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 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('-', ":"));
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));
}
}
}
}
}
std::thread::sleep(std::time::Duration::from_millis(100));
}
None
}
Err(_) => None
}
}
#[cfg(not(any(target_os = "linux", target_os = "windows")))]
{
@@ -280,6 +331,7 @@ pub fn get_mac_from_ip(ip: &str) -> Option<String> {
None
}
}
/// Sendet ein Wake-on-LAN Magic Packet an eine MAC-Adresse
///
/// # Parameter