Logging: server pipelining offer but no uptake
[exim.git] / src / src / transports / smtp.c
index 454c0f74dbe15b8a9f8b7ee78de05420897e794b..bca8c4941c6660e137fb354ed3a811190b551cd7 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 *     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"
 /* See the file NOTICE for conditions of use and distribution. */
 
 #include "../exim.h"
@@ -24,6 +24,10 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, address_retry_include_sender) },
   { "allow_localhost",      opt_bool,
       (void *)offsetof(smtp_transport_options_block, allow_localhost) },
       (void *)offsetof(smtp_transport_options_block, address_retry_include_sender) },
   { "allow_localhost",      opt_bool,
       (void *)offsetof(smtp_transport_options_block, allow_localhost) },
+#ifdef EXPERIMENTAL_ARC
+  { "arc_sign", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, arc_sign) },
+#endif
   { "authenticated_sender", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, authenticated_sender) },
   { "authenticated_sender_force", opt_bool,
   { "authenticated_sender", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, authenticated_sender) },
   { "authenticated_sender_force", opt_bool,
@@ -34,6 +38,10 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, connect_timeout) },
   { "connection_max_messages", opt_int | opt_public,
       (void *)offsetof(transport_instance, connection_max_messages) },
       (void *)offsetof(smtp_transport_options_block, connect_timeout) },
   { "connection_max_messages", opt_int | opt_public,
       (void *)offsetof(transport_instance, connection_max_messages) },
+# ifdef SUPPORT_DANE
+  { "dane_require_tls_ciphers", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, dane_require_tls_ciphers) },
+# endif
   { "data_timeout",         opt_time,
       (void *)offsetof(smtp_transport_options_block, data_timeout) },
   { "delay_after_cutoff", opt_bool,
   { "data_timeout",         opt_time,
       (void *)offsetof(smtp_transport_options_block, data_timeout) },
   { "delay_after_cutoff", opt_bool,
@@ -43,6 +51,10 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, dkim.dkim_canon) },
   { "dkim_domain", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, dkim.dkim_domain) },
       (void *)offsetof(smtp_transport_options_block, dkim.dkim_canon) },
   { "dkim_domain", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, dkim.dkim_domain) },
+  { "dkim_hash", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, dkim.dkim_hash) },
+  { "dkim_identity", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, dkim.dkim_identity) },
   { "dkim_private_key", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, dkim.dkim_private_key) },
   { "dkim_selector", opt_stringptr,
   { "dkim_private_key", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, dkim.dkim_private_key) },
   { "dkim_selector", opt_stringptr,
@@ -51,6 +63,8 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, dkim.dkim_sign_headers) },
   { "dkim_strict", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, dkim.dkim_strict) },
       (void *)offsetof(smtp_transport_options_block, dkim.dkim_sign_headers) },
   { "dkim_strict", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, dkim.dkim_strict) },
+  { "dkim_timestamps", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, dkim.dkim_timestamps) },
 #endif
   { "dns_qualify_single",   opt_bool,
       (void *)offsetof(smtp_transport_options_block, dns_qualify_single) },
 #endif
   { "dns_qualify_single",   opt_bool,
       (void *)offsetof(smtp_transport_options_block, dns_qualify_single) },
@@ -101,7 +115,7 @@ optionlist smtp_transport_options[] = {
   { "hosts_require_auth",   opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_require_auth) },
 #ifdef SUPPORT_TLS
   { "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
   { "hosts_require_dane",   opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_require_dane) },
 # endif
@@ -116,7 +130,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) },
       (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
   { "hosts_try_dane",       opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_try_dane) },
 #endif
@@ -176,100 +190,129 @@ optionlist smtp_transport_options[] = {
   { "tls_verify_certificates", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_verify_certificates) },
   { "tls_verify_hosts",     opt_stringptr,
   { "tls_verify_certificates", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_verify_certificates) },
   { "tls_verify_hosts",     opt_stringptr,
-      (void *)offsetof(smtp_transport_options_block, tls_verify_hosts) }
+      (void *)offsetof(smtp_transport_options_block, tls_verify_hosts) },
+#endif
+#ifdef SUPPORT_I18N
+  { "utf8_downconvert",            opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, utf8_downconvert) },
 #endif
 };
 
 /* Size of the options list. An extern variable has to be used so that its
 address can appear in the tables drtables.c. */
 
 #endif
 };
 
 /* Size of the options list. An extern variable has to be used so that its
 address can appear in the tables drtables.c. */
 
-int smtp_transport_options_count =
-  sizeof(smtp_transport_options)/sizeof(optionlist);
+int smtp_transport_options_count = nelem(smtp_transport_options);
+
+
+#ifdef MACRO_PREDEF
+
+/* Dummy values */
+smtp_transport_options_block smtp_transport_option_defaults = {0};
+void smtp_transport_init(transport_instance *tblock) {}
+BOOL smtp_transport_entry(transport_instance *tblock, address_item *addr) {return FALSE;}
+void smtp_transport_closedown(transport_instance *tblock) {}
+
+#else   /*!MACRO_PREDEF*/
+
 
 /* Default private options block for the smtp transport. */
 
 smtp_transport_options_block smtp_transport_option_defaults = {
 
 /* Default private options block for the smtp transport. */
 
 smtp_transport_options_block smtp_transport_option_defaults = {
-  NULL,                /* hosts */
-  NULL,                /* fallback_hosts */
-  NULL,                /* hostlist */
-  NULL,                /* fallback_hostlist */
-  NULL,                /* authenticated_sender */
-  US"$primary_hostname", /* helo_data */
-  NULL,                /* interface */
-  NULL,                /* port */
-  US"smtp",            /* protocol */
-  NULL,                /* DSCP */
-  NULL,                /* serialize_hosts */
-  NULL,                /* hosts_try_auth */
-  NULL,                /* hosts_require_auth */
-  US"*",               /* hosts_try_chunking */
-#ifdef EXPERIMENTAL_DANE
-  NULL,                /* hosts_try_dane */
-  NULL,                /* hosts_require_dane */
+  .hosts =                     NULL,
+  .fallback_hosts =            NULL,
+  .hostlist =                  NULL,
+  .fallback_hostlist =         NULL,
+  .helo_data =                 US"$primary_hostname",
+  .interface =                 NULL,
+  .port =                      NULL,
+  .protocol =                  US"smtp",
+  .dscp =                      NULL,
+  .serialize_hosts =           NULL,
+  .hosts_try_auth =            NULL,
+  .hosts_require_auth =                NULL,
+  .hosts_try_chunking =                US"*",
+#ifdef SUPPORT_DANE
+  .hosts_try_dane =            NULL,
+  .hosts_require_dane =                NULL,
+  .dane_require_tls_ciphers =  NULL,
 #endif
 #endif
-  NULL,                /* hosts_try_fastopen */
+  .hosts_try_fastopen =                NULL,
 #ifndef DISABLE_PRDR
 #ifndef DISABLE_PRDR
-  US"*",               /* hosts_try_prdr */
+  .hosts_try_prdr =            US"*",
 #endif
 #ifndef DISABLE_OCSP
 #endif
 #ifndef DISABLE_OCSP
-  US"*",               /* hosts_request_ocsp (except under DANE; tls_client_start()) */
-  NULL,                /* hosts_require_ocsp */
+  .hosts_request_ocsp =                US"*",               /* hosts_request_ocsp (except under DANE; tls_client_start()) */
+  .hosts_require_ocsp =                NULL,
 #endif
 #endif
-  NULL,                /* hosts_require_tls */
-  NULL,                /* hosts_avoid_tls */
-  NULL,                /* hosts_verify_avoid_tls */
-  NULL,                /* hosts_avoid_pipelining */
-  NULL,                /* hosts_avoid_esmtp */
+  .hosts_require_tls =         NULL,
+  .hosts_avoid_tls =           NULL,
+  .hosts_verify_avoid_tls =    NULL,
+  .hosts_avoid_pipelining =    NULL,
+  .hosts_avoid_esmtp =         NULL,
 #ifdef SUPPORT_TLS
 #ifdef SUPPORT_TLS
-  NULL,                /* hosts_nopass_tls */
-  US"*",              /* hosts_noproxy_tls */
+  .hosts_nopass_tls =          NULL,
+  .hosts_noproxy_tls =         US"*",
 #endif
 #endif
-  5*60,                /* command_timeout */
-  5*60,                /* connect_timeout; shorter system default overrides */
-  5*60,                /* data timeout */
-  10*60,               /* final timeout */
-  1024,                /* size_addition */
-  5,                   /* hosts_max_try */
-  50,                  /* hosts_max_try_hardlimit */
-  TRUE,                /* address_retry_include_sender */
-  FALSE,               /* allow_localhost */
-  FALSE,               /* authenticated_sender_force */
-  FALSE,               /* gethostbyname */
-  TRUE,                /* dns_qualify_single */
-  FALSE,               /* dns_search_parents */
-  { NULL, NULL },      /* dnssec_domains {request,require} */
-  TRUE,                /* delay_after_cutoff */
-  FALSE,               /* hosts_override */
-  FALSE,               /* hosts_randomize */
-  TRUE,                /* keepalive */
-  FALSE,               /* lmtp_ignore_quota */
-  NULL,                       /* expand_retry_include_ip_address */
-  TRUE                 /* retry_include_ip_address */
+  .command_timeout =           5*60,
+  .connect_timeout =           5*60,
+  .data_timeout =              5*60,
+  .final_timeout =             10*60,
+  .size_addition =             1024,
+  .hosts_max_try =             5,
+  .hosts_max_try_hardlimit =   50,
+  .address_retry_include_sender = TRUE,
+  .allow_localhost =           FALSE,
+  .authenticated_sender_force =        FALSE,
+  .gethostbyname =             FALSE,
+  .dns_qualify_single =                TRUE,
+  .dns_search_parents =                FALSE,
+  .dnssec = { .request=NULL, .require=NULL },
+  .delay_after_cutoff =                TRUE,
+  .hosts_override =            FALSE,
+  .hosts_randomize =           FALSE,
+  .keepalive =                 TRUE,
+  .lmtp_ignore_quota =         FALSE,
+  .expand_retry_include_ip_address =   NULL,
+  .retry_include_ip_address =  TRUE,
 #ifdef SUPPORT_SOCKS
 #ifdef SUPPORT_SOCKS
- ,NULL                 /* socks_proxy */
+  .socks_proxy =               NULL,
 #endif
 #ifdef SUPPORT_TLS
 #endif
 #ifdef SUPPORT_TLS
- ,NULL,                /* tls_certificate */
-  NULL,                /* tls_crl */
-  NULL,                /* tls_privatekey */
-  NULL,                /* tls_require_ciphers */
-  NULL,                /* tls_sni */
-  US"system",          /* tls_verify_certificates */
-  EXIM_CLIENT_DH_DEFAULT_MIN_BITS,
-                       /* tls_dh_min_bits */
-  TRUE,                /* tls_tempfail_tryclear */
-  NULL,                /* tls_verify_hosts */
-  US"*",               /* tls_try_verify_hosts */
-  US"*"                /* tls_verify_cert_hostnames */
+  .tls_certificate =           NULL,
+  .tls_crl =                   NULL,
+  .tls_privatekey =            NULL,
+  .tls_require_ciphers =       NULL,
+  .tls_sni =                   NULL,
+  .tls_verify_certificates =   US"system",
+  .tls_dh_min_bits =           EXIM_CLIENT_DH_DEFAULT_MIN_BITS,
+  .tls_tempfail_tryclear =     TRUE,
+  .tls_verify_hosts =          NULL,
+  .tls_try_verify_hosts =      US"*",
+  .tls_verify_cert_hostnames = US"*",
+#endif
+#ifdef SUPPORT_I18N
+  .utf8_downconvert =          NULL,
 #endif
 #ifndef DISABLE_DKIM
 #endif
 #ifndef DISABLE_DKIM
- , {NULL,              /* dkim_canon */
-    NULL,              /* dkim_domain */
-    NULL,              /* dkim_private_key */
-    NULL,              /* dkim_selector */
-    NULL,              /* dkim_sign_headers */
-    NULL,              /* dkim_strict */
-    FALSE}            /* dot_stuffed */
+ .dkim =
+   {.dkim_domain =             NULL,
+    .dkim_identity =           NULL,
+    .dkim_private_key =                NULL,
+    .dkim_selector =           NULL,
+    .dkim_canon =              NULL,
+    .dkim_sign_headers =       NULL,
+    .dkim_strict =             NULL,
+    .dkim_hash =               US"sha256",
+    .dkim_timestamps =         NULL,
+    .dot_stuffed =             FALSE,
+    .force_bodyhash =          FALSE,
+# ifdef EXPERIMENTAL_ARC
+    .arc_signspec =            NULL,
+# endif
+    },
+# ifdef EXPERIMENTAL_ARC
+  .arc_sign =                  NULL,
+# endif
 #endif
 };
 
 #endif
 };
 
