use crate::config::Context; use crate::utils::gethostname; use chrono::prelude::*; use ipnet::IpNet; use lazy_static::lazy_static; use regex::Regex; use reqwest::Error as ReqError; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::fmt::{Display, Formatter}; use std::io::{BufRead, BufReader, Read}; use std::net::IpAddr; lazy_static! { static ref R_IPV4: Regex = Regex::new(include_str!("regexps/ipv4.txt")).unwrap(); static ref R_IPV6: Regex = Regex::new(include_str!("regexps/ipv6.txt")).unwrap(); static ref R_DATE: Regex = Regex::new(include_str!("regexps/date.txt")).unwrap(); } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct IpEvent { pub msgtype: String, pub mode: String, pub hostname: String, pub ipdata: IpData, } #[derive(Clone, Debug, Serialize, Deserialize, Eq)] pub struct IpData { pub ip: String, pub src: String, pub date: String, pub hostname: String, } #[derive(Clone, Debug, Serialize, Deserialize)] pub struct BlockIpData { pub ipdata: IpData, pub tryfail: i64, pub blocktime: i64, pub starttime: DateTime, } impl PartialEq for IpData { fn eq(&self, other: &IpData) -> bool { self.ip.as_bytes() == other.ip.as_bytes() && self.src == other.src } fn ne(&self, other: &IpData) -> bool { !self.eq(other) } } impl Ord for IpData { fn cmp(&self, other: &IpData) -> Ordering { self.ip.as_bytes().cmp(&other.ip.as_bytes()) } } impl PartialOrd for IpData { fn partial_cmp(&self, other: &IpData) -> Option { Some(self.cmp(&other)) } } impl Display for IpData { fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { write!( f, "ip: {ip}, src: {src}, date: {date}, hostname: {hostname}", ip = self.ip, src = self.src, date = self.date, hostname = self.hostname, ) } } pub fn filter( lines: Box, list: &mut Vec, trustnets: &Vec, regex: &Regex, src: &String, last: &DateTime, ) -> isize { let mut ips = 0; let hostname = gethostname(true); for line in BufReader::new(lines).lines() { if let Ok(l) = line { if regex.is_match(l.as_str()) { let s_ipaddr: String; match R_IPV4.captures(l.as_str()) { Some(sv4) => { s_ipaddr = sv4.get(0).unwrap().as_str().to_string(); } None => { match R_IPV6.captures(l.as_str()) { Some(sv6) => { s_ipaddr = sv6.get(0).unwrap().as_str().to_string(); } None => { continue; } }; } }; let s_date: DateTime; match R_DATE.captures(l.as_str()) { Some(sdt) => { s_date = parse_date(sdt); if &s_date < last { continue; } } None => { s_date = Local::now(); } }; let ipaddr: IpAddr = match s_ipaddr.parse() { Ok(ip) => ip, Err(err) => { println!("unparseable IP: {err} {s_ipaddr}"); continue; } }; if !is_trusted(&ipaddr, &trustnets) { list.push(IpData { ip: s_ipaddr, src: src.to_owned(), date: s_date.to_rfc3339().to_owned(), hostname: hostname.to_owned(), }); ips += 1; }; } } } ips } fn parse_date(input: regex::Captures) -> DateTime { let mut ymd: Vec = vec![]; let mut hms: Vec = vec![]; let (daterange, hourrange) = (2..5, 5..8); for i in daterange { ymd.push(input.get(i).unwrap().as_str().parse::().unwrap()); } for i in hourrange { hms.push(input.get(i).unwrap().as_str().parse::().unwrap()); } let date = Local .with_ymd_and_hms( ymd[0] as i32, ymd[1] as u32, ymd[2] as u32, hms[0] as u32, hms[1] as u32, hms[2] as u32, ) .unwrap(); date } fn is_trusted(ip: &IpAddr, trustnets: &Vec) -> bool { for net in trustnets { if net.contains(ip) { return true; } } false } pub async fn _get_last(ctx: &Context) -> Result, ReqError> { let resp = ctx .client .get(format!("{server}/ips/last", server = ctx.flags.server)) .query(&[("interval", "3 hours")]) .send() .await; let req = match resp { Ok(re) => re, Err(err) => return Err(err), }; let data: Vec = match req.json::>().await { Ok(res) => res, Err(err) => return Err(err), }; Ok(data) }