Follow CNAME chains only one step. Bug 2264
authorJeremy Harris <jgh146exb@wizmail.org>
Thu, 7 Jun 2018 17:08:22 +0000 (18:08 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Thu, 7 Jun 2018 17:08:22 +0000 (18:08 +0100)
12 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
doc/doc-txt/OptionLists.txt
src/README.UPDATING
src/src/dns.c
src/src/globals.c
src/src/globals.h
src/src/readconf.c
src/src/verify.c
test/aux-var-src/tls_conf_prefix
test/stdout/0572
test/stdout/0577

index c4b3837da00c8616ebbf7bbf08c75f218df0f5b9..431d4560c1129a2cba642c35e780c2fbb4d2eb25 100644 (file)
@@ -13873,6 +13873,7 @@ listed in more than one group.
 .row &%av_scanner%&                  "specify virus scanner"
 .row &%check_rfc2047_length%&        "check length of RFC 2047 &""encoded &&&
                                       words""&"
 .row &%av_scanner%&                  "specify virus scanner"
 .row &%check_rfc2047_length%&        "check length of RFC 2047 &""encoded &&&
                                       words""&"
+.row &%dns_cname_loops%&             "follow CNAMEs returned by resolver"
 .row &%dns_csa_search_limit%&        "control CSA parent search depth"
 .row &%dns_csa_use_reverse%&         "en/disable CSA IP reverse search"
 .row &%header_maxsize%&              "total size of message header"
 .row &%dns_csa_search_limit%&        "control CSA parent search depth"
 .row &%dns_csa_use_reverse%&         "en/disable CSA IP reverse search"
 .row &%header_maxsize%&              "total size of message header"
@@ -14775,6 +14776,19 @@ This option controls whether or not an IP address, given as a CSA domain, is
 reversed and looked up in the reverse DNS, as described in more detail in
 section &<<SECTverifyCSA>>&.
 
 reversed and looked up in the reverse DNS, as described in more detail in
 section &<<SECTverifyCSA>>&.
 
+.new
+.option dns_cname_loops main integer 1
+.cindex DNS "CNAME following"
+This option controls the following of CNAME chains, needed if the resolver does
+not do it internally.
+As of 2018 most should, and the default can be left.
+If you have an ancient one, a value of 10 is likely needed.
+
+The default value of one CNAME-follow is needed
+thanks to the observed return for an MX request,
+given no MX presence but a CNAME to an A, of the CNAME.
+.wen
+
 
 .option dns_dnssec_ok main integer -1
 .cindex "DNS" "resolver options"
 
 .option dns_dnssec_ok main integer -1
 .cindex "DNS" "resolver options"
index 36f2d704856ddbc5aa75ae3505eb2fa187c8d06d..6b36763fe5077fa62d3a2455f16035c1893f22cf 100644 (file)
@@ -52,6 +52,13 @@ JH/09 Bug 2274: Fix logging of cmdline args when starting in an unlinked cwd.
 JH/10 Fix ARC signing for case when DKIM signing failed.  Previously this would
       segfault.
 
 JH/10 Fix ARC signing for case when DKIM signing failed.  Previously this would
       segfault.
 
+JH/11 Bug 2264: Exim now only follows CNAME chains one step by default. We'd
+      like zero, since the resolver should be doing this for us, But we need one
+      as a CNAME but no MX presence gets the CNAME returned; we need to check
+      that doesn't point to an MX to declare it "no MX returned" rather than
+      "error, loop".  A new main option is added so the older capability of
+      following some limited number of chain links is maintained.
+
 
 Exim version 4.91
 -----------------
 
 Exim version 4.91
 -----------------
index dfb0219cb19097a6bc49450a15c641a7d377fcc7..91ce182fd6fcc9b363d4416404e6cff5e9cfab17 100644 (file)
@@ -182,6 +182,7 @@ dmarc_history_file                   string          unset         main
 dmarc_tld_file                       string          unset         main              4.82 if experimental_dmarc
 dns_again_means_nonexist             domain list     unset         main              1.89
 dns_check_names_pattern              string          +             main              2.11
 dmarc_tld_file                       string          unset         main              4.82 if experimental_dmarc
 dns_again_means_nonexist             domain list     unset         main              1.89
 dns_check_names_pattern              string          +             main              2.11
+dns_cname_loops                      integer         0             main              4.92 Set to 9 for older behaviour
 dns_csa_search_limit                 integer         5             main              4.60
 dns_csa_use_reverse                  boolean         true          main              4.60
 dns_dnssec_ok                        integer         -1            main              4.82
 dns_csa_search_limit                 integer         5             main              4.60
 dns_csa_use_reverse                  boolean         true          main              4.60
 dns_dnssec_ok                        integer         -1            main              4.82
index 73b52e4a01739f12a1320a9b62b3b126d728f13e..2438cc953fb20443bb1ea5501bbfb9d17dc8bf60 100644 (file)
@@ -26,6 +26,14 @@ The rest of this document contains information about changes in 4.xx releases
 that might affect a running system.
 
 
 that might affect a running system.
 
 
+Exim version 4.92
+-----------------
+
+ * Exim used to manually follow CNAME chains, to a limited depth.  In this
+   day-and-age we expect the resolver to be doing this for us, so the loop
+   is limited to one retry unless the (new) config option dns_cname_loops
+   is changed.
+
 Exim version 4.91
 -----------------
 
 Exim version 4.91
 -----------------
 
index 83de7c26684ea3a6ad640089899c59e986dcb453..cb1766618ba26bc29cf2cf8ede723e34da7edecc 100644 (file)
@@ -674,10 +674,10 @@ 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),
   {
   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;
   }
 
   return previous->data.val;
   }
 