@@ -605,34 +648,34 @@ return FALSE;
 /* This writes to the main log and to the message log.
 
 Arguments:
 /* This writes to the main log and to the message log.
 
 Arguments:
-  addr     the address item containing error information
   host     the current host
   host     the current host
+  detail  the current message (addr_item->message)
+  basic_errno the errno (addr_item->basic_errno)
 
 Returns:   nothing
 */
 
 static void
 
 Returns:   nothing
 */
 
 static void
-write_logs(address_item *addr, host_item *host)
+write_logs(const host_item *host, const uschar *suffix, int basic_errno)
 {
 {
-uschar * message = LOGGING(outgoing_port)
+
+
+uschar *message = LOGGING(outgoing_port)
   ? string_sprintf("H=%s [%s]:%d", host->name, host->address,
                    host->port == PORT_NONE ? 25 : host->port)
   : string_sprintf("H=%s [%s]", host->name, host->address);
 
   ? string_sprintf("H=%s [%s]:%d", host->name, host->address,
                    host->port == PORT_NONE ? 25 : host->port)
   : string_sprintf("H=%s [%s]", host->name, host->address);
 
-if (addr->message)
+if (suffix)
   {
   {
-  message = string_sprintf("%s: %s", message, addr->message);
-  if (addr->basic_errno > 0)
-    message = string_sprintf("%s: %s", message, strerror(addr->basic_errno));
-  log_write(0, LOG_MAIN, "%s", message);
-  deliver_msglog("%s %s\n", tod_stamp(tod_log), message);
+  message = string_sprintf("%s: %s", message, suffix);
+  if (basic_errno > 0)
+    message = string_sprintf("%s: %s", message, strerror(basic_errno));
   }
 else
   }
 else
-  {
-  const uschar * s = exim_errstr(addr->basic_errno);
-  log_write(0, LOG_MAIN, "%s %s", message, s);
-  deliver_msglog("%s %s %s\n", tod_stamp(tod_log), message, s);
-  }
+  message = string_sprintf("%s %s", message, exim_errstr(basic_errno));
+
+log_write(0, LOG_MAIN, "%s", message);
+deliver_msglog("%s %s\n", tod_stamp(tod_log), message);
 }
 
 static void
 }
 
 static void
@@ -752,6 +795,7 @@ responses before returning, except after I/O errors and timeouts. */
 
 if (sx->pending_MAIL)
   {
 
 if (sx->pending_MAIL)
   {
+  DEBUG(D_transport) debug_printf("%s expect mail\n", __FUNCTION__);
   count--;
   if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
                          '2', ob->command_timeout))
   count--;
   if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
                          '2', ob->command_timeout))
@@ -803,6 +847,7 @@ while (count-- > 0)
   /* The address was accepted */
   addr->host_used = sx->host;
 
   /* The address was accepted */
   addr->host_used = sx->host;
 
+  DEBUG(D_transport) debug_printf("%s expect rcpt\n", __FUNCTION__);
   if (smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
                          '2', ob->command_timeout))
     {
   if (smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
                          '2', ob->command_timeout))
     {
@@ -920,25 +965,28 @@ if (addr) sx->sync_addr = addr->next;
 /* Handle a response to DATA. If we have not had any good recipients, either
 previously or in this block, the response is ignored. */
 
 /* Handle a response to DATA. If we have not had any good recipients, either
 previously or in this block, the response is ignored. */
 
-if (pending_DATA != 0 &&
-    !smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
-                       '3', ob->command_timeout))
+if (pending_DATA != 0)
   {
   {
-  int code;
-  uschar *msg;
-  BOOL pass_message;
-  if (pending_DATA > 0 || (yield & 1) != 0)
+  DEBUG(D_transport) debug_printf("%s expect data\n", __FUNCTION__);
+  if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+                       '3', ob->command_timeout))
     {
     {
-    if (errno == 0 && sx->buffer[0] == '4')
+    int code;
+    uschar *msg;
+    BOOL pass_message;
+    if (pending_DATA > 0 || (yield & 1) != 0)
       {
       {
-      errno = ERRNO_DATA4XX;
-      sx->first_addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
+      if (errno == 0 && sx->buffer[0] == '4')
+       {
+       errno = ERRNO_DATA4XX;
+       sx->first_addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
+       }
+      return -3;
       }
       }
-    return -3;
+    (void)check_response(sx->host, &errno, 0, sx->buffer, &code, &msg, &pass_message);
+    DEBUG(D_transport) debug_printf("%s\nerror for DATA ignored: pipelining "
+      "is in use and there were no good recipients\n", msg);
     }
     }
-  (void)check_response(sx->host, &errno, 0, sx->buffer, &code, &msg, &pass_message);
-  DEBUG(D_transport) debug_printf("%s\nerror for DATA ignored: pipelining "
-    "is in use and there were no good recipients\n", msg);
   }
 
 /* All responses read and handled; MAIL (if present) received 2xx and DATA (if
   }
 
 /* All responses read and handled; MAIL (if present) received 2xx and DATA (if
@@ -1006,7 +1054,7 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
     If one is found, attempt to authenticate by calling its client function.
     */
 
     If one is found, attempt to authenticate by calling its client function.
     */
 
