Add DNS support for strlist filters (i.e. rhbl).
authorFlorent Bruneau <florent.bruneau@polytechnique.org>
Sun, 5 Oct 2008 15:26:38 +0000 (17:26 +0200)
committerFlorent Bruneau <florent.bruneau@polytechnique.org>
Sun, 5 Oct 2008 15:26:38 +0000 (17:26 +0200)
Signed-off-by: Florent Bruneau <florent.bruneau@polytechnique.org>
example/postlicyd.conf
postlicyd/strlist.c

index 12d8ff5..57c5966 100644 (file)
@@ -72,6 +72,9 @@
 #             score of the IP
 #           - rbldns: (no)?lock:weight:filename
 #             this is an alias for file.
+#           - dns: weight:hostname
+#             use a rbl via DNS resolution with the given weight. If a DNS lookup error occurs
+#             the IP is considered as beeing "not found".
 #           - soft_threshold: score (default: 1)
 #             minimum score to match the soft_match return value
 #           - hard_threshold: score (default: 1)
@@ -79,6 +82,7 @@
 #        Return value:
 #          The score of a query is the sum of the weight of the blacklist it matched.
 #           - If the IP can not be parsed, returns error
+#           - If no rbl was available (no file and all dns down), returns error.
 #           - If the score is strictly greater >= than hard_threshold, returns hard_match
 #           - If the score is strictly greater >= than soft_threshold, returns soft_match
 #           - Else, returns fail
@@ -125,6 +129,10 @@ spamhaus_and_abuseat {
 #               * names beginning with '*' are sorted as 'domains' and are matched as suffix
 #               * names starting with an alphanumirical character are sorted as 'hostnames' and are
 #                process via exact matching.
+#           - dns: weight:hostname
+#             use a rhbl via DNS resolution with the given weight. If a DNS lookup error occurs
+#             the hostname is considered as beeing "not found". This can only be used with "hostnames"
+#             typed fields.
 #           - soft_threshold: score (default: 1)
 #             minimum score to match the soft_match return value
 #           - hard_threshold: score (default: 1)
@@ -140,6 +148,7 @@ spamhaus_and_abuseat {
 #             No space is allowed in this parameter.
 #        Return value:
 #          The score of a query is the sum of the weight of the list it matched.
+#           - If no rhbl was available (no file and all dns down), returns error.
 #           - If the score is strictly greater >= than hard_threshold, returns hard_match
 #           - If the score is strictly greater >= than soft_threshold, returns soft_match
 #           - Else, returns fail
index c2704f1..2f17f90 100644 (file)
@@ -37,6 +37,7 @@
 #include "trie.h"
 #include "file.h"
 #include "str.h"
+#include "rbl.h"
 #include "policy_tokens.h"
 
 typedef struct strlist_config_t {
@@ -45,6 +46,10 @@ typedef struct strlist_config_t {
     A(bool)    reverses;
     A(bool)    partiales;
 
+    A(char)     hosts;
+    A(int)      host_offsets;
+    A(int)      host_weights;
+
     int soft_threshold;
     int hard_threshold;
 
@@ -72,6 +77,9 @@ static void strlist_config_delete(strlist_config_t **config)
         array_wipe((*config)->weights);
         array_wipe((*config)->reverses);
         array_wipe((*config)->partiales);
+        array_wipe((*config)->hosts);
+        array_wipe((*config)->host_offsets);
+        array_wipe((*config)->host_weights);
         p_delete(config);
     }
 }
@@ -387,6 +395,39 @@ static bool strlist_filter_constructor(filter_t *filter)
             }
           } break;
 
