use anyhow::{Result, anyhow};
use std::net::{IpAddr};
use std::time::{Duration, Instant};
use crate::common::parse_hostname;
pub fn run_ping(target: &str, count: u32, timeout_sec: u64, interval_sec: u64, size: usize) -> Result<()> {
let target_ip = parse_hostname(target)?;
let working_port = find_open_port(&target_ip, Duration::from_secs(timeout_sec))?;
println!("PING {} ({}): {} data bytes", target, target_ip, size);
println!("Using TCP port {} for connectivity testing", working_port);
let timeout = Duration::from_secs(timeout_sec);
let interval = Duration::from_secs(interval_sec);
let mut packets_sent = 0;
let mut packets_received = 0;
let mut total_rtt = Duration::ZERO;
let mut min_rtt = Duration::MAX;
let mut max_rtt = Duration::ZERO;
for i in 0..count {
let start_time = Instant::now();
match std::net::TcpStream::connect_timeout(
&std::net::SocketAddr::new(target_ip, working_port),
timeout
) {
Ok(_) => {
let elapsed = start_time.elapsed();
packets_sent += 1;
packets_received += 1;
total_rtt += elapsed;
min_rtt = min_rtt.min(elapsed);
max_rtt = max_rtt.max(elapsed);
println!("Connected to {} via port {}: icmp_seq={} time={:.2}ms",
target, working_port, i, elapsed.as_millis());
}
Err(e) if e.kind() == std::io::ErrorKind::TimedOut => {
packets_sent += 1;
println!("Request timeout for icmp_seq {} (port {})", i, working_port);
}
Err(e) => {
packets_sent += 1;
println!("Connection failed for icmp_seq {}: {} (port {})", i, e, working_port);
}
}
if i < count - 1 {
std::thread::sleep(interval);
}
}
println!("\n--- {} ping statistics ---", target);
println!("{} packets transmitted, {} received, {:.0}% packet loss, time {:.0?}",
packets_sent,
packets_received,
if packets_sent > 0 {
((packets_sent - packets_received) as f64 / packets_sent as f64) * 100.0
} else { 0.0 },
total_rtt);
if packets_received > 0 {
let avg_rtt = total_rtt.as_millis() / packets_received as u128;
println!("rtt min/avg/max = {:.2}/{:.2}/{:.2} ms",
min_rtt.as_millis(),
avg_rtt,
max_rtt.as_millis());
}
println!("Note: This is a simplified ping using TCP port {} connectivity checks.", working_port);
println!("For true ICMP ping, raw socket access with elevated privileges is required.");
println!("If this fails, the target may not have any common TCP ports open.");
Ok(())
}
fn find_open_port(target_ip: &IpAddr, timeout: Duration) -> Result<u16> {
let common_ports = vec![
80, 443, 22, 21, 25, 53, 110, 143, 993, 995, 3389, 3306, 5432, 6379, 8080, 8443, ];
for &port in &common_ports {
match std::net::TcpStream::connect_timeout(
&std::net::SocketAddr::new(*target_ip, port),
timeout
) {
Ok(_) => {
return Ok(port);
}
Err(_) => {
continue;
}
}
}
println!("Warning: No common TCP ports found open on target. Falling back to port 80.");
Ok(80)
}