diff src/context.cpp @ 414:d5a1ed33d3ae

spf code now handles mx,exists,ptr tags, multiple A records, %{i} macro
author Carl Byington <carl@five-ten-sg.com>
date Tue, 25 Apr 2017 14:48:19 -0700
parents e63c6b4835ef
children 16451edcb962
line wrap: on
line diff
--- a/src/context.cpp	Wed Apr 19 09:36:16 2017 -0700
+++ b/src/context.cpp	Tue Apr 25 14:48:19 2017 -0700
@@ -1138,7 +1138,27 @@
     dns_interface(*priv, from, ns_t_txt, false, NULL, buf, maxlen);
     if (*buf) {
         log(priv->queueid, "found txt record %s", buf);
-        char *p = strchr(buf, ' '); // must start with 'v=spf1 '
+        // expand some macros here - a very restricted subset of all possible spf macros
+        // only expand the first one.
+        char *p = strstr(buf, "%{i}");
+        if (p) {
+            char repl[maxlen];
+            char adr[sizeof "255.255.255.255   "];
+            adr[0] = '\0';
+            inet_ntop(AF_INET, (const u_char *)&priv->ip, adr, sizeof(adr));
+            size_t bn = strlen(buf);
+            size_t an = strlen(adr);
+            if ((bn - 4 + an) < maxlen) {
+                size_t n = p - buf;         // leading part length
+                strncpy(repl, buf, n);      // leading part
+                strcpy(repl+n, adr);        // replacement
+                strcpy(repl+n+an, buf+n+4); // trailing part
+                strcpy(buf, repl);
+            }
+            log(priv->queueid, "have txt record %s", buf);
+        }
+        //
+        p = strchr(buf, ' ');       // must start with 'v=spf1 '
         if (!p) return false;       // broken spf
         char *e = p + strlen(p);    // point to trailing null
         while (true) {
@@ -1146,7 +1166,7 @@
             if (p >= e) break;
             char *b = strchr(p, ' ');
             if (b) *b = '\0';
-            if ((*p != '-') && (*p != '~')) {
+            if ((*p != '-') && (*p != '~') && (*p != '?')) {
                 if (*p == '+') p++;
                 if (strncmp(p, "ip4:", 4) == 0) {
                     p += 4;
@@ -1161,27 +1181,72 @@
                             ipy &= low ^ 0xffffffff;
                             if ((ipy <= ip) && (ip <= ipy + low)) {
                                 if (s) *s = '/';
-                                log(priv->queueid, "match %s", p);
+                                log(priv->queueid, "match ip4:%s", p);
                                 return true;
                             }
                         }
                     }
                 }
-                else if (strncmp(p, "a:", 2) == 0) {
-                    p += 2;
-                    uint32_t ipy = ntohl(dns_interface(*priv, p, ns_t_a));
-                    if (ipy == ip) {
-                        log(priv->queueid, "match %s", p);
+                else if (strncmp(p, "all", 3) == 0) {
+                    // ignore it before looking for (a or a:) below
+                }
+                else if (strncmp(p, "exists:", 7) == 0) {
+                    p += 7;
+                    char buf[maxlen];
+                    uint32_t ipy = ntohl(dns_interface(*priv, p, ns_t_a, false, NULL, buf, maxlen));
+                    uint32_t *a = (uint32_t *)buf;
+                    if (a[0]) {
+                        log(priv->queueid, "match exists:%s", p);
                         return true;
                     }
                 }
-                else if (strcmp(p, "a") == 0) {
-                    uint32_t ipy = ntohl(dns_interface(*priv, from, ns_t_a));
+                else if (strncmp(p, "mx", 2) == 0) {
+                    const char *name = (p[2] == ':') ? p+2 : from;
+                    char buf[maxlen];
+                    uint32_t c = ntohl(dns_interface(*priv, name, ns_t_mx, false, NULL, buf, maxlen));
+                    char *b = buf;
+                    while (*b) {
+                        log(priv->queueid, "found mx %s", b);
+                        char abuf[maxlen];
+                        uint32_t ipy = ntohl(dns_interface(*priv, b, ns_t_a, false, NULL, buf, maxlen));
+                        uint32_t *a = (uint32_t *)buf;
+                        size_t c = a[0];
+                        for (size_t i=1; i++; i<=c) {
+                            ipy = ntohl(a[i]);
                     if (ipy == ip) {
-                        log(priv->queueid, "match %s", from);
+                                log(priv->queueid, "match mx:%s", name);
                         return true;
                     }
                 }
+                        b += strlen(b) + 1;
+                    }
+                }
+                else if (p[0] == 'a') {
+                    const char *name = (p[1] == ':') ? p+2 : from;
+                    char buf[maxlen];
+                    uint32_t ipy = ntohl(dns_interface(*priv, name, ns_t_a, false, NULL, buf, maxlen));
+                    uint32_t *a = (uint32_t *)buf;
+                    size_t c = a[0];
+                    for (size_t i=1; i++; i<=c) {
+                        ipy = ntohl(a[i]);
+                        if (ipy == ip) {
+                            log(priv->queueid, "match a:%s", name);
+                            return true;
+                        }
+                    }
+                }
+                else if (priv->client_dns_name && (!priv->client_dns_forged) && (strncmp(p, "ptr", 3) == 0)) {
+                    const char *name = (p[3] == ':') ? p+4 : from;
+                    size_t n = strlen(name);
+                    size_t d = strlen(priv->client_dns_name);
+                    if (d >= n) {
+                        if ((strncmp(priv->client_dns_name+d-n, name, n) == 0) &&       // trailing part matches
+                            ((d == n) || (priv->client_dns_name[d-n-1] == '.'))) {      // same length, or dot just before match
+                            log(priv->queueid, "match ptr:%s", priv->client_dns_name);
+                            return true;
+                        }
+                    }
+                }
                 else if ((level < 5) && (strncmp(p, "redirect=", 9) == 0)) {
                     p += 9;
                     if (resolve_spf(p, ip, priv, level+1)) return true;