1use crate::ping::{PingResult, PingScanner};
2use crate::scan::{PortResult, PortScanner};
3use anyhow::Result;
4use serde::Serialize;
5use std::net::IpAddr;
6
7#[derive(Debug, Clone, Serialize)]
8pub struct InspectResult {
9 pub host: String,
10 pub ip: Option<IpAddr>,
11 pub ping: Option<PingResult>,
12 pub open_ports: Vec<PortResult>,
13 pub hostname: Option<String>,
14}
15
16pub struct InspectEngine {
17 ping: PingScanner,
18 scan: PortScanner,
19 ping_timeout_ms: u64,
20 scan_timeout_ms: u64,
21 dns_timeout_ms: u64,
22}
23
24impl InspectEngine {
25 pub fn new(concurrency: usize) -> Self {
26 Self::new_with_timeouts(
27 concurrency,
28 crate::DEFAULT_PING_TIMEOUT_MS,
29 crate::DEFAULT_SCAN_TIMEOUT_MS,
30 crate::DEFAULT_DNS_TIMEOUT_MS,
31 )
32 }
33
34 pub fn new_with_timeouts(
35 concurrency: usize,
36 ping_timeout_ms: u64,
37 scan_timeout_ms: u64,
38 dns_timeout_ms: u64,
39 ) -> Self {
40 let concurrency = concurrency.max(1);
41 Self {
42 ping: PingScanner::new(concurrency),
43 scan: PortScanner::new(concurrency),
44 ping_timeout_ms: ping_timeout_ms.max(1),
45 scan_timeout_ms: scan_timeout_ms.max(1),
46 dns_timeout_ms: dns_timeout_ms.max(1),
47 }
48 }
49
50 pub async fn inspect(&self, host: String, ports: Vec<u16>) -> Result<InspectResult> {
51 let ip_for_scan =
52 crate::ops::resolve_host_ip_with_timeout(&host, self.dns_timeout_ms).await?;
53
54 let ping_fut = self.ping.ping(ip_for_scan, self.ping_timeout_ms);
57 let scan_fut = self
58 .scan
59 .scan_host(ip_for_scan, ports, self.scan_timeout_ms);
60 let hostname_fut =
61 crate::dns::reverse_lookup_best_effort_timeout(ip_for_scan, self.dns_timeout_ms);
62
63 let (ping_res, ports_res, hostname) = tokio::join!(ping_fut, scan_fut, hostname_fut);
64
65 Ok(InspectResult {
66 host,
67 ip: Some(ip_for_scan),
68 ping: Some(ping_res),
69 open_ports: ports_res.into_iter().filter(|p| p.open).collect(),
70 hostname,
71 })
72 }
73}