Simplify.
[apps/pfixtools.git] / greylist.c
index 9f4895a..e556883 100644 (file)
 
 #include <tcbdb.h>
 
+#include "common.h"
 #include "greylist.h"
 #include "str.h"
 
-static struct {
-    int do_awl;
-    int awl_count;
-    bool lookup_by_host;
-
-    int delay;
-    int retry_window;
-
-    TCBDB *awl_db, *obj_db;
-} cfg;
+struct greylist_cfg greylist_cfg = {
+   .lookup_by_host = false,
+   .delay          = 300,
+   .retry_window   = 2 * 24 * 2600,
+   .client_awl     = 5,
+};
+static TCBDB *awl_db, *obj_db;
 
 struct awl_entry {
     int32_t count;
@@ -59,6 +57,50 @@ struct obj_entry {
     time_t last;
 };
 
+int greylist_initialize(const char *directory, const char *prefix)
+{
+    char path[PATH_MAX];
+
+    if (greylist_cfg.client_awl) {
+        snprintf(path, sizeof(path), "%s/%swhitelist.db", directory, prefix);
+        awl_db = tcbdbnew();
+        if (!tcbdbopen(awl_db, path, BDBOWRITER | BDBOCREAT)) {
+            tcbdbdel(awl_db);
+            awl_db = NULL;
+        }
+        return -1;
+    }
+
+    snprintf(path, sizeof(path), "%s/%sgreylist.db", directory, prefix);
+    obj_db = tcbdbnew();
+    if (!tcbdbopen(obj_db, path, BDBOWRITER | BDBOCREAT)) {
+        tcbdbdel(obj_db);
+        obj_db = NULL;
+        if (awl_db) {
+            tcbdbdel(awl_db);
+            awl_db = NULL;
+        }
+        return -1;
+    }
+
+    return 0;
+}
+
+static void greylist_shutdown(void)
+{
+    if (awl_db) {
+        tcbdbsync(awl_db);
+        tcbdbdel(awl_db);
+        awl_db = NULL;
+    }
+    if (obj_db) {
+        tcbdbsync(obj_db);
+        tcbdbdel(obj_db);
+        obj_db = NULL;
+    }
+}
+module_exit(greylist_shutdown);
+
 const char *sender_normalize(const char *sender, char *buf, int len)
 {
     const char *at = strchr(sender, '@');
@@ -100,7 +142,7 @@ c_net(const char *c_addr, const char *c_name, char *cnet, int cnetlen)
     char ip2[4], ip3[4];
     const char *dot, *p;
 
-    if (cfg.lookup_by_host)
+    if (greylist_cfg.lookup_by_host)
         return c_addr;
 
     if (!(dot = strchr(c_addr, '.')))
@@ -130,6 +172,11 @@ c_net(const char *c_addr, const char *c_name, char *cnet, int cnetlen)
 bool try_greylist(const char *sender, const char *c_addr,
                   const char *c_name, const char *rcpt)
 {
+#define INCR_AWL                                              \
+    aent.count++;                                             \
+    aent.last = now;                                          \
+    tcbdbput(awl_db, c_addr, c_addrlen, &aent, sizeof(aent));
+
     char sbuf[BUFSIZ], cnet[64], key[BUFSIZ];
     const void *res;
 
@@ -139,44 +186,70 @@ bool try_greylist(const char *sender, const char *c_addr,
 
     int len, klen, c_addrlen = strlen(c_addr);
 
-
-    if (cfg.do_awl) {
-        res = tcbdbget3(cfg.awl_db, c_addr, c_addrlen, &len);
+    /* Auto whitelist clients.
+     */
+    if (greylist_cfg.client_awl) {
+        res = tcbdbget3(awl_db, c_addr, c_addrlen, &len);
         if (res && len == sizeof(aent)) {
             memcpy(&aent, res, len);
         }
-        if (aent.count > cfg.awl_count) {
-            if (now < aent.last + 3600)
-                goto incr_aent;
+
+        /* Whitelist if count is enough.
+         */
+        if (aent.count > greylist_cfg.client_awl) {
+            if (now < aent.last + 3600) {
+                INCR_AWL
+            }
+
+            /* OK.
+             */
             return true;
         }
     }
 
+    /* Lookup.
+     */
     klen = snprintf(key, sizeof(key), "%s/%s/%s",
                     c_net(c_addr, c_name, cnet, sizeof(cnet)),
                     sender_normalize(sender, sbuf, sizeof(sbuf)), rcpt);
     klen = MIN(klen, ssizeof(key) - 1);
 
-    res = tcbdbget3(cfg.obj_db, key, klen, &len);
+    res = tcbdbget3(obj_db, key, klen, &len);
     if (res && len == sizeof(oent)) {
         memcpy(&oent, res, len);
     }
 
-    if (oent.last - oent.first < cfg.delay
-    &&  now - oent.first > cfg.retry_window)
-    {
+    /* Discard stored first-seen if it is the first retrial and
+     * it is beyong the retry window.
+     */
+    if (oent.last - oent.first < greylist_cfg.delay
+        &&  now - oent.first > greylist_cfg.retry_window) {
         oent.first = now;
     }
+
+    /* Update.
+     */
     oent.last = now;
-    tcbdbput(cfg.obj_db, key, klen, &oent, sizeof(oent));
-    if (oent.first + cfg.delay < now) {
-        if (cfg.do_awl) {
-          incr_aent:
-            aent.count++;
-            aent.last = now;
-            tcbdbput(cfg.awl_db, c_addr, c_addrlen, &aent, sizeof(aent));
+    tcbdbput(obj_db, key, klen, &oent, sizeof(oent));
+
+    /* Auto whitelist clients:
+     *  algorithm:
+     *    - on successful entry in the greylist db of a triplet:
+     *        - client not whitelisted yet ? -> increase count
+     *                                       -> withelist if count > limit
+     *        - client whitelisted already ? -> update last-seen timestamp.
+     */
+    if (oent.first + greylist_cfg.delay < now) {
+        if (greylist_cfg.client_awl) {
+            INCR_AWL
         }
+
+        /* OK
+         */
         return true;
     }
+
+    /* DUNNO
+     */
     return false;
 }