use std::net::{IpAddr, SocketAddr}; use std::time::Duration; use anyhow::Result; use tokio::net::TcpStream; use tokio::time::timeout; /// TCP Connectivity module for checking TCP connections pub struct TcpConnector { timeout: Duration, } impl TcpConnector { /// Create a new TCP connector with the default timeout (5 seconds) pub fn new() -> Self { Self { timeout: Duration::from_secs(5), } } /// Create a new TCP connector with a custom timeout pub fn with_timeout(timeout: Duration) -> Self { Self { timeout } } /// Check if a TCP port is open on a host pub async fn check_port>(&self, host: A, port: u16) -> Result { let addr = SocketAddr::new(host.into(), port); let connect_future = TcpStream::connect(addr); match timeout(self.timeout, connect_future).await { Ok(Ok(_)) => Ok(true), Ok(Err(_)) => Ok(false), Err(_) => Ok(false), // Timeout occurred } } /// Check if multiple TCP ports are open on a host pub async fn check_ports + Clone>(&self, host: A, ports: &[u16]) -> Result> { let mut results = Vec::with_capacity(ports.len()); for &port in ports { let is_open = self.check_port(host.clone(), port).await?; results.push((port, is_open)); } Ok(results) } /// Check if a host is reachable on the network using ICMP ping pub async fn ping>(&self, host: S) -> Result { // Convert to owned strings to avoid borrowing issues let host_str = host.as_ref().to_string(); let timeout_secs = self.timeout.as_secs().to_string(); // Run the ping command with explicit arguments let status = tokio::process::Command::new("ping") .arg("-c") .arg("1") // Just one ping .arg("-W") .arg(timeout_secs) // Timeout in seconds .arg(host_str) // Host to ping .output() .await?; Ok(status.status.success()) } } impl Default for TcpConnector { fn default() -> Self { Self::new() } }