use crate::config::PCAP_WAIT_TIME_MILLIS;
use crate::host::Host;
use crate::packet::frame::PacketFrame;
use crate::pcap::PacketCaptureOptions;
use crate::scan::setting::{HostScanSetting, PortScanSetting};
use netdev::Interface;
use nex::datalink::RawSender;
use nex::packet::ip::IpNextProtocol;
use std::collections::HashSet;
use std::net::SocketAddr;
use std::sync::mpsc::Sender;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::Duration;
use super::packet::{build_hostscan_packet, build_portscan_packet};
use super::result::{
ScanError, ScanResult, ScanStatus, parse_hostscan_result, parse_portscan_result,
};
use super::setting::{HostScanType, PortScanType};
pub(crate) fn send_hostscan_packets(
tx: &mut Box<dyn RawSender>,
interface: &Interface,
targets: &[Host],
ptx: &Arc<Mutex<Sender<Host>>>,
scan_type: HostScanType,
send_rate: Duration,
) {
for target in targets {
let packet = build_hostscan_packet(interface, target, &scan_type, false);
match tx.send(&packet) {
Some(_) => {
if let Ok(lock) = ptx.lock() {
if let Err(e) = lock.send(target.clone()) {
eprintln!("Failed to send message: {}", e);
}
}
}
None => {
eprintln!("Failed to send packet");
}
}
if !send_rate.is_zero() {
thread::sleep(send_rate);
}
}
}
pub(crate) fn send_portscan_packets(
tx: &mut Box<dyn RawSender>,
interface: &Interface,
targets: &[Host],
ptx: &Arc<Mutex<Sender<SocketAddr>>>,
scan_type: PortScanType,
send_rate: Duration,
) {
for target in targets {
match scan_type {
PortScanType::TcpSynScan => {
for port in &target.ports {
let packet =
build_portscan_packet(interface, target.ip_addr, port.number, false);
match tx.send(&packet) {
Some(_) => {
if let Ok(lock) = ptx.lock() {
if let Err(e) =
lock.send(SocketAddr::new(target.ip_addr, port.number))
{
eprintln!("Failed to send message: {}", e);
}
}
}
None => {
eprintln!("Failed to send packet");
}
}
if !send_rate.is_zero() {
thread::sleep(send_rate);
}
}
}
PortScanType::TcpConnectScan => {
}
}
}
}
pub(crate) fn scan_hosts(
scan_setting: HostScanSetting,
ptx: &Arc<Mutex<Sender<Host>>>,
) -> ScanResult {
let interface = match crate::interface::get_interface_by_index(scan_setting.if_index) {
Some(interface) => interface,
None => return ScanResult::error(ScanError::InterfaceNotFound),
};
let config = nex::datalink::Config {
write_buffer_size: 4096,
read_buffer_size: 4096,
read_timeout: Some(scan_setting.wait_time),
write_timeout: None,
channel_type: nex::datalink::ChannelType::Layer2,
bpf_fd_attempts: 1000,
linux_fanout: None,
promiscuous: false,
};
let (mut tx, mut rx) = match nex::datalink::channel(&interface, config) {
Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx),
Ok(_) => return ScanResult::error(ScanError::UnhandledChannelType),
Err(e) => return ScanResult::error(ScanError::ChannelCreationFailed(e.to_string())),
};
let mut capture_options: PacketCaptureOptions = PacketCaptureOptions {
interface_index: interface.index,
src_ips: HashSet::new(),
dst_ips: HashSet::new(),
src_ports: HashSet::new(),
dst_ports: HashSet::new(),
ether_types: HashSet::new(),
ip_protocols: HashSet::new(),
capture_timeout: scan_setting.timeout,
tunnel: interface.is_tun(),
loopback: interface.is_loopback(),
};
for target in scan_setting.targets.clone() {
capture_options.src_ips.insert(target.ip_addr);
}
match scan_setting.scan_type {
HostScanType::IcmpPingScan => {
capture_options.ip_protocols.insert(IpNextProtocol::Icmp);
capture_options.ip_protocols.insert(IpNextProtocol::Icmpv6);
}
HostScanType::TcpPingScan => {
capture_options.ip_protocols.insert(IpNextProtocol::Tcp);
for target in scan_setting.targets.clone() {
for port in target.ports {
capture_options.src_ports.insert(port.number);
}
}
}
HostScanType::UdpPingScan => {
capture_options.ip_protocols.insert(IpNextProtocol::Udp);
capture_options.ip_protocols.insert(IpNextProtocol::Icmp);
capture_options.ip_protocols.insert(IpNextProtocol::Icmpv6);
}
}
let stop: Arc<Mutex<bool>> = Arc::new(Mutex::new(false));
let stop_handle = Arc::clone(&stop);
let packets: Arc<Mutex<Vec<PacketFrame>>> = Arc::new(Mutex::new(vec![]));
let receive_packets: Arc<Mutex<Vec<PacketFrame>>> = Arc::clone(&packets);
let pcap_handler = thread::spawn(move || {
let packets: Vec<PacketFrame> =
crate::pcap::start_capture(&mut rx, capture_options, &stop_handle);
match receive_packets.lock() {
Ok(mut receive_packets) => {
for p in packets {
receive_packets.push(p);
}
}
Err(e) => {
eprintln!("Failed to lock receive_packets: {}", e);
}
}
});
thread::sleep(Duration::from_millis(PCAP_WAIT_TIME_MILLIS));
let start_time = std::time::Instant::now();
send_hostscan_packets(
&mut tx,
&interface,
&scan_setting.targets,
ptx,
scan_setting.scan_type,
scan_setting.send_rate,
);
thread::sleep(scan_setting.wait_time);
match stop.lock() {
Ok(mut stop) => {
*stop = true;
}
Err(e) => {
eprintln!("Failed to lock stop: {}", e);
}
}
match pcap_handler.join() {
Ok(_) => {}
Err(e) => {
eprintln!("Failed to join pcap_handler: {:?}", e);
}
}
let mut scan_result: ScanResult = ScanResult::new();
match packets.lock() {
Ok(packets) => {
scan_result = parse_hostscan_result(packets.clone(), scan_setting);
}
Err(e) => {
eprintln!("Failed to lock packets: {}", e);
}
}
scan_result.scan_time = start_time.elapsed();
scan_result.scan_status = ScanStatus::Done;
scan_result
}
pub(crate) fn scan_ports(
scan_setting: PortScanSetting,
ptx: &Arc<Mutex<Sender<SocketAddr>>>,
) -> ScanResult {
let interface = match crate::interface::get_interface_by_index(scan_setting.if_index) {
Some(interface) => interface,
None => return ScanResult::error(ScanError::InterfaceNotFound),
};
let config = nex::datalink::Config {
write_buffer_size: 4096,
read_buffer_size: 4096,
read_timeout: Some(scan_setting.wait_time),
write_timeout: None,
channel_type: nex::datalink::ChannelType::Layer2,
bpf_fd_attempts: 1000,
linux_fanout: None,
promiscuous: false,
};
let (mut tx, mut rx) = match nex::datalink::channel(&interface, config) {
Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx),
Ok(_) => return ScanResult::error(ScanError::UnhandledChannelType),
Err(e) => return ScanResult::error(ScanError::ChannelCreationFailed(e.to_string())),
};
let mut capture_options: PacketCaptureOptions = PacketCaptureOptions {
interface_index: interface.index,
src_ips: HashSet::new(),
dst_ips: HashSet::new(),
src_ports: HashSet::new(),
dst_ports: HashSet::new(),
ether_types: HashSet::new(),
ip_protocols: HashSet::new(),
capture_timeout: scan_setting.timeout,
tunnel: interface.is_tun(),
loopback: interface.is_loopback(),
};
for target in scan_setting.targets.clone() {
capture_options.src_ips.insert(target.ip_addr);
capture_options.src_ports.extend(target.get_ports());
}
match scan_setting.scan_type {
PortScanType::TcpSynScan => {
capture_options.ip_protocols.insert(IpNextProtocol::Tcp);
}
PortScanType::TcpConnectScan => {
capture_options.ip_protocols.insert(IpNextProtocol::Tcp);
}
}
let stop: Arc<Mutex<bool>> = Arc::new(Mutex::new(false));
let stop_handle = Arc::clone(&stop);
let packets: Arc<Mutex<Vec<PacketFrame>>> = Arc::new(Mutex::new(vec![]));
let receive_packets: Arc<Mutex<Vec<PacketFrame>>> = Arc::clone(&packets);
let pcap_handler = thread::spawn(move || {
let packets: Vec<PacketFrame> =
crate::pcap::start_capture(&mut rx, capture_options, &stop_handle);
match receive_packets.lock() {
Ok(mut receive_packets) => {
for p in packets {
receive_packets.push(p);
}
}
Err(e) => {
eprintln!("Failed to lock receive_packets: {}", e);
}
}
});
thread::sleep(Duration::from_millis(PCAP_WAIT_TIME_MILLIS));
let start_time = std::time::Instant::now();
send_portscan_packets(
&mut tx,
&interface,
&scan_setting.targets,
ptx,
scan_setting.scan_type,
scan_setting.send_rate,
);
thread::sleep(scan_setting.wait_time);
match stop.lock() {
Ok(mut stop) => {
*stop = true;
}
Err(e) => {
eprintln!("Failed to lock stop: {}", e);
}
}
match pcap_handler.join() {
Ok(_) => {}
Err(e) => {
eprintln!("Failed to join pcap_handler: {:?}", e);
}
}
let mut scan_result: ScanResult = ScanResult::new();
match packets.lock() {
Ok(packets) => {
scan_result = parse_portscan_result(packets.clone(), scan_setting);
}
Err(e) => {
eprintln!("Failed to lock packets: {}", e);
}
}
scan_result.scan_time = start_time.elapsed();
scan_result.scan_status = ScanStatus::Done;
scan_result
}