Skip to content

Commit b4be4f2

Browse files
committed
fix: improves host runner detection
Rather than waiting on an actual ARP reply for the host (runner) exectuting the program, immediately synthesize a response with 0ms latency
1 parent 236704b commit b4be4f2

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

lib/src/scanners/arp_scanner.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,24 @@ impl ARPScanner {
5858

5959
log::debug!("scanning ARP target: {}", target);
6060

61+
// The OS never sends an ARP reply to its own IP, so synthesize the
62+
// device entry immediately rather than waiting for a reply that will
63+
// never arrive.
64+
if target == self.interface.ipv4 {
65+
self.notifier
66+
.send(ScanMessage::ARPScanDevice(Device {
67+
hostname: String::new(),
68+
ip: self.interface.ipv4,
69+
mac: self.interface.mac,
70+
vendor: String::new(),
71+
is_current_host: true,
72+
open_ports: PortSet::new(),
73+
latency_ms: Some(0),
74+
}))
75+
.map_err(RLanLibError::from_channel_send_error)?;
76+
return Ok(());
77+
}
78+
6179
let arp_packet = ArpPacketBuilder::default()
6280
.source_ip(self.interface.ipv4)
6381
.source_mac(self.interface.mac)

lib/src/scanners/arp_scanner_tests.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,68 @@ fn reports_error_on_packet_send_errors() {
439439
assert!(result.is_err());
440440
}
441441

442+
#[test]
443+
fn emits_current_host_immediately_without_arp() {
444+
let interface = Arc::new(network::get_default_interface().unwrap());
445+
let host_ip = interface.ipv4;
446+
let host_mac = interface.mac;
447+
448+
let mut receiver = MockPacketReader::new();
449+
let mut sender = MockPacketSender::new();
450+
451+
// The heartbeat uses the sender; allow it. The ARP send for the
452+
// host's own IP should be skipped, so the only sends are heartbeats.
453+
sender.expect_send().returning(|_| Ok(()));
454+
// next_packet is called by the reader loop continuously; return a
455+
// non-ARP byte slice so it loops without emitting any spurious devices.
456+
receiver.expect_next_packet().returning(|| Ok(&[1]));
457+
458+
let idle_timeout = Duration::from_secs(2);
459+
let targets = IPTargets::new(vec![host_ip.to_string()]).unwrap();
460+
let (tx, rx) = channel();
461+
462+
let sender: Arc<Mutex<dyn Sender>> = Arc::new(Mutex::new(sender));
463+
let receiver: Arc<Mutex<dyn Reader>> = Arc::new(Mutex::new(receiver));
464+
let wire = Wire(sender, receiver);
465+
466+
let scanner = ARPScanner::builder()
467+
.interface(interface)
468+
.wire(wire)
469+
.targets(targets)
470+
.source_port(54321_u16)
471+
.include_vendor(false)
472+
.include_host_names(false)
473+
.idle_timeout(idle_timeout)
474+
.notifier(tx)
475+
.build()
476+
.unwrap();
477+
478+
let handle = scanner.scan().unwrap();
479+
480+
let mut detected_device: Option<Device> = None;
481+
482+
loop {
483+
if let Ok(msg) = rx.recv() {
484+
match msg {
485+
ScanMessage::Done => break,
486+
ScanMessage::ARPScanDevice(device) => {
487+
detected_device = Some(device);
488+
}
489+
_ => {}
490+
}
491+
}
492+
}
493+
494+
let result = handle.join().unwrap();
495+
assert!(result.is_ok());
496+
497+
let device = detected_device.expect("host device should be emitted");
498+
assert_eq!(device.ip, host_ip);
499+
assert_eq!(device.mac.to_string(), host_mac.to_string());
500+
assert!(device.is_current_host);
501+
assert_eq!(device.latency_ms, Some(0));
502+
}
503+
442504
#[test]
443505
fn reports_errors_from_read_handle() {
444506
let interface = Arc::new(network::get_default_interface().unwrap());

0 commit comments

Comments
 (0)