60 lines
1.9 KiB
Rust
60 lines
1.9 KiB
Rust
//! connflood — InspIRCd `m_connflood`. Refuse connections from an IP opening too
|
|
//! many too fast. Config: `connflood = <max> <secs>`. 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<IpAddr, Vec<u64>>);
|
|
|
|
/// `(max, secs)` from `connflood = <max> <secs>`, 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>(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::<ConnHistory>() {
|
|
h.0.retain(|_, times| {
|
|
times.retain(|&t| n.saturating_sub(t) < secs);
|
|
!times.is_empty()
|
|
});
|
|
}
|
|
}
|
|
}
|