network_toolset 0.1.0

A comprehensive network diagnostic toolset implemented in Rust
Documentation
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)?;

    // Try to find an open port first
    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();

        // Try to connect to the working port
        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);
            }
        }

        // Wait for next ping
        if i < count - 1 {
            std::thread::sleep(interval);
        }
    }

    // Print statistics
    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> {
    // Common ports to try in order of likelihood
    let common_ports = vec![
        80,    // HTTP
        443,   // HTTPS
        22,    // SSH
        21,    // FTP
        25,    // SMTP
        53,    // DNS
        110,   // POP3
        143,   // IMAP
        993,   // IMAPS
        995,   // POP3S
        3389,  // RDP
        3306,  // MySQL
        5432,  // PostgreSQL
        6379,  // Redis
        8080,  // HTTP Alt
        8443,  // HTTPS Alt
    ];

    for &port in &common_ports {
        match std::net::TcpStream::connect_timeout(
            &std::net::SocketAddr::new(*target_ip, port),
            timeout
        ) {
            Ok(_) => {
                return Ok(port);
            }
            Err(_) => {
                // Port is closed or filtered, try next one
                continue;
            }
        }
    }

    // If no common ports work, fall back to port 80 and let the user know
    println!("Warning: No common TCP ports found open on target. Falling back to port 80.");
    Ok(80)
}