@@ -836,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.
 /* 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
 
 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
@@ -871,9 +873,14 @@ int i;
 const uschar *orig_name = name;
 BOOL secure_so_far = TRUE;
 
 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;
   {
   uschar * data;
   dns_record *rr, cname_rr, type_rr;
index ec948151fc712a5e79c3ba338614ec25f74ea78f..138a29e8ad20408ed05834d0c1a69027c4db58e6 100644 (file)
@@ -698,6 +698,7 @@ BOOL    dmarc_enable_forensic   = FALSE;
 uschar *dns_again_means_nonexist = NULL;
 int     dns_csa_search_limit   = 5;
 BOOL    dns_csa_use_reverse    = TRUE;
 uschar *dns_again_means_nonexist = NULL;
 int     dns_csa_search_limit   = 5;
 BOOL    dns_csa_use_reverse    = TRUE;
+int    dns_cname_loops        = 1;
 #ifdef SUPPORT_DANE
 int     dns_dane_ok            = -1;
 #endif
 #ifdef SUPPORT_DANE
 int     dns_dane_ok            = -1;
 #endif
index 9c7d8ccd9cf02659b69ba082c4c04f6c13f8ffa3..f9be8b8324dc6d389d0517752401b89c0d9fa860 100644 (file)
@@ -421,6 +421,7 @@ extern BOOL    dmarc_enable_forensic;  /* Set via ACL control statement. When se
 extern uschar *dns_again_means_nonexist; /* Domains that are badly set up */
 extern int     dns_csa_search_limit;   /* How deep to search for CSA SRV records */
 extern BOOL    dns_csa_use_reverse;    /* Check CSA in reverse DNS? (non-standard) */
 extern uschar *dns_again_means_nonexist; /* Domains that are badly set up */
 extern int     dns_csa_search_limit;   /* How deep to search for CSA SRV records */
 extern BOOL    dns_csa_use_reverse;    /* Check CSA in reverse DNS? (non-standard) */
+extern int     dns_cname_loops;               /* Follow CNAMEs returned by resolver to this depth */
 extern uschar *dns_ipv4_lookup;        /* For these domains, don't look for AAAA (or A6) */
 #ifdef SUPPORT_DANE
 extern int     dns_dane_ok;            /* Ok to use DANE when checking TLS authenticity */
 extern uschar *dns_ipv4_lookup;        /* For these domains, don't look for AAAA (or A6) */
 #ifdef SUPPORT_DANE
 extern int     dns_dane_ok;            /* Ok to use DANE when checking TLS authenticity */
