/* * Extended ban to exempt from +f/+F checking. * Eg: +e ~flood:*:~account:TrustedBot * (C) Copyright 2023-.. Bram Matthys (Syzop) and the UnrealIRCd team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "unrealircd.h" ModuleHeader MOD_HEADER = { "extbans/flood", "1.0", "Extban ~flood - exempt from +f/+F checks", "UnrealIRCd Team", "unrealircd-6", }; /** Maximum length of the ~flood ban exemption */ #define MAX_FLOODBAN_LENGTH 128 /* Forward declarations */ int extban_flood_is_banned(BanContext *b); int flood_extban_is_ok(BanContext *b); const char *flood_extban_conv_param(BanContext *b, Extban *extban); /** Called upon module init */ MOD_INIT() { ExtbanInfo req; memset(&req, 0, sizeof(req)); req.letter = 'F'; req.name = "flood"; req.is_ok = flood_extban_is_ok; req.conv_param = flood_extban_conv_param; req.options = EXTBOPT_ACTMODIFIER; if (!ExtbanAdd(modinfo->handle, req)) { config_error("could not register extended ban type 'flood'"); return MOD_FAILED; } MARK_AS_OFFICIAL_MODULE(modinfo); return MOD_SUCCESS; } /** Called upon module load */ MOD_LOAD() { return MOD_SUCCESS; } /** Called upon unload */ MOD_UNLOAD() { return MOD_SUCCESS; } /** Check if letters in 'str' are valid flood-types. * TODO: ideally this would call a function in chanmode +F module!! */ static int flood_type_ok(char *str) { char *p; /* The * (asterisk) simply means ALL. */ if (!strcmp(str, "*")) return 1; for (p = str; *p; p++) if (!strchr("cjkmntr", *p)) return 0; return 1; } const char *flood_extban_conv_param(BanContext *b, Extban *extban) { static char retbuf[MAX_FLOODBAN_LENGTH+1]; char para[MAX_FLOODBAN_LENGTH+1]; char tmpmask[MAX_FLOODBAN_LENGTH+1]; char *type; /**< Type(s), such as 'j' */ char *matchby; /**< Matching method, such as 'n!u@h' */ const char *newmask; /**< Cleaned matching method, such as 'n!u@h' */ strlcpy(para, b->banstr, sizeof(para)); /* work on a copy (and truncate it) */ /* ~flood:type:n!u@h for direct matching * ~flood:type:~x:.... when calling another bantype */ type = para; matchby = strchr(para, ':'); if (!matchby || !matchby[1]) return NULL; *matchby++ = '\0'; /* don't verify type(s), already done in is_ok for local clients */ //if (!flood_type_ok(type)) // return NULL; /* ... but limit them to a reasonable value :D */ if (strlen(type) > 16) return NULL; b->banstr = matchby; newmask = extban_conv_param_nuh_or_extban(b, extban); if (BadPtr(newmask)) return NULL; snprintf(retbuf, sizeof(retbuf), "%s:%s", type, newmask); return retbuf; } int flood_extban_syntax(Client *client, int checkt, char *reason) { if (MyUser(client) && (checkt == EXBCHK_PARAM)) { sendnotice(client, "Error when setting ban exception: %s", reason); sendnotice(client, " Syntax: +e ~flood:floodtype(s):mask"); sendnotice(client, "Example: +e ~flood:*:~account:TrustedUser"); sendnotice(client, "Valid flood types are: c, j, k, m, n, t, r, and * for all"); sendnotice(client, "Valid masks are: nick!user@host or another extban type such as ~account, ~certfp, etc."); } return 0; /* FAIL: ban rejected */ } int flood_extban_is_ok(BanContext *b) { static char para[MAX_FLOODBAN_LENGTH+1]; char *type; /**< Type(s), such as 'j' */ char *matchby; /**< Matching method, such as 'n!u@h' */ char *newmask; /**< Cleaned matching method, such as 'n!u@h' */ /* Always permit deletion */ if (b->what == MODE_DEL) return 1; if (b->ban_type != EXBTYPE_EXCEPT) { if (b->is_ok_check == EXBCHK_PARAM) sendnotice(b->client, "Ban type ~flood only works with exceptions (+e) and not with bans or invex (+b/+I)"); return 0; /* reject */ } strlcpy(para, b->banstr, sizeof(para)); /* work on a copy (and truncate it) */ /* ~flood:type:n!u@h for direct matching * ~flood:type:~x:.... when calling another bantype */ type = para; matchby = strchr(para, ':'); if (!matchby || !matchby[1]) return flood_extban_syntax(b->client, b->is_ok_check, "Invalid syntax"); *matchby++ = '\0'; if (!flood_type_ok(type)) return flood_extban_syntax(b->client, b->is_ok_check, "Unknown flood type"); if (strlen(type) > 16) return flood_extban_syntax(b->client, b->is_ok_check, "Too many flood types specified"); b->banstr = matchby; if (extban_is_ok_nuh_extban(b) == 0) { /* This could be anything ranging from: * invalid n!u@h syntax, unknown (sub)extbantype, * disabled extban type in conf, too much recursion, etc. */ return flood_extban_syntax(b->client, b->is_ok_check, "Invalid matcher"); } return 1; /* OK */ }