Просмотр исходного кода

Merge branch '37-drone-floods'

* 37-drone-floods:
  Show what kind of mass flood was detected (#37)
  Fix ctcp flood getting attributed to 'pub'
  Remove unneeded chanset 'nomassjoin' (#37)
  Add chansets 'flood-mchan', 'flood-mbytes', 'flood-mctcp' to control reactions to mass floods (#37)
  Track mass floods by their flood-m* settings
  Track all clients to detect mass flooding (#37)
  Only process drone flood if opped (#37)
  Lockdown channel (+im) if banlist becomes full (#37)
  Use enum for reason for locking chan (#37)

Conflicts:
	doc/UPDATES
Bryan Drewery 14 лет назад
Родитель
Сommit
1941d21b82

+ 4 - 0
doc/UPDATES

@@ -39,6 +39,10 @@
   * Add 'chanset revenge' which will react/kick/ban/remove users who kick/ban/deop bots.
   * Add 'chanset revenge' which will react/kick/ban/remove users who kick/ban/deop bots.
   * Fix flood kicking not properly tracking multiple clients at once (#43)
   * Fix flood kicking not properly tracking multiple clients at once (#43)
   * Add chanset 'flood-bytes' to count how many bytes:second a user sends before getting kicked for flood. (#42)
   * Add chanset 'flood-bytes' to count how many bytes:second a user sends before getting kicked for flood. (#42)
+  * Bot will now lockdown channel (+im) if banlist becomes full (#37)
+  * Bot will now lockdown channel (+im) if a drone flood is detected (#37)
+    * Add chansets 'flood-mchan', 'flood-mbytes', 'flood-mctcp' to control reactions to mass floods (#37)
+  * Remove unneeded chanset 'nomassjoin'
 
 
 1.3.4 - http://wraith.botpack.net/milestone/1.3.4
 1.3.4 - http://wraith.botpack.net/milestone/1.3.4
   * Fix various compile warnings with newer GCC
   * Fix various compile warnings with newer GCC

+ 13 - 4
doc/help.txt

@@ -474,9 +474,6 @@ See also: link%{+a}, newhub%{-}
                        the bot from fighting with services such as ChanServ, or
                        the bot from fighting with services such as ChanServ, or
                        from kicking IRCops when setting channel modes without
                        from kicking IRCops when setting channel modes without
                        having ops.
                        having ops.
-        $bnomassjoin$b     If more than 6 clients join in 1 second, set +im
-                           for 2 minutes. (Could be triggered from net-split
-                           rejoin if clients hop servers and change nicks)
         $bprivate$b        This by far is probably the single most unique and
         $bprivate$b        This by far is probably the single most unique and
                        important feature of this pack. With this set, users with
                        important feature of this pack. With this set, users with
                        global +o will not implicitly have access to the channel.
                        global +o will not implicitly have access to the channel.
@@ -559,8 +556,20 @@ See also: link%{+a}, newhub%{-}
         $bflood-nick$b   Set here how many nick changes in how many seconds from
         $bflood-nick$b   Set here how many nick changes in how many seconds from
                     one host constitutes a flood. Setting this to 0 or 0:0
                     one host constitutes a flood. Setting this to 0 or 0:0
                     disables nick flood protection for the channel.
                     disables nick flood protection for the channel.
+   The following settings will trigger a lockdown (chanmode +mi) for 'flood-lock-time'
+   seconds.
         $bflood-mjoin$b  Set how many joins in how many seconds before triggering
         $bflood-mjoin$b  Set how many joins in how many seconds before triggering
-                    a lockdown for mass join. ($bnomassjoin$b must be set).
+                    a lockdown for mass join. Setting this to 0 or 0:0 disables mass
+                    join protection for the channel.
+        $bflood-mchan$b  Set here how many channel messages in how many seconds
+                    from any host constitutes a flood. Setting this to 0 or 0:0
+                    disables text flood protection for the channel.
+        $bflood-mbytes$b  Set here how many bytes in how many seconds from any
+                    host constitutes a flood. Setting this to 0 or 0:0
+                    disables text flood protection for the channel.
+        $bflood-mctcp$b  Set here how many channel ctcps in how many seconds from
+                    any host constitutes a flood. Setting this to 0 or 0:0
+                    disables ctcp flood protection for the channel.
    These can all be changed simultaneously with $bflood-*$b
    These can all be changed simultaneously with $bflood-*$b
  
  
 See also: %{-}%{+n}+chan, -chan%{-}%{+m|m}, chanset%{-}
 See also: %{-}%{+n}+chan, -chan%{-}%{+m|m}, chanset%{-}

+ 7 - 2
src/chan.h

@@ -168,8 +168,12 @@ struct chanset_t {
   uint32_t ircnet_status;
   uint32_t ircnet_status;
   int flood_pub_thr;
   int flood_pub_thr;
   interval_t flood_pub_time;
   interval_t flood_pub_time;
+  int flood_mpub_thr;
+  interval_t flood_mpub_time;
   int flood_bytes_thr;
   int flood_bytes_thr;
   interval_t flood_bytes_time;
   interval_t flood_bytes_time;
+  int flood_mbytes_thr;
+  interval_t flood_mbytes_time;
   int flood_join_thr;
   int flood_join_thr;
   interval_t flood_join_time;
   interval_t flood_join_time;
   int flood_deop_thr;
   int flood_deop_thr;
@@ -178,6 +182,8 @@ struct chanset_t {
   interval_t flood_kick_time;
   interval_t flood_kick_time;
   int flood_ctcp_thr;
   int flood_ctcp_thr;
   interval_t flood_ctcp_time;
   interval_t flood_ctcp_time;
+  int flood_mctcp_thr;
+  interval_t flood_mctcp_time;
   int flood_nick_thr;
   int flood_nick_thr;
   interval_t flood_nick_time;
   interval_t flood_nick_time;
   interval_t flood_lock_time;
   interval_t flood_lock_time;
@@ -253,7 +259,7 @@ struct chanset_t {
 #define CHAN_CYCLE          BIT11	/* cycle the channel if possible      */
 #define CHAN_CYCLE          BIT11	/* cycle the channel if possible      */
 #define CHAN_INACTIVE       BIT12	/* no irc support for this channel */
 #define CHAN_INACTIVE       BIT12	/* no irc support for this channel */
 #define CHAN_VOICE          BIT13	/* a bot +y|y will voice *, except +q */
 #define CHAN_VOICE          BIT13	/* a bot +y|y will voice *, except +q */
-#define CHAN_NOMASSJOIN     BIT14       /* watch for mass join for flood nets and react */
+//#define CHAN_NOMASSJOIN     BIT14       /* watch for mass join for flood nets and react */
 #define CHAN_NODESYNCH      BIT15
 #define CHAN_NODESYNCH      BIT15
 #define CHAN_FASTOP         BIT16	/* Bots will not use +o-b to op (no cookies) */ 
 #define CHAN_FASTOP         BIT16	/* Bots will not use +o-b to op (no cookies) */ 
 #define CHAN_PRIVATE        BIT17	/* users need |o to access chan */ 
 #define CHAN_PRIVATE        BIT17	/* users need |o to access chan */ 
@@ -317,7 +323,6 @@ struct chanset_t *findchan_by_dname(const char *name);
 #define channel_fastop(chan) (chan->status & CHAN_FASTOP)
 #define channel_fastop(chan) (chan->status & CHAN_FASTOP)
 #define channel_privchan(chan) (chan->status & CHAN_PRIVATE)
 #define channel_privchan(chan) (chan->status & CHAN_PRIVATE)
 #define channel_autoop(chan) (chan->status & CHAN_AUTOOP)
 #define channel_autoop(chan) (chan->status & CHAN_AUTOOP)
-#define channel_nomassjoin(chan) (chan->status & CHAN_NOMASSJOIN)
 #define channel_meankicks(chan) (chan->status & CHAN_MEANKICKS)
 #define channel_meankicks(chan) (chan->status & CHAN_MEANKICKS)
 #define channel_rbl(chan) (chan->status & CHAN_RBL)
 #define channel_rbl(chan) (chan->status & CHAN_RBL)
 #define channel_voicebitch(chan) (chan->status & CHAN_VOICEBITCH)
 #define channel_voicebitch(chan) (chan->status & CHAN_VOICEBITCH)

+ 23 - 4
src/mod/channels.mod/chanmisc.c

@@ -663,10 +663,6 @@ int channel_modify(char *result, struct chanset_t *chan, int items, char **item,
       chan->status |= CHAN_BOTBITCH;
       chan->status |= CHAN_BOTBITCH;
     else if (!strcmp(item[i], "-botbitch"))
     else if (!strcmp(item[i], "-botbitch"))
       chan->status &= ~CHAN_BOTBITCH;
       chan->status &= ~CHAN_BOTBITCH;
-    else if (!strcmp(item[i], "+nomassjoin"))
-      chan->status |= CHAN_NOMASSJOIN;
-    else if (!strcmp(item[i], "-nomassjoin"))
-      chan->status &= ~CHAN_NOMASSJOIN;
     else if (!strcmp(item[i], "+meankicks"))
     else if (!strcmp(item[i], "+meankicks"))
       chan->status |= CHAN_MEANKICKS;
       chan->status |= CHAN_MEANKICKS;
     else if (!strcmp(item[i], "-meankicks"))
     else if (!strcmp(item[i], "-meankicks"))
@@ -709,6 +705,8 @@ int channel_modify(char *result, struct chanset_t *chan, int items, char **item,
     else if (!cmd && !HAVE_TAKE && !strcmp(item[i], "-take")) ;
     else if (!cmd && !HAVE_TAKE && !strcmp(item[i], "-take")) ;
     else if (!cmd && !strcmp(item[i], "stopnethack-mode")) ;
     else if (!cmd && !strcmp(item[i], "stopnethack-mode")) ;
     else if (!cmd && !strcmp(item[i], "revenge-mode")) ;
     else if (!cmd && !strcmp(item[i], "revenge-mode")) ;
+    else if (!cmd && !strcmp(item[i], "+nomassjoin")) ;
+    else if (!cmd && !strcmp(item[i], "-nomassjoin")) ;
     else if (!cmd && !strcmp(item[i], "+revenge")) ;
     else if (!cmd && !strcmp(item[i], "+revenge")) ;
     else if (!cmd && !strcmp(item[i], "-revenge")) ;
     else if (!cmd && !strcmp(item[i], "-revenge")) ;
     else if (!cmd && !strcmp(item[i], "+revengebot")) ;
     else if (!cmd && !strcmp(item[i], "+revengebot")) ;
@@ -767,12 +765,18 @@ int channel_modify(char *result, struct chanset_t *chan, int items, char **item,
 
 
         chan->flood_pub_thr = thr;
         chan->flood_pub_thr = thr;
         chan->flood_pub_time = time;
         chan->flood_pub_time = time;
+        chan->flood_mpub_thr = thr;
+        chan->flood_mpub_time = time;
         chan->flood_bytes_thr = thr;
         chan->flood_bytes_thr = thr;
         chan->flood_bytes_time = time;
         chan->flood_bytes_time = time;
+        chan->flood_mbytes_thr = thr;
+        chan->flood_mbytes_time = time;
         chan->flood_join_thr = thr;
         chan->flood_join_thr = thr;
         chan->flood_join_time = time;
         chan->flood_join_time = time;
         chan->flood_ctcp_thr = thr;
         chan->flood_ctcp_thr = thr;
         chan->flood_ctcp_time = time;
         chan->flood_ctcp_time = time;
+        chan->flood_mctcp_thr = thr;
+        chan->flood_mctcp_time = time;
         chan->flood_kick_thr = thr;
         chan->flood_kick_thr = thr;
         chan->flood_kick_time = time;
         chan->flood_kick_time = time;
         chan->flood_deop_thr = thr;
         chan->flood_deop_thr = thr;
@@ -805,6 +809,15 @@ int channel_modify(char *result, struct chanset_t *chan, int items, char **item,
       } else if (!strcmp(item[i] + 6, "mjoin")) {
       } else if (!strcmp(item[i] + 6, "mjoin")) {
 	pthr = &chan->flood_mjoin_thr;
 	pthr = &chan->flood_mjoin_thr;
 	ptime = &chan->flood_mjoin_time;
 	ptime = &chan->flood_mjoin_time;
+      } else if (!strcmp(item[i] + 6, "mpub")) {
+	pthr = &chan->flood_mpub_thr;
+	ptime = &chan->flood_mpub_time;
+      } else if (!strcmp(item[i] + 6, "mbytes")) {
+	pthr = &chan->flood_mbytes_thr;
+	ptime = &chan->flood_mbytes_time;
+      } else if (!strcmp(item[i] + 6, "mctcp")) {
+	pthr = &chan->flood_mctcp_thr;
+	ptime = &chan->flood_mctcp_time;
       } else { /* Ignore for optimal forward compatibility */
       } else { /* Ignore for optimal forward compatibility */
         i++;
         i++;
         continue;
         continue;
@@ -1056,6 +1069,12 @@ int channel_add(char *result, const char *newname, char *options, bool isdefault
     chan->flood_nick_time = gfld_nick_time;
     chan->flood_nick_time = gfld_nick_time;
     chan->flood_mjoin_thr = 6;
     chan->flood_mjoin_thr = 6;
     chan->flood_mjoin_time = 1;
     chan->flood_mjoin_time = 1;
+    chan->flood_mpub_thr = 10;
+    chan->flood_mpub_time = 1;
+    chan->flood_mbytes_thr = 500;
+    chan->flood_mbytes_time = 1;
+    chan->flood_mctcp_thr = 7;
+    chan->flood_mctcp_time = 1;
     chan->limitraise = 20;
     chan->limitraise = 20;
     chan->ban_time = global_ban_time;
     chan->ban_time = global_ban_time;
     chan->exempt_time = global_exempt_time;
     chan->exempt_time = global_exempt_time;

+ 0 - 2
src/mod/channels.mod/channels.c

@@ -879,8 +879,6 @@ void channels_report(int idx, int details)
  *	if (channel_temp(chan))
  *	if (channel_temp(chan))
  *	  i += my_strcpy(s + i, "temp ");
  *	  i += my_strcpy(s + i, "temp ");
 */
 */
-        if (channel_nomassjoin(chan))
-          i += my_strcpy(s + i, "nomassjoin ");
         if (channel_botbitch(chan))
         if (channel_botbitch(chan))
           i += my_strcpy(s + i, "botbitch ");
           i += my_strcpy(s + i, "botbitch ");
         if (channel_backup(chan))
         if (channel_backup(chan))

+ 7 - 6
src/mod/channels.mod/cmdschan.c

@@ -1204,7 +1204,6 @@ static void cmd_chaninfo(int idx, char *par)
     SHOW_FLAG("inactive",	channel_inactive(chan));
     SHOW_FLAG("inactive",	channel_inactive(chan));
     SHOW_FLAG("meankicks",	channel_meankicks(chan));
     SHOW_FLAG("meankicks",	channel_meankicks(chan));
     SHOW_FLAG("nodesynch",	channel_nodesynch(chan));
     SHOW_FLAG("nodesynch",	channel_nodesynch(chan));
-    SHOW_FLAG("nomassjoin",	channel_nomassjoin(chan));
     SHOW_FLAG("private",	channel_privchan(chan));
     SHOW_FLAG("private",	channel_privchan(chan));
     SHOW_FLAG("protect",	channel_protect(chan));
     SHOW_FLAG("protect",	channel_protect(chan));
     SHOW_FLAG("rbl",		channel_rbl(chan));
     SHOW_FLAG("rbl",		channel_rbl(chan));
@@ -1257,17 +1256,19 @@ static void cmd_chaninfo(int idx, char *par)
     SHOW_INT("Protect-backup: ", chan->protect_backup, "Do!", "Don't!");
     SHOW_INT("Protect-backup: ", chan->protect_backup, "Do!", "Don't!");
     SHOW_INT("Voice-non-ident: ", chan->voice_non_ident, "Do!", "Don't!");
     SHOW_INT("Voice-non-ident: ", chan->voice_non_ident, "Do!", "Don't!");
 
 
-    dprintf(idx, "Flood settings:   chan bytes ctcp join kick deop nick mjoin\n");
-    dprintf(idx, "  number:          %3d  %4d  %3d  %3d  %3d  %3d  %3d  %3d\n",
+    dprintf(idx, "Flood settings:   chan bytes ctcp join kick deop nick mjoin mpub mbytes mctcp\n");
+    dprintf(idx, "  number:          %3d  %4d  %3d  %3d  %3d  %3d  %3d   %3d  %3d   %4d   %3d\n",
 	    chan->flood_pub_thr, chan->flood_bytes_thr, chan->flood_ctcp_thr,
 	    chan->flood_pub_thr, chan->flood_bytes_thr, chan->flood_ctcp_thr,
 	    chan->flood_join_thr, chan->flood_kick_thr,
 	    chan->flood_join_thr, chan->flood_kick_thr,
 	    chan->flood_deop_thr, chan->flood_nick_thr,
 	    chan->flood_deop_thr, chan->flood_nick_thr,
-            chan->flood_mjoin_thr);
-    dprintf(idx, "  time  :          %3u  %4u  %3u  %3u  %3u  %3u  %3u  %3u\n",
+            chan->flood_mjoin_thr, chan->flood_mpub_thr,
+            chan->flood_mbytes_thr, chan->flood_mctcp_thr);
+    dprintf(idx, "  time  :          %3u  %4u  %3u  %3u  %3u  %3u  %3u   %3u  %3u   %4u  %4u\n",
 	    chan->flood_pub_time, chan->flood_bytes_time, chan->flood_ctcp_time,
 	    chan->flood_pub_time, chan->flood_bytes_time, chan->flood_ctcp_time,
 	    chan->flood_join_time, chan->flood_kick_time,
 	    chan->flood_join_time, chan->flood_kick_time,
 	    chan->flood_deop_time, chan->flood_nick_time,
 	    chan->flood_deop_time, chan->flood_nick_time,
-            chan->flood_mjoin_time);
+            chan->flood_mjoin_time, chan->flood_mpub_time,
+            chan->flood_mbytes_time, chan->flood_mctcp_time);
   }
   }
 }
 }
 
 

+ 6 - 4
src/mod/channels.mod/userchan.c

@@ -717,13 +717,14 @@ bd::String channel_to_string(struct chanset_t* chan, bool force_inactive) {
 chanmode { %s } groups { %s } bad-cookie %d manop %d mdop %d mop %d limit %d revenge %d ban-type %d \
 chanmode { %s } groups { %s } bad-cookie %d manop %d mdop %d mop %d limit %d revenge %d ban-type %d \
 flood-chan %d:%d flood-bytes %d:%d flood-ctcp %d:%d flood-join %d:%d \
 flood-chan %d:%d flood-bytes %d:%d flood-ctcp %d:%d flood-join %d:%d \
 flood-kick %d:%d flood-deop %d:%d flood-nick %d:%d flood-mjoin %d:%d \
 flood-kick %d:%d flood-deop %d:%d flood-nick %d:%d flood-mjoin %d:%d \
+flood-mpub %d:%d flood-mbytes %d:%d flood-mctcp %d:%d \
 closed-ban %d closed-invite %d closed-private %d ban-time %d \
 closed-ban %d closed-invite %d closed-private %d ban-time %d \
 exempt-time %d invite-time %d voice-non-ident %d auto-delay %d \
 exempt-time %d invite-time %d voice-non-ident %d auto-delay %d \
 flood-exempt %d flood-lock-time %d knock %d fish-key { %s } \
 flood-exempt %d flood-lock-time %d knock %d fish-key { %s } \
 %cmeankicks %cenforcebans %cdynamicbans %cuserbans %cbitch %cfloodban \
 %cmeankicks %cenforcebans %cdynamicbans %cuserbans %cbitch %cfloodban \
 %cprivate %ccycle %cinactive %cdynamicexempts %cuserexempts \
 %cprivate %ccycle %cinactive %cdynamicexempts %cuserexempts \
 %cdynamicinvites %cuserinvites %cnodesynch %cclosed %cvoice \
 %cdynamicinvites %cuserinvites %cnodesynch %cclosed %cvoice \
-%cfastop %cautoop %cbotbitch %cbackup %cnomassjoin %crbl %cvoicebitch %cprotect protect-backup %d %c%s",
+%cfastop %cautoop %cbotbitch %cbackup %crbl %cvoicebitch %cprotect protect-backup %d %c%s",
 	w,
 	w,
 /* Chanchar template
 /* Chanchar template
  *      temp,
  *      temp,
@@ -745,6 +746,9 @@ flood-exempt %d flood-lock-time %d knock %d fish-key { %s } \
         chan->flood_deop_thr, chan->flood_deop_time,
         chan->flood_deop_thr, chan->flood_deop_time,
 	chan->flood_nick_thr, chan->flood_nick_time,
 	chan->flood_nick_thr, chan->flood_nick_time,
 	chan->flood_mjoin_thr, chan->flood_mjoin_time,
 	chan->flood_mjoin_thr, chan->flood_mjoin_time,
+	chan->flood_mpub_thr, chan->flood_mpub_time,
+	chan->flood_mbytes_thr, chan->flood_mbytes_time,
+	chan->flood_mctcp_thr, chan->flood_mctcp_time,
         chan->closed_ban,
         chan->closed_ban,
 /* Chanint template
 /* Chanint template
  *      chan->temp,
  *      chan->temp,
@@ -781,7 +785,6 @@ flood-exempt %d flood-lock-time %d knock %d fish-key { %s } \
         PLSMNS(channel_autoop(chan)),
         PLSMNS(channel_autoop(chan)),
         PLSMNS(channel_botbitch(chan)),
         PLSMNS(channel_botbitch(chan)),
         PLSMNS(channel_backup(chan)),
         PLSMNS(channel_backup(chan)),
-        PLSMNS(channel_nomassjoin(chan)),
         PLSMNS(channel_rbl(chan)),
         PLSMNS(channel_rbl(chan)),
         PLSMNS(channel_voicebitch(chan)),
         PLSMNS(channel_voicebitch(chan)),
         PLSMNS(channel_protect(chan)),
         PLSMNS(channel_protect(chan)),
@@ -839,7 +842,7 @@ exempt-time %d invite-time %d voice-non-ident %d auto-delay %d \
 %cenforcebans %cdynamicbans %cuserbans %cbitch \
 %cenforcebans %cdynamicbans %cuserbans %cbitch \
 %cprivate %ccycle %cinactive %cdynamicexempts %cuserexempts \
 %cprivate %ccycle %cinactive %cdynamicexempts %cuserexempts \
 %cdynamicinvites %cuserinvites %cnodesynch %cclosed %cvoice \
 %cdynamicinvites %cuserinvites %cnodesynch %cclosed %cvoice \
-%cfastop %cautoop %cbotbitch %cbackup %cnomassjoin %c%s}\n",
+%cfastop %cautoop %cbotbitch %cbackup %c%s}\n",
 	chan->dname,
 	chan->dname,
 	w,
 	w,
         chan->added_by,
         chan->added_by,
@@ -881,7 +884,6 @@ exempt-time %d invite-time %d voice-non-ident %d auto-delay %d \
         PLSMNS(channel_autoop(chan)),
         PLSMNS(channel_autoop(chan)),
         PLSMNS(channel_botbitch(chan)),
         PLSMNS(channel_botbitch(chan)),
         PLSMNS(channel_backup(chan)),
         PLSMNS(channel_backup(chan)),
-        PLSMNS(channel_nomassjoin(chan)),
 	HAVE_TAKE ? PLSMNS(channel_take(chan)) : ' ',
 	HAVE_TAKE ? PLSMNS(channel_take(chan)) : ' ',
         HAVE_TAKE ? "take " : " "
         HAVE_TAKE ? "take " : " "
     );
     );

+ 77 - 7
src/mod/irc.mod/chan.c

@@ -575,9 +575,9 @@ static bool detect_chan_flood(memberlist* m, const char *from, struct chanset_t
     return 0;
     return 0;
 
 
   char h[UHOSTLEN] = "", ftype[14] = "", *p = NULL;
   char h[UHOSTLEN] = "", ftype[14] = "", *p = NULL;
-  int thr = 0;
+  int thr = 0, mthr = 0;
   int increment = 1;
   int increment = 1;
-  time_t lapse = 0;
+  time_t lapse = 0, mlapse = 0;
 
 
   /* Determine how many are necessary to make a flood. */
   /* Determine how many are necessary to make a flood. */
   switch (which) {
   switch (which) {
@@ -585,18 +585,24 @@ static bool detect_chan_flood(memberlist* m, const char *from, struct chanset_t
   case FLOOD_NOTICE:
   case FLOOD_NOTICE:
     thr = chan->flood_pub_thr;
     thr = chan->flood_pub_thr;
     lapse = chan->flood_pub_time;
     lapse = chan->flood_pub_time;
+    mthr = chan->flood_mpub_thr;
+    mlapse = chan->flood_mpub_time;
     strlcpy(ftype, "pub", sizeof(ftype));
     strlcpy(ftype, "pub", sizeof(ftype));
     break;
     break;
   case FLOOD_BYTES:
   case FLOOD_BYTES:
     thr = chan->flood_bytes_thr;
     thr = chan->flood_bytes_thr;
     lapse = chan->flood_bytes_time;
     lapse = chan->flood_bytes_time;
+    mthr = chan->flood_mbytes_thr;
+    mlapse = chan->flood_mbytes_time;
     strlcpy(ftype, "bytes", sizeof(ftype));
     strlcpy(ftype, "bytes", sizeof(ftype));
     increment = static_cast<int>(strlen(msg));
     increment = static_cast<int>(strlen(msg));
     break;
     break;
   case FLOOD_CTCP:
   case FLOOD_CTCP:
     thr = chan->flood_ctcp_thr;
     thr = chan->flood_ctcp_thr;
     lapse = chan->flood_ctcp_time;
     lapse = chan->flood_ctcp_time;
-    strlcpy(ftype, "pub", sizeof(ftype));
+    mthr = chan->flood_mctcp_thr;
+    mlapse = chan->flood_mctcp_time;
+    strlcpy(ftype, "ctcp", sizeof(ftype));
     break;
     break;
   case FLOOD_NICK:
   case FLOOD_NICK:
     thr = chan->flood_nick_thr;
     thr = chan->flood_nick_thr;
@@ -620,8 +626,6 @@ static bool detect_chan_flood(memberlist* m, const char *from, struct chanset_t
     strlcpy(ftype, "kick", sizeof(ftype));
     strlcpy(ftype, "kick", sizeof(ftype));
     break;
     break;
   }
   }
-  if ((thr == 0) || (lapse == 0))
-    return 0;			/* no flood protection */
 
 
   if ((which == FLOOD_KICK) || (which == FLOOD_DEOP))
   if ((which == FLOOD_KICK) || (which == FLOOD_DEOP))
     p = m->nick;
     p = m->nick;
@@ -634,6 +638,40 @@ static bool detect_chan_flood(memberlist* m, const char *from, struct chanset_t
       return 0;
       return 0;
   }
   }
 
 
+  // Track across all clients in the channel
+  if (mlapse && mthr) {
+    if (!chan->channel.floodtime->contains("all")) {
+      (*chan->channel.floodtime)["all"][which] = now;
+      (*chan->channel.floodnum)["all"][which] = increment;
+      return 0;
+    }
+
+    bd::HashTable<flood_t, time_t>      *global_floodtime = &(*chan->channel.floodtime)["all"];
+    bd::HashTable<flood_t, int>         *global_floodnum = &(*chan->channel.floodnum)["all"];
+
+    if ((*global_floodtime)[which] < now - mlapse) {
+      /* Flood timer expired, reset it */
+      (*global_floodtime)[which] = now;
+      (*global_floodnum)[which] = increment;
+    } else {
+      (*global_floodnum)[which] += increment;
+
+      if ((*global_floodnum)[which] >= mthr) {	/* FLOOD */
+        /* Reset counters */
+        (*global_floodnum).remove(which);
+        (*global_floodtime).remove(which);
+        if (!chan->channel.drone_set_mode) {
+          lockdown_chan(chan, FLOOD_MASS_FLOOD, ftype);
+        }
+      }
+    }
+  }
+
+  if ((thr == 0) || (lapse == 0)) {
+    return 0;			/* no flood protection */
+  }
+
+  // Track individual hosts/clients
   bd::HashTable<flood_t, time_t>      *floodtime; // floodtime[FLOOD_PRIVMSG] = now;
   bd::HashTable<flood_t, time_t>      *floodtime; // floodtime[FLOOD_PRIVMSG] = now;
   bd::HashTable<flood_t, int>         *floodnum;  //  floodnum[FLOOD_PRIVMSG] = 1;
   bd::HashTable<flood_t, int>         *floodnum;  //  floodnum[FLOOD_PRIVMSG] = 1;
 
 
@@ -2446,6 +2484,37 @@ static int got475(char *from, char *msg)
   return 0;
   return 0;
 }
 }
 
 
+/* got 478: Channel ban list is full
+ * [@] irc.blessed.net 478 wtest #wraith-devel *!*@host.com :Channel ban list is full
+ */
+static int got478(char *from, char *msg)
+{
+  char *chname = NULL;
+  struct chanset_t *chan = NULL;
+
+  newsplit(&msg);
+  chname = newsplit(&msg);
+
+  /* !channel short names (also referred to as 'description names'
+   * can be received by skipping over the unique ID.
+   */
+  if ((chname[0] == '!') && (strlen(chname) > CHANNEL_ID_LEN)) {
+    chname += CHANNEL_ID_LEN;
+    chname[0] = '!';
+  }
+  /* We use dname because name is first set on JOIN and we might not
+   * have joined the channel yet.
+   */
+  chan = findchan_by_dname(chname);
+  if (chan && shouldjoin(chan)) {
+    // Only lockdown if not already locked down
+    if (!chan->channel.drone_set_mode) {
+      lockdown_chan(chan, FLOOD_BANLIST);
+    }
+  }
+  return 0;
+}
+
 /* got invitation
 /* got invitation
  */
  */
 static int gotinvite(char *from, char *msg)
 static int gotinvite(char *from, char *msg)
@@ -2706,7 +2775,7 @@ static int gotjoin(char *from, char *chname)
         bool is_op = chk_op(fr, chan);
         bool is_op = chk_op(fr, chan);
 
 
         /* Check for a mass join */
         /* Check for a mass join */
-        if (!splitjoin && channel_nomassjoin(chan) && !is_op) {
+        if (!splitjoin && chan->flood_mjoin_time && chan->flood_mjoin_thr && !is_op) {
           if (chan->channel.drone_jointime < now - chan->flood_mjoin_time) {      //expired, reset counter
           if (chan->channel.drone_jointime < now - chan->flood_mjoin_time) {      //expired, reset counter
             chan->channel.drone_joins = 0;
             chan->channel.drone_joins = 0;
           }
           }
@@ -2714,7 +2783,7 @@ static int gotjoin(char *from, char *chname)
           chan->channel.drone_jointime = now;
           chan->channel.drone_jointime = now;
 
 
           if (!chan->channel.drone_set_mode && chan->channel.drone_joins >= chan->flood_mjoin_thr) {  //flood from dronenet, let's attempt to set +im
           if (!chan->channel.drone_set_mode && chan->channel.drone_joins >= chan->flood_mjoin_thr) {  //flood from dronenet, let's attempt to set +im
-            detected_drone_flood(chan, m);
+            lockdown_chan(chan, FLOOD_MASSJOIN);
           }
           }
         }
         }
 
 
@@ -3411,6 +3480,7 @@ static cmd_t irc_raw[] =
   {"473",	"",	(Function) got473,	"irc:473", LEAF},
   {"473",	"",	(Function) got473,	"irc:473", LEAF},
   {"474",	"",	(Function) got474,	"irc:474", LEAF},
   {"474",	"",	(Function) got474,	"irc:474", LEAF},
   {"475",	"",	(Function) got475,	"irc:475", LEAF},
   {"475",	"",	(Function) got475,	"irc:475", LEAF},
+  {"478",	"",	(Function) got478,	"irc:478", LEAF},
   {"INVITE",	"",	(Function) gotinvite,	"irc:invite", LEAF},
   {"INVITE",	"",	(Function) gotinvite,	"irc:invite", LEAF},
   {"TOPIC",	"",	(Function) gottopic,	"irc:topic", LEAF},
   {"TOPIC",	"",	(Function) gottopic,	"irc:topic", LEAF},
   {"331",	"",	(Function) got331,	"irc:331", LEAF},
   {"331",	"",	(Function) got331,	"irc:331", LEAF},

+ 11 - 2
src/mod/irc.mod/irc.c

@@ -160,7 +160,10 @@ void unlock_chan(struct chanset_t *chan)
   chan->channel.drone_set_mode = 0;
   chan->channel.drone_set_mode = 0;
 }
 }
 
 
-void detected_drone_flood(struct chanset_t* chan, memberlist* m) {
+void lockdown_chan(struct chanset_t* chan, flood_reason_t reason, const char* flood_type) {
+  if (!me_op(chan)) {
+    return;
+  }
   egg_timeval_t howlong;
   egg_timeval_t howlong;
 
 
   chan->channel.drone_set_mode = 0;
   chan->channel.drone_set_mode = 0;
@@ -186,7 +189,13 @@ void detected_drone_flood(struct chanset_t* chan, memberlist* m) {
     howlong.usec = 0;
     howlong.usec = 0;
     timer_create_complex(&howlong, "unlock", (Function) unlock_chan, (void *) chan, 0);
     timer_create_complex(&howlong, "unlock", (Function) unlock_chan, (void *) chan, 0);
 
 
-    putlog(LOG_MISC, "*", "Flood detected in %s! Locking for %d seconds.", chan->dname, chan->flood_lock_time);
+    if (reason == FLOOD_MASSJOIN) {
+      putlog(LOG_MISC, "*", "Join flood detected in %s! Locking for %d seconds.", chan->dname, chan->flood_lock_time);
+    } else if (reason == FLOOD_BANLIST) {
+      putlog(LOG_MISC, "*", "Banlist full in %s! Locking for %d seconds.", chan->dname, chan->flood_lock_time);
+    } else if (reason == FLOOD_MASS_FLOOD) {
+      putlog(LOG_MISC, "*", "Mass flood (%s) detected in %s! Locking for %d seconds.", flood_type, chan->dname, chan->flood_lock_time);
+    }
   }
   }
 }
 }
 
 

+ 7 - 1
src/mod/irc.mod/irc.h

@@ -24,6 +24,12 @@ namespace bd {
   class String;
   class String;
 }
 }
 
 
+enum flood_reason_t {
+  FLOOD_MASSJOIN,
+  FLOOD_BANLIST,
+  FLOOD_MASS_FLOOD
+};
+
 #ifdef CACHE
 #ifdef CACHE
 typedef struct cache_chan_b {
 typedef struct cache_chan_b {
   struct cache_chan_b *next;
   struct cache_chan_b *next;
@@ -90,7 +96,7 @@ static bool killmember(struct chanset_t *chan, char *nick);
 static void check_lonely_channel(struct chanset_t *chan);
 static void check_lonely_channel(struct chanset_t *chan);
 static int gotmode(char *, char *);
 static int gotmode(char *, char *);
 void unset_im(struct chanset_t* chan);
 void unset_im(struct chanset_t* chan);
-void detected_drone_flood(struct chanset_t* chan, memberlist*);
+void lockdown_chan(struct chanset_t* chan, flood_reason_t reason, const char* flood_type = NULL);
 static void send_chan_who(int queue, struct chanset_t* chan, bool chain = 0);
 static void send_chan_who(int queue, struct chanset_t* chan, bool chain = 0);
 #define newban(chan, mask, who)         new_mask((chan)->channel.ban, mask, who)
 #define newban(chan, mask, who)         new_mask((chan)->channel.ban, mask, who)
 #define newexempt(chan, mask, who)      new_mask((chan)->channel.exempt, mask, who)
 #define newexempt(chan, mask, who)      new_mask((chan)->channel.exempt, mask, who)