network_toolset 0.1.0

A comprehensive network diagnostic toolset implemented in Rust
Documentation
use std::net::{IpAddr, Ipv4Addr};
use anyhow::Result;

/// Check if the current process has administrator/root privileges
pub fn check_admin_privileges() -> bool {
    #[cfg(unix)]
    {
        // On Unix systems, check if we're root
        unsafe { libc::geteuid() == 0 }
    }

    #[cfg(windows)]
    {
        // On Windows, simplified check - assume we need to try creating a raw socket
        // This will be checked more specifically when we try to create sockets
        true // We'll let the actual socket creation fail if we don't have privileges
    }

    #[cfg(not(any(unix, windows)))]
    {
        // Default to false for other platforms
        false
    }
}

/// Parse a hostname or IP address to an IpAddr
pub fn parse_hostname(hostname: &str) -> Result<IpAddr> {
    // Try to parse as IP address first
    if let Ok(ip) = hostname.parse::<IpAddr>() {
        return Ok(ip);
    }

    // If not an IP, try DNS resolution
    use std::net::ToSocketAddrs;

    let socket_addrs: Vec<_> = format!("{}:0", hostname)
        .to_socket_addrs()
        .map_err(|e| anyhow::anyhow!("DNS resolution failed for '{}': {}", hostname, e))?
        .collect();

    if let Some(addr) = socket_addrs.first() {
        Ok(addr.ip())
    } else {
        Err(anyhow::anyhow!("Could not resolve hostname: {}", hostname))
    }
}

/// Convert CIDR notation to a vector of IP addresses
pub fn cidr_to_ip_range(network: &str) -> Result<Vec<Ipv4Addr>> {
    let parts: Vec<&str> = network.split('/').collect();
    if parts.len() != 2 {
        return Err(anyhow::anyhow!("Invalid CIDR format: {}", network));
    }

    let base_ip: Ipv4Addr = parts[0].parse()
        .map_err(|e| anyhow::anyhow!("Invalid IP address: {}", e))?;

    let prefix_len: u32 = parts[1].parse()
        .map_err(|e| anyhow::anyhow!("Invalid prefix length: {}", e))?;

    if prefix_len > 32 {
        return Err(anyhow::anyhow!("Prefix length must be <= 32"));
    }

    let base_u32 = u32::from(base_ip);
    let host_bits = 32 - prefix_len;
    let host_count = if host_bits == 0 { 1 } else { 1u32 << host_bits };
    let mask = if host_bits == 0 { 0xFFFFFFFF } else { !((1u32 << host_bits) - 1) };
    let network_base = base_u32 & mask;

    let mut ips = Vec::new();
    for i in 0..host_count {
        let ip_u32 = network_base + i;
        ips.push(Ipv4Addr::from(ip_u32));
    }

    Ok(ips)
}

/// Calculate ICMP checksum
pub fn calculate_icmp_checksum(data: &[u8]) -> u16 {
    let mut sum: u32 = 0;

    // Sum 16-bit words
    for chunk in data.chunks_exact(2) {
        let word = u16::from_be_bytes([chunk[0], chunk[1]]) as u32;
        sum += word;
    }

    // Handle odd length
    if data.len() % 2 != 0 {
        sum += data[data.len() - 1] as u32;
    }

    // Add carry bits
    while sum >> 16 != 0 {
        sum = (sum & 0xFFFF) + (sum >> 16);
    }

    // One's complement
    !sum as u16
}

/// Get current timestamp in milliseconds
pub fn get_timestamp_ms() -> u64 {
    use std::time::{SystemTime, UNIX_EPOCH};
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap_or_default()
        .as_millis() as u64
}

/// Format bytes as hex string
pub fn format_hex(bytes: &[u8]) -> String {
    bytes.iter()
        .map(|b| format!("{:02x}", b))
        .collect::<Vec<_>>()
        .join(":")
}

/// Get a unique identifier for this process (for ICMP/packet identification)
pub fn get_process_id() -> u16 {
    std::process::id() as u16
}