Consolidate ARP/mDNS modules under scanners::{arp,mdns}::{finder,scanner,status}

Group each discovery protocol's primitive (finder), orchestration loop
(scanner), and status state under one module tree instead of splitting
them between device_finders/ and crate-root *_scanner / *_scanner_status
files. Also includes incidental rustfmt fixes to a few pre-existing
long lines.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
rzuasti
2026-05-30 09:48:29 -04:00
co-authored by Claude Opus 4.7
parent 54211c57d7
commit c12e700c27
16 changed files with 61 additions and 38 deletions
+16 -4
View File
@@ -208,7 +208,9 @@ pub fn seen(
// device_type is only written when the stored value is empty, so a value chosen by the
// user (via register) or previously deduced is never overwritten by a later sighting.
if !vendor.is_empty() {
sql.push_str(", vendor=?, device_type=CASE WHEN device_type='' THEN ? ELSE device_type END");
sql.push_str(
", vendor=?, device_type=CASE WHEN device_type='' THEN ? ELSE device_type END",
);
params.push(vendor.into());
params.push(device_type.into());
}
@@ -377,8 +379,17 @@ mod tests {
);
// Filter by owner substring - "oh" matches "John" but not "Sarah"
let devices: Vec<Device> =
list_devices(None, None, None, Some("oh".to_string()), None, None, None, None).unwrap();
let devices: Vec<Device> = list_devices(
None,
None,
None,
Some("oh".to_string()),
None,
None,
None,
None,
)
.unwrap();
assert!(
devices.len() >= 1,
@@ -426,7 +437,8 @@ mod tests {
tests_common::setup().await;
// First page with 2 devices
let first_page = list_devices(None, None, None, None, None, None, Some(0), Some(2)).unwrap();
let first_page =
list_devices(None, None, None, None, None, None, Some(0), Some(2)).unwrap();
assert_eq!(first_page.len(), 2, "First page should have 2 devices");
// Second page with 2 devices, should have at least 1 (seed data has >= 3 devices)
+5 -9
View File
@@ -2,16 +2,12 @@ use crate::settings::get_settings;
use clap::Parser;
use log::{LevelFilter, info};
mod arp_scanner;
mod arp_scanner_status;
mod data;
mod db;
mod device_finders;
mod events;
mod mdns_scanner;
mod mdns_scanner_status;
mod model;
mod retention;
mod scanners;
mod settings;
mod utils;
mod web_server;
@@ -61,13 +57,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
db::init_db().await?;
// Initialize scanner status tracking
arp_scanner_status::init();
mdns_scanner_status::init();
scanners::arp::status::init();
scanners::mdns::status::init();
// Start the device scanners, web server, and retention cleaner in parallel
tokio::join!(
arp_scanner::scan(),
mdns_scanner::listen(),
scanners::arp::scanner::scan(),
scanners::mdns::scanner::listen(),
web_server::serve(),
retention::run()
)
+4
View File
@@ -0,0 +1,4 @@
pub mod finder;
mod packet_send_receive;
pub mod scanner;
pub mod status;
@@ -1,14 +1,12 @@
mod packet_send_receive;
use crate::device_finders::error::{
use super::packet_send_receive::{listen_for_packets, send_packet};
use crate::model::devices::Device;
use crate::scanners::error::{
DataChannelError, InvalidDeviceError, NoIPAddressError, NoMACAddressError,
};
use crate::model::devices::Device;
use crate::settings::get_settings;
use crate::utils::network::select_interface;
use duration_string::DurationString;
use log::{debug, error, info, warn};
use packet_send_receive::{listen_for_packets, send_packet};
use pnet::{
datalink::{self, Channel, NetworkInterface},
ipnetwork::IpNetwork,
@@ -1,6 +1,6 @@
use crate::arp_scanner_status;
use super::finder;
use super::status as arp_scanner_status;
use crate::db;
use crate::device_finders;
use crate::events;
use crate::settings::get_settings;
use chrono::Utc;
@@ -12,8 +12,7 @@ pub async fn scan() -> Result<(), Box<dyn std::error::Error>> {
arp_scanner_status::set_running();
// Find online devices via ARP
let devices =
device_finders::arp::find(get_settings().networking.interface.clone()).await?;
let devices = finder::find(get_settings().networking.interface.clone()).await?;
info!("Done with ARP probes");
info!("Found {} online devices", devices.len());
+3
View File
@@ -0,0 +1,3 @@
pub mod finder;
pub mod scanner;
pub mod status;
@@ -67,7 +67,12 @@ pub fn parse_announcement(buf: &[u8]) -> Announcement {
}
};
let records = || packet.answers.iter().chain(packet.additional_records.iter());
let records = || {
packet
.answers
.iter()
.chain(packet.additional_records.iter())
};
let hostnames = records()
.filter(|record| matches!(record.rdata, RData::A(_)))
@@ -4,14 +4,14 @@ use std::time::Duration;
use chrono::Local;
use log::{debug, error, info, warn};
use crate::db;
use crate::device_finders::mdns;
use crate::events;
use super::finder as mdns;
use super::status as mdns_scanner_status;
use crate::data::mac_vendor_finder;
use crate::mdns_scanner_status;
use crate::data::vendor_device_type_finder;
use crate::db;
use crate::events;
use crate::model::devices::Device;
use crate::settings::get_settings;
use crate::data::vendor_device_type_finder;
/// Passively listen for mDNS/Bonjour announcements and feed discovered devices into the same
/// pipeline used by the ARP scanner (devices table + events + notifications).
@@ -62,13 +62,14 @@ async fn process_announcement(
interface: Option<String>,
probe_timeout: Duration,
) {
let mac = match crate::utils::network::resolve_mac_address(src_ip, interface, probe_timeout).await {
Some(mac) => mac.to_string(),
None => {
debug!("Could not resolve MAC for mDNS device {src_ip} ({hostname}); skipping");
return;
}
};
let mac =
match crate::utils::network::resolve_mac_address(src_ip, interface, probe_timeout).await {
Some(mac) => mac.to_string(),
None => {
debug!("Could not resolve MAC for mDNS device {src_ip} ({hostname}); skipping");
return;
}
};
let mut vendor = mac_vendor_finder::find(mac.get(0..8).unwrap_or("").to_string());
// Privacy MACs are locally administered and have no real OUI, so the lookup above fails.
+1 -1
View File
@@ -4,7 +4,7 @@ use log::error;
use serde::Serialize;
use utoipa::ToSchema;
use crate::arp_scanner_status;
use crate::scanners::arp::status as arp_scanner_status;
#[derive(Serialize, ToSchema)]
pub struct ArpScannerStatusResponse {
+6 -1
View File
@@ -176,7 +176,12 @@ pub async fn update(
);
}
match db::devices::update(mac_address, payload.owner, payload.device_type, payload.vendor) {
match db::devices::update(
mac_address,
payload.owner,
payload.device_type,
payload.vendor,
) {
Ok(_) => (axum::http::StatusCode::OK, "Device updated"),
Err(err) => {
error!("Error updating device in the database: {}", err);
+1 -1
View File
@@ -4,7 +4,7 @@ use log::error;
use serde::Serialize;
use utoipa::ToSchema;
use crate::mdns_scanner_status;
use crate::scanners::mdns::status as mdns_scanner_status;
#[derive(Serialize, ToSchema)]
pub struct MdnsScannerStatusResponse {