From 7f998c50bd30e0b2e51712e3a74a9a7d7e9f7d3d Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 7 Oct 2019 19:59:05 -0400 Subject: [PATCH] initial commit --- .gitignore | 2 + Cargo.lock | 141 +++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 12 ++++ src/main.rs | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 331 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..53eaa21 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +**/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..e306cd2 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,141 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "chrono" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "forums-help" +version = "0.1.0" +dependencies = [ + "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "num-integer" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "regex" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "thread_local" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "time" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[metadata] +"checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" +"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" +"checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" +"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" +"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" +"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" +"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" +"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" +"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" +"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b" +"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..49258f2 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "forums-help" +version = "0.1.0" +authors = ["Michael Lamparski "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +chrono = "0.4.9" +regex = "1.3.1" + diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..cd72827 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,176 @@ +// +// Simple program to analyze a history of MAC addresses showing up on switches +// and print out any that haven't appeared in at least 6 months. +// +// Mark Leisher +// 07 October 2019 +// +extern crate chrono; +extern crate regex; + +use std::env; +use std::fs::File; +use std::io::*; +use std::process; +use regex::Regex; +use std::collections::HashMap; +use chrono::{DateTime, NaiveDateTime, Datelike, Local}; + +static NMONTHS: u32 = 6; +static VERBOSE: bool = true; +static SWITCH_HISTORY: &str = "fakehistory.txt"; +static DATABASE: &str = "fakehosts.txt"; + +fn basename<'a>(path: &'a str, suff: &str) -> &'a str { + let mut end: usize = path.len(); + if path.ends_with(suff) { + end -= suff.len(); + } + &path[..end] +} + +struct Mac { + dates: Vec, + count: u64, +} +impl Mac { + pub fn new() -> Mac { + Mac { dates: Vec::with_capacity(2), count: 1 } + } +} + +// +// Load all the MAC addresses with their first and last seen dates. +// +fn load_switch_history(prog: &str, emap: &mut HashMap) { + let re = Regex::new(r"^(\d+)_\d+\s+\S+\s+\S+\s+\S+\s+(([0-9A-F]+,?)+)").unwrap(); + // + // Do the following as a block so the file will be closed. + // + { + let infile = match File::open(SWITCH_HISTORY) { + Err(_why) => { + println!("{}: unable to open switchwalk history '{}': {}", + prog, SWITCH_HISTORY, _why); + process::exit(1) + }, + Ok(infile) => infile, + }; + let reader = BufReader::new(infile); + for line in reader.lines() { + let l = line.unwrap(); + match re.captures(l.as_str()) { + Some(x) => { + let date = x[1].to_owned();; + let mac = x[2].to_owned(); + let e = emap.entry(mac).or_insert(Mac::new()); + if e.dates.len() == 2 { + e.dates.pop(); + } + e.dates.push(date); + e.count += 1; + }, + None => (), + }; + } + } +} + +// +// Function to determine the number of months between two dates. +// +fn nm(start: &String, end: Option<&String>, now: &DateTime) -> u32 { + let ey: u32; + let em: u32; + let s = NaiveDateTime::parse_from_str(start.as_str(), "%Y%m%d_%H%M%S").unwrap(); + + if !end.is_none() { + let edt = NaiveDateTime::parse_from_str(end.unwrap().as_str(), "%Y%m%d_%H%M%S").unwrap(); + ey = edt.year() as u32; + em = edt.month() as u32; + } else { + ey = now.year() as u32; + em = now.month() as u32; + } + + let sy = s.year() as u32; + let sm = s.month() as u32; + ((12 * (ey - sy)) + em - sm) +} + +// +// Function to scan the database file for any hosts that haven't appeared in the +// switch history in the last NMONTHS (NMONTHS specified above). +// +fn scan_database(prog: &str, now: &DateTime, emap: &HashMap) { + let res: [Regex; 3] = [ Regex::new(r"^#").unwrap(), + Regex::new(r"^\s*$").unwrap(), + Regex::new(r"host13|host42").unwrap() ]; + // + // Wrap in brackets to automatically close the file when it goes out of scope. + // + { + let infile = match File::open(DATABASE) { + Err(_why) => { + println!("{}: unable to open host database '{}': {}", prog, DATABASE, _why); + process::exit(1) + }, + Ok(infile) => infile, + }; + let mut lno: u32 = 1; + let reader = BufReader::new(infile); + 'outer: for line_result in reader.lines() { + println!("Line: {}", lno); + lno += 1; + let line = line_result.unwrap(); + for r in &res { + if r.is_match(line.as_str()) { + continue 'outer; + } + } + let l = line.as_str().split("%"). + enumerate(). + filter(|&(i,_)| i == 0 || i == 1 || i == 8). + map(|(_,f)| f); + let fvec: Vec<&str> = l.collect(); + let ip = fvec[0]; + let host = fvec[1]; + let mac_string = fvec[2].replace("-","").to_uppercase(); + let mac = mac_string.as_str(); + match emap.get(&mac_string) { + Some(v) => { + let n: u32; + if v.dates.len() == 1 { + n = nm(&v.dates[0], None, now); + } else { + n = nm(&v.dates[0], Some(&v.dates[1]), now); + } + if n >= NMONTHS { + println!("DING! {} {} {} Months: {}", ip, host, mac, n); + } + }, + None => () + }; + } + }; +} + +fn main() { + let arg = env::args().next().unwrap(); + let prog = basename(&arg, ""); + let now: DateTime = Local::now(); + let mut emap: HashMap = HashMap::new(); + + if VERBOSE { + eprint!("Loading switch history..."); + } + load_switch_history(prog, &mut emap); + if VERBOSE { + eprintln!("done."); + eprint!("Scanning database..."); + } + scan_database(prog, &now, &emap); + if VERBOSE { + eprintln!("done."); + } +}