DANE: move to mainline
[exim.git] / src / src / transports / smtp.c
index a1b677e190b484781213bc8b5fada4c04b617b3c..38660f79718aeb867e9eae9c299c6a1d627be0a8 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2017 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 #include "../exim.h"
@@ -105,7 +105,7 @@ optionlist smtp_transport_options[] = {
   { "hosts_require_auth",   opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_require_auth) },
 #ifdef SUPPORT_TLS
-# ifdef EXPERIMENTAL_DANE
+# ifdef SUPPORT_DANE
   { "hosts_require_dane",   opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_require_dane) },
 # endif
@@ -120,7 +120,7 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, hosts_try_auth) },
   { "hosts_try_chunking",   opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_try_chunking) },
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
   { "hosts_try_dane",       opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_try_dane) },
 #endif
@@ -219,7 +219,7 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   .hosts_try_auth =            NULL,
   .hosts_require_auth =                NULL,
   .hosts_try_chunking =                US"*",
-#ifdef EXPERIMENTAL_DANE
+#ifdef SUPPORT_DANE
   .hosts_try_dane =            NULL,
   .hosts_require_dane =                NULL,
 #endif
@@ -1190,7 +1190,7 @@ return FALSE;
 
 
 
-#ifdef EXPERIMENTAL_DANE
+#ifdef SUPPORT_DANE
 /* Lookup TLSA record for host/port.
 Return:  OK            success with dnssec; DANE mode
          DEFER         Do not use this host now, may retry later
@@ -1490,7 +1490,7 @@ Returns:          OK    - the connection was made and the delivery attempted;
 int
 smtp_setup_conn(smtp_context * sx, BOOL suppress_tls)
 {
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
 dns_answer tlsa_dnsa;
 #endif
 BOOL pass_message = FALSE;
@@ -1512,7 +1512,7 @@ sx->esmtp_sent = FALSE;
 sx->utf8_needed = FALSE;
 #endif
 sx->dsn_all_lasthop = TRUE;
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
 sx->dane = FALSE;
 sx->dane_required = verify_check_given_host(&sx->ob->hosts_require_dane, sx->host) == OK;
 #endif
@@ -1586,7 +1586,7 @@ if (!continue_hostname)
 
   smtp_port_for_connect(sx->host, sx->port);
 
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
     /* Do TLSA lookup for DANE */
     {
     tls_out.dane_verified = FALSE;
@@ -1936,7 +1936,7 @@ if (  smtp_peer_options & OPTION_TLS
     address_item * addr;
     uschar * errstr;
     int rc = tls_client_start(sx->inblock.sock, sx->host, sx->addrlist, sx->tblock,
-# ifdef EXPERIMENTAL_DANE
+# ifdef SUPPORT_DANE
                             sx->dane ? &tlsa_dnsa : NULL,
 # endif
                             &errstr);
@@ -1947,7 +1947,7 @@ if (  smtp_peer_options & OPTION_TLS
 
     if (rc != OK)
       {
-# ifdef EXPERIMENTAL_DANE
+# ifdef SUPPORT_DANE
       if (sx->dane) log_write(0, LOG_MAIN,
          "DANE attempt failed; TLS connection to %s [%s]: %s",
          sx->host->name, sx->host->address, errstr);
@@ -2034,7 +2034,7 @@ if (tls_out.active >= 0)
 have one. */
 
 else if (  sx->smtps
-# ifdef EXPERIMENTAL_DANE
+# ifdef SUPPORT_DANE
        || sx->dane
 # endif
        || verify_check_given_host(&sx->ob->hosts_require_tls, sx->host) == OK
@@ -3733,7 +3733,7 @@ uschar *tid = string_sprintf("%s transport", tblock->name);
 smtp_transport_options_block *ob =
   (smtp_transport_options_block *)(tblock->options_block);
 host_item *hostlist = addrlist->host_list;
-host_item *host = NULL;
+host_item *host;
 
 DEBUG(D_transport)
   {
@@ -3744,7 +3744,7 @@ DEBUG(D_transport)
     {
     debug_printf("hostlist:\n");
     for (host = hostlist; host; host = host->next)
-      debug_printf("  %s:%d\n", host->name, host->port);
+      debug_printf("  '%s' IP %s port %d\n", host->name, host->address, host->port);
     }
   if (continue_hostname)
     debug_printf("already connected to %s [%s] (on fd %d)\n",
@@ -3925,7 +3925,9 @@ for (cutoff_retry = 0;
   {
   host_item *nexthost = NULL;
   int unexpired_hosts_tried = 0;
+  BOOL continue_host_tried = FALSE;
 
+retry_non_continued:
   for (host = hostlist;
           host
        && unexpired_hosts_tried < ob->hosts_max_try
@@ -3990,7 +3992,7 @@ for (cutoff_retry = 0;
       /* Find by name if so configured, or if it's an IP address. We don't
       just copy the IP address, because we need the test-for-local to happen. */
 
-      flags = HOST_FIND_BY_A;
+      flags = HOST_FIND_BY_A | HOST_FIND_BY_AAAA;
       if (ob->dns_qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE;
       if (ob->dns_search_parents) flags |= HOST_FIND_SEARCH_PARENTS;
 
@@ -4058,14 +4060,16 @@ for (cutoff_retry = 0;
     result of the lookup. Set expired FALSE, to save the outer loop executing
     twice. */
 
-    if (  continue_hostname
-       && (  Ustrcmp(continue_hostname, host->name) != 0
-          || Ustrcmp(continue_host_address, host->address) != 0
-       )  )
-      {
-      expired = FALSE;
-      continue;      /* With next host */
-      }
+    if (continue_hostname)
+      if (  Ustrcmp(continue_hostname, host->name) != 0
+         || Ustrcmp(continue_host_address, host->address) != 0
+        )
+       {
+       expired = FALSE;
+       continue;      /* With next host */
+       }
+      else
+       continue_host_tried = TRUE;
 
     /* Reset the default next host in case a multihomed host whose addresses
     are not looked up till just above added to the host list. */
@@ -4523,6 +4527,32 @@ for (cutoff_retry = 0;
       }
     }   /* End of loop for trying multiple hosts. */
 
+  /* If we failed to find a matching host in the list, for an already-open
+  connection, just close it and start over with the list.  This can happen
+  for routing that changes from run to run, or big multi-IP sites with
+  round-robin DNS. */
+
+  if (continue_hostname && !continue_host_tried)
+    {
+    int fd = cutthrough.fd >= 0 ? cutthrough.fd : 0;
+
+    DEBUG(D_transport) debug_printf("no hosts match already-open connection\n");
+#ifdef SUPPORT_TLS
+    if (tls_out.active == fd)
+      {
+      (void) tls_write(FALSE, US"QUIT\r\n", 6, FALSE);
+      tls_close(FALSE, TRUE);
+      }
+    else
+#else
+      (void) write(fd, US"QUIT\r\n", 6);
+#endif
+    (void) close(fd);
+    cutthrough.fd = -1;
+    continue_hostname = NULL;
+    goto retry_non_continued;
+    }
+
   /* This is the end of the loop that repeats iff expired is TRUE and
   ob->delay_after_cutoff is FALSE. The second time round we will
   try those hosts that haven't been tried since the message arrived. */