1 #ifdef RCS
2 static char rcsid[]="$Id: flood.c,v 1.1.1.1 2000/11/13 02:42:41 holsta Exp $";
3 #endif
4 /******************************************************************************
5 * Internetting Cooperating Programmers
6 * ----------------------------------------------------------------------------
7 *
8 * ____ PROJECT
9 * | _ \ __ _ _ __ ___ ___ _ __
10 * | | | |/ _` | '_ \ / __/ _ \ '__|
11 * | |_| | (_| | | | | (_| __/ |
12 * |____/ \__,_|_| |_|\___\___|_| the IRC bot
13 *
14 * All files in this archive are subject to the GNU General Public License.
15 *
16 * $Source: /cvsroot/dancer/dancer/src/flood.c,v $
17 * $Revision: 1.1.1.1 $
18 * $Date: 2000/11/13 02:42:41 $
19 * $Author: holsta $
20 * $State: Exp $
21 * $Locker: $
22 *
23 * ---------------------------------------------------------------------------
24 *****************************************************************************/
25
26 #include "dancer.h"
27 #include "trio.h"
28 #include "strio.h"
29 #include "transfer.h"
30 #include "user.h"
31 #include "flood.h"
32 #include "bans.h"
33
34 extern time_t now;
35
36 extern bool uppercheck; /* check users for uppercase violations! */
37 extern bool beepcheck; /* check users for beep violations! */
38 extern bool colourcheck; /* check users for colourcode violations! */
39 extern bool ctcpmode; /* CTCP flood checks */
40 extern bool mute;
41 extern long levels[];
42 extern itemguest *guestHead;
43
44 int floodrate, floodtime; /* 'floodrate' messages within 'floodtime' secs */
45 int floodrepeatrate, floodrepeattime; /* 'floodrepeatrate' repetitions within 'floodrepeattime' secs */
46 int floodbeeps;
47 int floodjoins; /* max amount of joins from the same hostpattern */
48
49 /****************************************************************************
50 * _ _ _ __ __ _
51 * / \ | | ___ _ __| |_| \/ | ___ __| | ___
52 * / _ \ | |/ _ \ '__| __| |\/| |/ _ \ / _` |/ _ \
53 * / ___ \| | __/ | | |_| | | | (_) | (_| | __/
54 * /_/ \_\_|\___|_| \__|_| |_|\___/ \__,_|\___|
55 *
56 ***************************************************************************/
57
58 #define ALERT_TIMEOUT (10*60) /* at least 10 minutes from the last
59 alert ON */
60
61 /* --- AlertMode -------------------------------------------------- */
62
AlertMode(Alert what)63 int AlertMode(Alert what)
64 {
65 static time_t alerted = 0;
66 static int level = 0;
67
68 switch (what) {
69 case ALERT_ON:
70 alerted = now;
71 level++;
72 break;
73 case ALERT_RED:
74 level += 100;
75 alerted = now;
76 break;
77 case ALERT_OFF:
78 if (alerted && ((now - alerted) > ALERT_TIMEOUT)) {
79 Log(LOG, "Alert Mode OFF");
80 alerted = 0;
81 level = 0;
82 }
83 return 0;
84 default:
85 break;
86 }
87
88 if ((level > 0) && (level < 100))
89 Logf(LOG, "Alert Mode level %d", level);
90 else if ((level >= 100) && (level < 1000))
91 Logf(LOG, "Alert Mode *RED* level %d", level);
92
93 return level;
94 }
95
96 /* --- Warning ---------------------------------------------------- */
97
Warning(itemguest * g,char * msg,char * kickmsg)98 bool Warning(itemguest *g, char *msg, char *kickmsg)
99 {
100 int level;
101
102 level = g->ident->user ? g->ident->user->level : 0;
103 if (level < FLOODMIDHIGH) {
104 g->warnings++;
105 if (level < FLOODLOWMID) {
106 /* low -> mid */
107 if (g->warnings > WARNLOW) {
108 StickyKick(g, (g->warnings > 1) ? GetDefaultText(msg_no_more_mr_nice_bot) : kickmsg);
109 g->warnings = 0;
110 return TRUE; /* Kicked */
111 }
112 }
113 else if (g->warnings > WARNMID) {
114 /* mid -> high */
115 StickyKick(g, (g->warnings > 1) ? GetDefaultText(msg_no_more_mr_nice_bot) : kickmsg);
116 g->warnings = 0;
117 return TRUE; /* Kicked */
118 }
119 if (!mute)
120 Actionf(GetDefaultText(msg_usually_kicks_X), msg, g->warnings, g->ident->nick);
121 } /* high -> inf */
122 return FALSE;
123 }
124
125 /* --- FloodCheck ------------------------------------------------- */
126
FloodCheck(itemguest * g)127 void FloodCheck(itemguest *g)
128 {
129 if ((now - g->posttime) <= floodtime) {
130 g->postsame++;
131 if (g->postsame >= floodrate)
132 Warning(g, GetDefaultText(msg_flooders), GetDefaultText(msg_no_flooding));
133 }
134 else
135 g->postsame = 0;
136 }
137
138 /* --- Check ------------------------------------------------------ */
139
Check(itemguest * g,char * line)140 void Check(itemguest *g, char *line)
141 {
142 long uppers = 0, beeps = 0, colours = 0;
143 long length;
144
145 if (NULL == line)
146 return;
147
148 for (length = 0; line[length]; length++) {
149 if (beepcheck && ('\x07' == line[length]))
150 beeps++;
151 if (colourcheck && ('\x03' == line[length]) && isdigit(line[length + 1]))
152 colours++;
153 if (uppercheck && isupper(line[length]))
154 uppers++;
155 }
156
157 if (beeps) {
158 if (beeps <= floodbeeps)
159 Warning(g, GetDefaultText(msg_beepers), GetDefaultText(msg_kickmsg_no_beepers));
160 else
161 StickyKick(g, GetDefaultText(msg_kickmsg_no_beepers));
162 }
163 if (colours) {
164 Warning(g, GetDefaultText(msg_colour_users), GetDefaultText(msg_kickmsg_no_colours));
165 }
166 if (uppers && (length > 10)) {
167 if (uppers*1000/length > 750) /* This is a serious uppercase violation */
168 Warning(g, GetDefaultText(msg_shouters), GetDefaultText(msg_no_shouting));
169 }
170 }
171
172 /* --- RepeatCheck ------------------------------------------------ */
173
RepeatCheck(itemguest * g,char * line)174 void RepeatCheck(itemguest *g, char *line)
175 {
176 unsigned long hash = 0;
177
178 if (NULL == line)
179 return;
180
181 while (*line)
182 hash = (hash<<1) + *line++;
183
184 if ((hash == g->hashpost) && ((now - g->hashtime) <= floodrepeattime)) {
185 g->hashsame++;
186 if (g->hashsame >= floodrepeatrate)
187 Warning(g, GetDefaultText(msg_repeaters), GetDefaultText(msg_no_repeaters));
188 }
189 else {
190 g->hashpost = hash;
191 g->hashtime = now;
192 g->hashsame = 1;
193 }
194 }
195
196 /* --- AvalanceCheck ---------------------------------------------- */
197
AvalanceCheck(itemguest * g,char * param)198 void AvalanceCheck(itemguest *g, char *param)
199 {
200 char c;
201 int cntone = 0, cntesc = 0, cntbeep = 0;
202
203 if (NULL == param)
204 return;
205
206 while (c = *param++) {
207 switch (c) {
208 case '\001':
209 cntone++;
210 break;
211 case '\x1b':
212 if ('\026' == *param)
213 cntesc++;
214 break;
215 case '\007':
216 cntbeep++;
217 break;
218 default:
219 break;
220 }
221 }
222
223 if (cntone > 5) { /* Kick without warning */
224 StickyKick(g, GetDefaultText(msg_ctcp_bomb_detected));
225 }
226 else if (cntesc > 4) {
227 StickyKick(g, GetDefaultText(msg_tsunami_detected));
228 }
229 else if (cntbeep > 5) {
230 StickyKick(g, GetDefaultText(msg_beep_flood_detected));
231 }
232 }
233
234 /* --- CTCPFloodCheck --------------------------------------------- */
235
236 /* Number of codes */
237 #define MAX_ACTION 6
238 #define MAX_CTCP 3
239
240 /* Shortest time interval the MAX number of codes are accepted to flood */
241 #define TIME_ACTION 4
242 #define TIME_CTCP 6
243
CTCPFloodCheck(itemguest * g,bool action)244 bool CTCPFloodCheck(itemguest *g, bool action)
245 {
246 bool flooding = FALSE;
247 static int actcp = 0;
248 static int nctcp = 0;
249 int compare = 999;
250 int tmp;
251 static time_t atime[MAX_ACTION];
252 static time_t ntime[MAX_CTCP];
253
254 if (action) {
255 tmp = actcp % MAX_ACTION;
256 atime[tmp] = now;
257 if (actcp < (MAX_ACTION - 1))
258 tmp = -1;
259 actcp++;
260 }
261 else {
262 tmp = nctcp % MAX_CTCP;
263 ntime[tmp] = now;
264 if (nctcp < (MAX_CTCP - 1))
265 tmp = -1;
266 nctcp++;
267 }
268
269 if (tmp >= 0) {
270 /* Compare to fetch a possible hit */
271 if (action) {
272 #if 0
273 int n;
274 for (n=0; n < MAX_ACTION; n++) {
275 fprintf(stderr, "(%d) %d ", n, now - atime[n]);
276 }
277 fprintf(stderr, "\nnow %d <-> %d\n",
278 tmp, (tmp + 1) % MAX_ACTION);
279 #endif
280 compare = now - atime[(tmp + 1) % MAX_ACTION];
281 }
282 else {
283 compare = now - ntime[(tmp + 1) % MAX_CTCP];
284 }
285 }
286
287 /* Now we have a time to compare with */
288 if (g) {
289 if (action) {
290 if (compare <= TIME_ACTION)
291 flooding = TRUE;
292 }
293 else {
294 if (compare <= TIME_CTCP)
295 flooding = TRUE;
296 }
297
298 if (flooding) {
299 #if 0
300 StrFormatMax(buf, sizeof(buf), "Flood: CTCP%s flood by %s!%s",
301 action ? " ACTION" : "", g->ident->nick,
302 g->ident->host);
303 Multicast(REPORTCAST, buf);
304 #endif
305 if (ctcpmode && !action) {
306 /* Don't do this on ACTION floods, but we can still report ACTION
307 floods to trigger ignore */
308 Warning(g, "CTCP flooders", "CTCP flood detected");
309 }
310 }
311 }
312 else /* A ctcp request from outside the channel */
313 flooding = TRUE; /* Not really flooding, but we ignore it */
314 return flooding;
315 }
316
317 /* --- MultiCheck ------------------------------------------------- */
318
319 /*
320 * This should kick users that try to join using the same user account
321 * for the Xth time.
322 */
323
MultiCheck(char * domain)324 void MultiCheck(char *domain)
325 {
326 int count = 0; /* None so far */
327 itemguest *g;
328
329 if (domain) {
330 for (g = First(guestHead); g; g = Next(g)) {
331 if (StrEqual(g->ident->userdomain, domain) &&
332 ((g->ident->level < LEVELEXPERT) || !g->flags.chanop))
333 count++; /* Count this */
334 }
335
336 if (count > floodjoins) {
337 AlertMode(ALERT_ON);
338 for (g = First(guestHead); g; g = Next(g)) {
339 if (StrEqual(g->ident->userdomain, domain) &&
340 ((g->ident->level < LEVELEXPERT) || !g->flags.chanop))
341 StickyKick(g, "*bang* too many joined users");
342 }
343 }
344 }
345 }
346