r-lancli 0.11.0

A command-line interface for performing network scanning operations on local area networks (LANs)
use mockall::mock;
use mpsc::channel;
use r_lanlib::{
    error::Result,
    scanners::{Port, PortSet, Scanner},
    wire::DEFAULT_PACKET_SEND_TIMING,
};
use std::{
    net::Ipv4Addr,
    thread::{self, JoinHandle},
    time::Duration,
};

use super::*;

mock! {
    ArpScanner{}
    impl Scanner for ArpScanner {
        fn scan(&self) -> Result<JoinHandle<r_lanlib::error::Result<()>>>;
    }
}

mock! {
    SynScanner{}
    impl Scanner for SynScanner {
        fn scan(&self) -> Result<JoinHandle<r_lanlib::error::Result<()>>>;
    }
}

#[test]
fn prints_args() {
    let interface = network::get_default_interface().unwrap();

    let args = Args {
        json: false,
        arp_only: false,
        debug: false,
        host_names: true,
        idle_timeout_ms: 2000,
        interface: Some("interface_name".to_string()),
        ports: vec!["22".to_string()],
        quiet: false,
        source_port: 54321,
        targets: vec!["192.168.1.1".to_string()],
        vendor: true,
        throttle: DEFAULT_PACKET_SEND_TIMING,
    };

    print_args(&args, &interface);
}

#[test]
fn initializes_logger() {
    let args = Args {
        json: false,
        arp_only: false,
        debug: false,
        host_names: true,
        idle_timeout_ms: 2000,
        interface: Some("interface_name".to_string()),
        ports: vec!["22".to_string()],
        quiet: false,
        source_port: 54321,
        targets: vec!["192.168.1.1".to_string()],
        vendor: true,
        throttle: DEFAULT_PACKET_SEND_TIMING,
    };

    initialize_logger(&args).unwrap();
}

#[test]
fn prints_arp_table_results() {
    let args = Args {
        json: false,
        arp_only: false,
        debug: false,
        host_names: true,
        idle_timeout_ms: 2000,
        interface: Some("interface_name".to_string()),
        ports: vec!["22".to_string()],
        quiet: false,
        source_port: 54321,
        targets: vec!["192.168.1.1".to_string()],
        vendor: true,
        throttle: DEFAULT_PACKET_SEND_TIMING,
    };

    let device = Device {
        hostname: "hostname".to_string(),
        ip: Ipv4Addr::new(192, 168, 1, 1),
        vendor: "vendor".to_string(),
        ..Device::default()
    };

    print_arp(&args, &vec![device]).unwrap();
}

#[test]
fn prints_arp_json_results() {
    let args = Args {
        json: true,
        arp_only: false,
        debug: false,
        host_names: true,
        idle_timeout_ms: 2000,
        interface: Some("interface_name".to_string()),
        ports: vec!["22".to_string()],
        quiet: false,
        source_port: 54321,
        targets: vec!["192.168.1.1".to_string()],
        vendor: true,
        throttle: DEFAULT_PACKET_SEND_TIMING,
    };

    let device = Device {
        hostname: "hostname".to_string(),
        ip: Ipv4Addr::new(192, 168, 1, 1),
        vendor: "vendor".to_string(),
        ..Device::default()
    };

    print_arp(&args, &vec![device]).unwrap();
}

#[test]
fn prints_syn_table_results() {
    let args = Args {
        json: false,
        arp_only: false,
        debug: false,
        host_names: true,
        idle_timeout_ms: 2000,
        interface: Some("interface_name".to_string()),
        ports: vec!["22".to_string()],
        quiet: false,
        source_port: 54321,
        targets: vec!["192.168.1.1".to_string()],
        vendor: true,
        throttle: DEFAULT_PACKET_SEND_TIMING,
    };

    let port = Port {
        id: 22,
        service: "ssh".to_string(),
    };

    let mut open_ports = PortSet::new();
    open_ports.0.insert(port);

    let device = Device {
        hostname: "hostname".to_string(),
        ip: Ipv4Addr::new(192, 168, 1, 1),
        vendor: "vendor".to_string(),
        open_ports,
        ..Device::default()
    };

    let devices = HashMap::from([(device.ip, device)]);
    print_syn(&args, &devices).unwrap();
}

#[test]
fn prints_syn_json_results() {
    let args = Args {
        json: true,
        arp_only: false,
        debug: false,
        host_names: true,
        idle_timeout_ms: 2000,
        interface: Some("interface_name".to_string()),
        ports: vec!["22".to_string()],
        quiet: false,
        source_port: 54321,
        targets: vec!["192.168.1.1".to_string()],
        vendor: true,
        throttle: DEFAULT_PACKET_SEND_TIMING,
    };

    let port = Port {
        id: 22,
        service: "ssh".to_string(),
    };

    let mut open_ports = PortSet::new();
    open_ports.0.insert(port);

    let device = Device {
        hostname: "hostname".to_string(),
        ip: Ipv4Addr::new(192, 168, 1, 1),
        vendor: "vendor".to_string(),
        open_ports,
        ..Device::default()
    };

    let devices = HashMap::from([(device.ip, device)]);
    print_syn(&args, &devices).unwrap();
}

#[test]
fn performs_arp_scan() {
    let mut arp = MockArpScanner::new();

    let (tx, rx) = channel();

    let device = Device {
        hostname: "hostname".to_string(),
        ip: Ipv4Addr::new(192, 168, 1, 1),
        vendor: "vendor".to_string(),
        ..Device::default()
    };

    let device_clone = device.clone();

    thread::spawn(move || {
        let _ = tx.send(ScanMessage::ARPScanDevice(device_clone));
        thread::sleep(Duration::from_millis(500));
        let _ = tx.send(ScanMessage::Done);
    });

    arp.expect_scan().returning(|| {
        let handle: JoinHandle<r_lanlib::error::Result<()>> =
            thread::spawn(|| Ok(()));
        Ok(handle)
    });

    let result = process_arp(&arp, rx);

    assert!(result.is_ok());

    let (devices, _) = result.unwrap();

    assert_eq!(devices[0], device);
}

#[test]
fn performs_syn_scan() {
    let mut syn = MockSynScanner::new();

    let (tx, rx) = channel();

    let mut ports = PortSet::new();
    ports.0.insert(Port {
        id: 22,
        service: "ssh".to_string(),
    });

    let device = Device {
        hostname: "hostname".to_string(),
        ip: Ipv4Addr::new(192, 168, 1, 1),
        vendor: "vendor".to_string(),
        open_ports: ports,
        ..Device::default()
    };

    let device_clone = device.clone();

    thread::spawn(move || {
        let _ = tx.send(ScanMessage::SYNScanDevice(device_clone));
        thread::sleep(Duration::from_millis(500));
        let _ = tx.send(ScanMessage::Done);
    });

    syn.expect_scan().returning(|| {
        let handle: JoinHandle<r_lanlib::error::Result<()>> =
            thread::spawn(|| Ok(()));
        Ok(handle)
    });

    let result = process_syn(&syn, vec![device.clone()], rx);

    assert!(result.is_ok());

    let devices = result.unwrap();

    assert_eq!(devices.get(&device.ip), Some(&device));
}