Fix crash from SRV lookup hitting a CNAME
[exim.git] / src / src / dns.c
index cb1766618ba26bc29cf2cf8ede723e34da7edecc..b7978c52133519455a211bbc4ef2789175e9f7a0 100644 (file)
@@ -639,6 +639,10 @@ up nameservers that produce this error continually, so there is the option of
 providing a list of domains for which this is treated as a non-existent
 host.
 
+The dns_answer structure is pretty big; enough to hold a max-sized DNS message
+- so best allocated from fast-release memory.  As of writing, all our callers
+use a stack-auto variable.
+
 Arguments:
   dnsa      pointer to dns_answer structure
   name      name to look up
@@ -693,7 +697,7 @@ if ((previous = tree_search(tree_dns_fails, node_name)))
     DEBUG(D_dns)
       debug_printf("DNS name '%s' utf8 conversion to alabel failed: %s\n", name,
         errstr);
-    host_find_failed_syntax = TRUE;
+    f.host_find_failed_syntax = TRUE;
     return DNS_NOMATCH;
     }
   name = alabel;
@@ -712,7 +716,11 @@ lookup, which constructs the names itself, so they should be OK. Besides,
 bitstring labels don't conform to normal name syntax. (But the aren't used any
 more.)
 
-For SRV records, we omit the initial _smtp._tcp. components at the start. */
+For SRV records, we omit the initial _smtp._tcp. components at the start.
+The check has been seen to bite on the destination of a SRV lookup that
+initiall hit a CNAME, for which the next name had only two components.
+RFC2782 makes no mention of the possibiility of CNAMES, but the Wikipedia
+article on SRV says they are not a valid configuration. */
 
 #ifndef STAND_ALONE   /* Omit this for stand-alone tests */
 
@@ -728,8 +736,8 @@ if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT)
 
   if (type == T_SRV || type == T_TLSA)
     {
-    while (*checkname++ != '.');
-    while (*checkname++ != '.');
+    while (*checkname && *checkname++ != '.') ;
+    while (*checkname && *checkname++ != '.') ;
     }
 
   if (pcre_exec(regex_check_dns_names, NULL, CCS checkname, Ustrlen(checkname),
@@ -738,7 +746,7 @@ if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT)
     DEBUG(D_dns)
       debug_printf("DNS name syntax check failed: %s (%s)\n", name,
         dns_text_type(type));
-    host_find_failed_syntax = TRUE;
+    f.host_find_failed_syntax = TRUE;
     return DNS_NOMATCH;
     }
   }
@@ -761,7 +769,7 @@ if ((type == T_A || type == T_AAAA) && string_is_ip_address(name, NULL) != 0)
 (res_search), we call fakens_search(), which recognizes certain special
 domains, and interfaces to a fake nameserver for certain special zones. */
 
-dnsa->answerlen = running_in_test_harness
+dnsa->answerlen = f.running_in_test_harness
   ? fakens_search(name, type, dnsa->answer, sizeof(dnsa->answer))
   : res_search(CCS name, C_IN, type, dnsa->answer, sizeof(dnsa->answer));
 
@@ -885,7 +893,7 @@ for (i = 0; i <= dns_cname_loops; i++)
   uschar * data;
   dns_record *rr, cname_rr, type_rr;
   dns_scan dnss;
-  int datalen, rc;
+  int rc;
 
   /* DNS lookup failures get passed straight back. */
 
@@ -947,8 +955,8 @@ for (i = 0; i <= dns_cname_loops; i++)
     return DNS_FAIL;
 
   data = store_get(256);
-  if ((datalen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
-    cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256)) < 0)
+  if (dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen,
+      cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256) < 0)
     return DNS_FAIL;
   name = data;
 
@@ -1101,7 +1109,7 @@ switch (type)
          && (h->rcode == NOERROR || h->rcode == NXDOMAIN)
          && ntohs(h->qdcount) == 1 && ntohs(h->ancount) == 0
          && ntohs(h->nscount) >= 1)
-           dnsa->answerlen = MAXPACKET;
+           dnsa->answerlen = sizeof(dnsa->answer);
 
       for (rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY);
           rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)