201 lines
5.3 KiB
Rust
201 lines
5.3 KiB
Rust
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<Local>,
|
|
}
|
|
|
|
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<Ordering> {
|
|
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<dyn Read>,
|
|
list: &mut Vec<IpData>,
|
|
trustnets: &Vec<IpNet>,
|
|
regex: &Regex,
|
|
src: &String,
|
|
last: &DateTime<Local>,
|
|
) -> 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<Local>;
|
|
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<Local> {
|
|
let mut ymd: Vec<u64> = vec![];
|
|
let mut hms: Vec<u64> = vec![];
|
|
|
|
let (daterange, hourrange) = (2..5, 5..8);
|
|
|
|
for i in daterange {
|
|
ymd.push(input.get(i).unwrap().as_str().parse::<u64>().unwrap());
|
|
}
|
|
for i in hourrange {
|
|
hms.push(input.get(i).unwrap().as_str().parse::<u64>().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<IpNet>) -> bool {
|
|
for net in trustnets {
|
|
if net.contains(ip) {
|
|
return true;
|
|
}
|
|
}
|
|
false
|
|
}
|
|
|
|
pub async fn _get_last(ctx: &Context) -> Result<Vec<IpData>, 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<IpData> = match req.json::<Vec<IpData>>().await {
|
|
Ok(res) => res,
|
|
Err(err) => return Err(err),
|
|
};
|
|
|
|
Ok(data)
|
|
}
|