Initial Commit
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
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(mut 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
|
||||
}),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user