unrealircd- supernets unrealircd source & configuration |
git clone git://git.acid.vegas/unrealircd.git |
Log | Files | Refs | Archive | README | LICENSE |
targetfloodprot.c (9821B)
1 /* Target flood protection 2 * (C)Copyright 2020 Bram Matthys and the UnrealIRCd team 3 * License: GPLv2 or later 4 */ 5 6 #include "unrealircd.h" 7 8 ModuleHeader MOD_HEADER 9 = { 10 "targetfloodprot", 11 "5.0", 12 "Target flood protection (set::anti-flood::target-flood)", 13 "UnrealIRCd Team", 14 "unrealircd-6", 15 }; 16 17 #define TFP_PRIVMSG 0 18 #define TFP_NOTICE 1 19 #define TFP_TAGMSG 2 20 #define TFP_MAX 3 21 22 typedef struct TargetFlood TargetFlood; 23 struct TargetFlood { 24 unsigned short cnt[TFP_MAX]; 25 time_t t[TFP_MAX]; 26 }; 27 28 typedef struct TargetFloodConfig TargetFloodConfig; 29 struct TargetFloodConfig { 30 int cnt[TFP_MAX]; 31 int t[TFP_MAX]; 32 }; 33 34 /* Forward declarations */ 35 int targetfloodprot_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs); 36 int targetfloodprot_config_run(ConfigFile *cf, ConfigEntry *ce, int type); 37 void targetfloodprot_mdata_free(ModData *m); 38 int targetfloodprot_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype); 39 int targetfloodprot_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype); 40 41 /* Global variables */ 42 ModDataInfo *targetfloodprot_client_md = NULL; 43 ModDataInfo *targetfloodprot_channel_md = NULL; 44 TargetFloodConfig *channelcfg = NULL; 45 TargetFloodConfig *privatecfg = NULL; 46 47 MOD_TEST() 48 { 49 HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, targetfloodprot_config_test); 50 return MOD_SUCCESS; 51 } 52 53 /** Allocate config and set default configuration */ 54 void targetfloodprot_defaults(void) 55 { 56 channelcfg = safe_alloc(sizeof(TargetFloodConfig)); 57 privatecfg = safe_alloc(sizeof(TargetFloodConfig)); 58 59 /* set::anti-flood::target-flood::channel-privmsg */ 60 channelcfg->cnt[TFP_PRIVMSG] = 45; 61 channelcfg->t[TFP_PRIVMSG] = 5; 62 /* set::anti-flood::target-flood::channel-notice */ 63 channelcfg->cnt[TFP_NOTICE] = 15; 64 channelcfg->t[TFP_NOTICE] = 5; 65 /* set::anti-flood::target-flood::channel-tagmsg */ 66 channelcfg->cnt[TFP_TAGMSG] = 15; 67 channelcfg->t[TFP_TAGMSG] = 5; 68 69 /* set::anti-flood::target-flood::private-privmsg */ 70 privatecfg->cnt[TFP_PRIVMSG] = 30; 71 privatecfg->t[TFP_PRIVMSG] = 5; 72 /* set::anti-flood::target-flood::private-notice */ 73 privatecfg->cnt[TFP_NOTICE] = 10; 74 privatecfg->t[TFP_NOTICE] = 5; 75 /* set::anti-flood::target-flood::private-tagmsg */ 76 privatecfg->cnt[TFP_TAGMSG] = 10; 77 privatecfg->t[TFP_TAGMSG] = 5; 78 } 79 80 MOD_INIT() 81 { 82 ModDataInfo mreq; 83 84 MARK_AS_OFFICIAL_MODULE(modinfo); 85 86 HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, targetfloodprot_config_run); 87 HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_CHANNEL, 0, targetfloodprot_can_send_to_channel); 88 HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_USER, 0, targetfloodprot_can_send_to_user); 89 90 memset(&mreq, 0, sizeof(mreq)); 91 mreq.name = "targetfloodprot"; 92 mreq.serialize = NULL; 93 mreq.unserialize = NULL; 94 mreq.free = targetfloodprot_mdata_free; 95 mreq.sync = 0; 96 mreq.type = MODDATATYPE_LOCAL_CLIENT; 97 targetfloodprot_client_md = ModDataAdd(modinfo->handle, mreq); 98 99 memset(&mreq, 0, sizeof(mreq)); 100 mreq.name = "targetfloodprot"; 101 mreq.serialize = NULL; 102 mreq.unserialize = NULL; 103 mreq.free = targetfloodprot_mdata_free; 104 mreq.sync = 0; 105 mreq.type = MODDATATYPE_CHANNEL; 106 targetfloodprot_channel_md = ModDataAdd(modinfo->handle, mreq); 107 108 targetfloodprot_defaults(); 109 110 return MOD_SUCCESS; 111 } 112 113 MOD_LOAD() 114 { 115 return MOD_SUCCESS; 116 } 117 118 MOD_UNLOAD() 119 { 120 safe_free(channelcfg); 121 safe_free(privatecfg); 122 return MOD_SUCCESS; 123 } 124 125 int targetfloodprot_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs) 126 { 127 int errors = 0; 128 ConfigEntry *cep; 129 130 if (type != CONFIG_SET_ANTI_FLOOD) 131 return 0; 132 133 /* We are only interrested in set::anti-flood::target-flood.. */ 134 if (!ce || !ce->name || strcmp(ce->name, "target-flood")) 135 return 0; 136 137 for (cep = ce->items; cep; cep = cep->next) 138 { 139 CheckNull(cep); 140 141 if (!strcmp(cep->name, "channel-privmsg") || 142 !strcmp(cep->name, "channel-notice") || 143 !strcmp(cep->name, "channel-tagmsg") || 144 !strcmp(cep->name, "private-privmsg") || 145 !strcmp(cep->name, "private-notice") || 146 !strcmp(cep->name, "private-tagmsg")) 147 { 148 int cnt = 0, period = 0; 149 150 if (!config_parse_flood(cep->value, &cnt, &period) || 151 (cnt < 1) || (cnt > 10000) || (period < 1) || (period > 120)) 152 { 153 config_error("%s:%i: set::anti-flood::target-flood::%s error. " 154 "Syntax is '<count>:<period>' (eg 5:60). " 155 "Count must be 1-10000 and period must be 1-120.", 156 cep->file->filename, cep->line_number, 157 cep->name); 158 errors++; 159 } 160 } else 161 { 162 config_error("%s:%i: unknown directive set::anti-flood::target-flood:%s", 163 cep->file->filename, cep->line_number, cep->name); 164 errors++; 165 continue; 166 } 167 } 168 169 *errs = errors; 170 return errors ? -1 : 1; 171 } 172 173 int targetfloodprot_config_run(ConfigFile *cf, ConfigEntry *ce, int type) 174 { 175 ConfigEntry *cep, *cepp; 176 177 if (type != CONFIG_SET_ANTI_FLOOD) 178 return 0; 179 180 /* We are only interrested in set::anti-flood::target-flood.. */ 181 if (!ce || !ce->name || strcmp(ce->name, "target-flood")) 182 return 0; 183 184 for (cep = ce->items; cep; cep = cep->next) 185 { 186 if (!strcmp(cep->name, "channel-privmsg")) 187 config_parse_flood(cep->value, &channelcfg->cnt[TFP_PRIVMSG], &channelcfg->t[TFP_PRIVMSG]); 188 else if (!strcmp(cep->name, "channel-notice")) 189 config_parse_flood(cep->value, &channelcfg->cnt[TFP_NOTICE], &channelcfg->t[TFP_NOTICE]); 190 else if (!strcmp(cep->name, "channel-tagmsg")) 191 config_parse_flood(cep->value, &channelcfg->cnt[TFP_TAGMSG], &channelcfg->t[TFP_TAGMSG]); 192 else if (!strcmp(cep->name, "private-privmsg")) 193 config_parse_flood(cep->value, &privatecfg->cnt[TFP_PRIVMSG], &privatecfg->t[TFP_PRIVMSG]); 194 else if (!strcmp(cep->name, "private-notice")) 195 config_parse_flood(cep->value, &privatecfg->cnt[TFP_NOTICE], &privatecfg->t[TFP_NOTICE]); 196 else if (!strcmp(cep->name, "private-tagmsg")) 197 config_parse_flood(cep->value, &privatecfg->cnt[TFP_TAGMSG], &privatecfg->t[TFP_TAGMSG]); 198 } 199 200 return 1; 201 } 202 203 /** UnrealIRCd internals: free object. */ 204 void targetfloodprot_mdata_free(ModData *m) 205 { 206 /* we don't have any members to free, so this is easy */ 207 safe_free(m->ptr); 208 } 209 210 int sendtypetowhat(SendType sendtype) 211 { 212 if (sendtype == SEND_TYPE_PRIVMSG) 213 return 0; 214 if (sendtype == SEND_TYPE_NOTICE) 215 return 1; 216 if (sendtype == SEND_TYPE_TAGMSG) 217 return 2; 218 #ifdef DEBUGMODE 219 unreal_log(ULOG_ERROR, "flood", "BUG_SENDTYPETOWHAT_UNKNOWN_VALUE", NULL, 220 "[BUG] sendtypetowhat() called for unknown sendtype $send_type", 221 log_data_integer("send_type", sendtype)); 222 abort(); 223 #endif 224 return 0; /* otherwise, default to privmsg i guess */ 225 } 226 227 int targetfloodprot_can_send_to_channel(Client *client, Channel *channel, Membership *lp, const char **msg, const char **errmsg, SendType sendtype) 228 { 229 TargetFlood *flood; 230 static char errbuf[256]; 231 int what; 232 233 /* This is redundant, right? */ 234 if (!MyUser(client)) 235 return HOOK_CONTINUE; 236 237 /* U-Lines, servers and IRCOps override */ 238 if (IsULine(client) || !IsUser(client) || (IsOper(client) && ValidatePermissionsForPath("immune:target-flood",client,NULL,channel,NULL))) 239 return HOOK_CONTINUE; 240 241 what = sendtypetowhat(sendtype); 242 243 if (moddata_channel(channel, targetfloodprot_channel_md).ptr == NULL) 244 { 245 /* Alloc a new entry if it doesn't exist yet */ 246 moddata_channel(channel, targetfloodprot_channel_md).ptr = safe_alloc(sizeof(TargetFlood)); 247 } 248 249 flood = (TargetFlood *)moddata_channel(channel, targetfloodprot_channel_md).ptr; 250 251 if ((TStime() - flood->t[what]) >= channelcfg->t[what]) 252 { 253 /* Reset due to moving into a new time slot */ 254 flood->t[what] = TStime(); 255 flood->cnt[what] = 1; 256 return HOOK_CONTINUE; /* forget about it.. */ 257 } 258 259 if (flood->cnt[what] >= channelcfg->cnt[what]) 260 { 261 /* Flood detected */ 262 unreal_log(ULOG_INFO, "flood", "FLOOD_BLOCKED", client, 263 "Flood blocked ($flood_type) from $client.details [$client.ip] to $channel", 264 log_data_string("flood_type", "target-flood-channel"), 265 log_data_channel("channel", channel)); 266 snprintf(errbuf, sizeof(errbuf), "Channel is being flooded. Message not delivered."); 267 *errmsg = errbuf; 268 return HOOK_DENY; 269 } 270 271 flood->cnt[what]++; 272 return HOOK_CONTINUE; 273 } 274 275 int targetfloodprot_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype) 276 { 277 TargetFlood *flood; 278 static char errbuf[256]; 279 int what; 280 281 /* Check if it is our TARGET ('target'), so yeah 282 * be aware that 'client' may be remote client in all the code that follows! 283 */ 284 if (!MyUser(target)) 285 return HOOK_CONTINUE; 286 287 /* U-Lines, servers and IRCOps override */ 288 if (IsULine(client) || !IsUser(client) || (IsOper(client) && ValidatePermissionsForPath("immune:target-flood",client,target,NULL,NULL))) 289 return HOOK_CONTINUE; 290 291 what = sendtypetowhat(sendtype); 292 293 if (moddata_local_client(target, targetfloodprot_client_md).ptr == NULL) 294 { 295 /* Alloc a new entry if it doesn't exist yet */ 296 moddata_local_client(target, targetfloodprot_client_md).ptr = safe_alloc(sizeof(TargetFlood)); 297 } 298 299 flood = (TargetFlood *)moddata_local_client(target, targetfloodprot_client_md).ptr; 300 301 if ((TStime() - flood->t[what]) >= privatecfg->t[what]) 302 { 303 /* Reset due to moving into a new time slot */ 304 flood->t[what] = TStime(); 305 flood->cnt[what] = 1; 306 return HOOK_CONTINUE; /* forget about it.. */ 307 } 308 309 if (flood->cnt[what] >= privatecfg->cnt[what]) 310 { 311 /* Flood detected */ 312 unreal_log(ULOG_INFO, "flood", "FLOOD_BLOCKED", client, 313 "Flood blocked ($flood_type) from $client.details [$client.ip] to $target", 314 log_data_string("flood_type", "target-flood-user"), 315 log_data_client("target", target)); 316 snprintf(errbuf, sizeof(errbuf), "User is being flooded. Message not delivered."); 317 *errmsg = errbuf; 318 return HOOK_DENY; 319 } 320 321 flood->cnt[what]++; 322 return HOOK_CONTINUE; 323 }