First full working version.
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
use super::{IpVersion, NetworkTest};
|
||||
use crate::utils::{NetworkError, Result, TestResult};
|
||||
use tokio::time::Duration;
|
||||
|
||||
impl NetworkTest {
|
||||
pub async fn test_icmp(&self) -> Result<String> {
|
||||
// Use system ping command for simplicity and compatibility
|
||||
let target = &self.target;
|
||||
|
||||
let ping_cmd = match self.ip_version {
|
||||
IpVersion::V4 => "ping",
|
||||
IpVersion::V6 => "ping6",
|
||||
};
|
||||
|
||||
let output = tokio::process::Command::new(ping_cmd)
|
||||
.args(&["-c", "1", "-W", "5000", target]) // 1 ping, 5 second timeout
|
||||
.output()
|
||||
.await
|
||||
.map_err(|e| NetworkError::Io(e))?;
|
||||
|
||||
if output.status.success() {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
if let Some(line) = stdout.lines().find(|line| line.contains("time=")) {
|
||||
Ok(format!("ICMP ping successful: {}", line.trim()))
|
||||
} else {
|
||||
Ok("ICMP ping successful".to_string())
|
||||
}
|
||||
} else {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
Err(NetworkError::Other(format!("Ping failed: {}", stderr)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn ping_test(target: &str, ip_version: IpVersion, count: u32) -> Vec<TestResult> {
|
||||
let mut results = Vec::new();
|
||||
|
||||
for i in 0..count {
|
||||
let test = NetworkTest::new(target.to_string(), ip_version, super::NetworkProtocol::Icmp);
|
||||
|
||||
let mut result = test.run().await;
|
||||
result.test_name = format!("ICMP ping #{} to {} ({:?})", i + 1, target, ip_version);
|
||||
results.push(result);
|
||||
|
||||
if i < count - 1 {
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
}
|
||||
}
|
||||
|
||||
results
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
use crate::utils::{measure_time, NetworkError, Result, TestResult};
|
||||
use std::net::IpAddr;
|
||||
use std::time::Duration;
|
||||
|
||||
pub mod icmp;
|
||||
pub mod tcp;
|
||||
pub mod udp;
|
||||
|
||||
pub use icmp::*;
|
||||
pub use tcp::*;
|
||||
pub use udp::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum IpVersion {
|
||||
V4,
|
||||
V6,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum NetworkProtocol {
|
||||
Tcp,
|
||||
Udp,
|
||||
Icmp,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NetworkTest {
|
||||
pub target: String,
|
||||
pub ip_version: IpVersion,
|
||||
pub protocol: NetworkProtocol,
|
||||
pub port: Option<u16>,
|
||||
pub timeout: Duration,
|
||||
}
|
||||
|
||||
impl NetworkTest {
|
||||
pub fn new(target: String, ip_version: IpVersion, protocol: NetworkProtocol) -> Self {
|
||||
Self {
|
||||
target,
|
||||
ip_version,
|
||||
protocol,
|
||||
port: None,
|
||||
timeout: Duration::from_secs(5),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_port(mut self, port: u16) -> Self {
|
||||
self.port = Some(port);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_timeout(mut self, timeout: Duration) -> Self {
|
||||
self.timeout = timeout;
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn run(&self) -> TestResult {
|
||||
let test_name = format!(
|
||||
"{:?} test to {} {:?} {}",
|
||||
self.protocol,
|
||||
self.target,
|
||||
self.ip_version,
|
||||
self.port.map(|p| format!(":{}", p)).unwrap_or_default()
|
||||
);
|
||||
|
||||
let (duration, result) = measure_time(|| async {
|
||||
match self.protocol {
|
||||
NetworkProtocol::Tcp => self.test_tcp().await,
|
||||
NetworkProtocol::Udp => self.test_udp().await,
|
||||
NetworkProtocol::Icmp => self.test_icmp().await,
|
||||
}
|
||||
})
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(details) => TestResult::new(test_name).success(duration, details),
|
||||
Err(error) => TestResult::new(test_name).failure(duration, error),
|
||||
}
|
||||
}
|
||||
|
||||
async fn resolve_target(&self) -> Result<IpAddr> {
|
||||
use trust_dns_resolver::config::*;
|
||||
use trust_dns_resolver::TokioAsyncResolver;
|
||||
|
||||
let resolver =
|
||||
TokioAsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default());
|
||||
|
||||
let lookup = match self.ip_version {
|
||||
IpVersion::V4 => {
|
||||
let response = resolver
|
||||
.ipv4_lookup(&self.target)
|
||||
.await
|
||||
.map_err(|e| NetworkError::DnsResolution(e.to_string()))?;
|
||||
response.iter().next().map(|ip| IpAddr::V4(**ip))
|
||||
}
|
||||
IpVersion::V6 => {
|
||||
let response = resolver
|
||||
.ipv6_lookup(&self.target)
|
||||
.await
|
||||
.map_err(|e| NetworkError::DnsResolution(e.to_string()))?;
|
||||
response.iter().next().map(|ip| IpAddr::V6(**ip))
|
||||
}
|
||||
};
|
||||
|
||||
lookup.ok_or_else(|| NetworkError::DnsResolution("No IP found".to_string()))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
use super::{IpVersion, NetworkTest};
|
||||
use crate::utils::{NetworkError, Result, TestResult};
|
||||
use std::net::SocketAddr;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::time::timeout;
|
||||
|
||||
impl NetworkTest {
|
||||
pub async fn test_tcp(&self) -> Result<String> {
|
||||
let ip = self.resolve_target().await?;
|
||||
let port = self.port.unwrap_or(80);
|
||||
let addr = SocketAddr::new(ip, port);
|
||||
|
||||
let stream = timeout(self.timeout, TcpStream::connect(addr))
|
||||
.await
|
||||
.map_err(|_| NetworkError::Timeout)?
|
||||
.map_err(|e| NetworkError::Io(e))?;
|
||||
|
||||
let local_addr = stream.local_addr().map_err(NetworkError::Io)?;
|
||||
let peer_addr = stream.peer_addr().map_err(NetworkError::Io)?;
|
||||
|
||||
Ok(format!(
|
||||
"TCP connection successful: {} -> {}",
|
||||
local_addr, peer_addr
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn test_tcp_ports(target: &str, ports: &[u16], ip_version: IpVersion) -> Vec<TestResult> {
|
||||
let mut results = Vec::new();
|
||||
|
||||
for &port in ports {
|
||||
let test = NetworkTest::new(target.to_string(), ip_version, super::NetworkProtocol::Tcp)
|
||||
.with_port(port);
|
||||
results.push(test.run().await);
|
||||
}
|
||||
|
||||
results
|
||||
}
|
||||
|
||||
pub async fn test_tcp_common_ports(target: &str, ip_version: IpVersion) -> Vec<TestResult> {
|
||||
let common_ports = [22, 25, 53, 80, 110, 143, 443, 993, 995, 8080, 8443];
|
||||
test_tcp_ports(target, &common_ports, ip_version).await
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
use super::{IpVersion, NetworkTest};
|
||||
use crate::utils::{NetworkError, Result, TestResult};
|
||||
use std::net::SocketAddr;
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::time::timeout;
|
||||
|
||||
impl NetworkTest {
|
||||
pub async fn test_udp(&self) -> Result<String> {
|
||||
let ip = self.resolve_target().await?;
|
||||
let port = self.port.unwrap_or(53);
|
||||
let addr = SocketAddr::new(ip, port);
|
||||
|
||||
let bind_addr = match self.ip_version {
|
||||
IpVersion::V4 => "0.0.0.0:0",
|
||||
IpVersion::V6 => "[::]:0",
|
||||
};
|
||||
|
||||
let socket = UdpSocket::bind(bind_addr).await.map_err(NetworkError::Io)?;
|
||||
|
||||
socket.connect(addr).await.map_err(NetworkError::Io)?;
|
||||
|
||||
let test_data = b"test";
|
||||
|
||||
let send_result = timeout(self.timeout, socket.send(test_data))
|
||||
.await
|
||||
.map_err(|_| NetworkError::Timeout)?
|
||||
.map_err(NetworkError::Io)?;
|
||||
|
||||
let mut buf = [0; 1024];
|
||||
let recv_result =
|
||||
timeout(std::time::Duration::from_millis(100), socket.recv(&mut buf)).await;
|
||||
|
||||
let details = match recv_result {
|
||||
Ok(Ok(bytes)) => format!(
|
||||
"UDP test successful: sent {} bytes, received {} bytes to {}",
|
||||
send_result, bytes, addr
|
||||
),
|
||||
_ => format!(
|
||||
"UDP test (send only): sent {} bytes to {} (no response expected for basic connectivity test)",
|
||||
send_result, addr
|
||||
),
|
||||
};
|
||||
|
||||
Ok(details)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn test_udp_ports(target: &str, ports: &[u16], ip_version: IpVersion) -> Vec<TestResult> {
|
||||
let mut results = Vec::new();
|
||||
|
||||
for &port in ports {
|
||||
let test = NetworkTest::new(target.to_string(), ip_version, super::NetworkProtocol::Udp)
|
||||
.with_port(port);
|
||||
results.push(test.run().await);
|
||||
}
|
||||
|
||||
results
|
||||
}
|
||||
|
||||
pub async fn test_udp_common_ports(target: &str, ip_version: IpVersion) -> Vec<TestResult> {
|
||||
let common_ports = [53, 67, 68, 123, 161, 162, 514, 1194, 5353];
|
||||
test_udp_ports(target, &common_ports, ip_version).await
|
||||
}
|
||||
Reference in New Issue
Block a user