Initial Commit
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
use std::process::Stdio;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use serde::Deserialize;
|
||||
use tauri::{AppHandle, Emitter};
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
use tokio::process::Command;
|
||||
|
||||
static NMAP_CANCEL: std::sync::OnceLock<Arc<AtomicBool>> = std::sync::OnceLock::new();
|
||||
|
||||
fn get_nmap_cancel() -> Arc<AtomicBool> {
|
||||
NMAP_CANCEL
|
||||
.get_or_init(|| Arc::new(AtomicBool::new(false)))
|
||||
.clone()
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct NmapConfig {
|
||||
pub target: String,
|
||||
pub args: Vec<String>,
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn run_nmap(app: AppHandle, config: NmapConfig) -> Result<(), String> {
|
||||
// Check nmap exists
|
||||
let check = Command::new("nmap")
|
||||
.arg("--version")
|
||||
.output()
|
||||
.await
|
||||
.map_err(|_| "nmap not found. Please install nmap.".to_string())?;
|
||||
|
||||
if !check.status.success() {
|
||||
return Err("nmap not found. Please install nmap.".to_string());
|
||||
}
|
||||
|
||||
let cancel = get_nmap_cancel();
|
||||
cancel.store(false, Ordering::Relaxed);
|
||||
|
||||
let _ = app.emit("nmap:start", &config.target);
|
||||
|
||||
let mut cmd = Command::new("nmap");
|
||||
for arg in &config.args {
|
||||
cmd.arg(arg);
|
||||
}
|
||||
for target in config.target.split_whitespace() {
|
||||
cmd.arg(target);
|
||||
}
|
||||
cmd.stdout(Stdio::piped());
|
||||
cmd.stderr(Stdio::piped());
|
||||
|
||||
let mut child = cmd
|
||||
.spawn()
|
||||
.map_err(|e| format!("Failed to start nmap: {}", e))?;
|
||||
|
||||
let stdout = child
|
||||
.stdout
|
||||
.take()
|
||||
.ok_or_else(|| "Failed to capture nmap stdout".to_string())?;
|
||||
|
||||
let stderr = child
|
||||
.stderr
|
||||
.take()
|
||||
.ok_or_else(|| "Failed to capture nmap stderr".to_string())?;
|
||||
|
||||
// Stream stdout
|
||||
let app2 = app.clone();
|
||||
let cancel2 = cancel.clone();
|
||||
let stdout_handle = tokio::spawn(async move {
|
||||
let mut reader = BufReader::new(stdout).lines();
|
||||
while let Ok(Some(line)) = reader.next_line().await {
|
||||
if cancel2.load(Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
let _ = app2.emit("nmap:line", &line);
|
||||
}
|
||||
});
|
||||
|
||||
// Stream stderr
|
||||
let app3 = app.clone();
|
||||
let stderr_handle = tokio::spawn(async move {
|
||||
let mut reader = BufReader::new(stderr).lines();
|
||||
while let Ok(Some(line)) = reader.next_line().await {
|
||||
let _ = app3.emit("nmap:line", &line);
|
||||
}
|
||||
});
|
||||
|
||||
// Wait for completion or cancellation
|
||||
tokio::select! {
|
||||
status = child.wait() => {
|
||||
match status {
|
||||
Ok(s) if s.success() => {
|
||||
let _ = app.emit("nmap:complete", "Scan complete");
|
||||
}
|
||||
Ok(s) => {
|
||||
let _ = app.emit("nmap:complete",
|
||||
format!("nmap exited with code {}", s.code().unwrap_or(-1)));
|
||||
}
|
||||
Err(e) => {
|
||||
let _ = app.emit("nmap:error", format!("nmap error: {}", e));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = async {
|
||||
loop {
|
||||
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
|
||||
if cancel.load(Ordering::Relaxed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} => {
|
||||
let _ = child.kill().await;
|
||||
let _ = app.emit("nmap:complete", "Scan cancelled");
|
||||
}
|
||||
}
|
||||
|
||||
let _ = stdout_handle.await;
|
||||
let _ = stderr_handle.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn stop_nmap() -> Result<(), String> {
|
||||
let cancel = get_nmap_cancel();
|
||||
cancel.store(true, Ordering::Relaxed);
|
||||
tracing::info!("Nmap scan cancelled");
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user