? src/bopm.pid
? src/libopm
Index: src/config-lexer.l
===================================================================
RCS file: /data/cvs/bopm/src/config-lexer.l,v
retrieving revision 1.6
diff -u -6 -r1.6 config-lexer.l
--- src/config-lexer.l	19 Jun 2003 23:07:57 -0000	1.6
+++ src/config-lexer.l	22 Jun 2003 19:36:12 -0000
@@ -89,12 +89,13 @@
                                  return STRING;
                               }
                            }
              
                         }
 
+ALERT                   { return ALERT;        }
 AWAY                    { return AWAY;         }
 BAN_UNKNOWN             { return BAN_UNKNOWN;  }
 BLACKLIST               { return BLACKLIST;    }
 CHANNEL                 { return CHANNEL;      }
 CONNREGEX               { return CONNREGEX;    }
 DNS_FDLIMIT             { return DNS_FDLIMIT;  }
@@ -132,12 +133,13 @@
 TARGET_STRING           { return TARGET_STRING;}
 TIMEOUT                 { return TIMEOUT;      }
 TYPE                    { return TYPE;         }
 USER                    { return USER;         }
 USERNAME                { return USERNAME;     }
 VHOST                   { return VHOST;        }
+WHITELIST               { return WHITELIST;    }
 
 
 HTTP                    {
                           yylval.number = OPM_TYPE_HTTP;
                           return PROTOCOLTYPE;
                         }
Index: src/config-parser.y
===================================================================
RCS file: /data/cvs/bopm/src/config-parser.y,v
retrieving revision 1.7
diff -u -6 -r1.7 config-parser.y
--- src/config-parser.y	22 Jun 2003 13:19:39 -0000	1.7
+++ src/config-parser.y	22 Jun 2003 19:36:12 -0000
@@ -30,12 +30,13 @@
 
 int yydebug=0;
 void *tmp;        /* Variable to temporarily hold nodes before insertion to list */
 
 %}
 
+%token ALERT
 %token AWAY
 %token BAN_UNKNOWN
 %token BLACKLIST
 %token CHANNEL
 %token CONNREGEX
 %token DNS_FDLIMIT
@@ -74,12 +75,13 @@
 %token TARGET_STRING
 %token TIMEOUT
 %token TYPE
 %token USERNAME
 %token USER
 %token VHOST
+%token WHITELIST
 
 %union 
 {
         int number;
         char *string;
 }
@@ -526,12 +528,14 @@
 
    item = MyMalloc(sizeof *item);
 
    item->name = DupString("");
    item->kline = DupString("");
    item->ban_unknown = 0;
+   item->whitelist = 0;
+   item->alert = 1;
    item->type = A_BITMASK;
    item->reply = list_create();
 
    node = node_create(item);
    list_add(OpmItem->blacklists, node);
 
@@ -542,14 +546,16 @@
 blacklist_items: /* Empty */                 |
               blacklist_items blacklist_item |
               blacklist_item;
 
 blacklist_item: blacklist_name        |
                 blacklist_type        |
+                blacklist_whitelist   |
                 blacklist_kline       |
                 blacklist_ban_unknown |
+                blacklist_alert       |
                 blacklist_reply       |
                 error;
 
 blacklist_name: NAME '=' STRING ';' {
    struct BlacklistConf *item = tmp;
 
@@ -570,12 +576,24 @@
    if(strcmp("A record bitmask", $3) == 0)
       item->type = A_BITMASK;
    else if(strcmp("A record reply", $3) == 0)
       item->type = A_REPLY;
    else
       yyerror("Unknown blacklist type defined");
+};
+
+blacklist_whitelist: WHITELIST '=' NUMBER ';' {
+   struct BlacklistConf *item = tmp;
+
+   item->whitelist = $3;
+};
+
+blacklist_alert: ALERT '=' NUMBER ';' {
+    struct BlacklistConf *item = tmp;
+
+    item->alert = $3;
 };
 
 blacklist_ban_unknown: BAN_UNKNOWN '=' NUMBER ';' {
    struct BlacklistConf *item = tmp;
 
    item->ban_unknown = $3;
Index: src/config.h
===================================================================
RCS file: /data/cvs/bopm/src/config.h,v
retrieving revision 1.9
diff -u -6 -r1.9 config.h
--- src/config.h	21 Jun 2003 00:57:28 -0000	1.9
+++ src/config.h	22 Jun 2003 19:36:12 -0000
@@ -101,13 +101,15 @@
 
 struct BlacklistConf
 {
    char   *name;
    char   *kline;
    enum BlacklistType type;
+   int     whitelist;
    int     ban_unknown;
+   int     alert;
    list_t *reply;
    unsigned int stats_recv;
 };
 
 struct BlacklistReplyConf
 {
Index: src/dnsbl.c
===================================================================
RCS file: /data/cvs/bopm/src/dnsbl.c,v
retrieving revision 1.29
diff -u -6 -r1.29 dnsbl.c
--- src/dnsbl.c	22 Jun 2003 18:03:41 -0000	1.29
+++ src/dnsbl.c	22 Jun 2003 19:36:12 -0000
@@ -96,17 +96,27 @@
 
       if(res == -1 && fdns_errno != FDNS_ERR_FDLIMIT)
       {
          log_printf("DNSBL -> Error sending dns lookup for '%s': %s", lookup, firedns_strerror(fdns_errno));
          free(ds);
       }
-      else
+      else {
          ss->scans++; /* Increase scan count - one for each blacklist */
+         if (bl->whitelist)
+            ss->dnsbl_whitelist_count++;  /* Increase whitelist count
+                                           * for each whitelist */
+      }                               
    }
 }
 
