Skip to main content

netscli_core/
inspect.rs

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        // Run ping, scan, and reverse-DNS concurrently to keep inspect latency bounded
55        // by the slowest of the three rather than their sum.
56        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}