Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/VibeDevelopers/Comet.
Something went wrong. Try again.
6.6 kB · 271 lines
C
at dev
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272/* * Extban that combines other extbans. * * Basic example: * $&:~a,m:*!*@gateway/web/cgi-irc* * Which means: match unidentified webchat users. * ("m" is another new extban type, which just does a normal match). * * More complicated example: * $&:~a,|:(m:*!*@gateway/web/foo,m:*!*@gateway/web/bar) * Which means: unidentified and using the foo or bar gateway. * * Rules: * * - Optional pair of parens around data. * * - component bans are separated by commas, but commas between * matching pairs of parens are skipped. * * - Unbalanced parens are an error. * * - Parens, commas and backslashes can be escaped by backslashes. * * - A backslash before any character other than a paren or backslash * is just a backslash (backslash and character are both used). * * - Non-existant extbans are invalid. * This is primarily for consistency with non-combined bans: * the ircd does not let you set +b $f unless the 'f' extban is loaded, * so setting $&:f should be impossible too. * * Issues: * - Backslashes double inside nested bans. * Hopefully acceptable because they should be rare. * * - Is performance good enough? * I suspect it is, but have done no load testing. */
#include "stdinc.h"#include "modules.h"#include "client.h"#include "ircd.h"
static const char extb_desc[] = "Combination ($&, $|) extban types";
// #define MOD_DEBUG(s) sendto_realops_snomask(SNO_DEBUG, L_NETWIDE, (s))#define MOD_DEBUG(s)#define RETURN_INVALID { recursion_depth--; return EXTBAN_INVALID; }
static int _modinit(void);static void _moddeinit(void);static int eb_or(const char *data, struct Client *client_p, struct Channel *chptr, long mode_type);static int eb_and(const char *data, struct Client *client_p, struct Channel *chptr, long mode_type);static int eb_combi(const char *data, struct Client *client_p, struct Channel *chptr, long mode_type, bool is_and);static int recursion_depth = 0;
DECLARE_MODULE_AV2(extb_extended, _modinit, _moddeinit, NULL, NULL, NULL, NULL, NULL, extb_desc);
static int_modinit(void){ extban_table['&'] = eb_and; extban_table['|'] = eb_or;
return 0;}
static void_moddeinit(void){ extban_table['&'] = NULL; extban_table['|'] = NULL;}
static int eb_or(const char *data, struct Client *client_p, struct Channel *chptr, long mode_type){ return eb_combi(data, client_p, chptr, mode_type, false);}
static int eb_and(const char *data, struct Client *client_p, struct Channel *chptr, long mode_type){ return eb_combi(data, client_p, chptr, mode_type, true);}
static int eb_combi(const char *data, struct Client *client_p, struct Channel *chptr, long mode_type, bool is_and){ const char *p, *banend; bool have_result = false; int allowed_nodes = 11; size_t datalen;
if (recursion_depth >= 5) { MOD_DEBUG("combo invalid: recursion depth too high"); return EXTBAN_INVALID; }
if (EmptyString(data)) { MOD_DEBUG("combo invalid: empty data"); return EXTBAN_INVALID; }
datalen = strlen(data); if (datalen > BANLEN) { /* I'd be sad if this ever happened, but if it does we * could overflow the buffer used below, so... */ MOD_DEBUG("combo invalid: > BANLEN"); return EXTBAN_INVALID; } banend = data + datalen;
if (data[0] == '(') { p = data + 1; banend--; if (*banend != ')') { MOD_DEBUG("combo invalid: starting but no closing paren"); return EXTBAN_INVALID; } } else { p = data; }
/* Empty combibans are invalid. */ if (banend == p) { MOD_DEBUG("combo invalid: no data (after removing parens)"); return EXTBAN_INVALID; }
/* Implementation note: * I want it to be impossible to set a syntactically invalid combi-ban. * (mismatched parens). * That is: valid_extban should return false for those. * Ideally we do not parse the entire ban when actually matching it: * we can just short-circuit if we already know the ban is valid. * Unfortunately there is no separate hook or mode_type for validation, * so we always keep parsing even after we have determined a result. */
recursion_depth++;
while (--allowed_nodes) { bool invert = false; char *child_data, child_data_buf[BANLEN]; ExtbanFunc f;
if (*p == '~') { invert = true; p++; if (p == banend) { MOD_DEBUG("combo invalid: no data after ~"); RETURN_INVALID; } }
f = extban_table[(unsigned char) *p++]; if (!f) { MOD_DEBUG("combo invalid: non-existant child extban"); RETURN_INVALID; }
if (*p == ':') { unsigned int parencount = 0; bool escaped = false, done = false; char *o;
p++;
/* Possible optimization: we can skip the actual copy if * we already have_result. */ o = child_data = child_data_buf; while (true) { if (p == banend) { if (parencount) { MOD_DEBUG("combo invalid: EOD while in parens"); RETURN_INVALID; } break; }
if (escaped) { if (*p != '(' && *p != ')' && *p != '\\' && *p != ',') *o++ = '\\'; *o++ = *p++; escaped = false; } else { switch (*p) { case '\\': escaped = true; break; case '(': parencount++; *o++ = *p; break; case ')': if (!parencount) { MOD_DEBUG("combo invalid: negative parencount"); RETURN_INVALID; } parencount--; *o++ = *p; break; case ',': if (parencount) *o++ = *p; else done = true; break; default: *o++ = *p; break; } if (done) break; p++; } } *o = '\0'; } else { child_data = NULL; }
if (!have_result) { int child_result = f(child_data, client_p, chptr, mode_type);
if (child_result == EXTBAN_INVALID) { MOD_DEBUG("combo invalid: child invalid"); RETURN_INVALID; }
/* Convert child_result to a plain boolean result */ if (invert) child_result = child_result == EXTBAN_NOMATCH; else child_result = child_result == EXTBAN_MATCH;
if (is_and ? !child_result : child_result) have_result = true; }
if (p == banend) break;
if (*p++ != ',') { MOD_DEBUG("combo invalid: no ',' after ban"); RETURN_INVALID; }
if (p == banend) { MOD_DEBUG("combo invalid: banend after ','"); RETURN_INVALID; } }
/* at this point, *p should == banend */ if (p != banend) { MOD_DEBUG("combo invalid: more child extbans than allowed"); RETURN_INVALID; }
recursion_depth--;
if (is_and) return have_result ? EXTBAN_NOMATCH : EXTBAN_MATCH; else return have_result ? EXTBAN_MATCH : EXTBAN_NOMATCH;}