diff --git a/include/dynconf.h b/include/dynconf.h index 54bf5cbee..9bd7ea0a2 100644 --- a/include/dynconf.h +++ b/include/dynconf.h @@ -104,7 +104,7 @@ struct Configuration { char *channel_command_prefix; long handshake_data_flood_amount; long handshake_data_flood_ban_time; - int handshake_data_flood_ban_action; + BanAction *handshake_data_flood_ban_action; struct ChMode modes_on_join; int modes_on_join_set; char *level_on_join; diff --git a/include/h.h b/include/h.h index f950a393f..348192c6d 100644 --- a/include/h.h +++ b/include/h.h @@ -732,10 +732,22 @@ extern void ExtbanDel(Extban *); extern void extban_init(void); extern char *trim_str(char *str, int len); extern MODVAR char *ban_realhost, *ban_virthost, *ban_ip; -extern BanAction banact_stringtoval(const char *s); -extern const char *banact_valtostring(BanAction val); -extern BanAction banact_chartoval(char c); -extern char banact_valtochar(BanAction val); +extern BanAction *parse_ban_action_config(ConfigEntry *ce); +extern int test_ban_action_config(ConfigEntry *ce); +extern void free_single_ban_action(BanAction *action); +extern void free_all_ban_actions(BanAction *actions); +#define safe_free_all_ban_actions(x) do { free_all_ban_actions(x); x = NULL; } while(0) +#define safe_free_single_ban_action(x) do { free_single_ban_action(x); x = NULL; } while(0) +BanAction *duplicate_ban_actions(BanAction *actions); +extern BanActionValue banact_stringtoval(const char *s); +extern const char *banact_valtostring(BanActionValue val); +extern BanActionValue banact_chartoval(char c); +extern char banact_valtochar(BanActionValue val); +extern BanAction *banact_value_to_struct(BanActionValue val); +extern int only_actions_of_type(BanAction *actions, BanActionValue what); +extern int has_actions_of_type(BanAction *actions, BanActionValue what); +extern int only_soft_actions(BanAction *actions); +extern const char *ban_actions_to_string(BanAction *actions); extern int spamfilter_gettargets(const char *s, Client *client); extern char *spamfilter_target_inttostring(int v); extern char *our_strcasestr(const char *haystack, const char *needle); @@ -806,7 +818,7 @@ extern MODVAR TKL *(*tkl_add_banexception)(int type, const char *usermask, const time_t expire_at, time_t set_at, int soft, const char *bantypes, int flags); extern MODVAR TKL *(*tkl_add_nameban)(int type, const char *name, int hold, const char *reason, const char *setby, time_t expire_at, time_t set_at, int flags); -extern MODVAR TKL *(*tkl_add_spamfilter)(int type, unsigned short target, unsigned short action, Match *match, const char *setby, +extern MODVAR TKL *(*tkl_add_spamfilter)(int type, unsigned short target, BanAction *action, Match *match, const char *setby, time_t expire_at, time_t set_at, time_t spamf_tkl_duration, const char *spamf_tkl_reason, int flags); @@ -827,7 +839,7 @@ extern MODVAR TKL *(*find_tkline_match_zap)(Client *cptr); extern MODVAR void (*tkl_stats)(Client *cptr, int type, const char *para, int *cnt); extern MODVAR void (*tkl_sync)(Client *client); extern MODVAR void (*cmd_tkl)(Client *client, MessageTag *recv_mtags, int parc, const char *parv[]); -extern MODVAR int (*place_host_ban)(Client *client, BanAction action, const char *reason, long duration); +extern MODVAR int (*place_host_ban)(Client *client, BanAction *actions, const char *reason, long duration); extern MODVAR int (*match_spamfilter)(Client *client, const char *str_in, int type, const char *cmd, const char *target, int flags, TKL **rettk); extern MODVAR int (*match_spamfilter_mtags)(Client *client, MessageTag *mtags, const char *cmd); extern MODVAR int (*join_viruschan)(Client *client, TKL *tk, int type); diff --git a/include/modules.h b/include/modules.h index 8e32a1bdc..ab79c1e32 100644 --- a/include/modules.h +++ b/include/modules.h @@ -2099,12 +2099,12 @@ int hooktype_sasl_result(Client *client, int success); * This is called for automated bans such as spamfilter hits, flooding, etc. * This hook can be used to prevent the ban, or as used by the authprompt to delay it. * @param client The client that should be banned - * @param action The TKL type, such as BAN_ACT_GLINE + * @param action The actions to take, one of BAN_ACT_* * @param reason The ban reason * @param duration The duration of the ban, 0 for permanent ban * @return The magic value 99 is used to exempt the user (=do not ban!), otherwise the ban is added. */ -int hooktype_place_host_ban(Client *client, int action, const char *reason, long duration); +int hooktype_place_host_ban(Client *client, BanActionValue action, const char *reason, long duration); /** Called when a TKL ban is hit by this user (function prototype for HOOKTYPE_FIND_TKLINE_MATCH). * This is called when an existing TKL entry is hit by the user. diff --git a/include/struct.h b/include/struct.h index 2791793f0..e0b4b8f10 100644 --- a/include/struct.h +++ b/include/struct.h @@ -1111,7 +1111,7 @@ struct Secret { #define SPAMFLAG_NOWARN 0x0001 /* Ban actions. These must be ordered by severity (!) */ -typedef enum BanAction { +typedef enum BanActionValue { BAN_ACT_GZLINE =1100, BAN_ACT_GLINE =1000, BAN_ACT_SOFT_GLINE = 950, @@ -1132,7 +1132,22 @@ typedef enum BanAction { BAN_ACT_SOFT_BLOCK = 150, BAN_ACT_WARN = 100, BAN_ACT_SOFT_WARN = 50, -} BanAction; + BAN_ACT_SET = 40, +} BanActionValue; + +typedef enum VarActionValue { + VAR_ACT_SET = 1, + VAR_ACT_INCREASE = 2, +} VarActionValue; + +typedef struct BanAction BanAction; +struct BanAction { + BanAction *prev, *next; + BanActionValue action; + char *var; + int value; + VarActionValue var_action; +}; #define IsSoftBanAction(x) ((x == BAN_ACT_SOFT_GLINE) || (x == BAN_ACT_SOFT_KLINE) || \ (x == BAN_ACT_SOFT_SHUN) || (x == BAN_ACT_SOFT_KILL) || \ @@ -1159,7 +1174,7 @@ struct NameBan { /** Spamfilter sub-struct of TKL entry (Spamfilter) */ struct Spamfilter { unsigned short target; - BanAction action; /**< Ban action, see BAN_ACT* */ + BanAction *action; /**< Ban action */ Match *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 */ @@ -1940,8 +1955,8 @@ struct ConfigItem_link { struct ConfigItem_ban { ConfigItem_ban *prev, *next; ConfigFlag_ban flag; - char *mask, *reason; - unsigned short action; + char *mask, *reason; + BanAction *action; }; struct ConfigItem_deny_dcc { diff --git a/src/api-efunctions.c b/src/api-efunctions.c index dd38789e1..cee0a698f 100644 --- a/src/api-efunctions.c +++ b/src/api-efunctions.c @@ -55,7 +55,7 @@ TKL *(*tkl_add_serverban)(int type, const char *usermask, const char *hostmask, time_t expire_at, time_t set_at, int soft, int flags); TKL *(*tkl_add_nameban)(int type, const char *name, int hold, const char *reason, const char *setby, time_t expire_at, time_t set_at, int flags); -TKL *(*tkl_add_spamfilter)(int type, unsigned short target, unsigned short action, Match *match, const char *setby, +TKL *(*tkl_add_spamfilter)(int type, unsigned short target, BanAction *action, Match *match, const char *setby, time_t expire_at, time_t set_at, time_t spamf_tkl_duration, const char *spamf_tkl_reason, int flags); @@ -72,7 +72,7 @@ TKL *(*find_tkline_match_zap)(Client *client); void (*tkl_stats)(Client *client, int type, const char *para, int *cnt); void (*tkl_sync)(Client *client); void (*cmd_tkl)(Client *client, MessageTag *mtags, int parc, const char *parv[]); -int (*place_host_ban)(Client *client, BanAction action, const char *reason, long duration); +int (*place_host_ban)(Client *client, BanAction *action, const char *reason, long duration); int (*match_spamfilter)(Client *client, const char *str_in, int type, const char *cmd, const char *target, int flags, TKL **rettk); int (*match_spamfilter_mtags)(Client *client, MessageTag *mtags, const char *cmd); int (*join_viruschan)(Client *client, TKL *tk, int type); diff --git a/src/conf.c b/src/conf.c index e64d0f4ee..666d97d6b 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1648,6 +1648,7 @@ void free_iConf(Configuration *i) safe_free(i->helpchan); safe_free(i->stats_server); safe_free(i->sasl_server); + safe_free_all_ban_actions(i->handshake_data_flood_ban_action); // anti-flood: for (f = i->floodsettings; f; f = f_next) { @@ -1710,7 +1711,8 @@ void config_setdefaultsettings(Configuration *i) /* - everyone */ i->throttle_count = 3; i->throttle_period = 60; /* throttle protection: max 3 per 60s */ i->handshake_data_flood_amount = 4096; - i->handshake_data_flood_ban_action = BAN_ACT_ZLINE; + i->handshake_data_flood_ban_action = safe_alloc(sizeof(BanAction)); + i->handshake_data_flood_ban_action->action = BAN_ACT_ZLINE; i->handshake_data_flood_ban_time = 600; // (targetflood is in the targetflood module) /* - known-users */ @@ -2462,6 +2464,7 @@ void config_rehash() { safe_free(ban_ptr->mask); safe_free(ban_ptr->reason); + free_all_ban_actions(ban_ptr->action); DelListItem(ban_ptr, conf_ban); safe_free(ban_ptr); } @@ -6901,7 +6904,7 @@ int _conf_ban(ConfigFile *conf, ConfigEntry *ce) else if (!strcmp(cep->name, "reason")) safe_strdup(ca->reason, cep->value); else if (!strcmp(cep->name, "action")) - ca->action = banact_stringtoval(cep->value); + ca->action = parse_ban_action_config(cep); } AddListItem(ca, conf_ban); return 0; @@ -7000,13 +7003,7 @@ int _test_ban(ConfigFile *conf, ConfigEntry *ce) cep->line_number, "ban::action"); } has_action = 1; - if (!banact_stringtoval(cep->value)) - { - config_error("%s:%i: ban::action has unknown action type '%s'", - cep->file->filename, cep->line_number, - cep->value); - errors++; - } + errors += test_ban_action_config(cep); } } @@ -7800,8 +7797,8 @@ int _conf_set(ConfigFile *conf, ConfigEntry *ce) tempiConf.handshake_data_flood_amount = config_checkval(cep4->value, CFG_SIZE); else if (!strcmp(cep4->name, "ban-time")) tempiConf.handshake_data_flood_ban_time = config_checkval(cep4->value, CFG_TIME); - else if (!strcmp(cep4->name, "ban-action")) - tempiConf.handshake_data_flood_ban_action = banact_stringtoval(cep4->value); + else if (!strcmp(cep4->name, "ban-action") || !strcmp(cep4->name, "action")) + tempiConf.handshake_data_flood_ban_action = parse_ban_action_config(cep4); } } else if (!strcmp(ceppp->name, "away-flood")) @@ -8718,16 +8715,10 @@ int _test_set(ConfigFile *conf, ConfigEntry *ce) errors++; } } else - if (!strcmp(cep4->name, "ban-action")) + if (!strcmp(cep4->name, "ban-action") || !strcmp(cep4->name, "action")) { CheckNull(cep4); - if (!banact_stringtoval(cep4->value)) - { - config_error("%s:%i: set::anti-flood::handshake-data-flood::ban-action has unknown action type '%s'", - cep4->file->filename, cep4->line_number, - cep4->value); - errors++; - } + errors += test_ban_action_config(cep4); } else if (!strcmp(cep4->name, "ban-time")) { diff --git a/src/json.c b/src/json.c index 0c433b208..79e9b307c 100644 --- a/src/json.c +++ b/src/json.c @@ -571,7 +571,7 @@ void json_expand_tkl(json_t *root, const char *key, TKL *tkl, int detail) { json_object_set_new(j, "name", json_string_unreal(tkl->ptr.spamfilter->match->str)); json_object_set_new(j, "match_type", json_string_unreal(unreal_match_method_valtostr(tkl->ptr.spamfilter->match->type))); - json_object_set_new(j, "ban_action", json_string_unreal(banact_valtostring(tkl->ptr.spamfilter->action))); + json_object_set_new(j, "ban_action", json_string_unreal(ban_actions_to_string(tkl->ptr.spamfilter->action))); json_object_set_new(j, "ban_duration", json_integer(tkl->ptr.spamfilter->tkl_duration)); json_object_set_new(j, "ban_duration_string", json_string_unreal(pretty_time_val_r(buf, sizeof(buf), tkl->ptr.spamfilter->tkl_duration))); json_object_set_new(j, "spamfilter_targets", json_string_unreal(spamfilter_target_inttostring(tkl->ptr.spamfilter->target))); diff --git a/src/misc.c b/src/misc.c index 1b1f143a9..ca5609d01 100644 --- a/src/misc.c +++ b/src/misc.c @@ -79,6 +79,7 @@ static BanActTable banacttable[] = { { BAN_ACT_SOFT_VIRUSCHAN,'V', "soft-viruschan" }, { BAN_ACT_WARN, 'w', "warn" }, { BAN_ACT_SOFT_WARN, 'W', "soft-warn" }, + { BAN_ACT_SET, '1', "set" }, { 0, 0, 0 } }; @@ -781,8 +782,100 @@ int valid_vhost(const char *userhost) /*|| BAN ACTION ROUTINES FOLLOW ||*/ +int test_ban_action_config_helper(ConfigEntry *ce, const char *name, const char *value) +{ + int errors = 0; + BanActionValue action; + + action = banact_stringtoval(name); + if (!action) + { + config_error("%s:%d: unknown action: %s", + ce->file->filename, ce->line_number, name); + errors++; + } else + { + // TODO: parse values + } + + return errors; +} + +/** Test an action item in the config. + * @param ce The config entry to parse + * @returns Number of errors, so 0 means OK. + */ +int test_ban_action_config(ConfigEntry *ce) +{ + ConfigEntry *cep; + int errors = 0; + + if (ce->items && !ce->value) + { + /* action { xxx; } */ + for (cep = ce->items; cep; cep = cep->next) + errors += test_ban_action_config_helper(cep, cep->name, cep->value); + } else + if (!ce->value) + { + config_error("%s:%d: action has no value", ce->file->filename, ce->line_number); + errors++; + } else + { + /* action xxx; */ + errors += test_ban_action_config_helper(ce, ce->value, NULL); + } + + return errors; +} + +BanAction *parse_ban_action_config_helper(const char *name, const char *value) +{ + int errors = 0; + BanAction *action = safe_alloc(sizeof(BanAction)); + + action->action = banact_stringtoval(name); + if (!action->action) + abort(); /* impossible, is config tested earlier */ + + // TODO: parse values + + return action; +} + +/** Parse an action item in the config. + * @param ce The config entry to parse + * @returns BanAction item (can be more than one, in the linked list) + */ +BanAction *parse_ban_action_config(ConfigEntry *ce) +{ + ConfigEntry *cep; + BanAction *action_list = NULL; + BanAction *action; + + if (ce->items && !ce->value) + { + /* action { xxx; } */ + for (cep = ce->items; cep; cep = cep->next) + { + action = parse_ban_action_config_helper(cep->name, cep->value); + if (action) + AppendListItem(action, action_list); + } + } else + if (ce->value) + { + /* action xxx; */ + action = parse_ban_action_config_helper(ce->value, NULL); + if (action) + AppendListItem(action, action_list); + } + + return action_list; +} + /** Converts a banaction string (eg: "kill") to an integer value (eg: BAN_ACT_KILL) */ -BanAction banact_stringtoval(const char *s) +BanActionValue banact_stringtoval(const char *s) { BanActTable *b; @@ -793,7 +886,7 @@ BanAction banact_stringtoval(const char *s) } /** Converts a banaction character (eg: 'K') to an integer value (eg: BAN_ACT_KILL) */ -BanAction banact_chartoval(char c) +BanActionValue banact_chartoval(char c) { BanActTable *b; @@ -804,7 +897,7 @@ BanAction banact_chartoval(char c) } /** Converts a banaction value (eg: BAN_ACT_KILL) to a character value (eg: 'k') */ -char banact_valtochar(BanAction val) +char banact_valtochar(BanActionValue val) { BanActTable *b; @@ -815,7 +908,7 @@ char banact_valtochar(BanAction val) } /** Converts a banaction value (eg: BAN_ACT_KLINE) to a string (eg: "kline") */ -const char *banact_valtostring(BanAction val) +const char *banact_valtostring(BanActionValue val) { BanActTable *b; @@ -825,6 +918,12 @@ const char *banact_valtostring(BanAction val) return "UNKNOWN"; } +BanAction *banact_value_to_struct(BanActionValue val) +{ + BanAction *action = safe_alloc(sizeof(BanAction)); + action->action = val; + return action; +} /*|| BAN TARGET ROUTINES FOLLOW ||*/ /** Extract target flags from string 's'. */ @@ -2729,3 +2828,92 @@ const char *command_issued_by_rpc(MessageTag *mtags) return m->value; return NULL; } + +/** Check if action is only of type 'what', eg if they are all BAN_ACT_WARN */ +int only_actions_of_type(BanAction *actions, BanActionValue what) +{ + for (; actions; actions = actions->next) + if (actions->action != what) + return 0; + return 1; +} + +/** Check if action 'what' is in any of 'actions' */ +int has_actions_of_type(BanAction *actions, BanActionValue what) +{ + for (; actions; actions = actions->next) + if (actions->action == what) + return 1; + return 0; +} + +/** Check if only soft ban actions */ +int only_soft_actions(BanAction *actions) +{ + for (; actions; actions = actions->next) + if (!IsSoftBanAction(actions->action)) + return 0; + return 1; +} + +/** Turn a linked list BanAction * into a string of actions. + * Returns something like "gline" or "set, gline" + */ +const char *ban_actions_to_string(BanAction *actions) +{ + static char buf[512]; + const char *s; + BanAction *action; + *buf = '\0'; + + for (action = actions; action; action = action->next) + { + s = banact_valtostring(action->action); + if (!s) + s = "???"; + strlcat(buf, s, sizeof(buf)); + strlcat(buf, ", ", sizeof(buf)); + } + + /* Cut off trailing ", " */ + if (*buf) + buf[strlen(buf)-2] = '\0'; + + return buf; +} + +void free_single_ban_action(BanAction *action) +{ + safe_free(action->var); + safe_free(action); +} + +void free_all_ban_actions(BanAction *actions) +{ + BanAction *next; + for (; actions; actions = next) + { + next = actions->next; + free_single_ban_action(actions); + } +} + +/** Duplicate an entire BanAction linked list (deep copy) */ +BanAction *duplicate_ban_actions(BanAction *actions) +{ + BanAction *newlist = NULL; + BanAction *action, *n; + + for (action = actions; action; action = action->next) + { + n = safe_alloc(sizeof(BanAction)); + // This matches the struct.h order: + n->action = action->action; + safe_strdup(n->var, action->var); + n->value = action->value; + n->var_action = action->var_action; + /* And add to the list, maintain action ordering */ + AppendListItem(n, newlist); + } + return newlist; +} diff --git a/src/modules/antimixedutf8.c b/src/modules/antimixedutf8.c index a31f1a17f..6d7d54a13 100644 --- a/src/modules/antimixedutf8.c +++ b/src/modules/antimixedutf8.c @@ -57,7 +57,7 @@ ModuleHeader MOD_HEADER struct { int score; - BanAction ban_action; + BanAction *ban_action; char *ban_reason; long ban_time; SecurityGroup *except; @@ -685,24 +685,25 @@ CMD_OVERRIDE_FUNC(override_msg) score = lookalikespam_score(text, NULL, 0); if ((score >= cfg.score) && !find_tkl_exception(TKL_ANTIMIXEDUTF8, client)) { - /* Re-run with a log buffer... */ char logbuf[512]; + int retval; + + /* Re-run with a log buffer and log... */ logbuf[0] = '\0'; lookalikespam_score(text, logbuf, sizeof(logbuf)); - /* And log... */ unreal_log(ULOG_INFO, "antimixedutf8", "ANTIMIXEDUTF8_HIT", client, "[antimixedutf8] Client $client.details hit score $score -- taking action. Mixed scripts detected: $scripts", log_data_integer("score", score), log_data_string("scripts", logbuf)); - if ((cfg.ban_action == BAN_ACT_BLOCK) || - ((cfg.ban_action == BAN_ACT_SOFT_BLOCK) && !IsLoggedIn(client))) + + /* Take the action */ + retval = place_host_ban(client, cfg.ban_action, cfg.ban_reason, cfg.ban_time); + if (retval == 1) + return; + if (retval == BAN_ACT_BLOCK) { sendnotice(client, "%s", cfg.ban_reason); return; - } else { - if (place_host_ban(client, cfg.ban_action, cfg.ban_reason, cfg.ban_time)) - return; - /* a return value of 0 means the user is exempted, so fallthrough.. */ } } @@ -749,7 +750,7 @@ static void init_config(void) /* Default values */ cfg.score = 10; safe_strdup(cfg.ban_reason, "Possible mixed character spam"); - cfg.ban_action = BAN_ACT_BLOCK; + cfg.ban_action = banact_value_to_struct(BAN_ACT_BLOCK); cfg.ban_time = 60 * 60 * 4; /* irrelevant for block, but some default for others */ } @@ -757,6 +758,7 @@ static void free_config(void) { safe_free(cfg.ban_reason); free_security_group(cfg.except); + safe_free_all_ban_actions(cfg.ban_action); memset(&cfg, 0, sizeof(cfg)); /* needed! */ } @@ -792,12 +794,7 @@ int antimixedutf8_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *er } else if (!strcmp(cep->name, "ban-action")) { - if (!banact_stringtoval(cep->value)) - { - config_error("%s:%i: set::antimixedutf8::ban-action: unknown action '%s'", - cep->file->filename, cep->line_number, cep->value); - errors++; - } + errors += test_ban_action_config(cep); } else if (!strcmp(cep->name, "ban-reason")) { @@ -838,7 +835,9 @@ int antimixedutf8_config_run(ConfigFile *cf, ConfigEntry *ce, int type) } else if (!strcmp(cep->name, "ban-action")) { - cfg.ban_action = banact_stringtoval(cep->value); + if (cfg.ban_action) + safe_free_all_ban_actions(cfg.ban_action); + cfg.ban_action = parse_ban_action_config(cep); } else if (!strcmp(cep->name, "ban-reason")) { diff --git a/src/modules/antirandom.c b/src/modules/antirandom.c index 2846124bf..8ea88e377 100644 --- a/src/modules/antirandom.c +++ b/src/modules/antirandom.c @@ -499,7 +499,7 @@ struct { struct { int threshold; - BanAction ban_action; + BanAction *ban_action; char *ban_reason; long ban_time; int convert_to_lowercase; @@ -563,6 +563,7 @@ static void free_config(void) { safe_free(cfg.ban_reason); free_security_group(cfg.except); + safe_free_all_ban_actions(cfg.ban_action); memset(&cfg, 0, sizeof(cfg)); /* needed! */ } @@ -612,13 +613,8 @@ int antirandom_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) } else if (!strcmp(cep->name, "ban-action")) { - if (!banact_stringtoval(cep->value)) - { - config_error("%s:%i: set::antirandom::ban-action: unknown action '%s'", - cep->file->filename, cep->line_number, cep->value); - errors++; - } else - req.ban_action = 1; + req.ban_action = 1; + errors += test_ban_action_config(cep); } else if (!strcmp(cep->name, "ban-reason")) { @@ -678,7 +674,7 @@ int antirandom_config_run(ConfigFile *cf, ConfigEntry *ce, int type) } else if (!strcmp(cep->name, "ban-action")) { - cfg.ban_action = banact_stringtoval(cep->value); + cfg.ban_action = parse_ban_action_config(cep); } else if (!strcmp(cep->name, "ban-reason")) { @@ -870,21 +866,20 @@ int antirandom_preconnect(Client *client) score = get_spam_score(client); if (score > cfg.threshold) { - if (cfg.ban_action == BAN_ACT_WARN) + if (has_actions_of_type(cfg.ban_action, BAN_ACT_WARN)) { unreal_log(ULOG_INFO, "antirandom", "ANTIRANDOM_DENIED_USER", client, "[antirandom] would have denied access to user with score $score: $client.details:$client.user.realname", log_data_integer("score", score)); - return HOOK_CONTINUE; - } - if (cfg.show_failedconnects) + } else + if (cfg.show_failedconnects) // FIXME: this is not entirely correct, with like soft actions or var setting, etc! { unreal_log(ULOG_INFO, "antirandom", "ANTIRANDOM_DENIED_USER", client, "[antirandom] denied access to user with score $score: $client.details:$client.user.realname", log_data_integer("score", score)); } - place_host_ban(client, cfg.ban_action, cfg.ban_reason, cfg.ban_time); - return HOOK_DENY; + if (place_host_ban(client, cfg.ban_action, cfg.ban_reason, cfg.ban_time)) + return HOOK_DENY; } return HOOK_CONTINUE; } @@ -911,7 +906,7 @@ static int is_exempt(Client *client) return 1; /* Soft ban and logged in? */ - if (IsSoftBanAction(cfg.ban_action) && IsLoggedIn(client)) + if (only_soft_actions(cfg.ban_action) && IsLoggedIn(client)) return 1; return 0; diff --git a/src/modules/blacklist.c b/src/modules/blacklist.c index d29b3c2c7..6111a548b 100644 --- a/src/modules/blacklist.c +++ b/src/modules/blacklist.c @@ -64,7 +64,7 @@ struct Blacklist { char *name; BlacklistBackendType backend_type; BlacklistBackend *backend; - int action; + BanAction *action; long ban_time; char *reason; SecurityGroup *except; @@ -80,7 +80,7 @@ struct BLUser { int is_ipv6; int refcnt; /* The following save_* fields are used by softbans: */ - int save_action; + BanAction *save_action; long save_tkltime; char *save_opernotice; char *save_reason; @@ -237,6 +237,7 @@ void delete_blacklist_block(Blacklist *e) safe_free(e->name); safe_free(e->reason); + safe_free_all_ban_actions(e->action); free_security_group(e->except); safe_free(e); @@ -379,12 +380,7 @@ int blacklist_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) continue; } has_action = 1; - if (!banact_stringtoval(cep->value)) - { - config_error("%s:%i: blacklist::action has unknown action type '%s'", - cep->file->filename, cep->line_number, cep->value); - errors++; - } + errors += test_ban_action_config(cep); } else if (!strcmp(cep->name, "ban-time")) { @@ -467,8 +463,6 @@ int blacklist_config_run(ConfigFile *cf, ConfigEntry *ce, int type) d = safe_alloc(sizeof(Blacklist)); safe_strdup(d->name, ce->value); /* set some defaults */ - d->action = BAN_ACT_KILL; - safe_strdup(d->reason, "Your IP is on a DNS Blacklist"); d->ban_time = 3600; /* assume dns for now ;) */ @@ -529,7 +523,7 @@ int blacklist_config_run(ConfigFile *cf, ConfigEntry *ce, int type) } else if (!strcmp(cep->name, "action")) { - d->action = banact_stringtoval(cep->value); + d->action = parse_ban_action_config(cep); } else if (!strcmp(cep->name, "ban-time")) { @@ -692,6 +686,7 @@ void blacklist_free_bluser_if_able(BLUser *bl) safe_free(bl->save_opernotice); safe_free(bl->save_reason); + free_all_ban_actions(bl->save_action); safe_free(bl); } @@ -747,7 +742,7 @@ int blacklist_parse_reply(struct hostent *he, int entry) * from blacklist_preconnect() for softbans that need to be delayed * as to give the user the opportunity to do SASL Authentication. */ -int blacklist_action(Client *client, char *opernotice, BanAction ban_action, char *ban_reason, long ban_time, +int blacklist_action(Client *client, char *opernotice, BanAction *ban_action, char *ban_reason, long ban_time, char *blacklist, char *blacklist_dns_name, int blacklist_dns_reply) { unreal_log_raw(ULOG_INFO, "blacklist", "BLACKLIST_HIT", client, @@ -755,11 +750,9 @@ int blacklist_action(Client *client, char *opernotice, BanAction ban_action, cha log_data_string("blacklist_name", blacklist), log_data_string("blacklist_dns_name", blacklist_dns_name), log_data_integer("blacklist_dns_reply", blacklist_dns_reply), - log_data_string("ban_action", banact_valtostring(ban_action)), + log_data_string("ban_action", ban_actions_to_string(ban_action)), log_data_string("ban_reason", ban_reason), log_data_integer("ban_time", ban_time)); - if (ban_action == BAN_ACT_WARN) - return 0; return place_host_ban(client, ban_action, ban_reason, ban_time); } @@ -797,10 +790,10 @@ void blacklist_hit(Client *client, Blacklist *bl, int reply) buildvarstring(bl->reason, banbuf, sizeof(banbuf), name, value); - if (IsSoftBanAction(bl->action) && blu) + if (only_soft_actions(bl->action) && blu) { /* For soft bans, delay the action until later (so user can do SASL auth) */ - blu->save_action = bl->action; + blu->save_action = duplicate_ban_actions(bl->action); blu->save_tkltime = bl->ban_time; safe_strdup(blu->save_opernotice, opernotice); safe_strdup(blu->save_reason, banbuf); diff --git a/src/modules/message.c b/src/modules/message.c index bcea1e7ea..bc403627a 100644 --- a/src/modules/message.c +++ b/src/modules/message.c @@ -616,14 +616,10 @@ int ban_version(Client *client, const char *text) if ((ban = find_ban(NULL, ctcp_reply, CONF_BAN_VERSION))) { - if (IsSoftBanAction(ban->action) && IsLoggedIn(client)) - return 0; /* soft ban does not apply to us, we are logged in */ - if (find_tkl_exception(TKL_BAN_VERSION, client)) return 0; /* we are exempt */ - place_host_ban(client, ban->action, ban->reason, BAN_VERSION_TKL_TIME); - return 1; + return place_host_ban(client, ban->action, ban->reason, BAN_VERSION_TKL_TIME); } return 0; diff --git a/src/modules/nick.c b/src/modules/nick.c index fe4f7b590..3747feab7 100644 --- a/src/modules/nick.c +++ b/src/modules/nick.c @@ -1051,8 +1051,9 @@ int _register_user(Client *client) spamfilter_build_user_string(spamfilter_user, client->name, client); if (match_spamfilter(client, spamfilter_user, SPAMF_USER, NULL, NULL, 0, &savetkl)) { - if (savetkl && ((savetkl->ptr.spamfilter->action == BAN_ACT_VIRUSCHAN) || - (savetkl->ptr.spamfilter->action == BAN_ACT_SOFT_VIRUSCHAN))) + if (savetkl && + (has_actions_of_type(savetkl->ptr.spamfilter->action, BAN_ACT_VIRUSCHAN) || + has_actions_of_type(savetkl->ptr.spamfilter->action, BAN_ACT_SOFT_VIRUSCHAN))) { /* 'viruschan' action: * Continue with registering the client, and at the end diff --git a/src/modules/rpc/spamfilter.c b/src/modules/rpc/spamfilter.c index 16701912b..132827c15 100644 --- a/src/modules/rpc/spamfilter.c +++ b/src/modules/rpc/spamfilter.c @@ -100,9 +100,10 @@ RPC_CALL_FUNC(rpc_spamfilter_list) /* Shared code for selecting a spamfilter, for .add/.del/get */ int spamfilter_select_criteria(Client *client, json_t *request, json_t *params, const char **name, int *match_type, - int *targets, char *targetbuf, size_t targetbuflen, BanAction *action, char *actionbuf) + int *targets, char *targetbuf, size_t targetbuflen, BanActionValue *action, char *actionbuf) { const char *str; + int actval; *name = json_object_get_string(params, "name"); if (!*name) @@ -161,7 +162,7 @@ RPC_CALL_FUNC(rpc_spamfilter_get) int type = TKL_SPAMF|TKL_GLOBAL; const char *name; TKL *tkl; - BanAction action; + BanActionValue action; int match_type = 0; int targets = 0; char targetbuf[64]; @@ -193,7 +194,7 @@ RPC_CALL_FUNC(rpc_spamfilter_add) time_t ban_duration = 0; TKL *tkl; Match *m; - BanAction action; + BanActionValue action; int match_type = 0; int targets = 0; char targetbuf[64]; @@ -246,7 +247,7 @@ RPC_CALL_FUNC(rpc_spamfilter_add) return; } - tkl = tkl_add_spamfilter(type, targets, action, m, set_by, 0, TStime(), + tkl = tkl_add_spamfilter(type, targets, banact_value_to_struct(action), m, set_by, 0, TStime(), ban_duration, reason, 0); if (!tkl) @@ -270,7 +271,7 @@ RPC_CALL_FUNC(rpc_spamfilter_del) const char *name; const char *set_by; TKL *tkl; - BanAction action; + BanActionValue action; int match_type = 0; int targets = 0; char targetbuf[64]; diff --git a/src/modules/stats.c b/src/modules/stats.c index 7dcdce6f2..057b5f8fc 100644 --- a/src/modules/stats.c +++ b/src/modules/stats.c @@ -868,7 +868,7 @@ int stats_set(Client *client, const char *para) sendtxtnumeric(client, "link::bind-ip: %s", LINK_BINDIP); sendtxtnumeric(client, "anti-flood::connect-flood: %d per %s", THROTTLING_COUNT, pretty_time_val(THROTTLING_PERIOD)); sendtxtnumeric(client, "anti-flood::handshake-data-flood::amount: %ld bytes", iConf.handshake_data_flood_amount); - sendtxtnumeric(client, "anti-flood::handshake-data-flood::ban-action: %s", banact_valtostring(iConf.handshake_data_flood_ban_action)); + sendtxtnumeric(client, "anti-flood::handshake-data-flood::ban-action: %s", banact_valtostring(iConf.handshake_data_flood_ban_action->action)); sendtxtnumeric(client, "anti-flood::handshake-data-flood::ban-time: %s", pretty_time_val(iConf.handshake_data_flood_ban_time)); /* set::anti-flood */ diff --git a/src/modules/tkl.c b/src/modules/tkl.c index 3000f2d95..68e4c9b5d 100644 --- a/src/modules/tkl.c +++ b/src/modules/tkl.c @@ -68,7 +68,7 @@ TKL *_tkl_add_banexception(int type, char *usermask, char *hostmask, SecurityGro time_t expire_at, time_t set_at, int soft, char *bantypes, int flags); TKL *_tkl_add_nameban(int type, char *name, int hold, char *reason, char *set_by, time_t expire_at, time_t set_at, int flags); -TKL *_tkl_add_spamfilter(int type, unsigned short target, BanAction action, Match *match, char *set_by, +TKL *_tkl_add_spamfilter(int type, unsigned short target, BanAction *action, Match *match, char *set_by, time_t expire_at, time_t set_at, time_t spamf_tkl_duration, char *spamf_tkl_reason, int flags); @@ -88,7 +88,7 @@ TKL *_find_tkline_match_zap(Client *client); void _tkl_stats(Client *client, int type, const char *para, int *cnt); void _tkl_sync(Client *client); CMD_FUNC(_cmd_tkl); -int _place_host_ban(Client *client, BanAction action, char *reason, long duration); +int _place_host_ban(Client *client, BanAction *action, char *reason, long duration); int _match_spamfilter(Client *client, const char *str_in, int type, const char *cmd, const char *target, int flags, TKL **rettk); int _match_spamfilter_mtags(Client *client, MessageTag *mtags, char *cmd); int check_mtag_spamfilters_present(void); @@ -97,13 +97,13 @@ void _spamfilter_build_user_string(char *buf, char *nick, Client *client); int _match_user(const char *rmask, Client *client, int options); int _unreal_match_iplist(Client *client, NameList *l); int _match_user_extended_server_ban(const char *banstr, Client *client); -void ban_target_to_tkl_layer(BanTarget ban_target, BanAction action, Client *client, const char **tkl_username, const char **tkl_hostname); +void ban_target_to_tkl_layer(BanTarget ban_target, BanActionValue action, Client *client, const char **tkl_username, const char **tkl_hostname); int _tkl_ip_hash(char *ip); int _tkl_ip_hash_type(int type); TKL *_find_tkl_serverban(int type, char *usermask, char *hostmask, int softban); TKL *_find_tkl_banexception(int type, char *usermask, char *hostmask, int softban); TKL *_find_tkl_nameban(int type, char *name, int hold); -TKL *_find_tkl_spamfilter(int type, char *match_string, BanAction action, unsigned short target); +TKL *_find_tkl_spamfilter(int type, char *match_string, BanActionValue action, unsigned short target); int _find_tkl_exception(int ban_type, Client *client); int _server_ban_parse_mask(Client *client, int add, char type, const char *str, char **usermask_out, char **hostmask_out, int *soft, const char **error); int _server_ban_exception_parse_mask(Client *client, int add, const char *bantypes, const char *str, char **usermask_out, char **hostmask_out, int *soft, const char **error); @@ -317,14 +317,25 @@ int tkl_config_test_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type, int *e } continue; } - if (!cep->value) + else if (!strcmp(cep->name, "action")) + { + if (has_action) + { + config_warn_duplicate(cep->file->filename, + cep->line_number, "spamfilter::action"); + continue; + } + has_action = 1; + errors += test_ban_action_config(cep); + } + else if (!cep->value) { config_error_empty(cep->file->filename, cep->line_number, "spamfilter", cep->name); errors++; continue; } - if (!strcmp(cep->name, "reason")) + else if (!strcmp(cep->name, "reason")) { if (has_reason) { @@ -346,22 +357,6 @@ int tkl_config_test_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type, int *e has_match = 1; match = cep->value; } - else if (!strcmp(cep->name, "action")) - { - if (has_action) - { - config_warn_duplicate(cep->file->filename, - cep->line_number, "spamfilter::action"); - continue; - } - has_action = 1; - if (!banact_stringtoval(cep->value)) - { - config_error("%s:%i: spamfilter::action has unknown action type '%s'", - cep->file->filename, cep->line_number, cep->value); - errors++; - } - } else if (!strcmp(cep->name, "ban-time")) { if (has_bantime) @@ -482,7 +477,8 @@ int tkl_config_run_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type) char *word = NULL; time_t bantime = tempiConf.spamfilter_ban_time; char *banreason = tempiConf.spamfilter_ban_reason; - int action = 0, target = 0; + BanAction *action; + int target = 0; int match_type = 0; Match *m; @@ -508,7 +504,7 @@ int tkl_config_run_spamfilter(ConfigFile *cf, ConfigEntry *ce, int type) } else if (!strcmp(cep->name, "action")) { - action = banact_stringtoval(cep->value); + action = parse_ban_action_config(cep); } else if (!strcmp(cep->name, "reason")) { @@ -1556,7 +1552,7 @@ int _server_ban_parse_mask(Client *client, int add, char type, const char *str, Client *acptr; if ((acptr = find_user(mask, NULL))) { - BanAction action = BAN_ACT_KLINE; // just a dummy default + BanActionValue action = BAN_ACT_KLINE; // just a dummy default if ((type == 'z') || (type == 'Z')) action = BAN_ACT_ZLINE; // to indicate zline (no hostname, no dns, etc) ban_target_to_tkl_layer(iConf.manual_ban_target, action, acptr, (const char **)&usermask, (const char **)&hostmask); @@ -1897,7 +1893,7 @@ int _server_ban_exception_parse_mask(Client *client, int add, const char *bantyp Client *acptr; if ((acptr = find_user(mask, NULL))) { - BanAction action = BAN_ACT_KLINE; // just a dummy default + BanActionValue action = BAN_ACT_KLINE; // just a dummy default if (add && eline_type_requires_ip(bantypes)) action = BAN_ACT_ZLINE; // to indicate zline (no hostname, no dns, etc) ban_target_to_tkl_layer(iConf.manual_ban_target, action, acptr, (const char **)&usermask, (const char **)&hostmask); @@ -2115,7 +2111,7 @@ void spamfilter_del_by_id(Client *client, const char *id) /* Spamfilter found. Now fill the tkllayer */ tkllayer[1] = "-"; tkllayer[3] = spamfilter_target_inttostring(tk->ptr.spamfilter->target); /* target(s) */ - mo[0] = banact_valtochar(tk->ptr.spamfilter->action); + mo[0] = banact_valtochar(tk->ptr.spamfilter->action->action); mo[1] = '\0'; tkllayer[4] = mo; /* action */ tkllayer[5] = make_nick_user_host(client->name, client->user->username, GetHost(client)); @@ -2589,7 +2585,7 @@ TKL *tkl_find_head(char type, char *hostmask, TKL *def) * @returns The TKL entry, or NULL in case of a problem, * such as a regex failing to compile, memory problem, .. */ -TKL *_tkl_add_spamfilter(int type, unsigned short target, BanAction action, Match *match, char *set_by, +TKL *_tkl_add_spamfilter(int type, unsigned short target, BanAction *action, Match *match, char *set_by, time_t expire_at, time_t set_at, time_t tkl_duration, char *tkl_reason, int flags) @@ -3675,7 +3671,7 @@ int tkl_stats_matcher(Client *client, int type, const char *para, TKLFlag *tklfl (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), + banact_valtostring(tkl->ptr.spamfilter->action->action), (tkl->expire_at != 0) ? (long long)(tkl->expire_at - TStime()) : 0, (long long)(TStime() - tkl->set_at), (long long)tkl->ptr.spamfilter->tkl_duration, @@ -3841,7 +3837,7 @@ void tkl_sync_send_entry(int add, Client *sender, Client *to, TKL *tkl) add ? '+' : '-', typ, spamfilter_target_inttostring(tkl->ptr.spamfilter->target), - banact_valtochar(tkl->ptr.spamfilter->action), + banact_valtochar(tkl->ptr.spamfilter->action->action), tkl->set_by, (long long)tkl->expire_at, (long long)tkl->set_at, (long long)tkl->ptr.spamfilter->tkl_duration, tkl->ptr.spamfilter->tkl_reason, @@ -3991,7 +3987,7 @@ TKL *_find_tkl_nameban(int type, char *name, int hold) } /** Find a spamfilter TKL - only used to prevent duplicates and for deletion */ -TKL *_find_tkl_spamfilter(int type, char *match_string, BanAction action, unsigned short target) +TKL *_find_tkl_spamfilter(int type, char *match_string, BanActionValue action, unsigned short target) { char tpe = tkl_typetochar(type); TKL *tkl; @@ -4003,7 +3999,7 @@ TKL *_find_tkl_spamfilter(int type, char *match_string, BanAction action, unsign { if ((type == tkl->type) && !strcmp(match_string, tkl->ptr.spamfilter->match->str) && - (action == tkl->ptr.spamfilter->action) && + (action == tkl->ptr.spamfilter->action->action) && (target == tkl->ptr.spamfilter->target)) { return tkl; @@ -4102,7 +4098,7 @@ void _tkl_added(Client *client, TKL *tkl) sendnotice_tkl_add(tkl); /* spamfilter 'warn' action is special */ - if ((tkl->type & TKL_SPAMF) && (tkl->ptr.spamfilter->action == BAN_ACT_WARN) && (tkl->ptr.spamfilter->target & SPAMF_USER)) + if ((tkl->type & TKL_SPAMF) && (tkl->ptr.spamfilter->action->action == BAN_ACT_WARN) && (tkl->ptr.spamfilter->target & SPAMF_USER)) spamfilter_check_users(tkl); /* Ban checking executes during run loop for efficiency */ @@ -4283,7 +4279,7 @@ CMD_FUNC(cmd_tkl_add) Match *m; /* compiled match_string */ time_t tkl_duration; const char *tkl_reason; - BanAction action; + BanActionValue action; unsigned short target; /* helper variables */ char *err; @@ -4348,7 +4344,7 @@ CMD_FUNC(cmd_tkl_add) log_data_string("spamfilter_regex_error", err)); return; } - tkl = tkl_add_spamfilter(type, target, action, m, set_by, expire_at, set_at, + tkl = tkl_add_spamfilter(type, target, banact_value_to_struct(action), m, set_by, expire_at, set_at, tkl_duration, tkl_reason, 0); } } else @@ -4458,7 +4454,7 @@ CMD_FUNC(cmd_tkl_del) { const char *match_string; unsigned short target; - BanAction action; + BanActionValue action; if (parc < 9) { @@ -4588,7 +4584,7 @@ CMD_FUNC(_cmd_tkl) } /** Configure the username/hostname TKL layer based on the BAN_TARGET_* configuration */ -void ban_target_to_tkl_layer(BanTarget ban_target, BanAction action, Client *client, const char **tkl_username, const char **tkl_hostname) +void ban_target_to_tkl_layer(BanTarget ban_target, BanActionValue action, Client *client, const char **tkl_username, const char **tkl_hostname) { static char username[USERLEN+1]; static char hostname[HOSTLEN+8]; @@ -4649,100 +4645,125 @@ void ban_target_to_tkl_layer(BanTarget ban_target, BanAction action, Client *cli * @param reason The ban reason. * @param duration The ban duration in seconds. * @note This function assumes that client is a locally connected user. - * @retval 1 if action is taken, 0 if user is exempted. + * @retval 0 user is exempt or no action needs to be taken for other reasons (eg only var setting) + * @retval 1 user is banned/shunned/killed/whatever, caller usually returns + * @retval BAN_ACT_BLOCK the user was not banned/shunned/killed but should be "blocked" * @note Be sure to check IsDead(client) if return value is 1 and you are * considering to continue processing. */ -int _place_host_ban(Client *client, BanAction action, char *reason, long duration) +int _place_host_ban(Client *client, BanAction *actions, char *reason, long duration) { - /* If this is a soft action and the user is logged in, then the ban does not apply. - * NOTE: Actually in such a case it would be better if place_host_ban() would not - * be called at all. Or at least, the caller should not take any action - * (eg: the message should be delivered, the user may connect, etc..) - * The following is more like secondary protection in case the caller forgets... - */ - if (IsSoftBanAction(action) && IsLoggedIn(client)) - return 0; + BanAction *action; + int retval = 0; - switch(action) + for (action = actions; action; action = action->next) { - case BAN_ACT_TEMPSHUN: - /* We simply mark this connection as shunned and do not add a ban record */ - unreal_log(ULOG_INFO, "tkl", "TKL_ADD_TEMPSHUN", &me, - "Temporary shun added on user $target.details [reason: $shun_reason] [by: $client]", - log_data_string("shun_reason", reason), - log_data_client("target", client)); - SetShunned(client); - return 1; - case BAN_ACT_GZLINE: - case BAN_ACT_GLINE: - case BAN_ACT_SOFT_GLINE: - case BAN_ACT_ZLINE: - case BAN_ACT_KLINE: - case BAN_ACT_SOFT_KLINE: - case BAN_ACT_SHUN: - case BAN_ACT_SOFT_SHUN: + /* If this is a soft action and the user is logged in, then the ban does not apply. */ + if (IsSoftBanAction(action->action) && IsLoggedIn(client)) + return 0; + + switch(action->action) { - char ip[128], user[USERLEN+3], mo[100], mo2[100]; - const char *tkllayer[9] = { - me.name, /*0 server.name */ - "+", /*1 +|- */ - "?", /*2 type */ - "*", /*3 user */ - NULL, /*4 host */ - NULL, - NULL, /*6 expire_at */ - NULL, /*7 set_at */ - NULL /*8 reason */ - }; - - ban_target_to_tkl_layer(iConf.automatic_ban_target, action, client, &tkllayer[3], &tkllayer[4]); - - /* For soft bans we need to prefix the % in the username */ - if (IsSoftBanAction(action)) + case BAN_ACT_WARN: + /* No action taken by us */ + break; + case BAN_ACT_TEMPSHUN: + /* We simply mark this connection as shunned and do not add a ban record */ + unreal_log(ULOG_INFO, "tkl", "TKL_ADD_TEMPSHUN", &me, + "Temporary shun added on user $target.details [reason: $shun_reason] [by: $client]", + log_data_string("shun_reason", reason), + log_data_client("target", client)); + SetShunned(client); + retval = 1; + break; + case BAN_ACT_GZLINE: + case BAN_ACT_GLINE: + case BAN_ACT_SOFT_GLINE: + case BAN_ACT_ZLINE: + case BAN_ACT_KLINE: + case BAN_ACT_SOFT_KLINE: + case BAN_ACT_SHUN: + case BAN_ACT_SOFT_SHUN: { - char tmp[USERLEN+3]; - snprintf(tmp, sizeof(tmp), "%%%s", tkllayer[3]); - strlcpy(user, tmp, sizeof(user)); - tkllayer[3] = user; + char ip[128], user[USERLEN+3], mo[100], mo2[100]; + const char *tkllayer[9] = { + me.name, /*0 server.name */ + "+", /*1 +|- */ + "?", /*2 type */ + "*", /*3 user */ + NULL, /*4 host */ + NULL, + NULL, /*6 expire_at */ + NULL, /*7 set_at */ + NULL /*8 reason */ + }; + + ban_target_to_tkl_layer(iConf.automatic_ban_target, action->action, client, &tkllayer[3], &tkllayer[4]); + + /* For soft bans we need to prefix the % in the username */ + if (IsSoftBanAction(action->action)) + { + char tmp[USERLEN+3]; + snprintf(tmp, sizeof(tmp), "%%%s", tkllayer[3]); + strlcpy(user, tmp, sizeof(user)); + tkllayer[3] = user; + } + + if ((action->action == BAN_ACT_KLINE) || (action->action == BAN_ACT_SOFT_KLINE)) + tkllayer[2] = "k"; + else if (action->action == BAN_ACT_ZLINE) + tkllayer[2] = "z"; + else if (action->action == BAN_ACT_GZLINE) + tkllayer[2] = "Z"; + else if ((action->action == BAN_ACT_GLINE) || (action->action == BAN_ACT_SOFT_GLINE)) + tkllayer[2] = "G"; + else if ((action->action == BAN_ACT_SHUN) || (action->action == BAN_ACT_SOFT_SHUN)) + tkllayer[2] = "s"; + tkllayer[5] = me.name; + if (!duration) + strlcpy(mo, "0", sizeof(mo)); /* perm */ + else + ircsnprintf(mo, sizeof(mo), "%lld", (long long)(duration + TStime())); + ircsnprintf(mo2, sizeof(mo2), "%lld", (long long)TStime()); + tkllayer[6] = mo; + tkllayer[7] = mo2; + tkllayer[8] = reason; + cmd_tkl(&me, NULL, 9, tkllayer); + RunHookReturnInt(HOOKTYPE_PLACE_HOST_BAN, !=99, client, action->action, reason, duration); + if ((action->action == BAN_ACT_SHUN) || (action->action == BAN_ACT_SOFT_SHUN)) + { + find_shun(client); + retval = 1; + break; + } /* else.. */ + retval = find_tkline_match(client, 0); + break; } - - if ((action == BAN_ACT_KLINE) || (action == BAN_ACT_SOFT_KLINE)) - tkllayer[2] = "k"; - else if (action == BAN_ACT_ZLINE) - tkllayer[2] = "z"; - else if (action == BAN_ACT_GZLINE) - tkllayer[2] = "Z"; - else if ((action == BAN_ACT_GLINE) || (action == BAN_ACT_SOFT_GLINE)) - tkllayer[2] = "G"; - else if ((action == BAN_ACT_SHUN) || (action == BAN_ACT_SOFT_SHUN)) - tkllayer[2] = "s"; - tkllayer[5] = me.name; - if (!duration) - strlcpy(mo, "0", sizeof(mo)); /* perm */ - else - ircsnprintf(mo, sizeof(mo), "%lld", (long long)(duration + TStime())); - ircsnprintf(mo2, sizeof(mo2), "%lld", (long long)TStime()); - tkllayer[6] = mo; - tkllayer[7] = mo2; - tkllayer[8] = reason; - cmd_tkl(&me, NULL, 9, tkllayer); - RunHookReturnInt(HOOKTYPE_PLACE_HOST_BAN, !=99, client, action, reason, duration); - if ((action == BAN_ACT_SHUN) || (action == BAN_ACT_SOFT_SHUN)) - { - find_shun(client); - return 1; - } /* else.. */ - return find_tkline_match(client, 0); + case BAN_ACT_BLOCK: + /* We don't actively do something, but we do indicate it is blocked */ + if (retval != 1) + retval = BAN_ACT_BLOCK; + break; + case BAN_ACT_SOFT_BLOCK: + if (!IsLoggedIn(client)) + { + if (retval != 1) + retval = BAN_ACT_BLOCK; + } + break; + case BAN_ACT_SOFT_KILL: + case BAN_ACT_KILL: + default: + RunHookReturnInt(HOOKTYPE_PLACE_HOST_BAN, !=99, client, action->action, reason, duration); + exit_client(client, NULL, reason); + retval = 1; + break; } - case BAN_ACT_SOFT_KILL: - case BAN_ACT_KILL: - default: - RunHookReturnInt(HOOKTYPE_PLACE_HOST_BAN, !=99, client, action, reason, duration); - exit_client(client, NULL, reason); - return 1; + + if (IsDead(client)) + break; /* stop processing actions */ } - return 0; /* no action taken (weird) */ + return retval; } /** This function compares two spamfilters ('one' and 'two') and will return @@ -4901,13 +4922,13 @@ int _match_spamfilter(Client *client, const char *str_in, int target, const char if (!(tkl->ptr.spamfilter->target & target)) continue; - if ((flags & SPAMFLAG_NOWARN) && (tkl->ptr.spamfilter->action == BAN_ACT_WARN)) + if ((flags & SPAMFLAG_NOWARN) && only_actions_of_type(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(tkl->ptr.spamfilter->action) && IsLoggedIn(client)) + if (IsLoggedIn(client) && only_soft_actions(tkl->ptr.spamfilter->action)) continue; #ifdef SPAMFILTER_DETECTSLOW @@ -4950,6 +4971,8 @@ int _match_spamfilter(Client *client, const char *str_in, int target, const char if (!winner_tkl && destination && target_is_spamexcept(destination)) return 0; /* No problem! */ + // TODO: if (only_actions_of_type(tkl->ptr.spamfilter->action, BAN_ACT_SET)) then don't show the warning unless debugging or something :) + unreal_log(ULOG_INFO, "tkl", "SPAMFILTER_MATCH", client, "[Spamfilter] $client.details matches filter '$tkl': [cmd: $command$_space$destination: '$str'] [reason: $tkl.reason] [action: $tkl.ban_action]", log_data_tkl("tkl", tkl), @@ -4985,7 +5008,7 @@ int _match_spamfilter(Client *client, const char *str_in, int target, const char /* Spamfilter matched, take action: */ reason = unreal_decodespace(tkl->ptr.spamfilter->tkl_reason); - if ((tkl->ptr.spamfilter->action == BAN_ACT_BLOCK) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_BLOCK)) + if ((tkl->ptr.spamfilter->action->action == BAN_ACT_BLOCK) || (tkl->ptr.spamfilter->action->action == BAN_ACT_SOFT_BLOCK)) { switch(target) { @@ -5038,13 +5061,13 @@ int _match_spamfilter(Client *client, const char *str_in, int target, const char } return 1; } else - if ((tkl->ptr.spamfilter->action == BAN_ACT_WARN) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_WARN)) + if ((tkl->ptr.spamfilter->action->action == BAN_ACT_WARN) || (tkl->ptr.spamfilter->action->action == BAN_ACT_SOFT_WARN)) { if ((target != SPAMF_USER) && (target != SPAMF_QUIT)) sendnumeric(client, RPL_SPAMCMDFWD, cmd, reason); return 0; } else - if ((tkl->ptr.spamfilter->action == BAN_ACT_DCCBLOCK) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_DCCBLOCK)) + if ((tkl->ptr.spamfilter->action->action == BAN_ACT_DCCBLOCK) || (tkl->ptr.spamfilter->action->action == BAN_ACT_SOFT_DCCBLOCK)) { if (target == SPAMF_DCC) { @@ -5054,7 +5077,7 @@ int _match_spamfilter(Client *client, const char *str_in, int target, const char } return 1; } else - if ((tkl->ptr.spamfilter->action == BAN_ACT_VIRUSCHAN) || (tkl->ptr.spamfilter->action == BAN_ACT_SOFT_VIRUSCHAN)) + if ((tkl->ptr.spamfilter->action->action == BAN_ACT_VIRUSCHAN) || (tkl->ptr.spamfilter->action->action == BAN_ACT_SOFT_VIRUSCHAN)) { if (IsVirus(client)) /* Already tagged */ return 0; diff --git a/src/modules/tkldb.c b/src/modules/tkldb.c index 2316f0bba..14155b22b 100644 --- a/src/modules/tkldb.c +++ b/src/modules/tkldb.c @@ -430,7 +430,7 @@ int write_tkline(UnrealDB *db, const char *tmpfname, TKL *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); + char action = banact_valtochar(tkl->ptr.spamfilter->action->action); W_SAFE(unrealdb_write_str(db, match_type)); W_SAFE(unrealdb_write_str(db, tkl->ptr.spamfilter->match->str)); @@ -699,7 +699,7 @@ int read_tkldb(void) /* Action */ R_SAFE(unrealdb_read_char(db, &c)); - tkl->ptr.spamfilter->action = banact_chartoval(c); + tkl->ptr.spamfilter->action = banact_value_to_struct(banact_chartoval(c)); if (!tkl->ptr.spamfilter->action) { config_warn("[tkldb] Spamfilter '%s' without valid action (%c) -- spamfilter entry not added", @@ -712,7 +712,7 @@ int read_tkldb(void) tkl->ptr.spamfilter->tkl_duration = v; if (find_tkl_spamfilter(tkl->type, tkl->ptr.spamfilter->match->str, - tkl->ptr.spamfilter->action, + tkl->ptr.spamfilter->action->action, tkl->ptr.spamfilter->target)) { do_not_add = 1;