diff src/context.cpp @ 382:c378e9d03f37

start parsing spf txt records
author Carl Byington <carl@five-ten-sg.com>
date Mon, 06 Mar 2017 12:21:05 -0800
parents 879a470c6ac3
children 7b7066a51c33
line wrap: on
line diff
--- a/src/context.cpp	Tue Feb 28 17:02:07 2017 -0800
+++ b/src/context.cpp	Mon Mar 06 12:21:05 2017 -0800
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include <arpa/inet.h>
+#include <arpa/nameser.h>
 #include <net/if.h>
 #include <netdb.h>
 #include <netinet/in.h>
@@ -1122,17 +1123,52 @@
 }
 
 
-#ifdef NS_PACKETSZ
-bool CONTEXT::resolve_spf(const char *from, int32_t ip, mlfiPriv *priv)
+bool CONTEXT::resolve_spf(const char *from, uint32_t ip, mlfiPriv *priv, int level)
 {
     char buf[maxlen];
     dns_interface(*priv, from, ns_t_txt, false, NULL, buf, maxlen);
     if (*buf) {
-        log(priv->queueid, "found txt record %s", buf);
+        log(priv->queueid, "found txt record for %s", from);
+        log(priv->queueid, "found txt record value %s", buf);
+        char *p = buf;
+        char *e = p + strlen(p);    // point to trailing null
+        while (p = strstr(p, " ip4:")) {
+            p += 5;
+            char *b = strchr(p, ' ');
+            if (b) *b = '\0';
+            char *s = strchr(p, '/');
+            if (s) *s = '\0';
+            in_addr ipx;
+            if (inet_aton(p, &ipx)) {
+                if (s) {
+                    int mask = atoi(s+1);
+                    if ((mask >= 16) && (mask <= 32)) {
+                        int low = (1 << (32-mask)) - 1;
+                        ipx.s_addr &= low ^ 0xffffffff;
+                        if ((ip >= ipx.s_addr) && (ip <= ipx.s_addr + low)) {
+                            log(priv->queueid, "match %s", p);
+                            if (s) log(priv->queueid, "match /%s", s+1);
+                            return true;
+                        }
+                    }
+                }
+            }
+            if (b) *b = ' ';
+            if (s) *s = '/';
+            p = (b) ? b+1 : e;
+        }
+        p = buf;
+        while ((level < 5) && (p = strstr(p, " include:"))) {
+            p += 9;
+            char *b = strchr(p, ' ');
+            if (b) *b = '\0';
+            if (resolve_spf(p, ip, priv, level+1)) return true;
+            if (b) *b = ' ';
+            p = (b) ? b+1 : e;
+        }
     }
     return false;
 }
-#endif
 
 
 const char *CONTEXT::acceptable_content(recorder &memory, int score, int bulk, const char *queueid, string_set &signers, const char *from, mlfiPriv *priv, string& msg) {
@@ -1149,6 +1185,12 @@
 
     if (dk) {
         const char *st = dk->action;
+        if ((st == token_require_signed) &&
+            dk->signer &&
+            strcmp(dk->signer, " ") &&
+            resolve_spf(from, priv->ip, priv)) {
+                log(queueid, "spf pass for %s with required dkim signer", from);
+        }
         for (string_set::iterator s=signers.begin(); s!=signers.end(); s++) {
             // signed by a white listed signer
             if ((st == token_signed_white)   && in_signing_set(*s,dk->signer)) {
@@ -1169,13 +1211,12 @@
             }
         }
         if (st == token_require_signed) {
-#ifdef NS_PACKETSZ
-            // not signed by the required signers, but maybe passes strong spf check
-            if (resolve_spf(from, priv->ip, priv) {
+            // not signed by a required signer, but maybe passes strong spf check
+            // only check spf if the list of required signers is not a single blank.
+            if (dk->signer && strcmp(dk->signer, " ") && resolve_spf(from, priv->ip, priv)) {
                 log(queueid, "spf pass for %s rather than required dkim signer", from);
                 return token_white;
             }
-#endif
             char buf[maxlen];
             snprintf(buf, sizeof(buf), "Mail rejected - not dkim signed by %s", dk->signer);
             msg = string(buf);