Ref: Optimiert IP-Adressprüfung und verbessert MAC-Ermittlung durch zusätzliche Validierung und Mehrfachversuche
This commit is contained in:
@@ -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,54 +231,98 @@ 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")]
|
||||
{
|
||||
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<String> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Sendet ein Wake-on-LAN Magic Packet an eine MAC-Adresse
|
||||
///
|
||||
/// # Parameter
|
||||
|
||||
Reference in New Issue
Block a user