1use log::*;
4use pnet::packet::{Packet, arp, ethernet};
5use std::{
6 io::Error as IOError,
7 net,
8 sync::{self, Arc, Mutex},
9 thread::{self, JoinHandle},
10 time::Duration,
11};
12
13use crate::{
14 network::NetworkInterface,
15 packet::{self, Reader, Sender, arp_packet},
16 scanners::{Device, ScanError, Scanning},
17 targets::ips::IPTargets,
18};
19
20use super::{ScanMessage, Scanner, heartbeat::HeartBeat};
21
22pub struct ARPScanner<'net> {
24 interface: &'net NetworkInterface,
25 packet_reader: Arc<Mutex<dyn Reader>>,
26 packet_sender: Arc<Mutex<dyn Sender>>,
27 targets: Arc<IPTargets>,
28 source_port: u16,
29 include_vendor: bool,
30 include_host_names: bool,
31 idle_timeout: Duration,
32 notifier: sync::mpsc::Sender<ScanMessage>,
33}
34
35pub struct ARPScannerArgs<'net> {
37 pub interface: &'net NetworkInterface,
39 pub packet_reader: Arc<Mutex<dyn Reader>>,
42 pub packet_sender: Arc<Mutex<dyn Sender>>,
45 pub targets: Arc<IPTargets>,
47 pub source_port: u16,
50 pub include_vendor: bool,
52 pub include_host_names: bool,
54 pub idle_timeout: Duration,
57 pub notifier: sync::mpsc::Sender<ScanMessage>,
60}
61
62impl<'net> ARPScanner<'net> {
63 pub fn new(args: ARPScannerArgs<'net>) -> Self {
65 Self {
66 interface: args.interface,
67 packet_reader: args.packet_reader,
68 packet_sender: args.packet_sender,
69 targets: args.targets,
70 source_port: args.source_port,
71 include_vendor: args.include_vendor,
72 include_host_names: args.include_host_names,
73 idle_timeout: args.idle_timeout,
74 notifier: args.notifier,
75 }
76 }
77}
78
79impl ARPScanner<'_> {
80 fn read_packets(&self, done: sync::mpsc::Receiver<()>) -> JoinHandle<Result<(), ScanError>> {
83 let packet_reader = Arc::clone(&self.packet_reader);
84 let packet_sender = Arc::clone(&self.packet_sender);
85 let include_host_names = self.include_host_names;
86 let include_vendor = self.include_vendor;
87 let source_ipv4 = self.interface.ipv4;
88 let source_mac = self.interface.mac;
89 let source_port = self.source_port.to_owned();
90 let notifier = self.notifier.clone();
91 let (heartbeat_tx, heartbeat_rx) = sync::mpsc::channel::<()>();
92
93 thread::spawn(move || {
99 debug!("starting arp heartbeat thread");
100 let heartbeat = HeartBeat::new(source_mac, source_ipv4, source_port, packet_sender);
101 let interval = Duration::from_secs(1);
102 loop {
103 if heartbeat_rx.try_recv().is_ok() {
104 debug!("stopping arp heartbeat");
105 break;
106 }
107 debug!("sending arp heartbeat");
108 heartbeat.beat();
109 thread::sleep(interval);
110 }
111 });
112
113 thread::spawn(move || -> Result<(), ScanError> {
114 let mut reader = packet_reader.lock().map_err(|e| ScanError {
115 ip: None,
116 port: None,
117 error: Box::from(e.to_string()),
118 })?;
119
120 loop {
121 if done.try_recv().is_ok() {
122 debug!("exiting arp packet reader");
123 if let Err(e) = heartbeat_tx.send(()) {
124 error!("failed to stop heartbeat: {}", e);
125 }
126 break;
127 }
128
129 let pkt = reader.next_packet().map_err(|e| ScanError {
130 ip: None,
131 port: None,
132 error: e,
133 })?;
134
135 let eth = ethernet::EthernetPacket::new(pkt);
136
137 if eth.is_none() {
138 continue;
139 }
140
141 let eth = eth.unwrap();
142
143 let header = arp::ArpPacket::new(eth.payload());
144
145 if header.is_none() {
146 continue;
147 }
148
149 let header = header.unwrap();
150
151 let op = header.get_operation();
152
153 let is_expected_arp_packet = op == arp::ArpOperations::Reply;
156
157 if !is_expected_arp_packet {
158 continue;
159 }
160
161 let ip4 = header.get_sender_proto_addr();
162 let mac = eth.get_source().to_string();
163
164 let notification_sender = notifier.clone();
165
166 thread::spawn(move || {
169 let mut hostname: String = String::from("");
170 if include_host_names {
171 debug!("looking up hostname for {}", ip4);
172 if let Ok(host) = dns_lookup::lookup_addr(&ip4.into()) {
173 hostname = host;
174 }
175 }
176
177 let mut vendor = String::from("");
178 if include_vendor && let Some(vendor_data) = oui_data::lookup(&mac) {
179 vendor = vendor_data.organization().to_owned();
180 }
181
182 let _ = notification_sender.send(ScanMessage::ARPScanResult(Device {
183 hostname,
184 ip: ip4.to_string(),
185 mac,
186 vendor,
187 is_current_host: ip4 == source_ipv4,
188 }));
189 });
190 }
191
192 Ok(())
193 })
194 }
195}
196
197impl Scanner for ARPScanner<'_> {
199 fn scan(&self) -> JoinHandle<Result<(), ScanError>> {
200 debug!("performing ARP scan on targets: {:?}", self.targets);
201 debug!("include_vendor: {}", self.include_vendor);
202 debug!("include_host_names: {}", self.include_host_names);
203 debug!("starting arp packet reader");
204 let (done_tx, done_rx) = sync::mpsc::channel::<()>();
205 let notifier = self.notifier.clone();
206 let packet_sender = Arc::clone(&self.packet_sender);
207 let idle_timeout = self.idle_timeout;
208 let source_ipv4 = self.interface.ipv4;
209 let source_mac = self.interface.mac;
210 let targets = Arc::clone(&self.targets);
211
212 let read_handle = self.read_packets(done_rx);
213
214 thread::spawn(move || -> Result<(), ScanError> {
216 let process_target = |target_ipv4: net::Ipv4Addr| {
217 thread::sleep(packet::DEFAULT_PACKET_SEND_TIMING);
219
220 debug!("scanning ARP target: {}", target_ipv4);
221
222 let pkt_buf = arp_packet::build(source_ipv4, source_mac, target_ipv4);
223
224 notifier
226 .send(ScanMessage::Info(Scanning {
227 ip: target_ipv4.to_string(),
228 port: None,
229 }))
230 .map_err(|e| ScanError {
231 ip: Some(target_ipv4.to_string()),
232 port: None,
233 error: Box::from(e),
234 })?;
235
236 let mut pkt_sender = packet_sender.lock().map_err(|e| ScanError {
237 ip: Some(target_ipv4.to_string()),
238 port: None,
239 error: Box::from(IOError::other(e.to_string())),
240 })?;
241
242 pkt_sender.send(&pkt_buf).map_err(|e| ScanError {
244 ip: Some(target_ipv4.to_string()),
245 port: None,
246 error: e,
247 })?;
248
249 Ok(())
250 };
251
252 let mut scan_error: Option<ScanError> = None;
253
254 if let Err(err) = targets.lazy_loop(process_target) {
255 scan_error = Some(err);
256 }
257
258 thread::sleep(idle_timeout);
259
260 notifier.send(ScanMessage::Done).map_err(|e| ScanError {
261 ip: None,
262 port: None,
263 error: Box::from(e),
264 })?;
265
266 let _ = done_tx.send(());
269
270 let read_result = read_handle.join().map_err(|_| ScanError {
271 ip: None,
272 port: None,
273 error: Box::from("error encountered in arp packet reading thread"),
274 })?;
275
276 if let Some(err) = scan_error {
277 return Err(err);
278 }
279
280 read_result
281 })
282 }
283}
284
285#[cfg(test)]
286#[path = "./arp_scanner_tests.rs"]
287mod tests;