OperServ: per-IP session limiting with exceptions

The connection that puts an IP over its allowance is killed on connect.
The ircd's per-user IP (UID param 7) is now plumbed through UserConnect and
tracked per-IP in the live network view (incremented on connect, released
on quit).

- [session] default_limit sets the base allowance (0/absent = off).
- OperServ SESSION LIST <min> / VIEW <ip> inspect live counts.
- OperServ EXCEPTION ADD <ip-mask> <limit> [reason] / DEL / LIST override
  the limit per IP-mask (limit 0 = unlimited); event-sourced so they
  federate and survive restart, matched most-permissive-wins.

All admin-gated.
This commit is contained in:
Jean Chevronnet 2026-07-14 01:46:09 +00:00
parent 78ad909706
commit 76a7162227
No known key found for this signature in database
11 changed files with 410 additions and 116 deletions

View file

@ -16,7 +16,7 @@ pub enum NetEvent {
EndBurst,
Ping { token: String, from: Option<String> },
Privmsg { from: String, to: String, text: String },
UserConnect { uid: String, nick: String, host: String },
UserConnect { uid: String, nick: String, host: String, ip: String },
NickChange { uid: String, nick: String },
// A channel was created or bursted (an FJOIN). Subsequent single joins arrive
// as IJOIN and are not surfaced.
@ -764,6 +764,10 @@ pub trait Store {
fn oper_grant(&mut self, account: &str, privs: Vec<String>);
fn oper_revoke(&mut self, account: &str) -> bool;
fn opers_list(&self) -> Vec<(String, Vec<String>)>;
// Session-limit exceptions (OperServ SESSION EXCEPTION).
fn session_except_add(&mut self, mask: &str, limit: u32, reason: &str);
fn session_except_del(&mut self, mask: &str) -> bool;
fn session_exceptions(&self) -> Vec<(String, u32, String)>;
fn register_channel(&mut self, name: &str, founder: &str) -> Result<(), ChanError>;
fn drop_channel(&mut self, name: &str) -> Result<(), ChanError>;
fn set_mlock(&mut self, name: &str, on: &str, off: &str) -> Result<(), ChanError>;
@ -834,6 +838,9 @@ pub trait NetView {
fn channel_activity(&self, channel: &str) -> Option<(u64, Vec<(String, u64)>)>;
// The shared stat counters (namespaced key -> count), for StatServ.
fn stat_counters(&self) -> Vec<(String, u64)>;
// Session limiting: live sessions from an IP, and IPs at/over a threshold.
fn session_count(&self, ip: &str) -> u32;
fn sessions_over(&self, min: u32) -> Vec<(String, u32)>;
}
// A pseudo-client (NickServ, ChanServ, ...). Introduced at burst, receives the