changeset 255:d6d5c50b9278 stable-6-0-30

Allow dnswl_list and dnsbl_list to be empty, to override lists specified in the ancestor contexts. Add daily recipient limits as a multiple of the hourly limits.
author Carl Byington <carl@five-ten-sg.com>
date Mon, 09 Apr 2012 18:02:05 -0700
parents 720cdc2c303f
children 818cabace58f
files ChangeLog NEWS configure.in dnsbl.conf dnsbl.spec.in src/context.cpp src/context.h src/dnsbl.cpp xml/dnsbl.in
diffstat 9 files changed, 100 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Sun Apr 08 16:17:53 2012 -0700
+++ b/ChangeLog	Mon Apr 09 18:02:05 2012 -0700
@@ -1,3 +1,8 @@
+6.30 2012-04-09
+    Allow dnswl_list and dnsbl_list to be empty, to override lists
+    specified in the ancestor contexts.
+    Add daily recipient limits as a multiple of the hourly limits.
+
 6.29 2012-04-08
     Add dnswl support.
 
--- a/NEWS	Sun Apr 08 16:17:53 2012 -0700
+++ b/NEWS	Mon Apr 09 18:02:05 2012 -0700
@@ -1,3 +1,4 @@
+6.30 2012-04-09 Allow dnswl_list and dnsbl_list to be empty; add daily recipient limits.
 6.29 2012-04-08 Add dnswl support.
 6.28 2011-09-30 Add prvs decoding to envelope addresses.
 6.27 2011-08-15 const correctness fixes from new gcc
--- a/configure.in	Sun Apr 08 16:17:53 2012 -0700
+++ b/configure.in	Mon Apr 09 18:02:05 2012 -0700
@@ -1,6 +1,6 @@
 
 AC_PREREQ(2.59)
-AC_INIT(dnsbl,6.29,carl@five-ten-sg.com)
+AC_INIT(dnsbl,6.30,carl@five-ten-sg.com)
 AC_CONFIG_SRCDIR([config.h.in])
 AC_CONFIG_HEADER([config.h])
 
--- a/dnsbl.conf	Sun Apr 08 16:17:53 2012 -0700
+++ b/dnsbl.conf	Mon Apr 09 18:02:05 2012 -0700
@@ -29,7 +29,9 @@
     };
 
     // hourly recipient rate limit by smtp auth client id
-    rate_limit 30 { // default
+    // default hourly limit is 30
+    // daily limits are 4 times the hourly limit
+    rate_limit 30 4 { // default
         #fred 100;   // override default limits
         #joe  10;    // ""
     };
@@ -104,6 +106,8 @@
     };
 
     context blacklist {
+        dnsbl_list ;
+        dnswl_list ;
         env_to {
             # dcc_to many { include "/var/dcc/whitecommon"; };
         };
--- a/dnsbl.spec.in	Sun Apr 08 16:17:53 2012 -0700
+++ b/dnsbl.spec.in	Mon Apr 09 18:02:05 2012 -0700
@@ -103,7 +103,12 @@
 
 
 %changelog
-* Fri Apr 08 2012 Carl Byington <carl@five-ten-sg.com> - 6.29-1
+* Mon Apr 09 2012 Carl Byington <carl@five-ten-sg.com> - 6.30-1
+- Allow dnswl_list and dnsbl_list to be empty, to override lists
+  specified in the ancestor contexts.
+- Add daily recipient limits as a multiple of the hourly limits.
+
+* Sun Apr 08 2012 Carl Byington <carl@five-ten-sg.com> - 6.29-1
 - Add dnswl support.
 
 * Fri Sep 30 2011 Carl Byington <carl@five-ten-sg.com> - 6.28-1
--- a/src/context.cpp	Sun Apr 08 16:17:53 2012 -0700
+++ b/src/context.cpp	Mon Apr 09 18:02:05 2012 -0700
@@ -727,7 +727,10 @@
     require_match       = (parent) ? parent->require_match      : false;
     dcc_greylist        = (parent) ? parent->dcc_greylist       : false;
     dcc_bulk_threshold  = (parent) ? parent->dcc_bulk_threshold : 0;
-    default_rcpt_rate   = INT_MAX;
+    dnsbl_list_parsed   = false;
+    dnswl_list_parsed   = false;
+    default_rcpt_rate   = 36000;        // 10 per second
+    rcpt_daily_multiple = 3;
 }
 
 
