ipblc/src/ip.rs
Paul Lecuq af38ea1d84
Some checks failed
continuous-integration/drone/push Build is failing
added hostname field to ipevent
2023-01-15 23:26:18 +01:00

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)
}