//! connflood — InspIRCd `m_connflood`. Refuse connections from an IP opening too //! many too fast. Config: `connflood = `. Per-IP recent-connect times //! live in `Server.ext`, pruned on the tick — nothing lives on `Server`. use std::collections::HashMap; use std::net::IpAddr; use crate::module::Module; use crate::server::{now, Server}; /// per-IP recent connection timestamps. Stored in `Server.ext`. #[derive(Default)] pub struct ConnHistory(pub HashMap>); /// `(max, secs)` from `connflood = `, or `None` when disabled. fn cfg(s: &Server) -> Option<(u32, u64)> { let v = s.conf("connflood")?; let mut it = v.split_whitespace(); let mx: u32 = it.next()?.parse().ok()?; let sc: u64 = it.next()?.parse().ok()?; (mx > 0 && sc > 0).then_some((mx, sc)) } /// Record a connection from `ip`; returns true when it exceeds the limit (the /// caller should refuse it). No-op → false when connflood is unconfigured. pub fn over_limit(s: &mut Server, ip: IpAddr) -> bool { let Some((max, secs)) = cfg(s) else { return false; }; let n = now(); let hist = s .ext .get_or_insert_with::(ConnHistory::default) .0 .entry(ip) .or_default(); hist.retain(|&t| n.saturating_sub(t) < secs); hist.push(n); hist.len() as u32 > max } /// Prunes stale per-IP bookkeeping on the tick. pub struct ConnFlood; impl Module for ConnFlood { fn name(&self) -> &'static str { "connflood" } fn on_tick(&mut self, s: &mut Server) { let Some((_, secs)) = cfg(s) else { return; }; let n = now(); if let Some(h) = s.ext.get_mut::() { h.0.retain(|_, times| { times.retain(|&t| n.saturating_sub(t) < secs); !times.is_empty() }); } } }