-    for (au = auths; !smtp_authenticated && au != NULL; au = au->next)
+    for (au = auths; !smtp_authenticated && au; au = au->next)
       {
       uschar *p = names;
       if (!au->client ||
       {
       uschar *p = names;
       if (!au->client ||
@@ -1023,7 +1071,7 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
 
       /* Loop to scan supported server mechanisms */
 
 
       /* Loop to scan supported server mechanisms */
 
-      while (*p != 0)
+      while (*p)
        {
        int rc;
        int len = Ustrlen(au->public_name);
        {
        int rc;
        int len = Ustrlen(au->public_name);
@@ -1172,7 +1220,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
 /* Lookup TLSA record for host/port.
 Return:  OK            success with dnssec; DANE mode
          DEFER         Do not use this host now, may retry later
@@ -1199,14 +1247,15 @@ DEBUG(D_transport)
 
 switch (rc)
   {
 
 switch (rc)
   {
-  case DNS_SUCCEED:
-    if (sec) return OK;
-
-    log_write(0, LOG_MAIN, "DANE error: TLSA lookup not DNSSEC");
-    /*FALLTHROUGH*/
   case DNS_AGAIN:
     return DEFER; /* just defer this TLS'd conn */
 
   case DNS_AGAIN:
     return DEFER; /* just defer this TLS'd conn */
 
+  case DNS_SUCCEED:
+    if (sec) return OK;
+    log_write(0, LOG_MAIN,
+      "DANE error: TLSA lookup for %s not DNSSEC", host->name);
+    /*FALLTRHOUGH*/
+
   case DNS_NODATA:     /* no TLSA RR for this lookup */
   case DNS_NOMATCH:    /* no records at all for this lookup */
     return dane_required ? FAIL : FAIL_FORCED;
   case DNS_NODATA:     /* no TLSA RR for this lookup */
   case DNS_NOMATCH:    /* no records at all for this lookup */
     return dane_required ? FAIL : FAIL_FORCED;
@@ -1298,50 +1347,56 @@ return Ustrcmp(current_local_identity, message_local_identity) == 0;
 
 
 
 
 
 
-static uschar
-ehlo_response(uschar * buf, uschar checks)
+static unsigned
+ehlo_response(uschar * buf, unsigned checks)
 {
 size_t bsize = Ustrlen(buf);
 
 #ifdef SUPPORT_TLS
 {
 size_t bsize = Ustrlen(buf);
 
 #ifdef SUPPORT_TLS
-if (  checks & PEER_OFFERED_TLS
+if (  checks & OPTION_TLS
    && pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
    && pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
-  checks &= ~PEER_OFFERED_TLS;
+  checks &= ~OPTION_TLS;
+
+# ifdef EXPERIMENTAL_REQUIRETLS
+if (  checks & OPTION_REQUIRETLS
+   && pcre_exec(regex_REQUIRETLS, NULL, CS buf,bsize, 0, PCRE_EOPT, NULL,0) < 0)
+  checks &= ~OPTION_REQUIRETLS;
+# endif
 #endif
 
 #endif
 
-if (  checks & PEER_OFFERED_IGNQ
+if (  checks & OPTION_IGNQ
    && pcre_exec(regex_IGNOREQUOTA, NULL, CS buf, bsize, 0,
                PCRE_EOPT, NULL, 0) < 0)
    && pcre_exec(regex_IGNOREQUOTA, NULL, CS buf, bsize, 0,
                PCRE_EOPT, NULL, 0) < 0)
-  checks &= ~PEER_OFFERED_IGNQ;
+  checks &= ~OPTION_IGNQ;
 
 
-if (  checks & PEER_OFFERED_CHUNKING
+if (  checks & OPTION_CHUNKING
    && pcre_exec(regex_CHUNKING, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
    && pcre_exec(regex_CHUNKING, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
-  checks &= ~PEER_OFFERED_CHUNKING;
+  checks &= ~OPTION_CHUNKING;
 
 #ifndef DISABLE_PRDR
 
 #ifndef DISABLE_PRDR
-if (  checks & PEER_OFFERED_PRDR
+if (  checks & OPTION_PRDR
    && pcre_exec(regex_PRDR, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
    && pcre_exec(regex_PRDR, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
-  checks &= ~PEER_OFFERED_PRDR;
+  checks &= ~OPTION_PRDR;
 #endif
 
 #ifdef SUPPORT_I18N
 #endif
 
 #ifdef SUPPORT_I18N
-if (  checks & PEER_OFFERED_UTF8
+if (  checks & OPTION_UTF8
    && pcre_exec(regex_UTF8, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
    && pcre_exec(regex_UTF8, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
-  checks &= ~PEER_OFFERED_UTF8;
+  checks &= ~OPTION_UTF8;
 #endif
 
 #endif
 
-if (  checks & PEER_OFFERED_DSN
+if (  checks & OPTION_DSN
    && pcre_exec(regex_DSN, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
    && pcre_exec(regex_DSN, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
-  checks &= ~PEER_OFFERED_DSN;
+  checks &= ~OPTION_DSN;
 
 
-if (  checks & PEER_OFFERED_PIPE
+if (  checks & OPTION_PIPE
    && pcre_exec(regex_PIPELINING, NULL, CS buf, bsize, 0,
                PCRE_EOPT, NULL, 0) < 0)
    && pcre_exec(regex_PIPELINING, NULL, CS buf, bsize, 0,
                PCRE_EOPT, NULL, 0) < 0)
-  checks &= ~PEER_OFFERED_PIPE;
+  checks &= ~OPTION_PIPE;
 
 
-if (  checks & PEER_OFFERED_SIZE
+if (  checks & OPTION_SIZE
    && pcre_exec(regex_SIZE, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
    && pcre_exec(regex_SIZE, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
-  checks &= ~PEER_OFFERED_SIZE;
+  checks &= ~OPTION_SIZE;
 
 return checks;
 }
 
 return checks;
 }
@@ -1471,7 +1526,7 @@ Returns:          OK    - the connection was made and the delivery attempted;
 int
 smtp_setup_conn(smtp_context * sx, BOOL suppress_tls)
 {
 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;
 dns_answer tlsa_dnsa;
 #endif
 BOOL pass_message = FALSE;
@@ -1493,13 +1548,15 @@ sx->esmtp_sent = FALSE;
 sx->utf8_needed = FALSE;
 #endif
 sx->dsn_all_lasthop = TRUE;
 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 = FALSE;
-sx->dane_required = verify_check_given_host(&sx->ob->hosts_require_dane, sx->host) == OK;
+sx->dane_required =
+  verify_check_given_host(&sx->ob->hosts_require_dane, sx->host) == OK;
 #endif
 
 if ((sx->max_rcpt = sx->tblock->max_addresses) == 0) sx->max_rcpt = 999999;
 sx->peer_offered = 0;
 #endif
 
 if ((sx->max_rcpt = sx->tblock->max_addresses) == 0) sx->max_rcpt = 999999;
 sx->peer_offered = 0;
+sx->avoid_option = 0;
 sx->igquotstr = US"";
 if (!sx->helo_data) sx->helo_data = sx->ob->helo_data;
 #ifdef EXPERIMENTAL_DSN_INFO
 sx->igquotstr = US"";
 if (!sx->helo_data) sx->helo_data = sx->ob->helo_data;
 #ifdef EXPERIMENTAL_DSN_INFO
@@ -1566,7 +1623,7 @@ if (!continue_hostname)
 
   smtp_port_for_connect(sx->host, sx->port);
 
 
   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;
     /* Do TLSA lookup for DANE */
     {
     tls_out.dane_verified = FALSE;
@@ -1587,6 +1644,11 @@ if (!continue_hostname)
                                  string_sprintf("DANE error: tlsa lookup %s",
                                    rc == DEFER ? "DEFER" : "FAIL"),
                                  rc, FALSE);
                                  string_sprintf("DANE error: tlsa lookup %s",
                                    rc == DEFER ? "DEFER" : "FAIL"),
                                  rc, FALSE);
+# ifndef DISABLE_EVENT
+                               (void) event_raise(sx->tblock->event_action,
+                                 US"dane:fail", sx->dane_required
+                                   ?  US"dane-required" : US"dnssec-invalid");
+# endif
                                return rc;
          }
       }
                                return rc;
          }
       }
@@ -1595,6 +1657,10 @@ if (!continue_hostname)
       set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
        string_sprintf("DANE error: %s lookup not DNSSEC", sx->host->name),
        FAIL, FALSE);
       set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
        string_sprintf("DANE error: %s lookup not DNSSEC", sx->host->name),
        FAIL, FALSE);
+# ifndef DISABLE_EVENT
+      (void) event_raise(sx->tblock->event_action,
+       US"dane:fail", US"dane-required");
+# endif
       return FAIL;
       }
     }
       return FAIL;
       }
     }
@@ -1602,11 +1668,13 @@ if (!continue_hostname)
 
   /* Make the TCP connection */
 
 
   /* Make the TCP connection */
 
-  sx->inblock.sock = sx->outblock.sock =
+  sx->cctx.sock =
     smtp_connect(sx->host, sx->host_af, sx->interface,
                  sx->ob->connect_timeout, sx->tblock);
     smtp_connect(sx->host, sx->host_af, sx->interface,
                  sx->ob->connect_timeout, sx->tblock);
+  sx->cctx.tls_ctx = NULL;
+  sx->inblock.cctx = sx->outblock.cctx = &sx->cctx;
 
 
-  if (sx->inblock.sock < 0)
+  if (sx->cctx.sock < 0)
     {
     uschar * msg = NULL;
     if (sx->verify)
     {
     uschar * msg = NULL;
     if (sx->verify)
@@ -1659,7 +1727,7 @@ if (!continue_hostname)
     BOOL good_response;
 
 #ifdef TCP_QUICKACK
     BOOL good_response;
 
 #ifdef TCP_QUICKACK
-    (void) setsockopt(sx->inblock.sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
+    (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
 #endif
     good_response = smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
       '2', sx->ob->command_timeout);
 #endif
     good_response = smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
       '2', sx->ob->command_timeout);
@@ -1741,7 +1809,7 @@ goto SEND_QUIT;
 #ifdef SUPPORT_TLS
   if (sx->smtps)
     {
 #ifdef SUPPORT_TLS
   if (sx->smtps)
     {
-    smtp_peer_options |= PEER_OFFERED_TLS;
+    smtp_peer_options |= OPTION_TLS;
     suppress_tls = FALSE;
     sx->ob->tls_tempfail_tryclear = FALSE;
     smtp_command = US"SSL-on-connect";
     suppress_tls = FALSE;
     sx->ob->tls_tempfail_tryclear = FALSE;
     smtp_command = US"SSL-on-connect";
@@ -1801,23 +1869,23 @@ goto SEND_QUIT;
        errno = ERRNO_SMTPCLOSED;
        goto EHLOHELO_FAILED;
        }
        errno = ERRNO_SMTPCLOSED;
        goto EHLOHELO_FAILED;
        }
-      Ustrncpy(sx->buffer, rsp, sizeof(sx->buffer)/2);
+      memmove(sx->buffer, rsp, Ustrlen(rsp));
       goto RESPONSE_FAILED;
       }
     }
 
       goto RESPONSE_FAILED;
       }
     }
 
-  sx->peer_offered = smtp_peer_options = 0;
+  sx->avoid_option = sx->peer_offered = smtp_peer_options = 0;
 
   if (sx->esmtp || sx->lmtp)
     {
     sx->peer_offered = ehlo_response(sx->buffer,
 
   if (sx->esmtp || sx->lmtp)
     {
     sx->peer_offered = ehlo_response(sx->buffer,
-      PEER_OFFERED_TLS /* others checked later */
+      OPTION_TLS       /* others checked later */
       );
 
   /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
 
 #ifdef SUPPORT_TLS
       );
 
   /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
 
 #ifdef SUPPORT_TLS
-    smtp_peer_options |= sx->peer_offered & PEER_OFFERED_TLS;
+    smtp_peer_options |= sx->peer_offered & OPTION_TLS;
 #endif
     }
   }
 #endif
     }
   }
@@ -1838,16 +1906,18 @@ separate - we could match up by host ip+port as a bodge. */
 
 else
   {
 
 else
   {
-  if (cutthrough.fd >= 0 && cutthrough.callout_hold_only)
+  if (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only)
     {
     {
-    sx->inblock.sock = sx->outblock.sock = cutthrough.fd;
+    sx->cctx = cutthrough.cctx;
     sx->host->port = sx->port = cutthrough.host.port;
     }
   else
     {
     sx->host->port = sx->port = cutthrough.host.port;
     }
   else
     {
-    sx->inblock.sock = sx->outblock.sock = 0;  /* stdin */
-    sx->host->port = sx->port;    /* Record the port that was used */
+    sx->cctx.sock = 0;                         /* stdin */
+    sx->cctx.tls_ctx = NULL;
+    smtp_port_for_connect(sx->host, sx->port); /* Record the port that was used */
     }
     }
+  sx->inblock.cctx = sx->outblock.cctx = &sx->cctx;
   smtp_command = big_buffer;
   sx->helo_data = NULL;                /* ensure we re-expand ob->helo_data */
 
   smtp_command = big_buffer;
   sx->helo_data = NULL;                /* ensure we re-expand ob->helo_data */
 
@@ -1855,11 +1925,12 @@ else
   held-open verify connection with TLS, nothing more to do. */
 
   if (  continue_proxy_cipher
   held-open verify connection with TLS, nothing more to do. */
 
   if (  continue_proxy_cipher
-     || (cutthrough.fd >= 0 && cutthrough.callout_hold_only && cutthrough.is_tls)
+     || (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only
+         && cutthrough.is_tls)
      )
     {
     sx->peer_offered = smtp_peer_options;
      )
     {
     sx->peer_offered = smtp_peer_options;
-    pipelining_active = !!(smtp_peer_options & PEER_OFFERED_PIPE);
+    sx->pipelining_used = pipelining_active = !!(smtp_peer_options & OPTION_PIPE);
     HDEBUG(D_transport) debug_printf("continued connection, %s TLS\n",
       continue_proxy_cipher ? "proxied" : "verify conn with");
     return OK;
     HDEBUG(D_transport) debug_printf("continued connection, %s TLS\n",
       continue_proxy_cipher ? "proxied" : "verify conn with");
     return OK;
@@ -1876,7 +1947,7 @@ the client not be required to use TLS. If the response is bad, copy the buffer
 for error analysis. */
 
 #ifdef SUPPORT_TLS
 for error analysis. */
 
 #ifdef SUPPORT_TLS
-if (  smtp_peer_options & PEER_OFFERED_TLS
+if (  smtp_peer_options & OPTION_TLS
    && !suppress_tls
    && verify_check_given_host(&sx->ob->hosts_avoid_tls, sx->host) != OK
    && (  !sx->verify
    && !suppress_tls
    && verify_check_given_host(&sx->ob->hosts_avoid_tls, sx->host) != OK
    && (  !sx->verify
@@ -1915,22 +1986,30 @@ if (  smtp_peer_options & PEER_OFFERED_TLS
     {
     address_item * addr;
     uschar * errstr;
     {
     address_item * addr;
     uschar * errstr;
-    int rc = tls_client_start(sx->inblock.sock, sx->host, sx->addrlist, sx->tblock,
-# ifdef EXPERIMENTAL_DANE
+    sx->cctx.tls_ctx = tls_client_start(sx->cctx.sock, sx->host,
+                           sx->addrlist, sx->tblock,
+# ifdef SUPPORT_DANE
                             sx->dane ? &tlsa_dnsa : NULL,
 # endif
                             sx->dane ? &tlsa_dnsa : NULL,
 # endif
-                            &errstr);
-
-    /* TLS negotiation failed; give an error. From outside, this function may
-    be called again to try in clear on a new connection, if the options permit
-    it for this host. */
+                            &tls_out, &errstr);
 
 
-    if (rc != OK)
+    if (!sx->cctx.tls_ctx)
       {
       {
-# ifdef EXPERIMENTAL_DANE
-      if (sx->dane) log_write(0, LOG_MAIN,
+      /* TLS negotiation failed; give an error. From outside, this function may
+      be called again to try in clear on a new connection, if the options permit
+      it for this host. */
+
+# 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);
          "DANE attempt failed; TLS connection to %s [%s]: %s",
          sx->host->name, sx->host->address, errstr);
+#  ifndef DISABLE_EVENT
+       (void) event_raise(sx->tblock->event_action,
+         US"dane:fail", US"validation-failure");       /* could do with better detail */
+#  endif
+       }
 # endif
 
       errno = ERRNO_TLSFAILURE;
 # endif
 
       errno = ERRNO_TLSFAILURE;
@@ -1964,7 +2043,7 @@ another process, and so we won't have expanded helo_data above. We have to
 expand it here. $sending_ip_address and $sending_port are set up right at the
 start of the Exim process (in exim.c). */
 
 expand it here. $sending_ip_address and $sending_port are set up right at the
 start of the Exim process (in exim.c). */
 
-if (tls_out.active >= 0)
+if (tls_out.active.sock >= 0)
   {
   char *greeting_cmd;
   BOOL good_response;
   {
   char *greeting_cmd;
   BOOL good_response;
@@ -2014,16 +2093,30 @@ if (tls_out.active >= 0)
 have one. */
 
 else if (  sx->smtps
 have one. */
 
 else if (  sx->smtps
-# ifdef EXPERIMENTAL_DANE
+# ifdef SUPPORT_DANE
        || sx->dane
        || sx->dane
+# endif
+# ifdef EXPERIMENTAL_REQUIRETLS
+       || tls_requiretls & REQUIRETLS_MSG
 # endif
        || verify_check_given_host(&sx->ob->hosts_require_tls, sx->host) == OK
        )
   {
 # endif
        || verify_check_given_host(&sx->ob->hosts_require_tls, sx->host) == OK
        )
   {
-  errno = ERRNO_TLSREQUIRED;
+  errno =
+# ifdef EXPERIMENTAL_REQUIRETLS
+      tls_requiretls & REQUIRETLS_MSG ? ERRNO_REQUIRETLS :
+# endif
+      ERRNO_TLSREQUIRED;
   message = string_sprintf("a TLS session is required, but %s",
   message = string_sprintf("a TLS session is required, but %s",
-    smtp_peer_options & PEER_OFFERED_TLS
+    smtp_peer_options & OPTION_TLS
     ? "an attempt to start TLS failed" : "the server did not offer TLS support");
     ? "an attempt to start TLS failed" : "the server did not offer TLS support");
+# if defined(SUPPORT_DANE) && !defined(DISABLE_EVENT)
+  if (sx->dane)
+    (void) event_raise(sx->tblock->event_action, US"dane:fail",
+      smtp_peer_options & OPTION_TLS
+      ? US"validation-failure"         /* could do with better detail */
+      : US"starttls-not-supported");
+# endif
   goto TLS_FAILED;
   }
 #endif /*SUPPORT_TLS*/
   goto TLS_FAILED;
   }
 #endif /*SUPPORT_TLS*/
@@ -2035,7 +2128,7 @@ we skip this. */
 
 if (continue_hostname == NULL
 #ifdef SUPPORT_TLS
 
 if (continue_hostname == NULL
 #ifdef SUPPORT_TLS
-    || tls_out.active >= 0
+    || tls_out.active.sock >= 0
 #endif
     )
   {
 #endif
     )
   {
@@ -2043,60 +2136,73 @@ if (continue_hostname == NULL
     {
     sx->peer_offered = ehlo_response(sx->buffer,
        0 /* no TLS */
     {
     sx->peer_offered = ehlo_response(sx->buffer,
        0 /* no TLS */
-       | (sx->lmtp && sx->ob->lmtp_ignore_quota ? PEER_OFFERED_IGNQ : 0)
-       | PEER_OFFERED_CHUNKING
-       | PEER_OFFERED_PRDR
+       | (sx->lmtp && sx->ob->lmtp_ignore_quota ? OPTION_IGNQ : 0)
+       | OPTION_CHUNKING
+       | OPTION_PRDR
 #ifdef SUPPORT_I18N
 #ifdef SUPPORT_I18N
-       | (sx->addrlist->prop.utf8_msg ? PEER_OFFERED_UTF8 : 0)
+       | (sx->addrlist->prop.utf8_msg ? OPTION_UTF8 : 0)
          /*XXX if we hand peercaps on to continued-conn processes,
                must not depend on this addr */
 #endif
          /*XXX if we hand peercaps on to continued-conn processes,
                must not depend on this addr */
 #endif
-       | PEER_OFFERED_DSN
-       | PEER_OFFERED_PIPE
-       | (sx->ob->size_addition >= 0 ? PEER_OFFERED_SIZE : 0)
+       | OPTION_DSN
+       | OPTION_PIPE
+       | (sx->ob->size_addition >= 0 ? OPTION_SIZE : 0)
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+       | (tls_requiretls & REQUIRETLS_MSG ? OPTION_REQUIRETLS : 0)
+#endif
       );
 
     /* Set for IGNOREQUOTA if the response to LHLO specifies support and the
     lmtp_ignore_quota option was set. */
 
       );
 
     /* Set for IGNOREQUOTA if the response to LHLO specifies support and the
     lmtp_ignore_quota option was set. */
 
-    sx->igquotstr = sx->peer_offered & PEER_OFFERED_IGNQ ? US" IGNOREQUOTA" : US"";
+    sx->igquotstr = sx->peer_offered & OPTION_IGNQ ? US" IGNOREQUOTA" : US"";
 
     /* If the response to EHLO specified support for the SIZE parameter, note
     this, provided size_addition is non-negative. */
 
 
     /* If the response to EHLO specified support for the SIZE parameter, note
     this, provided size_addition is non-negative. */
 
-    smtp_peer_options |= sx->peer_offered & PEER_OFFERED_SIZE;
+    smtp_peer_options |= sx->peer_offered & OPTION_SIZE;
 
     /* Note whether the server supports PIPELINING. If hosts_avoid_esmtp matched
     the current host, esmtp will be false, so PIPELINING can never be used. If
     the current host matches hosts_avoid_pipelining, don't do it. */
 
 
     /* Note whether the server supports PIPELINING. If hosts_avoid_esmtp matched
     the current host, esmtp will be false, so PIPELINING can never be used. If
     the current host matches hosts_avoid_pipelining, don't do it. */
 
-    if (  sx->peer_offered & PEER_OFFERED_PIPE
+    if (  sx->peer_offered & OPTION_PIPE
        && verify_check_given_host(&sx->ob->hosts_avoid_pipelining, sx->host) != OK)
        && verify_check_given_host(&sx->ob->hosts_avoid_pipelining, sx->host) != OK)
-      smtp_peer_options |= PEER_OFFERED_PIPE;
+      smtp_peer_options |= OPTION_PIPE;
 
     DEBUG(D_transport) debug_printf("%susing PIPELINING\n",
 
     DEBUG(D_transport) debug_printf("%susing PIPELINING\n",
-      smtp_peer_options & PEER_OFFERED_PIPE ? "" : "not ");
+      smtp_peer_options & OPTION_PIPE ? "" : "not ");
 
 
-    if (  sx->peer_offered & PEER_OFFERED_CHUNKING
+    if (  sx->peer_offered & OPTION_CHUNKING
        && verify_check_given_host(&sx->ob->hosts_try_chunking, sx->host) != OK)
        && verify_check_given_host(&sx->ob->hosts_try_chunking, sx->host) != OK)
-      sx->peer_offered &= ~PEER_OFFERED_CHUNKING;
+      sx->peer_offered &= ~OPTION_CHUNKING;
 
 
-    if (sx->peer_offered & PEER_OFFERED_CHUNKING)
+    if (sx->peer_offered & OPTION_CHUNKING)
       {DEBUG(D_transport) debug_printf("CHUNKING usable\n");}
 
 #ifndef DISABLE_PRDR
       {DEBUG(D_transport) debug_printf("CHUNKING usable\n");}
 
 #ifndef DISABLE_PRDR
-    if (  sx->peer_offered & PEER_OFFERED_PRDR
+    if (  sx->peer_offered & OPTION_PRDR
        && verify_check_given_host(&sx->ob->hosts_try_prdr, sx->host) != OK)
        && verify_check_given_host(&sx->ob->hosts_try_prdr, sx->host) != OK)
-      sx->peer_offered &= ~PEER_OFFERED_PRDR;
+      sx->peer_offered &= ~OPTION_PRDR;
 
 
-    if (sx->peer_offered & PEER_OFFERED_PRDR)
+    if (sx->peer_offered & OPTION_PRDR)
       {DEBUG(D_transport) debug_printf("PRDR usable\n");}
 #endif
 
     /* Note if the server supports DSN */
       {DEBUG(D_transport) debug_printf("PRDR usable\n");}
 #endif
 
     /* Note if the server supports DSN */
-    smtp_peer_options |= sx->peer_offered & PEER_OFFERED_DSN;
+    smtp_peer_options |= sx->peer_offered & OPTION_DSN;
     DEBUG(D_transport) debug_printf("%susing DSN\n",
     DEBUG(D_transport) debug_printf("%susing DSN\n",
-                       sx->peer_offered & PEER_OFFERED_DSN ? "" : "not ");
+                       sx->peer_offered & OPTION_DSN ? "" : "not ");
+
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+    if (sx->peer_offered & OPTION_REQUIRETLS)
+      {
+      smtp_peer_options |= OPTION_REQUIRETLS;
+      DEBUG(D_transport) debug_printf(
+       tls_requiretls & REQUIRETLS_MSG
+       ? "using REQUIRETLS\n" : "REQUIRETLS offered\n");
+      }
+#endif
 
     /* Note if the response to EHLO specifies support for the AUTH extension.
     If it has, check that this host is one we want to authenticate to, and do
 
     /* Note if the response to EHLO specifies support for the AUTH extension.
     If it has, check that this host is one we want to authenticate to, and do
@@ -2113,7 +2219,7 @@ if (continue_hostname == NULL
       }
     }
   }
       }
     }
   }
-pipelining_active = !!(smtp_peer_options & PEER_OFFERED_PIPE);
+sx->pipelining_used = pipelining_active = !!(smtp_peer_options & OPTION_PIPE);
 
 /* The setting up of the SMTP call is now complete. Any subsequent errors are
 message-specific. */
 
 /* The setting up of the SMTP call is now complete. Any subsequent errors are
 message-specific. */
@@ -2123,6 +2229,38 @@ sx->setting_up = FALSE;
 #ifdef SUPPORT_I18N
 if (sx->addrlist->prop.utf8_msg)
   {
 #ifdef SUPPORT_I18N
 if (sx->addrlist->prop.utf8_msg)
   {
+  uschar * s;
+
+  /* If the transport sets a downconversion mode it overrides any set by ACL
+  for the message. */
+
+  if ((s = sx->ob->utf8_downconvert))
+    {
+    if (!(s = expand_string(s)))
+      {
+      message = string_sprintf("failed to expand utf8_downconvert: %s",
+        expand_string_message);
+      set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
+      yield = DEFER;
+      goto SEND_QUIT;
+      }
+    switch (*s)
+      {
+      case '1':        sx->addrlist->prop.utf8_downcvt = TRUE;
+               sx->addrlist->prop.utf8_downcvt_maybe = FALSE;
+               break;
+      case '0':        sx->addrlist->prop.utf8_downcvt = FALSE;
+               sx->addrlist->prop.utf8_downcvt_maybe = FALSE;
+               break;
+      case '-':        if (s[1] == '1')
+                 {
+                 sx->addrlist->prop.utf8_downcvt = FALSE;
+                 sx->addrlist->prop.utf8_downcvt_maybe = TRUE;
+                 }
+               break;
+      }
+    }
+
   sx->utf8_needed = !sx->addrlist->prop.utf8_downcvt
                    && !sx->addrlist->prop.utf8_downcvt_maybe;
   DEBUG(D_transport) if (!sx->utf8_needed)
   sx->utf8_needed = !sx->addrlist->prop.utf8_downcvt
                    && !sx->addrlist->prop.utf8_downcvt_maybe;
   DEBUG(D_transport) if (!sx->utf8_needed)
@@ -2131,11 +2269,27 @@ if (sx->addrlist->prop.utf8_msg)
   }
 
 /* If this is an international message we need the host to speak SMTPUTF8 */
   }
 
 /* If this is an international message we need the host to speak SMTPUTF8 */
-if (sx->utf8_needed && !(sx->peer_offered & PEER_OFFERED_UTF8))
+if (sx->utf8_needed && !(sx->peer_offered & OPTION_UTF8))
   {
   errno = ERRNO_UTF8_FWD;
   goto RESPONSE_FAILED;
   }
   {
   errno = ERRNO_UTF8_FWD;
   goto RESPONSE_FAILED;
   }
+#endif /*SUPPORT_I18N*/
+
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+  /*XXX should tls_requiretls actually be per-addr? */
+
+if (  tls_requiretls & REQUIRETLS_MSG
+   && !(sx->peer_offered & OPTION_REQUIRETLS)
+   )
+  {
+  sx->setting_up = TRUE;
+  errno = ERRNO_REQUIRETLS;
+  message = US"REQUIRETLS support is required from the server"
+    " but it was not offered";
+  DEBUG(D_transport) debug_printf("%s\n", message);
+  goto TLS_FAILED;
+  }
 #endif
 
 return OK;
 #endif
 
 return OK;
@@ -2148,6 +2302,7 @@ return OK;
     message = NULL;
     sx->send_quit = check_response(sx->host, &errno, sx->addrlist->more_errno,
       sx->buffer, &code, &message, &pass_message);
     message = NULL;
     sx->send_quit = check_response(sx->host, &errno, sx->addrlist->more_errno,
       sx->buffer, &code, &message, &pass_message);
+    yield = DEFER;
     goto FAILED;
 
   SEND_FAILED:
     goto FAILED;
 
   SEND_FAILED:
@@ -2155,43 +2310,59 @@ return OK;
     message = US string_sprintf("send() to %s [%s] failed: %s",
       sx->host->name, sx->host->address, strerror(errno));
     sx->send_quit = FALSE;
     message = US string_sprintf("send() to %s [%s] failed: %s",
       sx->host->name, sx->host->address, strerror(errno));
     sx->send_quit = FALSE;
+    yield = DEFER;
     goto FAILED;
 
     goto FAILED;
 
-  /* This label is jumped to directly when a TLS negotiation has failed,
-  or was not done for a host for which it is required. Values will be set
-  in message and errno, and setting_up will always be true. Treat as
-  a temporary error. */
-
   EHLOHELO_FAILED:
     code = '4';
     message = string_sprintf("Remote host closed connection in response to %s"
       " (EHLO response was: %s)", smtp_command, sx->buffer);
     sx->send_quit = FALSE;
   EHLOHELO_FAILED:
     code = '4';
     message = string_sprintf("Remote host closed connection in response to %s"
       " (EHLO response was: %s)", smtp_command, sx->buffer);
     sx->send_quit = FALSE;
+    yield = DEFER;
     goto FAILED;
 
     goto FAILED;
 
+  /* This label is jumped to directly when a TLS negotiation has failed,
+  or was not done for a host for which it is required. Values will be set
+  in message and errno, and setting_up will always be true. Treat as
+  a temporary error. */
+
 #ifdef SUPPORT_TLS
   TLS_FAILED:
 #ifdef SUPPORT_TLS
   TLS_FAILED:
-    code = '4';
+# ifdef EXPERIMENTAL_REQUIRETLS
+    if (errno == ERRNO_REQUIRETLS)
+      code = '5', yield = FAIL;
+      /*XXX DSN will be labelled 500; prefer 530 5.7.4 */
+    else
+# endif
+      code = '4', yield = DEFER;
     goto FAILED;
 #endif
 
   /* The failure happened while setting up the call; see if the failure was
   a 5xx response (this will either be on connection, or following HELO - a 5xx
     goto FAILED;
 #endif
 
   /* The failure happened while setting up the call; see if the failure was
   a 5xx response (this will either be on connection, or following HELO - a 5xx
-  after EHLO causes it to try HELO). If so, fail all addresses, as this host is
-  never going to accept them. For other errors during setting up (timeouts or
-  whatever), defer all addresses, and yield DEFER, so that the host is not
-  tried again for a while. */
+  after EHLO causes it to try HELO). If so, and there are no more hosts to try,
+  fail all addresses, as this host is never going to accept them. For other
+  errors during setting up (timeouts or whatever), defer all addresses, and
+  yield DEFER, so that the host is not tried again for a while.
+
+  XXX This peeking for another host feels like a layering violation. We want
+  to note the host as unusable, but down here we shouldn't know if this was
+  the last host to try for the addr(list).  Perhaps the upper layer should be
+  the one to do set_errno() ?  The problem is that currently the addr is where
+  errno etc. are stashed, but until we run out of hosts to try the errors are
+  host-specific.  Maybe we should enhance the host_item definition? */
 
 FAILED:
   sx->ok = FALSE;                /* For when reached by GOTO */
 
 FAILED:
   sx->ok = FALSE;                /* For when reached by GOTO */
-
-  yield = code == '5'
+  set_errno(sx->addrlist, errno, message,
+           sx->host->next
+           ? DEFER
+           : code == '5'
 #ifdef SUPPORT_I18N
 #ifdef SUPPORT_I18N
-         || errno == ERRNO_UTF8_FWD
+                       || errno == ERRNO_UTF8_FWD
 #endif
 #endif
-    ? FAIL : DEFER;
-
-  set_errno(sx->addrlist, errno, message, yield, pass_message, sx->host
+           ? FAIL : DEFER,
+           pass_message, sx->host
 #ifdef EXPERIMENTAL_DSN_INFO
            , sx->smtp_greeting, sx->helo_response
 #endif
 #ifdef EXPERIMENTAL_DSN_INFO
            , sx->smtp_greeting, sx->helo_response
 #endif
@@ -2205,7 +2376,11 @@ if (sx->send_quit)
   (void)smtp_write_command(&sx->outblock, SCMD_FLUSH, "QUIT\r\n");
 
 #ifdef SUPPORT_TLS
   (void)smtp_write_command(&sx->outblock, SCMD_FLUSH, "QUIT\r\n");
 
 #ifdef SUPPORT_TLS
-tls_close(FALSE, TRUE);
+if (sx->cctx.tls_ctx)
+  {
+  tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
+  sx->cctx.tls_ctx = NULL;
+  }
 #endif
 
 /* Close the socket, and return the appropriate value, first setting
 #endif
 
 /* Close the socket, and return the appropriate value, first setting
@@ -2216,14 +2391,14 @@ remote_max_parallel is forced to 1 when delivering over an existing connection,
 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
 if (sx->send_quit)
   {
 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
 if (sx->send_quit)
   {
-  shutdown(sx->outblock.sock, SHUT_WR);
-  if (fcntl(sx->inblock.sock, F_SETFL, O_NONBLOCK) == 0)
-    for (rc = 16; read(sx->inblock.sock, sx->inbuffer, sizeof(sx->inbuffer)) > 0 && rc > 0;)
+  shutdown(sx->cctx.sock, SHUT_WR);
+  if (fcntl(sx->cctx.sock, F_SETFL, O_NONBLOCK) == 0)
+    for (rc = 16; read(sx->cctx.sock, sx->inbuffer, sizeof(sx->inbuffer)) > 0 && rc > 0;)
       rc--;                            /* drain socket */
   sx->send_quit = FALSE;
   }
       rc--;                            /* drain socket */
   sx->send_quit = FALSE;
   }
-(void)close(sx->inblock.sock);
-sx->inblock.sock = sx->outblock.sock = -1;
+(void)close(sx->cctx.sock);
+sx->cctx.sock = -1;
 
 #ifndef DISABLE_EVENT
 (void) event_raise(sx->tblock->event_action, US"tcp:close", NULL);
 
 #ifndef DISABLE_EVENT
 (void) event_raise(sx->tblock->event_action, US"tcp:close", NULL);
@@ -2249,14 +2424,15 @@ int address_count;
 
 *p = 0;
 
 
 *p = 0;
 
-/* If we know the receiving MTA supports the SIZE qualification,
+/* If we know the receiving MTA supports the SIZE qualification, and we know it,
 send it, adding something to the message size to allow for imprecision
 and things that get added en route. Exim keeps the number of lines
 in a message, so we can give an accurate value for the original message, but we
 need some additional to handle added headers. (Double "." characters don't get
 included in the count.) */
 
 send it, adding something to the message size to allow for imprecision
 and things that get added en route. Exim keeps the number of lines
 in a message, so we can give an accurate value for the original message, but we
 need some additional to handle added headers. (Double "." characters don't get
 included in the count.) */
 
-if (sx->peer_offered & PEER_OFFERED_SIZE)
+if (  message_size > 0
+   && sx->peer_offered & OPTION_SIZE && !(sx->avoid_option & OPTION_SIZE))
   {
 /*XXX problem here under spool_files_wireformat?
 Or just forget about lines?  Or inflate by a fixed proportion? */
   {
 /*XXX problem here under spool_files_wireformat?
 Or just forget about lines?  Or inflate by a fixed proportion? */
@@ -2270,7 +2446,7 @@ Or just forget about lines?  Or inflate by a fixed proportion? */
 request that */
 
 sx->prdr_active = FALSE;
 request that */
 
 sx->prdr_active = FALSE;
-if (sx->peer_offered & PEER_OFFERED_PRDR)
+if (sx->peer_offered & OPTION_PRDR)
   for (addr = addrlist; addr; addr = addr->next)
     if (addr->transport_return == PENDING_DEFER)
       {
   for (addr = addrlist; addr; addr = addr->next)
     if (addr->transport_return == PENDING_DEFER)
       {
@@ -2289,13 +2465,18 @@ if (sx->peer_offered & PEER_OFFERED_PRDR)
 /* If it supports internationalised messages, and this meesage need that,
 request it */
 
 /* If it supports internationalised messages, and this meesage need that,
 request it */
 
-if (  sx->peer_offered & PEER_OFFERED_UTF8
+if (  sx->peer_offered & OPTION_UTF8
    && addrlist->prop.utf8_msg
    && !addrlist->prop.utf8_downcvt
    )
   Ustrcpy(p, " SMTPUTF8"), p += 9;
 #endif
 
    && addrlist->prop.utf8_msg
    && !addrlist->prop.utf8_downcvt
    )
   Ustrcpy(p, " SMTPUTF8"), p += 9;
 #endif
 
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+if (tls_requiretls & REQUIRETLS_MSG)
+  Ustrcpy(p, " REQUIRETLS") , p += 11;
+#endif
+
 /* check if all addresses have DSN-lasthop flag; do not send RET and ENVID if so */
 for (sx->dsn_all_lasthop = TRUE, addr = addrlist, address_count = 0;
      addr && address_count < sx->max_rcpt;
 /* check if all addresses have DSN-lasthop flag; do not send RET and ENVID if so */
 for (sx->dsn_all_lasthop = TRUE, addr = addrlist, address_count = 0;
      addr && address_count < sx->max_rcpt;
@@ -2311,7 +2492,7 @@ for (sx->dsn_all_lasthop = TRUE, addr = addrlist, address_count = 0;
 
 /* Add any DSN flags to the mail command */
 
 
 /* Add any DSN flags to the mail command */
 
-if (sx->peer_offered & PEER_OFFERED_DSN && !sx->dsn_all_lasthop)
+if (sx->peer_offered & OPTION_DSN && !sx->dsn_all_lasthop)
   {
   if (dsn_ret == dsn_ret_hdrs)
     { Ustrcpy(p, " RET=HDRS"); p += 9; }
   {
   if (dsn_ret == dsn_ret_hdrs)
     { Ustrcpy(p, " RET=HDRS"); p += 9; }
@@ -2347,7 +2528,7 @@ uschar * p = sx->buffer;
 
 /* Add any DSN flags to the rcpt command */
 
 
 /* Add any DSN flags to the rcpt command */
 
-if (sx->peer_offered & PEER_OFFERED_DSN && !(addr->dsn_flags & rf_dsnlasthop))
+if (sx->peer_offered & OPTION_DSN && !(addr->dsn_flags & rf_dsnlasthop))
   {
   if (addr->dsn_flags & rf_dsnflags)
     {
   {
   if (addr->dsn_flags & rf_dsnflags)
     {
@@ -2417,7 +2598,7 @@ sx->pending_MAIL = TRUE;     /* The block starts with MAIL */
   the delivery log line. */
 
   if (  sx->addrlist->prop.utf8_msg
   the delivery log line. */
 
   if (  sx->addrlist->prop.utf8_msg
-     && (sx->addrlist->prop.utf8_downcvt || !(sx->peer_offered & PEER_OFFERED_UTF8))
+     && (sx->addrlist->prop.utf8_downcvt || !(sx->peer_offered & OPTION_UTF8))
      )
     {
     if (s = string_address_utf8_to_alabel(s, &errstr), errstr)
      )
     {
     if (s = string_address_utf8_to_alabel(s, &errstr), errstr)
@@ -2481,7 +2662,13 @@ for (addr = sx->first_addr, address_count = 0;
   BOOL no_flush;
   uschar * rcpt_addr;
 
   BOOL no_flush;
   uschar * rcpt_addr;
 
-  addr->dsn_aware = sx->peer_offered & PEER_OFFERED_DSN
+  if (tcp_out_fastopen && !tcp_out_fastopen_logged)
+    {
+    setflag(addr, af_tcp_fastopen_conn);
+    if (tcp_out_fastopen > 1) setflag(addr, af_tcp_fastopen);
+    }
+
+  addr->dsn_aware = sx->peer_offered & OPTION_DSN
     ? dsn_support_yes : dsn_support_no;
 
   address_count++;
     ? dsn_support_yes : dsn_support_no;
 
   address_count++;
@@ -2534,6 +2721,7 @@ for (addr = sx->first_addr, address_count = 0;
     }
   }      /* Loop for next address */
 
     }
   }      /* Loop for next address */
 
+tcp_out_fastopen_logged = TRUE;
 sx->next_addr = addr;
 return 0;
 }
 sx->next_addr = addr;
 return 0;
 }
@@ -2544,29 +2732,40 @@ return 0;
 * Proxy TLS connection for another transport process *
 ******************************************************/
 /*
 * Proxy TLS connection for another transport process *
 ******************************************************/
 /*
-Use the given buffer as a staging area, and select on both the given fd
-and the TLS'd client-fd for data to read (per the coding in ip_recv() and
-fd_ready() this is legitimate).  Do blocking full-size writes, and reads
-under a timeout.
+Close the unused end of the pipe, fork once more, then use the given buffer
+as a staging area, and select on both the given fd and the TLS'd client-fd for
+data to read (per the coding in ip_recv() and fd_ready() this is legitimate).
+Do blocking full-size writes, and reads under a timeout.  Once both input
+channels are closed, exit the process.
 
 Arguments:
 
 Arguments:
+  ct_ctx       tls context
   buf          space to use for buffering
   bufsiz       size of buffer
   buf          space to use for buffering
   bufsiz       size of buffer
-  proxy_fd     comms to proxied process
+  pfd          pipe filedescriptor array; [0] is comms to proxied process
   timeout      per-read timeout, seconds
 */
 
 void
   timeout      per-read timeout, seconds
 */
 
 void
-smtp_proxy_tls(uschar * buf, size_t bsize, int proxy_fd, int timeout)
+smtp_proxy_tls(void * ct_ctx, uschar * buf, size_t bsize, int * pfd,
+  int timeout)
 {
 {
-fd_set fds;
-int max_fd = MAX(proxy_fd, tls_out.active) + 1;
+fd_set rfds, efds;
+int max_fd = MAX(pfd[0], tls_out.active.sock) + 1;
 int rc, i, fd_bits, nbytes;
 
 int rc, i, fd_bits, nbytes;
 
+close(pfd[1]);
+if ((rc = fork()))
+  {
+  DEBUG(D_transport) debug_printf("proxy-proc final-pid %d\n", rc);
+  _exit(rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+  }
+
+if (running_in_test_harness) millisleep(100); /* let parent debug out */
 set_process_info("proxying TLS connection for continued transport");
 set_process_info("proxying TLS connection for continued transport");
-FD_ZERO(&fds);
-FD_SET(tls_out.active, &fds);
-FD_SET(proxy_fd, &fds);
+FD_ZERO(&rfds);
+FD_SET(tls_out.active.sock, &rfds);
+FD_SET(pfd[0], &rfds);
 
 for (fd_bits = 3; fd_bits; )
   {
 
 for (fd_bits = 3; fd_bits; )
   {
@@ -2574,11 +2773,13 @@ for (fd_bits = 3; fd_bits; )
   time_t time_start = time(NULL);
 
   /* wait for data */
   time_t time_start = time(NULL);
 
   /* wait for data */
+  efds = rfds;
   do
     {
     struct timeval tv = { time_left, 0 };
 
   do
     {
     struct timeval tv = { time_left, 0 };
 
-    rc = select(max_fd, (SELECT_ARG2_TYPE *)&fds, NULL, NULL, &tv);
+    rc = select(max_fd,
+      (SELECT_ARG2_TYPE *)&rfds, NULL, (SELECT_ARG2_TYPE *)&efds, &tv);
 
     if (rc < 0 && errno == EINTR)
       if ((time_left -= time(NULL) - time_start) > 0) continue;
 
     if (rc < 0 && errno == EINTR)
       if ((time_left -= time(NULL) - time_start) > 0) continue;
@@ -2586,44 +2787,56 @@ for (fd_bits = 3; fd_bits; )
     if (rc <= 0)
       {
       DEBUG(D_transport) if (rc == 0) debug_printf("%s: timed out\n", __FUNCTION__);
     if (rc <= 0)
       {
       DEBUG(D_transport) if (rc == 0) debug_printf("%s: timed out\n", __FUNCTION__);
-      return;
+      goto done;
+      }
+
+    if (FD_ISSET(tls_out.active.sock, &efds) || FD_ISSET(pfd[0], &efds))
+      {
+      DEBUG(D_transport) debug_printf("select: exceptional cond on %s fd\n",
+       FD_ISSET(pfd[0], &efds) ? "proxy" : "tls");
+      goto done;
       }
     }
       }
     }
-  while (rc < 0 || !(FD_ISSET(tls_out.active, &fds) || FD_ISSET(proxy_fd, &fds)));
+  while (rc < 0 || !(FD_ISSET(tls_out.active.sock, &rfds) || FD_ISSET(pfd[0], &rfds)));
 
   /* handle inbound data */
 
   /* handle inbound data */
-  if (FD_ISSET(tls_out.active, &fds))
-    if ((rc = tls_read(FALSE, buf, bsize)) <= 0)
+  if (FD_ISSET(tls_out.active.sock, &rfds))
+    if ((rc = tls_read(ct_ctx, buf, bsize)) <= 0)
       {
       fd_bits &= ~1;
       {
       fd_bits &= ~1;
-      FD_CLR(tls_out.active, &fds);
-      shutdown(proxy_fd, SHUT_WR);
+      FD_CLR(tls_out.active.sock, &rfds);
+      shutdown(pfd[0], SHUT_WR);
+      timeout = 5;
       }
     else
       {
       for (nbytes = 0; rc - nbytes > 0; nbytes += i)
       }
     else
       {
       for (nbytes = 0; rc - nbytes > 0; nbytes += i)
-       if ((i = write(proxy_fd, buf + nbytes, rc - nbytes)) < 0) return;
+       if ((i = write(pfd[0], buf + nbytes, rc - nbytes)) < 0) goto done;
       }
   else if (fd_bits & 1)
       }
   else if (fd_bits & 1)
-    FD_SET(tls_out.active, &fds);
+    FD_SET(tls_out.active.sock, &rfds);
 
   /* handle outbound data */
 
   /* handle outbound data */
-  if (FD_ISSET(proxy_fd, &fds))
-    if ((rc = read(proxy_fd, buf, bsize)) <= 0)
+  if (FD_ISSET(pfd[0], &rfds))
+    if ((rc = read(pfd[0], buf, bsize)) <= 0)
       {
       {
-      fd_bits &= ~2;
-      FD_CLR(proxy_fd, &fds);
-      shutdown(tls_out.active, SHUT_WR);
+      fd_bits = 0;
+      tls_close(ct_ctx, TLS_SHUTDOWN_NOWAIT);
+      ct_ctx = NULL;
       }
     else
       {
       for (nbytes = 0; rc - nbytes > 0; nbytes += i)
       }
     else
       {
       for (nbytes = 0; rc - nbytes > 0; nbytes += i)
-       if ((i = tls_write(FALSE, buf + nbytes, rc - nbytes, FALSE)) < 0)
-         return;
+       if ((i = tls_write(ct_ctx, buf + nbytes, rc - nbytes, FALSE)) < 0)
+         goto done;
       }
   else if (fd_bits & 2)
       }
   else if (fd_bits & 2)
-    FD_SET(proxy_fd, &fds);
+    FD_SET(pfd[0], &rfds);
   }
   }
+
+done:
+  if (running_in_test_harness) millisleep(100);        /* let logging complete */
+  exim_exit(0, US"TLS proxy");
 }
 #endif
 
 }
 #endif
 
@@ -2651,7 +2864,8 @@ Arguments:
                   failed by one of them.
   host            host to deliver to
   host_af         AF_INET or AF_INET6
                   failed by one of them.
   host            host to deliver to
   host_af         AF_INET or AF_INET6
-  port            default TCP/IP port to use, in host byte order
+  defport         default TCP/IP port to use if host does not specify, in host
+                 byte order
   interface       interface to bind to, or NULL
   tblock          transport instance block
   message_defer   set TRUE if yield is OK, but all addresses were deferred
   interface       interface to bind to, or NULL
   tblock          transport instance block
   message_defer   set TRUE if yield is OK, but all addresses were deferred
@@ -2673,7 +2887,7 @@ Returns:          OK    - the connection was made and the delivery attempted;
 */
 
 static int
 */
 
 static int
-smtp_deliver(address_item *addrlist, host_item *host, int host_af, int port,
+smtp_deliver(address_item *addrlist, host_item *host, int host_af, int defport,
   uschar *interface, transport_instance *tblock,
   BOOL *message_defer, BOOL suppress_tls)
 {
   uschar *interface, transport_instance *tblock,
   BOOL *message_defer, BOOL suppress_tls)
 {
@@ -2681,22 +2895,22 @@ address_item *addr;
 int yield = OK;
 int save_errno;
 int rc;
 int yield = OK;
 int save_errno;
 int rc;
-time_t start_delivery_time = time(NULL);
+struct timeval start_delivery_time;
 
 BOOL pass_message = FALSE;
 uschar *message = NULL;
 uschar new_message_id[MESSAGE_ID_LENGTH + 1];
 
 BOOL pass_message = FALSE;
 uschar *message = NULL;
 uschar new_message_id[MESSAGE_ID_LENGTH + 1];
-uschar *p;
 
 smtp_context sx;
 
 
 smtp_context sx;
 
+gettimeofday(&start_delivery_time, NULL);
 suppress_tls = suppress_tls;  /* stop compiler warning when no TLS support */
 *message_defer = FALSE;
 
 sx.addrlist = addrlist;
 sx.host = host;
 sx.host_af = host_af,
 suppress_tls = suppress_tls;  /* stop compiler warning when no TLS support */
 *message_defer = FALSE;
 
 sx.addrlist = addrlist;
 sx.host = host;
 sx.host_af = host_af,
-sx.port = port;
+sx.port = defport;
 sx.interface = interface;
 sx.helo_data = NULL;
 sx.tblock = tblock;
 sx.interface = interface;
 sx.helo_data = NULL;
 sx.tblock = tblock;
@@ -2731,10 +2945,10 @@ if (tblock->filter_command)
   if (  transport_filter_argv
      && *transport_filter_argv
      && **transport_filter_argv
   if (  transport_filter_argv
      && *transport_filter_argv
      && **transport_filter_argv
-     && sx.peer_offered & PEER_OFFERED_CHUNKING
+     && sx.peer_offered & OPTION_CHUNKING
      )
     {
      )
     {
-    sx.peer_offered &= ~PEER_OFFERED_CHUNKING;
+    sx.peer_offered &= ~OPTION_CHUNKING;
     DEBUG(D_transport) debug_printf("CHUNKING not usable due to transport filter\n");
     }
   }
     DEBUG(D_transport) debug_printf("CHUNKING not usable due to transport filter\n");
     }
   }
@@ -2816,7 +3030,7 @@ are pipelining. The responses are all handled by sync_responses().
 If using CHUNKING, do not send a BDAT until we know how big a chunk we want
 to send is. */
 
 If using CHUNKING, do not send a BDAT until we know how big a chunk we want
 to send is. */
 
-if (  !(sx.peer_offered & PEER_OFFERED_CHUNKING)
+if (  !(sx.peer_offered & OPTION_CHUNKING)
    && (sx.ok || (pipelining_active && !mua_wrapper)))
   {
   int count = smtp_write_command(&sx.outblock, SCMD_FLUSH, "DATA\r\n");
    && (sx.ok || (pipelining_active && !mua_wrapper)))
   {
   int count = smtp_write_command(&sx.outblock, SCMD_FLUSH, "DATA\r\n");
@@ -2846,7 +3060,7 @@ for handling the SMTP dot-handling protocol, flagging to apply to headers as
 well as body. Set the appropriate timeout value to be used for each chunk.
 (Haven't been able to make it work using select() for writing yet.) */
 
 well as body. Set the appropriate timeout value to be used for each chunk.
 (Haven't been able to make it work using select() for writing yet.) */
 
-if (!(sx.peer_offered & PEER_OFFERED_CHUNKING) && !sx.ok)
+if (!(sx.peer_offered & OPTION_CHUNKING) && !sx.ok)
   {
   /* Save the first address of the next batch. */
   sx.first_addr = sx.next_addr;
   {
   /* Save the first address of the next batch. */
   sx.first_addr = sx.next_addr;
@@ -2856,7 +3070,7 @@ if (!(sx.peer_offered & PEER_OFFERED_CHUNKING) && !sx.ok)
 else
   {
   transport_ctx tctx = {
 else
   {
   transport_ctx tctx = {
-    sx.inblock.sock,
+    {sx.cctx.sock},            /*XXX will this need TLS info? */
     tblock,
     addrlist,
     US".", US"..",    /* Escaping strings */
     tblock,
     addrlist,
     US".", US"..",    /* Escaping strings */
@@ -2873,7 +3087,7 @@ else
   of responses.  The callback needs a whole bunch of state so set up
   a transport-context structure to be passed around. */
 
   of responses.  The callback needs a whole bunch of state so set up
   a transport-context structure to be passed around. */
 
-  if (sx.peer_offered & PEER_OFFERED_CHUNKING)
+  if (sx.peer_offered & OPTION_CHUNKING)
     {
     tctx.check_string = tctx.escape_string = NULL;
     tctx.options |= topt_use_bdat;
     {
     tctx.check_string = tctx.escape_string = NULL;
     tctx.options |= topt_use_bdat;
@@ -2898,13 +3112,37 @@ else
   transport_write_timeout = sx.ob->data_timeout;
   smtp_command = US"sending data block";   /* For error messages */
   DEBUG(D_transport|D_v)
   transport_write_timeout = sx.ob->data_timeout;
   smtp_command = US"sending data block";   /* For error messages */
   DEBUG(D_transport|D_v)
-    if (sx.peer_offered & PEER_OFFERED_CHUNKING)
+    if (sx.peer_offered & OPTION_CHUNKING)
       debug_printf("         will write message using CHUNKING\n");
     else
       debug_printf("  SMTP>> writing message and terminating \".\"\n");
   transport_count = 0;
 
 #ifndef DISABLE_DKIM
       debug_printf("         will write message using CHUNKING\n");
     else
       debug_printf("  SMTP>> writing message and terminating \".\"\n");
   transport_count = 0;
 
 #ifndef DISABLE_DKIM
+  dkim_exim_sign_init();
+# ifdef EXPERIMENTAL_ARC
+    {
+    uschar * s = sx.ob->arc_sign;
+    if (s)
+      {
+      if (!(sx.ob->dkim.arc_signspec = s = expand_string(s)))
+       {
+       if (!expand_string_forcedfail)
+         {
+         message = US"failed to expand arc_sign";
+         sx.ok = FALSE;
+         goto SEND_FAILED;
+         }
+       }
+      else if (*s)
+       {
+       /* Ask dkim code to hash the body for ARC */
+       (void) arc_ams_setup_sign_bodyhash();
+       sx.ob->dkim.force_bodyhash = TRUE;
+       }
+      }
+    }
+# endif
   sx.ok = dkim_transport_write_message(&tctx, &sx.ob->dkim, CUSS &message);
 #else
   sx.ok = transport_write_message(&tctx, 0);
   sx.ok = dkim_transport_write_message(&tctx, &sx.ob->dkim, CUSS &message);
 #else
   sx.ok = transport_write_message(&tctx, 0);
@@ -2933,7 +3171,7 @@ else
 
   smtp_command = US"end of data";
 
 
   smtp_command = US"end of data";
 
-  if (sx.peer_offered & PEER_OFFERED_CHUNKING && sx.cmd_count > 1)
+  if (sx.peer_offered & OPTION_CHUNKING && sx.cmd_count > 1)
     {
     /* Reap any outstanding MAIL & RCPT commands, but not a DATA-go-ahead */
     switch(sync_responses(&sx, sx.cmd_count-1, 0))
     {
     /* Reap any outstanding MAIL & RCPT commands, but not a DATA-go-ahead */
     switch(sync_responses(&sx, sx.cmd_count-1, 0))
@@ -3003,10 +3241,11 @@ else
   if (sx.ok)
     {
     int flag = '=';
   if (sx.ok)
     {
     int flag = '=';
-    int delivery_time = (int)(time(NULL) - start_delivery_time);
+    struct timeval delivery_time;
     int len;
     int len;
-    uschar *conf = NULL;
+    uschar * conf = NULL;
 
 
+    timesince(&delivery_time, &start_delivery_time);
     sx.send_rset = FALSE;
     pipelining_active = FALSE;
 
     sx.send_rset = FALSE;
     pipelining_active = FALSE;
 
@@ -3021,7 +3260,7 @@ else
       {
       const uschar *s = string_printing(sx.buffer);
       /* deconst cast ok here as string_printing was checked to have alloc'n'copied */
       {
       const uschar *s = string_printing(sx.buffer);
       /* deconst cast ok here as string_printing was checked to have alloc'n'copied */
-      conf = (s == sx.buffer)? (uschar *)string_copy(s) : US s;
+      conf = (s == sx.buffer)? US string_copy(s) : US s;
       }
 
     /* Process all transported addresses - for LMTP or PRDR, read a status for
       }
 
     /* Process all transported addresses - for LMTP or PRDR, read a status for
@@ -3081,14 +3320,17 @@ else
       actual host that was used. */
 
       addr->transport_return = OK;
       actual host that was used. */
 
       addr->transport_return = OK;
-      addr->more_errno = delivery_time;
+      addr->more_errno = delivery_time.tv_sec;
+      addr->delivery_usec = delivery_time.tv_usec;
       addr->host_used = host;
       addr->special_action = flag;
       addr->message = conf;
       addr->host_used = host;
       addr->special_action = flag;
       addr->message = conf;
+
+      if (sx.pipelining_used) setflag(addr, af_pipelining);
 #ifndef DISABLE_PRDR
 #ifndef DISABLE_PRDR
-      if (sx.prdr_active) addr->flags |= af_prdr_used;
+      if (sx.prdr_active) setflag(addr, af_prdr_used);
 #endif
 #endif
-      if (sx.peer_offered & PEER_OFFERED_CHUNKING) addr->flags |= af_chunking_used;
+      if (sx.peer_offered & OPTION_CHUNKING) setflag(addr, af_chunking_used);
       flag = '-';
 
 #ifndef DISABLE_PRDR
       flag = '-';
 
 #ifndef DISABLE_PRDR
@@ -3116,8 +3358,11 @@ else
 #ifndef DISABLE_PRDR
       if (sx.prdr_active)
         {
 #ifndef DISABLE_PRDR
       if (sx.prdr_active)
         {
+       const uschar * overall_message;
+
        /* PRDR - get the final, overall response.  For any non-success
        upgrade all the address statuses. */
        /* PRDR - get the final, overall response.  For any non-success
        upgrade all the address statuses. */
+
         sx.ok = smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer), '2',
           sx.ob->final_timeout);
         if (!sx.ok)
         sx.ok = smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer), '2',
           sx.ob->final_timeout);
         if (!sx.ok)
@@ -3133,7 +3378,14 @@ else
          goto RESPONSE_FAILED;
          }
 
          goto RESPONSE_FAILED;
          }
 
-       /* Update the journal, or setup retry. */
+       /* Append the overall response to the individual PRDR response for logging
+       and update the journal, or setup retry. */
+
+       overall_message = string_printing(sx.buffer);
+        for (addr = addrlist; addr != sx.first_addr; addr = addr->next)
+         if (addr->transport_return == OK)
+           addr->message = string_sprintf("%s\\n%s", addr->message, overall_message);
+
         for (addr = addrlist; addr != sx.first_addr; addr = addr->next)
          if (addr->transport_return == OK)
            {
         for (addr = addrlist; addr != sx.first_addr; addr = addr->next)
          if (addr->transport_return == OK)
            {
@@ -3259,8 +3511,9 @@ if (!sx.ok)
        set_rc = DEFER;
         if (save_errno > 0)
           message = US string_sprintf("%s: %s", message, strerror(save_errno));
        set_rc = DEFER;
         if (save_errno > 0)
           message = US string_sprintf("%s: %s", message, strerror(save_errno));
-        if (host->next != NULL) log_write(0, LOG_MAIN, "%s", message);
-       msglog_line(host, message);
+
+        write_logs(host, message, sx.first_addr ? sx.first_addr->basic_errno : 0);
+
         *message_defer = TRUE;
         }
       }
         *message_defer = TRUE;
         }
       }
@@ -3332,7 +3585,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
      || continue_more
      || (
 #ifdef SUPPORT_TLS
      || continue_more
      || (
 #ifdef SUPPORT_TLS
-          (  tls_out.active < 0  &&  !continue_proxy_cipher
+          (  tls_out.active.sock < 0  &&  !continue_proxy_cipher
            || verify_check_given_host(&sx.ob->hosts_nopass_tls, host) != OK
           )
         &&
            || verify_check_given_host(&sx.ob->hosts_nopass_tls, host) != OK
           )
         &&
@@ -3370,7 +3623,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
     if (sx.ok)
       {
       int pfd[2];
     if (sx.ok)
       {
       int pfd[2];
-      int socket_fd = sx.inblock.sock;
+      int socket_fd = sx.cctx.sock;
 
 
       if (sx.first_addr != NULL)         /* More addresses still to be sent */
 
 
       if (sx.first_addr != NULL)         /* More addresses still to be sent */
@@ -3385,7 +3638,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
       the connection still open. */
 
 #ifdef SUPPORT_TLS
       the connection still open. */
 
 #ifdef SUPPORT_TLS
-      if (tls_out.active >= 0)
+      if (tls_out.active.sock >= 0)
        if (  continue_more
           || verify_check_given_host(&sx.ob->hosts_noproxy_tls, host) == OK)
          {
        if (  continue_more
           || verify_check_given_host(&sx.ob->hosts_noproxy_tls, host) == OK)
          {
@@ -3395,7 +3648,8 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
          a new EHLO. If we don't get a good response, we don't attempt to pass
          the socket on. */
 
          a new EHLO. If we don't get a good response, we don't attempt to pass
          the socket on. */
 
-         tls_close(FALSE, TRUE);
+         tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_WAIT);
+         sx.cctx.tls_ctx = NULL;
          smtp_peer_options = smtp_peer_options_wrap;
          sx.ok = !sx.smtps
            && smtp_write_command(&sx.outblock, SCMD_FLUSH,
          smtp_peer_options = smtp_peer_options_wrap;
          sx.ok = !sx.smtps
            && smtp_write_command(&sx.outblock, SCMD_FLUSH,
@@ -3410,7 +3664,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
          {
          /* Set up a pipe for proxying TLS for the new transport process */
 
          {
          /* Set up a pipe for proxying TLS for the new transport process */
 
-         smtp_peer_options |= PEER_OFFERED_TLS;
+         smtp_peer_options |= OPTION_TLS;
          if (sx.ok = (socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0))
            socket_fd = pfd[1];
          else
          if (sx.ok = (socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0))
            socket_fd = pfd[1];
          else
@@ -3437,34 +3691,38 @@ propagate it from the initial
        {
         sx.send_quit = FALSE;
 
        {
         sx.send_quit = FALSE;
 
-       /* If TLS is still active, we need to proxy it for the transport we
+       /* We have passed the client socket to a fresh transport process.
+       If TLS is still active, we need to proxy it for the transport we
        just passed the baton to.  Fork a child to to do it, and return to
        get logging done asap.  Which way to place the work makes assumptions
        about post-fork prioritisation which may not hold on all platforms. */
 #ifdef SUPPORT_TLS
        just passed the baton to.  Fork a child to to do it, and return to
        get logging done asap.  Which way to place the work makes assumptions
        about post-fork prioritisation which may not hold on all platforms. */
 #ifdef SUPPORT_TLS
-       if (tls_out.active >= 0)
+       if (tls_out.active.sock >= 0)
          {
          int pid = fork();
          {
          int pid = fork();
+         if (pid == 0)         /* child; fork again to disconnect totally */
+           {
+           if (running_in_test_harness) millisleep(100); /* let parent debug out */
+           /* does not return */
+           smtp_proxy_tls(sx.cctx.tls_ctx, sx.buffer, sizeof(sx.buffer), pfd,
+                           sx.ob->command_timeout);
+           }
+
          if (pid > 0)          /* parent */
            {
            DEBUG(D_transport) debug_printf("proxy-proc inter-pid %d\n", pid);
          if (pid > 0)          /* parent */
            {
            DEBUG(D_transport) debug_printf("proxy-proc inter-pid %d\n", pid);
+           close(pfd[0]);
+           /* tidy the inter-proc to disconn the proxy proc */
            waitpid(pid, NULL, 0);
            waitpid(pid, NULL, 0);
-           tls_close(FALSE, FALSE);
-           (void)close(sx.inblock.sock);
+           tls_close(sx.cctx.tls_ctx, TLS_NO_SHUTDOWN);
+           sx.cctx.tls_ctx = NULL;
+           (void)close(sx.cctx.sock);
+           sx.cctx.sock = -1;
            continue_transport = NULL;
            continue_hostname = NULL;
            return yield;
            }
            continue_transport = NULL;
            continue_hostname = NULL;
            return yield;
            }
-         else if (pid == 0)    /* child; fork again to disconnect totally */
-           {
-           if ((pid = fork()))
-             {
-             DEBUG(D_transport) debug_printf("proxy-prox final-pid %d\n", pid);
-             _exit(pid ? EXIT_FAILURE : EXIT_SUCCESS);
-             }
-           smtp_proxy_tls(sx.buffer, sizeof(sx.buffer), pfd[0], sx.ob->command_timeout);
-           exim_exit(0);
-           }
+         log_write(0, LOG_PANIC_DIE, "fork failed");
          }
 #endif
        }
          }
 #endif
        }
@@ -3504,7 +3762,8 @@ if (sx.send_quit) (void)smtp_write_command(&sx.outblock, SCMD_FLUSH, "QUIT\r\n")
 END_OFF:
 
 #ifdef SUPPORT_TLS
 END_OFF:
 
 #ifdef SUPPORT_TLS
-tls_close(FALSE, TRUE);
+tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
+sx.cctx.tls_ctx = NULL;
 #endif
 
 /* Close the socket, and return the appropriate value, first setting
 #endif
 
 /* Close the socket, and return the appropriate value, first setting
@@ -3520,12 +3779,12 @@ case continue_more won't get set. */
 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
 if (sx.send_quit)
   {
 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
 if (sx.send_quit)
   {
-  shutdown(sx.outblock.sock, SHUT_WR);
-  if (fcntl(sx.inblock.sock, F_SETFL, O_NONBLOCK) == 0)
-    for (rc = 16; read(sx.inblock.sock, sx.inbuffer, sizeof(sx.inbuffer)) > 0 && rc > 0;)
+  shutdown(sx.cctx.sock, SHUT_WR);
+  if (fcntl(sx.cctx.sock, F_SETFL, O_NONBLOCK) == 0)
+    for (rc = 16; read(sx.cctx.sock, sx.inbuffer, sizeof(sx.inbuffer)) > 0 && rc > 0;)
       rc--;                            /* drain socket */
   }
       rc--;                            /* drain socket */
   }
-(void)close(sx.inblock.sock);
+(void)close(sx.cctx.sock);
 
 #ifndef DISABLE_EVENT
 (void) event_raise(tblock->event_action, US"tcp:close", NULL);
 
 #ifndef DISABLE_EVENT
 (void) event_raise(tblock->event_action, US"tcp:close", NULL);
@@ -3561,19 +3820,24 @@ smtp_transport_closedown(transport_instance *tblock)
 {
 smtp_transport_options_block *ob =
   (smtp_transport_options_block *)tblock->options_block;
 {
 smtp_transport_options_block *ob =
   (smtp_transport_options_block *)tblock->options_block;
+client_conn_ctx cctx;
 smtp_inblock inblock;
 smtp_outblock outblock;
 uschar buffer[256];
 uschar inbuffer[4096];
 uschar outbuffer[16];
 
 smtp_inblock inblock;
 smtp_outblock outblock;
 uschar buffer[256];
 uschar inbuffer[4096];
 uschar outbuffer[16];
 
-inblock.sock = fileno(stdin);
+/*XXX really we need an active-smtp-client ctx, rather than assuming stdout */
+cctx.sock = fileno(stdin);
+cctx.tls_ctx = cctx.sock == tls_out.active.sock ? tls_out.active.tls_ctx : NULL;
+
+inblock.cctx = &cctx;
 inblock.buffer = inbuffer;
 inblock.buffersize = sizeof(inbuffer);
 inblock.ptr = inbuffer;
 inblock.ptrend = inbuffer;
 
 inblock.buffer = inbuffer;
 inblock.buffersize = sizeof(inbuffer);
 inblock.ptr = inbuffer;
 inblock.ptrend = inbuffer;
 
-outblock.sock = inblock.sock;
+outblock.cctx = &cctx;
 outblock.buffersize = sizeof(outbuffer);
 outblock.buffer = outbuffer;
 outblock.ptr = outbuffer;
 outblock.buffersize = sizeof(outbuffer);
 outblock.buffer = outbuffer;
 outblock.ptr = outbuffer;
@@ -3583,7 +3847,7 @@ outblock.authenticating = FALSE;
 (void)smtp_write_command(&outblock, SCMD_FLUSH, "QUIT\r\n");
 (void)smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
   ob->command_timeout);
 (void)smtp_write_command(&outblock, SCMD_FLUSH, "QUIT\r\n");
 (void)smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
   ob->command_timeout);
-(void)close(inblock.sock);
+(void)close(cctx.sock);
 }
 
 
 }
 
 
@@ -3652,7 +3916,7 @@ smtp_transport_entry(
   address_item *addrlist)          /* addresses we are working on */
 {
 int cutoff_retry;
   address_item *addrlist)          /* addresses we are working on */
 {
 int cutoff_retry;
-int port;
+int defport;
 int hosts_defer = 0;
 int hosts_fail  = 0;
 int hosts_looked_up = 0;
 int hosts_defer = 0;
 int hosts_fail  = 0;
 int hosts_looked_up = 0;
@@ -3668,7 +3932,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;
 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)
   {
 
 DEBUG(D_transport)
   {
@@ -3679,12 +3943,12 @@ DEBUG(D_transport)
     {
     debug_printf("hostlist:\n");
     for (host = hostlist; host; host = host->next)
     {
     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",
       continue_hostname, continue_host_address,
     }
   if (continue_hostname)
     debug_printf("already connected to %s [%s] (on fd %d)\n",
       continue_hostname, continue_host_address,
-      cutthrough.fd >= 0 ? cutthrough.fd : 0);
+      cutthrough.cctx.sock >= 0 ? cutthrough.cctx.sock : 0);
   }
 
 /* Set the flag requesting that these hosts be added to the waiting
   }
 
 /* Set the flag requesting that these hosts be added to the waiting
@@ -3699,6 +3963,12 @@ same one in order to be passed to a single transport - or if the transport has
 a host list with hosts_override set, use the host list supplied with the
 transport. It is an error for this not to exist. */
 
 a host list with hosts_override set, use the host list supplied with the
 transport. It is an error for this not to exist. */
 
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+if (tls_requiretls & REQUIRETLS_MSG)
+  ob->tls_tempfail_tryclear = FALSE;   /*XXX surely we should have a local for this
+                                       rather than modifying the transport? */
+#endif
+
 if (!hostlist || (ob->hosts_override && ob->hosts))
   {
   if (!ob->hosts)
 if (!hostlist || (ob->hosts_override && ob->hosts))
   {
   if (!ob->hosts)
@@ -3807,7 +4077,7 @@ else if (ob->hosts_randomize && hostlist->mx == MX_NONE && !continue_hostname)
 
 /* Sort out the default port.  */
 
 
 /* Sort out the default port.  */
 
-if (!smtp_get_port(ob->port, addrlist, &port, tid)) return FALSE;
+if (!smtp_get_port(ob->port, addrlist, &defport, tid)) return FALSE;
 
 /* For each host-plus-IP-address on the list:
 
 
 /* For each host-plus-IP-address on the list:
 
@@ -3860,7 +4130,9 @@ for (cutoff_retry = 0;
   {
   host_item *nexthost = NULL;
   int unexpired_hosts_tried = 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
   for (host = hostlist;
           host
        && unexpired_hosts_tried < ob->hosts_max_try
@@ -3925,7 +4197,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. */
 
       /* 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;
 
       if (ob->dns_qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE;
       if (ob->dns_search_parents) flags |= HOST_FIND_SEARCH_PARENTS;
 
@@ -3993,14 +4265,16 @@ for (cutoff_retry = 0;
     result of the lookup. Set expired FALSE, to save the outer loop executing
     twice. */
 
     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. */
 
     /* Reset the default next host in case a multihomed host whose addresses
     are not looked up till just above added to the host list. */
@@ -4046,7 +4320,7 @@ for (cutoff_retry = 0;
     the default. */
 
     pistring = string_sprintf(":%d", host->port == PORT_NONE
     the default. */
 
     pistring = string_sprintf(":%d", host->port == PORT_NONE
-      ? port : host->port);
+      ? defport : host->port);
     if (Ustrcmp(pistring, ":25") == 0) pistring = US"";
 
     /* Select IPv4 or IPv6, and choose an outgoing interface. If the interface
     if (Ustrcmp(pistring, ":25") == 0) pistring = US"";
 
     /* Select IPv4 or IPv6, and choose an outgoing interface. If the interface
@@ -4125,7 +4399,7 @@ for (cutoff_retry = 0;
       {
       if (  !host->address
          || host->status != hstatus_unusable_expired
       {
       if (  !host->address
          || host->status != hstatus_unusable_expired
-        || host->last_try > received_time)
+        || host->last_try > received_time.tv_sec)
         continue;
       DEBUG(D_transport) debug_printf("trying expired host %s [%s]%s\n",
           host->name, host->address, pistring);
         continue;
       DEBUG(D_transport) debug_printf("trying expired host %s [%s]%s\n",
           host->name, host->address, pistring);
@@ -4246,7 +4520,7 @@ for (cutoff_retry = 0;
       /* Attempt the delivery. */
 
       total_hosts_tried++;
       /* Attempt the delivery. */
 
       total_hosts_tried++;
-      rc = smtp_deliver(addrlist, thost, host_af, port, interface, tblock,
+      rc = smtp_deliver(addrlist, thost, host_af, defport, interface, tblock,
         &message_defer, FALSE);
 
       /* Yield is one of:
         &message_defer, FALSE);
 
       /* Yield is one of:
@@ -4266,7 +4540,7 @@ for (cutoff_retry = 0;
 
       if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL
                      && first_addr->basic_errno != ERRNO_TLSFAILURE)
 
       if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL
                      && first_addr->basic_errno != ERRNO_TLSFAILURE)
-        write_logs(first_addr, host);
+        write_logs(host, first_addr->message, first_addr->basic_errno);
 
 #ifndef DISABLE_EVENT
       if (rc == DEFER)
 
 #ifndef DISABLE_EVENT
       if (rc == DEFER)
@@ -4293,10 +4567,10 @@ for (cutoff_retry = 0;
          "%s: delivering unencrypted to H=%s [%s] (not in hosts_require_tls)",
          first_addr->message, host->name, host->address);
         first_addr = prepare_addresses(addrlist, host);
          "%s: delivering unencrypted to H=%s [%s] (not in hosts_require_tls)",
          first_addr->message, host->name, host->address);
         first_addr = prepare_addresses(addrlist, host);
-        rc = smtp_deliver(addrlist, thost, host_af, port, interface, tblock,
+        rc = smtp_deliver(addrlist, thost, host_af, defport, interface, tblock,
           &message_defer, TRUE);
         if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL)
           &message_defer, TRUE);
         if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL)
-          write_logs(first_addr, host);
+          write_logs(host, first_addr->message, first_addr->basic_errno);
 # ifndef DISABLE_EVENT
         if (rc == DEFER)
           deferred_event_raise(first_addr, host);
 # ifndef DISABLE_EVENT
         if (rc == DEFER)
           deferred_event_raise(first_addr, host);
@@ -4445,7 +4719,7 @@ for (cutoff_retry = 0;
         for (last_rule = retry->rules;
              last_rule->next;
              last_rule = last_rule->next);
         for (last_rule = retry->rules;
              last_rule->next;
              last_rule = last_rule->next);
-        timedout = time(NULL) - received_time > last_rule->timeout;
+        timedout = time(NULL) - received_time.tv_sec > last_rule->timeout;
         }
       else timedout = TRUE;    /* No rule => timed out */
 
         }
       else timedout = TRUE;    /* No rule => timed out */
 
@@ -4458,6 +4732,38 @@ for (cutoff_retry = 0;
       }
     }   /* End of loop for trying multiple hosts. */
 
       }
     }   /* 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.cctx.sock >= 0 ? cutthrough.cctx.sock : 0;
+
+    DEBUG(D_transport) debug_printf("no hosts match already-open connection\n");
+#ifdef SUPPORT_TLS
+    /* A TLS conn could be open for a cutthrough, but not for a plain continued-
+    transport */
+/*XXX doublecheck that! */
+
+    if (cutthrough.cctx.sock >= 0 && cutthrough.is_tls)
+      {
+      (void) tls_write(cutthrough.cctx.tls_ctx, US"QUIT\r\n", 6, FALSE);
+      tls_close(cutthrough.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
+      cutthrough.cctx.tls_ctx = NULL;
+      cutthrough.is_tls = FALSE;
+      }
+    else
+#else
+      (void) write(fd, US"QUIT\r\n", 6);
+#endif
+    (void) close(fd);
+    cutthrough.cctx.sock = -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. */
   /* 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. */
@@ -4588,6 +4894,7 @@ DEBUG(D_transport) debug_printf("Leaving %s transport\n", tblock->name);
 return TRUE;   /* Each address has its status */
 }
 
 return TRUE;   /* Each address has its status */
 }
 
+#endif /*!MACRO_PREDEF*/
 /* vi: aw ai sw=2
 */
 /* End of transport/smtp.c */
 /* vi: aw ai sw=2
 */
 /* End of transport/smtp.c */