@@ -991,13 +994,13 @@
 
 
 dnsblp_list& CONTEXT::get_dnsbl_list() {
-    if (dnsbl_list.empty() && parent) return parent->get_dnsbl_list();
+    if (!dnsbl_list_parsed && parent) return parent->get_dnsbl_list();
     return dnsbl_list;
 }
 
 
 dnswlp_list& CONTEXT::get_dnswl_list() {
-    if (dnswl_list.empty() && parent) return parent->get_dnswl_list();
+    if (!dnswl_list_parsed && parent) return parent->get_dnswl_list();
     return dnswl_list;
 }
 
@@ -1050,25 +1053,23 @@
 
     {
     dnsblp_list dl = get_dnsbl_list();
-    if (!dl.empty()) {
         printf("%s     dnsbl_list", indent);
         for (dnsblp_list::iterator i=dl.begin(); i!=dl.end(); i++) {
             DNSBL &d = *(*i);
             printf(" %s", d.name);
         }
         printf("; \n");
-    }}
+    }
 
     {
     dnswlp_list dl = get_dnswl_list();
-    if (!dl.empty()) {
         printf("%s     dnswl_list", indent);
         for (dnswlp_list::iterator i=dl.begin(); i!=dl.end(); i++) {
             DNSWL &d = *(*i);
             printf(" %s", d.name);
         }
         printf("; \n");
-    }}
+    }
 
     if (content_filtering) {
         printf("%s     content on { \n", indent);
@@ -1185,7 +1186,7 @@
     printf("%s     }; \n", indent);
 
     if (isdefault) {
-        printf("%s     rate_limit %d { \n", indent, default_rcpt_rate);
+        printf("%s     rate_limit %d %d { \n", indent, default_rcpt_rate, rcpt_daily_multiple);
         for (rcpt_rates::iterator j=rcpt_per_hour.begin(); j!=rcpt_per_hour.end(); j++) {
             const char *u = (*j).first;
             int         l = (*j).second;
@@ -1305,6 +1306,7 @@
             return false;
         }
     }
+    me.set_dnsbll_parsed();
     return true;
 }
 
@@ -1326,6 +1328,7 @@
             return false;
         }
     }
+    me.set_dnswll_parsed();
     return true;
 }
 
@@ -1705,6 +1708,11 @@
     const char *def = tok.next();
     tok.push(def);
     if (def != token_lbrace) me.set_default_rate(tok.nextint());
