Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
BitchX
GitHub Repository: BitchX/BitchX1.3
Path: blob/master/source/flood.c
1069 views
1
/*
2
* flood.c: handle channel flooding.
3
*
4
* This attempts to give you some protection from flooding. Basically, it keeps
5
* track of how far apart (timewise) messages come in from different people.
6
* If a single nickname sends more than 3 messages in a row in under a
7
* second, this is considered flooding. It then activates the ON FLOOD with
8
* the nickname and type (appropriate for use with IGNORE).
9
*
10
* Thanks to Tomi Ollila <[email protected]> for this one.
11
*/
12
13
14
#include "irc.h"
15
static char cvsrevision[] = "$Id: flood.c 26 2008-04-30 13:57:56Z keaston $";
16
CVS_REVISION(flood_c)
17
#include "struct.h"
18
19
#include "alias.h"
20
#include "hook.h"
21
#include "ircaux.h"
22
#include "ignore.h"
23
#include "flood.h"
24
#include "vars.h"
25
#include "output.h"
26
#include "list.h"
27
#include "misc.h"
28
#include "server.h"
29
#include "userlist.h"
30
#include "timer.h"
31
#include "ignore.h"
32
#include "status.h"
33
#include "hash2.h"
34
#include "cset.h"
35
#define MAIN_SOURCE
36
#include "modval.h"
37
38
static char *ignore_types[] =
39
{
40
"",
41
"MSG",
42
"PUBLIC",
43
"NOTICE",
44
"WALL",
45
"WALLOP",
46
"CTCP",
47
"INVITE",
48
"CDCC",
49
"ACTION",
50
"NICK",
51
"DEOP",
52
"KICK",
53
"JOIN"
54
};
55
56
#define FLOOD_HASHSIZE 31
57
HashEntry no_flood_list[FLOOD_HASHSIZE];
58
HashEntry flood_list[FLOOD_HASHSIZE];
59
60
static int remove_oldest_flood_hashlist(HashEntry *, time_t, int);
61
62
63
64
65
extern char *FromUserHost;
66
extern unsigned int window_display;
67
extern int from_server;
68
69
static double allow_flood = 0.0;
70
static double this_flood = 0.0;
71
72
#define NO_RESET 0
73
#define RESET 1
74
75
char *get_flood_types(unsigned int type)
76
{
77
int x = 0;
78
while (type)
79
{
80
type = type >> 1;
81
x++;
82
}
83
return ignore_types[x];
84
}
85
86
#if 0
87
int get_flood_rate(int type, ChannelList * channel)
88
{
89
int flood_rate = get_int_var(FLOOD_RATE_VAR);
90
if (channel)
91
{
92
switch(type)
93
{
94
case JOIN_FLOOD:
95
flood_rate = get_cset_int_var(channel->csets, JOINFLOOD_TIME_CSET);
96
break;
97
case PUBLIC_FLOOD:
98
flood_rate = get_cset_int_var(channel->csets, PUBFLOOD_TIME_CSET);
99
break;
100
case NICK_FLOOD:
101
flood_rate = get_cset_int_var(channel->csets, NICKFLOOD_TIME_CSET);
102
break;
103
case KICK_FLOOD:
104
flood_rate = get_cset_int_var(channel->csets, KICKFLOOD_TIME_CSET);
105
break;
106
case DEOP_FLOOD:
107
flood_rate = get_cset_int_var(channel->csets, DEOPFLOOD_TIME_CSET);
108
break;
109
default:
110
break;
111
}
112
}
113
else
114
{
115
switch(type)
116
{
117
case CDCC_FLOOD:
118
flood_rate = get_int_var(CDCC_FLOOD_RATE_VAR);
119
break;
120
case CTCP_FLOOD:
121
flood_rate = get_int_var(CTCP_FLOOD_RATE_VAR);
122
case CTCP_ACTION_FLOOD:
123
default:
124
break;
125
}
126
}
127
return flood_rate;
128
}
129
130
int get_flood_count(int type, ChannelList * channel)
131
{
132
int flood_count = get_int_var(FLOOD_AFTER_VAR);
133
if (channel) {
134
switch(type)
135
{
136
case JOIN_FLOOD:
137
flood_count = get_cset_int_var(channel->csets, KICK_ON_JOINFLOOD_CSET);
138
break;
139
case PUBLIC_FLOOD:
140
flood_count = get_cset_int_var(channel->csets, KICK_ON_PUBFLOOD_CSET);
141
break;
142
case NICK_FLOOD:
143
flood_count = get_cset_int_var(channel->csets, KICK_ON_NICKFLOOD_CSET);
144
break;
145
case KICK_FLOOD:
146
flood_count = get_cset_int_var(channel->csets, KICK_ON_KICKFLOOD_CSET);
147
break;
148
case DEOP_FLOOD:
149
flood_count = get_cset_int_var(channel->csets, KICK_ON_DEOPFLOOD_CSET);
150
break;
151
default:
152
break;
153
}
154
}
155
else
156
{
157
switch(type)
158
{
159
case CDCC_FLOOD:
160
flood_count = get_int_var(CDCC_FLOOD_AFTER_VAR);
161
break;
162
case CTCP_FLOOD:
163
flood_count = get_int_var(CTCP_FLOOD_AFTER_VAR);
164
case CTCP_ACTION_FLOOD:
165
default:
166
break;
167
}
168
}
169
return flood_count;
170
}
171
#endif
172
173
void get_flood_val(ChannelList *chan, int type, int *flood_count, int *flood_rate)
174
{
175
*flood_count = get_int_var(FLOOD_AFTER_VAR);
176
*flood_rate = get_int_var(FLOOD_RATE_VAR);
177
if (chan)
178
{
179
switch(type)
180
{
181
case JOIN_FLOOD:
182
*flood_count = get_cset_int_var(chan->csets, KICK_ON_JOINFLOOD_CSET);
183
*flood_rate = get_cset_int_var(chan->csets, JOINFLOOD_TIME_CSET);
184
break;
185
case PUBLIC_FLOOD:
186
*flood_count = get_cset_int_var(chan->csets, KICK_ON_PUBFLOOD_CSET);
187
*flood_rate = get_cset_int_var(chan->csets, PUBFLOOD_TIME_CSET);
188
break;
189
case NICK_FLOOD:
190
*flood_count = get_cset_int_var(chan->csets, KICK_ON_NICKFLOOD_CSET);
191
*flood_rate = get_cset_int_var(chan->csets, NICKFLOOD_TIME_CSET);
192
break;
193
case KICK_FLOOD:
194
*flood_count = get_cset_int_var(chan->csets, KICK_ON_KICKFLOOD_CSET);
195
*flood_rate = get_cset_int_var(chan->csets, KICKFLOOD_TIME_CSET);
196
break;
197
case DEOP_FLOOD:
198
*flood_count = get_cset_int_var(chan->csets, KICK_ON_DEOPFLOOD_CSET);
199
*flood_rate = get_cset_int_var(chan->csets, DEOPFLOOD_TIME_CSET);
200
break;
201
default:
202
break;
203
}
204
}
205
else
206
{
207
switch(type)
208
{
209
case CDCC_FLOOD:
210
*flood_count = get_int_var(CDCC_FLOOD_AFTER_VAR);
211
*flood_rate = get_int_var(CDCC_FLOOD_RATE_VAR);
212
break;
213
case CTCP_FLOOD:
214
*flood_count = get_int_var(CTCP_FLOOD_AFTER_VAR);
215
*flood_rate = get_int_var(CTCP_FLOOD_RATE_VAR);
216
case CTCP_ACTION_FLOOD:
217
default:
218
break;
219
}
220
}
221
}
222
223
int set_flood(int type, time_t flood_time, int reset, NickList *tmpnick)
224
{
225
if (!tmpnick)
226
return 0;
227
switch(type)
228
{
229
case JOIN_FLOOD:
230
if (reset == RESET)
231
{
232
tmpnick->joincount = 1;
233
tmpnick->jointime = flood_time;
234
} else tmpnick->joincount++;
235
break;
236
case PUBLIC_FLOOD:
237
if (reset == RESET)
238
{
239
tmpnick->floodcount = 1;
240
tmpnick->floodtime = tmpnick->idle_time = flood_time;
241
} else tmpnick->floodcount++;
242
break;
243
case NICK_FLOOD:
244
if (reset == RESET)
245
{
246
tmpnick->nickcount = 1;
247
tmpnick->nicktime = flood_time;
248
} else tmpnick->nickcount++;
249
break;
250
case KICK_FLOOD:
251
if (reset == RESET)
252
{
253
tmpnick->kickcount = 1;
254
tmpnick->kicktime = flood_time;
255
} else tmpnick->kickcount++;
256
break;
257
case DEOP_FLOOD:
258
if (reset == RESET)
259
{
260
tmpnick->dopcount = 1;
261
tmpnick->doptime = flood_time;
262
} else tmpnick->dopcount++;
263
break;
264
default:
265
break;
266
}
267
return 1;
268
}
269
270
int BX_is_other_flood(ChannelList *channel, NickList *tmpnick, int type, int *t_flood)
271
{
272
time_t diff = 0, flood_time = 0;
273
int doit = 0;
274
int count = 0;
275
int flood_rate = 0, flood_count = 0;
276
277
flood_time = now;
278
279
280
if (!channel || !tmpnick)
281
return 0;
282
if (isme(tmpnick->nick))
283
return 0;
284
if (find_name_in_genericlist(tmpnick->nick, no_flood_list, FLOOD_HASHSIZE, 0))
285
return 0;
286
set_flood(type, flood_time, NO_RESET, tmpnick);
287
switch(type)
288
{
289
case JOIN_FLOOD:
290
if (!get_cset_int_var(channel->csets, JOINFLOOD_CSET))
291
break;
292
diff = flood_time - tmpnick->jointime;
293
count = tmpnick->joincount;
294
doit = 1;
295
break;
296
case PUBLIC_FLOOD:
297
if (!get_cset_int_var(channel->csets, PUBFLOOD_CSET))
298
break;
299
diff = flood_time - tmpnick->floodtime;
300
count = tmpnick->floodcount;
301
doit = 1;
302
break;
303
case NICK_FLOOD:
304
if (!get_cset_int_var(channel->csets, NICKFLOOD_CSET))
305
break;
306
diff = flood_time - tmpnick->nicktime;
307
count = tmpnick->nickcount;
308
doit = 1;
309
break;
310
case DEOP_FLOOD:
311
if (!get_cset_int_var(channel->csets, DEOPFLOOD_CSET))
312
break;
313
diff = flood_time - tmpnick->doptime;
314
count = tmpnick->dopcount;
315
doit = 1;
316
break;
317
case KICK_FLOOD:
318
if (!get_cset_int_var(channel->csets, KICKFLOOD_CSET))
319
break;
320
diff = flood_time - tmpnick->kicktime;
321
count = tmpnick->kickcount;
322
doit = 1;
323
break;
324
default:
325
return 0;
326
break;
327
}
328
if (doit)
329
{
330
int is_user = 0;
331
if (!get_int_var(FLOOD_PROTECTION_VAR))
332
return 0;
333
get_flood_val(channel, type, &flood_count, &flood_rate);
334
if ((tmpnick->userlist && (tmpnick->userlist->flags & ADD_FLOOD)))
335
is_user = 1;
336
if (!is_user && (count >= flood_count))
337
{
338
int flooded = 0;
339
if (count >= flood_count)
340
{
341
if (!diff || (flood_rate && (diff < flood_rate)))
342
{
343
*t_flood = diff;
344
flooded = 1;
345
do_hook(FLOOD_LIST, "%s %s %s %s", tmpnick->nick, get_flood_types(type),channel?channel->channel:zero, tmpnick->host);
346
}
347
set_flood(type, flood_time, RESET, tmpnick);
348
return flooded;
349
}
350
else if (diff > flood_rate)
351
set_flood(type, flood_time, RESET, tmpnick);
352
}
353
}
354
return 0;
355
}
356
357
/*
358
* check_flooding: This checks for message flooding of the type specified for
359
* the given nickname. This is described above. This will return 0 if no
360
* flooding took place, or flooding is not being monitored from a certain
361
* person. It will return 1 if flooding is being check for someone and an ON
362
* FLOOD is activated.
363
*/
364
365
int BX_check_flooding(char *nick, int type, char *line, char *channel)
366
{
367
static int users = 0,
368
pos = 0;
369
time_t flood_time = now,
370
diff = 0;
371
372
Flooding *tmp;
373
int flood_rate,
374
flood_count;
375
376
377
if (!(users = get_int_var(FLOOD_USERS_VAR)) || !*FromUserHost)
378
return 1;
379
if (find_name_in_genericlist(nick, no_flood_list, FLOOD_HASHSIZE, 0))
380
return 1;
381
if (!(tmp = find_name_in_floodlist(nick, FromUserHost, flood_list, FLOOD_HASHSIZE, 0)))
382
{
383
if (pos >= users)
384
{
385
pos -= remove_oldest_flood_hashlist(&flood_list[0], 0, (users + 1 - pos));
386
}
387
tmp = add_name_to_floodlist(nick, FromUserHost, channel, flood_list, FLOOD_HASHSIZE);
388
tmp->type = type;
389
tmp->cnt = 1;
390
tmp->start = flood_time;
391
tmp->flood = 0;
392
pos++;
393
return 1;
394
}
395
if (!(tmp->type & type))
396
{
397
tmp->type |= type;
398
return 1;
399
}
400
401
#if 0
402
flood_count = get_flood_count(type, NULL); /* FLOOD_AFTER_VAR */
403
flood_rate = get_flood_rate(type, NULL); /* FLOOD_RATE_VAR */
404
#endif
405
get_flood_val(NULL, type, &flood_count, &flood_rate);
406
if (!flood_count || !flood_rate)
407
return 1;
408
tmp->cnt++;
409
if (tmp->cnt > flood_count)
410
{
411
int ret;
412
diff = flood_time - tmp->start;
413
if (diff != 0)
414
this_flood = (double)tmp->cnt / (double)diff;
415
else
416
this_flood = 0;
417
allow_flood = (double)flood_count / (double)flood_rate;
418
if (!diff || !this_flood || (this_flood > allow_flood))
419
{
420
if (tmp->flood == 0)
421
{
422
tmp->flood = 1;
423
if ((ret = do_hook(FLOOD_LIST, "%s %s %s %s", nick, get_flood_types(type),channel?channel:zero, line)) != 1)
424
return ret;
425
switch(type)
426
{
427
case WALL_FLOOD:
428
case MSG_FLOOD:
429
case NOTICE_FLOOD:
430
case CDCC_FLOOD:
431
case CTCP_FLOOD:
432
if (flood_prot(nick, FromUserHost, get_flood_types(type), type, get_int_var(IGNORE_TIME_VAR), channel))
433
return 0;
434
break;
435
case CTCP_ACTION_FLOOD:
436
if (flood_prot(nick, FromUserHost, get_flood_types(CTCP_FLOOD), type, get_int_var(IGNORE_TIME_VAR), channel))
437
return 0;
438
default:
439
break;
440
}
441
if (get_int_var(FLOOD_WARNING_VAR))
442
put_it("%s", convert_output_format(fget_string_var(FORMAT_FLOOD_FSET), "%s %s %s %s %s", update_clock(GET_TIME), get_flood_types(type), nick, FromUserHost, channel?channel:"unknown"));
443
}
444
return 1;
445
}
446
else
447
{
448
tmp->flood = 0;
449
tmp->cnt = 1;
450
tmp->start = flood_time;
451
}
452
}
453
return 1;
454
}
455
456
void check_ctcp_ban_flood(char *channel, char *nick)
457
{
458
NickList *Nick = NULL;
459
ChannelList *chan = NULL;
460
for (chan = get_server_channels(from_server); chan; chan = chan->next)
461
if ((Nick = find_nicklist_in_channellist(nick, chan, 0)))
462
break;
463
if (chan && chan->have_op && get_cset_int_var(chan->csets, CTCP_FLOOD_BAN_CSET) && Nick)
464
{
465
if (!Nick->userlist || (Nick->userlist && !(Nick->userlist->flags & ADD_FLOOD)))
466
{
467
if (!nick_isop(Nick) || get_cset_int_var(chan->csets, KICK_OPS_CSET))
468
{
469
char *ban, *u, *h;
470
u = alloca(strlen(Nick->host)+1);
471
strcpy(u, Nick->host);
472
h = strchr(u, '@');
473
*h++ = 0;
474
ban = ban_it(Nick->nick, u, h, Nick->ip);
475
if (!ban_is_on_channel(ban, chan) && !eban_is_on_channel(ban, chan))
476
send_to_server("MODE %s +b %s", chan->channel, ban);
477
}
478
}
479
}
480
}
481
482
int BX_flood_prot (char *nick, char *userhost, char *type, int ctcp_type, int ignoretime, char *channel)
483
{
484
ChannelList *chan;
485
NickList *Nick;
486
char tmp[BIG_BUFFER_SIZE+1];
487
char *uh;
488
int old_window_display;
489
int kick_on_flood = 1;
490
491
if ((ctcp_type == CDCC_FLOOD || ctcp_type == CTCP_FLOOD || ctcp_type == CTCP_ACTION_FLOOD) && !get_int_var(CTCP_FLOOD_PROTECTION_VAR))
492
return 0;
493
else if (!get_int_var(FLOOD_PROTECTION_VAR))
494
return 0;
495
else if (!my_stricmp(nick, get_server_nickname(from_server)))
496
return 0;
497
switch (ctcp_type)
498
{
499
case WALL_FLOOD:
500
case MSG_FLOOD:
501
break;
502
case NOTICE_FLOOD:
503
break;
504
case PUBLIC_FLOOD:
505
if (channel)
506
{
507
if ((chan = lookup_channel(channel, from_server, 0)))
508
{
509
kick_on_flood = get_cset_int_var(chan->csets, PUBFLOOD_CSET);
510
if (kick_on_flood && (Nick = find_nicklist_in_channellist(nick, chan, 0)))
511
{
512
if (chan->have_op && (!Nick->userlist || (Nick->userlist && !(Nick->userlist->flags & ADD_FLOOD))))
513
if (!nick_isop(Nick) || get_cset_int_var(chan->csets, KICK_OPS_CSET))
514
send_to_server("KICK %s %s :\002%s\002 flooder", chan->channel, nick, type);
515
}
516
}
517
}
518
break;
519
case CTCP_FLOOD:
520
case CTCP_ACTION_FLOOD:
521
check_ctcp_ban_flood(channel, nick);
522
default:
523
if (get_int_var(FLOOD_KICK_VAR) && kick_on_flood && channel)
524
{
525
for (chan = get_server_channels(from_server); chan; chan = chan->next)
526
{
527
if (chan->have_op && (Nick = find_nicklist_in_channellist(nick, chan, 0)))
528
{
529
if ((!Nick->userlist || (Nick->userlist && !(Nick->userlist->flags & ADD_FLOOD))))
530
if (!nick_isop(Nick) || get_cset_int_var(chan->csets, KICK_OPS_CSET))
531
send_to_server("KICK %s %s :\002%s\002 flooder", chan->channel, nick, type);
532
}
533
}
534
}
535
}
536
if (!ignoretime)
537
return 0;
538
uh = clear_server_flags(userhost);
539
sprintf(tmp, "*!*%s", uh);
540
old_window_display = window_display;
541
window_display = 0;
542
ignore_nickname(tmp, ignore_type(type, strlen(type)), 0);
543
window_display = old_window_display;
544
sprintf(tmp, "%d ^IGNORE *!*%s NONE", ignoretime, uh);
545
timercmd("TIMER", tmp, NULL, NULL);
546
bitchsay("Auto-ignoring %s for %d minutes [\002%s\002 flood]", nick, ignoretime/60, type);
547
return 1;
548
}
549
550
static int remove_oldest_flood_hashlist(HashEntry *list, time_t timet, int count)
551
{
552
Flooding *ptr;
553
register time_t t;
554
int total = 0;
555
register unsigned long x;
556
t = now;
557
if (!count)
558
{
559
for (x = 0; x < FLOOD_HASHSIZE; x++)
560
{
561
ptr = (Flooding *) (list + x)->list;
562
if (!ptr || !*ptr->name)
563
continue;
564
while (ptr)
565
{
566
if ((ptr->start + timet) <= t)
567
{
568
if (!(ptr = find_name_in_floodlist(ptr->name, ptr->host, flood_list, FLOOD_HASHSIZE, 1)))
569
continue;
570
new_free(&(ptr->channel));
571
new_free(&(ptr->name));
572
new_free(&ptr->host);
573
new_free((char **)&ptr);
574
total++;
575
ptr = (Flooding *) (list + x)->list;
576
} else ptr = ptr->next;
577
}
578
}
579
}
580
else
581
{
582
for (x = 0; x < FLOOD_HASHSIZE; x++)
583
{
584
Flooding *next = NULL;
585
ptr = (Flooding *) (list + x)->list;
586
if (!ptr || !*ptr->name)
587
continue;
588
while(ptr && count)
589
{
590
if ((ptr = find_name_in_floodlist(ptr->name, ptr->host, flood_list, FLOOD_HASHSIZE, 1)))
591
{
592
next = ptr->next;
593
new_free(&(ptr->channel));
594
new_free(&(ptr->name));
595
new_free(&ptr->host);
596
new_free((char **)&ptr);
597
total++; count--;
598
ptr = (Flooding *) (list + x)->list;
599
ptr = next;
600
}
601
}
602
}
603
}
604
return total;
605
}
606
607
void clean_flood_list()
608
{
609
remove_oldest_flood_hashlist(&flood_list[0], get_int_var(FLOOD_RATE_VAR)+1, 0);
610
}
611
612