index 919fbc2158ffe7812f3e5913d3b8229dfaf38358..3f307fd5cc5ff37a961c58b155e732b522664768 100644 (file)
@@ -123,6 +123,7 @@ static optionlist optionlist_config[] = {
 #endif
   { "dns_again_means_nonexist", opt_stringptr,   &dns_again_means_nonexist },
   { "dns_check_names_pattern",  opt_stringptr,   &check_dns_names_pattern },
 #endif
   { "dns_again_means_nonexist", opt_stringptr,   &dns_again_means_nonexist },
   { "dns_check_names_pattern",  opt_stringptr,   &check_dns_names_pattern },
+  { "dns_cname_loops",         opt_int,         &dns_cname_loops },
   { "dns_csa_search_limit",     opt_int,         &dns_csa_search_limit },
   { "dns_csa_use_reverse",      opt_bool,        &dns_csa_use_reverse },
   { "dns_dnssec_ok",            opt_int,         &dns_dnssec_ok },
   { "dns_csa_search_limit",     opt_int,         &dns_csa_search_limit },
   { "dns_csa_use_reverse",      opt_bool,        &dns_csa_use_reverse },
   { "dns_dnssec_ok",            opt_int,         &dns_dnssec_ok },
index 95876d1cd2a7f6352fbf8aa44e029b0e4155b326..5d0551e89c5ea8ac87c75eb37b6c68955b69ed32 100644 (file)
@@ -3411,9 +3411,8 @@ else
 
   /* If the lookup succeeded, cache the RHS address. The code allows for
   more than one address - this was for complete generality and the possible
 
   /* If the lookup succeeded, cache the RHS address. The code allows for
   more than one address - this was for complete generality and the possible
-  use of A6 records. However, A6 records have been reduced to experimental
-  status (August 2001) and may die out. So they may never get used at all,
-  let alone in dnsbl records. However, leave the code here, just in case.
+  use of A6 records. However, A6 records are no longer supported. Leave the code
+  here, just in case.
 
   Quite apart from one A6 RR generating multiple addresses, there are DNS
   lists that return more than one A record, so we must handle multiple
 
   Quite apart from one A6 RR generating multiple addresses, there are DNS
   lists that return more than one A record, so we must handle multiple
index 20b6fe85f44674ce795ab494a3f3836edc5c800a..e357b996d3a4794dc2111eb17cf101b3ee01aac7 100644 (file)
@@ -11,4 +11,5 @@ log_file_path = DIR/spool/log/%slog
 
 gecos_pattern = ""
 gecos_name = CALLER_NAME
 
 gecos_pattern = ""
 gecos_name = CALLER_NAME
+dns_cname_loops = 9
 chunking_advertise_hosts =
 chunking_advertise_hosts =
index 96ab5611b10d95bef59ad6c11a2e7089380fccd3..272aa06a1345b1e18496c2e86b4aab21e973d94f 100644 (file)
@@ -85,6 +85,7 @@ spool_directory = TESTSUITE/spool
 log_file_path = TESTSUITE/spool/log/%slog
 gecos_pattern = ""
 gecos_name = CALLER_NAME
 log_file_path = TESTSUITE/spool/log/%slog
 gecos_pattern = ""
 gecos_name = CALLER_NAME
+dns_cname_loops = 9
 chunking_advertise_hosts =
 # 1 "TESTSUITE/aux-var/std_conf_prefix"
 # 5 "TESTSUITE/test-config"
 chunking_advertise_hosts =
 # 1 "TESTSUITE/aux-var/std_conf_prefix"
 # 5 "TESTSUITE/test-config"
@@ -124,6 +125,7 @@ spool_directory = TESTSUITE/spool
 log_file_path = TESTSUITE/spool/log/%slog
 gecos_pattern = ""
 gecos_name = CALLER_NAME
 log_file_path = TESTSUITE/spool/log/%slog
 gecos_pattern = ""
 gecos_name = CALLER_NAME
+dns_cname_loops = 9
 chunking_advertise_hosts =
 # 1 "TESTSUITE/aux-var/std_conf_prefix"
 # 5 "TESTSUITE/test-config"
 chunking_advertise_hosts =
 # 1 "TESTSUITE/aux-var/std_conf_prefix"
 # 5 "TESTSUITE/test-config"
index 893f9224327d2e24034f236135aacd04017027ea..80cf3da884032fcfb88356fda9c8bbf56688ba8f 100644 (file)
@@ -11,6 +11,7 @@ spool_directory = TESTSUITE/spool
 log_file_path = TESTSUITE/spool/log/%slog
 gecos_pattern = ""
 gecos_name = CALLER_NAME
 log_file_path = TESTSUITE/spool/log/%slog
 gecos_pattern = ""
 gecos_name = CALLER_NAME
+dns_cname_loops = 9
 chunking_advertise_hosts =
 # 1 "TESTSUITE/aux-var/std_conf_prefix"
 tls_advertise_hosts =
 chunking_advertise_hosts =
 # 1 "TESTSUITE/aux-var/std_conf_prefix"
 tls_advertise_hosts =