+          /* dns parameter.
+           *  weight:hostname.
+           * define a RBL to use through DNS resolution.
+           */
+          case ATK_DNS: {
+            int  weight = 0;
+            const char *current = param->value;
+            const char *p = m_strchrnul(param->value, ':');
+            char *next = NULL;
+            for (int i = 0 ; i < 2 ; ++i) {
+                PARSE_CHECK(i == 1 || *p,
+                            "host parameter must contains a weight option");
+                switch (i) {
+                  case 0:
+                    weight = strtol(current, &next, 10);
+                    PARSE_CHECK(next == p && weight >= 0 && weight <= 1024,
+                                "illegal weight value %.*s",
+                                (p - current), current);
+                    break;
+
+                  case 1:
+                    array_add(config->host_offsets, array_len(config->hosts));
+                    array_append(config->hosts, current, strlen(current) + 1);
+                    array_add(config->host_weights, weight);
+                    break;
+                }
+                if (i != 1) {
+                    current = p + 1;
+                    p = m_strchrnul(current, ':');
+                }
+            }
+          } break;
+
           /* hard_threshold parameter is an integer.
            *  If the matching score is greater or equal than this threshold,
            *  the hook "hard_match" is called.
@@ -465,6 +506,9 @@ static filter_result_t strlist_filter(const filter_t *filter, const query_t *que
     char normal[BUFSIZ];
     const strlist_config_t *config = filter->data;
     int sum = 0;
+    bool error = true;
+
+
     if (config->is_email && 
         ((config->match_sender && query->state < SMTP_MAIL)
         || (config->match_recipient && query->state != SMTP_RCPT))) {
@@ -492,19 +536,58 @@ static filter_result_t strlist_filter(const filter_t *filter, const query_t *que
                     return HTK_HARD_MATCH;                                     \
                 }                                                              \
             }                                                                  \
+            error = false;                                                     \
+        }                                                                      \
+    }
+#define DNS(Flag, Field)                                                       \
+    if (config->match_ ## Flag) {                                              \
+        const int len = m_strlen(query->Field);                                \
+        strlist_copy(normal, query->Field, len, false);                        \
+        for (uint32_t i = 0 ; len > 0 && i < config->tries.len ; ++i) {        \
+            const char *rbl    = array_ptr(config->hosts,                      \
+                                           array_elt(config->host_offsets, i));\
+            const int weight   = array_elt(config->host_weights, i);           \
+            switch (rhbl_check(normal, rbl)) {                                 \
+              case RBL_FOUND:                                                  \
+                error = false;                                                 \
+                sum += weight;                                                 \
+                if (sum >= config->hard_threshold) {                           \
+                    return HTK_HARD_MATCH;                                     \
+                }                                                              \
+                break;                                                         \
+              case RBL_NOTFOUND:                                               \
+                error = false;                                                 \
+                break;                                                         \
+              case RBL_ERROR:                                                  \
+                warn("rbl %s unavailable", rbl);                               \
+                break;                                                         \
+            }                                                                  \
         }                                                                      \
     }
+
     if (config->is_email) {
         LOOKUP(sender, sender);
         LOOKUP(recipient, recipient);
+        DNS(sender, sender);
+        DNS(recipient, recipient);
     } else if (config->is_hostname) {
         LOOKUP(helo, helo_name);
         LOOKUP(client, client_name);
         LOOKUP(reverse, reverse_client_name);
         LOOKUP(recipient, recipient_domain);
         LOOKUP(sender, sender_domain);
+        DNS(helo, helo_name);
+        DNS(client, client_name);
+        DNS(reverse, reverse_client_name);
+        DNS(recipient, recipient_domain);
+        DNS(sender, sender_domain);
     }
+#undef  DNS
 #undef  LOOKUP
+    if (error) {
+        err("filter %s: all the rbls returned an error", filter->name);
+        return HTK_ERROR;
+    }
     if (sum >= config->hard_threshold) {
         return HTK_HARD_MATCH;
     } else if (sum >= config->soft_threshold) {
@@ -530,6 +613,7 @@ static int strlist_init(void)
      */
     (void)filter_param_register(type, "file");
     (void)filter_param_register(type, "rbldns");
+    (void)filter_param_register(type, "dns");
     (void)filter_param_register(type, "hard_threshold");
     (void)filter_param_register(type, "soft_threshold");
     (void)filter_param_register(type, "fields");