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