mirror of
https://github.com/rzuasti/oott.git
synced 2026-07-08 19:21:54 +02:00
Add per-scanner enable/disable configuration
Each scanner (ARP, mDNS, SSDP/UPnP, DHCP) can now be turned off via an `enabled` flag in its config section, defaulting to true so existing deployments are unchanged. A disabled scanner's entry function returns early and never starts. Documented in the README options table (also fixing the stale `timings.*` key names to the actual `arp_scanner.*` keys), and added to the TOML samples and the NixOS module. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
4e975598f9
commit
08ee4fc0ea
@@ -9,6 +9,11 @@ use log::{debug, info};
|
||||
use tokio::time::{Duration, sleep};
|
||||
|
||||
pub async fn scan() -> Result<(), Box<dyn std::error::Error>> {
|
||||
if !get_settings().arp_scanner.enabled {
|
||||
info!("ARP scanner disabled in configuration; not starting");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
loop {
|
||||
status::set_running();
|
||||
|
||||
|
||||
@@ -9,12 +9,18 @@ use crate::db;
|
||||
use crate::events;
|
||||
use crate::model::device_events::DeviceEventScanner;
|
||||
use crate::model::devices::Device;
|
||||
use crate::settings::get_settings;
|
||||
|
||||
/// Passively snoop DHCP DISCOVER/REQUEST broadcasts and feed discovered devices into the
|
||||
/// same pipeline used by the ARP, mDNS and SSDP scanners (devices table + events +
|
||||
/// notifications). Because a device must request an address before doing almost anything
|
||||
/// else, this catches new devices very early — often before they have an IP.
|
||||
pub async fn listen() -> Result<(), Box<dyn std::error::Error>> {
|
||||
if !get_settings().dhcp_scanner.enabled {
|
||||
info!("DHCP scanner disabled in configuration; not starting");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let socket = finder::open_socket()?;
|
||||
status::set_listening();
|
||||
info!("DHCP scanner listening for client requests");
|
||||
|
||||
@@ -17,6 +17,11 @@ use crate::settings::get_settings;
|
||||
/// Passively listen for mDNS/Bonjour announcements and feed discovered devices into the same
|
||||
/// pipeline used by the ARP scanner (devices table + events + notifications).
|
||||
pub async fn listen() -> Result<(), Box<dyn std::error::Error>> {
|
||||
if !get_settings().mdns_scanner.enabled {
|
||||
info!("mDNS scanner disabled in configuration; not starting");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let interface = get_settings().networking.interface.clone();
|
||||
let socket = finder::open_socket(interface.clone())?;
|
||||
status::set_listening();
|
||||
|
||||
@@ -17,6 +17,11 @@ use crate::settings::get_settings;
|
||||
/// Passively listen for SSDP/UPnP NOTIFY announcements and feed discovered devices into the same
|
||||
/// pipeline used by the ARP and mDNS scanners (devices table + events + notifications).
|
||||
pub async fn listen() -> Result<(), Box<dyn std::error::Error>> {
|
||||
if !get_settings().ssdp_scanner.enabled {
|
||||
info!("SSDP scanner disabled in configuration; not starting");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let interface = get_settings().networking.interface.clone();
|
||||
let socket = finder::open_socket(interface.clone())?;
|
||||
status::set_listening();
|
||||
|
||||
@@ -6,6 +6,10 @@ use serde::Deserialize;
|
||||
// -----------------------------------------------------------
|
||||
// Configuration structure
|
||||
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct Database {
|
||||
pub path: String,
|
||||
@@ -23,6 +27,8 @@ pub struct Log {
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct ArpScanner {
|
||||
#[serde(default = "default_true")]
|
||||
pub enabled: bool,
|
||||
pub wait_between_scans: DurationString,
|
||||
pub sender_timeout: DurationString,
|
||||
pub scan_duration: DurationString,
|
||||
@@ -30,12 +36,15 @@ pub struct ArpScanner {
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct MdnsScanner {
|
||||
#[serde(default = "default_true")]
|
||||
pub enabled: bool,
|
||||
pub probe_timeout: DurationString,
|
||||
}
|
||||
|
||||
impl Default for MdnsScanner {
|
||||
fn default() -> Self {
|
||||
MdnsScanner {
|
||||
enabled: true,
|
||||
probe_timeout: DurationString::try_from("2s".to_string()).unwrap(),
|
||||
}
|
||||
}
|
||||
@@ -43,17 +52,32 @@ impl Default for MdnsScanner {
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct SsdpScanner {
|
||||
#[serde(default = "default_true")]
|
||||
pub enabled: bool,
|
||||
pub probe_timeout: DurationString,
|
||||
}
|
||||
|
||||
impl Default for SsdpScanner {
|
||||
fn default() -> Self {
|
||||
SsdpScanner {
|
||||
enabled: true,
|
||||
probe_timeout: DurationString::try_from("2s".to_string()).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct DhcpScanner {
|
||||
#[serde(default = "default_true")]
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
impl Default for DhcpScanner {
|
||||
fn default() -> Self {
|
||||
DhcpScanner { enabled: true }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Clone)]
|
||||
pub struct Pushover {
|
||||
pub token: String,
|
||||
@@ -101,6 +125,8 @@ pub struct Settings {
|
||||
pub mdns_scanner: MdnsScanner,
|
||||
#[serde(default)]
|
||||
pub ssdp_scanner: SsdpScanner,
|
||||
#[serde(default)]
|
||||
pub dhcp_scanner: DhcpScanner,
|
||||
}
|
||||
// End configuration structure
|
||||
// -----------------------------------------------------------
|
||||
@@ -138,3 +164,75 @@ pub fn get_settings() -> &'static Settings {
|
||||
pub fn init(config_path: String) {
|
||||
let _ = SETTINGS.set(Settings::new(config_path).unwrap());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use config::{Config, FileFormat};
|
||||
|
||||
fn parse(toml: &str) -> Settings {
|
||||
Config::builder()
|
||||
.add_source(config::File::from_str(toml, FileFormat::Toml))
|
||||
.build()
|
||||
.unwrap()
|
||||
.try_deserialize()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
const BASE_CONFIG: &str = r#"
|
||||
[database]
|
||||
path = "./oott.db"
|
||||
[networking]
|
||||
[log]
|
||||
level = "info"
|
||||
[arp_scanner]
|
||||
wait_between_scans = "1m"
|
||||
sender_timeout = "1m"
|
||||
scan_duration = "2m"
|
||||
[notifications]
|
||||
method = "none"
|
||||
notify_when_not_seen_for = "1w"
|
||||
[notifications.pushover]
|
||||
token = ""
|
||||
user_key = ""
|
||||
[web_server]
|
||||
ip_address = "0.0.0.0"
|
||||
port = 3000
|
||||
api_key = "test"
|
||||
"#;
|
||||
|
||||
#[test]
|
||||
fn scanners_enabled_by_default_when_flag_omitted() {
|
||||
let settings = parse(BASE_CONFIG);
|
||||
assert!(settings.arp_scanner.enabled);
|
||||
assert!(settings.mdns_scanner.enabled);
|
||||
assert!(settings.ssdp_scanner.enabled);
|
||||
assert!(settings.dhcp_scanner.enabled);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scanners_can_be_disabled() {
|
||||
let toml = format!(
|
||||
"{BASE_CONFIG}
|
||||
[mdns_scanner]
|
||||
enabled = false
|
||||
probe_timeout = \"2s\"
|
||||
[ssdp_scanner]
|
||||
enabled = false
|
||||
probe_timeout = \"2s\"
|
||||
[dhcp_scanner]
|
||||
enabled = false
|
||||
"
|
||||
);
|
||||
// Disable the ARP scanner via its existing section too.
|
||||
let toml = toml.replace(
|
||||
"wait_between_scans = \"1m\"",
|
||||
"enabled = false\n wait_between_scans = \"1m\"",
|
||||
);
|
||||
let settings = parse(&toml);
|
||||
assert!(!settings.arp_scanner.enabled);
|
||||
assert!(!settings.mdns_scanner.enabled);
|
||||
assert!(!settings.ssdp_scanner.enabled);
|
||||
assert!(!settings.dhcp_scanner.enabled);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user