sal-net 0.1.0

SAL Network - Network connectivity utilities for TCP, HTTP, and SSH
Documentation
//! Rhai wrappers for network module functions
//!
//! This module provides Rhai wrappers for network connectivity functions.

use rhai::{Engine, EvalAltResult, Module};

/// Create a Rhai module with network functions
pub fn create_module() -> Module {
    // For now, we'll use a simpler approach and register functions via engine
    // This ensures compatibility with Rhai's type system
    // The module is created but functions are registered through register_net_module

    Module::new()
}

/// Register network module functions with the Rhai engine
pub fn register_net_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
    // TCP functions
    engine.register_fn("tcp_check", tcp_check);
    engine.register_fn("tcp_ping", tcp_ping);

    // HTTP functions
    engine.register_fn("http_check", http_check);
    engine.register_fn("http_status", http_status);

    // SSH functions
    engine.register_fn("ssh_execute", ssh_execute);
    engine.register_fn("ssh_execute_output", ssh_execute_output);
    engine.register_fn("ssh_ping", ssh_ping_host);

    Ok(())
}

/// Check if a TCP port is open
pub fn tcp_check(host: &str, port: i64) -> bool {
    // Use std::net::TcpStream for synchronous connection test
    use std::net::{SocketAddr, TcpStream};
    use std::time::Duration;

    // Parse the address
    let addr_str = format!("{}:{}", host, port);
    if let Ok(socket_addr) = addr_str.parse::<SocketAddr>() {
        // Try to connect with a timeout
        TcpStream::connect_timeout(&socket_addr, Duration::from_secs(5)).is_ok()
    } else {
        // Try to resolve hostname first
        match std::net::ToSocketAddrs::to_socket_addrs(&addr_str) {
            Ok(mut addrs) => {
                if let Some(addr) = addrs.next() {
                    TcpStream::connect_timeout(&addr, Duration::from_secs(5)).is_ok()
                } else {
                    false
                }
            }
            Err(_) => false,
        }
    }
}

/// Ping a host using ICMP (cross-platform)
pub fn tcp_ping(host: &str) -> bool {
    // Use system ping command for synchronous operation
    use std::process::Command;

    // Cross-platform ping implementation
    let mut cmd = Command::new("ping");

    #[cfg(target_os = "windows")]
    {
        cmd.arg("-n").arg("1").arg("-w").arg("5000"); // Windows: -n count, -w timeout in ms
    }

    #[cfg(not(target_os = "windows"))]
    {
        cmd.arg("-c").arg("1").arg("-W").arg("5"); // Unix: -c count, -W timeout in seconds
    }

    cmd.arg(host);

    match cmd.output() {
        Ok(output) => output.status.success(),
        Err(_) => false,
    }
}

/// Check if an HTTP URL is reachable
pub fn http_check(url: &str) -> bool {
    use std::time::Duration;

    // Create a blocking HTTP client with timeout
    let client = match reqwest::blocking::Client::builder()
        .timeout(Duration::from_secs(10))
        .build()
    {
        Ok(client) => client,
        Err(_) => return false,
    };

    // Try to make a HEAD request
    match client.head(url).send() {
        Ok(response) => response.status().is_success(),
        Err(_) => false,
    }
}

/// Get HTTP status code from a URL
pub fn http_status(url: &str) -> i64 {
    use std::time::Duration;

    // Create a blocking HTTP client with timeout
    let client = match reqwest::blocking::Client::builder()
        .timeout(Duration::from_secs(10))
        .build()
    {
        Ok(client) => client,
        Err(_) => return -1,
    };

    // Try to make a HEAD request
    match client.head(url).send() {
        Ok(response) => response.status().as_u16() as i64,
        Err(_) => -1,
    }
}

/// Execute a command via SSH - returns exit code as i64
pub fn ssh_execute(host: &str, user: &str, command: &str) -> i64 {
    use std::process::Command;

    let mut cmd = Command::new("ssh");
    cmd.arg("-o")
        .arg("ConnectTimeout=5")
        .arg("-o")
        .arg("StrictHostKeyChecking=no")
        .arg(format!("{}@{}", user, host))
        .arg(command);

    match cmd.output() {
        Ok(output) => output.status.code().unwrap_or(-1) as i64,
        Err(_) => -1,
    }
}

/// Execute a command via SSH and get output - returns output as string
pub fn ssh_execute_output(host: &str, user: &str, command: &str) -> String {
    use std::process::Command;

    let mut cmd = Command::new("ssh");
    cmd.arg("-o")
        .arg("ConnectTimeout=5")
        .arg("-o")
        .arg("StrictHostKeyChecking=no")
        .arg(format!("{}@{}", user, host))
        .arg(command);

    match cmd.output() {
        Ok(output) => String::from_utf8_lossy(&output.stdout).to_string(),
        Err(_) => "SSH command failed".to_string(),
    }
}

/// Test SSH connectivity to a host
pub fn ssh_ping_host(host: &str, user: &str) -> bool {
    use std::process::Command;

    let mut cmd = Command::new("ssh");
    cmd.arg("-o")
        .arg("ConnectTimeout=5")
        .arg("-o")
        .arg("StrictHostKeyChecking=no")
        .arg("-o")
        .arg("BatchMode=yes") // Non-interactive
        .arg(format!("{}@{}", user, host))
        .arg("echo 'Connection successful'");

    match cmd.output() {
        Ok(output) => output.status.success(),
        Err(_) => false,
    }
}