فهرست منبع

Add flood counters to memberlist and split channel by uhost (#43)

This gives every member/uhost their own bucket for flood tracking, thus
fixing the issue of having the counters reset when another client is
active.
Bryan Drewery 14 سال پیش
والد
کامیت
59f597482f
8فایلهای تغییر یافته به همراه90 افزوده شده و 27 حذف شده
  1. 2 0
      doc/UPDATES
  2. 6 3
      src/chan.h
  3. 23 8
      src/eggdrop.h
  4. 12 0
      src/mod/channels.mod/chanmisc.c
  5. 2 0
      src/mod/channels.mod/channels.c
  6. 42 15
      src/mod/irc.mod/chan.c
  7. 2 0
      src/mod/irc.mod/irc.c
  8. 1 1
      src/mod/irc.mod/irc.h

+ 2 - 0
doc/UPDATES

@@ -1,3 +1,5 @@
+* Fix flood kicking not properly tracking multiple clients at once (#43)
+
 1.4.0 - http://wraith.botpack.net/milestone/1.4.0
   * Updated server list, 'set -yes servers -' and 'set -yes servers6 -' to get new list.
   * Change +bitch reaction to use normal queue for deopping opper/opped clients

+ 6 - 3
src/chan.h

@@ -27,6 +27,8 @@ typedef struct memstruct {
   char from[NICKLEN + UHOSTLEN];   /* nick!user@host */
   char fromip[NICKLEN + UHOSTLEN]; /* nick!user@ip */
   bool is_me;
+  bd::HashTable<flood_t, time_t>     *floodtime; // floodtime[FLOOD_PRIVMSG] = now;
+  bd::HashTable<flood_t, int>         *floodnum; // floodnum[FLOOD_PRIVMSG] = 1;
 } memberlist;
 
 #define CHAN_FLAG_OP	1
@@ -122,6 +124,10 @@ struct chan_t {
   char *topic;
   char *key;
   unsigned short int mode;
+
+  // Shared non-member counts (JOIN/PART)
+  bd::HashTable<bd::String, bd::HashTable<flood_t, time_t> >     *floodtime; // floodtime[uhost][FLOOD_PRIVMSG] = now;
+  bd::HashTable<bd::String, bd::HashTable<flood_t, int> >         *floodnum; //  floodnum[uhost][FLOOD_PRIVMSG] = 1;
 };
 
 #define CHANINV    BIT0		/* +i					*/
@@ -158,7 +164,6 @@ struct chanset_t {
     char *op;
   } ccmode[MODES_PER_LINE_MAX];                 /* parameter-type mode changes -        */
   /* detect floods */
-  time_t floodtime[FLOOD_CHAN_MAX];
   uint32_t status;
   uint32_t ircnet_status;
   int flood_pub_thr;
@@ -208,7 +213,6 @@ struct chanset_t {
   size_t bytes;			/* total bytes so far			*/
   size_t cbytes;
   int compat;			/* to prevent mixing old/new modes	*/
-  int floodnum[FLOOD_CHAN_MAX];
   int opreqtime[5];             /* remember when ops was requested */
 
   char *key;			/* new key to set			*/
@@ -223,7 +227,6 @@ struct chanset_t {
  */
   char topic[121];
   char added_by[HANDLEN + 1];	/* who added the channel? */
-  char floodwho[FLOOD_CHAN_MAX][128];
   char dname[81];               /* what the users know the channel as like !eggdev */
   char name[81];                /* what the servers know the channel as, like !ABCDEeggdev */
 };

+ 23 - 8
src/eggdrop.h

@@ -127,14 +127,29 @@ enum {		/* TAKE A GUESS */
 
 
 /* chan & global */
-#define FLOOD_PRIVMSG    0
-#define FLOOD_NOTICE     1
-#define FLOOD_CTCP       2
-#define FLOOD_NICK       3
-#define FLOOD_JOIN       4
-#define FLOOD_KICK       5
-#define FLOOD_DEOP       6
-#define FLOOD_PART       7
+enum flood_t {
+  FLOOD_PRIVMSG  = 0,
+  FLOOD_NOTICE   = 1,
+  FLOOD_CTCP     = 2,
+  FLOOD_NICK     = 3,
+  FLOOD_JOIN     = 4,
+  FLOOD_KICK     = 5,
+  FLOOD_DEOP     = 6,
+  FLOOD_PART     = 7
+};
+
+
+#include <bdlib/src/bdlib.h>
+BDLIB_NS_BEGIN
+template<typename T>
+  struct Hash;
+
+template<>
+  struct Hash<flood_t>
+  {
+    inline size_t operator()(flood_t val) const { return static_cast<size_t>(val); }
+  };
+BDLIB_NS_END
 
 #define FLOOD_CHAN_MAX   8
 #define FLOOD_GLOBAL_MAX 3

+ 12 - 0
src/mod/channels.mod/chanmisc.c

@@ -897,6 +897,8 @@ static void init_channel(struct chanset_t *chan, bool reset)
   chan->channel.member->nick[0] = 0;
   chan->channel.member->next = NULL;
   chan->channel.topic = NULL;
+  chan->channel.floodtime = new bd::HashTable<bd::String, bd::HashTable<flood_t, time_t> >;
+  chan->channel.floodnum  = new bd::HashTable<bd::String, bd::HashTable<flood_t, int> >;
 }
 
 static void clear_masklist(masklist *m)
@@ -923,6 +925,8 @@ void clear_channel(struct chanset_t *chan, bool reset)
     free(chan->channel.topic);
   for (m = chan->channel.member; m; m = m1) {
     m1 = m->next;
+    delete m->floodtime;
+    delete m->floodnum;
     free(m);
   }
 
@@ -938,6 +942,11 @@ void clear_channel(struct chanset_t *chan, bool reset)
   chan->ircnet_status = 0;
 //  chan->ircnet_status &= ~CHAN_HAVEBANS;
 
+  delete chan->channel.floodtime;
+  chan->channel.floodtime = NULL;
+  delete chan->channel.floodnum;
+  chan->channel.floodnum = NULL;
+
   if (reset)
     init_channel(chan, 1);
   for (size_t i = 0; i < MODES_PER_LINE_MAX; ++i) {
@@ -950,6 +959,7 @@ void clear_channel(struct chanset_t *chan, bool reset)
       chan->ccmode[i].op = NULL;
     }
   }
+
 }
 
 /* Create new channel and parse commands.
@@ -1050,6 +1060,8 @@ int channel_add(char *result, const char *newname, char *options, bool isdefault
     chan->channel.drone_jointime = 0;
     chan->channel.drone_joins = 0;
     chan->channel.last_eI = 0;
+    chan->channel.floodtime = NULL;
+    chan->channel.floodnum = NULL;
 
     /* We _only_ put the dname (display name) in here so as not to confuse
      * any code later on. chan->name gets updated with the channel name as

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

@@ -723,6 +723,8 @@ void remove_channel(struct chanset_t *chan)
    if (chan->groups) {
      delete(chan->groups);
    }
+   delete chan->channel.floodtime;
+   delete chan->channel.floodnum;
    free(chan);
 }
 

+ 42 - 15
src/mod/irc.mod/chan.c

@@ -280,6 +280,8 @@ static memberlist *newmember(struct chanset_t *chan, char *nick)
   }
 
   ++(chan->channel.members);
+  n->floodtime = new bd::HashTable<flood_t, time_t>;
+  n->floodnum  = new bd::HashTable<flood_t, int>;
   return n;
 }
 
@@ -538,7 +540,7 @@ static void do_mask(struct chanset_t *chan, masklist *m, char *mask, char Mode)
 /* This is a clone of detect_flood, but works for channel specificity now
  * and handles kick & deop as well.
  */
-static bool detect_chan_flood(memberlist* m, const char *from, struct chanset_t *chan, int which, const char *msg)
+static bool detect_chan_flood(memberlist* m, const char *from, struct chanset_t *chan, flood_t which, const char *msg)
 {
   /* Do not punish non-existant channel members and IRC services like
    * ChanServ
@@ -624,24 +626,49 @@ static bool detect_chan_flood(memberlist* m, const char *from, struct chanset_t
     if (!p)
       return 0;
   }
-  if (rfc_casecmp(chan->floodwho[which], p)) {	/* new */
-    strlcpy(chan->floodwho[which], p, sizeof(chan->floodwho[which]));
-    chan->floodtime[which] = now;
-    chan->floodnum[which] = 1;
-    return 0;
+
+  bd::HashTable<flood_t, time_t>      *floodtime; // floodtime[FLOOD_PRIVMSG] = now;
+  bd::HashTable<flood_t, int>         *floodnum;  //  floodnum[FLOOD_PRIVMSG] = 1;
+
+  switch (which) {
+    // These 2 don't have a persistent member, use chan list
+    case FLOOD_JOIN:
+    case FLOOD_PART:
+      // If not found, add them and start the count for next iteration
+      if (!chan->channel.floodtime->contains(m->userhost)) {
+        (*chan->channel.floodtime)[m->userhost][which] = now;
+        (*chan->channel.floodnum)[m->userhost][which] = 1;
+        return 0;
+      } else {
+        floodtime = &(*chan->channel.floodtime)[m->userhost];
+        floodnum = &(*chan->channel.floodnum)[m->userhost];
+      }
+      break;
+    default:
+      // Everything else, log to the member
+      // If not found, add them and start the count for next iteration
+      if (!m->floodtime->contains(which)) {
+        (*m->floodtime)[which] = now;
+        (*m->floodnum)[which] = 1;
+        return 0;
+      } else {
+        floodtime = m->floodtime;
+        floodnum = m->floodnum;
+      }
+      break;
   }
-  if (chan->floodtime[which] < now - lapse) {
+
+  if ((*floodtime)[which] < now - lapse) {
     /* Flood timer expired, reset it */
-    chan->floodtime[which] = now;
-    chan->floodnum[which] = 1;
+    (*floodtime)[which] = now;
+    (*floodnum)[which] = 1;
     return 0;
   }
-  chan->floodnum[which]++;
-  if (chan->floodnum[which] >= thr) {	/* FLOOD */
+  (*floodnum)[which]++;
+  if ((*floodnum)[which] >= thr) {	/* FLOOD */
     /* Reset counters */
-    chan->floodnum[which] = 0;
-    chan->floodtime[which] = 0;
-    chan->floodwho[which][0] = 0;
+    (*floodnum).remove(which);
+    (*floodtime).remove(which);
     switch (which) {
     case FLOOD_PRIVMSG:
     case FLOOD_NOTICE:
@@ -685,7 +712,7 @@ static bool detect_chan_flood(memberlist* m, const char *from, struct chanset_t
       if (!channel_enforcebans(chan) && me_op(chan)) {
 	  for (m = chan->channel.member; m && m->nick[0]; m = m->next) {	  
 	    if (!chan_sentkick(m) && wild_match(h, m->from) &&
-		(m->joined >= chan->floodtime[which]) &&
+		(m->joined >= (*floodtime)[which]) &&
 		!m->is_me && me_op(chan)) {
 	      m->flags |= SENTKICK;
 	      if (which == FLOOD_JOIN)

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

@@ -1077,6 +1077,8 @@ killmember(struct chanset_t *chan, char *nick)
     old->next = x->next;
   else
     chan->channel.member = x->next;
+  delete x->floodtime;
+  delete x->floodnum;
   free(x);
   chan->channel.members--;
 

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

@@ -82,7 +82,7 @@ static void do_protect(struct chanset_t* chan, const char* reason);
 static bool do_op(char *, struct chanset_t *, bool, bool);
 static void request_op(struct chanset_t *);
 static void request_in(struct chanset_t *);
-static bool detect_chan_flood(memberlist *m, const char* from, struct chanset_t *chan, int which, const char *msg = NULL);
+static bool detect_chan_flood(memberlist *m, const char* from, struct chanset_t *chan, flood_t which, const char *msg = NULL);
 static bool new_mask(masklist *, char *, char *);
 static void do_closed_kick(struct chanset_t *, memberlist *);
 static char *quickban(struct chanset_t *, const char *);