+
+    def = tok.next();
+    tok.push(def);
+    if (def != token_lbrace) me.set_daily_multiple(tok.nextint());
+
     if (!tsa(tok, token_lbrace)) return false;
     while (true) {
         const char *have = tok.next();
--- a/src/context.h	Sun Apr 08 16:17:53 2012 -0700
+++ b/src/context.h	Mon Apr 09 18:02:05 2012 -0700
@@ -175,9 +175,12 @@
     int             dcc_bulk_threshold; // off = 0, many = 1000
     dnsblp_map      dnsbl_names;        // name to dnsbl mapping for lists that are available in this context and children
     dnsblp_list     dnsbl_list;         // list of dnsbls to be used in this context
+    bool            dnsbl_list_parsed;  // true iff we have actually parsed a dnsbl_list
     dnswlp_map      dnswl_names;        // name to dnswl mapping for lists that are available in this context and children
     dnswlp_list     dnswl_list;         // list of dnswls to be used in this context
+    bool            dnswl_list_parsed;  // true iff we have actually parsed a dnswl_list
     int             default_rcpt_rate;  // if not specified per user
+    int             rcpt_daily_multiple;// daily multiplier applied to hourly rate
     rcpt_rates      rcpt_per_hour;      // per user limits on number of recipients per hour
 
 
@@ -203,8 +206,10 @@
     WHITELISTERP find_autowhite(const char *from, const char *to);
 
     void        set_default_rate(int limit)                 {default_rcpt_rate   = limit;};
+    void        set_daily_multiple(int multiple)            {rcpt_daily_multiple = multiple;};
     void        add_rate(const char *user, int limit)       {rcpt_per_hour[user] = limit;};
     int         find_rate(const char *user);
+    int         get_daily_multiple()                        {return rcpt_daily_multiple;};
 
     void        add_to(const char *to)                            {env_to.insert(to);};
     void        add_from(const char *from, const char *status)    {env_from[from] = status;};
@@ -234,10 +239,12 @@
     void        add_dnsbl(const char *name, DNSBLP dns)     {dnsbl_names[name] = dns;  };
     void        add_dnsbl(DNSBLP dns)                       {dnsbl_list.push_back(dns);};
     DNSBLP      find_dnsbl(const char *name);
+    void        set_dnsbll_parsed()                         {dnsbl_list_parsed = true;};
 
     void        add_dnswl(const char *name, DNSWLP dns)     {dnswl_names[name] = dns;  };
     void        add_dnswl(DNSWLP dns)                       {dnswl_list.push_back(dns);};
     DNSWLP      find_dnswl(const char *name);
+    void        set_dnswll_parsed()                         {dnswl_list_parsed = true;};
 
     bool        set_white(const char *regx);
     bool        white_match(const char *from);
--- a/src/dnsbl.cpp	Sun Apr 08 16:17:53 2012 -0700
+++ b/src/dnsbl.cpp	Mon Apr 09 18:02:05 2012 -0700
@@ -114,7 +114,8 @@
 time_t      last_error_time     = 0;
 int         resolver_sock_count = 0;        // protected with fd_pool_mutex
 int         resolver_pool_size  = 0;        // protected with fd_pool_mutex
-rcpt_rates  rcpt_counts;                    // protected with rate_mutex
+rcpt_rates  rcpt_hourly_counts;             // protected with rate_mutex
+rcpt_rates  rcpt_daily_counts;              // protected with rate_mutex
 
 
 struct ns_map {
@@ -163,20 +164,29 @@
 ////////////////////////////////////////////////
 // helper to manipulate recipient counts
 //
-int incr_rcpt_count(const char *user);
-int incr_rcpt_count(const char *user) {
+void incr_rcpt_count(const char *user, int &hourly, int &daily);
+void incr_rcpt_count(const char *user, int &hourly, int &daily) {
     pthread_mutex_lock(&rate_mutex);
-        rcpt_rates::iterator i = rcpt_counts.find(user);
-        int c = 1;
-        if (i == rcpt_counts.end()) {
+        rcpt_rates::iterator i = rcpt_hourly_counts.find(user);
+        hourly = 1;
+        if (i == rcpt_hourly_counts.end()) {
             user = strdup(user);
-            rcpt_counts[user] = c;
+            rcpt_hourly_counts[user] = hourly;
         }
         else {
-            c = ++((*i).second);
+            hourly = ++((*i).second);
+        }
+
+        rcpt_rates::iterator j = rcpt_daily_counts.find(user);
+        daily = 1;
+        if (j == rcpt_daily_counts.end()) {
+            user = strdup(user);
+            rcpt_daily_counts[user] = daily;
+        }
+        else {
+            daily = ++((*j).second);
         }
     pthread_mutex_unlock(&rate_mutex);
-    return c;
 }
 
 ////////////////////////////////////////////////
@@ -1171,14 +1181,16 @@
         return SMFIS_REJECT;
     }
     if (priv.authenticated) {
-        int c = incr_rcpt_count(priv.authenticated);
-        int l = dc.default_context->find_rate(priv.authenticated);
+        int hourly, daily;
+        incr_rcpt_count(priv.authenticated, hourly, daily);
+        int h_limit = dc.default_context->find_rate(priv.authenticated);
+        int d_limit = dc.default_context->get_daily_multiple() * h_limit;
         if (debug_syslog > 1) {
             char msg[maxlen];
-            snprintf(msg, sizeof(msg), "authenticated id %s (%d recipients, %d limit)", priv.authenticated, c, l);
+            snprintf(msg, sizeof(msg), "authenticated id %s (%d %d recipients, %d %d limits)", priv.authenticated, hourly, daily, h_limit, d_limit);
             my_syslog(&priv, msg);
         }
-        if (c > l) {
+        if ((hourly > h_limit) || (daily > d_limit)){
             smfi_setreply(ctx, (char*)"550", (char*)"5.7.1", (char*)"recipient rate limit exceeded");
             return SMFIS_REJECT;
         }
@@ -1525,20 +1537,32 @@
 //
 extern "C" {void* config_loader(void *arg);}
 void* config_loader(void *arg) {
-    int loop = 0;
+    int loop1 = 0;
+    int loop2 = 0;
     while (loader_run) {
         sleep(180);  // look for modifications every 3 minutes
         if (!loader_run) break;
-        loop++;
-        if (loop == 20) {
+        loop1++;
+        loop2++;
+        if (loop1 == 20) {
             // three minutes thru each loop, 20 loops per hour
-            // clear the recipient counts
+            // clear the recipient hourly counts
             pthread_mutex_lock(&rate_mutex);
-                for (rcpt_rates::iterator i=rcpt_counts.begin(); i!=rcpt_counts.end(); i++) {
+                for (rcpt_rates::iterator i=rcpt_hourly_counts.begin(); i!=rcpt_hourly_counts.end(); i++) {
                     (*i).second = 0;
                 }
             pthread_mutex_unlock(&rate_mutex);
-            loop = 0;
+            loop1 = 0;
+        }
+        if (loop2 == 480) {
+            // three minutes thru each loop, 480 loops per day
+            // clear the recipient daily counts
+            pthread_mutex_lock(&rate_mutex);
+                for (rcpt_rates::iterator i=rcpt_daily_counts.begin(); i!=rcpt_daily_counts.end(); i++) {
+                    (*i).second = 0;
+                }
+            pthread_mutex_unlock(&rate_mutex);
+            loop2 = 0;
         }
         CONFIG &dc = *config;
         time_t then = dc.load_time;
--- a/xml/dnsbl.in	Sun Apr 08 16:17:53 2012 -0700
+++ b/xml/dnsbl.in	Mon Apr 09 18:02:05 2012 -0700
@@ -661,10 +661,11 @@
              | RATE-LIMIT) ";"
 
 DNSBL      = "dnsbl" NAME DNSPREFIX ERROR-MSG1
-DNSBLLIST  = "dnsbl_list" {NAME}+
+DNSBLLIST  = "dnsbl_list" {NAME}*
 
-DNSWL      = "dnswl" NAME DNSPREFIX INTEGER
-DNSWLLIST  = "dnswl_list" {NAME}+
+DNSWL      = "dnswl" NAME DNSPREFIX LEVEL
+DNSWLLIST  = "dnswl_list" {NAME}*
+LEVEL      = INTEGER
 
 CONTENT    = "content" ("on" | "off") "{" {CONTENT-ST}+ "}"
 CONTENT-ST = (FILTER | URIBL | IGNORE | TLD     | CCTLD   | HTML-TAGS |
@@ -708,8 +709,11 @@
 FROM-ADDR  = ADDRESS VALUE [";"]
 DCC-FROM   = "dcc_from" "{" DCCINCLUDEFILE "}" ";"
 
-RATE-LIMIT = "rate_limit" [DEFAULTLIMIT] "{" (RATE)+ "}"
+RATE-LIMIT     = "rate_limit" [DEFAULT_LIMIT [DAILY_MULTIPLE]] "{" (RATE)+ "}"
 RATE       = USER LIMIT [";"]
+LIMIT          = INTEGER
+DEFAULT_LIMIT  = INTEGER
+DAILY_MULTIPLE = INTEGER
 
 DEFAULT    = ("white" | "black" | "unknown" | "inherit" | "")
 ADDRESS    = (USER@ | DOMAIN | USER@DOMAIN)
@@ -750,7 +754,9 @@
     };
 
     // hourly recipient rate limit by smtp auth client id
-    rate_limit 30 { // default
+    // default hourly limit is 30
+    // daily limits are 4 times the hourly limit
+    rate_limit 30 4 { // default
         #fred 100;   // override default limits
         #joe  10;    // ""
     };
@@ -825,6 +831,8 @@
     };
 
     context blacklist {
+        dnsbl_list ;
+        dnswl_list ;
         env_to {
             # dcc_to many { include "/var/dcc/whitecommon"; };
         };