Follow CNAME chains only one step. Bug 2264
[exim.git] / src / src / dns.c
index 815a978e9d38664bbce8db48491a18301fd3049b..cb1766618ba26bc29cf2cf8ede723e34da7edecc 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2016 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for interfacing with the DNS. */
@@ -271,10 +271,7 @@ else
     {
     int j;
     for (j = 0; j < 32; j += 4)
-      {
-      sprintf(CS pp, "%x.", (v6[i] >> j) & 15);
-      pp += 2;
-      }
+      pp += sprintf(CS pp, "%x.", (v6[i] >> j) & 15);
     }
   Ustrcpy(pp, "ip6.arpa.");
 
@@ -599,6 +596,15 @@ switch(t)
 *        Cache a failed DNS lookup result        *
 *************************************************/
 
+static void
+dns_fail_tag(uschar * buf, const uschar * name, int dns_type)
+{
+res_state resp = os_get_dns_resolver_res();
+sprintf(CS buf, "%.255s-%s-%lx", name, dns_text_type(dns_type),
+  (unsigned long) resp->options);
+}
+
+
 /* We cache failed lookup results so as not to experience timeouts many
 times for the same domain. We need to retain the resolver options because they
 may change. For successful lookups, we rely on resolver and/or name server
@@ -615,10 +621,8 @@ Returns:     the return code
 static int
 dns_return(const uschar * name, int type, int rc)
 {
-res_state resp = os_get_dns_resolver_res();
 tree_node *node = store_get_perm(sizeof(tree_node) + 290);
-sprintf(CS node->name, "%.255s-%s-%lx", name, dns_text_type(type),
-  (unsigned long) resp->options);
+dns_fail_tag(node->name, name, type);
 node->data.val = rc;
 (void)tree_insertnode(&tree_dns_fails, node);
 return rc;
@@ -656,7 +660,6 @@ dns_basic_lookup(dns_answer *dnsa, const uschar *name, int type)
 int rc = -1;
 const uschar *save_domain;
 #endif
-res_state resp = os_get_dns_resolver_res();
 
 tree_node *previous;
 uschar node_name[290];
@@ -666,16 +669,15 @@ a timeout on one domain doesn't happen time and time again for messages that
 have many addresses in the same domain. We rely on the resolver and name server
 caching for successful lookups. */
 
-sprintf(CS node_name, "%.255s-%s-%lx", name, dns_text_type(type),
-  (unsigned long) resp->options);
+dns_fail_tag(node_name, name, type);
 if ((previous = tree_search(tree_dns_fails, node_name)))
   {
   DEBUG(D_dns) debug_printf("DNS lookup of %.255s-%s: using cached value %s\n",
     name, dns_text_type(type),
-      (previous->data.val == DNS_NOMATCH)? "DNS_NOMATCH" :
-      (previous->data.val == DNS_NODATA)? "DNS_NODATA" :
-      (previous->data.val == DNS_AGAIN)? "DNS_AGAIN" :
-      (previous->data.val == DNS_FAIL)? "DNS_FAIL" : "??");
+      previous->data.val == DNS_NOMATCH ? "DNS_NOMATCH" :
+      previous->data.val == DNS_NODATA ? "DNS_NODATA" :
+      previous->data.val == DNS_AGAIN ? "DNS_AGAIN" :
+      previous->data.val == DNS_FAIL ? "DNS_FAIL" : "??");
   return previous->data.val;
   }
 
@@ -765,8 +767,9 @@ dnsa->answerlen = running_in_test_harness
 
 if (dnsa->answerlen > (int) sizeof(dnsa->answer))
   {
-  DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) resulted in overlong packet (size %d), truncating to %d.\n",
-    name, dns_text_type(type), dnsa->answerlen, sizeof(dnsa->answer));
+  DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) resulted in overlong packet"
+    " (size %d), truncating to %u.\n",
+    name, dns_text_type(type), dnsa->answerlen, (unsigned int) sizeof(dnsa->answer));
   dnsa->answerlen = sizeof(dnsa->answer);
   }
 
@@ -833,6 +836,8 @@ return DNS_SUCCEED;
 /* Look up the given domain name, using the given type. Follow CNAMEs if
 necessary, but only so many times. There aren't supposed to be CNAME chains in
 the DNS, but you are supposed to cope with them if you find them.
+By default, follow one CNAME since a resolver has been seen, faced with
+an MX request and a CNAME (to an A) but no MX present, returning the CNAME.
 
 The assumption is made that if the resolver gives back records of the
 requested type *and* a CNAME, we don't need to make another call to look up
@@ -868,9 +873,14 @@ int i;
 const uschar *orig_name = name;
 BOOL secure_so_far = TRUE;
 
-/* Loop to follow CNAME chains so far, but no further... */
+/* By default, assume the resolver follows CNAME chains (and returns NODATA for
+an unterminated one). If it also does that for a CNAME loop, fine; if it returns
+a CNAME (maybe the last?) whine about it.  However, retain the coding for dumb
+resolvers hiding behind a config variable. Loop to follow CNAME chains so far,
+but no further...  The testsuite tests the latter case, mostly assuming that the
+former will work. */
 
-for (i = 0; i < 10; i++)
+for (i = 0; i <= dns_cname_loops; i++)
   {
   uschar * data;
   dns_record *rr, cname_rr, type_rr;
@@ -1018,7 +1028,7 @@ switch (type)
   assertion field. */
   case T_CSA:
     {
-    uschar *srvname, *namesuff, *tld, *p;
+    uschar *srvname, *namesuff, *tld;
     int priority, weight, port;
     int limit, rc, i;
     BOOL ipv6;