pub use crate::message::*; pub use crate::monitor::*; use serde_derive::*; extern crate regex; use regex::Regex; extern crate chrono; use chrono::{DateTime, Local}; #[derive(Debug)] pub struct DHCPLeases { config: DHCPLeasesConfig, rgx_lease: Regex, rgx_mac: Regex, rgx_date_start: Regex, rgx_date_ends: Regex, } #[derive(Debug, PartialEq, Serialize, Deserialize)] struct DHCPLeasesConfig { path: String, mac_rules: HashMap>, } impl Monitor for DHCPLeases { fn new(config: &HashMap) -> Self { let config_node = config.get("config").expect("Missing `config` key").clone(); let config = serde_yaml::from_value(config_node).expect("Invalid config for wifi_availability"); // Regex compilation let rgx_lease = Regex::new(r"(?s)lease\s+(\d+(?:\.\d+){3})\s*\{\n?(.*?)\}").unwrap(); let rgx_mac = Regex::new(r"^\s*hardware\s+ethernet\s([a-f0-9]{2}(?:\:[a-f0-9]{2}){5})\s*;").unwrap(); let rgx_date_start = Regex::new(r"^\s*starts\s+(.*?)\s*;").unwrap(); let rgx_date_ends = Regex::new(r"^\s*ends\s+(.*?)\s*;").unwrap(); DHCPLeases { config: config, rgx_lease: rgx_lease, rgx_mac: rgx_mac, rgx_date_start: rgx_date_start, rgx_date_ends: rgx_date_ends, } } fn run(&mut self, sender: &mpsc::Sender) { use std::fs::File; use std::io::prelude::*; let mut config_file = File::open(&self.config.path).expect("Could not open DHCP leases file"); let mut config_content = String::new(); config_file .read_to_string(&mut config_content) .expect("Could not read DHCP leases file"); let mut unauthorized_macs: Vec = vec![]; for cap in self.rgx_lease.captures_iter(&config_content) { let ip = cap.get(1).unwrap().as_str(); let content = cap.get(2).unwrap().as_str(); let mac = self .rgx_mac .captures(content) .expect("No 'hardware ethernet' field found for MAC address") .get(0) .unwrap() .as_str(); let starts_str = self .rgx_date_start .captures(content) .expect("No 'starts' field found in lease") .get(0) .unwrap() .as_str(); let starts = chrono::naive::NaiveDateTime::parse_from_str(starts_str, "%U %Y/%m/%d %H:%M:%S") .expect("Bad date format"); let ends_str = self .rgx_date_ends .captures(content) .expect("No 'ends' field found in lease") .get(0) .unwrap() .as_str(); let ends = chrono::naive::NaiveDateTime::parse_from_str(ends_str, "%U %Y/%m/%d %H:%M:%S") .expect("Bad date format"); let now = Local::now().naive_local(); if starts <= now && now < ends { // Lease is active if let Some(rules) = self.config.mac_rules.get(mac) { for rule in rules { if content.find(rule).is_none() { unauthorized_macs.push(mac.to_owned()); sender .send(Message { emitter: "dhcp_leases".to_owned(), level: Level::Error, msg_type: "dhcp_leases.unauthorized_mac.rule".to_owned(), text: format!("Mismatching rule '{}' for device {}", rule, mac), }) .unwrap(); break; } } } else { unauthorized_macs.push(mac.to_owned()); sender .send(Message { emitter: "dhcp_leases".to_owned(), level: Level::Error, msg_type: "dhcp_leases.unauthorized_mac.unknown".to_owned(), text: format!("Unauthorized device on network: {}", mac), }) .unwrap(); } } } if unauthorized_macs.len() > 0 {} // let leases: Vec<(&str, &str)> = lease_rgx // .captures_iter(&config_content) // .map(|c| (c.get(1).unwrap().as_str(), c.get(2).unwrap().as_str())) // .collect(); // println!("{:?}", leases); // let cap = lease_rgx.captures(config_content); // sender // .send(Message { // emitter: "dhcp_leases".to_owned(), // level: 10, // msg_type: "string".to_owned(), // text: format!("frfr"), // }) // .unwrap(); std::thread::sleep(std::time::Duration::from_millis(2000)); } } /* lease 192.168.0.26 { starts 4 2018/08/16 22:31:22; ends 4 2018/08/16 23:01:22; tstp 4 2018/08/16 23:01:22; cltt 4 2018/08/16 22:31:22; binding state free; hardware ethernet 84:4b:f5:16:f4:94; } */