unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
floodprot.c (59896B)
1 /* 2 * Channel Mode +f and +F 3 * (C) Copyright 2019-.. Syzop and the UnrealIRCd team 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 1, or (at your option) 8 * any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 18 */ 19 20 #include "unrealircd.h" 21 22 ModuleHeader MOD_HEADER 23 = { 24 "chanmodes/floodprot", 25 "6.0", 26 "Channel Mode +f and +F", 27 "UnrealIRCd Team", 28 "unrealircd-6", 29 }; 30 31 typedef enum Flood { 32 CHFLD_CTCP = 0, 33 CHFLD_JOIN = 1, 34 CHFLD_KNOCK = 2, 35 CHFLD_MSG = 3, 36 CHFLD_NICK = 4, 37 CHFLD_TEXT = 5, 38 CHFLD_REPEAT = 6, 39 } Flood; 40 #define NUMFLD 7 /* 7 flood types */ 41 42 /** Configuration settings */ 43 struct { 44 unsigned char modef_default_unsettime; 45 unsigned char modef_max_unsettime; 46 long boot_delay; 47 long split_delay; 48 int modef_alternate_action_percentage_threshold; 49 unsigned char modef_alternative_ban_action_unsettime; 50 char *default_profile; 51 } cfg; 52 53 typedef struct FloodType { 54 char letter; 55 Flood index; 56 char *description; 57 char default_action; 58 char *actions; 59 char *alternative_ban_action; 60 int timedban_required; 61 } FloodType; 62 63 /* All the floodtypes that are tracked. 64 * IMPORTANT: the first row MUST be in alphabetic order!! 65 */ 66 FloodType floodtypes[] = { 67 { 'c', CHFLD_CTCP, "CTCPflood", 'C', "", NULL, 0, }, 68 { 'j', CHFLD_JOIN, "joinflood", 'i', "R", "~security-group:unknown-users", 0, }, 69 { 'k', CHFLD_KNOCK, "knockflood", 'K', "", NULL, 0, }, 70 { 'm', CHFLD_MSG, "msg/noticeflood", 'm', "M", "~quiet:~security-group:unknown-users", 0, }, 71 { 'n', CHFLD_NICK, "nickflood", 'N', "", "~nickchange:~security-group:unknown-users", 0, }, 72 { 't', CHFLD_TEXT, "msg/noticeflood", '\0', "bd", NULL, 1, }, 73 { 'r', CHFLD_REPEAT, "repeating", '\0', "bd", NULL, 1, }, 74 }; 75 76 #define MODEF_DEFAULT_UNSETTIME cfg.modef_default_unsettime 77 #define MODEF_MAX_UNSETTIME cfg.modef_max_unsettime 78 79 typedef struct ChannelFloodProtection ChannelFloodProtection; 80 typedef struct ChannelFloodProfile ChannelFloodProfile; 81 typedef struct RemoveChannelModeTimer RemoveChannelModeTimer; 82 83 struct RemoveChannelModeTimer { 84 struct RemoveChannelModeTimer *prev, *next; 85 Channel *channel; 86 char m; /* mode to be removed */ 87 time_t when; /* scheduled at */ 88 }; 89 90 typedef struct MemberFlood MemberFlood; 91 struct MemberFlood { 92 unsigned short nmsg; 93 unsigned short nmsg_repeat; 94 time_t firstmsg; 95 uint64_t lastmsg; 96 uint64_t prevmsg; 97 }; 98 99 /* Maximum timers, iotw: max number of possible actions. 100 * Currently this is: CNmMKiRd (8) 101 * But bumped to 15 because we now have cmode.flood_type_action 102 * so there could be more ;). 103 */ 104 #define MAXCHMODEFACTIONS 15 105 106 /** Per-channel flood protection settings and counters */ 107 struct ChannelFloodProtection { 108 unsigned short per; /**< setting: per <XX> seconds */ 109 time_t timer[NUMFLD]; /**< runtime: timers */ 110 unsigned short counter[NUMFLD]; /**< runtime: counters */ 111 unsigned short counter_unknown_users[NUMFLD]; /**< runtime: counters */ 112 unsigned short limit[NUMFLD]; /**< setting: limit */ 113 unsigned char action[NUMFLD]; /**< setting: action */ 114 unsigned char remove_after[NUMFLD]; /**< setting: remove-after <this> minutes */ 115 unsigned char timers_running[MAXCHMODEFACTIONS+1]; /**< if for example a '-m' timer is running then this contains 'm' */ 116 char *profile; 117 }; 118 119 struct ChannelFloodProfile { 120 ChannelFloodProfile *prev, *next; 121 ChannelFloodProtection settings; 122 }; 123 124 /* Global variables */ 125 ModDataInfo *mdflood = NULL; 126 Cmode_t EXTMODE_FLOODLIMIT = 0L; 127 Cmode_t EXTMODE_FLOOD_PROFILE = 0L; 128 static int timedban_available = 1; /**< Set to 1 if extbans/timedban module is loaded. Assumed 1 during config load due to set::modes-on-join race. */ 129 RemoveChannelModeTimer *removechannelmodetimer_list = NULL; 130 ChannelFloodProfile *channel_flood_profiles = NULL; 131 char *floodprot_msghash_key = NULL; 132 long long floodprot_splittime = 0; 133 134 #define IsFloodLimit(x) (((x)->mode.mode & EXTMODE_FLOODLIMIT) || ((x)->mode.mode & EXTMODE_FLOOD_PROFILE) || (cfg.default_profile && GETPARASTRUCT(channel, 'F'))) 135 136 /* Forward declarations */ 137 static void init_config(void); 138 int floodprot_rehash_complete(void); 139 int floodprot_config_test_set_block(ConfigFile *, ConfigEntry *, int, int *); 140 int floodprot_config_run_set_block(ConfigFile *, ConfigEntry *, int); 141 int floodprot_config_test_antiflood_block(ConfigFile *, ConfigEntry *, int, int *); 142 int floodprot_config_run_antiflood_block(ConfigFile *, ConfigEntry *, int); 143 void floodprottimer_del(Channel *channel, ChannelFloodProtection *fld, char mflag); 144 void floodprottimer_stopchantimers(Channel *channel); 145 static inline char *chmodefstrhelper(char *buf, char t, char tdef, unsigned short l, unsigned char a, unsigned char r); 146 static int compare_floodprot_modes(ChannelFloodProtection *a, ChannelFloodProtection *b); 147 static int do_floodprot(Channel *channel, Client *client, int what); 148 char *channel_modef_string(ChannelFloodProtection *x, char *str); 149 void do_floodprot_action(Channel *channel, int what); 150 void floodprottimer_add(Channel *channel, ChannelFloodProtection *fld, char mflag, time_t when); 151 uint64_t gen_floodprot_msghash(const char *text); 152 int cmodef_is_ok(Client *client, Channel *channel, char mode, const char *para, int type, int what); 153 void *cmodef_put_param(void *r_in, const char *param); 154 const char *cmodef_get_param(void *r_in); 155 const char *cmodef_conv_param(const char *param_in, Client *client, Channel *channel); 156 int cmodef_free_param(void *r, int soft); 157 void *cmodef_dup_struct(void *r_in); 158 int cmodef_sjoin_check(Channel *channel, void *ourx, void *theirx); 159 int cmodef_profile_is_ok(Client *client, Channel *channel, char mode, const char *param, int type, int what); 160 void *cmodef_profile_put_param(void *r_in, const char *param); 161 const char *cmodef_profile_get_param(void *r_in); 162 const char *cmodef_profile_conv_param(const char *param_in, Client *client, Channel *channel); 163 int cmodef_profile_sjoin_check(Channel *channel, void *ourx, void *theirx); 164 int floodprot_join(Client *client, Channel *channel, MessageTag *mtags); 165 EVENT(modef_event); 166 int cmodef_channel_create(Channel *channel); 167 int cmodef_channel_destroy(Channel *channel, int *should_destroy); 168 int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype); 169 int floodprot_post_chanmsg(Client *client, Channel *channel, int sendflags, const char *prefix, const char *target, MessageTag *mtags, const char *text, SendType sendtype); 170 int floodprot_knock(Client *client, Channel *channel, MessageTag *mtags, const char *comment); 171 int floodprot_nickchange(Client *client, MessageTag *mtags, const char *oldnick); 172 int floodprot_chanmode_del(Channel *channel, int m); 173 void memberflood_free(ModData *md); 174 int floodprot_stats(Client *client, const char *flag); 175 void floodprot_free_removechannelmodetimer_list(ModData *m); 176 void floodprot_free_msghash_key(ModData *m); 177 CMD_OVERRIDE_FUNC(floodprot_override_mode); 178 ChannelFloodProtection *get_channel_flood_profile(const char *name); 179 int parse_channel_mode_flood(const char *param, ChannelFloodProtection *fld, int strict, Client *client, const char **error_out); 180 int parse_channel_mode_flood_failed(const char **error_out, ChannelFloodProtection *fld, FORMAT_STRING(const char *fmt), ...) __attribute__((format(printf,3,4))); 181 int floodprot_server_quit(Client *client, MessageTag *mtags); 182 void inherit_settings(ChannelFloodProtection *from, ChannelFloodProtection *to); 183 void reapply_profiles(void); 184 185 MOD_TEST() 186 { 187 HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, floodprot_config_test_set_block); 188 HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, floodprot_config_test_antiflood_block); 189 return MOD_SUCCESS; 190 } 191 192 MOD_INIT() 193 { 194 CmodeInfo creq; 195 ModDataInfo mreq; 196 197 MARK_AS_OFFICIAL_MODULE(modinfo); 198 199 LoadPersistentLongLong(modinfo, floodprot_splittime); 200 201 memset(&creq, 0, sizeof(creq)); 202 creq.paracount = 1; 203 creq.is_ok = cmodef_is_ok; 204 creq.letter = 'f'; 205 creq.unset_with_param = 1; /* ah yeah, +f is special! */ 206 creq.put_param = cmodef_put_param; 207 creq.get_param = cmodef_get_param; 208 creq.conv_param = cmodef_conv_param; 209 creq.free_param = cmodef_free_param; 210 creq.dup_struct = cmodef_dup_struct; 211 creq.sjoin_check = cmodef_sjoin_check; 212 CmodeAdd(modinfo->handle, creq, &EXTMODE_FLOODLIMIT); 213 214 memset(&creq, 0, sizeof(creq)); 215 creq.paracount = 1; 216 creq.is_ok = cmodef_profile_is_ok; 217 creq.letter = 'F'; 218 creq.put_param = cmodef_profile_put_param; 219 creq.get_param = cmodef_profile_get_param; 220 creq.conv_param = cmodef_profile_conv_param; 221 creq.free_param = cmodef_free_param; // +f & +F uses same code 222 creq.dup_struct = cmodef_dup_struct; // +f & +F uses same code 223 creq.sjoin_check = cmodef_profile_sjoin_check; 224 CmodeAdd(modinfo->handle, creq, &EXTMODE_FLOOD_PROFILE); 225 226 init_config(); 227 228 LoadPersistentPointer(modinfo, removechannelmodetimer_list, floodprot_free_removechannelmodetimer_list); 229 LoadPersistentPointer(modinfo, floodprot_msghash_key, floodprot_free_msghash_key); 230 231 memset(&mreq, 0, sizeof(mreq)); 232 mreq.name = "floodprot"; 233 mreq.type = MODDATATYPE_MEMBERSHIP; 234 mreq.free = memberflood_free; 235 mdflood = ModDataAdd(modinfo->handle, mreq); 236 if (!mdflood) 237 abort(); 238 if (!floodprot_msghash_key) 239 { 240 floodprot_msghash_key = safe_alloc(16); 241 siphash_generate_key(floodprot_msghash_key); 242 } 243 244 HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, floodprot_config_run_set_block); 245 HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, floodprot_config_run_antiflood_block); 246 HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_CHANNEL, 0, floodprot_can_send_to_channel); 247 HookAdd(modinfo->handle, HOOKTYPE_CHANMSG, 0, floodprot_post_chanmsg); 248 HookAdd(modinfo->handle, HOOKTYPE_KNOCK, 0, floodprot_knock); 249 HookAdd(modinfo->handle, HOOKTYPE_LOCAL_NICKCHANGE, 0, floodprot_nickchange); 250 HookAdd(modinfo->handle, HOOKTYPE_REMOTE_NICKCHANGE, 0, floodprot_nickchange); 251 HookAdd(modinfo->handle, HOOKTYPE_MODECHAR_DEL, 0, floodprot_chanmode_del); 252 HookAdd(modinfo->handle, HOOKTYPE_LOCAL_JOIN, 0, floodprot_join); 253 HookAdd(modinfo->handle, HOOKTYPE_REMOTE_JOIN, 0, floodprot_join); 254 HookAdd(modinfo->handle, HOOKTYPE_CHANNEL_CREATE, 0, cmodef_channel_create); 255 HookAdd(modinfo->handle, HOOKTYPE_CHANNEL_DESTROY, 0, cmodef_channel_destroy); 256 HookAdd(modinfo->handle, HOOKTYPE_REHASH_COMPLETE, 0, floodprot_rehash_complete); 257 HookAdd(modinfo->handle, HOOKTYPE_STATS, 0, floodprot_stats); 258 HookAdd(modinfo->handle, HOOKTYPE_SERVER_QUIT, 0, floodprot_server_quit); 259 return MOD_SUCCESS; 260 } 261 262 MOD_LOAD() 263 { 264 EventAdd(modinfo->handle, "modef_event", modef_event, NULL, 10000, 0); 265 CommandOverrideAdd(modinfo->handle, "MODE", 0, floodprot_override_mode); 266 floodprot_rehash_complete(); 267 reapply_profiles(); 268 return MOD_SUCCESS; 269 } 270 271 void free_channel_flood_profile(ChannelFloodProfile *f) 272 { 273 safe_free(f->settings.profile); 274 safe_free(f); 275 } 276 277 void free_channel_flood_profiles(void) 278 { 279 ChannelFloodProfile *f, *f_next; 280 281 for (f = channel_flood_profiles; f; f = f_next) 282 { 283 f_next = f->next; 284 DelListItem(f, channel_flood_profiles); 285 free_channel_flood_profile(f); 286 } 287 } 288 289 MOD_UNLOAD() 290 { 291 SavePersistentPointer(modinfo, removechannelmodetimer_list); 292 SavePersistentPointer(modinfo, floodprot_msghash_key); 293 SavePersistentLongLong(modinfo, floodprot_splittime); 294 295 free_channel_flood_profiles(); 296 297 return MOD_SUCCESS; 298 } 299 300 int floodprot_rehash_complete(void) 301 { 302 timedban_available = is_module_loaded("extbans/timedban"); 303 return 0; 304 } 305 306 /** Set a new channel anti flood profile. 307 * Caller MUST ensure that the 'value' is valid, eg by calling 308 * parse_channel_mode_flood() or is_ok() prior. 309 */ 310 static void set_channel_flood_profile(const char *name, const char *value) 311 { 312 ChannelFloodProfile *f; 313 314 for (f = channel_flood_profiles; f; f = f->next) 315 if (!strcasecmp(f->settings.profile, name)) 316 break; 317 if (!f) 318 { 319 f = safe_alloc(sizeof(ChannelFloodProfile)); 320 AddListItem(f, channel_flood_profiles); 321 } 322 323 safe_strdup(f->settings.profile, name); 324 cmodef_put_param(&f->settings, value); 325 } 326 327 static void init_default_channel_flood_profiles(void) 328 { 329 ChannelFloodProfile *f; 330 331 f = safe_alloc(sizeof(ChannelFloodProfile)); 332 cmodef_put_param(&f->settings, "[10j#R10,30m#M10,7c#C15,5n#N15,10k#K15]:15"); 333 safe_strdup(f->settings.profile, "very-strict"); 334 AddListItem(f, channel_flood_profiles); 335 336 f = safe_alloc(sizeof(ChannelFloodProfile)); 337 cmodef_put_param(&f->settings, "[15j#R10,40m#M10,7c#C15,8n#N15,10k#K15]:15"); 338 safe_strdup(f->settings.profile, "strict"); 339 AddListItem(f, channel_flood_profiles); 340 341 f = safe_alloc(sizeof(ChannelFloodProfile)); 342 cmodef_put_param(&f->settings, "[30j#R10,40m#M10,7c#C15,8n#N15,10k#K15]:15"); 343 safe_strdup(f->settings.profile, "normal"); 344 AddListItem(f, channel_flood_profiles); 345 346 f = safe_alloc(sizeof(ChannelFloodProfile)); 347 cmodef_put_param(&f->settings, "[45j#R10,60m#M10,7c#C15,10n#N15,10k#K15]:15"); 348 safe_strdup(f->settings.profile, "relaxed"); 349 AddListItem(f, channel_flood_profiles); 350 351 f = safe_alloc(sizeof(ChannelFloodProfile)); 352 cmodef_put_param(&f->settings, "[60j#R10,90m#M10,7c#C15,10n#N15,10k#K15]:15"); 353 safe_strdup(f->settings.profile, "very-relaxed"); 354 AddListItem(f, channel_flood_profiles); 355 356 f = safe_alloc(sizeof(ChannelFloodProfile)); 357 safe_strdup(f->settings.profile, "off"); 358 AddListItem(f, channel_flood_profiles); 359 } 360 361 static void init_config(void) 362 { 363 /* This sets some default values */ 364 memset(&cfg, 0, sizeof(cfg)); 365 cfg.modef_default_unsettime = 0; 366 cfg.modef_max_unsettime = 60; /* 1 hour seems enough :p */ 367 cfg.boot_delay = 75; 368 cfg.split_delay = 75; 369 cfg.modef_alternate_action_percentage_threshold = 75; /* 75% */ 370 cfg.modef_alternative_ban_action_unsettime = 15; /* 15min */ 371 init_default_channel_flood_profiles(); 372 } 373 374 int floodprot_config_test_set_block(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) 375 { 376 int errors = 0; 377 378 if (type != CONFIG_SET) 379 return 0; 380 381 if (!strcmp(ce->name, "modef-default-unsettime")) 382 { 383 if (!ce->value) 384 { 385 config_error_empty(ce->file->filename, ce->line_number, 386 "set", ce->name); 387 errors++; 388 } else { 389 int v = atoi(ce->value); 390 if ((v <= 0) || (v > 255)) 391 { 392 config_error("%s:%i: set::modef-default-unsettime: value '%d' out of range (should be 1-255)", 393 ce->file->filename, ce->line_number, v); 394 errors++; 395 } 396 } 397 } else 398 if (!strcmp(ce->name, "modef-max-unsettime")) 399 { 400 if (!ce->value) 401 { 402 config_error_empty(ce->file->filename, ce->line_number, 403 "set", ce->name); 404 errors++; 405 } else { 406 int v = atoi(ce->value); 407 if ((v <= 0) || (v > 255)) 408 { 409 config_error("%s:%i: set::modef-max-unsettime: value '%d' out of range (should be 1-255)", 410 ce->file->filename, ce->line_number, v); 411 errors++; 412 } 413 } 414 } else 415 if (!strcmp(ce->name, "modef-boot-delay")) 416 { 417 config_error("%s:%i: set::modef-boot-delay is now called set::anti-flood::channel::boot-delay. " 418 "See https://www.unrealircd.org/docs/Channel_anti-flood_settings#config", 419 ce->file->filename, ce->line_number); 420 errors++; 421 } else 422 { 423 /* Not handled by us */ 424 return 0; 425 } 426 427 *errs = errors; 428 return errors ? -1 : 1; 429 } 430 431 int floodprot_config_run_set_block(ConfigFile *cf, ConfigEntry *ce, int type) 432 { 433 if (type != CONFIG_SET) 434 return 0; 435 436 if (!strcmp(ce->name, "modef-default-unsettime")) 437 cfg.modef_default_unsettime = (unsigned char)atoi(ce->value); 438 else if (!strcmp(ce->name, "modef-max-unsettime")) 439 cfg.modef_max_unsettime = (unsigned char)atoi(ce->value); 440 else 441 return 0; /* not handled by us */ 442 443 return 1; 444 } 445 446 /** Check if 'str' is a flood profile name 447 */ 448 int valid_flood_profile_name(const char *str) 449 { 450 if (strlen(str) > 24) 451 return 0; 452 for (; *str; str++) 453 if (!islower(*str) && !isdigit(*str) && !strchr("_-", *str)) 454 return 0; 455 return 1; 456 } 457 458 int floodprot_config_test_antiflood_block(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) 459 { 460 int errors = 0; 461 ConfigEntry *cep; 462 463 /* We only deal with set::anti-flood::channel */ 464 if ((type != CONFIG_SET_ANTI_FLOOD) || strcmp(ce->parent->name, "channel")) 465 return 0; 466 467 for (; ce; ce = ce->next) 468 { 469 if (!strcmp(ce->name, "default-profile")) 470 { 471 if (!ce->value) 472 { 473 config_error_noname(ce->file->filename, ce->line_number, 474 "set::anti-flood::channel::default-profile"); 475 errors++; 476 continue; 477 } 478 } else 479 if (!strcmp(ce->name, "boot-delay") || !strcmp(ce->name, "split-delay")) 480 { 481 if (!ce->value) 482 { 483 config_error_empty(ce->file->filename, ce->line_number, 484 "set", ce->name); 485 errors++; 486 } else { 487 long v = config_checkval(ce->value, CFG_TIME); 488 if ((v < 0) || (v > 600)) 489 { 490 config_error("%s:%i: set::anti-flood::channel::%s: value '%ld' out of range (should be 0-600)", 491 ce->file->filename, ce->line_number, 492 ce->name, 493 v); 494 errors++; 495 } 496 } 497 } else 498 if (!strcmp(ce->name, "profile")) 499 { 500 if (!ce->value) 501 { 502 config_error_noname(ce->file->filename, ce->line_number, 503 "set::anti-flood::channel::profile"); 504 errors++; 505 continue; 506 } 507 if (!valid_flood_profile_name(ce->value)) 508 { 509 config_error("%s:%i: set::anti-flood::channel: profile '%s' name is invalid. " 510 "Name can be 24 characters max and may only contain characters a-z, 0-9, _ and -", 511 ce->file->filename, ce->line_number, ce->value); 512 errors++; 513 continue; 514 } 515 for (cep = ce->items; cep; cep = cep->next) 516 { 517 if (!strcmp(cep->name, "flood-mode")) 518 { 519 ChannelFloodProtection fld; 520 const char *err; 521 522 if (!cep->value) 523 { 524 config_error("%s:%i: set::anti-flood::channel::profile %s::flood-mode has no value", 525 cep->file->filename, cep->line_number, ce->value); 526 errors++; 527 continue; 528 } 529 memset(&fld, 0, sizeof(fld)); 530 if (!parse_channel_mode_flood(cep->value, &fld, 1, NULL, &err)) 531 { 532 config_error("%s:%i: set::anti-flood::channel::profile %s::flood-mode: %s", 533 cep->file->filename, cep->line_number, 534 ce->value, 535 cep->value); 536 errors++; 537 } else if (!BadPtr(err)) 538 { 539 config_warn("%s:%i: set::anti-flood::channel::profile %s::flood-mode: %s", 540 cep->file->filename, cep->line_number, 541 ce->value, 542 err); 543 } 544 if (fld.limit[CHFLD_TEXT] || fld.limit[CHFLD_REPEAT]) 545 { 546 config_error("%s:%i: set::anti-flood::channel::profile %s::flood-mode: " 547 "subtypes 't' and 'r' are not supported for +F profiles at the moment.", 548 cep->file->filename, cep->line_number, 549 ce->value); 550 errors++; 551 } 552 } else { 553 config_error_unknown(cep->file->filename, cep->line_number, 554 "set::anti-flood::channel::profile", cep->name); 555 errors++; 556 } 557 } 558 } else 559 { 560 config_error_unknown(ce->file->filename, ce->line_number, 561 "set::anti-flood::channel", ce->name); 562 errors++; 563 } 564 } 565 566 *errs = errors; 567 return errors ? -2 : 2; 568 } 569 570 int floodprot_config_run_antiflood_block(ConfigFile *cf, ConfigEntry *ce, int type) 571 { 572 ConfigEntry *cep; 573 574 /* We only deal with set::anti-flood::channel */ 575 if ((type != CONFIG_SET_ANTI_FLOOD) || strcmp(ce->parent->name, "channel")) 576 return 0; 577 578 for (; ce; ce = ce->next) 579 { 580 if (!strcmp(ce->name, "default-profile")) 581 { 582 safe_strdup(cfg.default_profile, ce->value); 583 } else 584 if (!strcmp(ce->name, "boot-delay")) 585 { 586 cfg.boot_delay = config_checkval(ce->value, CFG_TIME); 587 } else 588 if (!strcmp(ce->name, "split-delay")) 589 { 590 cfg.split_delay = config_checkval(ce->value, CFG_TIME); 591 } else 592 if (!strcmp(ce->name, "profile")) 593 { 594 for (cep = ce->items; cep; cep = cep->next) 595 { 596 if (!strcmp(cep->name, "flood-mode")) 597 set_channel_flood_profile(ce->value, cep->value); 598 } 599 } 600 } 601 return 2; 602 } 603 604 FloodType *find_floodprot_by_letter(char c) 605 { 606 int i; 607 for (i=0; i < ARRAY_SIZEOF(floodtypes); i++) 608 if (floodtypes[i].letter == c) 609 return &floodtypes[i]; 610 611 return NULL; 612 } 613 614 FloodType *find_floodprot_by_index(Flood index) 615 { 616 int i; 617 for (i=0; i < ARRAY_SIZEOF(floodtypes); i++) 618 if (floodtypes[i].index == index) 619 return &floodtypes[i]; 620 621 return NULL; 622 } 623 624 ChannelFloodProtection *get_channel_flood_profile(const char *name) 625 { 626 ChannelFloodProfile *f; 627 628 for (f = channel_flood_profiles; f; f = f->next) 629 if (!strcasecmp(f->settings.profile, name)) 630 return &f->settings; 631 632 return NULL; 633 } 634 635 /** Helper function for parse_channel_mode_flood() */ 636 int parse_channel_mode_flood_failed(const char **error_out, ChannelFloodProtection *fld, const char *fmt, ...) 637 { 638 static char retbuf[512]; 639 int v; 640 641 va_list vl; 642 va_start(vl, fmt); 643 vsnprintf(retbuf, sizeof(retbuf), fmt, vl); 644 va_end(vl); 645 646 /* Zero out all settings */ 647 for (v=0; v < NUMFLD; v++) 648 { 649 fld->limit[v] = 0; 650 fld->action[v] = 0; 651 fld->remove_after[v] = 0; 652 } 653 654 if (error_out) 655 *error_out = retbuf; 656 657 return 0; 658 } 659 660 int floodprot_valid_alternate_action(char action, FloodType *floodtype) 661 { 662 Cmode *cm; 663 664 /* Built-in actions */ 665 if (strchr(floodtype->actions, action)) 666 return 1; 667 668 cm = find_channel_mode_handler(action); 669 if (cm && cm->flood_type_action == floodtype->letter) 670 return 1; 671 672 return 0; 673 } 674 675 /** Parse channel mode +f string. 676 * @param param The parameter string to parse 677 * @param fld The setting struct to fill, this MAY already contain data. 678 * @param strict If set to 1 then reject invalid setting, used for ex .is_ok(). 679 * If set to 0 then do your best to make something out of it, 680 * and skip invalid stuff for forward-compatibility, eg for .put_param(). 681 * @param client The client requesting the mode change, can be NULL 682 * (used for local vs remote things, not for sending errors) 683 * @param error Used for returning the error or warning string, can be NULL. 684 * @retval 1 On success, although there could still be a warning stored in *error_out. 685 * @retval 0 On failure, the error will be in *error_out 686 */ 687 int parse_channel_mode_flood(const char *param, ChannelFloodProtection *fld, int strict, Client *client, const char **error_out) 688 { 689 static char retbuf[512]; 690 char xbuf[256], c, a, *p, *p2, *x = xbuf+1; 691 int v; 692 unsigned short breakit; 693 unsigned char r; 694 FloodType *floodtype; 695 Flood index; 696 char localclient = (client && MyUser(client)) ? 1 : 0; 697 char warn_unknown_flood_type[32]; 698 699 *warn_unknown_flood_type = '\0'; 700 if (error_out) 701 *error_out = NULL; 702 703 /* always reset settings (l, a, r) */ 704 for (v=0; v < NUMFLD; v++) 705 { 706 fld->limit[v] = 0; 707 fld->action[v] = 0; 708 fld->remove_after[v] = 0; 709 } 710 711 strlcpy(xbuf, param, sizeof(xbuf)); 712 713 if (*xbuf != '[') 714 return parse_channel_mode_flood_failed(error_out, fld, "Invalid format (brackets missing)"); 715 716 /* '['<number><1 letter>[optional: '#'+1 letter],[next..]']'':'<number> */ 717 p2 = strchr(xbuf+1, ']'); 718 if (!p2) 719 return parse_channel_mode_flood_failed(error_out, fld, "Invalid format (brackets missing)"); 720 *p2 = '\0'; 721 if (*(p2+1) != ':') 722 return parse_channel_mode_flood_failed(error_out, fld, "Invalid format (:XX period missing)"); 723 724 breakit = 0; 725 for (x = strtok(xbuf+1, ","); x; x = strtok(NULL, ",")) 726 { 727 /* <number><1 letter>[optional: '#'+1 letter] */ 728 p = x; 729 while(isdigit(*p)) { p++; } 730 731 /* letter */ 732 c = *p; 733 floodtype = find_floodprot_by_letter(c); 734 if (!floodtype) 735 { 736 strlcat_letter(warn_unknown_flood_type, c, sizeof(warn_unknown_flood_type)); 737 continue; /* continue instead of break for forward compatability. */ 738 } 739 *p = '\0'; 740 741 /* floodcount (number) */ 742 v = atoi(x); 743 if (strict) 744 { 745 if ((v < 1) || (v > 999)) 746 return parse_channel_mode_flood_failed(error_out, fld, "Flood count for '%c' must be 1-999 (got %d)", c, v); 747 } 748 if (v < 1) 749 v = 1; 750 if (v > 999) 751 v = 999; 752 p++; 753 a = '\0'; 754 755 /* Removal */ 756 r = localclient ? MODEF_DEFAULT_UNSETTIME : 0; 757 if (*p != '\0') 758 { 759 if (*p == '#') 760 { 761 p++; 762 a = *p; 763 p++; 764 if (*p != '\0') 765 { 766 int tv; 767 tv = atoi(p); 768 if (tv <= 0) 769 tv = 0; /* (ignored) */ 770 if (tv > 255) 771 tv = 255; /* always max limit, as it is a char */ 772 if (strict && localclient && (tv > MODEF_MAX_UNSETTIME)) 773 tv = MODEF_MAX_UNSETTIME; 774 r = (unsigned char)tv; 775 } 776 } 777 } 778 779 index = floodtype->index; 780 fld->limit[index] = v; 781 if (a && floodprot_valid_alternate_action(a, floodtype)) 782 fld->action[index] = a; 783 else 784 fld->action[index] = floodtype->default_action; 785 if (!floodtype->timedban_required || (floodtype->timedban_required && timedban_available)) 786 fld->remove_after[index] = r; 787 } /* for */ 788 789 /* parse 'per' */ 790 p2++; 791 if (*p2 != ':') 792 return parse_channel_mode_flood_failed(error_out, fld, "Invalid format (:XX period missing)"); 793 p2++; 794 if (!*p2) 795 return parse_channel_mode_flood_failed(error_out, fld, "Invalid format (:XX period missing)"); 796 v = atoi(p2); 797 if (v < 1) 798 v = 1; 799 800 /* If new 'per xxx seconds' is smaller than current 'per' then reset timers/counters (t, c) */ 801 if (v < fld->per) 802 { 803 int i; 804 for (i=0; i < NUMFLD; i++) 805 { 806 fld->timer[i] = 0; 807 fld->counter[i] = 0; 808 fld->counter_unknown_users[i] = 0; 809 } 810 } 811 fld->per = v; 812 813 /* Is anything turned on? (to stop things like '+f []:15' */ 814 breakit = 1; 815 for (v=0; v < NUMFLD; v++) 816 if (fld->limit[v]) 817 breakit=0; 818 if (breakit) 819 { 820 /* Nothing is turned on.. */ 821 if (*warn_unknown_flood_type) 822 return parse_channel_mode_flood_failed(error_out, fld, "Unknown flood type(s) '%s'", warn_unknown_flood_type); 823 return parse_channel_mode_flood_failed(error_out, fld, "None of the floodtypes set"); 824 } 825 826 /* Finally, this is a warning only */ 827 if (*warn_unknown_flood_type && error_out) 828 { 829 snprintf(retbuf, sizeof(retbuf), "Unknown flood type(s) '%s'", warn_unknown_flood_type); 830 *error_out = retbuf; 831 } 832 833 return 1; 834 } 835 836 int cmodef_is_ok(Client *client, Channel *channel, char mode, const char *param, int type, int what) 837 { 838 if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR)) 839 { 840 if (IsUser(client) && check_channel_access(client, channel, "oaq")) 841 return EX_ALLOW; 842 if (type == EXCHK_ACCESS_ERR) /* can only be due to being halfop */ 843 sendnumeric(client, ERR_NOTFORHALFOPS, 'f'); 844 return EX_DENY; 845 } else 846 if (type == EXCHK_PARAM) 847 { 848 ChannelFloodProtection fld; 849 const char *err; 850 851 memset(&fld, 0, sizeof(fld)); 852 if (!parse_channel_mode_flood(param, &fld, 1, client, &err)) 853 { 854 sendnumeric(client, ERR_CANNOTCHANGECHANMODE, 'f', err); 855 return EX_DENY; 856 } else if (err) 857 { 858 sendnotice(client, "WARNING: Channel mode +f: %s", err); 859 /* fallthrough */ 860 } 861 return EX_ALLOW; 862 } 863 864 /* fallthrough -- should not be used */ 865 return EX_DENY; 866 } 867 868 void *cmodef_put_param(void *fld_in, const char *param) 869 { 870 ChannelFloodProtection *fld = (ChannelFloodProtection *)fld_in; 871 int v; 872 873 if (!fld) 874 fld = safe_alloc(sizeof(ChannelFloodProtection)); 875 876 parse_channel_mode_flood(param, fld, 0, NULL, NULL); 877 878 return fld; 879 } 880 881 const char *cmodef_get_param(void *r_in) 882 { 883 ChannelFloodProtection *r = (ChannelFloodProtection *)r_in; 884 static char retbuf[512]; 885 886 if (!r) 887 return NULL; 888 889 channel_modef_string(r, retbuf); 890 return retbuf; 891 } 892 893 /** Convert parameter to something proper. 894 * NOTE: client may be NULL if called for e.g. set::modes-on-join 895 */ 896 const char *cmodef_conv_param(const char *param_in, Client *client, Channel *channel) 897 { 898 static char retbuf[256]; 899 ChannelFloodProtection fld; 900 const char *err; 901 902 memset(&fld, 0, sizeof(fld)); 903 if (!parse_channel_mode_flood(param_in, &fld, 0, client, &err)) 904 return NULL; 905 906 *retbuf = '\0'; 907 channel_modef_string(&fld, retbuf); 908 return retbuf; 909 } 910 911 int cmodef_free_param(void *r, int soft) 912 { 913 ChannelFloodProtection *fld = (ChannelFloodProtection *)r; 914 915 if (!fld) 916 return 0; 917 918 if (soft && fld->profile && cfg.default_profile) 919 { 920 /* Resist freeing */ 921 if (strcmp(fld->profile, cfg.default_profile)) 922 { 923 /* But reset */ 924 ChannelFloodProtection *base = get_channel_flood_profile(cfg.default_profile); 925 if (!base) 926 base = get_channel_flood_profile("normal"); /* fallback, always exists */ 927 inherit_settings(base, fld); 928 safe_strdup(fld->profile, base->profile); 929 } 930 return 1; /* NO FREE */ 931 } else 932 { 933 // TODO: consider cancelling timers just to be sure? or maybe in DEBUGMODE? 934 safe_free(fld->profile); 935 safe_free(r); 936 } 937 return 0; 938 } 939 940 void *cmodef_dup_struct(void *r_in) 941 { 942 ChannelFloodProtection *r = (ChannelFloodProtection *)r_in; 943 ChannelFloodProtection *w = safe_alloc(sizeof(ChannelFloodProtection)); 944 945 /* We can copy most members in a lazy way... */ 946 memcpy(w, r, sizeof(ChannelFloodProtection)); 947 /* ... except this one. 948 * NOTE: can't use safe_strdup() here because 949 * w->profile = r->profile at this point due 950 * to the memcpy and safe_strdup() would free 951 * it (and thus both). 952 */ 953 if (r->profile) 954 w->profile = raw_strdup(r->profile); 955 return (void *)w; 956 } 957 958 int cmodef_sjoin_check(Channel *channel, void *ourx, void *theirx) 959 { 960 ChannelFloodProtection *our = (ChannelFloodProtection *)ourx; 961 ChannelFloodProtection *their = (ChannelFloodProtection *)theirx; 962 int i; 963 964 if (compare_floodprot_modes(our, their) == 0) 965 return EXSJ_SAME; 966 967 our->per = MAX(our->per, their->per); 968 for (i=0; i < NUMFLD; i++) 969 { 970 our->limit[i] = MAX(our->limit[i], their->limit[i]); 971 our->action[i] = MAX(our->action[i], their->action[i]); 972 our->remove_after[i] = MAX(our->remove_after[i], their->remove_after[i]); 973 } 974 975 return EXSJ_MERGE; 976 } 977 978 void floodprot_show_profiles(Client *client) 979 { 980 ChannelFloodProfile *fld; 981 char buf[512]; 982 int padding; 983 int max_length = 0; 984 985 sendnotice(client, "List of available flood profiles for +F:"); 986 for (fld = channel_flood_profiles; fld; fld = fld->next) 987 { 988 int n = strlen(fld->settings.profile); 989 if (n > max_length) 990 max_length = n; 991 } 992 993 for (fld = channel_flood_profiles; fld; fld = fld->next) 994 { 995 *buf = '\0'; 996 channel_modef_string(&fld->settings, buf); 997 padding = max_length - strlen(fld->settings.profile); 998 sendnotice(client, " %*s%s: %s", 999 padding, "", 1000 fld->settings.profile, 1001 buf); 1002 } 1003 sendnotice(client, "See also https://www.unrealircd.org/docs/Channel_anti-flood_settings"); 1004 } 1005 1006 int cmodef_profile_is_ok(Client *client, Channel *channel, char mode, const char *param, int type, int what) 1007 { 1008 if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR)) 1009 { 1010 if (IsUser(client) && check_channel_access(client, channel, "oaq")) 1011 return EX_ALLOW; 1012 if (type == EXCHK_ACCESS_ERR) /* can only be due to being halfop */ 1013 sendnumeric(client, ERR_NOTFORHALFOPS, 'f'); 1014 return EX_DENY; 1015 } else 1016 if (type == EXCHK_PARAM) 1017 { 1018 if (get_channel_flood_profile(param)) 1019 return EX_ALLOW; 1020 sendnumeric(client, ERR_CANNOTCHANGECHANMODE, 'F', "Invalid flood profile specified for +F"); 1021 floodprot_show_profiles(client); 1022 return EX_DENY; 1023 } 1024 1025 /* fallthrough -- should not be used */ 1026 return EX_DENY; 1027 } 1028 1029 void inherit_settings(ChannelFloodProtection *from, ChannelFloodProtection *to) 1030 { 1031 int i; 1032 1033 /* If new 'per xxx seconds' is smaller than current 'per' then reset timers/counters (t, c) */ 1034 if (from->per < to->per) 1035 { 1036 for (i=0; i < NUMFLD; i++) 1037 { 1038 to->timer[i] = 0; 1039 to->counter[i] = 0; 1040 to->counter_unknown_users[i] = 0; 1041 } 1042 } 1043 1044 /* inherit settings (limit/action/remove_after) */ 1045 for (i=0; i < NUMFLD; i++) 1046 { 1047 to->limit[i] = from->limit[i]; 1048 to->action[i] = from->action[i]; 1049 to->remove_after[i] = from->remove_after[i]; 1050 } 1051 to->per = from->per; 1052 } 1053 1054 void *cmodef_profile_put_param(void *fld_in, const char *param) 1055 { 1056 ChannelFloodProtection *fld = (ChannelFloodProtection *)fld_in; 1057 ChannelFloodProtection *base; 1058 int v; 1059 1060 if (!fld) 1061 fld = safe_alloc(sizeof(ChannelFloodProtection)); 1062 1063 base = get_channel_flood_profile(param); 1064 if (!base) 1065 base = get_channel_flood_profile("normal"); /* fallback, always exists */ 1066 1067 safe_strdup(fld->profile, param); 1068 inherit_settings(base, fld); 1069 1070 return (void *)fld; 1071 } 1072 1073 const char *cmodef_profile_get_param(void *r_in) 1074 { 1075 ChannelFloodProtection *r = (ChannelFloodProtection *)r_in; 1076 static char retbuf[512]; 1077 1078 if (!r) 1079 return NULL; 1080 1081 strlcpy(retbuf, r->profile ? r->profile : "???", sizeof(retbuf)); 1082 return retbuf; 1083 } 1084 1085 /** Convert parameter to something proper. 1086 * NOTE: client may be NULL if called for e.g. set::modes-on-join 1087 */ 1088 const char *cmodef_profile_conv_param(const char *param_in, Client *client, Channel *channel) 1089 { 1090 static char retbuf[256]; 1091 ChannelFloodProtection *fld; 1092 1093 fld = get_channel_flood_profile(param_in); 1094 if (!fld) 1095 return NULL; 1096 1097 strlcpy(retbuf, fld->profile, sizeof(retbuf)); 1098 1099 return retbuf; 1100 } 1101 1102 int cmodef_profile_sjoin_check(Channel *channel, void *ourx, void *theirx) 1103 { 1104 ChannelFloodProtection *our = (ChannelFloodProtection *)ourx; 1105 ChannelFloodProtection *their = (ChannelFloodProtection *)theirx; 1106 int i; 1107 1108 if (!strcmp(our->profile, their->profile)) 1109 return EXSJ_SAME; 1110 1111 if (strcmp(our->profile, their->profile) < 0) 1112 return EXSJ_THEYWON; 1113 1114 return EXSJ_WEWON; 1115 } 1116 1117 int is_floodprot_exempt(Client *client, Channel *channel, char flood_type_letter) 1118 { 1119 Ban *ban; 1120 char *p; 1121 BanContext *b = safe_alloc(sizeof(BanContext)); 1122 1123 b->client = client; 1124 b->channel = channel; 1125 b->ban_check_types = BANCHK_MSG; 1126 1127 for (ban = channel->exlist; ban; ban=ban->next) 1128 { 1129 char *p, *x; 1130 char *matchby; 1131 char type[16]; 1132 1133 if (!strncmp(ban->banstr, "~F:", 3)) 1134 p = ban->banstr + 3; 1135 else if (!strncmp(ban->banstr, "~flood:", 7)) 1136 p = ban->banstr + 7; 1137 else 1138 continue; 1139 1140 strlcpy(type, p, sizeof(type)); 1141 x = strchr(type, ':'); 1142 if (x) 1143 *x = '\0'; 1144 1145 if (!strcmp(type, "*") || strchr(type, flood_type_letter)) 1146 { 1147 matchby = strchr(p, ':'); 1148 if (!matchby) 1149 continue; 1150 matchby++; 1151 1152 b->banstr = matchby; 1153 if (ban_check_mask(b)) 1154 { 1155 safe_free(b); 1156 return HOOK_ALLOW; /* Yes, user is exempt */ 1157 } 1158 } 1159 } 1160 1161 safe_free(b); 1162 return HOOK_CONTINUE; /* No, may NOT bypass. */ 1163 } 1164 1165 int floodprot_join(Client *client, Channel *channel, MessageTag *mtags) 1166 { 1167 /* I'll explain this only once: 1168 * 1. if channel is +f 1169 * 2. local client OR synced server 1170 * 3. server uptime more than XX seconds (if this information is available) 1171 * 4. is not a uline 1172 * call do_floodprot, which will: 1173 * 5. then, increase floodcounter 1174 * 6. if we reached the limit AND only if source was a local client.. do the action (+i). 1175 * Nr 6 is done because otherwise you would have a noticeflood with 'joinflood detected' 1176 * from all servers. 1177 */ 1178 if (IsFloodLimit(channel) && 1179 (MyUser(client) || client->uplink->server->flags.synced) && 1180 (client->uplink->server->boottime && (TStime() - client->uplink->server->boottime >= cfg.boot_delay)) && 1181 (TStime() - floodprot_splittime >= cfg.split_delay) && 1182 !IsULine(client)) 1183 { 1184 do_floodprot(channel, client, CHFLD_JOIN); 1185 } 1186 return 0; 1187 } 1188 1189 int cmodef_cleanup_user2(Client *client) 1190 { 1191 return 0; 1192 } 1193 1194 /** Install a default +F profile, if set::anti-flood::channel::default-profile is set */ 1195 int cmodef_channel_create(Channel *channel) 1196 { 1197 ChannelFloodProtection *base; 1198 ChannelFloodProtection *fld; 1199 1200 if (!cfg.default_profile) 1201 return 0; 1202 1203 base = get_channel_flood_profile(cfg.default_profile); 1204 if (!base) 1205 base = get_channel_flood_profile("normal"); /* fallback, always exists */ 1206 1207 GETPARASTRUCT(channel, 'F') = fld = safe_alloc(sizeof(ChannelFloodProtection)); 1208 inherit_settings(base, fld); 1209 safe_strdup(fld->profile, base->profile); 1210 1211 return 0; 1212 } 1213 1214 int cmodef_channel_destroy(Channel *channel, int *should_destroy) 1215 { 1216 floodprottimer_stopchantimers(channel); 1217 return 0; 1218 } 1219 1220 /* [just a helper for channel_modef_string()] */ 1221 static inline char *chmodefstrhelper(char *buf, char t, char tdef, unsigned short l, unsigned char a, unsigned char r) 1222 { 1223 char *p; 1224 char tmpbuf[16], *p2 = tmpbuf; 1225 1226 sprintf(buf, "%hd", l); 1227 p = buf + strlen(buf); 1228 *p++ = t; 1229 if (a && ((a != tdef) || r)) 1230 { 1231 *p++ = '#'; 1232 *p++ = a; 1233 if (r) 1234 { 1235 sprintf(tmpbuf, "%hd", (short)r); 1236 while ((*p = *p2++)) 1237 p++; 1238 } 1239 } 1240 *p++ = ','; 1241 return p; 1242 } 1243 1244 /** returns the channelmode +f string (ie: '[5k,40j]:10'). 1245 * 'retbuf' is suggested to be of size 512, which is more than X times the maximum (for safety). 1246 */ 1247 char *channel_modef_string(ChannelFloodProtection *x, char *retbuf) 1248 { 1249 int i; 1250 char *p = retbuf; 1251 FloodType *f; 1252 1253 *p++ = '['; 1254 1255 for (i=0; i < ARRAY_SIZEOF(floodtypes); i++) 1256 { 1257 f = &floodtypes[i]; 1258 if (x->limit[f->index]) 1259 p = chmodefstrhelper(p, f->letter, f->default_action, x->limit[f->index], x->action[f->index], x->remove_after[f->index]); 1260 } 1261 1262 if (*(p - 1) == ',') 1263 p--; 1264 *p++ = ']'; 1265 sprintf(p, ":%hd", x->per); 1266 return retbuf; 1267 } 1268 1269 ChannelFloodProtection *get_channel_flood_settings(Channel *channel, int what) 1270 { 1271 ChannelFloodProtection *fld; 1272 1273 if (channel->mode.mode & EXTMODE_FLOODLIMIT) 1274 { 1275 fld = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'f'); 1276 if (fld->action[what]) 1277 return fld; 1278 } 1279 1280 fld = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'F'); 1281 if (fld && fld->action[what]) 1282 return fld; 1283 1284 return NULL; 1285 } 1286 1287 int floodprot_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype) 1288 { 1289 Membership *mb; 1290 ChannelFloodProtection *fld; 1291 MemberFlood *memberflood; 1292 uint64_t msghash; 1293 unsigned char is_flooding_text=0, is_flooding_repeat=0; 1294 static char errbuf[256]; 1295 1296 /* This is redundant, right? */ 1297 if (!MyUser(client)) 1298 return HOOK_CONTINUE; 1299 1300 if (sendtype == SEND_TYPE_TAGMSG) 1301 return 0; // TODO: some TAGMSG specific limit? (1 of 2) 1302 1303 if (ValidatePermissionsForPath("channel:override:flood",client,NULL,channel,NULL) || !IsFloodLimit(channel) || check_channel_access(client, channel, "hoaq")) 1304 return HOOK_CONTINUE; 1305 1306 if (!(mb = find_membership_link(client->user->channel, channel))) 1307 return HOOK_CONTINUE; /* not in channel */ 1308 1309 /* config test rejects having 't' in +F and 'r' in +f or vice versa, 1310 * otherwise we would be screwed here :D. 1311 */ 1312 fld = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'f'); 1313 1314 if (!fld || !(fld->limit[CHFLD_TEXT] || fld->limit[CHFLD_REPEAT])) 1315 return HOOK_CONTINUE; 1316 1317 if (moddata_membership(mb, mdflood).ptr == NULL) 1318 { 1319 /* Alloc a new entry if it doesn't exist yet */ 1320 moddata_membership(mb, mdflood).ptr = safe_alloc(sizeof(MemberFlood)); 1321 } 1322 1323 memberflood = (MemberFlood *)moddata_membership(mb, mdflood).ptr; 1324 1325 if ((TStime() - memberflood->firstmsg) >= fld->per) 1326 { 1327 /* Reset due to moving into a new time slot */ 1328 memberflood->firstmsg = TStime(); 1329 memberflood->nmsg = 1; 1330 memberflood->nmsg_repeat = 1; 1331 if (fld->limit[CHFLD_REPEAT]) 1332 { 1333 memberflood->lastmsg = gen_floodprot_msghash(*msg); 1334 memberflood->prevmsg = 0; 1335 } 1336 return HOOK_CONTINUE; /* forget about it.. */ 1337 } 1338 1339 /* Anti-repeat ('r') */ 1340 if (fld->limit[CHFLD_REPEAT]) 1341 { 1342 msghash = gen_floodprot_msghash(*msg); 1343 if (memberflood->lastmsg) 1344 { 1345 if ((memberflood->lastmsg == msghash) || (memberflood->prevmsg == msghash)) 1346 { 1347 memberflood->nmsg_repeat++; 1348 if (memberflood->nmsg_repeat > fld->limit[CHFLD_REPEAT]) 1349 is_flooding_repeat = 1; 1350 } 1351 memberflood->prevmsg = memberflood->lastmsg; 1352 } 1353 memberflood->lastmsg = msghash; 1354 } 1355 1356 if (fld->limit[CHFLD_TEXT]) 1357 { 1358 /* increase msgs */ 1359 memberflood->nmsg++; 1360 if (memberflood->nmsg > fld->limit[CHFLD_TEXT]) 1361 is_flooding_text = 1; 1362 } 1363 1364 /* Do we need to take any action? */ 1365 if (is_flooding_text || is_flooding_repeat) 1366 { 1367 char mask[256]; 1368 MessageTag *mtags; 1369 int flood_type; 1370 1371 if ((is_flooding_text && is_floodprot_exempt(client, channel, 't')) || 1372 (is_flooding_repeat && is_floodprot_exempt(client, channel, 'r'))) 1373 { 1374 /* No action taken */ 1375 memberflood->nmsg = 0; 1376 memberflood->nmsg_repeat = 0; 1377 return HOOK_CONTINUE; 1378 } 1379 1380 /* Repeat takes precedence over text flood */ 1381 if (is_flooding_repeat) 1382 { 1383 snprintf(errbuf, sizeof(errbuf), "Flooding (Your last message is too similar to previous ones)"); 1384 flood_type = CHFLD_REPEAT; 1385 } else 1386 { 1387 snprintf(errbuf, sizeof(errbuf), "Flooding (Limit is %i lines per %i seconds)", fld->limit[CHFLD_TEXT], fld->per); 1388 flood_type = CHFLD_TEXT; 1389 } 1390 1391 if (fld->action[flood_type] == 'd') 1392 { 1393 /* Drop the message */ 1394 *errmsg = errbuf; 1395 return HOOK_DENY; 1396 } 1397 1398 if (fld->action[flood_type] == 'b') 1399 { 1400 /* Ban the user */ 1401 if (timedban_available && (fld->remove_after[flood_type] > 0)) 1402 { 1403 if (iConf.named_extended_bans) 1404 snprintf(mask, sizeof(mask), "~time:%d:*!*@%s", fld->remove_after[flood_type], GetHost(client)); 1405 else 1406 snprintf(mask, sizeof(mask), "~t:%d:*!*@%s", fld->remove_after[flood_type], GetHost(client)); 1407 } else { 1408 snprintf(mask, sizeof(mask), "*!*@%s", GetHost(client)); 1409 } 1410 if (add_listmode(&channel->banlist, &me, channel, mask) == 0) 1411 { 1412 mtags = NULL; 1413 new_message(&me, NULL, &mtags); 1414 sendto_server(NULL, 0, 0, mtags, ":%s MODE %s +b %s 0", me.id, channel->name, mask); 1415 sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, 1416 ":%s MODE %s +b %s", me.name, channel->name, mask); 1417 free_message_tags(mtags); 1418 } /* else.. ban list is full */ 1419 } 1420 mtags = NULL; 1421 kick_user(NULL, channel, &me, client, errbuf); 1422 *errmsg = errbuf; /* not used, but needs to be set */ 1423 return HOOK_DENY; 1424 } 1425 return HOOK_CONTINUE; 1426 } 1427 1428 int floodprot_post_chanmsg(Client *client, Channel *channel, int sendflags, const char *prefix, const char *target, MessageTag *mtags, const char *text, SendType sendtype) 1429 { 1430 if (!IsFloodLimit(channel) || check_channel_access(client, channel, "hoaq") || IsULine(client)) 1431 return 0; 1432 1433 if (sendtype == SEND_TYPE_TAGMSG) 1434 return 0; // TODO: some TAGMSG specific limit? (2 of 2) 1435 1436 /* HINT: don't be so stupid to reorder the items in the if's below.. you'll break things -- Syzop. */ 1437 1438 do_floodprot(channel, client, CHFLD_MSG); 1439 1440 if ((text[0] == '\001') && strncmp(text+1, "ACTION ", 7)) 1441 do_floodprot(channel, client, CHFLD_CTCP); 1442 1443 return 0; 1444 } 1445 1446 int floodprot_knock(Client *client, Channel *channel, MessageTag *mtags, const char *comment) 1447 { 1448 if (IsFloodLimit(channel) && !IsULine(client)) 1449 do_floodprot(channel, client, CHFLD_KNOCK); 1450 return 0; 1451 } 1452 1453 int floodprot_nickchange(Client *client, MessageTag *mtags, const char *oldnick) 1454 { 1455 Membership *mp; 1456 1457 /* Ignore u-lines, as usual */ 1458 if (IsULine(client)) 1459 return 0; 1460 1461 /* Don't count forced nick changes, eg from NickServ */ 1462 if (find_mtag(mtags, "unrealircd.org/issued-by")) 1463 return 0; 1464 1465 for (mp = client->user->channel; mp; mp = mp->next) 1466 { 1467 Channel *channel = mp->channel; 1468 if (channel && IsFloodLimit(channel) && !check_channel_access_membership(mp, "vhoaq")) 1469 { 1470 do_floodprot(channel, client, CHFLD_NICK); 1471 } 1472 } 1473 return 0; 1474 } 1475 1476 void floodprot_chanmode_del_helper(ChannelFloodProtection *fld, char modechar) 1477 { 1478 /* reset joinflood on -i, reset msgflood on -m, etc.. */ 1479 switch(modechar) 1480 { 1481 case 'C': 1482 fld->counter[CHFLD_CTCP] = 0; 1483 fld->counter_unknown_users[CHFLD_CTCP] = 0; 1484 break; 1485 case 'N': 1486 fld->counter[CHFLD_NICK] = 0; 1487 fld->counter_unknown_users[CHFLD_NICK] = 0; 1488 break; 1489 case 'm': 1490 fld->counter[CHFLD_MSG] = 0; 1491 fld->counter[CHFLD_CTCP] = 0; 1492 fld->counter_unknown_users[CHFLD_MSG] = 0; 1493 fld->counter_unknown_users[CHFLD_CTCP] = 0; 1494 break; 1495 case 'K': 1496 fld->counter[CHFLD_KNOCK] = 0; 1497 fld->counter_unknown_users[CHFLD_KNOCK] = 0; 1498 break; 1499 case 'i': 1500 fld->counter[CHFLD_JOIN] = 0; 1501 fld->counter_unknown_users[CHFLD_JOIN] = 0; 1502 break; 1503 case 'M': 1504 fld->counter[CHFLD_MSG] = 0; 1505 fld->counter[CHFLD_CTCP] = 0; 1506 fld->counter_unknown_users[CHFLD_MSG] = 0; 1507 fld->counter_unknown_users[CHFLD_CTCP] = 0; 1508 break; 1509 case 'R': 1510 fld->counter[CHFLD_JOIN] = 0; 1511 fld->counter_unknown_users[CHFLD_JOIN] = 0; 1512 break; 1513 default: 1514 break; 1515 } 1516 } 1517 1518 int floodprot_chanmode_del(Channel *channel, int modechar) 1519 { 1520 ChannelFloodProtection *fld; 1521 1522 if (!IsFloodLimit(channel)) 1523 return 0; 1524 1525 fld = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'f'); 1526 if (fld) 1527 { 1528 floodprot_chanmode_del_helper(fld, modechar); 1529 floodprottimer_del(channel, fld, modechar); 1530 } 1531 1532 fld = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'F'); 1533 if (fld) 1534 { 1535 floodprot_chanmode_del_helper(fld, modechar); 1536 floodprottimer_del(channel, fld, modechar); 1537 } 1538 1539 return 0; 1540 } 1541 1542 RemoveChannelModeTimer *floodprottimer_find(Channel *channel, char mflag) 1543 { 1544 RemoveChannelModeTimer *e; 1545 1546 for (e=removechannelmodetimer_list; e; e=e->next) 1547 { 1548 if ((e->channel == channel) && (e->m == mflag)) 1549 return e; 1550 } 1551 return NULL; 1552 } 1553 1554 /** strcat-like */ 1555 void strccat(char *s, char c) 1556 { 1557 for (; *s; s++); 1558 *s++ = c; 1559 *s++ = '\0'; 1560 } 1561 1562 /* 1563 * Adds a "remove channelmode set by +f" timer. 1564 * channel Channel 1565 * mflag Mode flag, eg 'C' 1566 * when when it should be removed 1567 * NOTES: 1568 * - This function takes care of overwriting of any previous timer 1569 * for the same modechar. 1570 * - The function takes care of channel->mode.floodprot->timers_running, 1571 * do not modify it yourself. 1572 * - channel->mode.floodprot is asumed to be non-NULL. 1573 */ 1574 void floodprottimer_add(Channel *channel, ChannelFloodProtection *fld, char mflag, time_t when) 1575 { 1576 RemoveChannelModeTimer *e = NULL; 1577 unsigned char add=1; 1578 1579 if (strchr(fld->timers_running, mflag)) 1580 { 1581 /* Already exists... */ 1582 e = floodprottimer_find(channel, mflag); 1583 if (e) 1584 add = 0; 1585 } 1586 1587 if (!strchr(fld->timers_running, mflag)) 1588 { 1589 if (strlen(fld->timers_running)+1 >= sizeof(fld->timers_running)) 1590 { 1591 unreal_log(ULOG_WARNING, "flood", "BUG_FLOODPROTTIMER_ADD", NULL, 1592 "[BUG] floodprottimer_add: too many timers running for $channel ($timers_running)", 1593 log_data_channel("channel", channel), 1594 log_data_string("timers_running", fld->timers_running)); 1595 return; 1596 } 1597 strccat(fld->timers_running, mflag); /* bounds already checked ^^ */ 1598 } 1599 1600 if (add) 1601 e = safe_alloc(sizeof(RemoveChannelModeTimer)); 1602 1603 e->channel = channel; 1604 e->m = mflag; 1605 e->when = when; 1606 1607 if (add) 1608 AddListItem(e, removechannelmodetimer_list); 1609 } 1610 1611 void floodprottimer_del(Channel *channel, ChannelFloodProtection *fld, char mflag) 1612 { 1613 RemoveChannelModeTimer *e; 1614 1615 if (fld && !strchr(fld->timers_running, mflag)) 1616 return; /* nothing to remove.. */ 1617 e = floodprottimer_find(channel, mflag); 1618 if (!e) 1619 return; 1620 1621 DelListItem(e, removechannelmodetimer_list); 1622 safe_free(e); 1623 1624 if (fld) 1625 { 1626 char newtf[MAXCHMODEFACTIONS+1]; 1627 char *i, *o; 1628 for (i=fld->timers_running, o=newtf; *i; i++) 1629 if (*i != mflag) 1630 *o++ = *i; 1631 *o = '\0'; 1632 strcpy(fld->timers_running, newtf); /* always shorter (or equal) */ 1633 } 1634 } 1635 1636 EVENT(modef_event) 1637 { 1638 RemoveChannelModeTimer *e, *e_next; 1639 time_t now; 1640 1641 now = TStime(); 1642 1643 for (e = removechannelmodetimer_list; e; e = e_next) 1644 { 1645 e_next = e->next; 1646 if (e->when <= now) 1647 { 1648 /* Remove chanmode... */ 1649 Cmode_t extmode = get_extmode_bitbychar(e->m); 1650 1651 if (extmode && (e->channel->mode.mode & extmode)) 1652 { 1653 MessageTag *mtags = NULL; 1654 1655 new_message(&me, NULL, &mtags); 1656 sendto_server(NULL, 0, 0, mtags, ":%s MODE %s -%c 0", me.id, e->channel->name, e->m); 1657 sendto_channel(e->channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, 1658 ":%s MODE %s -%c", 1659 me.name, e->channel->name, e->m); 1660 free_message_tags(mtags); 1661 e->channel->mode.mode &= ~extmode; 1662 } 1663 1664 /* And delete... */ 1665 DelListItem(e, removechannelmodetimer_list); 1666 safe_free(e); 1667 } 1668 } 1669 } 1670 1671 void floodprottimer_stopchantimers(Channel *channel) 1672 { 1673 RemoveChannelModeTimer *e, *e_next; 1674 1675 for (e = removechannelmodetimer_list; e; e = e_next) 1676 { 1677 e_next = e->next; 1678 if (e->channel == channel) 1679 { 1680 DelListItem(e, removechannelmodetimer_list); 1681 safe_free(e); 1682 } 1683 } 1684 } 1685 1686 int do_floodprot(Channel *channel, Client *client, int what) 1687 { 1688 ChannelFloodProtection *fld = get_channel_flood_settings(channel, what); 1689 FloodType *floodtype = find_floodprot_by_index(what); 1690 char unknown_user; 1691 1692 if (!fld) 1693 return 0; /* no +f active */ 1694 1695 if (floodtype && is_floodprot_exempt(client, channel, floodtype->letter)) 1696 return 0; /* exempt: not counted and no action taken */ 1697 1698 unknown_user = user_allowed_by_security_group_name(client, "known-users") ? 0 : 1; 1699 1700 if (fld->limit[what]) 1701 { 1702 if (TStime() - fld->timer[what] >= fld->per) 1703 { 1704 /* reset */ 1705 fld->timer[what] = TStime(); 1706 fld->counter[what] = 1; 1707 fld->counter_unknown_users[what] = unknown_user; 1708 } else 1709 { 1710 fld->counter[what]++; 1711 1712 if (unknown_user) 1713 fld->counter_unknown_users[what]++; 1714 1715 if ((fld->counter[what] > fld->limit[what]) && 1716 (TStime() - fld->timer[what] < fld->per)) 1717 { 1718 if (MyUser(client)) 1719 do_floodprot_action(channel, what); 1720 return 1; /* flood detected! */ 1721 } 1722 } 1723 } 1724 return 0; 1725 } 1726 1727 /** Helper for do_floodprot_action() - standard action +i/+R/etc.. */ 1728 void do_floodprot_action_standard(Channel *channel, int what, FloodType *floodtype, Cmode_t extmode, char m) 1729 { 1730 ChannelFloodProtection *fld = get_channel_flood_settings(channel, what); 1731 char comment[512], target[CHANNELLEN + 8]; 1732 MessageTag *mtags; 1733 const char *text = floodtype->description; 1734 1735 /* First the notice to the chanops */ 1736 mtags = NULL; 1737 new_message(&me, NULL, &mtags); 1738 ircsnprintf(comment, sizeof(comment), "*** Channel %s detected (limit is %d per %d seconds), setting mode +%c", 1739 text, fld->limit[what], fld->per, m); 1740 ircsnprintf(target, sizeof(target), "%%%s", channel->name); 1741 sendto_channel(channel, &me, NULL, "ho", 1742 0, SEND_ALL, mtags, 1743 ":%s NOTICE %s :%s", me.name, target, comment); 1744 free_message_tags(mtags); 1745 1746 /* Then the MODE broadcast */ 1747 mtags = NULL; 1748 new_message(&me, NULL, &mtags); 1749 sendto_server(NULL, 0, 0, mtags, ":%s MODE %s +%c 0", me.id, channel->name, m); 1750 sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s +%c", me.name, channel->name, m); 1751 free_message_tags(mtags); 1752 1753 /* Actually set the mode internally */ 1754 channel->mode.mode |= extmode; 1755 1756 /* Add remove-chanmode timer */ 1757 if (fld->remove_after[what]) 1758 { 1759 floodprottimer_add(channel, fld, m, TStime() + ((long)fld->remove_after[what] * 60) - 5); 1760 /* (since the floodprot timer event is called every 10s, we do -5 here so the accurancy will 1761 * be -5..+5, without it it would be 0..+10.) 1762 */ 1763 } 1764 } 1765 1766 /** Helper for do_floodprot_action() - alternative action like +b ~security-group:unknown-users */ 1767 int do_floodprot_action_alternative(Channel *channel, int what, FloodType *floodtype) 1768 { 1769 ChannelFloodProtection *fld = get_channel_flood_settings(channel, what); 1770 char ban[512]; 1771 char comment[512], target[CHANNELLEN + 8]; 1772 MessageTag *mtags; 1773 const char *text = floodtype->description; 1774 1775 snprintf(ban, sizeof(ban), "~time:%d:%s", 1776 fld->remove_after[what] ? fld->remove_after[what] : cfg.modef_alternative_ban_action_unsettime, 1777 floodtype->alternative_ban_action); 1778 1779 /* Add the ban internally */ 1780 if (add_listmode(&channel->banlist, &me, channel, ban) == -1) 1781 return 0; /* ban list full (or ban already exists) */ 1782 1783 /* First the notice to the chanops */ 1784 mtags = NULL; 1785 new_message(&me, NULL, &mtags); 1786 ircsnprintf(comment, sizeof(comment), 1787 "*** Channel %s detected (limit is %d per %d seconds), " 1788 "mostly caused by 'unknown-users', setting mode +b %s", 1789 text, fld->limit[what], fld->per, ban); 1790 ircsnprintf(target, sizeof(target), "%%%s", channel->name); 1791 sendto_channel(channel, &me, NULL, "ho", 1792 0, SEND_ALL, mtags, 1793 ":%s NOTICE %s :%s", me.name, target, comment); 1794 free_message_tags(mtags); 1795 1796 /* Then the MODE broadcast */ 1797 mtags = NULL; 1798 new_message(&me, NULL, &mtags); 1799 sendto_server(NULL, 0, 0, mtags, ":%s MODE %s +b %s 0", me.id, channel->name, ban); 1800 sendto_channel(channel, &me, NULL, 0, 0, SEND_LOCAL, mtags, ":%s MODE %s +b %s", me.name, channel->name, ban); 1801 free_message_tags(mtags); 1802 1803 return 1; 1804 } 1805 1806 1807 void do_floodprot_action(Channel *channel, int what) 1808 { 1809 Cmode_t extmode = 0; 1810 ChannelFloodProtection *fld = get_channel_flood_settings(channel, what); 1811 FloodType *floodtype = find_floodprot_by_index(what); 1812 char ban_exists; 1813 double perc; 1814 char m; 1815 1816 if (!fld || !floodtype) 1817 return; 1818 1819 /* For drop action we don't actually have to do anything here, but we still have to prevent Unreal 1820 * from setting chmode +d (which is useless against floods anyways) =] 1821 */ 1822 if (fld->action[what] == 'd') 1823 return; 1824 1825 m = fld->action[what]; 1826 if (!m) 1827 return; 1828 1829 extmode = get_extmode_bitbychar(m); 1830 if (!extmode) 1831 return; 1832 1833 if (extmode && (channel->mode.mode & extmode)) 1834 return; /* channel mode is already set, so nothing to do */ 1835 1836 /* Do we have other options, instead of setting the channel +i/etc ? */ 1837 if (floodtype->alternative_ban_action) 1838 { 1839 /* The 'ban_exists' assignments and checks below are carefully ordered and checked. 1840 * Don't try to "optimize" this code by rearranging or scratching certain checks 1841 * or assignments! -- Syzop 1842 */ 1843 ban_exists = ban_exists_ignore_time(channel->banlist, floodtype->alternative_ban_action); 1844 1845 if (!ban_exists) 1846 { 1847 /* Calculate the percentage of unknown-users that is responsible for the action trigger */ 1848 perc = ((double)fld->counter_unknown_users[what] / (double)fld->counter[what])*100; 1849 if (perc >= cfg.modef_alternate_action_percentage_threshold) 1850 { 1851 /* ACTION: We need to add the ban (+b) */ 1852 ban_exists = do_floodprot_action_alternative(channel, what, floodtype); 1853 } 1854 } 1855 1856 /* Now recheck, before we fallback to do_floodprot_action_standard() below, 1857 * taking into account that all unknown-users are banned now 1858 * or will be banned (eg: some actions still go through because of lag 1859 * between servers). 1860 */ 1861 if (ban_exists && (fld->counter[what] - fld->counter_unknown_users[what] <= fld->limit[what])) 1862 return; /* flood limit not reached by known-users group */ 1863 } 1864 1865 /* ACTION: We need to set the channel mode */ 1866 do_floodprot_action_standard(channel, what, floodtype, extmode, m); 1867 } 1868 1869 uint64_t gen_floodprot_msghash(const char *text) 1870 { 1871 int i; 1872 int is_ctcp, is_action; 1873 char *plaintext; 1874 size_t len; 1875 1876 is_ctcp = is_action = 0; 1877 // Remove any control chars (colours/bold/CTCP/etc) and convert it to lowercase before hashing it 1878 if (text[0] == '\001') 1879 { 1880 if (!strncmp(text + 1, "ACTION ", 7)) 1881 is_action = 1; 1882 else 1883 is_ctcp = 1; 1884 } 1885 plaintext = (char *)StripControlCodes(text); 1886 for (i = 0; plaintext[i]; i++) 1887 { 1888 // Don't need to bother with non-printables and various symbols and numbers 1889 if (plaintext[i] > 64) 1890 plaintext[i] = tolower(plaintext[i]); 1891 } 1892 if (is_ctcp || is_action) 1893 { 1894 // Remove the \001 chars around the message 1895 if ((len = strlen(plaintext)) && plaintext[len - 1] == '\001') 1896 plaintext[len - 1] = '\0'; 1897 plaintext++; 1898 if (is_action) 1899 plaintext += 7; 1900 } 1901 1902 return siphash(text, floodprot_msghash_key); 1903 } 1904 1905 // FIXME: REMARK: make sure you can only do a +f/-f once (latest in line wins). 1906 1907 /* Checks if 2 ChannelFloodProtection modes (chmode +f) are different. 1908 * This is a bit more complicated than 1 simple memcmp(a,b,..) because 1909 * counters are also stored in this struct so we have to do 1910 * it manually :( -- Syzop. 1911 */ 1912 static int compare_floodprot_modes(ChannelFloodProtection *a, ChannelFloodProtection *b) 1913 { 1914 if (memcmp(a->limit, b->limit, sizeof(a->limit)) || 1915 memcmp(a->action, b->action, sizeof(a->action)) || 1916 memcmp(a->remove_after, b->remove_after, sizeof(a->remove_after))) 1917 return 1; 1918 else 1919 return 0; 1920 } 1921 1922 void memberflood_free(ModData *md) 1923 { 1924 /* We don't have any struct members (anymore) that need freeing */ 1925 safe_free(md->ptr); 1926 } 1927 1928 int floodprot_stats(Client *client, const char *flag) 1929 { 1930 if (*flag != 'S') 1931 return 0; 1932 1933 sendtxtnumeric(client, "modef-default-unsettime: %hd", (unsigned short)MODEF_DEFAULT_UNSETTIME); 1934 sendtxtnumeric(client, "modef-max-unsettime: %hd", (unsigned short)MODEF_MAX_UNSETTIME); 1935 return 1; 1936 } 1937 1938 /** Admin unloading the floodprot module for good. Bad. */ 1939 void floodprot_free_removechannelmodetimer_list(ModData *m) 1940 { 1941 RemoveChannelModeTimer *e, *e_next; 1942 1943 for (e=removechannelmodetimer_list; e; e=e_next) 1944 { 1945 e_next = e->next; 1946 safe_free(e); 1947 } 1948 } 1949 1950 void floodprot_free_msghash_key(ModData *m) 1951 { 1952 safe_free(floodprot_msghash_key); 1953 } 1954 1955 CMD_OVERRIDE_FUNC(floodprot_override_mode) 1956 { 1957 if (MyUser(client) && (parc == 3) && 1958 (parv[1][0] == '#') && 1959 (!strcasecmp(parv[2], "f") || !strcasecmp(parv[2], "+f"))) 1960 { 1961 /* Query (not set!) request for #channel */ 1962 Channel *channel = find_channel(parv[1]); 1963 ChannelFloodProtection *profile, *advanced; 1964 char buf[512]; 1965 1966 if (!channel) 1967 { 1968 sendnumeric(client, ERR_NOSUCHCHANNEL, parv[1]); 1969 return; 1970 } 1971 1972 advanced = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'f'); 1973 profile = (ChannelFloodProtection *)GETPARASTRUCT(channel, 'F'); 1974 if (!advanced && !profile) 1975 { 1976 sendnotice(client, "No channel mode +f/+F is active on %s", channel->name); 1977 } else 1978 if (advanced && !profile) 1979 { 1980 channel_modef_string(advanced, buf); 1981 sendnotice(client, "Channel '%s' has effective flood setting '%s' (custom settings via +f)", 1982 channel->name, buf); 1983 } else 1984 if (profile && !advanced) 1985 { 1986 channel_modef_string(profile, buf); 1987 sendnotice(client, "Channel '%s' has effective flood setting '%s' (flood profile '%s')", 1988 channel->name, buf, profile->profile); 1989 } else { 1990 /* Both +f and +F are set */ 1991 int v; 1992 ChannelFloodProtection mix; 1993 FloodType *t; 1994 char overridden[64]; 1995 *overridden = '\0'; 1996 memcpy(&mix, profile, sizeof(mix)); 1997 for (v=0; v < NUMFLD; v++) 1998 { 1999 if ((advanced->limit[v]>0) && (mix.limit[v]>0)) 2000 { 2001 mix.limit[v] = 0; 2002 mix.action[v] = 0; 2003 t = find_floodprot_by_index(v); 2004 if (t) 2005 strlcat_letter(overridden, t->letter, sizeof(overridden)); 2006 } 2007 } 2008 channel_modef_string(&mix, buf); 2009 if (*overridden) 2010 { 2011 sendnotice(client, "Channel '%s' uses flood profile '%s', without action(s) '%s' as they are overridden by +f.", 2012 channel->name, profile->profile, overridden); 2013 sendnotice(client, "Effective flood setting via +F: '%s'", buf); 2014 } else { 2015 sendnotice(client, "Channel '%s' has effective flood setting '%s' (flood profile '%s')", 2016 channel->name, buf, profile->profile); 2017 } 2018 channel_modef_string(advanced, buf); 2019 sendnotice(client, "Plus flood setting via +f: '%s'", buf); 2020 } 2021 sendnotice(client, "-"); 2022 floodprot_show_profiles(client); 2023 return; 2024 } 2025 2026 CALL_NEXT_COMMAND_OVERRIDE(); 2027 } 2028 2029 int floodprot_server_quit(Client *client, MessageTag *mtags) 2030 { 2031 if (!IsULine(client)) 2032 floodprot_splittime = TStime(); 2033 return 0; 2034 } 2035 2036 void reapply_profiles(void) 2037 { 2038 Channel *channel; 2039 2040 for (channel = channels; channel; channel=channel->nextch) 2041 { 2042 ChannelFloodProtection *fld = GETPARASTRUCT(channel, 'F'); 2043 ChannelFloodProtection *base; 2044 2045 if (channel->mode.mode & EXTMODE_FLOOD_PROFILE) 2046 { 2047 /* Channel is +F: simply re-apply the setting the +F <profile> */ 2048 base = get_channel_flood_profile(fld->profile); 2049 if (base) 2050 inherit_settings(base, fld); 2051 } else 2052 { 2053 /* Channel is -F */ 2054 if (cfg.default_profile) 2055 { 2056 /* We are -F and set::anti-flood::channel::default-profile 2057 * is configured, so apply it or re-apply it. 2058 */ 2059 if (!fld) 2060 { 2061 cmodef_channel_create(channel); 2062 } else { 2063 base = get_channel_flood_profile(cfg.default_profile); 2064 if (base) 2065 { 2066 inherit_settings(base, fld); 2067 safe_strdup(fld->profile, cfg.default_profile); 2068 } 2069 } 2070 } else { 2071 if (fld) 2072 { 2073 /* Not +F, previously we had a default profile 2074 * and now set::anti-flood::channel::default-profile 2075 * is unset, so remove the flood profile. 2076 */ 2077 cmodef_free_param(fld, 0); 2078 GETPARASTRUCT(channel, 'F') = NULL; 2079 } 2080 } 2081 } 2082 } 2083 } 2084 2085 // TODO: handle mismatch of flood profiles between servers 2086 2087 // TODO: perhaps an option to turn off +F in the IRCd without turning off +f ? maybe as a multi-option.