Przeglądaj źródła

Merge branch 'rbl-fixes'

* rbl-fixes:
  * Fix NULL dereference if receiving an RBL lookup response after the channel has been removed
  * Fix how DNS query IDs are generated to avoid a race condition / false results
  * Fix RBL not checking fully if any user is found in the channel
  * Fix RBL not being lookup up properly on members with a cached userip
  * Fix IPv6 RBL lookups
  * Fix listing in dns cache listing
Bryan Drewery 16 lat temu
rodzic
commit
ed25f302a2
2 zmienionych plików z 66 dodań i 27 usunięć
  1. 18 2
      src/adns.c
  2. 48 25
      src/mod/irc.mod/chan.c

+ 18 - 2
src/adns.c

@@ -297,11 +297,27 @@ static void free_query(dns_query_t*& q) {
 	q = NULL;
 }
 
+static int get_dns_id() {
+	while (1) {
+		int id = randint(65534);
+		bool found = 0;
+		// Make sure this ID is not already in use, avoid a race condition
+		for (dns_query_t *q = query_head; q; q = q->next) {
+			if (q->id == id) {
+				found = 1;
+				break;
+			}
+		}
+		if (found) continue;
+		return id;
+	}
+}
+
 static dns_query_t *alloc_query(void *client_data, dns_callback_t callback, const char *query)
 {
 	dns_query_t *q = (dns_query_t *) my_calloc(1, sizeof(*q));
 
-	q->id = randint(65534);
+	q->id = get_dns_id();
 	q->query = strdup(query);
 	q->answers = 0;
         q->answer = new bd::Array<bd::String>;
@@ -1264,7 +1280,7 @@ void tell_dnsdebug(int idx)
 	for (int i = 0; i < ncache; i++) {
 		dprintf(idx, "cache(%d) %s expires in %ds\n", i, cache[i].query, (int) (cache[i].expiretime - now));
 		for (size_t n = 0; n < cache[i].answer->size(); n++)
-			dprintf(idx, "%zu: %s\n", n, cache[i].answer->join(',').c_str());
+			dprintf(idx, "%zu: %s\n", n, bd::String((*(cache[i].answer))[n]).c_str());
 	}
 }
 

+ 48 - 25
src/mod/irc.mod/chan.c

