comparison 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
comparison
equal deleted inserted replaced
380:0495e767bfb7 381:879a470c6ac3
300 return sock; 300 return sock;
301 } 301 }
302 302
303 303
304 //////////////////////////////////////////////// 304 ////////////////////////////////////////////////
305 // ask a dns question and get an A record answer in network byte order 305 // Ask a dns question and get an A record answer in network byte order.
306 // we don't try very hard, just using the default resolver retry settings. 306 // We don't try very hard, just using the default resolver retry settings.
307 // If we cannot get an answer, we just accept the mail. 307 // If we cannot get an answer, we just accept the mail.
308 // If the qtype is ns_t_txt, the answer is placed in txt_answer which
309 // must be non-null, and the return value can be ignored.
310 // If the qtype is ns_t_a, the ip address is returned in network byte order.
308 // 311 //
309 // 312 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);
310 uint32_t dns_interface(mlfiPriv &priv, const char *question, bool maybe_ip, ns_map *nameservers); 313 uint32_t dns_interface(mlfiPriv &priv, const char *question, int qtype, bool maybe_ip, ns_map *nameservers, char *txt_answer, size_t txt_size) {
311 uint32_t dns_interface(mlfiPriv &priv, const char *question, bool maybe_ip, ns_map *nameservers) {
312 // tell sendmail we are still working 314 // tell sendmail we are still working
313 #if _FFR_SMFI_PROGRESS 315 #if _FFR_SMFI_PROGRESS
314 if (priv.eom) smfi_progress(priv.ctx); 316 if (priv.eom) smfi_progress(priv.ctx);
315 #endif 317 #endif
316 318
317 // this part can be done without locking the resolver mutex. Each 319 // this part can be done without locking the resolver mutex. Each
318 // milter thread is talking over its own socket to a separate resolver 320 // milter thread is talking over its own socket to a separate resolver
319 // process, which does the actual dns resolution. 321 // process, which does the actual dns resolution.
320 if (priv.err) return 0; // cannot ask more questions on this socket. 322 if (priv.err) return 0; // cannot ask more questions on this socket.
321 if (maybe_ip) { 323 if (maybe_ip && (qtype == ns_t_a)) {
322 // might be a bare ip address, try this first to avoid dns lookups that may not be needed 324 // might be a bare ip address, try this first to avoid dns lookups that may not be needed
323 in_addr ip; 325 in_addr ip;
324 if (inet_aton(question, &ip)) { 326 if (inet_aton(question, &ip)) {
325 return ip.s_addr; 327 return ip.s_addr;
326 } 328 }
327 } 329 }
330 int8_t qt = qtype;
331 priv.my_write((const char *)&qt, 1);// write the query type
328 size_t n = strlen(question); 332 size_t n = strlen(question);
329 if (question[n-1] == '.') { 333 if (question[n-1] == '.') {
330 priv.my_write(question, n+1); // write the question including the null terminator 334 priv.my_write(question, n+1); // write the question including the null terminator
331 } 335 }
332 else { 336 else {
333 priv.my_write(question, n); // write the question 337 priv.my_write(question, n); // write the question
334 priv.my_write(".", 2); // and the fully qualified . terminator and null string terminator 338 priv.my_write(".", 2); // and the fully qualified . terminator and null string terminator
335 } 339 }
340
336 glommer glom; 341 glommer glom;
337 char *buf = (char *)&glom; 342 char *buf = (char *)&glom;
338 priv.my_read(buf, sizeof(glom.length)); 343 priv.my_read(buf, sizeof(glom.length));
339 buf += sizeof(glom.length); 344 buf += sizeof(glom.length);
340 #ifdef RESOLVER_DEBUG 345 #ifdef RESOLVER_DEBUG
403 } 408 }
404 } 409 }
405 } 410 }
406 } 411 }
407 int rrnum = 0; 412 int rrnum = 0;
408 while (ns_parserr(&handle, ns_s_an, rrnum++, &rr) == 0) { 413 if (qtype == ns_t_a) {
409 if (ns_rr_type(rr) == ns_t_a) { 414 while (ns_parserr(&handle, ns_s_an, rrnum++, &rr) == 0) {
410 uint32_t address; 415 if (ns_rr_type(rr) == qtype) {
411 memcpy(&address, ns_rr_rdata(rr), sizeof(address)); 416 uint32_t address;
412 ret_address = address; 417 memcpy(&address, ns_rr_rdata(rr), sizeof(address));
418 ret_address = address;
419 }
420 }
421 }
422 if ((qtype == ns_t_txt) && (txt_answer) && (txt_size > 5)) {
423 txt_size--; // allow room for terminating null;
424 txt_answer[0] = '\0'; // return null string if there are no answers
425 while (ns_parserr(&handle, ns_s_an, rrnum++, &rr) == 0) {
426 size_t offset = 0;
427 if (ns_rr_type(rr) == qtype) {
428 size_t rdlen = ns_rr_rdlen(rr);
429 const unsigned char *rdata = ns_rr_rdata(rr);
430 while ((offset < txt_size) && rdlen) {
431 size_t slen = size_t(*(rdata++));
432 rdlen--;
433 size_t m = min(slen, rdlen);
434 m = min(m, txt_size-offset);
435 memcpy(txt_answer+offset, rdata, m);
436 offset += m;
437 rdata += m;
438 rdlen -= m;
439 }
440 }
441 txt_answer[offset] = '\0'; // trailing null
442 if (strcasecmp(txt_answer, "v=spf1 ") == 0) break;
413 } 443 }
414 } 444 }
415 } 445 }
416 pthread_mutex_unlock(&resolve_mutex); 446 pthread_mutex_unlock(&resolve_mutex);
417 #ifdef RESOLVER_DEBUG 447 #ifdef RESOLVER_DEBUG
434 // 464 //
435 bool uriblookup(mlfiPriv &priv, string_set &hosts, const char *hostname, const char *&found) ; 465 bool uriblookup(mlfiPriv &priv, string_set &hosts, const char *hostname, const char *&found) ;
436 bool uriblookup(mlfiPriv &priv, string_set &hosts, const char *hostname, const char *&found) { 466 bool uriblookup(mlfiPriv &priv, string_set &hosts, const char *hostname, const char *&found) {
437 char buf[maxlen]; 467 char buf[maxlen];
438 snprintf(buf, sizeof(buf), "%s.%s.", hostname, priv.uribl_suffix); 468 snprintf(buf, sizeof(buf), "%s.%s.", hostname, priv.uribl_suffix);
439 uint32_t ip = ntohl(dns_interface(priv, buf, false, NULL)); 469 uint32_t ip = ntohl(dns_interface(priv, buf, ns_t_a));
440 if (ip and (ip != 0x7f000000)) { 470 if (ip and (ip != 0x7f000000)) {
441 if (debug_syslog > 2) { 471 if (debug_syslog > 2) {
442 char tmp[maxlen]; 472 char tmp[maxlen];
443 snprintf(tmp, sizeof(tmp), "found %s on %s", hostname, priv.uribl_suffix); 473 snprintf(tmp, sizeof(tmp), "found %s on %s", hostname, priv.uribl_suffix);
444 my_syslog(tmp); 474 my_syslog(tmp);
719 } 749 }
720 return rs; 750 return rs;
721 } 751 }
722 752
723 const char *mlfiPriv::check_uribl_signers() { 753 const char *mlfiPriv::check_uribl_signers() {
724 const char *st;
725 if (uribl_suffix) { 754 if (uribl_suffix) {
726 for (string_set::iterator s=dkim_signers.begin(); s!=dkim_signers.end(); s++) { 755 for (string_set::iterator s=dkim_signers.begin(); s!=dkim_signers.end(); s++) {
727 if (check_uribl(*this, hosts_uribl, *s, host_uribl)) return host_uribl; 756 if (check_uribl(*this, hosts_uribl, *s, host_uribl)) return host_uribl;
728 } 757 }
729 } 758 }
851 880
852 // find the answer 881 // find the answer
853 #ifdef NS_PACKETSZ 882 #ifdef NS_PACKETSZ
854 #ifdef RESOLVER_DEBUG 883 #ifdef RESOLVER_DEBUG
855 char text[1000]; 884 char text[1000];
856 snprintf(text, sizeof(text), "process_resolver_requests() has a question %s", question); 885 snprintf(text, sizeof(text), "process_resolver_requests() has a question %s qtype %d", question+1, int8_t(question[0]));
857 my_syslog(text); 886 my_syslog(text);
858 #endif 887 #endif
859 int res_result = res_search(question, ns_c_in, ns_t_a, glom.answer, sizeof(glom.answer)); 888 int res_result = res_search(question+1, ns_c_in, int8_t(question[0]), glom.answer, sizeof(glom.answer));
860 if (res_result < 0) glom.length = 0; // represent all errors as zero length answers 889 if (res_result < 0) glom.length = 0; // represent all errors as zero length answers
861 else glom.length = (size_t)res_result; 890 else glom.length = (size_t)res_result;
862 #else 891 #else
863 glom.length = sizeof(glom.answer); 892 glom.length = sizeof(glom.answer);
864 glom.answer = 0; 893 glom.answer = 0;
865 struct hostent *host = gethostbyname(question); 894 struct hostent *host = gethostbyname(question+1);
866 if (host && (host->h_addrtype == AF_INET)) { 895 if (host && (host->h_addrtype == AF_INET)) {
867 memcpy(&glom.answer, host->h_addr, sizeof(glom.answer)); 896 memcpy(&glom.answer, host->h_addr, sizeof(glom.answer));
868 } 897 }
869 #endif 898 #endif
870 899
911 #else 940 #else
912 char question[1000]; 941 char question[1000];
913 #endif 942 #endif
914 snprintf(question, sizeof(question), "%u.%u.%u.%u.%s.", src[3], src[2], src[1], src[0], suffix); 943 snprintf(question, sizeof(question), "%u.%u.%u.%u.%s.", src[3], src[2], src[1], src[0], suffix);
915 // ask the question, if we get an A record it implies a blacklisted ip address 944 // ask the question, if we get an A record it implies a blacklisted ip address
916 return dns_interface(priv, question, false, NULL); 945 return dns_interface(priv, question, ns_t_a);
917 } 946 }
918 947
919 948
920 //////////////////////////////////////////////// 949 ////////////////////////////////////////////////
921 // check a single dnsbl 950 // check a single dnsbl
1026 } 1055 }
1027 continue; 1056 continue;
1028 } 1057 }
1029 } 1058 }
1030 count++; 1059 count++;
1031 ip = dns_interface(priv, host, true, &nameservers); 1060 ip = dns_interface(priv, host, ns_t_a, true, &nameservers);
1032 if (debug_syslog > 2) { 1061 if (debug_syslog > 2) {
1033 char buf[maxlen]; 1062 char buf[maxlen];
1034 if (ip) { 1063 if (ip) {
1035 char adr[sizeof "255.255.255.255 "]; 1064 char adr[sizeof "255.255.255.255 "];
1036 adr[0] = '\0'; 1065 adr[0] = '\0';
1064 for (ns_mapper::iterator i=nameservers.ns_ip.begin(); i!=nameservers.ns_ip.end(); i++) { 1093 for (ns_mapper::iterator i=nameservers.ns_ip.begin(); i!=nameservers.ns_ip.end(); i++) {
1065 count++; 1094 count++;
1066 if ((count > limit) && (limit > 0)) return false; // too many name servers to check them all 1095 if ((count > limit) && (limit > 0)) return false; // too many name servers to check them all
1067 host = (*i).first; // a transient reference that needs to be replaced before we return it 1096 host = (*i).first; // a transient reference that needs to be replaced before we return it
1068 ip = (*i).second; 1097 ip = (*i).second;
1069 if (!ip) ip = dns_interface(priv, host, false, NULL); 1098 if (!ip) ip = dns_interface(priv, host, ns_t_a);
1070 if (debug_syslog > 2) { 1099 if (debug_syslog > 2) {
1071 char buf[maxlen]; 1100 char buf[maxlen];
1072 if (ip) { 1101 if (ip) {
1073 char adr[sizeof "255.255.255.255 "]; 1102 char adr[sizeof "255.255.255.255 "];
1074 adr[0] = '\0'; 1103 adr[0] = '\0';
1653 } 1682 }
1654 1683
1655 for (context_map::iterator i=priv.env_to.begin(); i!=priv.env_to.end(); i++) { 1684 for (context_map::iterator i=priv.env_to.begin(); i!=priv.env_to.end(); i++) {
1656 const char *rcpt = (*i).first; 1685 const char *rcpt = (*i).first;
1657 CONTEXT &con = *((*i).second); 1686 CONTEXT &con = *((*i).second);
1658 const char *st = con.acceptable_content(*priv.memory, score, bulk, priv.queueid, priv.dkim_signers, priv.fromaddr, msg); 1687 const char *st = con.acceptable_content(*priv.memory, score, bulk, priv.queueid, priv.dkim_signers, priv.fromaddr, &priv, msg);
1659 if (st == token_black) { 1688 if (st == token_black) {
1660 // bad html tags or excessive hosts or 1689 // bad html tags or excessive hosts or
1661 // high spam assassin score or dcc bulk threshold exceedeed 1690 // high spam assassin score or dcc bulk threshold exceedeed
1662 // or signed by a dkim signer that we don't like 1691 // or signed by a dkim signer that we don't like
1663 // or header from requires dkim signer that is missing 1692 // or header from requires dkim signer that is missing