Files
argus/src-tauri/src/scanner/ping.rs
T
2026-09-23 10:22:06 -04:00

131 lines
4.3 KiB
Rust

use crate::models::host::{HostResult, PingResult};
use crate::net::dns::reverse_lookup;
use crate::scanner::netbios::query_netbios;
use crate::scanner::ports::{scan_host_ports, DEFAULT_PORTS};
use ping_async::IcmpEchoRequestor;
use std::net::IpAddr;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;
use tauri::{AppHandle, Emitter};
use tokio::task::JoinSet;
const MAX_CONCURRENT: usize = 128;
async fn ping_host(ip: IpAddr, timeout: Duration) -> HostResult {
let requestor = match IcmpEchoRequestor::new(ip, None, None, Some(timeout)) {
Ok(r) => r,
Err(e) => {
tracing::debug!("Failed to create ping request for {}: {}", ip, e);
return HostResult::down(ip);
}
};
match requestor.send().await {
Ok(reply) => match reply.status() {
ping_async::IcmpEchoStatus::Success => {
let rtt = reply.round_trip_time();
let hostname = reverse_lookup(ip).await;
let mut result = HostResult::from_ping(
ip,
PingResult {
rtt_ms: rtt.as_secs_f64() * 1000.0,
ttl: None,
loss_pct: 0.0,
},
);
result.hostname = hostname;
result
}
other => {
tracing::debug!("{} returned {:?}", ip, other);
HostResult::down(ip)
}
},
Err(e) => {
tracing::debug!("Ping error for {}: {}", ip, e);
HostResult::down(ip)
}
}
}
pub async fn ping_sweep(
app: &AppHandle,
ips: &[IpAddr],
timeout_ms: u64,
cancel: Arc<AtomicBool>,
port_scan: bool,
ports: Option<Vec<u16>>,
port_timeout_ms: u64,
) {
let total = ips.len() as u32;
let timeout = Duration::from_millis(timeout_ms);
let mut done: u32 = 0;
for chunk in ips.chunks(MAX_CONCURRENT) {
if cancel.load(Ordering::Relaxed) {
break;
}
let mut join_set = JoinSet::new();
for &ip in chunk {
let timeout = timeout;
join_set.spawn(async move { ping_host(ip, timeout).await });
}
while let Some(result) = join_set.join_next().await {
done += 1;
match result {
Ok(host_result) => {
// Emit immediately so the host shows up in the table
let _ = app.emit("scan:host-found", &host_result);
// Port scan Up hosts in place
if host_result.status == crate::models::host::HostStatus::Up {
let app2 = app.clone();
let ports2 = ports.clone();
tokio::spawn(async move {
let mut enriched = host_result;
// NetBIOS and port scan concurrently
let nb_fut = query_netbios(enriched.ip, 500);
if port_scan {
let scan_ports = ports2
.unwrap_or_else(|| DEFAULT_PORTS.to_vec());
let (nb_result, port_results) = tokio::join!(
nb_fut,
scan_host_ports(enriched.ip, &scan_ports, port_timeout_ms)
);
enriched.netbios = nb_result;
enriched.ports = port_results;
} else {
enriched.netbios = nb_fut.await;
}
let _ = app2.emit("scan:host-found", &enriched);
});
}
}
Err(e) => {
tracing::warn!("Ping task panicked: {}", e);
}
}
let _ = app.emit(
"scan:progress",
serde_json::json!({
"done": done,
"total": total
}),
);
}
}
let _ = app.emit(
"scan:complete",
serde_json::json!({
"total_hosts": done
}),
);
}