From 58618bf2b6ccefd631b671fb055e75d44ddc7452 Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Sun, 1 Sep 2019 20:45:47 +0200 Subject: [PATCH] Add support for ban exceptions, via /ELINE and via the config file. Still need to fix some FIXME/TODO items and things haven't been fully tested yet, so server sync issues or crashes are still possible. Release notes will be updated another day as well.. --- include/h.h | 1 + include/modules.h | 3 +- include/struct.h | 9 +- src/api-efunctions.c | 2 + src/bsd.c | 2 +- src/conf.c | 386 +++------------------------- src/hash.c | 2 +- src/modules/blacklist.c | 2 +- src/modules/stats.c | 26 +- src/modules/tkl.c | 550 ++++++++++++++++++++++++++++++++++------ 10 files changed, 532 insertions(+), 451 deletions(-) diff --git a/include/h.h b/include/h.h index c1e38b80c..462b05175 100644 --- a/include/h.h +++ b/include/h.h @@ -714,6 +714,7 @@ extern MODVAR void (*broadcast_md_globalvar)(ModDataInfo *mdi, ModData *md); extern MODVAR void (*broadcast_md_globalvar_cmd)(aClient *except, aClient *sender, char *varname, char *value); extern MODVAR int (*tkl_ip_hash)(char *ip); extern MODVAR int (*tkl_ip_hash_type)(int type); +extern MODVAR int (*find_tkl_exception)(int ban_type, aClient *cptr); /* /Efuncs */ extern MODVAR aMotdFile opermotd, svsmotd, motd, botmotd, smotd, rules; diff --git a/include/modules.h b/include/modules.h index c4b0ba55e..bf606d3bf 100644 --- a/include/modules.h +++ b/include/modules.h @@ -972,7 +972,7 @@ int hooktype_remote_nickchange(aClient *cptr, aClient *sptr, char *newnick); int hooktype_channel_create(aClient *sptr, aChannel *chptr); int hooktype_channel_destroy(aChannel *chptr, int *should_destroy); int hooktype_remote_chanmode(aClient *cptr, aClient *sptr, aChannel *chptr, char *modebuf, char *parabuf, time_t sendts, int samode); -int hooktype_tkl_except(aClient *cptr, aTKline *tkl); +int hooktype_tkl_except(aClient *cptr, int ban_type); int hooktype_umode_change(aClient *sptr, long setflags, long newflags); int hooktype_topic(aClient *cptr, aClient *sptr, aChannel *chptr, char *topic); int hooktype_rehash_complete(void); @@ -1245,6 +1245,7 @@ enum EfunctionType { EFUNC_FIND_TKL_BANEXCEPTION, EFUNC_FIND_TKL_NAMEBAN, EFUNC_FIND_TKL_SPAMFILTER, + EFUNC_FIND_TKL_EXCEPTION, }; /* Module flags */ diff --git a/include/struct.h b/include/struct.h index c1e63b0d2..32b365e3b 100644 --- a/include/struct.h +++ b/include/struct.h @@ -716,6 +716,9 @@ struct Server { #define TKL_SPAMF 0x0020 #define TKL_NAME 0x0040 #define TKL_EXCEPTION 0x0080 +/* these are not real tkl types, but only used for exceptions: */ +#define TKL_THROTTLE 0x1000 +#define TKL_BLACKLIST 0x2000 #define TKLIsServerBan(tkl) ((tkl)->type & (TKL_KILL|TKL_ZAP|TKL_SHUN)) #define TKLIsServerBanType(tpe) ((tpe) & (TKL_KILL|TKL_ZAP|TKL_SHUN)) @@ -1072,12 +1075,6 @@ struct _configflag_tld #define CRULE_ALL 0 #define CRULE_AUTO 1 -#define CONF_EXCEPT_BAN 1 -#define CONF_EXCEPT_TKL 2 -#define CONF_EXCEPT_THROTTLE 3 -#define CONF_EXCEPT_BLACKLIST 4 - - struct _configitem { ConfigItem *prev, *next; ConfigFlag flag; diff --git a/src/api-efunctions.c b/src/api-efunctions.c index 5fd5d4d09..5ef5dc229 100644 --- a/src/api-efunctions.c +++ b/src/api-efunctions.c @@ -116,6 +116,7 @@ aTKline *(*find_tkl_serverban)(int type, char *usermask, char *hostmask, int sof aTKline *(*find_tkl_banexception)(int type, char *usermask, char *hostmask, int softban); aTKline *(*find_tkl_nameban)(int type, char *name, int hold); aTKline *(*find_tkl_spamfilter)(int type, char *match_string, unsigned short action, unsigned short target); +int (*find_tkl_exception)(int ban_type, aClient *cptr); Efunction *EfunctionAddMain(Module *module, EfunctionType eftype, int (*func)(), void (*vfunc)(), void *(*pvfunc)(), char *(*cfunc)()) { @@ -352,4 +353,5 @@ void efunctions_init(void) efunc_init_function(EFUNC_FIND_TKL_BANEXCEPTION, find_tkl_banexception, NULL); efunc_init_function(EFUNC_FIND_TKL_NAMEBAN, find_tkl_nameban, NULL); efunc_init_function(EFUNC_FIND_TKL_SPAMFILTER, find_tkl_spamfilter, NULL); + efunc_init_function(EFUNC_FIND_TKL_EXCEPTION, find_tkl_exception, NULL); } diff --git a/src/bsd.c b/src/bsd.c index 585b04c43..8f7f5bed3 100644 --- a/src/bsd.c +++ b/src/bsd.c @@ -977,7 +977,7 @@ refuse_client: j = 1; - if (!Find_except(acptr, CONF_EXCEPT_THROTTLE)) + if (!find_tkl_exception(TKL_THROTTLE, acptr)) { list_for_each_entry(acptr2, &unknown_list, lclient_node) { diff --git a/src/conf.c b/src/conf.c index eb867bb67..0b4289c26 100644 --- a/src/conf.c +++ b/src/conf.c @@ -2935,19 +2935,9 @@ ConfigItem_ban *Find_ban(aClient *sptr, char *host, short type) if (sptr) { if (match_user(ban->mask, sptr, MATCH_CHECK_REAL)) - { - /* Person got a exception */ - // FIXME: this code is for the transition - // it should not be called blindly like - // we do now, since it would allow bypassing - // of even qlines and such.. - // contact Syzop when in doubt :D - if (Find_except(sptr, CONF_EXCEPT_BAN)) - return NULL; return ban; - } } - else if (match_simple(ban->mask, host)) /* We don't worry about exceptions */ + else if (match_simple(ban->mask, host)) return ban; } } @@ -2969,14 +2959,9 @@ ConfigItem_ban *Find_banEx(aClient *sptr, char *host, short type, short type2) if (sptr) { if (match_user(ban->mask, sptr, MATCH_CHECK_REAL)) - { - /* Person got a exception */ - if (Find_except(sptr, type)) - return NULL; return ban; - } } - else if (match_simple(ban->mask, host)) /* We don't worry about exceptions */ + else if (match_simple(ban->mask, host)) return ban; } } @@ -5493,117 +5478,26 @@ int _test_allow_dcc(ConfigFile *conf, ConfigEntry *ce) return errors; } -void create_tkl_except_ii(char *mask, char *type) +int _conf_except(ConfigFile *conf, ConfigEntry *ce) { - ConfigItem_except *ca; - NameValue *opf; - ca = MyMallocEx(sizeof(ConfigItem_except)); - ca->mask = strdup(mask); - - opf = config_binary_flags_search(ExceptTklFlags, type, ARRAY_SIZEOF(ExceptTklFlags)); - ca->type = opf->flag; - ca->flag.type = CONF_EXCEPT_TKL; - AddListItem(ca, conf_except); -} - -void create_tkl_except(char *mask, char *type) -{ - if (!strcmp(type, "all")) - { - /* Special treatment */ - int i; - for (i = 0; i < ARRAY_SIZEOF(ExceptTklFlags); i++) - if (ExceptTklFlags[i].flag) - create_tkl_except_ii(mask, ExceptTklFlags[i].name); - } - else - create_tkl_except_ii(mask, type); -} - -int _conf_except(ConfigFile *conf, ConfigEntry *ce) -{ - - ConfigEntry *cep; - ConfigItem_except *ca; Hook *h; + int value; - if (!strcmp(ce->ce_vardata, "ban")) { - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!strcmp(cep->ce_varname, "mask")) { - ca = MyMallocEx(sizeof(ConfigItem_except)); - ca->mask = strdup(cep->ce_vardata); - ca->flag.type = CONF_EXCEPT_BAN; - AddListItem(ca, conf_except); - } - else { - } - } + for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) + { + value = (*(h->func.intfunc))(conf,ce,CONFIG_EXCEPT); + if (value == 1) + break; } - else if (!strcmp(ce->ce_vardata, "throttle")) { - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!strcmp(cep->ce_varname, "mask")) { - ca = MyMallocEx(sizeof(ConfigItem_except)); - ca->mask = strdup(cep->ce_vardata); - ca->flag.type = CONF_EXCEPT_THROTTLE; - AddListItem(ca, conf_except); - } - else { - } - } - } - else if (!strcmp(ce->ce_vardata, "tkl")) { - ConfigEntry *mask = NULL, *type = NULL; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!strcmp(cep->ce_varname, "mask")) - mask = cep; - else if (!strcmp(cep->ce_varname, "type")) - type = cep; - } - if (type->ce_vardata) - create_tkl_except(mask->ce_vardata, type->ce_vardata); - else - { - ConfigEntry *cepp; - for (cepp = type->ce_entries; cepp; cepp = cepp->ce_next) - create_tkl_except(mask->ce_vardata, cepp->ce_varname); - } - } - else if (!strcmp(ce->ce_vardata, "blacklist")) { - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!strcmp(cep->ce_varname, "mask")) { - ca = MyMallocEx(sizeof(ConfigItem_except)); - ca->mask = strdup(cep->ce_vardata); - ca->flag.type = CONF_EXCEPT_BLACKLIST; - AddListItem(ca, conf_except); - } - else { - } - } - - } - else { - int value; - for (h = Hooks[HOOKTYPE_CONFIGRUN]; h; h = h->next) - { - value = (*(h->func.intfunc))(conf,ce,CONFIG_EXCEPT); - if (value == 1) - break; - } - } return 1; } -int _test_except(ConfigFile *conf, ConfigEntry *ce) +int _test_except(ConfigFile *conf, ConfigEntry *ce) { - ConfigEntry *cep; - int errors = 0; + int errors = 0; Hook *h; - char has_mask = 0; + int used = 0; if (!ce->ce_vardata) { @@ -5612,243 +5506,41 @@ int _test_except(ConfigFile *conf, ConfigEntry *ce) return 1; } - if (!strcmp(ce->ce_vardata, "ban")) + for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) { - for (cep = ce->ce_entries; cep; cep = cep->ce_next) + int value, errs = 0; + if (h->owner && !(h->owner->flags & MODFLAG_TESTING) + && !(h->owner->options & MOD_OPT_PERM)) + continue; + value = (*(h->func.intfunc))(conf,ce,CONFIG_EXCEPT,&errs); + if (value == 2) + used = 1; + if (value == 1) { - if (config_is_blankorempty(cep, "except ban")) - { - errors++; - continue; - } - if (!strcmp(cep->ce_varname, "mask")) - { - if (has_mask) - { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "except ban::mask"); - continue; - } - has_mask = 1; - } - else - { - config_error_unknown(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "except ban", cep->ce_varname); - errors++; - continue; - } + used = 1; + break; } - if (!has_mask) + if (value == -1) { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - "except ban::mask"); - errors++; + used = 1; + errors += errs; + break; + } + if (value == -2) + { + used = 1; + errors += errs; } - return errors; } - else if (!strcmp(ce->ce_vardata, "throttle")) { - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (config_is_blankorempty(cep, "except throttle")) - { - errors++; - continue; - } - if (!strcmp(cep->ce_varname, "mask")) - { - has_mask = 1; - } - else - { - config_error_unknown(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "except throttle", cep->ce_varname); - errors++; - continue; - } - } - if (!has_mask) - { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - "except throttle::mask"); - errors++; - } - return errors; - } - else if (!strcmp(ce->ce_vardata, "tkl")) { - char has_type = 0; - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (!strcmp(cep->ce_varname, "mask")) - { - if (!cep->ce_vardata) - { - config_error_empty(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "except tkl", "mask"); - errors++; - continue; - } - if (has_mask) - { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "except tkl::mask"); - continue; - } - has_mask = 1; - } - else if (!strcmp(cep->ce_varname, "type")) - { - if (has_type) - { - config_warn_duplicate(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "except tkl::type"); - continue; - } - if (cep->ce_vardata) - { - if (!strcmp(cep->ce_vardata, "tkline") || - !strcmp(cep->ce_vardata, "tzline")) - { - config_error("%s:%i: except tkl of type %s is" - " deprecated. Use except ban {}" - " instead", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, - cep->ce_vardata); - errors++; - } - if (!config_binary_flags_search(ExceptTklFlags, - cep->ce_vardata, ARRAY_SIZEOF(ExceptTklFlags))) - { - config_error("%s:%i: unknown except tkl type %s", - cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, - cep->ce_vardata); - return 1; - } - } - else if (cep->ce_entries) - { - ConfigEntry *cepp; - for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) - { - if (!strcmp(cepp->ce_varname, "tkline") || - !strcmp(cepp->ce_varname, "tzline")) - { - config_error("%s:%i: except tkl of type %s is" - " deprecated. Use except ban {}" - " instead", - cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, - cepp->ce_varname); - errors++; - } - if (!config_binary_flags_search(ExceptTklFlags, - cepp->ce_varname, ARRAY_SIZEOF(ExceptTklFlags))) - { - config_error("%s:%i: unknown except tkl type %s", - cepp->ce_fileptr->cf_filename, - cepp->ce_varlinenum, - cepp->ce_varname); - return 1; - } - } - } - else - { - config_error_empty(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "except tkl", "type"); - errors++; - continue; - } - has_type = 1; - } - else - { - config_error_unknown(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "except tkl", cep->ce_varname); - errors++; - continue; - } - } - if (!has_mask) - { - config_error("%s:%i: except tkl without mask item", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - return 1; - } - if (!has_type) - { - config_error("%s:%i: except tkl without type item", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum); - return 1; - } - return errors; - } - else if (!strcmp(ce->ce_vardata, "blacklist")) { - for (cep = ce->ce_entries; cep; cep = cep->ce_next) - { - if (config_is_blankorempty(cep, "except blacklist")) - { - errors++; - continue; - } - if (!strcmp(cep->ce_varname, "mask")) - { - has_mask = 1; - } - else - { - config_error_unknown(cep->ce_fileptr->cf_filename, - cep->ce_varlinenum, "except blacklist", cep->ce_varname); - errors++; - continue; - } - } - if (!has_mask) - { - config_error_missing(ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - "except blacklist::mask"); - errors++; - } - return errors; - } - else { - int used = 0; - for (h = Hooks[HOOKTYPE_CONFIGTEST]; h; h = h->next) - { - int value, errs = 0; - if (h->owner && !(h->owner->flags & MODFLAG_TESTING) - && !(h->owner->options & MOD_OPT_PERM)) - continue; - value = (*(h->func.intfunc))(conf,ce,CONFIG_EXCEPT,&errs); - if (value == 2) - used = 1; - if (value == 1) - { - used = 1; - break; - } - if (value == -1) - { - used = 1; - errors += errs; - break; - } - if (value == -2) - { - used = 1; - errors += errs; - } - } - if (!used) { - config_error("%s:%i: unknown except type %s", - ce->ce_fileptr->cf_filename, ce->ce_varlinenum, - ce->ce_vardata); - return 1; - } + if (!used) + { + config_error("%s:%i: unknown except type %s", + ce->ce_fileptr->cf_filename, ce->ce_varlinenum, + ce->ce_vardata); + return 1; } + return errors; } diff --git a/src/hash.c b/src/hash.c index d7cfde630..4e607c48e 100644 --- a/src/hash.c +++ b/src/hash.c @@ -999,7 +999,7 @@ int throttle_can_connect(aClient *sptr) return 1; else { - if (Find_except(sptr, CONF_EXCEPT_THROTTLE)) + if (find_tkl_exception(TKL_THROTTLE, sptr)) return 2; if (b->count+1 > (THROTTLING_COUNT ? THROTTLING_COUNT : 3)) return 0; diff --git a/src/modules/blacklist.c b/src/modules/blacklist.c index 15ad0c44d..d3522fd6c 100644 --- a/src/modules/blacklist.c +++ b/src/modules/blacklist.c @@ -560,7 +560,7 @@ int blacklist_start_check(aClient *cptr) Blacklist *bl; /* If the user is on 'except blacklist' then don't bother checking... */ - if (Find_except(cptr, CONF_EXCEPT_BLACKLIST)) + if (find_tkl_exception(TKL_BLACKLIST, cptr)) return 0; if (!BLUSER(cptr)) diff --git a/src/modules/stats.c b/src/modules/stats.c index 0022da632..082eb88f9 100644 --- a/src/modules/stats.c +++ b/src/modules/stats.c @@ -59,7 +59,7 @@ int stats_banversion(aClient *, char *); int stats_links(aClient *, char *); int stats_denylinkall(aClient *, char *); int stats_gline(aClient *, char *); -int stats_exceptban(aClient *, char *); +int stats_except(aClient *, char *); int stats_allow(aClient *, char *); int stats_command(aClient *, char *); int stats_oper(aClient *, char *); @@ -71,7 +71,6 @@ int stats_uline(aClient *, char *); int stats_vhost(aClient *, char *); int stats_mem(aClient *, char *); int stats_denylinkauto(aClient *, char *); -int stats_exceptthrottle(aClient *, char *); int stats_denydcc(aClient *, char *); int stats_kline(aClient *, char *); int stats_banrealname(aClient *, char *); @@ -107,7 +106,7 @@ struct statstab StatsTable[] = { { 'B', "banversion", stats_banversion, 0 }, { 'C', "link", stats_links, 0 }, { 'D', "denylinkall", stats_denylinkall, 0 }, - { 'E', "exceptban", stats_exceptban, 0 }, + { 'E', "except", stats_except, 0 }, { 'F', "denydcc", stats_denydcc, 0 }, { 'G', "gline", stats_gline, FLAGS_AS_PARA }, { 'H', "link", stats_links, 0 }, @@ -129,7 +128,6 @@ struct statstab StatsTable[] = { { 'Z', "mem", stats_mem, 0 }, { 'c', "link", stats_links, 0 }, { 'd', "denylinkauto", stats_denylinkauto, 0 }, - { 'e', "exceptthrottle",stats_exceptthrottle, 0 }, { 'f', "spamfilter", stats_spamfilter, FLAGS_AS_PARA }, { 'g', "gline", stats_gline, FLAGS_AS_PARA }, { 'h', "link", stats_links, 0 }, @@ -490,8 +488,10 @@ int stats_spamfilter(aClient *sptr, char *para) return 0; } -int stats_exceptban(aClient *sptr, char *para) +int stats_except(aClient *sptr, char *para) { +#if 0 + // FIXME: update numeric and send list to users ;) ConfigItem_except *excepts; for (excepts = conf_except; excepts; excepts = excepts->next) { @@ -500,6 +500,7 @@ int stats_exceptban(aClient *sptr, char *para) else if (excepts->flag.type == CONF_EXCEPT_TKL) sendnumeric(sptr, RPL_STATSEXCEPTTKL, tkl_typetochar(excepts->type), excepts->mask); } +#endif return 0; } @@ -924,15 +925,6 @@ int stats_denylinkauto(aClient *sptr, char *para) return 0; } -int stats_exceptthrottle(aClient *sptr, char *para) -{ - ConfigItem_except *excepts; - for (excepts = conf_except; excepts; excepts = excepts->next) - if (excepts->flag.type == CONF_EXCEPT_THROTTLE) - sendnumeric(sptr, RPL_STATSELINE, excepts->mask); - return 0; -} - int stats_denydcc(aClient *sptr, char *para) { ConfigItem_deny_dcc *denytmp; @@ -976,12 +968,6 @@ int stats_kline(aClient *sptr, char *para) tkl_stats(sptr, TKL_KILL, NULL); tkl_stats(sptr, TKL_ZAP, NULL); - - for (excepts = conf_except; excepts; excepts = excepts->next) - { - if (excepts->flag.type == CONF_EXCEPT_BAN) - sendnumeric(sptr, RPL_STATSKLINE, "E", excepts->mask, ""); - } return 0; } diff --git a/src/modules/tkl.c b/src/modules/tkl.c index ae49d3d88..09052b1c4 100644 --- a/src/modules/tkl.c +++ b/src/modules/tkl.c @@ -28,6 +28,8 @@ int tkl_config_test_spamfilter(ConfigFile *, ConfigEntry *, int, int *); int tkl_config_run_spamfilter(ConfigFile *, ConfigEntry *, int); int tkl_config_test_ban(ConfigFile *, ConfigEntry *, int, int *); int tkl_config_run_ban(ConfigFile *, ConfigEntry *, int); +int tkl_config_test_except(ConfigFile *, ConfigEntry *, int, int *); +int tkl_config_run_except(ConfigFile *, ConfigEntry *, int); CMD_FUNC(m_gline); CMD_FUNC(m_shun); CMD_FUNC(m_tempshun); @@ -35,6 +37,7 @@ CMD_FUNC(m_gzline); CMD_FUNC(m_kline); CMD_FUNC(m_zline); CMD_FUNC(m_spamfilter); +CMD_FUNC(m_eline); int m_tkl_line(aClient *cptr, aClient *sptr, int parc, char *parv[], char* type); int _tkl_hash(unsigned int c); char _tkl_typetochar(int type); @@ -76,6 +79,7 @@ aTKline *_find_tkl_serverban(int type, char *usermask, char *hostmask, int softb aTKline *_find_tkl_banexception(int type, char *usermask, char *hostmask, int softban); aTKline *_find_tkl_nameban(int type, char *name, int hold); aTKline *_find_tkl_spamfilter(int type, char *match_string, unsigned short action, unsigned short target); +int _find_tkl_exception(int ban_type, aClient *cptr); /* Externals (only for us :D) */ extern int MODVAR spamf_ugly_vchanoverride; @@ -94,6 +98,7 @@ MOD_TEST(tkl) MARK_AS_OFFICIAL_MODULE(modinfo); HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkl_config_test_spamfilter); HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkl_config_test_ban); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, tkl_config_test_except); EfunctionAdd(modinfo->handle, EFUNC_TKL_HASH, _tkl_hash); EfunctionAdd(modinfo->handle, EFUNC_TKL_TYPETOCHAR, TO_INTFUNC(_tkl_typetochar)); EfunctionAdd(modinfo->handle, EFUNC_TKL_CHARTOTYPE, TO_INTFUNC(_tkl_chartotype)); @@ -126,6 +131,7 @@ MOD_TEST(tkl) EfunctionAdd(modinfo->handle, EFUNC_TKL_IP_HASH_TYPE, _tkl_ip_hash_type); EfunctionAddVoid(modinfo->handle, EFUNC_SENDNOTICE_TKL_ADD, _sendnotice_tkl_add); EfunctionAddVoid(modinfo->handle, EFUNC_SENDNOTICE_TKL_DEL, _sendnotice_tkl_del); + EfunctionAdd(modinfo->handle, EFUNC_FIND_TKL_EXCEPTION, _find_tkl_exception); return MOD_SUCCESS; } @@ -134,6 +140,7 @@ MOD_INIT(tkl) MARK_AS_OFFICIAL_MODULE(modinfo); HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkl_config_run_spamfilter); HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkl_config_run_ban); + HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkl_config_run_except); CommandAdd(modinfo->handle, "GLINE", m_gline, 3, M_OPER); CommandAdd(modinfo->handle, "SHUN", m_shun, 3, M_OPER); CommandAdd(modinfo->handle, "TEMPSHUN", m_tempshun, 2, M_OPER); @@ -141,6 +148,7 @@ MOD_INIT(tkl) CommandAdd(modinfo->handle, "KLINE", m_kline, 3, M_OPER); CommandAdd(modinfo->handle, "GZLINE", m_gzline, 3, M_OPER); CommandAdd(modinfo->handle, "SPAMFILTER", m_spamfilter, 7, M_OPER); + CommandAdd(modinfo->handle, "ELINE", m_eline, 4, M_OPER); CommandAdd(modinfo->handle, "TKL", _m_tkl, MAXPARA, M_OPER|M_SERVER); MARK_AS_OFFICIAL_MODULE(modinfo); return MOD_SUCCESS; @@ -577,6 +585,176 @@ int tkl_config_run_ban(ConfigFile *cf, ConfigEntry *ce, int configtype) return 1; } +int tkl_config_test_except(ConfigFile *cf, ConfigEntry *ce, int configtype, int *errs) +{ + ConfigEntry *cep; + ConfigItem_except *ca; + Hook *h; + char *bantypes = NULL; + int errors = 0; + + /* We are only interested in except { } blocks */ + if (configtype != CONFIG_EXCEPT) + return 0; + + /* These are the types that we handle */ + if (strcmp(ce->ce_vardata, "ban") && strcmp(ce->ce_vardata, "throttle") && + strcmp(ce->ce_vardata, "tkl") && strcmp(ce->ce_vardata, "blacklist") && + strcmp(ce->ce_vardata, "spamfilter")) + { + return 0; + } + + if (!strcmp(ce->ce_vardata, "tkl")) + { + config_error("%s:%d: except tkl { } has been renamed to except ban { }", + ce->ce_fileptr->cf_filename, ce->ce_varlinenum); + config_status("Please rename your block in the configuration file."); + *errs = 1; + return -1; + } + + for (cep = ce->ce_entries; cep; cep = cep->ce_next) + { + if (!strcmp(cep->ce_varname, "mask")) + { + // TODO: verify mask, can be both a list or just 1 directly + } else + if (!strcmp(cep->ce_varname, "type")) + { + // TODO: verify type, can be both a list or just 1 directly + } else { + config_error_unknown(cep->ce_fileptr->cf_filename, + cep->ce_varlinenum, "except", cep->ce_varname); + errors++; + continue; + } + } + + *errs = errors; + return errors ? -1 : 1; +} + +void config_create_tkl_except(char *mask, char *bantypes) +{ + char *usermask = NULL; + char *hostmask = NULL; + int soft = 0; + char buf[256], *p; + + if (*mask == '%') + { + soft = 1; + mask++; + } + strlcpy(buf, mask, sizeof(buf)); + p = strchr(buf, '@'); + if (!p) + { + usermask = "*"; + hostmask = buf; + } else { + *p++ = '\0'; + usermask = buf; + hostmask = p; + } + + if ((*usermask == ':') || (*hostmask == ':')) + { + config_error("Cannot add illegal ban '%s': for a given user@host neither" + "user nor host may start with a : character (semicolon)", mask); + return; + } + + tkl_add_banexception(TKL_EXCEPTION, usermask, hostmask, "Added in configuration file", + "-config-", 0, TStime(), soft, bantypes, TKL_FLAG_CONFIG); +} + +void tkl_config_run_except_add_bantype(char *bantypes, size_t bantypeslen, char *name) +{ + if (!strcmp(name, "kline")) + strlcat(bantypes, "k", sizeof(bantypes)); + // etc etc.. but probably in a different way ;) +} + +int tkl_config_run_except(ConfigFile *cf, ConfigEntry *ce, int configtype) +{ + ConfigEntry *cep, *cepp; + ConfigItem_except *ca; + Hook *h; + char *default_bantypes = NULL; + char bantypes[64]; + + /* We are only interested in except { } blocks */ + if (configtype != CONFIG_EXCEPT) + return 0; + + /* These are the types that we handle */ + if (strcmp(ce->ce_vardata, "ban") && strcmp(ce->ce_vardata, "throttle") && + strcmp(ce->ce_vardata, "blacklist") && + strcmp(ce->ce_vardata, "spamfilter")) + { + return 0; + } + + *bantypes = '\0'; + + /* First configure all the types */ + for (cep = ce->ce_entries; cep; cep = cep->ce_next) + { + if (!strcmp(cep->ce_varname, "type")) + { + if (cep->ce_entries) + { + /* type { x; y; z; }; */ + for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + tkl_config_run_except_add_bantype(bantypes, sizeof(bantypes), cepp->ce_varname); + } else + if (cep->ce_vardata) + { + /* type x; */ + tkl_config_run_except_add_bantype(bantypes, sizeof(bantypes), cep->ce_vardata); + } + } + } + + if (!*bantypes) + { + /* Default setting if no 'type' is specified: */ + if (!strcmp(ce->ce_vardata, "ban")) + strlcpy(bantypes, "kgzZs", sizeof(bantypes)); + else if (!strcmp(ce->ce_vardata, "throttle")) + strlcpy(bantypes, "t", sizeof(bantypes)); + else if (!strcmp(ce->ce_vardata, "blacklist")) + strlcpy(bantypes, "b", sizeof(bantypes)); + else if (!strcmp(ce->ce_vardata, "spamfilter")) + strlcpy(bantypes, "f", sizeof(bantypes)); + else + abort(); /* someone can't code */ + } + + /* Now walk through all mask entries */ + for (cep = ce->ce_entries; cep; cep = cep->ce_next) + { + if (!strcmp(cep->ce_varname, "mask")) + { + if (cep->ce_entries) + { + /* mask { *@1.1.1.1; *@2.2.2.2; *@3.3.3.3; }; */ + for (cepp = cep->ce_entries; cepp; cepp = cepp->ce_next) + config_create_tkl_except(cepp->ce_varname, bantypes); + } else + if (cep->ce_vardata) + { + /* mask *@1.1.1.1; */ + config_create_tkl_except(cep->ce_vardata, bantypes); + } + } + } + + return 1; +} + /** Return unique spamfilter id for aTKline */ char *spamfilter_id(aTKline *tk) { @@ -614,7 +792,6 @@ CMD_FUNC(m_gline) } return m_tkl_line(cptr, sptr, parc, parv, "G"); - } /** GZLINE - Global zline. @@ -891,7 +1068,7 @@ int ban_too_broad(char *usermask, char *hostmask) * This allows us doing some syntax checking and other helpful * things that are the same for many types of *LINES. */ -int m_tkl_line(aClient *cptr, aClient *sptr, int parc, char *parv[], char* type) +int m_tkl_line(aClient *cptr, aClient *sptr, int parc, char *parv[], char *type) { time_t secs; int whattodo = 0; /* 0 = add 1 = del */ @@ -971,7 +1148,7 @@ int m_tkl_line(aClient *cptr, aClient *sptr, int parc, char *parv[], char* type) } if (*hostmask == ':') { - sendnotice(sptr, "[error] For (weird) technical reasons you cannot start the host with a ':', sorry"); + sendnotice(sptr, "[error] For technical reasons you cannot start the host with a ':', sorry"); return 0; } if (((*type == 'z') || (*type == 'Z')) && !whattodo) @@ -1088,6 +1265,217 @@ int m_tkl_line(aClient *cptr, aClient *sptr, int parc, char *parv[], char* type) return 0; } +int eline_syntax(aClient *sptr) +{ + sendnotice(sptr, " Syntax: /ELINE "); + sendnotice(sptr, "Valid bantypes are:"); + sendnotice(sptr, "k: K-Line g: G-Line"); + sendnotice(sptr, "z: Z-Line Z: Global Z-Line"); + sendnotice(sptr, "q: Q-Line"); + sendnotice(sptr, "s: Shun"); + sendnotice(sptr, "f: Spamfilter"); + sendnotice(sptr, "t: Throttling"); + sendnotice(sptr, "b: Blacklist checking"); + sendnotice(sptr, "Example: /ELINE *@unrealircd.org kgzZ 0 This user is exempt"); + sendnotice(sptr, "-"); + sendnotice(sptr, "To get a list of all current ELINEs, type: /STATS exceptban"); + return 0; +} + +/** Check if any of the specified types require the + * exception to be placed on *@ip rather than + * user@host or *@host. For eg zlines. + */ +int eline_type_requires_ip(char *bantypes) +{ + if (strchr(bantypes, 'z') || strchr(bantypes, 'Z') || strchr(bantypes, 't') || strchr(bantypes, 'b')) + return 1; + return 0; +} + +CMD_FUNC(m_eline) +{ + time_t secs = 0; + int add = 1; + time_t i; + aClient *acptr = NULL; + char *mask = NULL; + char mo[1024], mo2[1024]; + char *p, *usermask, *hostmask, *bantypes=NULL, *reason; + char *tkllayer[11] = { + me.name, /*0 server.name */ + NULL, /*1 +|- */ + NULL, /*2 E */ + NULL, /*3 user */ + NULL, /*4 host */ + NULL, /*5 set_by */ + "0", /*6 expire_at */ + "-", /*7 set_at */ + "-", /*8 ban types */ + "-", /*9 reason */ + NULL + }; + struct tm *t; + + if (IsServer(sptr)) + return 0; + + if (!ValidatePermissionsForPath("server-ban:eline",sptr,NULL,NULL,NULL)) + { + sendnumeric(sptr, ERR_NOPRIVILEGES); + return 0; + } + + /* For del we need at least: + * ELINE -user@host + * The 'add' case is checked later. + */ + if ((parc < 2) || BadPtr(parv[1])) + return eline_syntax(sptr); + + mask = parv[1]; + if (*mask == '-') + { + add = 0; + mask++; + } + else if (*mask == '+') + { + add = 1; + mask++; + } + + /* For add we need more: + * ELINE user@host bantypes expiry :reason + */ + if (add) + { + if ((parc < 5) || BadPtr(parv[4])) + return eline_syntax(sptr); + bantypes = parv[2]; + reason = parv[4]; + } + + if (strchr(mask, '!')) + { + sendnotice(sptr, "[error] Cannot have '!' in masks."); + return 0; + } + if (*mask == ':') + { + sendnotice(sptr, "[error] Mask cannot start with a ':'."); + return 0; + } + if (strchr(mask, ' ')) + return 0; + + /* Check if it's a hostmask and legal .. */ + p = strchr(mask, '@'); + if (p) + { + if ((p == mask) || !p[1]) + { + sendnotice(sptr, "Error: no user@host specified"); + return 0; + } + usermask = strtok(mask, "@"); + hostmask = strtok(NULL, ""); + if (BadPtr(hostmask)) { + if (BadPtr(usermask)) { + return 0; + } + hostmask = usermask; + usermask = "*"; + } + if (*hostmask == ':') + { + sendnotice(sptr, "[error] For technical reasons you cannot start the host with a ':', sorry"); + return 0; + } + if (add && eline_type_requires_ip(bantypes)) + { + /* Trying to exempt a user from a (G)ZLINE, + * make sure the user isn't specifying a host then. + */ + if (strcmp(usermask, "*")) + { + sendnotice(sptr, "ERROR: Ban exceptions with type z/Z/t/b need to be placed at \037*\037@ipmask, not \037user\037@ipmask. " + "This is because checking (g)zlines, throttling and blacklists is done BEFORE any dns and ident lookups."); + return -1; + } + for (p=hostmask; *p; p++) + if (isalpha(*p) && !isxdigit(*p)) + { + sendnotice(sptr, "ERROR: Ban exceptions with type z/Z/t/b need to be placed at *@\037ipmask\037, not *@\037hostmask\037. " + "(so for example *@192.168.* is ok, but *@*.aol.com is not). " + "This is because checking (g)zlines, throttling and blacklists is done BEFORE any dns and ident lookups."); + return -1; + } + } + } + else + { + /* It's seemingly a nick .. let's see if we can find the user */ + if ((acptr = find_person(mask, NULL))) + { + usermask = "*"; + hostmask = GetIP(acptr); + if (!hostmask) + { + sendnotice(sptr, "Could not get IP for user '%s'", acptr->name); + return 0; + } + } + else + { + sendnumeric(sptr, ERR_NOSUCHNICK, mask); + return 0; + } + } + + if (add) + { + secs = atime(parv[3]); + if ((secs <= 0) && (*parv[3] != '0')) + { + sendnotice(sptr, "*** [error] The expiry time you specified is out of range!"); + return eline_syntax(sptr); + } + } + + tkllayer[1] = add ? "+" : "-"; + tkllayer[2] = "E"; + tkllayer[3] = usermask; + tkllayer[4] = hostmask; + tkllayer[5] = make_nick_user_host(sptr->name, sptr->user->username, GetHost(sptr)); + + if (add) + { + /* Add ELINE */ + if (secs == 0) + ircsnprintf(mo, sizeof(mo), "%lld", (long long)secs); /* "0" */ + else + ircsnprintf(mo, sizeof(mo), "%lld", (long long)(secs + TStime())); + ircsnprintf(mo2, sizeof(mo2), "%lld", (long long)TStime()); + tkllayer[6] = mo; + tkllayer[7] = mo2; + tkllayer[8] = bantypes; + tkllayer[9] = reason; + /* call the tkl layer .. */ + m_tkl(&me, &me, NULL, 10, tkllayer); + } + else + { + /* Remove ELINE */ + /* call the tkl layer .. */ + m_tkl(&me, &me, NULL, 10, tkllayer); + + } + return 0; +} + + + /** Helper function for m_spamfilter, explaining usage. */ int spamfilter_usage(aClient *sptr) { @@ -1447,6 +1835,49 @@ int _tkl_chartotype(char c) /* NOTREACHED */ } +int tkl_banexception_chartotype(char c) +{ + int ret = _tkl_chartotype(c); + if (ret == 0) + { + if (c == 't') + ret = TKL_THROTTLE; + else if (c == 'b') + ret = TKL_BLACKLIST; + } + return ret; +} + +int tkl_banexception_matches_type(aTKline *except, int bantype) +{ + char *p; + int extype; + + if (!TKLIsBanException(except)) + abort(); + + for (p = except->ptr.banexception->bantypes; *p; p++) + { + extype = tkl_banexception_chartotype(*p); + if ((extype & TKL_SPAMF) || (extype & TKL_SHUN) || (extype & TKL_NAME)) + { + /* For spamfilter, shun and qline we don't care + * whether they are global or not. That would only + * be confusing to the admin. + */ + extype &= ~TKL_GLOBAL; + if (bantype & extype) + return 1; + } else { + /* Rest requires an exact match */ + if (bantype == extype) + return 1; + } + } + + return 0; +} + /** Used for finding out which element of the tkl_ip hash table is used (primary element) */ int _tkl_ip_hash(char *ip) { @@ -1972,31 +2403,44 @@ EVENT(tkl_check_expire) } } -/** Helper function for find_tkl_exception() */ -int find_tkl_exception_matcher(aClient *cptr, aTKline *tkl) +/* This is just a helper function for find_tkl_exception() */ +static int find_tkl_exception_matcher(aClient *cptr, int ban_type, aTKline *except_tkl) { char uhost[NICKLEN+HOSTLEN+1]; Hook *hook; - if (!TKLIsBanException(tkl)) + if (!TKLIsBanException(except_tkl)) return 0; - snprintf(uhost, sizeof(uhost), "%s@%s", tkl->ptr.banexception->usermask, tkl->ptr.banexception->hostmask); + if (!tkl_banexception_matches_type(except_tkl, ban_type)) + return 0; - // FIXME: check exception ban types!! (right now it always matches) + snprintf(uhost, sizeof(uhost), "%s@%s", + except_tkl->ptr.banexception->usermask, except_tkl->ptr.banexception->hostmask); if (match_user(uhost, cptr, MATCH_CHECK_REAL)) { - if (!(tkl->ptr.banexception->subtype & TKL_SUBTYPE_SOFT)) + if (!(except_tkl->ptr.banexception->subtype & TKL_SUBTYPE_SOFT)) return 1; /* hard ban exempt */ - if ((tkl->ptr.banexception->subtype & TKL_SUBTYPE_SOFT) && IsLoggedIn(cptr)) + if ((except_tkl->ptr.banexception->subtype & TKL_SUBTYPE_SOFT) && IsLoggedIn(cptr)) return 1; /* soft ban exempt - only matches if user is logged in */ } return 0; /* not found */ } -int find_tkl_exception(aTKline *ban_tkl, aClient *cptr) +/** Search for TKL Exceptions for this user. + * @param ban_type The ban type to check, normally ban_tkl->type. + * @param cptr The user + * @returns 1 if ban exempt, 0 if not. + * @notes + * If you have a TKL ban that matched, say, 'ban_tkl'. + * Then you call this function like this: + * if (find_tkl_exception(ban_tkl->type, cptr)) + * return 0; // User is exempt + * [.. continue and ban the user..] + */ +int _find_tkl_exception(int ban_type, aClient *cptr) { aTKline *tkl, *ret; int index, index2; @@ -2012,7 +2456,7 @@ int find_tkl_exception(aTKline *ban_tkl, aClient *cptr) { for (tkl = tklines_ip_hash[index][index2]; tkl; tkl = tkl->next) { - if (find_tkl_exception_matcher(cptr, tkl)) + if (find_tkl_exception_matcher(cptr, ban_type, tkl)) return 1; /* exempt */ } } @@ -2020,7 +2464,7 @@ int find_tkl_exception(aTKline *ban_tkl, aClient *cptr) /* If not banned (yet), then check regular entries.. */ for (tkl = tklines[tkl_hash('e')]; tkl; tkl = tkl->next) { - if (find_tkl_exception_matcher(cptr, tkl)) + if (find_tkl_exception_matcher(cptr, ban_type, tkl)) return 1; /* exempt */ } @@ -2028,7 +2472,7 @@ int find_tkl_exception(aTKline *ban_tkl, aClient *cptr) for (hook = Hooks[HOOKTYPE_TKL_EXCEPT]; hook; hook = hook->next) { - if (hook->func.intfunc(cptr, tkl) > 0) + if (hook->func.intfunc(cptr, ban_type) > 0) return 1; /* exempt by hook */ } return 0; /* Not exempt */ @@ -2056,7 +2500,7 @@ int find_tkline_match_matcher(aClient *cptr, int skip_soft, aTKline *tkl) ((tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) && !IsLoggedIn(cptr))) { /* Found match. Now check for exception... */ - if (find_tkl_exception(tkl, cptr)) + if (find_tkl_exception(tkl->type, cptr)) return 0; /* exempted */ return 1; /* banned */ } @@ -2142,7 +2586,6 @@ int _find_shun(aClient *cptr) ConfigItem_except *excepts; int match_type = 0; Hook *hook; - int banned = 0; if (IsServer(cptr) || IsMe(cptr)) return -1; @@ -2169,39 +2612,15 @@ int _find_shun(aClient *cptr) ((tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) && !IsLoggedIn(cptr))) { /* Found match. Now check for exception... */ - banned = 1; - match_type = CONF_EXCEPT_TKL; - for (excepts = conf_except; excepts; excepts = excepts->next) - { - if (excepts->flag.type != match_type || (match_type == CONF_EXCEPT_TKL && - excepts->type != tkl->type)) - continue; - - if (match_user(excepts->mask, cptr, MATCH_CHECK_REAL)) - { - banned = 0; /* exempt by except block */ - break; - } - } - for (hook = Hooks[HOOKTYPE_TKL_EXCEPT]; hook; hook = hook->next) - { - if (hook->func.intfunc(cptr, tkl) > 0) - { - banned = 0; /* exempt by hook */ - break; - } - } - if (banned) - break; + if (find_tkl_exception(TKL_SHUN, cptr)) + return 1; + SetShunned(cptr); + return 2; /* Shunned */ } } } - if (!banned) - return 1; - - SetShunned(cptr); - return 2; + return 1; /* No match */ } /** Helper function for spamfilter_build_user_string(). @@ -2351,20 +2770,15 @@ aTKline *_find_qline(aClient *cptr, char *name, int *ishold) if (points != 1) return NULL; - /* It's a services hold */ + /* It's a services hold (except bans don't override this) */ if (tkl->ptr.nameban->hold) { *ishold = 1; return tkl; } - for (excepts = conf_except; excepts; excepts = excepts->next) - { - if (excepts->flag.type != CONF_EXCEPT_TKL || excepts->type != TKL_NAME) - continue; - if (match_user(excepts->mask, cptr, MATCH_CHECK_REAL)) - return NULL; /* exempt */ - } + if (find_tkl_exception(TKL_NAME, cptr)) + return NULL; /* exempt */ return tkl; } @@ -2380,26 +2794,12 @@ aTKline *find_tkline_match_zap_matcher(aClient *cptr, aTKline *tkl) if (match_user(tkl->ptr.serverban->hostmask, cptr, MATCH_CHECK_IP)) { - for (excepts = conf_except; excepts; excepts = excepts->next) - { - /* This used to be: - * if (excepts->flag.type != CONF_EXCEPT_TKL || excepts->type != tkl->type) - * It now checks for 'except ban', hope this is what most people want, - * it is at least the same as in find_tkline_match, which is how it currently - * is when a user is connected. -- Syzop/20081221 - */ - if (excepts->flag.type != CONF_EXCEPT_BAN) - continue; - if (match_user(excepts->mask, cptr, MATCH_CHECK_IP)) - return NULL; /* exempt */ - } - for (hook = Hooks[HOOKTYPE_TKL_EXCEPT]; hook; hook = hook->next) - if (hook->func.intfunc(cptr, tkl) > 0) - return NULL; /* exempt */ - - return tkl; + if (find_tkl_exception(TKL_ZAP, cptr)) + return NULL; /* exempt */ + return tkl; /* banned */ } - return NULL; + + return NULL; /* no match */ } /** Find matching (G)ZLINE, if any. @@ -2993,13 +3393,15 @@ void _sendnotice_tkl_add(aTKline *tkl) { if (tkl->expire_at != 0) { - ircsnprintf(buf, sizeof(buf), "Exception added for %s%s@%s for types '%s' on %s GMT (from %s to expire at %s GMT: %s)", + ircsnprintf(buf, sizeof(buf), "%s added for %s%s@%s for types '%s' on %s GMT (from %s to expire at %s GMT: %s)", + tkl_type_str, (tkl->ptr.banexception->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", tkl->ptr.banexception->usermask, tkl->ptr.banexception->hostmask, tkl->ptr.banexception->bantypes, set_at, tkl->set_by, expire_at, tkl->ptr.banexception->reason); } else { - ircsnprintf(buf, sizeof(buf), "Permanent exception added for %s%s@%s for types '%s' on %s GMT (from %s: %s)", + ircsnprintf(buf, sizeof(buf), "Permanent %s added for %s%s@%s for types '%s' on %s GMT (from %s: %s)", + tkl_type_str, (tkl->ptr.banexception->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", tkl->ptr.banexception->usermask, tkl->ptr.banexception->hostmask, tkl->ptr.banexception->bantypes,