diff src/dnsbl.cpp @ 381:879a470c6ac3

fetch spf txt records for required dkim signers
author Carl Byington <carl@five-ten-sg.com>
date Tue, 28 Feb 2017 17:02:07 -0800
parents e42923f8f3fd
children c378e9d03f37
line wrap: on
line diff
--- a/src/dnsbl.cpp	Mon Feb 20 08:43:41 2017 -0800
+++ b/src/dnsbl.cpp	Tue Feb 28 17:02:07 2017 -0800
@@ -302,13 +302,15 @@
 
 
 ////////////////////////////////////////////////
-//  ask a dns question and get an A record answer in network byte order
-//  we don't try very hard, just using the default resolver retry settings.
+//  Ask a dns question and get an A record answer in network byte order.
+//  We don't try very hard, just using the default resolver retry settings.
 //  If we cannot get an answer, we just accept the mail.
+//  If the qtype is ns_t_txt, the answer is placed in txt_answer which
+//  must be non-null, and the return value can be ignored.
+//  If the qtype is ns_t_a, the ip address is returned in network byte order.
 //
-//
-uint32_t dns_interface(mlfiPriv &priv, const char *question, bool maybe_ip, ns_map *nameservers);
-uint32_t dns_interface(mlfiPriv &priv, const char *question, bool maybe_ip, ns_map *nameservers) {
+uint32_t dns_interface(mlfiPriv &priv, const char *question, int qtype, bool maybe_ip = false, ns_map *nameservers = NULL, char *txt_answer = NULL, size_t txt_size = 0);
+uint32_t dns_interface(mlfiPriv &priv, const char *question, int qtype, bool maybe_ip, ns_map *nameservers, char *txt_answer, size_t txt_size) {
     // tell sendmail we are still working
     #if _FFR_SMFI_PROGRESS
         if (priv.eom) smfi_progress(priv.ctx);
@@ -318,13 +320,15 @@
     // milter thread is talking over its own socket to a separate resolver
     // process, which does the actual dns resolution.
     if (priv.err) return 0; // cannot ask more questions on this socket.
-    if (maybe_ip) {
+    if (maybe_ip && (qtype == ns_t_a)) {
         // might be a bare ip address, try this first to avoid dns lookups that may not be needed
         in_addr ip;
         if (inet_aton(question, &ip)) {
             return ip.s_addr;
         }
     }
+    int8_t qt = qtype;
+    priv.my_write((const char *)&qt, 1);// write the query type
     size_t n = strlen(question);
     if (question[n-1] == '.') {
         priv.my_write(question, n+1);   // write the question including the null terminator
@@ -405,14 +409,39 @@
                 }
             }
             int rrnum = 0;
+            if (qtype == ns_t_a) {
             while (ns_parserr(&handle, ns_s_an, rrnum++, &rr) == 0) {
-                if (ns_rr_type(rr) == ns_t_a) {
+                    if (ns_rr_type(rr) == qtype) {
                     uint32_t address;
                     memcpy(&address, ns_rr_rdata(rr), sizeof(address));
                     ret_address = address;
                 }
             }
         }
+            if ((qtype == ns_t_txt) && (txt_answer) && (txt_size > 5)) {
+                txt_size--; // allow room for terminating null;
+                txt_answer[0] = '\0';   // return null string if there are no answers
+                while (ns_parserr(&handle, ns_s_an, rrnum++, &rr) == 0) {
+                    size_t offset = 0;
+                    if (ns_rr_type(rr) == qtype) {
+                        size_t rdlen = ns_rr_rdlen(rr);
+                        const unsigned char *rdata = ns_rr_rdata(rr);
+                        while ((offset < txt_size) && rdlen) {
+                            size_t slen = size_t(*(rdata++));
+                            rdlen--;
+                            size_t m = min(slen, rdlen);
+                            m = min(m, txt_size-offset);
+                            memcpy(txt_answer+offset, rdata, m);
+                            offset += m;
+                            rdata  += m;
+                            rdlen  -= m;
+                        }
+                    }
+                    txt_answer[offset] = '\0';  // trailing null
+                    if (strcasecmp(txt_answer, "v=spf1 ") == 0) break;
+                }
+            }
+        }
     pthread_mutex_unlock(&resolve_mutex);
     #ifdef RESOLVER_DEBUG
         snprintf(text, sizeof(text), "dns_interface() found ip %d", ret_address);
@@ -436,7 +465,7 @@
 bool uriblookup(mlfiPriv &priv, string_set &hosts, const char *hostname, const char *&found) {
     char buf[maxlen];
     snprintf(buf, sizeof(buf), "%s.%s.", hostname, priv.uribl_suffix);
-    uint32_t ip = ntohl(dns_interface(priv, buf, false, NULL));
+    uint32_t ip = ntohl(dns_interface(priv, buf, ns_t_a));
     if (ip and (ip != 0x7f000000)) {
         if (debug_syslog > 2) {
             char tmp[maxlen];
@@ -721,7 +750,6 @@
 }
 
 const char *mlfiPriv::check_uribl_signers() {
-    const char *st;
     if (uribl_suffix) {
         for (string_set::iterator s=dkim_signers.begin(); s!=dkim_signers.end(); s++) {
             if (check_uribl(*this, hosts_uribl, *s, host_uribl)) return host_uribl;
@@ -853,16 +881,16 @@
 #ifdef NS_PACKETSZ
         #ifdef RESOLVER_DEBUG
             char text[1000];
-            snprintf(text, sizeof(text), "process_resolver_requests() has a question %s", question);
+            snprintf(text, sizeof(text), "process_resolver_requests() has a question %s qtype %d", question+1, int8_t(question[0]));
             my_syslog(text);
         #endif
-        int res_result = res_search(question, ns_c_in, ns_t_a, glom.answer, sizeof(glom.answer));
+        int res_result = res_search(question+1, ns_c_in, int8_t(question[0]), glom.answer, sizeof(glom.answer));
         if (res_result < 0) glom.length = 0;   // represent all errors as zero length answers
         else                glom.length = (size_t)res_result;
 #else
         glom.length = sizeof(glom.answer);
         glom.answer = 0;
-        struct hostent *host = gethostbyname(question);
+        struct hostent *host = gethostbyname(question+1);
         if (host && (host->h_addrtype == AF_INET)) {
             memcpy(&glom.answer, host->h_addr, sizeof(glom.answer));
         }
@@ -913,7 +941,7 @@
 #endif
     snprintf(question, sizeof(question), "%u.%u.%u.%u.%s.", src[3], src[2], src[1], src[0], suffix);
     // ask the question, if we get an A record it implies a blacklisted ip address
-    return dns_interface(priv, question, false, NULL);
+    return dns_interface(priv, question, ns_t_a);
 }
 
 
@@ -1028,7 +1056,7 @@
             }
         }
         count++;
-        ip = dns_interface(priv, host, true, &nameservers);
+        ip = dns_interface(priv, host, ns_t_a, true, &nameservers);
         if (debug_syslog > 2) {
             char buf[maxlen];
             if (ip) {
@@ -1066,7 +1094,7 @@
         if ((count > limit) && (limit > 0)) return false;   // too many name servers to check them all
         host = (*i).first;  // a transient reference that needs to be replaced before we return it
         ip   = (*i).second;
-        if (!ip) ip = dns_interface(priv, host, false, NULL);
+        if (!ip) ip = dns_interface(priv, host, ns_t_a);
         if (debug_syslog > 2) {
             char buf[maxlen];
             if (ip) {
@@ -1655,7 +1683,7 @@
         for (context_map::iterator i=priv.env_to.begin(); i!=priv.env_to.end(); i++) {
             const char *rcpt   = (*i).first;
             CONTEXT &con = *((*i).second);
-            const char *st = con.acceptable_content(*priv.memory, score, bulk, priv.queueid, priv.dkim_signers, priv.fromaddr, msg);
+            const char *st = con.acceptable_content(*priv.memory, score, bulk, priv.queueid, priv.dkim_signers, priv.fromaddr, &priv, msg);
             if (st == token_black) {
                 // bad html tags or excessive hosts or
                 // high spam assassin score or dcc bulk threshold exceedeed