html terminal
alerts
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | src/alerts.rs | 73 | ||||
| -rw-r--r-- | src/main.rs | 3 | ||||
| -rw-r--r-- | src/server.rs | 2 | ||||
| -rw-r--r-- | src/webhook.rs | 2 |
5 files changed, 79 insertions, 2 deletions
@@ -5,3 +5,4 @@ html/ **.vd Cargo.lock webhook +aook diff --git a/src/alerts.rs b/src/alerts.rs new file mode 100644 index 0000000..a16ba0e --- /dev/null +++ b/src/alerts.rs @@ -0,0 +1,73 @@ +use emoji::named::*; +use regex::Regex; +use std::{ + sync::{ + atomic::{ + AtomicU64, + Ordering::{Acquire, Relaxed}, + }, + LazyLock, + }, + time::Duration, +}; +use tokio::{ + io::{AsyncBufReadExt, AsyncReadExt}, + sync::broadcast::error::TryRecvError, +}; +pub async fn run() { + let (tx, mut rx) = tokio::sync::broadcast::channel::<u64>(10); + static COUNT: AtomicU64 = AtomicU64::new(0); + let mut f = + tokio::io::BufReader::new(tokio::fs::File::open("/var/log/kern.log").await.unwrap()); + let mut buf = [0; 1 << 20]; + while f.read(&mut buf).await.unwrap() != 0 {} + static RE: LazyLock<Regex> = LazyLock::new(|| { + Regex::new("[^ ]+ [0-9]+ [0-9]+:[0-9]+:[0-9]+ .+ kernel: [[0-9]+.[0-9]+] IN=.+ OUT= MAC=.+ SRC=([0-9]+.[0-9]+.[0-9]+.[0-9]+) DST=[0-9]+.[0-9]+.[0-9]+.[0-9]+ LEN=[0-9]+ TOS=0x[0-9a-f]+ PREC=0x[0-9a-f]+ TTL=[0-9]+ ID=[0-9]+(?: DF)? PROTO=TCP SPT=[0-9]+ DPT=[0-9]+ WINDOW=[0-9]+ RES=0x[a-f0-9]+ SYN URGP=[0-9]").unwrap() + }); + tokio::spawn(async move { + loop { + let mut s = String::new(); + f.read_line(&mut s).await.unwrap(); + if RE.is_match(&s) { + COUNT.fetch_add(1, Relaxed); + } + tokio::time::sleep(tokio::time::Duration::from_millis(5)).await; + } + }); + tokio::spawn(async move { + let mut alarmed = tokio::time::Instant::now() - Duration::from_secs(5 * 60); + loop { + let before = COUNT.load(Acquire); + tokio::time::sleep(tokio::time::Duration::from_secs(5)).await; + let after = COUNT.load(Acquire); + let δ = after - before; + let now = tokio::time::Instant::now(); + if δ > 10 && alarmed < now - Duration::from_secs(5 * 60) { + alarmed = now; + tx.send((δ + 1) / 5).unwrap(); + } + } + }); + let http = serenity::http::Http::new(""); + let wh = + std::env::var("AOOK").unwrap_or(std::fs::read_to_string("aook").expect("wher webhook")); + let wh = crate::webhook::Webhook::new(&http, &wh).await; + loop { + match rx.try_recv() { + Ok(x) => { + wh.send(|m| { + m.content(&format!( + "{WARNING} <@&1202414272030974033> attacked by {x} bots/s" + )) + }) + .await; + } + Err(TryRecvError::Closed) => panic!(), + Err(TryRecvError::Lagged(_)) => continue, + Err(TryRecvError::Empty) => { + tokio::time::sleep(Duration::from_secs(4)).await; + continue; + } + } + } +} diff --git a/src/main.rs b/src/main.rs index a954441..a05b28c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,9 @@ #![feature(lazy_cell, let_chains, iter_intersperse)] +#![allow(mixed_script_confusables)] use std::str::FromStr; #[macro_use] mod logging; +mod alerts; mod bot; mod process; mod server; @@ -12,6 +14,7 @@ use std::net::SocketAddr; #[tokio::main(flavor = "current_thread")] async fn main() { + tokio::spawn(alerts::run()); Server::spawn(SocketAddr::from(( [0, 0, 0, 0], std::env::var("PORT").map_or(4001, |x| u16::from_str(&x).unwrap()), diff --git a/src/server.rs b/src/server.rs index e12b7cc..a823311 100644 --- a/src/server.rs +++ b/src/server.rs @@ -75,7 +75,7 @@ async fn map_file( pub struct Server; impl Server { pub async fn spawn(addr: SocketAddr) { - let (stdin_tx, stdin) = broadcast::channel(2); + let (stdin_tx, stdin) = broadcast::channel(8); let state = Arc::new(State::new(stdin_tx)); let router = Router::new() .route("/", html!(index)) diff --git a/src/webhook.rs b/src/webhook.rs index c67b93d..ea2c320 100644 --- a/src/webhook.rs +++ b/src/webhook.rs @@ -28,7 +28,7 @@ impl<'a> Webhook<'a> { } } - async fn send<F>(&self, block: F) + pub async fn send<F>(&self, block: F) where for<'b> F: FnOnce(ExecuteWebhook) -> ExecuteWebhook, { |