From d4d3886379fd5521ea41d56e1bb2a9cb5165f5f8 Mon Sep 17 00:00:00 2001 From: reverse Date: Wed, 19 Aug 2026 01:54:45 +0000 Subject: [PATCH] restrictcommands: cache the parsed restriction list (config_gen-tagged) instead of re-tokenizing every restrictcommand line on every command; clone only the one matched rule so the ext borrow drops before the server is used mutably --- src/modules/restrictcommands.rs | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/modules/restrictcommands.rs b/src/modules/restrictcommands.rs index 520493b..ecc6288 100644 --- a/src/modules/restrictcommands.rs +++ b/src/modules/restrictcommands.rs @@ -15,6 +15,7 @@ use crate::server::{now, Server}; use crate::Uid; /// One parsed `restrictcommand` line. +#[derive(Clone)] struct Restriction { command: String, // uppercased connectdelay: u64, @@ -95,6 +96,13 @@ fn parse(s: &Server) -> Vec { out } +/// Parsed restrictions cached against the config generation they were parsed at. +#[derive(Default)] +struct RestrictCache { + gen: u64, + rules: Vec, +} + pub struct RestrictCommands; impl Module for RestrictCommands { @@ -113,11 +121,21 @@ impl Module for RestrictCommands { if srv.conf_all("restrictcommand").is_empty() { return ModResult::Passthru; } - let restrictions = parse(srv); - let Some(r) = restrictions - .iter() - .find(|r| r.command.eq_ignore_ascii_case(cmd)) - else { + // cache the parsed restrictions (config_gen-tagged); re-parse only on rehash, + // not on every command. Clone the one matched rule so the ext borrow drops + // before we touch the server mutably below. + let gen = srv.config_gen; + let stale = srv.ext.get::().map(|c| c.gen != gen).unwrap_or(true); + if stale { + let rules = parse(srv); + srv.ext.set(RestrictCache { gen, rules }); + } + let Some(r) = srv.ext.get::().and_then(|c| { + c.rules + .iter() + .find(|r| r.command.eq_ignore_ascii_case(cmd)) + .cloned() + }) else { return ModResult::Passthru; };