use crate::error::AppResult;
use pnet::ipnetwork;
use pnet::packet::arp::{ArpHardwareTypes, ArpOperations, MutableArpPacket};
use pnet::packet::ethernet::{EtherTypes, MutableEthernetPacket};
use pnet::packet::{MutablePacket, Packet};
use pnet_datalink::{MacAddr as PnetMacAddr, NetworkInterface};
use std::net::Ipv4Addr;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use tokio::sync::mpsc;
use tokio::time::sleep;
use crate::backend::PacketSink;
use crate::constants::{network, timing};
use crate::event::{Event, ScannerEvent};
use crate::scanner::arp_validator::ArpValidation;
use crate::types::MacAddr;
fn sanitize_ip(ip: Ipv4Addr) -> String {
let octets = ip.octets();
format!("{}.{}.{}.x", octets[0], octets[1], octets[2])
}
fn sanitize_mac(_mac: MacAddr) -> &'static str {
"xx:xx:xx:xx:xx:xx"
}
pub fn find_source_ip(network_interface: &NetworkInterface) -> AppResult<Ipv4Addr> {
let potential_network = network_interface
.ips
.iter()
.find(|network| network.is_ipv4());
match potential_network.map(|network| network.ip()) {
Some(std::net::IpAddr::V4(ipv4_addr)) => Ok(ipv4_addr),
Some(other) => Err(format!("Expected IPv4, found: {}", other).into()),
None => Err("No IPv4 address found on interface".into()),
}
}
pub fn send_arp_request(
packet_sink: &mut Box<dyn PacketSink>,
interface: &NetworkInterface,
target_ip: Ipv4Addr,
arp_validator: Option<&Arc<Mutex<crate::scanner::arp_validator::ArpValidator>>>,
) -> AppResult<()> {
let mut ethernet_buffer = vec![0u8; network::ETHERNET_ARP_BUFFER_SIZE];
let mut ethernet_packet = MutableEthernetPacket::new(&mut ethernet_buffer)
.ok_or("Failed to create Ethernet packet from buffer")?;
let target_mac_broadcast = PnetMacAddr::broadcast();
let source_mac = interface.mac.ok_or("Interface missing MAC address")?;
ethernet_packet.set_destination(target_mac_broadcast);
ethernet_packet.set_source(source_mac);
let selected_ethertype = EtherTypes::Arp;
ethernet_packet.set_ethertype(selected_ethertype);
let mut arp_buffer = [0u8; network::ARP_PACKET_SIZE];
let mut arp_packet =
MutableArpPacket::new(&mut arp_buffer).ok_or("Failed to create ARP packet from buffer")?;
let source_ip = find_source_ip(interface)?;
arp_packet.set_hardware_type(ArpHardwareTypes::Ethernet);
arp_packet.set_protocol_type(EtherTypes::Ipv4);
arp_packet.set_hw_addr_len(6);
arp_packet.set_proto_addr_len(4);
arp_packet.set_operation(ArpOperations::Request);
arp_packet.set_sender_hw_addr(source_mac);
arp_packet.set_sender_proto_addr(source_ip);
arp_packet.set_target_hw_addr(target_mac_broadcast);
arp_packet.set_target_proto_addr(target_ip);
ethernet_packet.set_payload(arp_packet.packet_mut());
if let Some(validator) = arp_validator {
if let Ok(mut validator) = validator.try_lock() {
validator.record_request(target_ip);
}
}
packet_sink
.send_packet(ethernet_packet.to_immutable().packet())
.map_err(|e| format!("Failed to send ARP request: {}", e))?;
Ok(())
}
pub async fn scan_range(
nif: &NetworkInterface,
ip_network: ipnetwork::IpNetwork,
scanner_outputs: mpsc::Sender<Event>,
packet_sink: &mut Box<dyn PacketSink>,
arp_validator: Option<&Arc<Mutex<crate::scanner::arp_validator::ArpValidator>>>,
) {
use std::net::IpAddr;
if let Err(e) = scanner_outputs
.send(Event::Scanner(ScannerEvent::BeginScan))
.await
{
tracing::error!("Failed to send BeginScan event: {}", e);
return;
}
let sender_clone = scanner_outputs.clone();
let sender = sender_clone;
for ip_addr in ip_network.iter() {
if let IpAddr::V4(ipv4_address) = ip_addr {
sleep(Duration::from_millis(timing::ARP_SCAN_DELAY_MS)).await;
let _ = send_arp_request(packet_sink, nif, ipv4_address, arp_validator);
}
}
if let Err(e) = sender.send(Event::Scanner(ScannerEvent::Complete)).await {
tracing::error!("Failed to send Complete event: {}", e);
}
}
pub fn get_host_infos(
buffer: &[u8],
arp_validator: Option<&Arc<Mutex<crate::scanner::arp_validator::ArpValidator>>>,
) -> Option<crate::host::Host> {
use crate::types::MacAddr;
use chrono::Local;
use pnet::packet::arp::{ArpOperations, ArpPacket};
use pnet::packet::ethernet::MutableEthernetPacket;
let arp_packet = ArpPacket::new(&buffer[MutableEthernetPacket::minimum_packet_size()..]);
if let Some(arp) = arp_packet {
let operation = arp.get_operation();
if operation != ArpOperations::Reply {
return None;
}
let sender_ipv4 = arp.get_sender_proto_addr();
let sender_mac_raw = arp.get_sender_hw_addr();
let sender_mac: MacAddr = sender_mac_raw.into();
if let Some(validator) = arp_validator {
let validation = if let Ok(mut validator) = validator.try_lock() {
validator.validate_reply(sender_ipv4)
} else {
ArpValidation::Unsolicited
};
match validation {
ArpValidation::Valid => {
}
ArpValidation::Unsolicited => {
tracing::warn!(
"Rejected unsolicited ARP reply for {} from {} (potential ARP poisoning)",
sanitize_ip(sender_ipv4),
sanitize_mac(sender_mac)
);
return None;
}
ArpValidation::Expired => {
tracing::debug!(
"Rejected expired ARP reply for {} from {}",
sanitize_ip(sender_ipv4),
sanitize_mac(sender_mac)
);
return None;
}
}
}
let host = crate::host::Host {
hostname: None,
time: Local::now(),
mac: sender_mac,
ipv4: sender_ipv4,
speed: None,
};
Some(host)
} else {
None
}
}