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 // 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. // user (via register) or previously deduced is never overwritten by a later sighting.
if !vendor.is_empty() { 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(vendor.into());
params.push(device_type.into()); params.push(device_type.into());
} }
@@ -377,8 +379,17 @@ mod tests {
); );
// Filter by owner substring - "oh" matches "John" but not "Sarah" // Filter by owner substring - "oh" matches "John" but not "Sarah"
let devices: Vec<Device> = let devices: Vec<Device> = list_devices(
list_devices(None, None, None, Some("oh".to_string()), None, None, None, None).unwrap(); None,
None,
None,
Some("oh".to_string()),
None,
None,
None,
None,
)
.unwrap();
assert!( assert!(
devices.len() >= 1, devices.len() >= 1,
@@ -426,7 +437,8 @@ mod tests {
tests_common::setup().await; tests_common::setup().await;
// First page with 2 devices // 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"); 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) // 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 clap::Parser;
use log::{LevelFilter, info}; use log::{LevelFilter, info};
mod arp_scanner;
mod arp_scanner_status;
mod data; mod data;
mod db; mod db;
mod device_finders;
mod events; mod events;
mod mdns_scanner;
mod mdns_scanner_status;
mod model; mod model;
mod retention; mod retention;
mod scanners;
mod settings; mod settings;
mod utils; mod utils;
mod web_server; mod web_server;
@@ -61,13 +57,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
db::init_db().await?; db::init_db().await?;
// Initialize scanner status tracking // Initialize scanner status tracking
arp_scanner_status::init(); scanners::arp::status::init();
mdns_scanner_status::init(); scanners::mdns::status::init();
// Start the device scanners, web server, and retention cleaner in parallel // Start the device scanners, web server, and retention cleaner in parallel
tokio::join!( tokio::join!(
arp_scanner::scan(), scanners::arp::scanner::scan(),
mdns_scanner::listen(), scanners::mdns::scanner::listen(),
web_server::serve(), web_server::serve(),
retention::run() 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 super::packet_send_receive::{listen_for_packets, send_packet};
use crate::model::devices::Device;
use crate::device_finders::error::{ use crate::scanners::error::{
DataChannelError, InvalidDeviceError, NoIPAddressError, NoMACAddressError, DataChannelError, InvalidDeviceError, NoIPAddressError, NoMACAddressError,
}; };
use crate::model::devices::Device;
use crate::settings::get_settings; use crate::settings::get_settings;
use crate::utils::network::select_interface; use crate::utils::network::select_interface;
use duration_string::DurationString; use duration_string::DurationString;
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use packet_send_receive::{listen_for_packets, send_packet};
use pnet::{ use pnet::{
datalink::{self, Channel, NetworkInterface}, datalink::{self, Channel, NetworkInterface},
ipnetwork::IpNetwork, 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::db;
use crate::device_finders;
use crate::events; use crate::events;
use crate::settings::get_settings; use crate::settings::get_settings;
use chrono::Utc; use chrono::Utc;
@@ -12,8 +12,7 @@ pub async fn scan() -> Result<(), Box<dyn std::error::Error>> {
arp_scanner_status::set_running(); arp_scanner_status::set_running();
// Find online devices via ARP // Find online devices via ARP
let devices = let devices = finder::find(get_settings().networking.interface.clone()).await?;
device_finders::arp::find(get_settings().networking.interface.clone()).await?;
info!("Done with ARP probes"); info!("Done with ARP probes");
info!("Found {} online devices", devices.len()); 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() let hostnames = records()
.filter(|record| matches!(record.rdata, RData::A(_))) .filter(|record| matches!(record.rdata, RData::A(_)))
@@ -4,14 +4,14 @@ use std::time::Duration;
use chrono::Local; use chrono::Local;
use log::{debug, error, info, warn}; use log::{debug, error, info, warn};
use crate::db; use super::finder as mdns;
use crate::device_finders::mdns; use super::status as mdns_scanner_status;
use crate::events;
use crate::data::mac_vendor_finder; 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::model::devices::Device;
use crate::settings::get_settings; 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 /// Passively listen for mDNS/Bonjour announcements and feed discovered devices into the same
/// pipeline used by the ARP scanner (devices table + events + notifications). /// pipeline used by the ARP scanner (devices table + events + notifications).
@@ -62,7 +62,8 @@ async fn process_announcement(
interface: Option<String>, interface: Option<String>,
probe_timeout: Duration, probe_timeout: Duration,
) { ) {
let mac = match crate::utils::network::resolve_mac_address(src_ip, interface, probe_timeout).await { let mac =
match crate::utils::network::resolve_mac_address(src_ip, interface, probe_timeout).await {
Some(mac) => mac.to_string(), Some(mac) => mac.to_string(),
None => { None => {
debug!("Could not resolve MAC for mDNS device {src_ip} ({hostname}); skipping"); debug!("Could not resolve MAC for mDNS device {src_ip} ({hostname}); skipping");
+1 -1
View File
@@ -4,7 +4,7 @@ use log::error;
use serde::Serialize; use serde::Serialize;
use utoipa::ToSchema; use utoipa::ToSchema;
use crate::arp_scanner_status; use crate::scanners::arp::status as arp_scanner_status;
#[derive(Serialize, ToSchema)] #[derive(Serialize, ToSchema)]
pub struct ArpScannerStatusResponse { 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"), Ok(_) => (axum::http::StatusCode::OK, "Device updated"),
Err(err) => { Err(err) => {
error!("Error updating device in the database: {}", err); error!("Error updating device in the database: {}", err);
+1 -1
View File
@@ -4,7 +4,7 @@ use log::error;
use serde::Serialize; use serde::Serialize;
use utoipa::ToSchema; use utoipa::ToSchema;
use crate::mdns_scanner_status; use crate::scanners::mdns::status as mdns_scanner_status;
#[derive(Serialize, ToSchema)] #[derive(Serialize, ToSchema)]
pub struct MdnsScannerStatusResponse { pub struct MdnsScannerStatusResponse {