whomac
This commit is contained in:
commit
54f7b82834
|
@ -0,0 +1 @@
|
||||||
|
/target
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/whomac.iml" filepath="$PROJECT_DIR$/.idea/whomac.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="EMPTY_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "whomac"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
csv = "1"
|
||||||
|
rmp-serde = "1.3"
|
||||||
|
reqwest = { version = "0.12", features = ["blocking"] }
|
|
@ -0,0 +1,183 @@
|
||||||
|
use std::env::args;
|
||||||
|
use std::fs;
|
||||||
|
use std::fs::OpenOptions;
|
||||||
|
use std::io::{Cursor, Write};
|
||||||
|
use std::os::unix::fs::OpenOptionsExt;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::process::exit;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub struct MacEntry {
|
||||||
|
pub registry: Registry,
|
||||||
|
pub assignment: String,
|
||||||
|
pub org: String,
|
||||||
|
pub address: String
|
||||||
|
}
|
||||||
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
|
pub enum Registry {
|
||||||
|
#[serde(rename = "MA-L")]
|
||||||
|
MAL,
|
||||||
|
#[serde(rename = "MA-M")]
|
||||||
|
MAM,
|
||||||
|
#[serde(rename = "MA-S")]
|
||||||
|
MAS,
|
||||||
|
#[serde(rename = "CID")]
|
||||||
|
CID
|
||||||
|
}
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct Database {
|
||||||
|
entries: Vec<MacEntry>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub const DBDIR: &str = "/var/cache/";
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub const DBDIR: &str = "C:\\Program Files\\whomac";
|
||||||
|
|
||||||
|
fn create_dbdir() {
|
||||||
|
match fs::create_dir_all(DBDIR) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("% Error creating database directory {DBDIR}: {e}");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn dbfile() -> PathBuf {
|
||||||
|
Path::new(DBDIR).join(Path::new("whomac.db"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const IEEE_LOAD_URL: &str = "http://standards-oui.ieee.org/oui/oui.csv";
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut args = args();
|
||||||
|
if args.len() != 2 {
|
||||||
|
eprintln!("usage: whomac <sync|mac address>");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let sc = args.next_back().unwrap();
|
||||||
|
if sc == "sync" {
|
||||||
|
println!("% Syncing local database from {}", IEEE_LOAD_URL);
|
||||||
|
let r = reqwest::blocking::get(IEEE_LOAD_URL);
|
||||||
|
let r = match r {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("% Error syncing database with {}: {}", IEEE_LOAD_URL, e);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut rdr = csv::Reader::from_reader(r);
|
||||||
|
|
||||||
|
println!("% Processing database - please wait, this will take a while");
|
||||||
|
|
||||||
|
let mut db = Database {
|
||||||
|
entries: vec![],
|
||||||
|
};
|
||||||
|
|
||||||
|
for result in rdr.records() {
|
||||||
|
if db.entries.len() % 1000 == 0 {
|
||||||
|
println!("% Database processing: {} entries processed so far", db.entries.len());
|
||||||
|
}
|
||||||
|
let record = match result {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("% Error parsing database loaded from {}: {}", IEEE_LOAD_URL, e);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let r = MacEntry {
|
||||||
|
registry: match &record[0] {
|
||||||
|
"MA-L" => Registry::MAL,
|
||||||
|
"MA-M" => Registry::MAM,
|
||||||
|
"MA-S" => Registry::MAS,
|
||||||
|
"CID" => Registry::CID,
|
||||||
|
_ => {
|
||||||
|
eprintln!("% Error processing registry for {}: invalid value {}", &record[1], &record[0]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
assignment: record[1].to_owned(),
|
||||||
|
org: record[2].to_owned(),
|
||||||
|
address: record[3].to_owned(),
|
||||||
|
};
|
||||||
|
db.entries.push(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
create_dbdir();
|
||||||
|
|
||||||
|
#[cfg(unix)] {
|
||||||
|
let mut f = match OpenOptions::new().mode(0o777).write(true).create(true).open(dbfile()) {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("% Error opening database file at {}: {e}", dbfile().display());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match f.write(&rmp_serde::to_vec(&db).unwrap()) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("% Error writing database file to {}: {e}", dbfile().display());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(windows)] {
|
||||||
|
match fs::write(dbfile(), rmp_serde::to_vec(&db).unwrap()) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("% Error writing database file to {}: {e}", dbfile().display());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eprintln!("% Imported {} registration entries from {}", db.entries.len(), IEEE_LOAD_URL);
|
||||||
|
} else {
|
||||||
|
let address = sc.replace(":", "");
|
||||||
|
let f = match fs::read(dbfile()) {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("% Error reading database file at {}: {e}", dbfile().display());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let db: Database = match rmp_serde::from_slice(&f) {
|
||||||
|
Ok(f) => f,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("% Error parsing database file at {}: {e}", dbfile().display());
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut best_entry: Option<MacEntry> = None;
|
||||||
|
|
||||||
|
for entry in &db.entries {
|
||||||
|
if address.to_lowercase().starts_with(&entry.assignment.to_lowercase()) {
|
||||||
|
if let Some(e) = &mut best_entry {
|
||||||
|
if entry.assignment.len() > e.assignment.len() {
|
||||||
|
// more specific assignment
|
||||||
|
*e = entry.clone();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
best_entry = Some(entry.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(entry) = best_entry {
|
||||||
|
println!("oui-num: {}", entry.assignment);
|
||||||
|
println!("org: {}", entry.org);
|
||||||
|
println!("address: {}", entry.address);
|
||||||
|
println!("registry: {}", match entry.registry {
|
||||||
|
Registry::CID => "company-id",
|
||||||
|
Registry::MAS => "ma-s",
|
||||||
|
Registry::MAM => "ma-m",
|
||||||
|
Registry::MAL => "ma-l"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
println!("No match found for a {}", address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue