ipblc/src/ip.rs

259 lines
6.8 KiB
Rust
Raw Normal View History

use crate::config::Context;
2023-01-06 13:11:08 +01:00
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, Eq)]
pub struct IpData {
pub ip: String,
pub src: String,
pub date: String,
pub hostname: String,
pub mode: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct BlockIpData {
pub ipdata: IpData,
pub tryfail: i64,
pub blocktime: i64,
2023-01-06 11:50:26 +01:00
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}, mode: {mode}",
ip = self.ip,
src = self.src,
date = self.date,
hostname = self.hostname,
mode = self.mode,
)
}
}
pub async fn push_ip(ctx: &Context, ip: &IpData, ret: &mut Vec<String>) -> Result<(), ReqError> {
let result: String;
let mut data: Vec<IpData> = vec![];
data.push(IpData {
ip: ip.ip.to_string(),
src: ip.src.to_string(),
date: ip.date.to_string(),
hostname: ip.hostname.to_string(),
mode: "file".to_string(),
});
let resp = ctx
.client
.post(format!("{server}/ips", server = ctx.flags.server))
.json(&data)
.send()
.await?;
ret.push(format!("status: {status}", status = resp.status()));
let res = resp.text().await.unwrap();
if res.trim().len() > 0 {
result = res.trim().to_string();
} else {
result = "".to_string();
}
ret.push(format!("response: {result}"));
Ok(())
}
pub async fn _push_ip_bulk(
ctx: &Context,
ips: &Vec<IpData>,
ret: &mut Vec<String>,
) -> Result<(), ReqError> {
let result: String;
let mut data: Vec<IpData> = vec![];
for ip in ips {
data.push(IpData {
ip: ip.ip.to_string(),
src: ip.src.to_string(),
date: ip.date.to_string(),
hostname: ip.hostname.to_string(),
mode: "file".to_string(),
})
}
let resp = ctx
.client
.post(format!("{server}/ips", server = ctx.flags.server))
.json(&data)
.send()
.await?;
ret.push(format!("status: {status}", status = resp.status()));
let res = resp.text().await.unwrap();
if res.trim().len() > 0 {
result = res.trim().to_string();
} else {
result = "".to_string();
}
ret.push(format!("response: {result}"));
Ok(())
}
pub fn filter(
lines: Box<dyn Read>,
list: &mut Vec<IpData>,
trustnets: &Vec<IpNet>,
regex: &Regex,
src: &String,
2022-12-30 20:18:15 +01:00
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 => {
2022-12-30 20:18:15 +01:00
match R_IPV6.captures(l.as_str()) {
Some(sv6) => {
s_ipaddr = sv6.get(0).unwrap().as_str().to_string();
}
None => {
continue;
}
2022-12-30 20:18:15 +01:00
};
}
};
let s_date: DateTime<Local>;
match R_DATE.captures(l.as_str()) {
Some(sdt) => {
s_date = parse_date(sdt);
2022-12-30 20:18:15 +01:00
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(),
mode: "file".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
.ymd(ymd[0] as i32, ymd[1] as u32, ymd[2] as u32)
.and_hms(hms[0] as u32, hms[1] as u32, hms[2] as u32);
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)
}