diff src/dnsbl.cpp @ 278:368572c57013

add limits on unique ip addresses per hour per authenticated user
author Carl Byington <carl@five-ten-sg.com>
date Tue, 17 Dec 2013 15:35:23 -0800
parents a99b6c1f5f67
children 2b77295fb9a7
line wrap: on
line diff
--- a/src/dnsbl.cpp	Wed Dec 11 22:57:06 2013 -0800
+++ b/src/dnsbl.cpp	Tue Dec 17 15:35:23 2013 -0800
@@ -114,8 +114,10 @@
 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_hourly_counts;             // protected with rate_mutex
-rcpt_rates  rcpt_daily_counts;              // protected with rate_mutex
+rates       rcpt_hourly_counts;             // protected with rate_mutex
+rates       rcpt_daily_counts;              // protected with rate_mutex
+auth_addresses auth_hourly_addresses;       // protected with rate_mutex
+auth_addresses auth_daily_addresses;        // protected with rate_mutex
 
 
 struct ns_map {
@@ -167,7 +169,7 @@
 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_hourly_counts.find(user);
+        rates::iterator i = rcpt_hourly_counts.find(user);
         hourly = 1;
         if (i == rcpt_hourly_counts.end()) {
             user = strdup(user);
@@ -177,7 +179,7 @@
             hourly = ++((*i).second);
         }
 
-        rcpt_rates::iterator j = rcpt_daily_counts.find(user);
+        rates::iterator j = rcpt_daily_counts.find(user);
         daily = 1;
         if (j == rcpt_daily_counts.end()) {
             user = strdup(user);
@@ -189,6 +191,36 @@
     pthread_mutex_unlock(&rate_mutex);
 }
 
+
+void add_auth_address(const char *user, int &hourly, int &daily, int32_t ip);
+void add_auth_address(const char *user, int &hourly, int &daily, int32_t ip) {
+    pthread_mutex_lock(&rate_mutex);
+        auth_addresses::iterator ii = auth_hourly_addresses.find(user);
+        if (ii == auth_hourly_addresses.end()) {
+            auth_hourly_addresses[user] = new int32_t_set;
+            auth_hourly_addresses[user]->insert(ip);
+            hourly = 1;
+        }
+        else {
+            int32_t_set::iterator i = ((*ii).second)->find(ip);
+            if (i == ((*ii).second)->end()) ((*ii).second)->insert(ip);
+            hourly = ((*ii).second)->size();
+        }
+
+        auth_addresses::iterator jj = auth_daily_addresses.find(user);
+        if (jj == auth_daily_addresses.end()) {
+            auth_daily_addresses[user] = new int32_t_set;
+            auth_daily_addresses[user]->insert(ip);
+            daily = 1;
+        }
+        else {
+            int32_t_set::iterator i = ((*jj).second)->find(ip);
+            if (i == ((*jj).second)->end()) ((*jj).second)->insert(ip);
+            daily = ((*jj).second)->size();
+        }
+    pthread_mutex_unlock(&rate_mutex);
+}
+
 ////////////////////////////////////////////////
 // helper to discard the strings held by a context_map
 //
@@ -1135,12 +1167,31 @@
 sfsistat mlfi_envfrom(SMFICTX *ctx, char **from)
 {
     mlfiPriv &priv     = *MLFIPRIV;
+    CONFIG &dc         = *priv.pc;
     priv.mailaddr      = to_lower_string(from[0]);
     priv.queueid       = strdup(smfi_getsymval(ctx, (char*)"i"));
     priv.authenticated = smfi_getsymval(ctx, (char*)"{auth_authen}");
     priv.client_name   = smfi_getsymval(ctx, (char*)"_");
     if (!priv.helo)         priv.helo          = strdup("unknown");
     if (priv.authenticated) priv.authenticated = strdup(priv.authenticated);
+    {
+        const char *uid = (priv.authenticated) ? priv.authenticated : priv.mailaddr;
+        if (priv.authenticated || dc.default_context->is_unauthenticated_limited(priv.mailaddr)) {
+            int hourly, daily;
+            add_auth_address(uid, hourly, daily, priv.ip);
+            int h_limit = dc.default_context->find_address_limit(uid);
+            int d_limit = dc.default_context->get_daily_address_multiple() * h_limit;
+            if (debug_syslog > 1) {
+                char msg[maxlen];
+                snprintf(msg, sizeof(msg), "connect for %s (%d %d addresses, %d %d limits)", uid, hourly, daily, h_limit, d_limit);
+                my_syslog(&priv, msg);
+            }
+            if ((hourly > h_limit) || (daily > d_limit)){
+                //smfi_setreply(ctx, (char*)"550", (char*)"5.7.1", (char*)"unique connection address limit exceeded");
+                //return SMFIS_REJECT;
+            }
+        }
+    }
     if (priv.client_name) {
         priv.client_name = strdup(priv.client_name);
         const char *p = strstr(priv.client_name, " [");
@@ -1211,8 +1262,8 @@
     if (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;
+        int h_limit = dc.default_context->find_rate_limit(priv.authenticated);
+        int d_limit = dc.default_context->get_daily_rate_multiple() * h_limit;
         if (debug_syslog > 1) {
             char msg[maxlen];
             snprintf(msg, sizeof(msg), "authenticated id %s (%d %d recipients, %d %d limits)", priv.authenticated, hourly, daily, h_limit, d_limit);
@@ -1295,8 +1346,8 @@
     if (!priv.authenticated && dc.default_context->is_unauthenticated_limited(priv.mailaddr)) {
         int hourly, daily;
         incr_rcpt_count(priv.mailaddr, hourly, daily);
-        int h_limit = dc.default_context->find_rate(priv.mailaddr);
-        int d_limit = dc.default_context->get_daily_multiple() * h_limit;
+        int h_limit = dc.default_context->find_rate_limit(priv.mailaddr);
+        int d_limit = dc.default_context->get_daily_rate_multiple() * h_limit;
         if (debug_syslog > 1) {
             char msg[maxlen];
             snprintf(msg, sizeof(msg), "unauthenticated address %s (%d %d recipients, %d %d limits)", priv.mailaddr, hourly, daily, h_limit, d_limit);
@@ -1600,9 +1651,12 @@
             // three minutes thru each loop, 20 loops per hour
             // clear the recipient hourly counts
             pthread_mutex_lock(&rate_mutex);
-                for (rcpt_rates::iterator i=rcpt_hourly_counts.begin(); i!=rcpt_hourly_counts.end(); i++) {
+                for (rates::iterator i=rcpt_hourly_counts.begin(); i!=rcpt_hourly_counts.end(); i++) {
                     (*i).second = 0;
                 }
+                for (auth_addresses::iterator j=auth_hourly_addresses.begin(); j!=auth_hourly_addresses.end(); j++) {
+                    delete (*j).second;
+                }
             pthread_mutex_unlock(&rate_mutex);
             loop1 = 0;
         }
@@ -1610,9 +1664,12 @@
             // 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++) {
+                for (rates::iterator i=rcpt_daily_counts.begin(); i!=rcpt_daily_counts.end(); i++) {
                     (*i).second = 0;
                 }
+                for (auth_addresses::iterator j=auth_daily_addresses.begin(); j!=auth_daily_addresses.end(); j++) {
+                    delete (*j).second;
+                }
             pthread_mutex_unlock(&rate_mutex);
             loop2 = 0;
         }