diff --git a/src/network.rs b/src/network.rs index 920bcbf..da42b20 100644 --- a/src/network.rs +++ b/src/network.rs @@ -95,10 +95,33 @@ pub async fn run_nmap_scan( // unabhängig von der System-Locale funktioniert (z. B. "Passwort ist notwendig" // auf einem deutschen System würde sonst nicht erkannt werden). cmd.env("LC_ALL", "C").env("LANG", "C"); + cmd.kill_on_drop(true); + // Eigene Prozessgruppe: `kill_on_drop`/Child::kill() signalisiert bei einem + // Timeout nur den direkten Kind-Prozess. Im Sudo-Fall ist das `sudo` selbst, + // nicht das von `sudo` geforkte (als root laufende) `nmap`. Ohne eigene + // Prozessgruppe würde `sudo` beim SIGKILL sterben, während `nmap` als + // root-Waise weiterläuft und den Scan fortsetzt. + cmd.process_group(0); - let out = tokio::time::timeout(Duration::from_secs(timeout_secs), cmd.output()) + let child = cmd.spawn()?; + let pgid = child.id(); + + let out = match tokio::time::timeout(Duration::from_secs(timeout_secs), child.wait_with_output()) .await - .map_err(|_| NmapRunError::Timeout)??; + { + Ok(out) => out?, + Err(_) => { + if let Some(pgid) = pgid { + // Negative PID = Signal an die gesamte Prozessgruppe (sudo + nmap). + let _ = tokio::process::Command::new("kill") + .arg("-KILL") + .arg(format!("-{pgid}")) + .output() + .await; + } + return Err(NmapRunError::Timeout); + } + }; if !out.status.success() { let stderr = String::from_utf8_lossy(&out.stderr);