Skip to main content

r_lanlib/
oui.rs

1//! OUI lookup for resolving MAC address prefixes to organization names.
2use std::{sync::Arc, time::Duration};
3
4use directories::ProjectDirs;
5
6use crate::{
7    error::{RLanLibError, Result},
8    oui::{db::OuiDb, offline_db::OfflineOuiDb, traits::Oui},
9};
10
11/// OUI (Organizationally Unique Identifier) lookup for MAC addresses.
12///
13/// Downloads and caches IEEE OUI CSV data locally, then resolves a MAC
14/// address prefix to the registered organization name.
15pub mod db;
16/// Offline database used when unable to download IEEE data files from site
17pub mod offline_db;
18/// [`Oui`] trait definition and test mocks.
19pub mod traits;
20/// Data types used by the OUI database.
21pub mod types;
22
23/// Initializes a default Oui DB using provided project name and max age
24pub fn default(project_name: &str, max_age: Duration) -> Result<Arc<dyn Oui>> {
25    log::info!("initializing oui data dir");
26
27    let project_dirs =
28        ProjectDirs::from("", "", project_name).ok_or(RLanLibError::Oui(
29            format!("failed to find \"{project_name}\" oui data directory"),
30        ))?;
31
32    let data_dir = project_dirs.data_dir();
33
34    std::fs::create_dir_all(data_dir).map_err(|e| {
35        RLanLibError::Oui(format!(
36            "failed to initialize oui data directory: {} : {}",
37            data_dir.display(),
38            e
39        ))
40    })?;
41
42    let mut oui = OuiDb::new(data_dir);
43    let oui_age = oui.age();
44
45    if let Some(age) = oui_age
46        && let Ok(elapsed) = age.elapsed()
47        && elapsed > max_age
48    {
49        log::info!("oui data files are out of date: updating...");
50        match oui.update() {
51            Ok(_) => log::info!("successfully downloaded vendor data"),
52            Err(err) => {
53                log::warn!("failed to download vendor data: {}", err);
54                log::warn!(
55                    "continuing with use of previously downloaded data: vendor data may be slightly out-of-date"
56                );
57            }
58        }
59    } else if oui_age.is_none() {
60        log::info!("downloading oui data files to {}", data_dir.display());
61        match oui.update() {
62            Ok(_) => log::info!("successfully downloaded vendor data"),
63            Err(err) => {
64                log::warn!("failed to download vendor data: {}", err);
65                log::warn!(
66                    "loading offline data: vendor data may be out-of-date"
67                );
68                return Ok(Arc::new(OfflineOuiDb));
69            }
70        }
71    }
72
73    log::info!("loading oui data...");
74    oui.load_data()?;
75
76    Ok(Arc::new(oui))
77}