sal_net/
tcp.rs

1use std::net::{IpAddr, SocketAddr};
2use std::time::Duration;
3
4use anyhow::Result;
5use tokio::net::TcpStream;
6use tokio::time::timeout;
7
8/// TCP Connectivity module for checking TCP connections
9pub struct TcpConnector {
10    timeout: Duration,
11}
12
13impl TcpConnector {
14    /// Create a new TCP connector with the default timeout (5 seconds)
15    pub fn new() -> Self {
16        Self {
17            timeout: Duration::from_secs(5),
18        }
19    }
20
21    /// Create a new TCP connector with a custom timeout
22    pub fn with_timeout(timeout: Duration) -> Self {
23        Self { timeout }
24    }
25
26    /// Check if a TCP port is open on a host
27    pub async fn check_port<A: Into<IpAddr>>(&self, host: A, port: u16) -> Result<bool> {
28        let addr = SocketAddr::new(host.into(), port);
29        let connect_future = TcpStream::connect(addr);
30
31        match timeout(self.timeout, connect_future).await {
32            Ok(Ok(_)) => Ok(true),
33            Ok(Err(_)) => Ok(false),
34            Err(_) => Ok(false), // Timeout occurred
35        }
36    }
37
38    /// Check if multiple TCP ports are open on a host
39    pub async fn check_ports<A: Into<IpAddr> + Clone>(
40        &self,
41        host: A,
42        ports: &[u16],
43    ) -> Result<Vec<(u16, bool)>> {
44        let mut results = Vec::with_capacity(ports.len());
45
46        for &port in ports {
47            let is_open = self.check_port(host.clone(), port).await?;
48            results.push((port, is_open));
49        }
50
51        Ok(results)
52    }
53
54    /// Check if a host is reachable on the network using ICMP ping
55    pub async fn ping<S: AsRef<str>>(&self, host: S) -> Result<bool> {
56        // Convert to owned strings to avoid borrowing issues
57        let host_str = host.as_ref().to_string();
58        let timeout_secs = self.timeout.as_secs().to_string();
59
60        // Run the ping command with explicit arguments
61        let status = tokio::process::Command::new("ping")
62            .arg("-c")
63            .arg("1") // Just one ping
64            .arg("-W")
65            .arg(timeout_secs) // Timeout in seconds
66            .arg(host_str) // Host to ping
67            .output()
68            .await?;
69
70        Ok(status.status.success())
71    }
72}
73
74impl Default for TcpConnector {
75    fn default() -> Self {
76        Self::new()
77    }
78}