From 8a6c84876e2b517865a27c13da2153e130aaa92d Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Sat, 31 Aug 2019 15:24:52 +0200 Subject: [PATCH] Rewrite/cleanup huge portion of TKL handling (16 files updated, but src/modules/tkl.c is the main one). Also move DB writing/reading functions to src/misc.c so they can be removed out of channeldb and tkldb. Important note to current tkldb users: Unfortunately due to the major cleanup I had to remove upgrading for previously saved tkl db files. That seemed not worth the effort for maybe <15 current users or so. It also makes the tkldb code a lot more cleaner. Otherwise it would be a huge mess. Currently a FIXME item: spamfilter support in RMTKL. --- include/h.h | 24 +- include/modules.h | 10 +- include/struct.h | 49 +- src/conf.c | 28 +- src/misc.c | 159 ++++ src/modules.c | 28 +- src/modules/antirandom.c | 29 +- src/modules/authprompt.c | 11 +- src/modules/channeldb.c | 74 -- src/modules/join.c | 2 +- src/modules/nick.c | 4 +- src/modules/pass.c | 2 +- src/modules/rmtkl.c | 67 +- src/modules/stats.c | 4 +- src/modules/tkl.c | 1510 +++++++++++++++++++++----------------- src/modules/tkldb.c | 590 ++++++--------- 16 files changed, 1391 insertions(+), 1200 deletions(-) diff --git a/include/h.h b/include/h.h index 247cf8307..b538a6551 100644 --- a/include/h.h +++ b/include/h.h @@ -645,8 +645,20 @@ extern MODVAR int (*tkl_hash)(unsigned int c); extern MODVAR char (*tkl_typetochar)(int type); extern MODVAR int (*tkl_chartotype)(char c); extern MODVAR char *(*tkl_type_string)(aTKline *tk); -extern MODVAR aTKline *(*tkl_add_line)(int type, char *usermask, char *hostmask, char *reason, char *setby, - time_t expire_at, time_t set_at, time_t spamf_tkl_duration, char *spamf_tkl_reason, MatchType spamf_match_type, int soft, int flags); +extern MODVAR aTKline *(*tkl_add_serverban)(int type, char *usermask, char *hostmask, char *reason, char *setby, + time_t expire_at, time_t set_at, int soft, int flags); +extern MODVAR aTKline *(*tkl_add_nameban)(int type, char *name, int hold, char *reason, char *setby, + time_t expire_at, time_t set_at, int flags); +extern MODVAR aTKline *(*tkl_add_spamfilter)(int type, unsigned short target, unsigned short action, aMatch *match, char *setby, + time_t expire_at, time_t set_at, + time_t spamf_tkl_duration, char *spamf_tkl_reason, + int flags); +extern MODVAR aTKline *(*find_tkl_serverban)(int type, char *usermask, char *hostmask, int softban); +extern MODVAR aTKline *(*find_tkl_nameban)(int type, char *name, int hold); +extern MODVAR aTKline *(*find_tkl_spamfilter)(int type, char *match_string, unsigned short action, unsigned short target); +extern MODVAR void (*sendnotice_tkl_del)(char *removed_by, aTKline *tkl); +extern MODVAR void (*sendnotice_tkl_add)(aTKline *tkl); +extern MODVAR void (*free_tkl)(aTKline *tkl); extern MODVAR aTKline *(*tkl_del_line)(aTKline *tkl); extern MODVAR void (*tkl_check_local_remove_shun)(aTKline *tmp); extern MODVAR int (*find_tkline_match)(aClient *cptr, int skip_soft); @@ -861,3 +873,11 @@ extern int history_request(aClient *acptr, char *object, HistoryFilter *filter); extern int history_del(char *object, int max_lines, long max_time); extern int history_destroy(char *object); extern void special_delayed_unloading(void); +extern int write_int64(FILE *fd, uint64_t t); +extern int write_int32(FILE *fd, uint32_t t); +extern int read_int64(FILE *fd, uint64_t *t); +extern int read_int32(FILE *fd, uint32_t *t); +extern int read_data(FILE *fd, void *buf, size_t len); +extern int write_data(FILE *fd, const void *buf, size_t len); +extern int write_str(FILE *fd, char *x); +extern int read_str(FILE *fd, char **x); diff --git a/include/modules.h b/include/modules.h index e9403c08c..ef65def85 100644 --- a/include/modules.h +++ b/include/modules.h @@ -1170,7 +1170,7 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum #define EFUNC_REGISTER_USER 7 #define EFUNC_TKL_HASH 8 #define EFUNC_TKL_TYPETOCHAR 9 -#define EFUNC_TKL_ADD_LINE 10 +#define EFUNC_TKL_ADD_SERVERBAN 10 #define EFUNC_TKL_DEL_LINE 11 #define EFUNC_TKL_CHECK_LOCAL_REMOVE_SHUN 12 #define EFUNC_TKL_EXPIRE 13 @@ -1227,6 +1227,14 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum #define EFUNC_BROADCAST_MD_GLOBALVAR_CMD 67 #define EFUNC_TKL_IP_HASH 68 #define EFUNC_TKL_IP_HASH_TYPE 69 +#define EFUNC_TKL_ADD_NAMEBAN 70 +#define EFUNC_TKL_ADD_SPAMFILTER 71 +#define EFUNC_SENDNOTICE_TKL_ADD 72 +#define EFUNC_SENDNOTICE_TKL_DEL 73 +#define EFUNC_FREE_TKL 74 +#define EFUNC_FIND_TKL_SERVERBAN 75 +#define EFUNC_FIND_TKL_NAMEBAN 76 +#define EFUNC_FIND_TKL_SPAMFILTER 77 /* Module flags */ #define MODFLAG_NONE 0x0000 diff --git a/include/struct.h b/include/struct.h index d99b1e6fc..062969192 100644 --- a/include/struct.h +++ b/include/struct.h @@ -67,8 +67,9 @@ typedef struct aloopStruct LoopStruct; typedef struct ConfItem aConfItem; typedef struct t_kline aTKline; typedef struct _spamfilter Spamfilter; +typedef struct _serverban ServerBan; +typedef struct _nameban NameBan; typedef struct _spamexcept SpamExcept; -/* New Config Stuff */ typedef struct _conditionalconfig ConditionalConfig; typedef struct _configentry ConfigEntry; typedef struct _configfile ConfigFile; @@ -711,11 +712,17 @@ struct Server { #define TKL_ZAP 0x0002 #define TKL_GLOBAL 0x0004 #define TKL_SHUN 0x0008 -#define TKL_QUIET 0x0010 #define TKL_SPAMF 0x0020 -#define TKL_NICK 0x0040 +#define TKL_NAME 0x0040 #define TKL_EXCEPT 0x0080 +#define TKLIsServerBan(tkl) ((tkl)->type & (TKL_KILL|TKL_ZAP|TKL_SHUN)) +#define TKLIsServerBanType(tpe) ((tpe) & (TKL_KILL|TKL_ZAP|TKL_SHUN)) +#define TKLIsSpamfilter(tkl) ((tkl)->type & TKL_SPAMF) +#define TKLIsSpamfilterType(tpe) ((tpe) & TKL_SPAMF) +#define TKLIsNameBan(tkl) ((tkl)->type & TKL_NAME) +#define TKLIsNameBanType(tpe) ((tpe) & TKL_NAME) + #define SPAMF_CHANMSG 0x0001 /* c */ #define SPAMF_USERMSG 0x0002 /* p */ #define SPAMF_USERNOTICE 0x0004 /* n */ @@ -730,14 +737,30 @@ struct Server { /* Other flags only for function calls: */ #define SPAMFLAG_NOWARN 0x0001 -/** Spamfilter sub-struct of TKL entry */ +/** Spamfilter sub-struct of TKL entry (Spamfilter) */ struct _spamfilter { + unsigned short target; unsigned short action; /**< Ban action, see BAN_ACT* */ - aMatch *expr; /**< Spamfilter matcher */ + aMatch *match; /**< Spamfilter matcher */ char *tkl_reason; /**< Reason to use for bans placed by this spamfilter, escaped by unreal_encodespace(). */ time_t tkl_duration; /**< Duration of bans placed by this spamfilter */ }; +/** Server ban sub-struct of TKL entry (KLINE/GLINE/ZLINE/GZLINE/SHUN) */ +struct _serverban { + char *usermask; /**< User mask */ + char *hostmask; /**< Host mask */ + unsigned short subtype; /**< See TKL_SUBTYPE_* */ + char *reason; /**< Reason */ +}; + +/* Name ban sub-struct of TKL entry (QLINE) */ +struct _nameban { + char hold; /**< nickname hold is used by services */ + char *name; /**< the nick or channel that is banned */ + char *reason; /**< Reason */ +}; + #define TKL_SUBTYPE_NONE 0x0000 #define TKL_SUBTYPE_SOFT 0x0001 /* (require SASL) */ @@ -747,17 +770,15 @@ struct _spamfilter { struct t_kline { aTKline *prev, *next; unsigned int type; /**< TKL type. One of TKL_*, such as TKL_KILL|TKL_GLOBAL for gline */ - unsigned short subtype; /* TKL subtype. For spamfilter one of SPAMF_*, otherwise TKL_SUBTYPE_* */ unsigned short flags; /**< One of TKL_FLAG_*, such as TKL_FLAG_CONFIG */ - union { - Spamfilter *spamf; - } ptr; - char usermask[USERLEN + 3]; /**< User mask [different for spamfilter] */ - char *hostmask; /**< Host mask [different for spamfilter] */ - char *reason; /**< Reason [different for spamfilter] */ - char *setby; /**< By who was this entry added */ - time_t expire_at; /**< When this entry will expire */ + char *set_by; /**< By who was this entry added */ time_t set_at; /**< When this entry was added */ + time_t expire_at; /**< When this entry will expire */ + union { + Spamfilter *spamfilter; + ServerBan *serverban; + NameBan *nameban; + } ptr; }; /** A spamfilter except entry */ diff --git a/src/conf.c b/src/conf.c index 81241c627..eb867bb67 100644 --- a/src/conf.c +++ b/src/conf.c @@ -174,9 +174,9 @@ static NameValue _LogFlags[] = { static NameValue ExceptTklFlags[] = { { 0, "all" }, { TKL_GLOBAL|TKL_KILL, "gline" }, - { TKL_GLOBAL|TKL_NICK, "gqline" }, + { TKL_GLOBAL|TKL_NAME, "gqline" }, { TKL_GLOBAL|TKL_ZAP, "gzline" }, - { TKL_NICK, "qline" }, + { TKL_NAME, "qline" }, { TKL_GLOBAL|TKL_SHUN, "shun" } }; @@ -1644,14 +1644,14 @@ void postconf_defaults(void) // which is more meaningful. for (tk = tklines[tkl_hash('q')]; tk; tk = tk->next) { - if (tk->type != TKL_NICK) + if (tk->type != TKL_NAME) continue; - if (!tk->setby) + if (!tk->set_by) { if (me.name[0] != '\0') - tk->setby = strdup(me.name); + tk->set_by = strdup(me.name); else - tk->setby = strdup(conf_me->name ? conf_me->name : "~server~"); + tk->set_by = strdup(conf_me->name ? conf_me->name : "~server~"); } } @@ -1659,19 +1659,19 @@ void postconf_defaults(void) { if (tk->type != TKL_SPAMF) continue; /* global entry or something else.. */ - if (!strcmp(tk->ptr.spamf->tkl_reason, "")) + if (!strcmp(tk->ptr.spamfilter->tkl_reason, "")) { - MyFree(tk->ptr.spamf->tkl_reason); - tk->ptr.spamf->tkl_reason = strdup(encoded); - tk->ptr.spamf->tkl_duration = SPAMFILTER_BAN_TIME; + MyFree(tk->ptr.spamfilter->tkl_reason); + tk->ptr.spamfilter->tkl_reason = strdup(encoded); + tk->ptr.spamfilter->tkl_duration = SPAMFILTER_BAN_TIME; } /* This one is even more ugly, but our config crap is VERY confusing :[ */ - if (!tk->setby) + if (!tk->set_by) { if (me.name[0] != '\0') - tk->setby = strdup(me.name); + tk->set_by = strdup(me.name); else - tk->setby = strdup(conf_me->name ? conf_me->name : "~server~"); + tk->set_by = strdup(conf_me->name ? conf_me->name : "~server~"); } } @@ -6882,7 +6882,7 @@ int _conf_require(ConfigFile *conf, ConfigEntry *ce) if (!reason) reason = strdup("-"); - tkl_add_line(TKL_KILL, usermask, hostmask, reason, "-config-", 0, TStime(), 0, NULL, 0, 1, TKL_FLAG_CONFIG); + tkl_add_serverban(TKL_KILL, usermask, hostmask, reason, "-config-", 0, TStime(), 1, TKL_FLAG_CONFIG); safefree(usermask); safefree(hostmask); safefree(reason); diff --git a/src/misc.c b/src/misc.c index c0f531433..8c056941d 100644 --- a/src/misc.c +++ b/src/misc.c @@ -1418,3 +1418,162 @@ time_t server_time_to_unix_time(const char *tbuf) ret = my_timegm(&tm); return ret; } + +/** Write a 64 bit integer. + * @param fd File descriptor + * @param t The value to write + * @example write_int64(fd, 1234); + * @returns 1 on success, 0 on failure. + */ +int write_int64(FILE *fd, uint64_t t) +{ + if (fwrite(&t, 1, sizeof(t), fd) < sizeof(t)) + return 0; + return 1; +} + +/** Write a 32 bit integer. + * @param fd File descriptor + * @param t The value to write + * @example write_int32(fd, 1234); + * @returns 1 on success, 0 on failure. + */ +int write_int32(FILE *fd, uint32_t t) +{ + if (fwrite(&t, 1, sizeof(t), fd) < sizeof(t)) + return 0; + return 1; +} + +/** Read a 64 bit integer. + * @param fd File descriptor + * @param t The value to write + * @example read_int64(fd, &var); + * @returns 1 on success, 0 on failure. + */ +int read_int64(FILE *fd, uint64_t *t) +{ + if (fread(t, 1, sizeof(uint64_t), fd) < sizeof(uint64_t)) + return 0; + return 1; +} + +/** Read a 64 bit integer. + * @param fd File descriptor + * @param t The value to write + * @example read_int64(fd, &var); + * @returns 1 on success, 0 on failure. + */ +int read_int32(FILE *fd, uint32_t *t) +{ + if (fread(t, 1, sizeof(uint32_t), fd) < sizeof(uint32_t)) + return 0; + return 1; +} + +/** Read binary data. + * @param fd File descriptor + * @param buf Pointer to buffer + * @param len Size of buffer + * @example read_data(fd, buf, sizeof(buf)); + * @notes This function is not used much, in most cases + * you should use read_str(), read_int32() or + * read_int64() instead. + * @returns 1 on success, 0 on failure. + */ +int read_data(FILE *fd, void *buf, size_t len) +{ + if (fread(buf, 1, len, fd) < len) + return 0; + return 1; +} + +/** Write binary data. + * @param fd File descriptor + * @param buf Pointer to buffer + * @param len Size of buffer + * @example write_data(fd, buf, sizeof(buf)); + * @notes This function is not used much, in most cases + * you should use write_str(), write_int32() or + * write_int64() instead. + * @returns 1 on success, 0 on failure. + */ +int write_data(FILE *fd, const void *buf, size_t len) +{ + if (fwrite(buf, 1, len, fd) < len) + return 0; + return 1; +} + +/** Write a string. + * @param fd File descriptor + * @param x Pointer to string + * @example write_str(fd, "hello there!"); + * @notes This function can write a string up to 65534 + * characters, which should be plenty for usage + * in UnrealIRCd. + * Note that 'x' can safely be NULL. + * @returns 1 on success, 0 on failure. + */ +int write_str(FILE *fd, char *x) +{ + uint16_t len; + + len = x ? strlen(x) : 0xffff; + if (!write_data(fd, &len, sizeof(len))) + return 0; + if ((len > 0) && (len < 0xffff)) + { + if (!write_data(fd, x, len)) + return 0; + } + return 1; +} + +/** Read a string. + * @param fd File descriptor + * @param x Pointer to string pointer + * @example write_str(fd, &str); + * @notes This function will allocate memory for the data + * and set the string pointer to this value. + * If a NULL pointer was written via write_str() + * then read_str() may also return a NULL pointer. + * @returns 1 on success, 0 on failure. + */ +int read_str(FILE *fd, char **x) +{ + uint16_t len; + size_t size; + + *x = NULL; + + if (!read_data(fd, &len, sizeof(len))) + return 0; + + if (len == 0xffff) + { + /* Magic value meaning NULL */ + *x = NULL; + return 1; + } + + if (len == 0) + { + /* 0 means empty string */ + *x = strdup(""); + return 1; + } + + if (len > 10000) + return 0; + + size = len; + *x = MyMallocEx(size + 1); + if (!read_data(fd, *x, size)) + { + safefree(*x); + return 0; + } + (*x)[len] = 0; + return 1; +} diff --git a/src/modules.c b/src/modules.c index f1c8f347b..e29c7d117 100644 --- a/src/modules.c +++ b/src/modules.c @@ -73,8 +73,14 @@ int (*tkl_hash)(unsigned int c); char (*tkl_typetochar)(int type); int (*tkl_chartotype)(char c); char *(*tkl_type_string)(aTKline *tk); -aTKline *(*tkl_add_line)(int type, char *usermask, char *hostmask, char *reason, char *setby, - time_t expire_at, time_t set_at, time_t spamf_tkl_duration, char *spamf_tkl_reason, MatchType spamf_match_type, int soft, int flags); +aTKline *(*tkl_add_serverban)(int type, char *usermask, char *hostmask, char *reason, char *setby, + time_t expire_at, time_t set_at, int soft, int flags); +aTKline *(*tkl_add_nameban)(int type, char *name, int hold, char *reason, char *setby, + time_t expire_at, time_t set_at, int flags); +aTKline *(*tkl_add_spamfilter)(int type, unsigned short target, unsigned short action, aMatch *match, char *setby, + time_t expire_at, time_t set_at, + time_t spamf_tkl_duration, char *spamf_tkl_reason, + int flags); aTKline *(*tkl_del_line)(aTKline *tkl); void (*tkl_check_local_remove_shun)(aTKline *tmp); int (*find_tkline_match)(aClient *cptr, int skip_soft); @@ -128,6 +134,12 @@ void (*broadcast_md_globalvar)(ModDataInfo *mdi, ModData *md); void (*broadcast_md_globalvar_cmd)(aClient *except, aClient *sender, char *varname, char *value); int (*tkl_ip_hash)(char *ip); int (*tkl_ip_hash_type)(int type); +void (*sendnotice_tkl_del)(char *removed_by, aTKline *tkl); +void (*sendnotice_tkl_add)(aTKline *tkl); +void (*free_tkl)(aTKline *tkl); +aTKline *(*find_tkl_serverban)(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); static const EfunctionsList efunction_table[MAXEFUNCTIONS] = { /* 00 */ {NULL, NULL, NULL}, @@ -140,7 +152,7 @@ static const EfunctionsList efunction_table[MAXEFUNCTIONS] = { /* 07 */ {"register_user", (void *)®ister_user, NULL}, /* 08 */ {"tkl_hash", (void *)&tkl_hash, NULL}, /* 09 */ {"tkl_typetochar", (void *)&tkl_typetochar, NULL}, -/* 10 */ {"tkl_add_line", (void *)&tkl_add_line, NULL}, +/* 10 */ {"tkl_add_serverban", (void *)&tkl_add_serverban, NULL}, /* 11 */ {"tkl_del_line", (void *)&tkl_del_line, NULL}, /* 12 */ {"tkl_check_local_remove_shun", (void *)&tkl_check_local_remove_shun, NULL}, /* 13 */ {NULL, NULL, NULL}, @@ -200,7 +212,15 @@ static const EfunctionsList efunction_table[MAXEFUNCTIONS] = { /* 67 */ {"broadcast_md_globalvar_cmd", (void *)&broadcast_md_globalvar_cmd, NULL}, /* 68 */ {"tkl_ip_hash", (void *)&tkl_ip_hash, NULL}, /* 69 */ {"tkl_ip_hash_type", (void *)&tkl_ip_hash_type, NULL}, -/* 70 */ {NULL, NULL, NULL}, +/* 70 */ {"tkl_add_nameban", (void *)&tkl_add_nameban, NULL}, +/* 71 */ {"tkl_add_spamfilter", (void *)&tkl_add_spamfilter, NULL}, +/* 72 */ {"sendnotice_tkl_add", (void *)&sendnotice_tkl_add, NULL}, +/* 73 */ {"sendnotice_tkl_del", (void *)&sendnotice_tkl_del, NULL}, +/* 74 */ {"free_tkl", (void *)&free_tkl, NULL}, +/* 75 */ {"find_tkl_serverban", (void *)&find_tkl_serverban, NULL}, +/* 76 */ {"find_tkl_nameban", (void *)&find_tkl_nameban, NULL}, +/* 77 */ {"find_tkl_spamfilter", (void *)&find_tkl_spamfilter, NULL}, +/* 78 */ {NULL, NULL, NULL}, }; #ifdef UNDERSCORE diff --git a/src/modules/antirandom.c b/src/modules/antirandom.c index 74510d50d..a84ce2bf6 100644 --- a/src/modules/antirandom.c +++ b/src/modules/antirandom.c @@ -580,21 +580,6 @@ MOD_UNLOAD(antirandom) return MOD_SUCCESS; } -/* Sends a message to all (local) opers AND logs to the ircdlog (as LOG_ERROR) */ -static void multi_log(char *fmt, ...) -{ - va_list vl; - static char buf[2048]; - - va_start(vl, fmt); - vsnprintf(buf, sizeof(buf), fmt, vl); - va_end(vl); - - sendto_realops("%s", buf); - ircd_log(LOG_ERROR, "%s", buf); -} - - static void free_config(void) { safefree(cfg.ban_reason); @@ -845,21 +830,21 @@ static int internal_getscore(char *str) { score += 5 + (digits - 5); #ifdef DEBUGMODE - multi_log("score@'%s': MATCH for digits check", str); + sendto_ops_and_log("score@'%s': MATCH for digits check", str); #endif } if (vowels >= 4) { score += 4 + (vowels - 4); #ifdef DEBUGMODE - multi_log("score@'%s': MATCH for vowels check", str); + sendto_ops_and_log("score@'%s': MATCH for vowels check", str); #endif } if (consonants >= 4) { score += 4 + (consonants - 4); #ifdef DEBUGMODE - multi_log("score@'%s': MATCH for consonants check", str); + sendto_ops_and_log("score@'%s': MATCH for consonants check", str); #endif } @@ -870,7 +855,7 @@ static int internal_getscore(char *str) { score++; /* OK */ #ifdef DEBUGMODE - multi_log("score@'%s': MATCH for '%s[%s]' %c/%c/%c", str, t->two, t->rest, + sendto_ops_and_log("score@'%s': MATCH for '%s[%s]' %c/%c/%c", str, t->two, t->rest, s[0], s[1], s[2]); #endif } @@ -934,7 +919,7 @@ static int get_spam_score(aClient *sptr) ((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec)); #endif #ifdef DEBUGMODE - multi_log("got score: %d/%d/%d = %d", + sendto_ops_and_log("got score: %d/%d/%d = %d", nscore, uscore, gscore, score); #endif @@ -979,12 +964,12 @@ int antirandom_preconnect(aClient *sptr) { if (cfg.ban_action == BAN_ACT_WARN) { - multi_log("[antirandom] would have denied access to user with score %d: %s!%s@%s:%s", + sendto_ops_and_log("[antirandom] would have denied access to user with score %d: %s!%s@%s:%s", score, sptr->name, sptr->user->username, sptr->user->realhost, sptr->info); return 0; } if (cfg.show_failedconnects) - multi_log("[antirandom] denied access to user with score %d: %s!%s@%s:%s", + sendto_ops_and_log("[antirandom] denied access to user with score %d: %s!%s@%s:%s", score, sptr->name, sptr->user->username, sptr->user->realhost, sptr->info); return place_host_ban(sptr, cfg.ban_action, cfg.ban_reason, cfg.ban_time); } diff --git a/src/modules/authprompt.c b/src/modules/authprompt.c index 0fcf8eb7a..fd9d393a9 100644 --- a/src/modules/authprompt.c +++ b/src/modules/authprompt.c @@ -448,16 +448,19 @@ int authprompt_place_host_ban(aClient *sptr, int action, char *reason, long dura } /** Called upon "check for KLINE/GLINE" */ -int authprompt_find_tkline_match(aClient *sptr, aTKline *tk) +int authprompt_find_tkline_match(aClient *sptr, aTKline *tkl) { /* If it's a soft-xx action and the user is not logged in * and the user is not yet online, then we will handle this user. */ - if ((tk->subtype & TKL_SUBTYPE_SOFT) && !IsLoggedIn(sptr) && !IsPerson(sptr)) + if (TKLIsServerBan(tkl) && + (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) && + !IsLoggedIn(sptr) && + !IsPerson(sptr)) { /* Send ban reason */ - if (tk->reason) - sendnotice(sptr, "%s", tk->reason); + if (tkl->ptr.serverban->reason) + sendnotice(sptr, "%s", tkl->ptr.serverban->reason); /* And tag the user */ authprompt_tag_as_auth_required(sptr); diff --git a/src/modules/channeldb.c b/src/modules/channeldb.c index a725040c7..2879335b0 100644 --- a/src/modules/channeldb.c +++ b/src/modules/channeldb.c @@ -51,12 +51,6 @@ EVENT(write_channeldb_evt); int write_channeldb(void); int write_channel_entry(FILE *fd, const char *tmpfname, aChannel *chptr); int read_channeldb(void); -static inline int read_data(FILE *fd, void *buf, size_t len); -static inline int write_data(FILE *fd, void *buf, size_t len); -static inline int write_int64(FILE *fd, uint64_t t); -static inline int write_int32(FILE *fd, uint32_t t); -static int write_str(FILE *fd, char *x); -static int read_str(FILE *fd, char **x); static void set_channel_mode(aChannel *chptr, char *modes, char *parameters); // Globals @@ -479,74 +473,6 @@ int read_channeldb(void) #undef FreeChannelEntry #undef R_SAFE -static inline int read_data(FILE *fd, void *buf, size_t len) -{ - if (fread(buf, 1, len, fd) < len) - return 0; - return 1; -} - -static inline int write_data(FILE *fd, void *buf, size_t len) -{ - if (fwrite(buf, 1, len, fd) < len) - return 0; - return 1; -} - -static inline int write_int64(FILE *fd, uint64_t t) -{ - if (fwrite(&t, 1, sizeof(t), fd) < sizeof(t)) - return 0; - return 1; -} -static inline int write_int32(FILE *fd, uint32_t t) -{ - if (fwrite(&t, 1, sizeof(t), fd) < sizeof(t)) - return 0; - return 1; -} - -static int write_str(FILE *fd, char *x) -{ - uint16_t len = (x ? strlen(x) : 0); - if (!write_data(fd, &len, sizeof(len))) - return 0; - if (len) - { - if (!write_data(fd, x, len)) - return 0; - } - return 1; -} - -static int read_str(FILE *fd, char **x) -{ - uint16_t len; - size_t size; - - *x = NULL; - - if (!read_data(fd, &len, sizeof(len))) - return 0; - - if (!len) - { - *x = strdup(""); - return 1; - } - - size = len; - *x = MyMallocEx(size + 1); - if (!read_data(fd, *x, size)) - { - MyFree(*x); - *x = NULL; - return 0; - } - (*x)[len] = 0; - return 1; -} - static void set_channel_mode(aChannel *chptr, char *modes, char *parameters) { char buf[512]; diff --git a/src/modules/join.c b/src/modules/join.c index d13c4c37d..4b015d791 100644 --- a/src/modules/join.c +++ b/src/modules/join.c @@ -530,7 +530,7 @@ int _do_join(aClient *cptr, aClient *sptr, int parc, char *parv[]) } if (ValidatePermissionsForPath("immune:server-ban:deny-channel",sptr,NULL,NULL,NULL) && (tklban = find_qline(sptr, name, &ishold))) { - sendnumeric(sptr, ERR_FORBIDDENCHANNEL, name, tklban->reason); + sendnumeric(sptr, ERR_FORBIDDENCHANNEL, name, tklban->ptr.nameban->reason); continue; } /* ugly set::spamfilter::virus-help-channel-deny hack.. */ diff --git a/src/modules/nick.c b/src/modules/nick.c index 679789c3c..a264e4b61 100644 --- a/src/modules/nick.c +++ b/src/modules/nick.c @@ -743,13 +743,13 @@ CMD_FUNC(m_nick) { if (ishold) { - sendnumeric(sptr, ERR_ERRONEUSNICKNAME, nick, tklban->reason); + sendnumeric(sptr, ERR_ERRONEUSNICKNAME, nick, tklban->ptr.nameban->reason); return 0; } if (!ValidatePermissionsForPath("immune:server-ban:ban-nick",sptr,NULL,NULL,nick)) { sptr->local->since += 4; /* lag them up */ - sendnumeric(sptr, ERR_ERRONEUSNICKNAME, nick, tklban->reason); + sendnumeric(sptr, ERR_ERRONEUSNICKNAME, nick, tklban->ptr.nameban->reason); sendto_snomask(SNO_QLINE, "Forbidding Q-lined nick %s from %s.", nick, get_client_name(cptr, FALSE)); return 0; /* NICK message ignored */ diff --git a/src/modules/pass.c b/src/modules/pass.c index b372044ab..d95a14281 100644 --- a/src/modules/pass.c +++ b/src/modules/pass.c @@ -73,7 +73,7 @@ int _check_banned(aClient *cptr, int exitflags) if ((tk = find_tkline_match_zap(cptr))) { - return banned_client(cptr, "Z-Lined", tk->reason, (tk->type & TKL_GLOBAL)?1:0, exitflags); + return banned_client(cptr, "Z-Lined", tk->ptr.serverban->reason, (tk->type & TKL_GLOBAL)?1:0, exitflags); } else { diff --git a/src/modules/rmtkl.c b/src/modules/rmtkl.c index 739b9ad30..0c16c948f 100644 --- a/src/modules/rmtkl.c +++ b/src/modules/rmtkl.c @@ -49,7 +49,7 @@ TKLType tkl_types[] = { { TKL_KILL | TKL_GLOBAL, 'G', "G-Line", "server-ban:gline:remove" }, { TKL_ZAP | TKL_GLOBAL, 'Z', "Global Z-Line", "server-ban:zline:global:remove" }, { TKL_SHUN | TKL_GLOBAL, 's', "Shun", "server-ban:shun:remove" }, - { TKL_SPAMF | TKL_GLOBAL, 'F', "Global Spamfilter", "server-ban:spamfilter:remove" }, +// { TKL_SPAMF | TKL_GLOBAL, 'F', "Global Spamfilter", "server-ban:spamfilter:remove" }, TODO: re-add spamfilter support { 0, 0, "Unknown *-Line", 0 }, }; @@ -71,8 +71,8 @@ static char *rmtkl_help[] = { " [remove \037all\037 supported TKLs except spamfilters]", " - \002/rmtkl *@*.mx GZ\002 * -skipperm", " [remove all Mexican G/Z-Lines while skipping over permanent ones]", - " - \002/rmtkl * * *Zombie*\002", - " [remove all non-spamfilter bans having \037Zombie\037 in the reason field]", +/* " - \002/rmtkl * * *Zombie*\002", + " [remove all non-spamfilter bans having \037Zombie\037 in the reason field]", TODO: re-add spamfilter support */ "*** \002End of help\002 ***", NULL }; @@ -143,66 +143,37 @@ int rmtkl_tryremove(aClient *sptr, aClient *cptr, TKLType *tkltype, aTKline *tkl return 0; // Let's not touch Q-Lines - if (tkl->type & TKL_NICK) + if (tkl->type & TKL_NAME) return 0; - if (tkl->type & TKL_SPAMF) - { - // Skip default spamfilters and ones added through configs, 'f' is the proper flag for that - if (tkltype->flag == 'f') - return 0; + /* Don't touch TKL's that were added through config */ + if (tkl->flags & TKL_FLAG_CONFIG) + return 0; + if (TKLIsSpamfilter(tkl)) + { +#if 0 +//FIXME: re-add spamfilter support // Is a spamfilter added through IRC, we can remove this if the "user" mask matches the reason if (!match_simple(uhmask, tkl->reason)) return 0; +#endif } else + if (TKLIsServerBan(tkl)) { - if (!match_simple(uhmask, make_user_host(tkl->usermask, tkl->hostmask))) + if (!match_simple(uhmask, make_user_host(tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask))) return 0; - if (commentmask && !match_simple(commentmask, tkl->reason)) + if (commentmask && !match_simple(commentmask, tkl->ptr.serverban->reason)) return 0; - } + } else + return 0; if (skipperm && tkl->expire_at == 0) return 0; - // FIXME: use sendnotice_tkl_del() once it exists !! - - // Convert "set at" timestamp to human readable time, we'll try to remove the TKL even if this fails (might be a bogus entry in that case) - if (tkl->set_at) - { - if ((timeret = short_date(tkl->set_at, NULL))) - strlcpy(gmt, timeret, sizeof(gmt)); - else - strlcpy(gmt, "", sizeof(gmt)); - } - - // Spamfilters have a slightly different format - if (tkl->type & TKL_SPAMF) - { - if (!silent) - { - sendto_snomask(SNO_TKL, "%s removed %s [%s] %s (set at %s " "- reason: %s)", - sptr->name, tkltype->txt, banact_valtostring(tkl->ptr.spamf->action), - tkl->reason, gmt, tkl->ptr.spamf->tkl_reason); - } - - ircd_log(LOG_TKL, "%s removed %s [%s] %s (set at %s " "- reason: %s)", - sptr->name, tkltype->txt, banact_valtostring(tkl->ptr.spamf->action), - tkl->reason, gmt, tkl->ptr.spamf->tkl_reason); - } else - { - if (!silent) - { - sendto_snomask(SNO_TKL, "%s removed %s %s@%s (set at %s - reason: %s)", - sptr->name, tkltype->txt, tkl->usermask, tkl->hostmask, - gmt, tkl->reason); - } - ircd_log(LOG_TKL, "%s removed %s %s@%s (set at %s - reason: %s)", - sptr->name, tkltype->txt, tkl->usermask, tkl->hostmask, - gmt, tkl->reason); - } + if (!silent) + sendnotice_tkl_del(sptr->name, tkl); RunHook5(HOOKTYPE_TKL_DEL, cptr, sptr, tkl, NULL, NULL); diff --git a/src/modules/stats.c b/src/modules/stats.c index 33d0e640b..a480299d9 100644 --- a/src/modules/stats.c +++ b/src/modules/stats.c @@ -587,7 +587,7 @@ int stats_port(aClient *sptr, char *para) int stats_bannick(aClient *sptr, char *para) { - tkl_stats(sptr, TKL_NICK, para); + tkl_stats(sptr, TKL_NAME, para); return 0; } @@ -1000,7 +1000,7 @@ int stats_banrealname(aClient *sptr, char *para) int stats_sqline(aClient *sptr, char *para) { - tkl_stats(sptr, TKL_NICK|TKL_GLOBAL, para); + tkl_stats(sptr, TKL_NAME|TKL_GLOBAL, para); return 0; } diff --git a/src/modules/tkl.c b/src/modules/tkl.c index 3fecd91a3..de6b6df33 100644 --- a/src/modules/tkl.c +++ b/src/modules/tkl.c @@ -1,45 +1,27 @@ /* - * Unreal Internet Relay Chat Daemon, src/modules/m_tkl.c - * TKL Commands and TKL Layer - * (C) 1999-2019 The UnrealIRCd Team + * Unreal Internet Relay Chat Daemon, src/modules/m_tkl.c + * TKL Commands: server bans, spamfilters, etc. + * (C) 1999-2019 Bram Matthys and The UnrealIRCd Team * - * See file AUTHORS in IRC package for additional names of - * the programmers. + * See file AUTHORS in IRC package for additional names of + * the programmers. * - * 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 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. + * 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. + * 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" -/* This is all for getrusage and friends.. taken from src/s_debug.c so should be safe. */ -#ifdef HPUX -# include -# define getrusage(a,b) syscall(SYS_GETRUSAGE, a, b) -#endif -#ifdef GETRUSAGE_2 -# ifdef _SOLARIS -# include -# ifdef RUSAGEH -# include -# endif -# endif -# include -#else -# ifdef TIMES_2 -# include -# endif -#endif /* Forward declarations */ int tkl_config_test_spamfilter(ConfigFile *, ConfigEntry *, int, int *); @@ -58,8 +40,17 @@ int _tkl_hash(unsigned int c); char _tkl_typetochar(int type); int _tkl_chartotype(char c); char *_tkl_type_string(aTKline *tk); -aTKline *_tkl_add_line(int type, char *usermask, char *hostmask, char *reason, char *setby, - time_t expire_at, time_t set_at, time_t spamf_tkl_duration, char *spamf_tkl_reason, MatchType spamf_match_type, int soft, int flags); +aTKline *_tkl_add_serverban(int type, char *usermask, char *hostmask, char *reason, char *set_by, + time_t expire_at, time_t set_at, int soft, int flags); +aTKline *_tkl_add_nameban(int type, char *name, int hold, char *reason, char *set_by, + time_t expire_at, time_t set_at, int flags); +aTKline *_tkl_add_spamfilter(int type, unsigned short target, unsigned short action, aMatch *match, char *set_by, + time_t expire_at, time_t set_at, + time_t spamf_tkl_duration, char *spamf_tkl_reason, + int flags); +void _sendnotice_tkl_del(char *removed_by, aTKline *tkl); +void _sendnotice_tkl_add(aTKline *tkl); +void _free_tkl(aTKline *tkl); void _tkl_del_line(aTKline *tkl); static void _tkl_check_local_remove_shun(aTKline *tmp); void tkl_expire_entry(aTKline * tmp); @@ -79,6 +70,9 @@ void _spamfilter_build_user_string(char *buf, char *nick, aClient *acptr); int _match_user(char *rmask, aClient *acptr, int options); int _tkl_ip_hash(char *ip); int _tkl_ip_hash_type(int type); +aTKline *_find_tkl_serverban(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); /* Externals (only for us :D) */ extern int MODVAR spamf_ugly_vchanoverride; @@ -101,14 +95,20 @@ MOD_TEST(tkl) EfunctionAdd(modinfo->handle, EFUNC_TKL_TYPETOCHAR, TO_INTFUNC(_tkl_typetochar)); EfunctionAdd(modinfo->handle, EFUNC_TKL_CHARTOTYPE, TO_INTFUNC(_tkl_chartotype)); EfunctionAddPChar(modinfo->handle, EFUNC_TKL_TYPE_STRING, _tkl_type_string); - EfunctionAddPVoid(modinfo->handle, EFUNC_TKL_ADD_LINE, TO_PVOIDFUNC(_tkl_add_line)); + EfunctionAddPVoid(modinfo->handle, EFUNC_TKL_ADD_SERVERBAN, TO_PVOIDFUNC(_tkl_add_serverban)); + EfunctionAddPVoid(modinfo->handle, EFUNC_TKL_ADD_NAMEBAN, TO_PVOIDFUNC(_tkl_add_nameban)); + EfunctionAddPVoid(modinfo->handle, EFUNC_TKL_ADD_SPAMFILTER, TO_PVOIDFUNC(_tkl_add_spamfilter)); EfunctionAddVoid(modinfo->handle, EFUNC_TKL_DEL_LINE, _tkl_del_line); + EfunctionAddVoid(modinfo->handle, EFUNC_FREE_TKL, _free_tkl); EfunctionAddVoid(modinfo->handle, EFUNC_TKL_CHECK_LOCAL_REMOVE_SHUN, _tkl_check_local_remove_shun); EfunctionAdd(modinfo->handle, EFUNC_FIND_TKLINE_MATCH, _find_tkline_match); EfunctionAdd(modinfo->handle, EFUNC_FIND_SHUN, _find_shun); EfunctionAdd(modinfo->handle, EFUNC_FIND_SPAMFILTER_USER, _find_spamfilter_user); EfunctionAddPVoid(modinfo->handle, EFUNC_FIND_QLINE, TO_PVOIDFUNC(_find_qline)); EfunctionAddPVoid(modinfo->handle, EFUNC_FIND_TKLINE_MATCH_ZAP, TO_PVOIDFUNC(_find_tkline_match_zap)); + EfunctionAddPVoid(modinfo->handle, EFUNC_FIND_TKL_SERVERBAN, TO_PVOIDFUNC(_find_tkl_serverban)); + EfunctionAddPVoid(modinfo->handle, EFUNC_FIND_TKL_NAMEBAN, TO_PVOIDFUNC(_find_tkl_nameban)); + EfunctionAddPVoid(modinfo->handle, EFUNC_FIND_TKL_SPAMFILTER, TO_PVOIDFUNC(_find_tkl_spamfilter)); EfunctionAddVoid(modinfo->handle, EFUNC_TKL_STATS, _tkl_stats); EfunctionAddVoid(modinfo->handle, EFUNC_TKL_SYNCH, _tkl_synch); EfunctionAdd(modinfo->handle, EFUNC_M_TKL, _m_tkl); @@ -119,6 +119,8 @@ MOD_TEST(tkl) EfunctionAdd(modinfo->handle, EFUNC_MATCH_USER, _match_user); EfunctionAdd(modinfo->handle, EFUNC_TKL_IP_HASH, _tkl_ip_hash); 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); return MOD_SUCCESS; } @@ -372,22 +374,21 @@ int tkl_config_run_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type) ConfigEntry *cep; ConfigEntry *cepp; aTKline *tk; - char *word = NULL, *reason = NULL, *bantime = NULL; + char *word = NULL, *reason = NULL; + time_t bantime = (SPAMFILTER_BAN_TIME ? SPAMFILTER_BAN_TIME : 86400); + char *banreason = ""; int action = 0, target = 0; - char has_reason = 0, has_bantime = 0; int match_type = 0; + aMatch *m; /* We are only interested in spamfilter { } blocks */ if ((type != CONFIG_MAIN) || strcmp(ce->ce_varname, "spamfilter")) return 0; - tk = MyMallocEx(sizeof(aTKline)); for (cep = ce->ce_entries; cep; cep = cep->ce_next) { if (!strcmp(cep->ce_varname, "match")) { - tk->reason = strdup(cep->ce_vardata); - word = cep->ce_vardata; } else if (!strcmp(cep->ce_varname, "target")) @@ -403,47 +404,32 @@ int tkl_config_run_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type) else if (!strcmp(cep->ce_varname, "action")) { action = banact_stringtoval(cep->ce_vardata); - tk->hostmask = strdup(cep->ce_vardata); } else if (!strcmp(cep->ce_varname, "reason")) { - has_reason = 1; reason = cep->ce_vardata; } else if (!strcmp(cep->ce_varname, "ban-time")) { - has_bantime = 1; - bantime = cep->ce_vardata; + bantime = config_checkval(cep->ce_vardata, CFG_TIME); } else if (!strcmp(cep->ce_varname, "match-type")) { match_type = unreal_match_method_strtoval(cep->ce_vardata); } } - tk->type = TKL_SPAMF; - tk->flags = TKL_FLAG_CONFIG; - tk->expire_at = 0; - tk->set_at = TStime(); - strlcpy(tk->usermask, spamfilter_target_inttostring(target), sizeof(tk->usermask)); - tk->subtype = target; - - tk->setby = BadPtr(me.name) ? NULL : strdup(me.name); /* Hmm! */ - tk->ptr.spamf = MyMallocEx(sizeof(Spamfilter)); - tk->ptr.spamf->expr = unreal_create_match(match_type, word, NULL); - tk->ptr.spamf->action = action; - - if (has_reason && reason) - tk->ptr.spamf->tkl_reason = strdup(unreal_encodespace(reason)); - else - tk->ptr.spamf->tkl_reason = strdup(""); - - if (has_bantime) - tk->ptr.spamf->tkl_duration = config_checkval(bantime, CFG_TIME); - else - tk->ptr.spamf->tkl_duration = (SPAMFILTER_BAN_TIME ? SPAMFILTER_BAN_TIME : 86400); - - AddListItem(tk, tklines[tkl_hash('f')]); + m = unreal_create_match(match_type, word, NULL); + tkl_add_spamfilter(TKL_SPAMF, + target, + action, + m, + me.name, + 0, + TStime(), + bantime, + banreason, + TKL_FLAG_CONFIG); return 1; } @@ -542,7 +528,6 @@ int tkl_config_run_ban(ConfigFile *cf, ConfigEntry *ce, int configtype) { if (!strcmp(cep->ce_varname, "mask")) { - // FIXME: mask with ban nick? ehh... ;) char buf[512], *p; strlcpy(buf, cep->ce_vardata, sizeof(buf)); p = strchr(buf, '@'); @@ -568,7 +553,7 @@ int tkl_config_run_ban(ConfigFile *cf, ConfigEntry *ce, int configtype) reason = strdup("-"); if (!strcmp(ce->ce_vardata, "nick")) - tkltype = TKL_NICK; + tkltype = TKL_NAME; else if (!strcmp(ce->ce_vardata, "user")) tkltype = TKL_KILL; else if (!strcmp(ce->ce_vardata, "ip")) @@ -576,7 +561,11 @@ int tkl_config_run_ban(ConfigFile *cf, ConfigEntry *ce, int configtype) else abort(); /* impossible */ - tkl_add_line(tkltype, usermask, hostmask, reason, "-config-", 0, TStime(), 0, NULL, 0, 0, TKL_FLAG_CONFIG); + if (TKLIsNameBanType(tkltype)) + tkl_add_nameban(tkltype, hostmask, 0, reason, "-config-", 0, TStime(), TKL_FLAG_CONFIG); + else if (TKLIsServerBanType(tkltype)) + tkl_add_serverban(tkltype, usermask, hostmask, reason, "-config-", 0, TStime(), 0, TKL_FLAG_CONFIG); + safefree(usermask); safefree(hostmask); safefree(reason); @@ -912,7 +901,7 @@ int m_tkl_line(aClient *cptr, aClient *sptr, int parc, char *parv[], char* type) NULL, /*2 G */ NULL, /*3 user */ NULL, /*4 host */ - NULL, /*5 setby */ + NULL, /*5 set_by */ "0", /*6 expire_at */ NULL, /*7 set_at */ "no reason", /*8 reason */ @@ -1130,7 +1119,7 @@ int spamfilter_del_by_id(aClient *sptr, char *id) "F", /* 2 F */ NULL, /* 3 usermask (targets) */ NULL, /* 4 hostmask (action) */ - NULL, /* 5 setby */ + NULL, /* 5 set_by */ "0", /* 6 expire_at */ "0", /* 7 set_at */ "", /* 8 tkl time */ @@ -1162,15 +1151,15 @@ int spamfilter_del_by_id(aClient *sptr, char *id) /* Spamfilter found. Now fill the tkllayer */ tkllayer[1] = "-"; - tkllayer[3] = spamfilter_target_inttostring(tk->subtype); /* target(s) */ - mo[0] = banact_valtochar(tk->ptr.spamf->action); + tkllayer[3] = spamfilter_target_inttostring(tk->ptr.spamfilter->target); /* target(s) */ + mo[0] = banact_valtochar(tk->ptr.spamfilter->action); mo[1] = '\0'; tkllayer[4] = mo; /* action */ tkllayer[5] = make_nick_user_host(sptr->name, sptr->user->username, GetHost(sptr)); tkllayer[8] = "-"; tkllayer[9] = "-"; - tkllayer[10] = unreal_match_method_valtostr(tk->ptr.spamf->expr->type); /* matching type */ - tkllayer[11] = tk->reason; /* regex */ + tkllayer[10] = unreal_match_method_valtostr(tk->ptr.spamfilter->match->type); /* matching type */ + tkllayer[11] = tk->ptr.spamfilter->match->str; /* regex */ ircsnprintf(mo2, sizeof(mo2), "%lld", (long long)TStime()); tkllayer[7] = mo2; /* deletion time */ @@ -1195,7 +1184,7 @@ CMD_FUNC(m_spamfilter) "F", /* 2 F */ NULL, /* 3 usermask (targets) */ NULL, /* 4 hostmask (action) */ - NULL, /* 5 setby */ + NULL, /* 5 set_by */ "0", /* 6 expire_at */ "0", /* 7 set_at */ "", /* 8 tkl time */ @@ -1334,7 +1323,7 @@ CMD_FUNC(m_spamfilter) /* SPAMFILTER LENGTH CHECK. * We try to limit it here so '/stats f' output shows ok, output of that is: - * :servername 229 destname F : + * :servername 229 destname F : * : ^NICKLEN ^ NICKLEN ^check ^check ^check * And for the other fields (and spacing/etc) we count on max 40 characters. * We also do >500 instead of >510, since that looks cleaner ;).. so actually we count @@ -1397,7 +1386,7 @@ char _tkl_typetochar(int type) return 's'; if (type & TKL_SPAMF) return 'F'; - if (type & TKL_NICK) + if (type & TKL_NAME) return 'Q'; if (type & TKL_EXCEPT) return 'E'; @@ -1408,7 +1397,7 @@ char _tkl_typetochar(int type) return 'z'; if (type & TKL_SPAMF) return 'f'; - if (type & TKL_NICK) + if (type & TKL_NAME) return 'q'; if (type & TKL_EXCEPT) return 'e'; @@ -1434,7 +1423,7 @@ int _tkl_chartotype(char c) case 'F': return TKL_SPAMF|TKL_GLOBAL; case 'Q': - return TKL_NICK|TKL_GLOBAL; + return TKL_NAME|TKL_GLOBAL; case 'k': return TKL_KILL; case 'z': @@ -1442,7 +1431,7 @@ int _tkl_chartotype(char c) case 'f': return TKL_SPAMF; case 'q': - return TKL_NICK; + return TKL_NAME; case 'E': return TKL_EXCEPT|TKL_GLOBAL; case 'e': @@ -1531,95 +1520,104 @@ aTKline *tkl_find_head(char type, char *hostmask, aTKline *def) return def; } -/** Add a TKL entry. +/** Add a spamfilter entry to the list. + * @param type TKL_SPAMF or TKL_SPAMF|TKL_GLOBAL. + * @param target The spamfilter target (SPAMF_*) + * @param action The spamfilter action (BAN_ACT_*) + * @param match The match (this struct may contain a regex for example) + * @param set_by Who (or what) set the ban + * @param expire_at When will the ban expire (0 for permanent) + * @param set_at When was the ban set + * @param spamf_tkl_duration When will the ban placed by spamfilter expire + * @param spamf_tkl_reason What is the reason for bans placed by spamfilter + * @param flags Any TKL_FLAG_* (TKL_FLAG_CONFIG, etc..) + * @returns The TKL entry, or NULL in case of a problem, + * such as a regex failing to compile, memory problem, .. + */ +aTKline *_tkl_add_spamfilter(int type, unsigned short target, unsigned short action, aMatch *match, char *set_by, + time_t expire_at, time_t set_at, + time_t tkl_duration, char *tkl_reason, + int flags) +{ + aTKline *tkl; + int index; + + if (!(type & TKL_SPAMF)) + abort(); + + tkl = MyMallocEx(sizeof(aTKline)); + /* First the common fields */ + tkl->type = type; + tkl->flags = flags; + tkl->set_at = set_at; + tkl->set_by = strdup(set_by); + tkl->expire_at = expire_at; + /* Then the spamfilter fields */ + tkl->ptr.spamfilter = MyMallocEx(sizeof(Spamfilter)); + tkl->ptr.spamfilter->target = target; + tkl->ptr.spamfilter->action = action; + tkl->ptr.spamfilter->match = match; + tkl->ptr.spamfilter->tkl_reason = strdup(tkl_reason); + tkl->ptr.spamfilter->tkl_duration = tkl_duration; + + if (tkl->ptr.spamfilter->target & SPAMF_USER) + loop.do_bancheck_spamf_user = 1; + if (tkl->ptr.spamfilter->target & SPAMF_AWAY) + loop.do_bancheck_spamf_away = 1; + + /* Spamfilters go via the normal TKL list... */ + index = tkl_hash(tkl_typetochar(type)); + AddListItem(tkl, tklines[index]); + + return tkl; +} + +/** Add a server ban TKL entry. * @param type The TKL type, one of TKL_*, * optionally OR'ed with TKL_GLOBAL. * @param usermask The user mask * @param hostmask The host mask * @param reason The reason for the ban - * @param setby Who (or what) set the ban + * @param set_by Who (or what) set the ban * @param expire_at When will the ban expire (0 for permanent) * @param set_at When was the ban set - * @param spamf_tkl_duration When will the ban placed by spamfilter expire [*] - * @param spamf_tkl_reason What is the reason for bans placed by spamfilter [*] - * @param spamf_match_type The spamfilter match type (MATCH_SIMPLE, etc..) [*] * @param soft Whether it's a soft-ban * @param flags Any TKL_FLAG_* (TKL_FLAG_CONFIG, etc..) * @returns The TKL entry, or NULL in case of a problem, * such as a regex failing to compile, memory problem, .. * @notes - * For non-spamfilter entries the items marked with [*] are NULL or 0. - * For spamfilter entries, certain fields have a different meaning, - * such as 'reason' being the spamfilter match string (regex). + * Be sure not to call this function for spamfilters, + * qlines or exempts, which have their own function! */ -aTKline *_tkl_add_line(int type, char *usermask, char *hostmask, char *reason, char *setby, - time_t expire_at, time_t set_at, - time_t spamf_tkl_duration, char *spamf_tkl_reason, MatchType spamf_match_type, - int soft, int flags) +aTKline *_tkl_add_serverban(int type, char *usermask, char *hostmask, char *reason, char *set_by, + time_t expire_at, time_t set_at, int soft, int flags) { aTKline *tkl; int index, index2; - aMatch *m = NULL; - /* Pre-allocate etc check for spamfilters that fail to compile. - * This could happen if for example the regex engine supports - * a feature on server X, but not * on server Y. - */ - if (type & TKL_SPAMF) - { - char *err = NULL; - m = unreal_create_match(spamf_match_type, reason, &err); - if (!m) - { - sendto_realops("[TKL ERROR] ERROR: Spamfilter was added but did not compile. ERROR='%s', Spamfilter='%s'", - err, reason); - return NULL; - } - } + if (!TKLIsServerBanType(type)) + abort(); tkl = MyMallocEx(sizeof(aTKline)); - + /* First the common fields */ tkl->type = type; tkl->flags = flags; - tkl->expire_at = expire_at; tkl->set_at = set_at; - strlcpy(tkl->usermask, usermask, sizeof(tkl->usermask)); - tkl->hostmask = strdup(hostmask); - tkl->reason = strdup(reason); - tkl->setby = strdup(setby); - - if (type & TKL_SPAMF) - { - /* Need to set some additional flags like 'targets' and 'action'.. */ - tkl->subtype = spamfilter_gettargets(usermask, NULL); - tkl->ptr.spamf = MyMallocEx(sizeof(Spamfilter)); - tkl->ptr.spamf->expr = m; - tkl->ptr.spamf->action = banact_chartoval(*hostmask); - tkl->expire_at = 0; /* temporary spamfilters are NOT supported! (makes no sense) */ - if (!spamf_tkl_reason) - { - /* no exttkl support, use default values... */ - tkl->ptr.spamf->tkl_duration = SPAMFILTER_BAN_TIME; - tkl->ptr.spamf->tkl_reason = strdup(unreal_encodespace(SPAMFILTER_BAN_REASON)); - } else { - tkl->ptr.spamf->tkl_duration = spamf_tkl_duration; - tkl->ptr.spamf->tkl_reason = strdup(spamf_tkl_reason); /* already encoded */ - } - if (tkl->subtype & SPAMF_USER) - loop.do_bancheck_spamf_user = 1; - if (tkl->subtype & SPAMF_AWAY) - loop.do_bancheck_spamf_away = 1; - - } else { - if (soft) - tkl->subtype = TKL_SUBTYPE_SOFT; - } + tkl->set_by = strdup(set_by); + tkl->expire_at = expire_at; + /* Now the server ban fields */ + tkl->ptr.serverban = MyMallocEx(sizeof(ServerBan)); + tkl->ptr.serverban->usermask = strdup(usermask); + tkl->ptr.serverban->hostmask = strdup(hostmask); + if (soft) + tkl->ptr.serverban->subtype = TKL_SUBTYPE_SOFT; + tkl->ptr.serverban->reason = strdup(reason); /* For ip hash table TKL's... */ index = tkl_ip_hash_type(tkl_typetochar(type)); if (index >= 0) { - index2 = tkl_ip_hash(tkl->hostmask); + index2 = tkl_ip_hash(tkl->ptr.serverban->hostmask); if (index2 >= 0) { AddListItem(tkl, tklines_ip_hash[index][index2]); @@ -1634,7 +1632,87 @@ aTKline *_tkl_add_line(int type, char *usermask, char *hostmask, char *reason, c return tkl; } -/** Delete a TKL entry. +/** Add a name ban TKL entry (Q-Line), used for banning nicks and channels. + * @param type The TKL type, one of TKL_*, + * optionally OR'ed with TKL_GLOBAL. + * @param name The nick or channel to be banned (wildcards accepted) + * @param hold Flag to indicate services hold + * @param reason The reason for the ban + * @param set_by Who (or what) set the ban + * @param expire_at When will the ban expire (0 for permanent) + * @param set_at When was the ban set + * @param flags Any TKL_FLAG_* (TKL_FLAG_CONFIG, etc..) + * @returns The TKL entry, or NULL in case of a problem, + * such as a regex failing to compile, memory problem, .. + * @notes + * Be sure not to call this function for spamfilters, + * qlines or exempts, which have their own function! + */ +aTKline *_tkl_add_nameban(int type, char *name, int hold, char *reason, char *set_by, + time_t expire_at, time_t set_at, int flags) +{ + aTKline *tkl; + int index, index2; + + if (!TKLIsNameBanType(type)) + abort(); + + tkl = MyMallocEx(sizeof(aTKline)); + /* First the common fields */ + tkl->type = type; + tkl->flags = flags; + tkl->set_at = set_at; + tkl->set_by = strdup(set_by); + tkl->expire_at = expire_at; + /* Now the name ban fields */ + tkl->ptr.nameban = MyMallocEx(sizeof(ServerBan)); + tkl->ptr.nameban->name = strdup(name); + tkl->ptr.nameban->hold = hold; + tkl->ptr.serverban->reason = strdup(reason); + + /* Name bans go via the normal TKL list.. */ + index = tkl_hash(tkl_typetochar(type)); + AddListItem(tkl, tklines[index]); + + return tkl; +} + + +/** Free a TKL entry but do not remove from the list. + * (this assumes that it was not added yet or is already removed) + * Most people will use tkl_del_line() instead. + */ +void _free_tkl(aTKline *tkl) +{ + /* Free the entry */ + /* First, the common fields */ + safefree(tkl->set_by); + /* Now the type specific fields */ + if (TKLIsServerBan(tkl) && tkl->ptr.serverban) + { + safefree(tkl->ptr.serverban->usermask); + safefree(tkl->ptr.serverban->hostmask); + safefree(tkl->ptr.serverban->reason); + MyFree(tkl->ptr.serverban); + } else + if (TKLIsNameBan(tkl) && tkl->ptr.nameban) + { + safefree(tkl->ptr.nameban->name); + safefree(tkl->ptr.nameban->reason); + MyFree(tkl->ptr.nameban); + } else + if (TKLIsSpamfilter(tkl) && tkl->ptr.spamfilter) + { + /* Spamfilter */ + safefree(tkl->ptr.spamfilter->tkl_reason); + if (tkl->ptr.spamfilter->match) + unreal_delete_match(tkl->ptr.spamfilter->match); + MyFree(tkl->ptr.spamfilter); + } + MyFree(tkl); +} + +/** Delete a TKL entry from the list and free it. * @param tkl The TKL entry. */ void _tkl_del_line(aTKline *tkl) @@ -1642,11 +1720,13 @@ void _tkl_del_line(aTKline *tkl) int index, index2; int found = 0; - /* Try to find it in the ip TKL hash table first (if this applies) */ + /* Try to find it in the ip TKL hash table first + * (this only applies to server bans) + */ index = tkl_ip_hash_type(tkl_typetochar(tkl->type)); if (index >= 0) { - index2 = tkl_ip_hash(tkl->hostmask); + index2 = tkl_ip_hash(tkl->ptr.serverban->hostmask); if (index2 >= 0) { DelListItem(tkl, tklines_ip_hash[index][index2]); @@ -1661,18 +1741,8 @@ void _tkl_del_line(aTKline *tkl) DelListItem(tkl, tklines[index]); } - /* Free the entry */ - MyFree(tkl->hostmask); - MyFree(tkl->reason); - MyFree(tkl->setby); - if (tkl->type & TKL_SPAMF && tkl->ptr.spamf) - { - unreal_delete_match(tkl->ptr.spamf->expr); - if (tkl->ptr.spamf->tkl_reason) - MyFree(tkl->ptr.spamf->tkl_reason); - MyFree(tkl->ptr.spamf); - } - MyFree(tkl); + /* Finally, free the entry */ + free_tkl(tkl); } /* @@ -1700,15 +1770,15 @@ void _tkl_check_local_remove_shun(aTKline *tmp) cip = GetIP(acptr); - if ((*tmp->hostmask >= '0') && (*tmp->hostmask <= '9')) + if ((*tmp->ptr.serverban->hostmask >= '0') && (*tmp->ptr.serverban->hostmask <= '9')) is_ip = 1; else is_ip = 0; if (is_ip == 0 ? - (match_simple(tmp->hostmask, chost) && match_simple(tmp->usermask, cname)) : - (match_simple(tmp->hostmask, chost) || match_simple(tmp->hostmask, cip)) - && match_simple(tmp->usermask, cname)) + (match_simple(tmp->ptr.serverban->hostmask, chost) && match_simple(tmp->ptr.serverban->usermask, cname)) : + (match_simple(tmp->ptr.serverban->hostmask, chost) || match_simple(tmp->ptr.serverban->hostmask, cip)) + && match_simple(tmp->ptr.serverban->usermask, cname)) { /* before blindly marking this user as un-shunned, we need to check @@ -1719,15 +1789,15 @@ void _tkl_check_local_remove_shun(aTKline *tmp) */ keep_shun = 0; for(tk = tklines[tkl_hash('s')]; tk && !keep_shun; tk = tk->next) - if(tk != tmp && match_simple(tk->usermask, cname)) + if(tk != tmp && match_simple(tk->ptr.serverban->usermask, cname)) { - if ((*tk->hostmask >= '0') && (*tk->hostmask <= '9') + if ((*tk->ptr.serverban->hostmask >= '0') && (*tk->ptr.serverban->hostmask <= '9') /* the hostmask is an IP */ - && (match_simple(tk->hostmask, chost) || match_simple(tk->hostmask, cip))) + && (match_simple(tk->ptr.serverban->hostmask, chost) || match_simple(tk->ptr.serverban->hostmask, cip))) keep_shun = 1; else /* the hostmask is not an IP */ - if (match_simple(tk->hostmask, chost) && match_simple(tk->usermask, cname)) + if (match_simple(tk->ptr.serverban->hostmask, chost) && match_simple(tk->ptr.serverban->usermask, cname)) keep_shun = 1; } @@ -1743,42 +1813,48 @@ void _tkl_check_local_remove_shun(aTKline *tmp) /** Deal with expiration of a specific TKL entry. * This is a helper function for tkl_check_expire(). */ -void tkl_expire_entry(aTKline * tmp) +void tkl_expire_entry(aTKline *tkl) { - char *whattype = tkl_type_string(tmp); + char *whattype = tkl_type_string(tkl); - if (!tmp) + if (!tkl) return; - whattype[0] = 0; - - if (!(tmp->type & TKL_NICK)) + if (tkl->type & TKL_SPAMF) + { + /* Impossible */ + } else + if (TKLIsServerBan(tkl)) { sendto_snomask(SNO_TKL, "*** Expiring %s (%s@%s) made by %s (Reason: %s) set %lld seconds ago", - whattype, tmp->usermask, tmp->hostmask, tmp->setby, tmp->reason, - (long long)(TStime() - tmp->set_at)); + whattype, tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask, tkl->set_by, tkl->ptr.serverban->reason, + (long long)(TStime() - tkl->set_at)); ircd_log (LOG_TKL, "Expiring %s (%s@%s) made by %s (Reason: %s) set %lld seconds ago", - whattype, tmp->usermask, tmp->hostmask, tmp->setby, tmp->reason, - (long long)(TStime() - tmp->set_at)); + whattype, tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask, tkl->set_by, tkl->ptr.serverban->reason, + (long long)(TStime() - tkl->set_at)); } - else if (!(*tmp->usermask == 'H')) /* Q-Line but not a hold */ + else if (TKLIsNameBan(tkl)) { - sendto_snomask(SNO_TKL, - "*** Expiring %s (%s) made by %s (Reason: %s) set %lld seconds ago", - whattype, tmp->hostmask, tmp->setby, tmp->reason, - (long long)(TStime() - tmp->set_at)); - ircd_log - (LOG_TKL, "Expiring %s (%s) made by %s (Reason: %s) set %lld seconds ago", - whattype, tmp->hostmask, tmp->setby, tmp->reason, - (long long)(TStime() - tmp->set_at)); + if (!tkl->ptr.nameban->hold) + { + sendto_snomask(SNO_TKL, + "*** Expiring %s (%s) made by %s (Reason: %s) set %lld seconds ago", + whattype, tkl->ptr.nameban->name, tkl->set_by, tkl->ptr.nameban->reason, + (long long)(TStime() - tkl->set_at)); + ircd_log + (LOG_TKL, "Expiring %s (%s) made by %s (Reason: %s) set %lld seconds ago", + whattype, tkl->ptr.nameban->name, tkl->set_by, tkl->ptr.nameban->reason, + (long long)(TStime() - tkl->set_at)); + } } - if (tmp->type & TKL_SHUN) - tkl_check_local_remove_shun(tmp); - RunHook3(HOOKTYPE_TKL_DEL, NULL, NULL, tmp); - tkl_del_line(tmp); + if (tkl->type & TKL_SHUN) + tkl_check_local_remove_shun(tkl); + + RunHook3(HOOKTYPE_TKL_DEL, NULL, NULL, tkl); + tkl_del_line(tkl); } /** Regularly check TKL entries for expiration */ @@ -1828,19 +1904,19 @@ int find_tkline_match_matcher(aClient *cptr, int skip_soft, aTKline *tkl) int match_type = 0; Hook *hook; - if ((tkl->type & TKL_SHUN) || (tkl->type & TKL_SPAMF) || (tkl->type & TKL_NICK) || (tkl->type & TKL_EXCEPT)) + if (!TKLIsServerBan(tkl) || (tkl->type & TKL_SHUN)) return 0; - if (skip_soft && (tkl->subtype & TKL_SUBTYPE_SOFT)) + if (skip_soft && (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT)) return 0; - snprintf(uhost, sizeof(uhost), "%s@%s", tkl->usermask, tkl->hostmask); + snprintf(uhost, sizeof(uhost), "%s@%s", tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask); if (match_user(uhost, cptr, MATCH_CHECK_REAL)) { /* If hard-ban, or soft-ban&unauthenticated.. */ - if (!(tkl->subtype & TKL_SUBTYPE_SOFT) || - ((tkl->subtype & TKL_SUBTYPE_SOFT) && !IsLoggedIn(cptr))) + if (!(tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) || + ((tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) && !IsLoggedIn(cptr))) { /* Found match. Now check for exception... */ if (((tkl->type & TKL_KILL) || (tkl->type & TKL_ZAP)) && !(tkl->type & TKL_GLOBAL)) @@ -1926,14 +2002,14 @@ int _find_tkline_match(aClient *cptr, int skip_soft) { ircstp->is_ref++; if (tkl->type & TKL_GLOBAL) - return banned_client(cptr, "G-Lined", tkl->reason, 1, 0); + return banned_client(cptr, "G-Lined", tkl->ptr.serverban->reason, 1, 0); else - return banned_client(cptr, "K-Lined", tkl->reason, 0, 0); + return banned_client(cptr, "K-Lined", tkl->ptr.serverban->reason, 0, 0); } if (tkl->type & TKL_ZAP) { ircstp->is_ref++; - return banned_client(cptr, "Z-Lined", tkl->reason, (tkl->type & TKL_GLOBAL)?1:0, 0); + return banned_client(cptr, "Z-Lined", tkl->ptr.serverban->reason, (tkl->type & TKL_GLOBAL)?1:0, 0); } return 3; @@ -1942,7 +2018,7 @@ int _find_tkline_match(aClient *cptr, int skip_soft) /** Check if user is shunned. Returns 2 in such a case (FIXME: why 2 ?) */ int _find_shun(aClient *cptr) { - aTKline *lp; + aTKline *tkl; ConfigItem_except *excepts; int match_type = 0; Hook *hook; @@ -1957,20 +2033,20 @@ int _find_shun(aClient *cptr) if (ValidatePermissionsForPath("immune:server-ban:shun",cptr,NULL,NULL,NULL)) return 1; - for (lp = tklines[tkl_hash('s')]; lp; lp = lp->next) + for (tkl = tklines[tkl_hash('s')]; tkl; tkl = tkl->next) { char uhost[NICKLEN+HOSTLEN+1]; - if (!(lp->type & TKL_SHUN)) + if (!(tkl->type & TKL_SHUN)) continue; - snprintf(uhost, sizeof(uhost), "%s@%s", lp->usermask, lp->hostmask); + snprintf(uhost, sizeof(uhost), "%s@%s", tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask); if (match_user(uhost, cptr, MATCH_CHECK_REAL)) { /* If hard-ban, or soft-ban&unauthenticated.. */ - if (!(lp->subtype & TKL_SUBTYPE_SOFT) || - ((lp->subtype & TKL_SUBTYPE_SOFT) && !IsLoggedIn(cptr))) + if (!(tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) || + ((tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) && !IsLoggedIn(cptr))) { /* Found match. Now check for exception... */ banned = 1; @@ -1978,7 +2054,7 @@ int _find_shun(aClient *cptr) for (excepts = conf_except; excepts; excepts = excepts->next) { if (excepts->flag.type != match_type || (match_type == CONF_EXCEPT_TKL && - excepts->type != lp->type)) + excepts->type != tkl->type)) continue; if (match_user(excepts->mask, cptr, MATCH_CHECK_REAL)) @@ -1989,7 +2065,7 @@ int _find_shun(aClient *cptr) } for (hook = Hooks[HOOKTYPE_TKL_EXCEPT]; hook; hook = hook->next) { - if (hook->func.intfunc(cptr, lp) > 0) + if (hook->func.intfunc(cptr, tkl) > 0) { banned = 0; /* exempt by hook */ break; @@ -2056,7 +2132,7 @@ int _find_spamfilter_user(aClient *sptr, int flags) /** Check a spamfilter against all local users and print a message. * This is only used for the 'warn' action (BAN_ACT_WARN). */ -int spamfilter_check_users(aTKline *tk) +int spamfilter_check_users(aTKline *tkl) { char spamfilter_user[NICKLEN + USERLEN + HOSTLEN + REALLEN + 64]; /* n!u@h:r */ char buf[1024]; @@ -2068,19 +2144,19 @@ int spamfilter_check_users(aTKline *tk) if (MyClient(acptr)) { spamfilter_build_user_string(spamfilter_user, acptr->name, acptr); - if (!unreal_match(tk->ptr.spamf->expr, spamfilter_user)) + if (!unreal_match(tkl->ptr.spamfilter->match, spamfilter_user)) continue; /* No match */ /* matched! */ ircsnprintf(buf, sizeof(buf), "[Spamfilter] %s!%s@%s matches filter '%s': [%s: '%s'] [%s]", acptr->name, acptr->user->username, acptr->user->realhost, - tk->reason, + tkl->ptr.spamfilter->match->str, "user", spamfilter_user, - unreal_decodespace(tk->ptr.spamf->tkl_reason)); + unreal_decodespace(tkl->ptr.spamfilter->tkl_reason)); sendto_snomask_global(SNO_SPAMF, "%s", buf); ircd_log(LOG_SPAMFILTER, "%s", buf); - RunHook6(HOOKTYPE_LOCAL_SPAMFILTER, acptr, spamfilter_user, spamfilter_user, SPAMF_USER, NULL, tk); + RunHook6(HOOKTYPE_LOCAL_SPAMFILTER, acptr, spamfilter_user, spamfilter_user, SPAMF_USER, NULL, tkl); matches++; } } @@ -2091,7 +2167,7 @@ int spamfilter_check_users(aTKline *tk) /** Similarly to previous, but match against all global users. * FUNCTION IS UNUSED !! */ -int spamfilter_check_all_users(aClient *from, aTKline *tk) +int spamfilter_check_all_users(aClient *from, aTKline *tkl) { char spamfilter_user[NICKLEN + USERLEN + HOSTLEN + REALLEN + 64]; /* n!u@h:r */ int matches = 0; @@ -2102,15 +2178,15 @@ int spamfilter_check_all_users(aClient *from, aTKline *tk) if (IsPerson(acptr)) { spamfilter_build_user_string(spamfilter_user, acptr->name, acptr); - if (!unreal_match(tk->ptr.spamf->expr, spamfilter_user)) + if (!unreal_match(tkl->ptr.spamfilter->match, spamfilter_user)) continue; /* No match */ /* matched! */ sendnotice(from, "[Spamfilter] %s!%s@%s matches filter '%s': [%s: '%s'] [%s]", acptr->name, acptr->user->username, acptr->user->realhost, - tk->reason, + tkl->ptr.spamfilter->match->str, "user", spamfilter_user, - unreal_decodespace(tk->ptr.spamf->tkl_reason)); + unreal_decodespace(tkl->ptr.spamfilter->tkl_reason)); matches++; } } @@ -2118,18 +2194,18 @@ int spamfilter_check_all_users(aClient *from, aTKline *tk) return matches; } -/** Find a QLINE. +/** Check if the nick or channel name is banned (Q-Line). * @param cptr The possibly affected user. - * @param nick The nick (since we could be nick-changing, cptr->nick is not used) + * @param name The nick or channel to check. * @param is_hold This will be SET (so OUT) if it's a services hold. * * @notes Special handling: * #*ble* will match with #bbleh * *ble* will NOT match with #bbleh, will with bbleh */ -aTKline *_find_qline(aClient *cptr, char *nick, int *ishold) +aTKline *_find_qline(aClient *cptr, char *name, int *ishold) { - aTKline *lp; + aTKline *tkl; int points = 0; ConfigItem_except *excepts; *ishold = 0; @@ -2137,14 +2213,15 @@ aTKline *_find_qline(aClient *cptr, char *nick, int *ishold) if (IsServer(cptr) || IsMe(cptr)) return NULL; - for (lp = tklines[tkl_hash('q')]; lp; lp = lp->next) + for (tkl = tklines[tkl_hash('q')]; tkl; tkl = tkl->next) { points = 0; - if (!(lp->type & TKL_NICK)) + if (!TKLIsNameBan(tkl)) continue; - if (((*lp->hostmask == '#' && *nick == '#') || (*lp->hostmask != '#' && *nick != '#')) && match_simple(lp->hostmask, nick)) + if (((*tkl->ptr.nameban->name == '#' && *name == '#') || (*tkl->ptr.nameban->name != '#' && *name != '#')) + && match_simple(tkl->ptr.nameban->name, name)) { points = 1; break; @@ -2155,21 +2232,21 @@ aTKline *_find_qline(aClient *cptr, char *nick, int *ishold) return NULL; /* It's a services hold */ - if (*lp->usermask == 'H') + if (tkl->ptr.nameban->hold) { *ishold = 1; - return lp; + return tkl; } for (excepts = conf_except; excepts; excepts = excepts->next) { - if (excepts->flag.type != CONF_EXCEPT_TKL || excepts->type != TKL_NICK) + if (excepts->flag.type != CONF_EXCEPT_TKL || excepts->type != TKL_NAME) continue; if (match_user(excepts->mask, cptr, MATCH_CHECK_REAL)) return NULL; /* exempt */ } - return lp; + return tkl; } /** Helper function for find_tkline_match_zap() */ @@ -2178,7 +2255,10 @@ aTKline *find_tkline_match_zap_matcher(aClient *cptr, aTKline *tkl) ConfigItem_except *excepts; Hook *hook; - if ((tkl->type & TKL_ZAP) && match_user(tkl->hostmask, cptr, MATCH_CHECK_IP)) + if (!(tkl->type & TKL_ZAP)) + return NULL; + + if (match_user(tkl->ptr.serverban->hostmask, cptr, MATCH_CHECK_IP)) { for (excepts = conf_except; excepts; excepts = excepts->next) { @@ -2249,7 +2329,7 @@ typedef struct { int flags; char *mask; char *reason; - char *setby; + char *set_by; } TKLFlag; /** Parse STATS tkl parameters. @@ -2296,13 +2376,13 @@ static void parse_stats_params(char *para, TKLFlag *flag) flag->reason = tmp; break; case 's': - if (flag->setby || !(tmp = strtok(NULL, " "))) + if (flag->set_by || !(tmp = strtok(NULL, " "))) continue; if (what == '+') flag->flags |= BY_SETBY; else flag->flags |= NOT_BY_SETBY; - flag->setby = tmp; + flag->set_by = tmp; break; } } @@ -2311,99 +2391,106 @@ static void parse_stats_params(char *para, TKLFlag *flag) /** Does this TKL entry match the search terms? * This is a helper function for tkl_stats(). */ -void tkl_stats_matcher(aClient *cptr, int type, char *para, TKLFlag *tklflags, aTKline *tk) +void tkl_stats_matcher(aClient *cptr, int type, char *para, TKLFlag *tklflags, aTKline *tkl) { + /* This handles the selection */ if (!BadPtr(para)) { - if (tklflags->flags & BY_MASK) - { - if (tk->type & TKL_NICK) - { - if (!match_simple(tklflags->mask, tk->hostmask)) - return; - } - else if (!match_simple(tklflags->mask, make_user_host(tk->usermask, tk->hostmask))) - return; - } - if (tklflags->flags & NOT_BY_MASK) - { - if (tk->type & TKL_NICK) - { - if (match_simple(tklflags->mask, tk->hostmask)) - return; - } - else if (match_simple(tklflags->mask, make_user_host(tk->usermask, tk->hostmask))) - return; - } - if (tklflags->flags & BY_REASON) - if (!match_simple(tklflags->reason, tk->reason)) - return; - if (tklflags->flags & NOT_BY_REASON) - if (match_simple(tklflags->reason, tk->reason)) - return; if (tklflags->flags & BY_SETBY) - if (!match_simple(tklflags->setby, tk->setby)) + if (!match_simple(tklflags->set_by, tkl->set_by)) return; if (tklflags->flags & NOT_BY_SETBY) - if (match_simple(tklflags->setby, tk->setby)) + if (match_simple(tklflags->set_by, tkl->set_by)) return; + if (TKLIsServerBan(tkl)) + { + if (tklflags->flags & BY_MASK) + { + if (tkl->type & TKL_NAME) + { + if (!match_simple(tklflags->mask, tkl->ptr.nameban->name)) + return; + } + else if (!match_simple(tklflags->mask, make_user_host(tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask))) + return; + } + if (tklflags->flags & NOT_BY_MASK) + { + if (tkl->type & TKL_NAME) + { + if (match_simple(tklflags->mask, tkl->ptr.nameban->name)) + return; + } + else if (match_simple(tklflags->mask, make_user_host(tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask))) + return; + } + if (tklflags->flags & BY_REASON) + if (!match_simple(tklflags->reason, tkl->ptr.serverban->reason)) + return; + if (tklflags->flags & NOT_BY_REASON) + if (match_simple(tklflags->reason, tkl->ptr.serverban->reason)) + return; + } } - if (tk->type == (TKL_KILL | TKL_GLOBAL)) + + /* If we are still here, then we will send the STATS entry */ + + if (tkl->type == (TKL_KILL | TKL_GLOBAL)) { sendnumeric(cptr, RPL_STATSGLINE, 'G', - (tk->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", - tk->usermask, tk->hostmask, - (tk->expire_at != 0) ? (tk->expire_at - TStime()) : 0, - (TStime() - tk->set_at), tk->setby, tk->reason); + (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", + tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask, + (tkl->expire_at != 0) ? (tkl->expire_at - TStime()) : 0, + (TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); } - if (tk->type == (TKL_ZAP | TKL_GLOBAL)) + if (tkl->type == (TKL_ZAP | TKL_GLOBAL)) { sendnumeric(cptr, RPL_STATSGLINE, 'Z', - (tk->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", - tk->usermask, tk->hostmask, - (tk->expire_at != 0) ? (tk->expire_at - TStime()) : 0, - (TStime() - tk->set_at), tk->setby, tk->reason); + (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", + tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask, + (tkl->expire_at != 0) ? (tkl->expire_at - TStime()) : 0, + (TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); } - if (tk->type == (TKL_SHUN | TKL_GLOBAL)) + if (tkl->type == (TKL_SHUN | TKL_GLOBAL)) { sendnumeric(cptr, RPL_STATSGLINE, 's', - (tk->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", - tk->usermask, tk->hostmask, - (tk->expire_at != 0) ? (tk->expire_at - TStime()) : 0, - (TStime() - tk->set_at), tk->setby, tk->reason); + (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", + tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask, + (tkl->expire_at != 0) ? (tkl->expire_at - TStime()) : 0, + (TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); } - if (tk->type == (TKL_KILL)) + if (tkl->type == (TKL_KILL)) { sendnumeric(cptr, RPL_STATSGLINE, 'K', - (tk->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", - tk->usermask, tk->hostmask, - (tk->expire_at != 0) ? (tk->expire_at - TStime()) : 0, - (TStime() - tk->set_at), tk->setby, tk->reason); + (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", + tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask, + (tkl->expire_at != 0) ? (tkl->expire_at - TStime()) : 0, + (TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); } - if (tk->type == (TKL_ZAP)) + if (tkl->type == (TKL_ZAP)) { sendnumeric(cptr, RPL_STATSGLINE, 'z', - (tk->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", - tk->usermask, tk->hostmask, - (tk->expire_at != 0) ? (tk->expire_at - TStime()) : 0, - (TStime() - tk->set_at), tk->setby, tk->reason); + (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", + tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask, + (tkl->expire_at != 0) ? (tkl->expire_at - TStime()) : 0, + (TStime() - tkl->set_at), tkl->set_by, tkl->ptr.serverban->reason); } - if (tk->type & TKL_SPAMF) + if (tkl->type & TKL_SPAMF) { sendnumeric(cptr, RPL_STATSSPAMF, - (tk->type & TKL_GLOBAL) ? 'F' : 'f', - unreal_match_method_valtostr(tk->ptr.spamf->expr->type), - spamfilter_target_inttostring(tk->subtype), - banact_valtostring(tk->ptr.spamf->action), - (tk->expire_at != 0) ? (tk->expire_at - TStime()) : 0, - TStime() - tk->set_at, - tk->ptr.spamf->tkl_duration, tk->ptr.spamf->tkl_reason, - tk->setby, - tk->reason); + (tkl->type & TKL_GLOBAL) ? 'F' : 'f', + unreal_match_method_valtostr(tkl->ptr.spamfilter->match->type), + spamfilter_target_inttostring(tkl->ptr.spamfilter->target), + banact_valtostring(tkl->ptr.spamfilter->action), + (tkl->expire_at != 0) ? (tkl->expire_at - TStime()) : 0, + TStime() - tkl->set_at, + tkl->ptr.spamfilter->tkl_duration, tkl->ptr.spamfilter->tkl_reason, + tkl->set_by, + tkl->ptr.spamfilter->match->str); if (para && !strcasecmp(para, "del")) { - char *hash = spamfilter_id(tk); - if (tk->type & TKL_GLOBAL) + char *hash = spamfilter_id(tkl); + if (tkl->type & TKL_GLOBAL) { sendtxtnumeric(cptr, "To delete this spamfilter, use /SPAMFILTER del %s", hash); sendtxtnumeric(cptr, "-"); @@ -2413,11 +2500,11 @@ void tkl_stats_matcher(aClient *cptr, int type, char *para, TKLFlag *tklflags, a } } } - if (tk->type & TKL_NICK) + if (tkl->type & TKL_NAME) { - sendnumeric(cptr, RPL_STATSQLINE, (tk->type & TKL_GLOBAL) ? 'Q' : 'q', - tk->hostmask, (tk->expire_at != 0) ? (tk->expire_at - TStime()) : 0, - TStime() - tk->set_at, tk->setby, tk->reason); + sendnumeric(cptr, RPL_STATSQLINE, (tkl->type & TKL_GLOBAL) ? 'Q' : 'q', + tkl->ptr.nameban->name, (tkl->expire_at != 0) ? (tkl->expire_at - TStime()) : 0, + TStime() - tkl->set_at, tkl->set_by, tkl->ptr.nameban->reason); } } @@ -2469,7 +2556,7 @@ void _tkl_stats(aClient *cptr, int type, char *para) * @param to The remote server. * @param tkl The TKL entry. */ -void tkl_synch_send_entry(aClient *sender, aClient *to, aTKline *tkl) +void tkl_synch_send_entry(int add, aClient *sender, aClient *to, aTKline *tkl) { char typ; @@ -2478,23 +2565,40 @@ void tkl_synch_send_entry(aClient *sender, aClient *to, aTKline *tkl) typ = tkl_typetochar(tkl->type); - if (tkl->type & TKL_SPAMF) + if (TKLIsSpamfilter(tkl)) { - sendto_one(to, NULL, ":%s TKL + %c %s %s %s %lld %lld %lld %s %s :%s", sender->name, + sendto_one(to, NULL, ":%s TKL %c %c %s %s %s %lld %lld %lld %s %s :%s", sender->name, + add ? '+' : '-', typ, - tkl->usermask, tkl->hostmask, tkl->setby, + spamfilter_target_inttostring(tkl->ptr.spamfilter->target), + banact_valtostring(tkl->ptr.spamfilter->action), + tkl->set_by, (long long)tkl->expire_at, (long long)tkl->set_at, - (long long)tkl->ptr.spamf->tkl_duration, tkl->ptr.spamf->tkl_reason, - unreal_match_method_valtostr(tkl->ptr.spamf->expr->type), - tkl->reason); + (long long)tkl->ptr.spamfilter->tkl_duration, tkl->ptr.spamfilter->tkl_reason, + unreal_match_method_valtostr(tkl->ptr.spamfilter->match->type), + tkl->ptr.spamfilter->match->str); } else + if (TKLIsServerBan(tkl)) { - /* All other types (non-spamfilter)... */ - sendto_one(to, NULL, ":%s TKL + %c %s%s %s %s %lld %lld :%s", sender->name, + sendto_one(to, NULL, ":%s TKL %c %c %s%s %s %s %lld %lld :%s", sender->name, + add ? '+' : '-', typ, - (tkl->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", - *tkl->usermask ? tkl->usermask : "*", tkl->hostmask, tkl->setby, - (long long)tkl->expire_at, (long long)tkl->set_at, tkl->reason); + (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", + *tkl->ptr.serverban->usermask ? tkl->ptr.serverban->usermask : "*", + tkl->ptr.serverban->hostmask, tkl->set_by, + (long long)tkl->expire_at, (long long)tkl->set_at, + tkl->ptr.serverban->reason); + } else + if (TKLIsNameBan(tkl)) + { + sendto_one(to, NULL, ":%s TKL %c %c %c %s %s %lld %lld :%s", sender->name, + add ? '+' : '-', + typ, + tkl->ptr.nameban->hold ? 'H' : '*', + tkl->ptr.nameban->name, + tkl->set_by, + (long long)tkl->expire_at, (long long)tkl->set_at, + tkl->ptr.nameban->reason); } } @@ -2503,7 +2607,7 @@ void tkl_synch_send_entry(aClient *sender, aClient *to, aTKline *tkl) * @param skip The client to skip, eg cptr or NULL. * @param tkl The TKL entry to synchronize with the other servers. */ -void tkl_broadcast_entry(aClient *sender, aClient *skip, aTKline *tkl) +void tkl_broadcast_entry(int add, aClient *sender, aClient *skip, aTKline *tkl) { aClient *cptr; @@ -2512,7 +2616,7 @@ void tkl_broadcast_entry(aClient *sender, aClient *skip, aTKline *tkl) if (skip && cptr == skip->from) continue; - tkl_synch_send_entry(sender, cptr, tkl); + tkl_synch_send_entry(add, sender, cptr, tkl); } } @@ -2531,7 +2635,7 @@ void _tkl_synch(aClient *sptr) { for (tkl = tklines_ip_hash[index][index2]; tkl; tkl = tkl->next) { - tkl_synch_send_entry(&me, sptr, tkl); + tkl_synch_send_entry(1, &me, sptr, tkl); } } } @@ -2541,22 +2645,22 @@ void _tkl_synch(aClient *sptr) { for (tkl = tklines[index]; tkl; tkl = tkl->next) { - tkl_synch_send_entry(&me, sptr, tkl); + tkl_synch_send_entry(1, &me, sptr, tkl); } } } /** Show TKL type as a string (used when adding/removing) */ -char *_tkl_type_string(aTKline *tk) +char *_tkl_type_string(aTKline *tkl) { static char txt[256]; *txt = '\0'; - if (!(tk->type & TKL_SPAMF) && (tk->subtype == TKL_SUBTYPE_SOFT)) + if (TKLIsServerBan(tkl) && (tkl->ptr.serverban->subtype == TKL_SUBTYPE_SOFT)) strlcpy(txt, "Soft ", sizeof(txt)); - switch (tk->type) + switch (tkl->type) { case TKL_KILL: strlcat(txt, "K-Line", sizeof(txt)); @@ -2573,10 +2677,10 @@ char *_tkl_type_string(aTKline *tk) case TKL_SHUN | TKL_GLOBAL: strlcat(txt, "Shun", sizeof(txt)); break; - case TKL_NICK | TKL_GLOBAL: + case TKL_NAME | TKL_GLOBAL: strlcat(txt, "Global Q-Line", sizeof(txt)); break; - case TKL_NICK: + case TKL_NAME: strlcat(txt, "Q-Line", sizeof(txt)); break; case TKL_SPAMF: @@ -2598,47 +2702,73 @@ char *_tkl_type_string(aTKline *tk) return txt; } -/** Find a TKL entry by type and user@mask (and some more) */ -aTKline *find_tkline(int type, int softban, char *usermask, char *hostmask, char *spamfilter) +/** Find a server ban TKL - only used to prevent duplicates and for deletion */ +aTKline *_find_tkl_serverban(int type, char *usermask, char *hostmask, int softban) { char tpe = tkl_typetochar(type); - aTKline *head, *tk; + aTKline *head, *tkl; + + if (!TKLIsServerBanType(type)) + abort(); head = tkl_find_head(tpe, hostmask, tklines[tkl_hash(tpe)]); - for (tk = head; tk; tk = tk->next) + for (tkl = head; tkl; tkl = tkl->next) { - if (tk->type == type) + if (tkl->type == type) { - if (type & TKL_NICK) - { - if (!stricmp(tk->hostmask, hostmask)) - return tk; - } else - if (type & TKL_SPAMF) - { - if (!strcmp(tk->hostmask, hostmask) && !strcmp(tk->usermask, usermask) && - !stricmp(tk->reason, spamfilter)) - { - return tk; - } - } else - if (type & TKL_EXCEPT) - { - /* skip? */ - } else /* all other types... */ - if (!stricmp(tk->hostmask, hostmask) && !stricmp(tk->usermask, usermask)) + if (!stricmp(tkl->ptr.serverban->hostmask, hostmask) && + !stricmp(tkl->ptr.serverban->usermask, usermask)) { /* And an extra check for soft/hard ban mismatches.. */ - if ((tk->subtype & TKL_SUBTYPE_SOFT) == softban) - return tk; + if ((tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) == softban) + return tkl; } } } return NULL; /* Not found */ } +/** Find a name ban TKL (qline) - only used to prevent duplicates and for deletion */ +aTKline *_find_tkl_nameban(int type, char *name, int hold) +{ + char tpe = tkl_typetochar(type); + aTKline *tkl; + + if (!TKLIsNameBanType(type)) + abort(); + + for (tkl = tklines[tkl_hash(tpe)]; tkl; tkl = tkl->next) + { + if ((tkl->type == type) && !stricmp(tkl->ptr.nameban->name, name)) + return tkl; + } + return NULL; /* Not found */ +} + +/** Find a spamfilter TKL - only used to prevent duplicates and for deletion */ +aTKline *_find_tkl_spamfilter(int type, char *match_string, unsigned short action, unsigned short target) +{ + char tpe = tkl_typetochar(type); + aTKline *tkl; + + if (!TKLIsSpamfilterType(type)) + abort(); + + for (tkl = tklines[tkl_hash(tpe)]; tkl; tkl = tkl->next) + { + if ((type == tkl->type) && + !strcmp(match_string, tkl->ptr.spamfilter->match->str) && + (action == tkl->ptr.spamfilter->action) && + (target == tkl->ptr.spamfilter->target)) + { + return tkl; + } + } + return NULL; /* Not found */ +} + /** Send a notice to opers about the TKL that is being added */ -void sendnotice_tkl_add(aTKline *tk) +void _sendnotice_tkl_add(aTKline *tkl) { char buf[512]; char set_at[128]; @@ -2646,53 +2776,58 @@ void sendnotice_tkl_add(aTKline *tk) char *tkl_type_str; /**< Eg: "K-Line" */ /* Don't show notices for temporary nick holds (issued by services) */ - if ((tk->type & TKL_NICK) && (tk->usermask[0] == 'H')) + if (TKLIsNameBan(tkl) && tkl->ptr.nameban->hold) return; - tkl_type_str = tkl_type_string(tk); + tkl_type_str = tkl_type_string(tkl); *buf = *set_at = *expire_at = '\0'; - short_date(tk->set_at, set_at); - if (tk->expire_at > 0) - short_date(tk->expire_at, expire_at); + short_date(tkl->set_at, set_at); + if (tkl->expire_at > 0) + short_date(tkl->expire_at, expire_at); - if (tk->type & TKL_SPAMF) + if (TKLIsServerBan(tkl)) + { + if (tkl->expire_at != 0) + { + ircsnprintf(buf, sizeof(buf), "%s added for %s%s@%s on %s GMT (from %s to expire at %s GMT: %s)", + tkl_type_str, + (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", + tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask, + set_at, tkl->set_by, expire_at, tkl->ptr.serverban->reason); + } else { + ircsnprintf(buf, sizeof(buf), "Permanent %s added for %s%s@%s on %s GMT (from %s: %s)", + tkl_type_str, + (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", + tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask, + set_at, tkl->set_by, tkl->ptr.serverban->reason); + } + } else + if (TKLIsNameBan(tkl)) + { + if (tkl->expire_at > 0) + { + ircsnprintf(buf, sizeof(buf), "%s added for %s on %s GMT (from %s to expire at %s GMT: %s)", + tkl_type_str, tkl->ptr.nameban->name, set_at, tkl->set_by, expire_at, tkl->ptr.nameban->reason); + } else { + ircsnprintf(buf, sizeof(buf), "Permanent %s added for %s on %s GMT (from %s: %s)", + tkl_type_str, tkl->ptr.nameban->name, set_at, tkl->set_by, tkl->ptr.nameban->reason); + } + } else + if (TKLIsSpamfilter(tkl)) { /* Spamfilter */ ircsnprintf(buf, sizeof(buf), - "Spamfilter added: '%s' [target: %s] [action: %s] [reason: %s] on %s GMT (from %s)", - tk->reason, tk->usermask, banact_valtostring(banact_chartoval(*tk->hostmask)), - tk->ptr.spamf->tkl_reason, - set_at, - tk->setby); - } else { - - if (tk->expire_at != 0) - { - /* Temporary *LINE */ - if (tk->type & TKL_NICK) - { - ircsnprintf(buf, sizeof(buf), "%s added for %s on %s GMT (from %s to expire at %s GMT: %s)", - tkl_type_str, tk->hostmask, set_at, tk->setby, expire_at, tk->reason); - } else { - ircsnprintf(buf, sizeof(buf), "%s added for %s%s@%s on %s GMT (from %s to expire at %s GMT: %s)", - tkl_type_str, - (tk->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", tk->usermask, tk->hostmask, - set_at, tk->setby, expire_at, tk->reason); - } - } else { - /* Permanent *LINE */ - if (tk->type & TKL_NICK) - { - ircsnprintf(buf, sizeof(buf), "Permanent %s added for %s on %s GMT (from %s: %s)", - tkl_type_str, tk->hostmask, set_at, tk->setby, tk->reason); - } else { - ircsnprintf(buf, sizeof(buf), "Permanent %s added for %s%s@%s on %s GMT (from %s: %s)", - tkl_type_str, - (tk->subtype & TKL_SUBTYPE_SOFT) ? "%" : "", tk->usermask, tk->hostmask, - set_at, tk->setby, tk->reason); - } - } + "Spamfilter added: '%s' [target: %s] [action: %s] [reason: %s] on %s GMT (from %s)", + tkl->ptr.spamfilter->match->str, + spamfilter_target_inttostring(tkl->ptr.spamfilter->target), + banact_valtostring(tkl->ptr.spamfilter->action), + unreal_decodespace(tkl->ptr.spamfilter->tkl_reason), + set_at, + tkl->set_by); + } else + { + ircsnprintf(buf, sizeof(buf), "[BUG] %s added but type unhandled in sendnotice_tkl_add()!!!", tkl_type_str); } sendto_snomask(SNO_TKL, "*** %s", buf); @@ -2700,52 +2835,57 @@ void sendnotice_tkl_add(aTKline *tk) } /** Send a notice to opers about the TKL that is being deleted */ -void sendnotice_tkl_del(aTKline *tk) +void _sendnotice_tkl_del(char *removed_by, aTKline *tkl) { char buf[512]; char set_at[128]; char *tkl_type_str; /* Don't show notices for temporary nick holds (issued by services) */ - if ((tk->type & TKL_NICK) && (tk->usermask[0] == 'H')) + if (TKLIsNameBan(tkl) && tkl->ptr.nameban->hold) return; - tkl_type_str = tkl_type_string(tk); /* eg: "K-Line" */ + tkl_type_str = tkl_type_string(tkl); /* eg: "K-Line" */ *buf = *set_at = '\0'; - short_date(tk->set_at, set_at); + short_date(tkl->set_at, set_at); - if (tk->type & TKL_NICK) + if (TKLIsServerBan(tkl)) + { + ircsnprintf(buf, sizeof(buf), + "%s removed %s %s@%s (set at %s - reason: %s)", + removed_by, tkl_type_str, + tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask, + set_at, tkl->ptr.serverban->reason); + } else + if (TKLIsNameBan(tkl)) { ircsnprintf(buf, sizeof(buf), "%s removed %s %s (set at %s - reason: %s)", - tk->setby, tkl_type_str, tk->hostmask, set_at, tk->reason); - } - else if (tk->type & TKL_SPAMF) + removed_by, tkl_type_str, tkl->ptr.nameban->name, set_at, tkl->ptr.nameban->reason); + } else + if (TKLIsSpamfilter(tkl)) { ircsnprintf(buf, sizeof(buf), "%s removed Spamfilter '%s' (set at %s)", - tk->setby, tk->reason, set_at); - } else { - ircsnprintf(buf, sizeof(buf), - "%s removed %s %s@%s (set at %s - reason: %s)", - tk->setby, tkl_type_str, tk->usermask, - tk->hostmask, set_at, tk->reason); + removed_by, tkl->ptr.spamfilter->match->str, set_at); + } else + { + ircsnprintf(buf, sizeof(buf), "[BUG] %s added but type unhandled in sendnotice_tkl_del()!!!!!", tkl_type_str); } sendto_snomask(SNO_TKL, "*** %s", buf); ircd_log(LOG_TKL, "%s", buf); } -/** Add a TKL using the TKL layer. See m_tklC for parv[] and protocol documentation. */ +/** Add a TKL using the TKL layer. See m_tkl for parv[] and protocol documentation. */ CMD_FUNC(m_tkl_add) { - aTKline *tk; + aTKline *tkl; int type; - time_t expiry_1, setat_1, spamf_tklduration = 0; - MatchType spamf_match_method = MATCH_PCRE_REGEX; /* default */ - char *reason = NULL; - int softban = 0; + time_t expire_at, set_at; + char *set_by; + char tkl_entry_exists = 0; /* we rely on servers to be failsafe.. */ if (!IsServer(sptr) && !IsMe(sptr)) @@ -2754,196 +2894,225 @@ CMD_FUNC(m_tkl_add) if (parc < 9) return 0; - /* In case of a soft ban, strip the percent sign early, - * so parv[3] (username) is really the username without any prefix. - * Set the 'softban' flag if this is the case. - */ - if (parv[3][0] == '%') - { - softban = 1; - parv[3] = parv[3]+1; - } - type = tkl_chartotype(parv[2][0]); if (!type) return 0; - expiry_1 = atol(parv[6]); - setat_1 = atol(parv[7]); - reason = parv[8]; + /* All TKL types have the following fields in common when adding: + * parv[5]: set_by + * parv[6]: expire_at + * parv[7]: set_at + * ... so we validate them here at the beginning. + */ - if (expiry_1 < 0) + set_by = parv[5]; + expire_at = atol(parv[6]); + set_at = atol(parv[7]); + + /* Validate set and expiry time */ + if ((set_at < 0) || !short_date(set_at, NULL)) { - sendto_realops("Invalid TKL entry from %s, negative expire time (%ld) -- not added. Clock on other server incorrect?", - sptr->name, (long)expiry_1); + sendto_realops("Invalid TKL entry from %s, set-at time is out of range (%lld) -- not added. Clock on other server incorrect or bogus entry.", + sptr->name, (long long)set_at); + return 0; + } + if ((expire_at < 0) || !short_date(expire_at, NULL)) + { + sendto_realops("Invalid TKL entry from %s, expiry time is out of range (%lld) -- not added. Clock on other server incorrect or bogus entry.", + sptr->name, (long long)expire_at); return 0; } - if (setat_1 < 0) - { - sendto_realops("Invalid TKL entry from %s, negative set-at time (%ld) -- not added. Clock on other server incorrect?", - sptr->name, (long)setat_1); - return 0; - } + /* Now comes type-specific validation + * and we check if the TKL entry already exists and needs updating too. + */ - if ((type & TKL_SPAMF) && (parc >= 11)) + if (TKLIsServerBanType(type)) { - if (parc >= 12) + /* Validate server ban TKL fields */ + int softban = 0; + char *usermask = parv[3]; + char *hostmask = parv[4]; + char *reason = parv[8]; + + /* Some simple validation on usermask and hostmask: + * may not contain an @. Yeah, some services or self-written + * linked servers are known to have sent this in the past. + */ + if (strchr(usermask, '@') || strchr(hostmask, '@')) { - reason = parv[11]; - if (!strcasecmp(parv[10], "posix")) - { - sendto_realops("Ignoring spamfilter from %s. Spamfilter is of type 'posix' (TRE) which " - "is not supported in UnrealIRCd 5. Suggestion: upgrade the other server.", - sptr->name); - return 0; - } - spamf_match_method = unreal_match_method_strtoval(parv[10]); - if (spamf_match_method == 0) - { - sendto_realops("Ignoring spamfilter from %s with unknown match type '%s'", - sptr->name, parv[10]); - return 0; - } + sendto_realops("Ignoring TKL entry %s@%s from %s. " + "Invalid usermask '%s' or hostmask '%s'.", + usermask, hostmask, sptr->name, usermask, hostmask); + return 0; + } + + /* In case of a soft ban, strip the percent sign early, + * so parv[3] (username) is really the username without any prefix. + * Set the 'softban' flag if this is the case. + */ + if (*usermask == '%') + { + usermask++; + softban = 1; + } + + tkl = find_tkl_serverban(type, usermask, hostmask, softban); + if (tkl) + { + tkl_entry_exists = 1; } else { - /* Actually this only happens for 3.2.x or lower */ - reason = parv[10]; + tkl = tkl_add_serverban(type, usermask, hostmask, reason, + set_by, expire_at, set_at, softban, 0); + } + } else + if (TKLIsNameBanType(type)) + { + /* Validate name ban TKL fields */ + int hold = 0; + char *name = parv[4]; + char *reason = parv[8]; + + if (*parv[3] == 'H') + hold = 1; + + tkl = find_tkl_nameban(type, name, hold); + if (tkl) + { + tkl_entry_exists = 1; + } else { + tkl = tkl_add_nameban(type, name, hold, reason, set_by, expire_at, + set_at, 0); + } + } else + if (TKLIsSpamfilterType(type)) + { + /* Validate spamfilter-specific TKL fields */ + MatchType match_method; + char *match_string; + aMatch *m; /* compiled match_string */ + time_t tkl_duration; + char *tkl_reason; + unsigned short action; + unsigned short target; + /* helper variables */ + char *err; + + if (parc < 12) + { + sendto_realops("Ignoring spamfilter from %s. Running very old UnrealIRCd protocol (3.2.X?)", sptr->name); + return 0; + } + + match_string = parv[11]; + + if (!strcasecmp(parv[10], "posix")) + { sendto_realops("Ignoring spamfilter from %s. Spamfilter is of type 'posix' (TRE) which " "is not supported in UnrealIRCd 5. Suggestion: upgrade the other server.", sptr->name); return 0; } - spamf_tklduration = config_checkval(parv[8], CFG_TIME); /* was: atol(parv[8]); */ - } - - /* Search for existing entry... */ - tk = find_tkline(type, softban, parv[3], parv[4], reason); - if (tk) - { - /* The *LINE already exists. - * This could be an update to things like reason / duration / .. - */ - - if (type & TKL_NICK) + match_method = unreal_match_method_strtoval(parv[10]); + if (match_method == 0) { - /* for sqline: usermask = H overrides */ - /* FIXME: wait.. is this true? it would mean a temporary - * hold from nickserv could cause 'H' to be set and then - * on un-hold it would remove our *line ? - */ - if (*parv[3] == 'H') - *tk->usermask = 'H'; + sendto_realops("Ignoring spamfilter '%s' from %s with unknown match type '%s'", + match_string, sptr->name, parv[10]); + return 0; } - if ((setat_1 < tk->set_at) || (expiry_1 != tk->expire_at) || - strcmp(tk->reason, reason) || strcmp(tk->setby, parv[5])) + if (!(target = spamfilter_gettargets(parv[3], NULL))) + { + sendto_realops("Ignoring spamfilter '%s' from %s with unknown target type '%s'", + match_string, sptr->name, parv[3]); + return 0; + } + + if (!(action = banact_chartoval(*parv[4]))) + { + sendto_realops("Ignoring spamfilter '%s' from %s with unknown action type '%s'", + match_string, sptr->name, parv[4]); + return 0; + } + + tkl_duration = config_checkval(parv[8], CFG_TIME); + tkl_reason = parv[9]; + + tkl = find_tkl_spamfilter(type, match_string, action, target); + + if (tkl) + { + tkl_entry_exists = 1; + } else { + m = unreal_create_match(match_method, match_string, &err); + if (!m) + { + sendto_realops("[TKL ERROR] ERROR: Trying to add a spamfilter which does not compile. " + " ERROR='%s', Spamfilter='%s', from='%s'", + err, match_string, sptr->name); + return 0; + } + tkl = tkl_add_spamfilter(type, target, action, m, set_by, expire_at, set_at, + tkl_duration, tkl_reason, 0); + } + } else + { + /* Unhandled, should never happen */ + abort(); + } + + if (!tkl) + return 0; + + if (tkl_entry_exists) + { + /* Let's see if we need to update the existing entry. + * Note that we only update common fields, + * which is acceptable to me. -- Syzop + */ + if ((set_at < tkl->set_at) || (expire_at != tkl->expire_at) || strcmp(tkl->set_by, parv[5])) { /* here's how it goes: * set_at: oldest wins * expire_at: longest wins - * reason: highest strcmp wins - * setby: highest strcmp wins + * set_by: highest strcmp wins + * * We broadcast the result of this back to all servers except * cptr's direction, because cptr will do the same thing and * send it back to his servers (except us)... no need for a * double networkwide flood ;p. -- Syzop */ - tk->set_at = MIN(tk->set_at, setat_1); + tkl->set_at = MIN(tkl->set_at, set_at); - if (!tk->expire_at || !expiry_1) - tk->expire_at = 0; + if (!tkl->expire_at || !expire_at) + tkl->expire_at = 0; else - tk->expire_at = MAX(tk->expire_at, expiry_1); + tkl->expire_at = MAX(tkl->expire_at, expire_at); - if (strcmp(tk->reason, reason) < 0) - safestrdup(tk->reason, reason); + if (strcmp(tkl->set_by, parv[5]) < 0) + safestrdup(tkl->set_by, parv[5]); - if (strcmp(tk->setby, parv[5]) < 0) - safestrdup(tk->setby, parv[5]); - - /* TODO: improve this notice */ - if (tk->type & TKL_NICK) - { - if (*tk->usermask != 'H') - sendto_snomask(SNO_TKL, "tkl update for %s/reason='%s'/by=%s/set=%lld/expire=%lld [causedby: %s]", - tk->hostmask, tk->reason, tk->setby, - (long long)tk->set_at, (long long)tk->expire_at, sptr->name); - } else { - sendto_snomask(SNO_TKL, "tkl update for %s@%s/reason='%s'/by=%s/set=%lld/expire=%lld [causedby: %s]", - tk->usermask, tk->hostmask, tk->reason, tk->setby, - (long long)tk->set_at, (long long)tk->expire_at, sptr->name); - } - - if ((parc == 11) && (type & TKL_SPAMF)) - { - /* I decided to only send updates to OPT_TKLEXT in this case, - * it's pretty useless to send it also to OPT_NOT_TKLEXT because - * spamfilter entries are permanent (no expire time), the only stuff - * that can differ for non-opt is the 'setby' and 'setat' field... - */ - - /* FIXME check if this works correctly with TKLEXT2 ? */ - sendto_server(cptr, PROTO_TKLEXT, 0, NULL, - ":%s TKL %s %s %s %s %s %lld %lld %lld %s :%s", sptr->name, - parv[1], parv[2], parv[3], parv[4], - tk->setby, - (long long)tk->expire_at, (long long)tk->set_at, (long long)tk->ptr.spamf->tkl_duration, - tk->ptr.spamf->tkl_reason, tk->reason); - } - else if (type & TKL_GLOBAL) - { - sendto_server(cptr, 0, 0, NULL, - ":%s TKL %s %s %s%s %s %s %lld %lld :%s", sptr->name, - parv[1], parv[2], (softban?"%":""), parv[3], parv[4], - tk->setby, (long long)tk->expire_at, (long long)tk->set_at, tk->reason); - } + if (type & TKL_GLOBAL) + tkl_broadcast_entry(1, sptr, cptr, tkl); } return 0; } - /* Validate set and expiry time */ - if (!short_date(setat_1, NULL)) - { - sendto_realops("Invalid TKL entry from %s, set-at time is out of range (%lld) -- not added. Clock on other server incorrect or bogus entry.", - sptr->name, (long long)setat_1); - return 0; - } - if (!short_date(expiry_1, NULL)) - { - sendto_realops("Invalid TKL entry from %s, expiry time is out of range (%lld) -- not added. Clock on other server incorrect or bogus entry.", - sptr->name, (long long)expiry_1); - return 0; - } + /* Below this line we will only use 'tkl'. No parc/parv reading anymore. */ - /* Actually add the TKL entry */ - if ((type & TKL_SPAMF) && (parc >= 11)) - { - tk = tkl_add_line(type, parv[3], parv[4], reason, parv[5], - expiry_1, setat_1, spamf_tklduration, parv[9], - spamf_match_method, 0, 0); - } else { - tk = tkl_add_line(type, parv[3], parv[4], reason, parv[5], - expiry_1, setat_1, 0, NULL, - 0, softban, 0); - } + RunHook3(HOOKTYPE_TKL_ADD, cptr, sptr, tkl); - if (!tk) - return 0; /* ERROR on allocate or something else... */ - - /* Below here we will use 'tk' and not parc/parv anymore */ - RunHook3(HOOKTYPE_TKL_ADD, cptr, sptr, tk); - - sendnotice_tkl_add(tk); + sendnotice_tkl_add(tkl); /* spamfilter 'warn' action is special */ - if ((tk->type & TKL_SPAMF) && (tk->ptr.spamf->action == BAN_ACT_WARN) && (tk->subtype & SPAMF_USER)) - spamfilter_check_users(tk); + if ((tkl->type & TKL_SPAMF) && (tkl->ptr.spamfilter->action == BAN_ACT_WARN) && (tkl->ptr.spamfilter->target & SPAMF_USER)) + spamfilter_check_users(tkl); /* Ban checking executes during run loop for efficiency */ loop.do_bancheck = 1; if (type & TKL_GLOBAL) - tkl_broadcast_entry(sptr, cptr, tk); + tkl_broadcast_entry(1, sptr, cptr, tkl); return 0; } @@ -2951,10 +3120,9 @@ CMD_FUNC(m_tkl_add) /** Delete a TKL using the TKL layer. See m_tkl for parv[] and protocol documentation. */ CMD_FUNC(m_tkl_del) { - aTKline *tk; + aTKline *tkl; int type; - char *reason = NULL; - int softban = 0; + char *removed_by; if (!IsServer(sptr) && !IsMe(sptr)) return 0; @@ -2962,23 +3130,41 @@ CMD_FUNC(m_tkl_del) if (parc < 6) return 0; - /* In case of a soft ban, strip the percent sign early, - * so parv[3] (username) is really the username without any prefix. - * Set the 'softban' flag if this is the case. - */ - if (parv[3][0] == '%') - { - softban = 1; - parv[3] = parv[3]+1; - } - type = tkl_chartotype(parv[2][0]); if (type == 0) - { return 0; - } - if (type & TKL_SPAMF) + + removed_by = parv[5]; + + if (TKLIsServerBanType(type)) { + char *usermask = parv[3]; + char *hostmask = parv[4]; + int softban = 0; + + if (*usermask == '%') + { + usermask++; + softban = 1; + } + + tkl = find_tkl_serverban(type, usermask, hostmask, softban); + } + else if (TKLIsNameBanType(type)) + { + int hold = 0; + char *name = parv[4]; + + if (*parv[3] == 'H') + hold = 1; + tkl = find_tkl_nameban(type, name, hold); + } + else if (TKLIsSpamfilterType(type)) + { + char *match_string; + unsigned short target; + unsigned short action; + if (parc < 9) { sendto_realops("[BUG] m_tkl called with bogus spamfilter removal request [f/F], from=%s, parc=%d", @@ -2986,45 +3172,52 @@ CMD_FUNC(m_tkl_del) return 0; /* bogus */ } if (parc >= 12) - reason = parv[11]; + match_string = parv[11]; else if (parc >= 11) - reason = parv[10]; + match_string = parv[10]; else - reason = parv[8]; + match_string = parv[8]; + + if (!(target = spamfilter_gettargets(parv[3], NULL))) + { + sendto_realops("Ignoring spamfilter deletion request for '%s' from %s with unknown target type '%s'", + match_string, sptr->name, parv[3]); + return 0; + } + + if (!(action = banact_chartoval(*parv[4]))) + { + sendto_realops("Ignoring spamfilter deletion request for '%s' from %s with unknown action type '%s'", + match_string, sptr->name, parv[4]); + return 0; + } + tkl = find_tkl_spamfilter(type, match_string, action, target); + } else + { + /* This can never happen, unless someone added a TKL type + * to UnrealIRCd but forgot to add the removal code :D. + */ + abort(); } - tk = find_tkline(type, softban, parv[3], parv[4], reason); - if (!tk) - return 0; /* Item not found */ + if (!tkl) + return 0; /* Item not found, nothing to remove. */ - if (tk->flags & TKL_FLAG_CONFIG) + if (tkl->flags & TKL_FLAG_CONFIG) return 0; /* Item is in the configuration file (persistent) */ /* broadcast remove msg to opers... */ - sendnotice_tkl_del(tk); + sendnotice_tkl_del(removed_by, tkl); if (type & TKL_SHUN) - tkl_check_local_remove_shun(tk); + tkl_check_local_remove_shun(tkl); - RunHook3(HOOKTYPE_TKL_DEL, cptr, sptr, tk); + RunHook3(HOOKTYPE_TKL_DEL, cptr, sptr, tkl); - /* Broadcast removal to all other servers */ if (type & TKL_GLOBAL) - { - if ((parc > 8) && (type & TKL_SPAMF)) - { - /* Spamfilter... */ - sendto_server(cptr, 0, 0, NULL, ":%s TKL %s %s %s %s %s %s %s :%s", - sptr->name, parv[1], parv[2], parv[3], parv[4], parv[5], - parv[6], parv[7], reason); - } else { - /* Any other TKL (eg: gline) */ - sendto_server(cptr, 0, 0, NULL, ":%s TKL %s %s %s%s %s %s", - sptr->name, parv[1], parv[2], (softban?"%":""), parv[3], parv[4], parv[5]); - } - } + tkl_broadcast_entry(0, sptr, cptr, tkl); - tkl_del_line(tk); + tkl_del_line(tkl); return 0; } @@ -3045,7 +3238,7 @@ CMD_FUNC(m_tkl_del) * parv[ 2]: type type type type type * parv[ 3]: user user target target hold * parv[ 4]: host host action action host - * parv[ 5]: setby removedby (un)setby setby setby + * parv[ 5]: set_by removedby (un)set_by set_by set_by * parv[ 6]: expire_at expire_at (0) expire_at (0) expire_at * parv[ 7]: set_at set_at set_at set_at * parv[ 8]: reason regex tkl duration reason @@ -3204,25 +3397,28 @@ aTKline *choose_winning_spamfilter(aTKline *one, aTKline *two) { int n; + if (!TKLIsSpamfilter(one) || !TKLIsSpamfilter(two)) + abort(); + /* First, see if the action field differs... */ - if (one->ptr.spamf->action != two->ptr.spamf->action) + if (one->ptr.spamfilter->action != two->ptr.spamfilter->action) { /* We can simply compare the action. Highest (strongest) wins. */ - if (one->ptr.spamf->action > two->ptr.spamf->action) + if (one->ptr.spamfilter->action > two->ptr.spamfilter->action) return one; else return two; } /* Ok, try comparing the regex then.. */ - n = strcmp(one->reason, two->reason); + n = strcmp(one->ptr.spamfilter->match->str, two->ptr.spamfilter->match->str); if (n < 0) return one; if (n > 0) return two; /* Hmm.. regex is identical. Try the 'reason' field. */ - n = strcmp(one->ptr.spamf->tkl_reason, two->ptr.spamf->tkl_reason); + n = strcmp(one->ptr.spamfilter->tkl_reason, two->ptr.spamfilter->tkl_reason); if (n < 0) return one; if (n > 0) @@ -3231,7 +3427,7 @@ aTKline *choose_winning_spamfilter(aTKline *one, aTKline *two) /* Hmm.. 'reason' is identical as well. * Make a final decision, could still be identical but would be unlikely. */ - return (one->subtype > two->subtype) ? one : two; + return (one->ptr.spamfilter->target > two->ptr.spamfilter->target) ? one : two; } /** Checks if 'target' is on the spamfilter exception list. @@ -3255,7 +3451,7 @@ static int target_is_spamexcept(char *target) * @param type The spamfilter type (SPAMF_*) * TODO: Looks redundant? */ -int _join_viruschan(aClient *sptr, aTKline *tk, int type) +int _join_viruschan(aClient *sptr, aTKline *tkl, int type) { char *xparv[3], chbuf[CHANNELLEN + 16], buf[2048]; aChannel *chptr; @@ -3275,7 +3471,7 @@ int _join_viruschan(aClient *sptr, aTKline *tk, int type) return FLUSH_BUFFER; /* don't ask me how we could have died... */ sendnotice(sptr, "You are now restricted to talking in %s: %s", - SPAMFILTER_VIRUSCHAN, unreal_decodespace(tk->ptr.spamf->tkl_reason)); + SPAMFILTER_VIRUSCHAN, unreal_decodespace(tkl->ptr.spamfilter->tkl_reason)); chptr = find_channel(SPAMFILTER_VIRUSCHAN, NULL); if (chptr) @@ -3283,8 +3479,8 @@ int _join_viruschan(aClient *sptr, aTKline *tk, int type) MessageTag *mtags = NULL; ircsnprintf(chbuf, sizeof(chbuf), "@%s", chptr->chname); ircsnprintf(buf, sizeof(buf), "[Spamfilter] %s matched filter '%s' [%s] [%s]", - sptr->name, tk->reason, cmdname_by_spamftarget(type), - unreal_decodespace(tk->ptr.spamf->tkl_reason)); + sptr->name, tkl->ptr.spamfilter->match->str, cmdname_by_spamftarget(type), + unreal_decodespace(tkl->ptr.spamfilter->tkl_reason)); new_message(&me, NULL, &mtags); sendto_channel(chptr, &me, NULL, PREFIX_OP|PREFIX_ADMIN|PREFIX_OWNER, 0, SEND_ALL|SKIP_DEAF, mtags, @@ -3297,31 +3493,32 @@ int _join_viruschan(aClient *sptr, aTKline *tk, int type) /** run_spamfilter: executes the spamfilter on the input string. * @param str The text (eg msg text, notice text, part text, quit text, etc - * @param type The spamfilter type (SPAMF_*) - * @param target The target as a text string (can be NULL, eg: for away) + * @param target The spamfilter target (SPAMF_*) + * @param destination The destination as a text string (eg: "somenick", can be NULL.. eg for away) * @param flags Any flags (SPAMFLAG_*) - * @param rettk Pointer to an aTKLline struct, _used for special circumstances only_ + * @param rettkl Pointer to an aTKLline struct, _used for special circumstances only_ * RETURN VALUE: * 0 if not matched, non-0 if it should be blocked. * Return value can be FLUSH_BUFFER (-2) which means 'sptr' is * _NOT_ valid anymore so you should return immediately * (like from m_message, m_part, m_quit, etc). */ -int _run_spamfilter(aClient *sptr, char *str_in, int type, char *target, int flags, aTKline **rettk) +int _run_spamfilter(aClient *sptr, char *str_in, int target, char *destination, int flags, aTKline **rettkl) { - aTKline *tk; - aTKline *winner_tk = NULL; + aTKline *tkl; + aTKline *winner_tkl = NULL; char *str; int ret = -1; + char *reason = NULL; #ifdef SPAMFILTER_DETECTSLOW struct rusage rnow, rprev; long ms_past; #endif - if (rettk) - *rettk = NULL; /* initialize to NULL */ + if (rettkl) + *rettkl = NULL; /* initialize to NULL */ - if (type == SPAMF_USER) + if (target == SPAMF_USER) str = str_in; else str = (char *)StripControlCodes(str_in); @@ -3332,18 +3529,18 @@ int _run_spamfilter(aClient *sptr, char *str_in, int type, char *target, int fla if (!sptr->user || ValidatePermissionsForPath("immune:server-ban:spamfilter",sptr,NULL,NULL,NULL) || IsULine(sptr)) return 0; - for (tk = tklines[tkl_hash('F')]; tk; tk = tk->next) + for (tkl = tklines[tkl_hash('F')]; tkl; tkl = tkl->next) { - if (!(tk->subtype & type)) + if (!(tkl->ptr.spamfilter->target & target)) continue; - if ((flags & SPAMFLAG_NOWARN) && (tk->ptr.spamf->action == BAN_ACT_WARN)) + if ((flags & SPAMFLAG_NOWARN) && (tkl->ptr.spamfilter->action == BAN_ACT_WARN)) continue; /* If the action is 'soft' (for non-logged in users only) then * don't bother running the spamfilter if the user is logged in. */ - if (IsSoftBanAction(tk->ptr.spamf->action) && IsLoggedIn(sptr)) + if (IsSoftBanAction(tkl->ptr.spamfilter->action) && IsLoggedIn(sptr)) continue; #ifdef SPAMFILTER_DETECTSLOW @@ -3353,7 +3550,7 @@ int _run_spamfilter(aClient *sptr, char *str_in, int type, char *target, int fla getrusage(RUSAGE_SELF, &rprev); #endif - ret = unreal_match(tk->ptr.spamf->expr, str); + ret = unreal_match(tkl->ptr.spamfilter->match, str); #ifdef SPAMFILTER_DETECTSLOW getrusage(RUSAGE_SELF, &rnow); @@ -3364,14 +3561,14 @@ int _run_spamfilter(aClient *sptr, char *str_in, int type, char *target, int fla if ((SPAMFILTER_DETECTSLOW_FATAL > 0) && (ms_past > SPAMFILTER_DETECTSLOW_FATAL)) { sendto_realops("[Spamfilter] WARNING: Too slow spamfilter detected (took %ld msec to execute) " - "-- spamfilter will be \002REMOVED!\002: %s", ms_past, tk->reason); - tkl_del_line(tk); + "-- spamfilter will be \002REMOVED!\002: %s", ms_past, tkl->ptr.spamfilter->match->str); + tkl_del_line(tkl); return 0; /* Act as if it didn't match, even if it did.. it's gone now anyway.. */ } else if ((SPAMFILTER_DETECTSLOW_WARN > 0) && (ms_past > SPAMFILTER_DETECTSLOW_WARN)) { sendto_realops("[Spamfilter] WARNING: SLOW Spamfilter detected (took %ld msec to execute): %s", - ms_past, tk->reason); + ms_past, tkl->ptr.spamfilter->match->str); } #endif @@ -3379,70 +3576,68 @@ int _run_spamfilter(aClient *sptr, char *str_in, int type, char *target, int fla { /* We have a match! */ char buf[1024]; - char targetbuf[48]; + char destinationbuf[48]; - if (target) { - targetbuf[0] = ' '; - strlcpy(targetbuf+1, target, sizeof(targetbuf)-1); /* cut it off */ + if (destination) { + destinationbuf[0] = ' '; + strlcpy(destinationbuf+1, destination, sizeof(destinationbuf)-1); /* cut it off */ } else - targetbuf[0] = '\0'; + destinationbuf[0] = '\0'; /* Hold on.. perhaps it's on the exceptions list... */ - if (!winner_tk && target && target_is_spamexcept(target)) + if (!winner_tkl && destination && target_is_spamexcept(destination)) return 0; /* No problem! */ ircsnprintf(buf, sizeof(buf), "[Spamfilter] %s!%s@%s matches filter '%s': [%s%s: '%s'] [%s]", sptr->name, sptr->user->username, sptr->user->realhost, - tk->reason, - cmdname_by_spamftarget(type), targetbuf, str, - unreal_decodespace(tk->ptr.spamf->tkl_reason)); + tkl->ptr.spamfilter->match->str, + cmdname_by_spamftarget(target), destinationbuf, str, + unreal_decodespace(tkl->ptr.spamfilter->tkl_reason)); sendto_snomask_global(SNO_SPAMF, "%s", buf); ircd_log(LOG_SPAMFILTER, "%s", buf); - RunHook6(HOOKTYPE_LOCAL_SPAMFILTER, sptr, str, str_in, type, target, tk); + RunHook6(HOOKTYPE_LOCAL_SPAMFILTER, sptr, str, str_in, target, destination, tkl); /* If we should stop after the first match, we end here... */ if (SPAMFILTER_STOP_ON_FIRST_MATCH) { - winner_tk = tk; + winner_tkl = tkl; break; } - /* Otherwise.. we set 'winner_tk' to the spamfilter with the strongest action. */ - if (!winner_tk) - winner_tk = tk; + /* Otherwise.. we set 'winner_tkl' to the spamfilter with the strongest action. */ + if (!winner_tkl) + winner_tkl = tkl; else - winner_tk = choose_winning_spamfilter(tk, winner_tk); + winner_tkl = choose_winning_spamfilter(tkl, winner_tkl); /* and continue.. */ } } - tk = winner_tk; + tkl = winner_tkl; - if (!tk) + if (!tkl) return 0; /* NOMATCH, we are done */ /* Spamfilter matched, take action: */ - if ((tk->ptr.spamf->action == BAN_ACT_BLOCK) || (tk->ptr.spamf->action == BAN_ACT_SOFT_BLOCK)) + reason = unreal_decodespace(tkl->ptr.spamfilter->tkl_reason); + if ((tkl->ptr.spamfilter->action == BAN_ACT_BLOCK) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_BLOCK)) { - switch(type) + switch(target) { case SPAMF_USERMSG: case SPAMF_USERNOTICE: - sendnotice(sptr, "Message to %s blocked: %s", - target, unreal_decodespace(tk->ptr.spamf->tkl_reason)); + sendnotice(sptr, "Message to %s blocked: %s", destination, reason); break; case SPAMF_CHANMSG: case SPAMF_CHANNOTICE: sendto_one(sptr, NULL, ":%s 404 %s %s :Message blocked: %s", - me.name, sptr->name, target, - unreal_decodespace(tk->ptr.spamf->tkl_reason)); + me.name, sptr->name, destination, reason); break; case SPAMF_DCC: - sendnotice(sptr, "DCC to %s blocked: %s", - target, unreal_decodespace(tk->ptr.spamf->tkl_reason)); + sendnotice(sptr, "DCC to %s blocked: %s", destination, reason); break; case SPAMF_AWAY: /* hack to deal with 'after-away-was-set-filters' */ @@ -3457,32 +3652,30 @@ int _run_spamfilter(aClient *sptr, char *str_in, int type, char *target, int fla case SPAMF_TOPIC: //... sendnotice(sptr, "Setting of topic on %s to that text is blocked: %s", - target, unreal_decodespace(tk->ptr.spamf->tkl_reason)); + destination, reason); break; default: break; } return -1; } else - if ((tk->ptr.spamf->action == BAN_ACT_WARN) || (tk->ptr.spamf->action == BAN_ACT_SOFT_WARN)) + if ((tkl->ptr.spamfilter->action == BAN_ACT_WARN) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_WARN)) { - if ((type != SPAMF_USER) && (type != SPAMF_QUIT)) - sendnumeric(sptr, RPL_SPAMCMDFWD, cmdname_by_spamftarget(type), - unreal_decodespace(tk->ptr.spamf->tkl_reason)); + if ((target != SPAMF_USER) && (target != SPAMF_QUIT)) + sendnumeric(sptr, RPL_SPAMCMDFWD, cmdname_by_spamftarget(target), reason); return 0; } else - if ((tk->ptr.spamf->action == BAN_ACT_DCCBLOCK) || (tk->ptr.spamf->action == BAN_ACT_SOFT_DCCBLOCK)) + if ((tkl->ptr.spamfilter->action == BAN_ACT_DCCBLOCK) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_DCCBLOCK)) { - if (type == SPAMF_DCC) + if (target == SPAMF_DCC) { - sendnotice(sptr, "DCC to %s blocked: %s", - target, unreal_decodespace(tk->ptr.spamf->tkl_reason)); + sendnotice(sptr, "DCC to %s blocked: %s", destination, reason); sendnotice(sptr, "*** You have been blocked from sending files, reconnect to regain permission to send files"); sptr->flags |= FLAGS_DCCBLOCK; } return -1; } else - if ((tk->ptr.spamf->action == BAN_ACT_VIRUSCHAN) || (tk->ptr.spamf->action == BAN_ACT_SOFT_VIRUSCHAN)) + if ((tkl->ptr.spamfilter->action == BAN_ACT_VIRUSCHAN) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_VIRUSCHAN)) { if (IsVirus(sptr)) /* Already tagged */ return 0; @@ -3492,16 +3685,15 @@ int _run_spamfilter(aClient *sptr, char *str_in, int type, char *target, int fla */ if (!IsClient(sptr)) { - if (rettk) - *rettk = tk; + if (rettkl) + *rettkl = tkl; return -5; } - join_viruschan(sptr, tk, type); + join_viruschan(sptr, tkl, target); return -5; } else - return place_host_ban(sptr, tk->ptr.spamf->action, - unreal_decodespace(tk->ptr.spamf->tkl_reason), tk->ptr.spamf->tkl_duration); + return place_host_ban(sptr, tkl->ptr.spamfilter->action, reason, tkl->ptr.spamfilter->tkl_duration); return 0; /* NOTREACHED */ } diff --git a/src/modules/tkldb.c b/src/modules/tkldb.c index 98884eea8..3984ad4d7 100644 --- a/src/modules/tkldb.c +++ b/src/modules/tkldb.c @@ -27,7 +27,8 @@ ModuleHeader MOD_HEADER(tkldb) = { "unrealircd-5", }; -#define TKL_DB_VERSION 1100 +#define TKL_DB_MAGIC 0x10101010 +#define TKL_DB_VERSION 4999 #define TKL_DB_SAVE_EVERY 299 #ifdef DEBUGMODE @@ -45,15 +46,8 @@ ModuleHeader MOD_HEADER(tkldb) = { #define FreeTKLRead() \ do { \ /* Some of these might be NULL */ \ - safefree(tkltype); \ - safefree(usermask); \ - safefree(hostmask); \ - safefree(reason); \ - safefree(setby); \ - safefree(spamf_check); \ - safefree(spamf_expr); \ - safefree(spamf_matchtype); \ - safefree(spamf_tkl_reason); \ + if (tkl) \ + free_tkl(tkl); \ } while(0) #define R_SAFE(x) \ @@ -93,20 +87,12 @@ EVENT(write_tkldb_evt); int write_tkldb(void); int write_tkline(FILE *fd, const char *tmpfname, aTKline *tkl); int read_tkldb(void); -static inline int read_data(FILE *fd, void *buf, size_t len); -static inline int write_data(FILE *fd, void *buf, size_t len); -static int write_str(FILE *fd, char *x); -static int read_str(FILE *fd, char **x); // Globals static ModDataInfo *tkldb_md; -static uint32_t tkl_db_version = TKL_DB_VERSION; +const uint32_t tkl_db_version = TKL_DB_VERSION; struct cfgstruct { char *database; - - // Stored .db might still work with flags instead of actual values (will be corrected on next write) - // This backport stuff will eventually be removed ;] - unsigned int backport_tkl1000; }; static struct cfgstruct cfg; @@ -177,7 +163,6 @@ void setcfg(void) // Default: data/tkl.db cfg.database = strdup("tkl.db"); convert_to_absolute_path(&cfg.database, PERMDATADIR); - cfg.backport_tkl1000 = 0; } void freecfg(void) @@ -262,6 +247,7 @@ int write_tkldb(void) return 0; } + W_SAFE(write_int32(fd, TKL_DB_MAGIC)); W_SAFE(write_data(fd, &tkl_db_version, sizeof(tkl_db_version))); // Count the *-Lines @@ -337,83 +323,68 @@ int write_tkldb(void) return 1; } +/** Write a TKL entry */ int write_tkline(FILE *fd, const char *tmpfname, aTKline *tkl) { - // Since we can't just write 'tkl' in its entirety, we have to get the relevant variables instead - // These will be used to reconstruct the proper internal m_tkl() call ;] char tkltype; - char usermask_subtype[256]; // Might need to prefix something to usermask - char spamf_action; // Storing the action not as unsigned short, but as char is more reliable for the future - uint64_t expire_at, set_at, spamf_tkl_duration; // To prevent 32 vs 64 bit incompatibilies regarding the TS data type(def) + char buf[256]; + /* First, write the common attributes */ tkltype = tkl_typetochar(tkl->type); W_SAFE(write_data(fd, &tkltype, sizeof(tkltype))); // TKL char - // Might be a softban - if (!tkl->ptr.spamf && (tkl->subtype & TKL_SUBTYPE_SOFT)) + W_SAFE(write_str(fd, tkl->set_by)); + W_SAFE(write_int64(fd, tkl->set_at)); + W_SAFE(write_int64(fd, tkl->expire_at)); + + if (TKLIsServerBan(tkl)) { - snprintf(usermask_subtype, sizeof(usermask_subtype), "%%%s", tkl->usermask); - W_SAFE(write_str(fd, usermask_subtype)); // User mask (targets for spamfilter, like 'cpnNPqdatu'), includes % for softbans + char *usermask = tkl->ptr.serverban->usermask; + if (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) + { + snprintf(buf, sizeof(buf), "%%%s", tkl->ptr.serverban->usermask); + usermask = buf; + } + W_SAFE(write_str(fd, usermask)); + W_SAFE(write_str(fd, tkl->ptr.serverban->hostmask)); + W_SAFE(write_str(fd, tkl->ptr.serverban->reason)); } else + if (TKLIsNameBan(tkl)) { - W_SAFE(write_str(fd, tkl->usermask)); + char *hold = tkl->ptr.nameban->hold ? "H" : "*"; + W_SAFE(write_str(fd, hold)); + W_SAFE(write_str(fd, tkl->ptr.nameban->name)); + W_SAFE(write_str(fd, tkl->ptr.nameban->reason)); + } else + if (TKLIsSpamfilter(tkl)) + { + char *match_type = unreal_match_method_valtostr(tkl->ptr.spamfilter->match->type); + char *target = spamfilter_target_inttostring(tkl->ptr.spamfilter->target); + char action = banact_valtochar(tkl->ptr.spamfilter->action); + + W_SAFE(write_str(fd, match_type)); + W_SAFE(write_str(fd, tkl->ptr.spamfilter->match->str)); + W_SAFE(write_str(fd, target)); + W_SAFE(write_data(fd, &action, sizeof(action))); + W_SAFE(write_str(fd, tkl->ptr.spamfilter->tkl_reason)); + W_SAFE(write_int64(fd, tkl->ptr.spamfilter->tkl_duration)); } - W_SAFE(write_str(fd, tkl->hostmask)); // Host mask (action for spamfilter, like 'block') - W_SAFE(write_str(fd, tkl->reason)); // Ban reason (TKL time for spamfilters, in case of *-Line actions) - W_SAFE(write_str(fd, tkl->setby)); - - expire_at = tkl->expire_at; - set_at = tkl->set_at; - W_SAFE(write_data(fd, &expire_at, sizeof(expire_at))); - W_SAFE(write_data(fd, &set_at, sizeof(set_at))); - - if (tkl->ptr.spamf) - { - W_SAFE(write_str(fd, "SPAMF")); // Write a string so we know to expect more when reading the DB - spamf_action = banact_valtochar(tkl->ptr.spamf->action); // Block, GZ-Line, etc; also refer to BAN_ACT_* - W_SAFE(write_data(fd, &spamf_action, sizeof(spamf_action))); - W_SAFE(write_str(fd, tkl->ptr.spamf->tkl_reason)); - spamf_tkl_duration = tkl->ptr.spamf->tkl_duration; - W_SAFE(write_data(fd, &spamf_tkl_duration, sizeof(spamf_tkl_duration))); - W_SAFE(write_str(fd, tkl->ptr.spamf->expr->str)); // Actual expression/regex/etc - - // Expression type (simple/PCRE), see also enum MatchType - if (tkl->ptr.spamf->expr->type == MATCH_PCRE_REGEX) - W_SAFE(write_str(fd, "regex")); - else - W_SAFE(write_str(fd, "simple")); - } else - { - W_SAFE(write_str(fd, "NOSPAMF")); - } return 1; } +/** Read all entries from the TKL db */ int read_tkldb(void) { FILE *fd; - uint64_t i; + aTKline *tkl = NULL; + uint32_t magic = 0; + uint64_t cnt; uint64_t tklcount = 0; - size_t tklcount_tkl1000 = 0; uint32_t version; - int added = 0; - int expired = 0; + int added_cnt = 0; char c; - - // Variables for all TKL types - // Some of them need to be declared and NULL initialised early due to the macro FreeTKLRead() being used by R_SAFE() on error - char *tkltype = NULL; - char *usermask = NULL; - char *hostmask = NULL; - char *reason = NULL; - char *setby = NULL; - - // Some stuff related to spamfilters - char *spamf_check = NULL; - char *spamf_expr = NULL; - char *spamf_matchtype = NULL; - char *spamf_tkl_reason = NULL; + char *str; #ifdef BENCHMARK struct timeval tv_alpha, tv_beta; @@ -421,9 +392,6 @@ int read_tkldb(void) gettimeofday(&tv_alpha, NULL); #endif - ircd_log(LOG_ERROR, "[tkldb] Reading stored *-Lines from '%s'", cfg.database); - sendto_realops("[tkldb] Reading stored *-Lines from '%s'", cfg.database); // Probably won't be seen ever, but just in case ;] - fd = fopen(cfg.database, "rb"); if (!fd) { @@ -438,240 +406,224 @@ int read_tkldb(void) } } - R_SAFE(read_data(fd, &version, sizeof(version))); - if (version > tkl_db_version) + /* The database starts with a "magic value" - unless it's some old version or corrupt */ + R_SAFE(read_data(fd, &magic, sizeof(magic))); + if (magic != TKL_DB_MAGIC) { - // Older DBs should still work with newer versions of this module - config_warn("[tkldb] Database '%s' has a wrong version: expected it to be <= %u but got %u instead", cfg.database, tkl_db_version, version); - if (fclose(fd) != 0) - config_warn("[tkldb] Got an error when trying to close database file '%s' (possible corruption occurred): %s", cfg.database, strerror(errno)); + config_error("[tkldb] Database '%s' uses an old and unsupported format OR is corrupt", cfg.database); + config_status("If you are upgrading from UnrealIRCd 4 (or 5.0.0-alpha1) then we suggest you to " + "delete the existing database. Just keep at least 1 server linked during the upgrade " + "process to preserve your global *LINES and Spamfilters."); + fclose(fd); return 0; } - cfg.backport_tkl1000 = (version <= 1000 ? 1 : 0); - if (cfg.backport_tkl1000) + /* Now do a version check */ + R_SAFE(read_data(fd, &version, sizeof(version))); + if (version < 4999) { - R_SAFE(read_data(fd, &tklcount_tkl1000, sizeof(tklcount_tkl1000))); - tklcount = tklcount_tkl1000; - } else + config_error("[tkldb] Database '%s' uses an unsupport - possibly old - format (%ld).", cfg.database, (long)version); + fclose(fd); + return 0; + } + if (version > tkl_db_version) { - R_SAFE(read_data(fd, &tklcount, sizeof(tklcount))); + config_warn("[tkldb] Database '%s' has version %lu while we only support %lu. Did you just downgrade UnrealIRCd? Sorry this is not suported", + cfg.database, (unsigned long)tkl_db_version, (unsigned long)version); + fclose(fd); + return 0; } - for (i = 1; i <= tklcount; i++) + R_SAFE(read_data(fd, &tklcount, sizeof(tklcount))); + + for (cnt = 0; cnt < tklcount; cnt++) { - int type; - unsigned short subtype; - int parc = 0; + int do_not_add = 0; - // Variables for all TKL types - usermask = NULL; - hostmask = NULL; - reason = NULL; - setby = NULL; - char tklflag; - tkltype = NULL; - uint64_t expire_at, set_at; - time_t expire_at_tkl1000, set_at_tkl1000; - char setTime[100], expTime[100], spamfTime[100]; + tkl = MyMallocEx(sizeof(aTKline)); - // Some stuff related to spamfilters - spamf_check = NULL; - int spamf = 0; - char spamf_action; - unsigned short spamf_actionval; - spamf_tkl_reason = NULL; - time_t spamf_tkl_duration_tkl1000; - uint64_t spamf_tkl_duration; - spamf_expr = NULL; - MatchType matchtype; - spamf_matchtype = NULL; - - int doadd = 1; - aTKline *tkl; - - char *tkllayer[13] = { // Args for m_tkl() - me.name, // 0: Server name - "+", // 1: Direction - NULL, // 2: Type, like G - NULL, // 3: User mask (targets for spamfilter) - NULL, // 4: Host mask (action for spamfilter) - NULL, // 5: Set by who - NULL, // 6: Expiration time - NULL, // 7: Set-at time - NULL, // 8: Reason (TKL time for spamfilters, in case of *-Line actions) - NULL, // 9: Spamfilter only: TKL reason (w/ underscores and all) - NULL, // 10: Spamfilter only: Match type (simple/regex) - NULL, // 11: Spamfilter only: Match string/regex - NULL, // 12: Some functions rely on the post-last entry being NULL =] - }; - - if (cfg.backport_tkl1000) - { - R_SAFE(read_data(fd, &type, sizeof(type))); - tklflag = tkl_typetochar(type); - tkltype = MyMallocEx(2); - tkltype[0] = tklflag; - tkltype[1] = '\0'; - R_SAFE(read_data(fd, &subtype, sizeof(subtype))); // Subtype is kinda redundant so we're not using it past v1000 anymore - } - else { - // No need for tkl_typetochar() on read anymore - R_SAFE(read_data(fd, &tklflag, sizeof(tklflag))); - tkltype = MyMallocEx(2); - tkltype[0] = tklflag; - tkltype[1] = '\0'; - } - - R_SAFE(read_str(fd, &usermask)); - R_SAFE(read_str(fd, &hostmask)); - R_SAFE(read_str(fd, &reason)); - R_SAFE(read_str(fd, &setby)); - - if (cfg.backport_tkl1000) - { - R_SAFE(read_data(fd, &expire_at_tkl1000, sizeof(expire_at_tkl1000))); - R_SAFE(read_data(fd, &set_at_tkl1000, sizeof(set_at_tkl1000))); - expire_at = expire_at_tkl1000; - set_at = set_at_tkl1000; - } - else { - R_SAFE(read_data(fd, &expire_at, sizeof(expire_at))); - R_SAFE(read_data(fd, &set_at, sizeof(set_at))); - } - - R_SAFE(read_str(fd, &spamf_check)); - if (!strcmp(spamf_check, "SPAMF")) - { - spamf = 1; - - if (cfg.backport_tkl1000) - { - R_SAFE(read_data(fd, &spamf_actionval, sizeof(spamf_actionval))); - // FIXME: BUG: spamf_action is not set - } else { - R_SAFE(read_data(fd, &spamf_action, sizeof(spamf_action))); - spamf_actionval = banact_chartoval(spamf_action); - } - - R_SAFE(read_str(fd, &spamf_tkl_reason)); - - if (cfg.backport_tkl1000) - { - R_SAFE(read_data(fd, &spamf_tkl_duration_tkl1000, sizeof(spamf_tkl_duration_tkl1000))); - spamf_tkl_duration = spamf_tkl_duration_tkl1000; - } else { - R_SAFE(read_data(fd, &spamf_tkl_duration, sizeof(spamf_tkl_duration))); - } - - R_SAFE(read_str(fd, &spamf_expr)); - if (cfg.backport_tkl1000) - { - R_SAFE(read_data(fd, &matchtype, sizeof(matchtype))); - if (matchtype == MATCH_PCRE_REGEX) - spamf_matchtype = strdup("regex"); - else - spamf_matchtype = strdup("simple"); - } - else { - // We have better compatibility by just using the strings, since its MATCH_* counterpart might just change in value someday - R_SAFE(read_str(fd, &spamf_matchtype)); - if (!strcmp(spamf_matchtype, "regex")) - matchtype = MATCH_PCRE_REGEX; - else - matchtype = MATCH_SIMPLE; - } - } - - // v1000 still stored local Q-Lines and spamfilters, but those are either added through a .conf or already built-in - if (strchr("qf", tklflag)) + /* First, fetch the TKL type.. */ + R_SAFE(read_data(fd, &c, sizeof(c))); + tkl->type = tkl_chartotype(c); + if (!tkl->type) { + /* We can't continue reading the DB if we don't know the TKL type, + * since we don't know how long the entry will be, we can't skip it. + * This is "impossible" anyway, unless we some day remove a TKL type + * in core UnrealIRCd. In which case we should add some skipping code + * here to gracefully handle that situation ;) + */ + config_error("[tkldb] Invalid type '%c' encountered - STOPPED READING DATABASE!", tkl->type); FreeTKLRead(); - continue; + break; /* we MUST stop reading */ } - // Don't add the TKL if it's expired - if (expire_at != 0 && expire_at <= TStime()) + /* Read the common types (same for all TKLs) */ + R_SAFE(read_str(fd, &tkl->set_by)); + R_SAFE(read_int64(fd, &tkl->set_at)); + R_SAFE(read_int64(fd, &tkl->expire_at)); + + /* Save some CPU... if it's already expired then don't bother adding */ + if (tkl->expire_at != 0 && tkl->expire_at <= TStime()) + do_not_add = 1; + + /* Now handle all the specific types */ + if (TKLIsServerBan(tkl)) { -#ifdef DEBUGMODE - ircd_log(LOG_ERROR, "[tkldb] Not re-adding expired %c:-Line '%s@%s' [%s]", tklflag, usermask, hostmask, reason); - sendto_realops("[tkldb] Not re-adding expired %c-Line '%s@%s' [%s]", tklflag, usermask, hostmask, reason); // Probably won't be seen ever, but just in case ;] -#endif - expired++; + int softban = 0; + + tkl->ptr.serverban = MyMallocEx(sizeof(ServerBan)); + + /* Usermask - but taking into account that the + * %-prefix means a soft ban. + */ + R_SAFE(read_str(fd, &str)); + if (*str == '%') + { + softban = 1; + str++; + } + tkl->ptr.serverban->usermask = strdup(str); + safefree(str); + + /* And the other 2 fields.. */ + R_SAFE(read_str(fd, &tkl->ptr.serverban->hostmask)); + R_SAFE(read_str(fd, &tkl->ptr.serverban->reason)); + + if (find_tkl_serverban(tkl->type, tkl->ptr.serverban->usermask, + tkl->ptr.serverban->hostmask, softban)) + { + do_not_add = 1; + } + + if (!do_not_add) + { + tkl_add_serverban(tkl->type, tkl->ptr.serverban->usermask, + tkl->ptr.serverban->hostmask, + tkl->ptr.serverban->reason, + tkl->set_by, tkl->expire_at, + tkl->set_at, softban, 0); + } + } else + if (TKLIsNameBan(tkl)) + { + tkl->ptr.nameban = MyMallocEx(sizeof(NameBan)); + + R_SAFE(read_str(fd, &str)); + if (*str == 'H') + tkl->ptr.nameban->hold = 1; + safefree(str); + R_SAFE(read_str(fd, &tkl->ptr.nameban->name)); + R_SAFE(read_str(fd, &tkl->ptr.nameban->reason)); + + if (find_tkl_nameban(tkl->type, tkl->ptr.nameban->name, + tkl->ptr.nameban->hold)) + { + do_not_add = 1; + } + + if (!do_not_add) + { + tkl_add_nameban(tkl->type, tkl->ptr.nameban->name, + tkl->ptr.nameban->hold, + tkl->ptr.nameban->reason, + tkl->set_by, tkl->expire_at, + tkl->set_at, 0); + } + } else + if (TKLIsSpamfilter(tkl)) + { + int match_method; + char *err = NULL; + + tkl->ptr.spamfilter = MyMallocEx(sizeof(Spamfilter)); + + /* Match method */ + R_SAFE(read_str(fd, &str)); + match_method = unreal_match_method_strtoval(str); + if (!match_method) + { + config_warn("[tkldb] Unhandled spamfilter match method '%s' -- spamfilter entry not added", str); + do_not_add = 1; + } + safefree(str); + + /* Match string (eg: regex) */ + R_SAFE(read_str(fd, &str)); + tkl->ptr.spamfilter->match = unreal_create_match(match_method, str, &err); + if (!tkl->ptr.spamfilter->match) + { + config_warn("[tkldb] Spamfilter '%s' does not compile: %s -- spamfilter entry not added", str, err); + do_not_add = 1; + } + safefree(str); + + /* Target (eg: cpn) */ + R_SAFE(read_str(fd, &str)); + tkl->ptr.spamfilter->target = spamfilter_gettargets(str, NULL); + if (!tkl->ptr.spamfilter->target) + { + config_warn("[tkldb] Spamfilter '%s' without any valid targets (%s) -- spamfilter entry not added", + tkl->ptr.spamfilter->match->str, str); + do_not_add = 1; + } + safefree(str); + + /* Action */ + R_SAFE(read_data(fd, &c, sizeof(c))); + tkl->ptr.spamfilter->action = banact_chartoval(c); + if (!tkl->ptr.spamfilter->action) + { + config_warn("[tkldb] Spamfilter '%s' without valid action (%c) -- spamfilter entry not added", + tkl->ptr.spamfilter->match->str, c); + do_not_add = 1; + } + + R_SAFE(read_str(fd, &tkl->ptr.spamfilter->tkl_reason)); + R_SAFE(read_int64(fd, &tkl->ptr.spamfilter->tkl_duration)); + + if (find_tkl_spamfilter(tkl->type, tkl->ptr.spamfilter->match->str, + tkl->ptr.spamfilter->action, + tkl->ptr.spamfilter->target)) + { + do_not_add = 1; + } + + if (!do_not_add) + { + tkl_add_spamfilter(tkl->type, tkl->ptr.spamfilter->target, + tkl->ptr.spamfilter->action, + tkl->ptr.spamfilter->match, + tkl->set_by, tkl->expire_at, tkl->set_at, + tkl->ptr.spamfilter->tkl_duration, + tkl->ptr.spamfilter->tkl_reason, + 0); + /* tkl_add_spamfilter() does not copy the match but assign it. + * so set to NULL here to avoid a read-after-free later on. + */ + tkl->ptr.spamfilter->match = NULL; + } + } else + { + config_error("[tkldb] Unhandled type!! TKLDB is missing support for type %ld -- STOPPED reading db entries!", (long)tkl->type); FreeTKLRead(); - continue; + break; /* we MUST stop reading */ } - ircsnprintf(setTime, sizeof(setTime), "%lld", (long long)set_at); - ircsnprintf(expTime, sizeof(expTime), "%lld", (long long)expire_at); + if (!do_not_add) + added_cnt++; - // Build TKL args - // All of these except [8] are the same for all (only odd one is spamfilter) - parc = 9; - tkllayer[2] = tkltype; - tkllayer[3] = usermask; - tkllayer[4] = hostmask; - tkllayer[5] = setby; - tkllayer[6] = expTime; - tkllayer[7] = setTime; - tkllayer[8] = reason; - - if (spamf) - { - parc = 12; - // Make sure this particular *-Line isn't already active somehow - for (tkl = tklines[tkl_hash(tklflag)]; tkl; tkl = tkl->next) - { - // We can assume it's the same spamfilter if all of the following match: spamfilter expression, targets, TKL reason, action, matchtype and TKL duration - if (!strcmp(tkl->ptr.spamf->expr->str, spamf_expr) && !strcmp(tkl->usermask, usermask) && !strcmp(tkl->ptr.spamf->tkl_reason, spamf_tkl_reason) && - tkl->ptr.spamf->action == spamf_action && tkl->ptr.spamf->expr->type == matchtype && tkl->ptr.spamf->tkl_duration == spamf_tkl_duration) - { - doadd = 0; - break; - } - } - - if (doadd) - { - ircsnprintf(spamfTime, sizeof(spamfTime), "%lld", (long long)spamf_tkl_duration); - tkllayer[8] = spamfTime; - tkllayer[9] = spamf_tkl_reason; - tkllayer[10] = spamf_matchtype; - tkllayer[11] = spamf_expr; - } - } - else { - for (tkl = tklines[tkl_hash(tklflag)]; tkl; tkl = tkl->next) - { - if (!strcmp(tkl->usermask, usermask) && !strcmp(tkl->hostmask, hostmask) && !strcmp(tkl->reason, reason) && tkl->expire_at == expire_at) - { - doadd = 0; - break; - } - } - } - - if (doadd) - { - m_tkl(&me, &me, NULL, parc, tkllayer); - added++; - } FreeTKLRead(); } /* If everything went fine, then reading a single byte should cause an EOF error */ if (fread(&c, 1, 1, fd) == 1) - { - ircd_log(LOG_ERROR, "[warning] [tkldb] Database possibly corrupt. Extra data found at end of DB file."); - sendto_realops("[warning] [tkldb] Database possibly corrupt. Extra data found at end of DB file."); - } + config_warn("[tkldb] Database invalid. Extra data found at end of DB file."); fclose(fd); - if (added || expired) - { - ircd_log(LOG_ERROR, "[tkldb] Re-added %d *-Lines (skipped %d expired)", added, expired); - sendto_realops("[tkldb] Re-added %d *-Lines (skipped %d expired)", added, expired); // Probably won't be seen ever, but just in case ;] - } + if (added_cnt) + config_status("[tkldb] Re-added %d *-Lines", added_cnt); + #ifdef BENCHMARK gettimeofday(&tv_beta, NULL); ircd_log(LOG_ERROR, "[tkldb] Benchmark: LOAD DB: %lld microseconds", @@ -680,69 +632,3 @@ int read_tkldb(void) return 1; } -static inline int read_data(FILE *fd, void *buf, size_t len) -{ - if (fread(buf, 1, len, fd) < len) - return 0; - return 1; -} - -static inline int write_data(FILE *fd, void *buf, size_t len) -{ - if (fwrite(buf, 1, len, fd) < len) - return 0; - return 1; -} - -static int write_str(FILE *fd, char *x) -{ - uint16_t len = (x ? strlen(x) : 0); - if (!write_data(fd, &len, sizeof(len))) - return 0; - if (len) - { - if (!write_data(fd, x, len)) - return 0; - } - return 1; -} - -static int read_str(FILE *fd, char **x) -{ - uint16_t len; - size_t len_tkl1000; // len used to be of type size_t, but this has portability problems when writing to/reading from binary files - size_t size; - - *x = NULL; - - if (cfg.backport_tkl1000) - { - if (!read_data(fd, &len_tkl1000, sizeof(len_tkl1000))) - return 0; - len = len_tkl1000; - } else - { - if (!read_data(fd, &len, sizeof(len))) - return 0; - } - - if (!len) - { - *x = strdup(""); // It's safer for m_tkl to work with empty strings instead of NULLs - return 1; - } - - if (len > 10000) - return 0; - - size = len; - *x = MyMallocEx(size + 1); - if (!read_data(fd, *x, size)) - { - MyFree(*x); - *x = NULL; - return 0; - } - (*x)[len] = 0; - return 1; -}