@@ -38,7 +38,7 @@ static time_t last_invtime = (time_t) 0L;
 static char   last_invchan[300] = "";
 
 typedef struct resolvstruct {
-  struct chanset_t *chan;
+  char *chan;
   char *host;
   bd::String* servers;
   bd::String* server;
@@ -47,9 +47,11 @@ typedef struct resolvstruct {
 static void resolv_member_callback(int id, void *client_data, const char *host, bd::Array<bd::String> ips)
 {
   resolv_member *r = (resolv_member *) client_data;
+  struct chanset_t* chan = NULL;
 
-  if (!r || !r->chan || !r->host || !ips.size()) {
+  if (!r || !r->chan || !r->host || !ips.size() || !(chan = findchan_by_dname(r->chan))) {
     if (r) {
+      if (r->chan) free(r->chan);
       if (r->host) free(r->host);
       free(r);
     }
@@ -61,7 +63,7 @@ static void resolv_member_callback(int id, void *client_data, const char *host,
   bool matched_user = 0;
 
   /* Apply lookup results to all matching members by host */
-  for (m = r->chan->channel.member; m && m->nick[0]; m = m->next) {
+  for (m = chan->channel.member; m && m->nick[0]; m = m->next) {
     if (!m->userip[0] && m->userhost[0]) {
       pe = strchr(m->userhost, '@');
       if (pe && !strcmp(pe + 1, r->host)) {
@@ -75,16 +77,17 @@ static void resolv_member_callback(int id, void *client_data, const char *host,
           if (m->user)
             check_this_user(m->user->handle, 0, NULL);
         }
+        if (m->user)
+          matched_user = 1;
       }
     }
-    if (m->user)
-      matched_user = 1;
   }
 
-  if (!matched_user && channel_rbl(r->chan))
-    resolve_to_rbl(r->chan, bd::String(ips[0]).c_str());
+  if (!matched_user && channel_rbl(chan))
+    resolve_to_rbl(chan, bd::String(ips[0]).c_str());
 
   free(r->host);
+  free(r->chan);
   free(r);
   return;
 }
@@ -94,13 +97,14 @@ void resolve_to_member(struct chanset_t *chan, char *nick, char *host)
 {
   resolv_member *r = (resolv_member *) my_calloc(1, sizeof(resolv_member));
 
-  r->chan = chan;
+  r->chan = strdup(chan->dname);
   r->host = strdup(host);
 
   if (egg_dns_lookup(host, 20, resolv_member_callback, (void *) r) == -2) { //Already querying?
     // Querying on clones will cause the callback to not be called and this nick will be ignored
     // cleanup memory as this chain is not even starting.
     free(r->host);
+    free(r->chan);
     free(r);
   }
 }
@@ -109,46 +113,48 @@ void resolve_to_member(struct chanset_t *chan, char *nick, char *host)
 static void resolve_rbl_callback(int id, void *client_data, const char *host, bd::Array<bd::String> ips)
 {
   resolv_member *r = (resolv_member *) client_data;
+  struct chanset_t* chan = NULL;
 
-  if (!r || !r->chan || !r->host || !ips.size()) {
-    if (r && r->chan && r->host) {
+  if (!r || !r->chan || !r->host || !(chan = findchan_by_dname(r->chan)) || !ips.size()) {
+    if (r && chan && r->host) {
       // Lookup from the next RBL
-      resolve_to_rbl(r->chan, r->host, r);
+      resolve_to_rbl(chan, r->host, r);
     }
     return;
   }
 
-  sdprintf("RBL match for %s:%s: %s", r->chan->dname, r->host, bd::String(ips[0]).c_str());
+  sdprintf("RBL match for %s:%s: %s", chan->dname, r->host, bd::String(ips[0]).c_str());
 
   char s1[UHOSTLEN] = "";
   simple_snprintf(s1, sizeof(s1), "*!*@%s", r->host);
 
   bd::String reason = "Listed in RBL: " + *(r->server);
 
-  u_addmask('b', r->chan, s1, conf.bot->nick, reason.c_str(), now + (60 * (r->chan->ban_time ? r->chan->ban_time : 300)), 0);
+  u_addmask('b', chan, s1, conf.bot->nick, reason.c_str(), now + (60 * (chan->ban_time ? chan->ban_time : 300)), 0);
 
-  if (me_op(r->chan)) {
-    do_mask(r->chan, r->chan->channel.ban, s1, 'b');
+  if (me_op(chan)) {
+    do_mask(chan, chan->channel.ban, s1, 'b');
 
     memberlist *m = NULL;
     char *pe = NULL;
 
     /* Apply lookup results to all matching members by host */
-    for (m = r->chan->channel.member; m && m->nick[0]; m = m->next) {
+    for (m = chan->channel.member; m && m->nick[0]; m = m->next) {
       if (!m->user && !chan_sentkick(m) && m->userip[0]) {
         pe = strchr(m->userip, '@');
         if (pe && !strcmp(pe + 1, r->host)) {
           m->flags |= SENTKICK;
-          dprintf(DP_MODE, "KICK %s %s :%s%s\n", r->chan->name, m->nick, bankickprefix, reason.c_str());
+          dprintf(DP_MODE, "KICK %s %s :%s%s\n", chan->name, m->nick, bankickprefix, reason.c_str());
         }
       }
     }
   }
 
-  sdprintf("Done checking rbl (matched) for %s:%s", r->chan->dname, r->host);
+  sdprintf("Done checking rbl (matched) for %s:%s", chan->dname, r->host);
   delete r->servers;
   delete r->server;
   free(r->host);
+  free(r->chan);
   free(r);
   return;
 }
@@ -156,10 +162,14 @@ static void resolve_rbl_callback(int id, void *client_data, const char *host, bd
 
 void resolve_to_rbl(struct chanset_t *chan, const char *host, resolv_member *r)
 {
+  // Skip past user@ if present
+  char *p = strchr((char*)host, '@');
+  if (p)
+    host = p + 1;
   if (!r) {
     r = (resolv_member *) my_calloc(1, sizeof(resolv_member));
 
-    r->chan = chan;
+    r->chan = strdup(chan->dname);
     r->host = strdup(host);
     r->servers = new bd::String(rbl_servers);
     r->server = new bd::String;
@@ -172,16 +182,28 @@ void resolve_to_rbl(struct chanset_t *chan, const char *host, resolv_member *r)
     delete r->servers;
     delete r->server;
     free(r->host);
+    free(r->chan);
     free(r);
     return; //No more servers
   }
 
   *(r->server) = rbl_server;
 
-  size_t iplen = strlen(host) + 1 + rbl_server.length() + 1;
+  size_t iplen = rbl_server.length() + 1;
+  bool v6 = 0;
+  if (strchr(host, ':')) {
+    v6 = 1;
+    iplen += 128 + 1;
+  } else
+    iplen += strlen(host) + 1;
+
   char *ip = (char *) my_calloc(1, iplen);
-  reverse_ip(host, ip);
-  strlcat(ip, ".", iplen);
+  if (v6)
+    socket_ipv6_to_dots(host, ip);
+  else {
+    reverse_ip(host, ip);
+    strlcat(ip, ".", iplen);
+  }
   strlcat(ip, rbl_server.c_str(), iplen);
 
   if (egg_dns_lookup(ip, 20, resolve_rbl_callback, (void *) r, DNS_A) == -2) { //Already querying?
@@ -190,6 +212,7 @@ void resolve_to_rbl(struct chanset_t *chan, const char *host, resolv_member *r)
     delete r->servers;
     delete r->server;
     free(r->host);
+    free(r->chan);
     free(r);
   }
 
@@ -1899,7 +1922,7 @@ static int got352or4(struct chanset_t *chan, char *user, char *host, char *nick,
   if (!m->userip[0] && doresolv(chan))
     resolve_to_member(chan, nick, host);
   else if (!me && !m->user && m->userip[0] && doresolv(chan) && channel_rbl(chan))
-    resolve_to_rbl(chan, host);
+    resolve_to_rbl(chan, m->userip);
 
 
   get_user_flagrec(m->user, &fr, chan->dname, chan);
@@ -2604,7 +2627,7 @@ static int gotjoin(char *from, char *chname)
             if (is_dotted_ip(host)) {
               strlcpy(m->userip, uhost, sizeof(m->userip));
               if (channel_rbl(chan))
-                resolve_to_rbl(chan, host);
+                resolve_to_rbl(chan, m->userip);
             } else
               resolve_to_member(chan, nick, host);
           }
@@ -2627,7 +2650,7 @@ static int gotjoin(char *from, char *chname)
         if (!m->userip[0] && doresolv(chan))
           resolve_to_member(chan, nick, host);
         else if (!m->user && m->userip[0] && doresolv(chan) && channel_rbl(chan))
-          resolve_to_rbl(chan, host);
+          resolve_to_rbl(chan, m->userip);
 
 //	m->flags |= STOPWHO;