fix possible crash on user deletion
[srvx.git] / src / mod-blacklist.c
index c2d1e5eabc4f13acee579a57460a601ff08b0754..61d0adf5072f973d10065af23ae4bffa273863ce 100644 (file)
@@ -32,6 +32,7 @@ struct dnsbl_zone {
     const char *reason;
     unsigned int duration;
     unsigned int mask;
+    unsigned int debug : 1;
     char zone[1];
 };
 
@@ -46,9 +47,17 @@ static dict_t blacklist_hosts; /* maps IPs or hostnames to reasons from blacklis
 static dict_t blacklist_reasons; /* maps strings to themselves (poor man's data sharing) */
 
 static struct {
+    struct userNode *debug_bot;
+    struct chanNode *debug_channel;
     unsigned long gline_duration;
 } conf;
 
+#if defined(GCC_VARMACROS)
+# define blacklist_debug(ARGS...) do { if (conf.debug_bot && conf.debug_channel) send_channel_message(conf.debug_channel, conf.debug_bot, ARGS); } while (0)
+#elif defined(C99_VARMACROS)
+# define blacklist_debug(...) do { if (conf.debug_bot && conf.debug_channel) send_channel_message(conf.debug_channel, conf.debug_bot, __VA_ARGS__); } while (0)
+#endif
+
 static void
 do_expandos(char *output, unsigned int out_len, const char *input, ...)
 {
@@ -133,19 +142,26 @@ dnsbl_hit(struct sar_request *req, struct dns_header *hdr, struct dns_rr *rr, un
                 message = "client is blacklisted";
         }
 
-        /* Expand elements of the message as necessary. */
-        do_expandos(reason, sizeof(reason), message, "%txt%", (txt ? txt : "(no-txt)"), "%ip%", data->client_ip, NULL);
+        /* Prepend "AUTO " prefix so the g-lined are put in a different snomask */
+        strcpy(reason, "AUTO ");
 
-        /* Now generate the G-line. */
-        target[0] = '*';
-        target[1] = '@';
-        strcpy(target + 2, data->client_ip);
-        gline_add(self->name, target, zone->duration, reason, now, now, 1);
+        /* Expand elements of the message as necessary. */
+        do_expandos(reason + 5, sizeof(reason) - 5, message, "%txt%", (txt ? txt : "(no-txt)"), "%ip%", data->client_ip, NULL);
+
+        if (zone->debug) {
+            blacklist_debug("DNSBL match: [%s] %s (%s)", zone->zone, data->client_ip, reason);
+        } else {
+            /* Now generate the G-line. */
+            target[0] = '*';
+            target[1] = '@';
+            strcpy(target + 2, data->client_ip);
+            gline_add(self->name, target, zone->duration, reason, now, now, 0, 1);
+        }
     }
     free(txt);
 }
 
-static int
+static void
 blacklist_check_user(struct userNode *user)
 {
     static const char *hexdigits = "0123456789abcdef";
@@ -157,9 +173,13 @@ blacklist_check_user(struct userNode *user)
     char ip[IRC_NTOP_MAX_SIZE];
     char dnsbl_target[128];
 
+    /* Users added during burst should not be checked. */
+    if (user->uplink->burst)
+        return;
+
     /* Users with bogus IPs are probably service bots. */
     if (!irc_in_addr_is_valid(user->ip))
-        return 0;
+        return;
 
     /* Check local file-based blacklist. */
     irc_ntop(ip, sizeof(ip), &user->ip);
@@ -173,7 +193,8 @@ blacklist_check_user(struct userNode *user)
         target[0] = '*';
         target[1] = '@';
         strcpy(target + 2, host);
-        gline_add(self->name, target, conf.gline_duration, reason, now, now, 1);
+        /* We do not prepend AUTO here so it can be done in the blacklist file. */
+        gline_add(self->name, target, conf.gline_duration, reason, now, now, 0, 1);
     }
 
     /* Figure out the base part of a DNS blacklist hostname. */
@@ -188,7 +209,7 @@ blacklist_check_user(struct userNode *user)
         }
         dnsbl_len = 48;
     } else {
-        return 0;
+        return;
     }
 
     /* Start a lookup for the appropriate hostname in each DNSBL. */
@@ -203,10 +224,9 @@ blacklist_check_user(struct userNode *user)
         if (req) {
             data = (struct dnsbl_data*)(req + 1);
             strcpy(data->client_ip, ip);
-            strcpy(data->zone_name, zone);            
+            strcpy(data->zone_name, zone);
         }
     }
-    return 0;
 }
 
 static void
@@ -278,7 +298,7 @@ blacklist_conf_read(void)
 
     dict_delete(blacklist_zones);
     blacklist_zones = dict_new();
-    dict_set_free_data(blacklist_zones, free);
+    dict_set_free_data(blacklist_zones, dnsbl_zone_free);
 
     dict_delete(blacklist_hosts);
     blacklist_hosts = dict_new();
@@ -286,12 +306,27 @@ blacklist_conf_read(void)
 
     dict_delete(blacklist_reasons);
     blacklist_reasons = dict_new();
-    dict_set_free_keys(blacklist_reasons, dnsbl_zone_free);
+    dict_set_free_keys(blacklist_reasons, free);
 
     node = conf_get_data("modules/blacklist", RECDB_OBJECT);
     if (node == NULL)
         return;
 
+    str1 = database_get_data(node, "debug_bot", RECDB_QSTRING);
+    if (str1)
+        conf.debug_bot = GetUserH(str1);
+
+    str1 = database_get_data(node, "debug_channel", RECDB_QSTRING);
+    if (conf.debug_bot && str1) {
+        str2 = database_get_data(node, "debug_channel_modes", RECDB_QSTRING);
+        if (!str2)
+            str2 = "+tinms";
+        conf.debug_channel = AddChannel(str1, now, str2, NULL);
+        AddChannelUser(conf.debug_bot, conf.debug_channel)->modes |= MODE_CHANOP;
+    } else {
+        conf.debug_channel = NULL;
+    }
+
     str1 = database_get_data(node, "file", RECDB_QSTRING);
     str2 = database_get_data(node, "file_reason", RECDB_QSTRING);
     blacklist_load_file(str1, str2);
@@ -324,6 +359,8 @@ blacklist_conf_read(void)
             zone->duration = str1 ? ParseInterval(str1) : 3600;
             str1 = database_get_data(dnsbl, "mask", RECDB_QSTRING);
             zone->mask = str1 ? strtoul(str1, NULL, 0) : ~0u;
+            str1 = database_get_data(dnsbl, "debug", RECDB_QSTRING);
+            zone->debug = str1 ? enabled_string(str1) : 0;
             zone->reasons.used = 0;
             zone->reasons.size = 0;
             zone->reasons.list = NULL;