+/* This function gets called when:
+ * - a positive result was obtained from a blacklist
+ * - the last result from the whitelist has been received,
+ *   and a previous blacklist result was positive
+ */
+
 static void dnsbl_positive(struct scan_struct *ss, struct BlacklistConf *bl, 
 		unsigned char type)
 {
    char text_type[128];
    struct BlacklistReplyConf *item;
    node_t *p;
@@ -142,43 +152,64 @@
       }
    }
    
    if(text_type[0] == '\0' && bl->ban_unknown == 0)
    {
       if(OPT_DEBUG)
-         log_printf("DNSBL -> Unknown result from BL zone %s (%d)", bl->name, type);
+         log_printf("DNSBL -> Unknown result from %s zone %s (%d)",
+               (bl->whitelist ? "WL" : "BL"), bl->name, type);
       return;
    }
 
+   /* If this was a positive result from a whitelist, flag this user
+    * as whitelisted in the scan struct. This will prevent any future
+    * positive DNSBL blacklist result from klining.
+    */
+   if(bl->whitelist)
+      ss->dnsbl_whitelisted = 1; /* Mark this user as whitelisted */
+   else if(ss->dnsbl_whitelist_count > 0) /* Store data */
+   {
+      ss->dnsbl_positive_bl = bl;
+      ss->dnsbl_positive_type = type;
+      return;     /* Wait until whitelists have finished */
+   }
+
    if(ss->manual_target)
    {
-      irc_send("PRIVMSG %s :CHECK -> DNSBL -> %s appears in BL zone %s (%s)",
-            ss->manual_target->name, ss->ip, bl->name, text_type);
+      irc_send("PRIVMSG %s :CHECK -> DNSBL -> %s appears in %s zone %s (%s)",
+         ss->manual_target->name, ss->ip, (bl->whitelist ? "WL" : "BL"),
+         bl->name, text_type);
    }
    else if(!ss->positive)
    {
-      /* Only report it if no other scans have found positives yet. */
-      scan_positive(ss, (bl->kline[0] ? bl->kline : IRCItem->kline),
-            text_type);
-
-      irc_send_channels("DNSBL -> %s!%s@%s appears in BL zone %s (%s)",
-            ss->irc_nick, ss->irc_username, ss->irc_hostname, bl->name,
-            text_type);
-      log_printf("DNSBL -> %s!%s@%s appears in BL zone %s (%s)",
-            ss->irc_nick, ss->irc_username, ss->irc_hostname, bl->name,
-            text_type);
+      /* Only report it if no other scans have found positives yet,
+       * all whitelists are done, and the user has not been whitelisted. */
+      if(ss->dnsbl_whitelist_count == 0 && !ss->dnsbl_whitelisted)
+         scan_positive(ss, (bl->kline[0] ? bl->kline : IRCItem->kline), text_type);
+ 
+      if(bl->alert)  
+         irc_send_channels("DNSBL -> %s!%s@%s appears in %s zone %s (%s)",
+               ss->irc_nick, ss->irc_username, ss->irc_hostname,
+               (bl->whitelist ? "WL" : "BL"), bl->name, text_type);
+      
+      log_printf("DNSBL -> %s!%s@%s appears in %s zone %s (%s)",
+            ss->irc_nick, ss->irc_username, ss->irc_hostname,
+            (bl->whitelist ? "WL" : "BL"), bl->name, text_type);
    }
 
    /* record stat */
    stats_dnsblrecv(bl);
 }
 
 void dnsbl_result(struct firedns_result *res)
 {
 	struct dnsbl_scan *ds = res->info;
 
+   if(ds->bl->whitelist)
+      ds->ss->dnsbl_whitelist_count--; /* one less whitelist to wait for */
+    
    if(OPT_DEBUG)
       log_printf("DNSBL -> Lookup result for %s!%s@%s (%s) %d.%d.%d.%d (error: %d)",
           ds->ss->irc_nick,
           ds->ss->irc_username,
           ds->ss->irc_hostname,
           res->lookup,
@@ -187,15 +218,21 @@
           (unsigned char)res->text[2],
           (unsigned char)res->text[3], fdns_errno);
 
    /* Everything is OK */
    if(res->text[0] == '\0' && fdns_errno == FDNS_ERR_NXDOMAIN)
    {
+      /* If any previous positive blacklist result was blocked, waiting
+       * for whitelists, handle it now
+       */
+      if(ds->bl->whitelist && ds->ss->dnsbl_whitelist_count == 0 && ds->ss->dnsbl_positive_bl != NULL)
+         dnsbl_positive(ds->ss, ds->ss->dnsbl_positive_bl, ds->ss->dnsbl_positive_type);
+      
       if(ds->ss->manual_target != NULL)
-         irc_send("PRIVMSG %s :CHECK -> DNSBL -> %s does not appear in BL zone %s", 
-                   ds->ss->manual_target->name, ds->ss->ip,
+         irc_send("PRIVMSG %s :CHECK -> DNSBL -> %s does not appear in %s zone %s", 
+                   ds->ss->manual_target->name, ds->ss->ip, (ds->bl->whitelist ? "WL" : "BL"),
                     (strlen(ds->ss->ip) < strlen(res->lookup))
 						   ? (res->lookup + strlen(ds->ss->ip) + 1)
 							: res->lookup);
 
 
       ds->ss->scans--;            /* we are done with ss here */
@@ -207,12 +244,18 @@
    /* Either an error, or a positive lookup */
 
    if(fdns_errno == FDNS_ERR_NONE)
       dnsbl_positive(ds->ss, ds->bl, (unsigned char)res->text[3]);
    else
 	{
+      /* If any previous positive blacklist result was blocked, waiting
+       * for whitelists, handle it now
+       */
+      if(ds->bl->whitelist && ds->ss->dnsbl_whitelist_count == 0 && ds->ss->dnsbl_positive_bl != NULL)
+         dnsbl_positive(ds->ss, ds->ss->dnsbl_positive_bl, ds->ss->dnsbl_positive_type);
+       
       log_printf("DNSBL -> Lookup error on %s: %s", res->lookup,
 	      firedns_strerror(fdns_errno));
 		if(fdns_errno != FDNS_ERR_TIMEOUT)
 			irc_send_channels("DNSBL -> Lookup error on %s: %s", res->lookup,
 				firedns_strerror(fdns_errno));
 	}
Index: src/scan.c
===================================================================
RCS file: /data/cvs/bopm/src/scan.c,v
retrieving revision 1.33
diff -u -6 -r1.33 scan.c
--- src/scan.c	22 Jun 2003 17:05:30 -0000	1.33
+++ src/scan.c	22 Jun 2003 19:36:12 -0000
@@ -477,13 +477,18 @@
    ss->ip = (char *) DupString(user[3]);
    ss->proof = (char *) DupString(msg);
 
    ss->remote = opm_remote_create(ss->ip);
    ss->scans = 0;
    ss->positive = 0;
-
+   
+   ss->dnsbl_whitelist_count = 0;
+   ss->dnsbl_whitelisted = 0;
+   ss->dnsbl_positive_bl = NULL;
+   ss->dnsbl_positive_type = '\0';
+   
    ss->manual_target = NULL;
 
    assert(ss->remote);
    return ss;
 }
 
Index: src/scan.h
===================================================================
RCS file: /data/cvs/bopm/src/scan.h,v
retrieving revision 1.7
diff -u -6 -r1.7 scan.h
--- src/scan.h	20 Jun 2003 04:18:38 -0000	1.7
+++ src/scan.h	22 Jun 2003 19:36:12 -0000
@@ -12,13 +12,17 @@
    char *ip;
    char *proof;
    OPM_REMOTE_T *remote;
 
    unsigned short scans;
    unsigned short positive;
-
+   unsigned short dnsbl_whitelisted;
+   unsigned short dnsbl_whitelist_count;
+   unsigned char dnsbl_positive_type;
+   struct BlacklistConf *dnsbl_positive_bl;
+   
    struct ChannelConf *manual_target;
 };
 
 
 struct scanner_struct
 {
