DANE: ignore undersized TLSA records
[exim.git] / src / src / transports / smtp.c
index 77b3eb8181f20e239b3a81e2754b6db782302602..f3e09ada7c91cd69c1875fbc6b5e50e9ee35d439 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,
@@ -55,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) },
@@ -105,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
@@ -120,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
@@ -153,13 +163,13 @@ optionlist smtp_transport_options[] = {
   { "serialize_hosts",      opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, serialize_hosts) },
   { "size_addition",        opt_int,
   { "serialize_hosts",      opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, serialize_hosts) },
   { "size_addition",        opt_int,
-      (void *)offsetof(smtp_transport_options_block, size_addition) }
+      (void *)offsetof(smtp_transport_options_block, size_addition) },
 #ifdef SUPPORT_SOCKS
 #ifdef SUPPORT_SOCKS
,{ "socks_proxy",          opt_stringptr,
-      (void *)offsetof(smtp_transport_options_block, socks_proxy) }
 { "socks_proxy",          opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, socks_proxy) },
 #endif
 #ifdef SUPPORT_TLS
 #endif
 #ifdef SUPPORT_TLS
,{ "tls_certificate",      opt_stringptr,
 { "tls_certificate",      opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_certificate) },
   { "tls_crl",              opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_crl) },
       (void *)offsetof(smtp_transport_options_block, tls_certificate) },
   { "tls_crl",              opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_crl) },
@@ -180,15 +190,18 @@ 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
 
 
 #ifdef MACRO_PREDEF
@@ -209,7 +222,6 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   .fallback_hosts =            NULL,
   .hostlist =                  NULL,
   .fallback_hostlist =         NULL,
   .fallback_hosts =            NULL,
   .hostlist =                  NULL,
   .fallback_hostlist =         NULL,
-  .authenticated_sender =      NULL,
   .helo_data =                 US"$primary_hostname",
   .interface =                 NULL,
   .port =                      NULL,
   .helo_data =                 US"$primary_hostname",
   .interface =                 NULL,
   .port =                      NULL,
@@ -219,9 +231,10 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   .hosts_try_auth =            NULL,
   .hosts_require_auth =                NULL,
   .hosts_try_chunking =                US"*",
   .hosts_try_auth =            NULL,
   .hosts_require_auth =                NULL,
   .hosts_try_chunking =                US"*",
-#ifdef EXPERIMENTAL_DANE
+#ifdef SUPPORT_DANE
   .hosts_try_dane =            NULL,
   .hosts_require_dane =                NULL,
   .hosts_try_dane =            NULL,
   .hosts_require_dane =                NULL,
+  .dane_require_tls_ciphers =  NULL,
 #endif
   .hosts_try_fastopen =                NULL,
 #ifndef DISABLE_PRDR
 #endif
   .hosts_try_fastopen =                NULL,
 #ifndef DISABLE_PRDR
@@ -277,6 +290,9 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   .tls_try_verify_hosts =      US"*",
   .tls_verify_cert_hostnames = US"*",
 #endif
   .tls_try_verify_hosts =      US"*",
   .tls_verify_cert_hostnames = US"*",
 #endif
+#ifdef SUPPORT_I18N
+  .utf8_downconvert =          NULL,
+#endif
 #ifndef DISABLE_DKIM
  .dkim =
    {.dkim_domain =             NULL,
 #ifndef DISABLE_DKIM
  .dkim =
    {.dkim_domain =             NULL,
@@ -287,7 +303,16 @@ smtp_transport_options_block smtp_transport_option_defaults = {
     .dkim_sign_headers =       NULL,
     .dkim_strict =             NULL,
     .dkim_hash =               US"sha256",
     .dkim_sign_headers =       NULL,
     .dkim_strict =             NULL,
     .dkim_hash =               US"sha256",
-    .dot_stuffed =             FALSE},
+    .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
 };
 
@@ -770,8 +795,9 @@ 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--;
   count--;
-  if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+  if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
                          '2', ob->command_timeout))
     {
     DEBUG(D_transport) debug_printf("bad response for MAIL\n");
                          '2', ob->command_timeout))
     {
     DEBUG(D_transport) debug_printf("bad response for MAIL\n");
@@ -787,7 +813,7 @@ if (sx->pending_MAIL)
         }
       while (count-- > 0)
         {
         }
       while (count-- > 0)
         {
-        if (!smtp_read_response(&sx->inblock, flushbuffer, sizeof(flushbuffer),
+        if (!smtp_read_response(sx, flushbuffer, sizeof(flushbuffer),
                    '2', ob->command_timeout)
             && (errno != 0 || flushbuffer[0] == 0))
           break;
                    '2', ob->command_timeout)
             && (errno != 0 || flushbuffer[0] == 0))
           break;
@@ -821,7 +847,8 @@ while (count-- > 0)
   /* The address was accepted */
   addr->host_used = sx->host;
 
   /* The address was accepted */
   addr->host_used = sx->host;
 
-  if (smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+  DEBUG(D_transport) debug_printf("%s expect rcpt\n", __FUNCTION__);
+  if (smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
                          '2', ob->command_timeout))
     {
     yield |= 1;
                          '2', ob->command_timeout))
     {
     yield |= 1;
@@ -938,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, 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
@@ -971,11 +1001,8 @@ return yield;
 /* Do the client side of smtp-level authentication */
 /*
 Arguments:
 /* Do the client side of smtp-level authentication */
 /*
 Arguments:
+  sx           smtp connection
   buffer       EHLO response from server (gets overwritten)
   buffer       EHLO response from server (gets overwritten)
-  addrlist      chain of potential addresses to deliver
-  host          host to deliver to
-  ob           transport options
-  ibp, obp     comms channel control blocks
 
 Returns:
   OK                   Success, or failed (but not required): global "smtp_authenticated" set
 
 Returns:
   OK                   Success, or failed (but not required): global "smtp_authenticated" set
@@ -986,23 +1013,22 @@ Returns:
   FAIL                 - response
 */
 
   FAIL                 - response
 */
 
-int
-smtp_auth(uschar *buffer, unsigned bufsize, address_item *addrlist, host_item *host,
-    smtp_transport_options_block *ob, BOOL is_esmtp,
-    smtp_inblock *ibp, smtp_outblock *obp)
+static int
+smtp_auth(smtp_context * sx, uschar * buffer, unsigned bufsize)
 {
 {
+smtp_transport_options_block * ob = sx->ob;
 int require_auth;
 uschar *fail_reason = US"server did not advertise AUTH support";
 
 int require_auth;
 uschar *fail_reason = US"server did not advertise AUTH support";
 
-smtp_authenticated = FALSE;
+f.smtp_authenticated = FALSE;
 client_authenticator = client_authenticated_id = client_authenticated_sender = NULL;
 client_authenticator = client_authenticated_id = client_authenticated_sender = NULL;
-require_auth = verify_check_given_host(&ob->hosts_require_auth, host);
+require_auth = verify_check_given_host(CUSS &ob->hosts_require_auth, sx->host);
 
 
-if (is_esmtp && !regex_AUTH) regex_AUTH =
+if (sx->esmtp && !regex_AUTH) regex_AUTH =
     regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)",
          FALSE, TRUE);
 
     regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)",
          FALSE, TRUE);
 
-if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
+if (sx->esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
   {
   uschar *names = string_copyn(expand_nstring[1], expand_nlength[1]);
   expand_nmax = -1;                          /* reset */
   {
   uschar *names = string_copyn(expand_nstring[1], expand_nlength[1]);
   expand_nmax = -1;                          /* reset */
@@ -1011,7 +1037,7 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
   regex match above. */
 
   if (require_auth == OK ||
   regex match above. */
 
   if (require_auth == OK ||
-      verify_check_given_host(&ob->hosts_try_auth, host) == OK)
+      verify_check_given_host(CUSS &ob->hosts_try_auth, sx->host) == OK)
     {
     auth_instance *au;
     fail_reason = US"no common mechanisms were found";
     {
     auth_instance *au;
     fail_reason = US"no common mechanisms were found";
@@ -1024,7 +1050,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; au = au->next)
+    for (au = auths; !f.smtp_authenticated && au; au = au->next)
       {
       uschar *p = names;
       if (!au->client ||
       {
       uschar *p = names;
       if (!au->client ||
@@ -1059,10 +1085,10 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
        that reflections don't show it. */
 
        fail_reason = US"authentication attempt(s) failed";
        that reflections don't show it. */
 
        fail_reason = US"authentication attempt(s) failed";
-       obp->authenticating = TRUE;
-       rc = (au->info->clientcode)(au, ibp, obp,
+       sx->outblock.authenticating = TRUE;
+       rc = (au->info->clientcode)(au, sx,
          ob->command_timeout, buffer, bufsize);
          ob->command_timeout, buffer, bufsize);
-       obp->authenticating = FALSE;
+       sx->outblock.authenticating = FALSE;
        DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n",
          au->name, rc);
 
        DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n",
          au->name, rc);
 
@@ -1074,7 +1100,7 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
        switch(rc)
          {
          case OK:
        switch(rc)
          {
          case OK:
-         smtp_authenticated = TRUE;   /* stops the outer loop */
+         f.smtp_authenticated = TRUE;   /* stops the outer loop */
          client_authenticator = au->name;
          if (au->set_client_id != NULL)
            client_authenticated_id = expand_string(au->set_client_id);
          client_authenticator = au->name;
          if (au->set_client_id != NULL)
            client_authenticated_id = expand_string(au->set_client_id);
@@ -1090,7 +1116,7 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
          case FAIL:
          if (errno != 0 || buffer[0] != '5') return FAIL;
          log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s",
          case FAIL:
          if (errno != 0 || buffer[0] != '5') return FAIL;
          log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s",
-           au->name, host->name, host->address, buffer);
+           au->name, sx->host->name, sx->host->address, buffer);
          break;
 
          /* Failure by some other means. In effect, the authenticator
          break;
 
          /* Failure by some other means. In effect, the authenticator
@@ -1102,14 +1128,14 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
          case CANCELLED:
          if (*buffer != 0)
            log_write(0, LOG_MAIN, "%s authenticator cancelled "
          case CANCELLED:
          if (*buffer != 0)
            log_write(0, LOG_MAIN, "%s authenticator cancelled "
-             "authentication H=%s [%s] %s", au->name, host->name,
-             host->address, buffer);
+             "authentication H=%s [%s] %s", au->name, sx->host->name,
+             sx->host->address, buffer);
          break;
 
          /* Internal problem, message in buffer. */
 
          case ERROR:
          break;
 
          /* Internal problem, message in buffer. */
 
          case ERROR:
-         set_errno_nohost(addrlist, ERRNO_AUTHPROB, string_copy(buffer),
+         set_errno_nohost(sx->addrlist, ERRNO_AUTHPROB, string_copy(buffer),
                    DEFER, FALSE);
          return ERROR;
          }
                    DEFER, FALSE);
          return ERROR;
          }
@@ -1122,9 +1148,9 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
 
 /* If we haven't authenticated, but are required to, give up. */
 
 
 /* If we haven't authenticated, but are required to, give up. */
 
-if (require_auth == OK && !smtp_authenticated)
+if (require_auth == OK && !f.smtp_authenticated)
   {
   {
-  set_errno_nohost(addrlist, ERRNO_AUTHFAIL,
+  set_errno_nohost(sx->addrlist, ERRNO_AUTHFAIL,
     string_sprintf("authentication required but %s", fail_reason), DEFER,
     FALSE);
   return DEFER;
     string_sprintf("authentication required but %s", fail_reason), DEFER,
     FALSE);
   return DEFER;
@@ -1141,7 +1167,7 @@ Arguments
   addrlist      chain of potential addresses to deliver
   ob           transport options
 
   addrlist      chain of potential addresses to deliver
   ob           transport options
 
-Globals                smtp_authenticated
+Globals                f.smtp_authenticated
                client_authenticated_sender
 Return True on error, otherwise buffer has (possibly empty) terminated string
 */
                client_authenticated_sender
 Return True on error, otherwise buffer has (possibly empty) terminated string
 */
@@ -1153,7 +1179,7 @@ smtp_mail_auth_str(uschar *buffer, unsigned bufsize, address_item *addrlist,
 uschar *local_authenticated_sender = authenticated_sender;
 
 #ifdef notdef
 uschar *local_authenticated_sender = authenticated_sender;
 
 #ifdef notdef
-  debug_printf("smtp_mail_auth_str: as<%s> os<%s> SA<%s>\n", authenticated_sender, ob->authenticated_sender, smtp_authenticated?"Y":"N");
+  debug_printf("smtp_mail_auth_str: as<%s> os<%s> SA<%s>\n", authenticated_sender, ob->authenticated_sender, f.smtp_authenticated?"Y":"N");
 #endif
 
 if (ob->authenticated_sender != NULL)
 #endif
 
 if (ob->authenticated_sender != NULL)
@@ -1161,7 +1187,7 @@ if (ob->authenticated_sender != NULL)
   uschar *new = expand_string(ob->authenticated_sender);
   if (new == NULL)
     {
   uschar *new = expand_string(ob->authenticated_sender);
   if (new == NULL)
     {
-    if (!expand_string_forcedfail)
+    if (!f.expand_string_forcedfail)
       {
       uschar *message = string_sprintf("failed to expand "
         "authenticated_sender: %s", expand_string_message);
       {
       uschar *message = string_sprintf("failed to expand "
         "authenticated_sender: %s", expand_string_message);
@@ -1174,7 +1200,7 @@ if (ob->authenticated_sender != NULL)
 
 /* Add the authenticated sender address if present */
 
 
 /* Add the authenticated sender address if present */
 
-if ((smtp_authenticated || ob->authenticated_sender_force) &&
+if ((f.smtp_authenticated || ob->authenticated_sender_force) &&
     local_authenticated_sender != NULL)
   {
   string_format(buffer, bufsize, " AUTH=%s",
     local_authenticated_sender != NULL)
   {
   string_format(buffer, bufsize, " AUTH=%s",
@@ -1190,7 +1216,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
@@ -1221,7 +1247,30 @@ switch (rc)
     return DEFER; /* just defer this TLS'd conn */
 
   case DNS_SUCCEED:
     return DEFER; /* just defer this TLS'd conn */
 
   case DNS_SUCCEED:
-    if (sec) return OK;
+    if (sec)
+      {
+      DEBUG(D_transport)
+       {
+       dns_scan dnss;
+       dns_record * rr;
+       for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
+            rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
+         if (rr->type == T_TLSA && rr->size > 3)
+           {
+           uint16_t payload_length = rr->size - 3;
+           uschar s[MAX_TLSA_EXPANDED_SIZE], * sp = s, * p = US rr->data;
+
+           sp += sprintf(CS sp, "%d ", *p++); /* usage */
+           sp += sprintf(CS sp, "%d ", *p++); /* selector */
+           sp += sprintf(CS sp, "%d ", *p++); /* matchtype */
+           while (payload_length-- > 0 && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4))
+             sp += sprintf(CS sp, "%02x", *p++);
+
+           debug_printf(" %s\n", s);
+           }
+       }
+      return OK;
+      }
     log_write(0, LOG_MAIN,
       "DANE error: TLSA lookup for %s not DNSSEC", host->name);
     /*FALLTRHOUGH*/
     log_write(0, LOG_MAIN,
       "DANE error: TLSA lookup for %s not DNSSEC", host->name);
     /*FALLTRHOUGH*/
@@ -1317,8 +1366,8 @@ 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);
 
 {
 size_t bsize = Ustrlen(buf);
 
@@ -1326,6 +1375,12 @@ size_t bsize = Ustrlen(buf);
 if (  checks & OPTION_TLS
    && pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
   checks &= ~OPTION_TLS;
 if (  checks & OPTION_TLS
    && pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
   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
 
 if (  checks & OPTION_IGNQ
 #endif
 
 if (  checks & OPTION_IGNQ
@@ -1400,7 +1455,7 @@ there may be more writes (like, the chunk data) done soon. */
 
 if (chunk_size > 0)
   {
 
 if (chunk_size > 0)
   {
-  if((cmd_count = smtp_write_command(&sx->outblock,
+  if((cmd_count = smtp_write_command(sx,
              flags & tc_reap_prev ? SCMD_FLUSH : SCMD_MORE,
              "BDAT %u%s\r\n", chunk_size, flags & tc_chunk_last ? " LAST" : "")
      ) < 0) return ERROR;
              flags & tc_reap_prev ? SCMD_FLUSH : SCMD_MORE,
              "BDAT %u%s\r\n", chunk_size, flags & tc_chunk_last ? " LAST" : "")
      ) < 0) return ERROR;
@@ -1443,7 +1498,7 @@ if (sx->pending_BDAT)
   {
   DEBUG(D_transport) debug_printf("look for one response for BDAT\n");
 
   {
   DEBUG(D_transport) debug_printf("look for one response for BDAT\n");
 
-  if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), '2',
+  if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
        ob->command_timeout))
     {
     if (errno == 0 && sx->buffer[0] == '4')
        ob->command_timeout))
     {
     if (errno == 0 && sx->buffer[0] == '4')
@@ -1490,7 +1545,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;
@@ -1512,9 +1567,10 @@ 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(CUSS &sx->ob->hosts_require_dane, sx->host) == OK;
 #endif
 
 if ((sx->max_rcpt = sx->tblock->max_addresses) == 0) sx->max_rcpt = 999999;
 #endif
 
 if ((sx->max_rcpt = sx->tblock->max_addresses) == 0) sx->max_rcpt = 999999;
@@ -1586,7 +1642,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;
@@ -1595,7 +1651,7 @@ if (!continue_hostname)
     if (sx->host->dnssec == DS_YES)
       {
       if(  sx->dane_required
     if (sx->host->dnssec == DS_YES)
       {
       if(  sx->dane_required
-       || verify_check_given_host(&sx->ob->hosts_try_dane, sx->host) == OK
+       || verify_check_given_host(CUSS &sx->ob->hosts_try_dane, sx->host) == OK
        )
        switch (rc = tlsa_lookup(sx->host, &tlsa_dnsa, sx->dane_required))
          {
        )
        switch (rc = tlsa_lookup(sx->host, &tlsa_dnsa, sx->dane_required))
          {
@@ -1607,6 +1663,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;
          }
       }
@@ -1615,6 +1676,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;
       }
     }
@@ -1622,11 +1687,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)
@@ -1679,9 +1746,9 @@ 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
 #endif
-    good_response = smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+    good_response = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
       '2', sx->ob->command_timeout);
 #ifdef EXPERIMENTAL_DSN_INFO
     sx->smtp_greeting = string_copy(sx->buffer);
       '2', sx->ob->command_timeout);
 #ifdef EXPERIMENTAL_DSN_INFO
     sx->smtp_greeting = string_copy(sx->buffer);
@@ -1753,7 +1820,7 @@ goto SEND_QUIT;
   mailers use upper case for some reason (the RFC is quite clear about case
   independence) so, for peace of mind, I gave in. */
 
   mailers use upper case for some reason (the RFC is quite clear about case
   independence) so, for peace of mind, I gave in. */
 
-  sx->esmtp = verify_check_given_host(&sx->ob->hosts_avoid_esmtp, sx->host) != OK;
+  sx->esmtp = verify_check_given_host(CUSS &sx->ob->hosts_avoid_esmtp, sx->host) != OK;
 
   /* Alas; be careful, since this goto is not an error-out, so conceivably
   we might set data between here and the target which we assume to exist
 
   /* Alas; be careful, since this goto is not an error-out, so conceivably
   we might set data between here and the target which we assume to exist
@@ -1771,11 +1838,11 @@ goto SEND_QUIT;
 
   if (sx->esmtp)
     {
 
   if (sx->esmtp)
     {
-    if (smtp_write_command(&sx->outblock, SCMD_FLUSH, "%s %s\r\n",
+    if (smtp_write_command(sx, SCMD_FLUSH, "%s %s\r\n",
          sx->lmtp ? "LHLO" : "EHLO", sx->helo_data) < 0)
       goto SEND_FAILED;
     sx->esmtp_sent = TRUE;
          sx->lmtp ? "LHLO" : "EHLO", sx->helo_data) < 0)
       goto SEND_FAILED;
     sx->esmtp_sent = TRUE;
-    if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), '2',
+    if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
            sx->ob->command_timeout))
       {
       if (errno != 0 || sx->buffer[0] == 0 || sx->lmtp)
            sx->ob->command_timeout))
       {
       if (errno != 0 || sx->buffer[0] == 0 || sx->lmtp)
@@ -1804,10 +1871,9 @@ goto SEND_QUIT;
     if (sx->esmtp_sent && (n = Ustrlen(sx->buffer)) < sizeof(sx->buffer)/2)
       { rsp = sx->buffer + n + 1; n = sizeof(sx->buffer) - n; }
 
     if (sx->esmtp_sent && (n = Ustrlen(sx->buffer)) < sizeof(sx->buffer)/2)
       { rsp = sx->buffer + n + 1; n = sizeof(sx->buffer) - n; }
 
-    if (smtp_write_command(&sx->outblock, SCMD_FLUSH, "HELO %s\r\n", sx->helo_data) < 0)
+    if (smtp_write_command(sx, SCMD_FLUSH, "HELO %s\r\n", sx->helo_data) < 0)
       goto SEND_FAILED;
       goto SEND_FAILED;
-    good_response = smtp_read_response(&sx->inblock, rsp, n,
-      '2', sx->ob->command_timeout);
+    good_response = smtp_read_response(sx, rsp, n, '2', sx->ob->command_timeout);
 #ifdef EXPERIMENTAL_DSN_INFO
     sx->helo_response = string_copy(rsp);
 #endif
 #ifdef EXPERIMENTAL_DSN_INFO
     sx->helo_response = string_copy(rsp);
 #endif
@@ -1858,16 +1924,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->cctx.sock = 0;                         /* stdin */
+    sx->cctx.tls_ctx = NULL;
     smtp_port_for_connect(sx->host, sx->port); /* Record the port that was used */
     }
     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 */
 
@@ -1875,11 +1943,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 & OPTION_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;
@@ -1898,13 +1967,13 @@ for error analysis. */
 #ifdef SUPPORT_TLS
 if (  smtp_peer_options & OPTION_TLS
    && !suppress_tls
 #ifdef SUPPORT_TLS
 if (  smtp_peer_options & OPTION_TLS
    && !suppress_tls
-   && verify_check_given_host(&sx->ob->hosts_avoid_tls, sx->host) != OK
+   && verify_check_given_host(CUSS &sx->ob->hosts_avoid_tls, sx->host) != OK
    && (  !sx->verify
    && (  !sx->verify
-      || verify_check_given_host(&sx->ob->hosts_verify_avoid_tls, sx->host) != OK
+      || verify_check_given_host(CUSS &sx->ob->hosts_verify_avoid_tls, sx->host) != OK
    )  )
   {
   uschar buffer2[4096];
    )  )
   {
   uschar buffer2[4096];
-  if (smtp_write_command(&sx->outblock, SCMD_FLUSH, "STARTTLS\r\n") < 0)
+  if (smtp_write_command(sx, SCMD_FLUSH, "STARTTLS\r\n") < 0)
     goto SEND_FAILED;
 
   /* If there is an I/O error, transmission of this message is deferred. If
     goto SEND_FAILED;
 
   /* If there is an I/O error, transmission of this message is deferred. If
@@ -1914,7 +1983,7 @@ if (  smtp_peer_options & OPTION_TLS
   STARTTLS, we carry on. This means we will try to send the message in clear,
   unless the host is in hosts_require_tls (tested below). */
 
   STARTTLS, we carry on. This means we will try to send the message in clear,
   unless the host is in hosts_require_tls (tested below). */
 
-  if (!smtp_read_response(&sx->inblock, buffer2, sizeof(buffer2), '2',
+  if (!smtp_read_response(sx, buffer2, sizeof(buffer2), '2',
       sx->ob->command_timeout))
     {
     if (  errno != 0
       sx->ob->command_timeout))
     {
     if (  errno != 0
@@ -1935,22 +2004,31 @@ if (  smtp_peer_options & OPTION_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. */
+      DEBUG(D_tls) debug_printf("TLS session fail: %s\n", errstr);
+
+# 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;
@@ -1984,7 +2062,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;
@@ -2001,7 +2079,7 @@ if (tls_out.active >= 0)
   /* For SMTPS we need to wait for the initial OK response. */
   if (sx->smtps)
     {
   /* For SMTPS we need to wait for the initial OK response. */
   if (sx->smtps)
     {
-    good_response = smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+    good_response = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
       '2', sx->ob->command_timeout);
 #ifdef EXPERIMENTAL_DSN_INFO
     sx->smtp_greeting = string_copy(sx->buffer);
       '2', sx->ob->command_timeout);
 #ifdef EXPERIMENTAL_DSN_INFO
     sx->smtp_greeting = string_copy(sx->buffer);
@@ -2018,10 +2096,10 @@ if (tls_out.active >= 0)
       debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
     }
 
       debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
     }
 
-  if (smtp_write_command(&sx->outblock, SCMD_FLUSH, "%s %s\r\n",
+  if (smtp_write_command(sx, SCMD_FLUSH, "%s %s\r\n",
         sx->lmtp ? "LHLO" : greeting_cmd, sx->helo_data) < 0)
     goto SEND_FAILED;
         sx->lmtp ? "LHLO" : greeting_cmd, sx->helo_data) < 0)
     goto SEND_FAILED;
-  good_response = smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+  good_response = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
     '2', sx->ob->command_timeout);
 #ifdef EXPERIMENTAL_DSN_INFO
   sx->helo_response = string_copy(sx->buffer);
     '2', sx->ob->command_timeout);
 #ifdef EXPERIMENTAL_DSN_INFO
   sx->helo_response = string_copy(sx->buffer);
@@ -2034,16 +2112,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
 # endif
        || sx->dane
 # endif
-       || verify_check_given_host(&sx->ob->hosts_require_tls, sx->host) == OK
+# ifdef EXPERIMENTAL_REQUIRETLS
+       || tls_requiretls & REQUIRETLS_MSG
+# endif
+       || verify_check_given_host(CUSS &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",
     smtp_peer_options & OPTION_TLS
     ? "an attempt to start TLS failed" : "the server did not offer TLS support");
   message = string_sprintf("a TLS session is required, but %s",
     smtp_peer_options & OPTION_TLS
     ? "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*/
@@ -2055,7 +2147,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
     )
   {
@@ -2074,6 +2166,9 @@ if (continue_hostname == NULL
        | OPTION_DSN
        | OPTION_PIPE
        | (sx->ob->size_addition >= 0 ? OPTION_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
       );
 
     /* Set for IGNOREQUOTA if the response to LHLO specifies support and the
@@ -2091,14 +2186,14 @@ if (continue_hostname == NULL
     the current host matches hosts_avoid_pipelining, don't do it. */
 
     if (  sx->peer_offered & OPTION_PIPE
     the current host matches hosts_avoid_pipelining, don't do it. */
 
     if (  sx->peer_offered & OPTION_PIPE
-       && verify_check_given_host(&sx->ob->hosts_avoid_pipelining, sx->host) != OK)
+       && verify_check_given_host(CUSS &sx->ob->hosts_avoid_pipelining, sx->host) != OK)
       smtp_peer_options |= OPTION_PIPE;
 
     DEBUG(D_transport) debug_printf("%susing PIPELINING\n",
       smtp_peer_options & OPTION_PIPE ? "" : "not ");
 
     if (  sx->peer_offered & OPTION_CHUNKING
       smtp_peer_options |= OPTION_PIPE;
 
     DEBUG(D_transport) debug_printf("%susing PIPELINING\n",
       smtp_peer_options & OPTION_PIPE ? "" : "not ");
 
     if (  sx->peer_offered & OPTION_CHUNKING
-       && verify_check_given_host(&sx->ob->hosts_try_chunking, sx->host) != OK)
+       && verify_check_given_host(CUSS &sx->ob->hosts_try_chunking, sx->host) != OK)
       sx->peer_offered &= ~OPTION_CHUNKING;
 
     if (sx->peer_offered & OPTION_CHUNKING)
       sx->peer_offered &= ~OPTION_CHUNKING;
 
     if (sx->peer_offered & OPTION_CHUNKING)
@@ -2106,7 +2201,7 @@ if (continue_hostname == NULL
 
 #ifndef DISABLE_PRDR
     if (  sx->peer_offered & OPTION_PRDR
 
 #ifndef DISABLE_PRDR
     if (  sx->peer_offered & OPTION_PRDR
-       && verify_check_given_host(&sx->ob->hosts_try_prdr, sx->host) != OK)
+       && verify_check_given_host(CUSS &sx->ob->hosts_try_prdr, sx->host) != OK)
       sx->peer_offered &= ~OPTION_PRDR;
 
     if (sx->peer_offered & OPTION_PRDR)
       sx->peer_offered &= ~OPTION_PRDR;
 
     if (sx->peer_offered & OPTION_PRDR)
@@ -2118,13 +2213,22 @@ if (continue_hostname == NULL
     DEBUG(D_transport) debug_printf("%susing DSN\n",
                        sx->peer_offered & OPTION_DSN ? "" : "not ");
 
     DEBUG(D_transport) debug_printf("%susing DSN\n",
                        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
     the business. The host name and address must be available when the
     authenticator's client driver is running. */
 
     /* 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
     the business. The host name and address must be available when the
     authenticator's client driver is running. */
 
-    switch (yield = smtp_auth(sx->buffer, sizeof(sx->buffer), sx->addrlist, sx->host,
-                             sx->ob, sx->esmtp, &sx->inblock, &sx->outblock))
+    switch (yield = smtp_auth(sx, sx->buffer, sizeof(sx->buffer)))
       {
       default:         goto SEND_QUIT;
       case OK:         break;
       {
       default:         goto SEND_QUIT;
       case OK:         break;
@@ -2133,7 +2237,7 @@ if (continue_hostname == NULL
       }
     }
   }
       }
     }
   }
-pipelining_active = !!(smtp_peer_options & OPTION_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. */
@@ -2143,6 +2247,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)
@@ -2156,6 +2292,22 @@ 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;
@@ -2168,6 +2320,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:
@@ -2175,6 +2328,7 @@ 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;
 
   EHLOHELO_FAILED:
     goto FAILED;
 
   EHLOHELO_FAILED:
@@ -2182,6 +2336,7 @@ return OK;
     message = string_sprintf("Remote host closed connection in response to %s"
       " (EHLO response was: %s)", smtp_command, sx->buffer);
     sx->send_quit = FALSE;
     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;
 
   /* This label is jumped to directly when a TLS negotiation has failed,
     goto FAILED;
 
   /* This label is jumped to directly when a TLS negotiation has failed,
@@ -2191,7 +2346,13 @@ return OK;
 
 #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
 
     goto FAILED;
 #endif
 
@@ -2224,17 +2385,20 @@ FAILED:
            , sx->smtp_greeting, sx->helo_response
 #endif
            );
            , sx->smtp_greeting, sx->helo_response
 #endif
            );
-  yield = DEFER;
   }
 
 
 SEND_QUIT:
 
 if (sx->send_quit)
   }
 
 
 SEND_QUIT:
 
 if (sx->send_quit)
-  (void)smtp_write_command(&sx->outblock, SCMD_FLUSH, "QUIT\r\n");
+  (void)smtp_write_command(sx, SCMD_FLUSH, "QUIT\r\n");
 
 #ifdef SUPPORT_TLS
 
 #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
@@ -2245,14 +2409,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);
@@ -2326,6 +2490,11 @@ if (  sx->peer_offered & OPTION_UTF8
   Ustrcpy(p, " SMTPUTF8"), p += 9;
 #endif
 
   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;
@@ -2460,7 +2629,7 @@ sx->pending_MAIL = TRUE;     /* The block starts with MAIL */
     }
 #endif
 
     }
 #endif
 
-  rc = smtp_write_command(&sx->outblock, pipelining_active ? SCMD_BUFFER : SCMD_FLUSH,
+  rc = smtp_write_command(sx, pipelining_active ? SCMD_BUFFER : SCMD_FLUSH,
          "MAIL FROM:<%s>%s\r\n", s, sx->buffer);
   }
 
          "MAIL FROM:<%s>%s\r\n", s, sx->buffer);
   }
 
@@ -2472,7 +2641,7 @@ switch(rc)
     return -5;
 
   case +1:                /* Cmd was sent */
     return -5;
 
   case +1:                /* Cmd was sent */
-    if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), '2',
+    if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
        sx->ob->command_timeout))
       {
       if (errno == 0 && sx->buffer[0] == '4')
        sx->ob->command_timeout))
       {
       if (errno == 0 && sx->buffer[0] == '4')
@@ -2511,7 +2680,7 @@ for (addr = sx->first_addr, address_count = 0;
   BOOL no_flush;
   uschar * rcpt_addr;
 
   BOOL no_flush;
   uschar * rcpt_addr;
 
-  if (tcp_out_fastopen && !tcp_out_fastopen_logged)
+  if (tcp_out_fastopen && !f.tcp_out_fastopen_logged)
     {
     setflag(addr, af_tcp_fastopen_conn);
     if (tcp_out_fastopen > 1) setflag(addr, af_tcp_fastopen);
     {
     setflag(addr, af_tcp_fastopen_conn);
     if (tcp_out_fastopen > 1) setflag(addr, af_tcp_fastopen);
@@ -2544,7 +2713,7 @@ for (addr = sx->first_addr, address_count = 0;
     }
 #endif
 
     }
 #endif
 
-  count = smtp_write_command(&sx->outblock, no_flush ? SCMD_BUFFER : SCMD_FLUSH,
+  count = smtp_write_command(sx, no_flush ? SCMD_BUFFER : SCMD_FLUSH,
     "RCPT TO:<%s>%s%s\r\n", rcpt_addr, sx->igquotstr, sx->buffer);
 
   if (count < 0) return -5;
     "RCPT TO:<%s>%s%s\r\n", rcpt_addr, sx->igquotstr, sx->buffer);
 
   if (count < 0) return -5;
@@ -2570,7 +2739,7 @@ for (addr = sx->first_addr, address_count = 0;
     }
   }      /* Loop for next address */
 
     }
   }      /* Loop for next address */
 
-tcp_out_fastopen_logged = TRUE;
+f.tcp_out_fastopen_logged = TRUE;
 sx->next_addr = addr;
 return 0;
 }
 sx->next_addr = addr;
 return 0;
 }
@@ -2588,6 +2757,7 @@ Do blocking full-size writes, and reads under a timeout.  Once both input
 channels are closed, exit the process.
 
 Arguments:
 channels are closed, exit the process.
 
 Arguments:
+  ct_ctx       tls context
   buf          space to use for buffering
   bufsiz       size of buffer
   pfd          pipe filedescriptor array; [0] is comms to proxied process
   buf          space to use for buffering
   bufsiz       size of buffer
   pfd          pipe filedescriptor array; [0] is comms to proxied process
@@ -2595,10 +2765,11 @@ Arguments:
 */
 
 void
 */
 
 void
-smtp_proxy_tls(uschar * buf, size_t bsize, int * pfd, int timeout)
+smtp_proxy_tls(void * ct_ctx, uschar * buf, size_t bsize, int * pfd,
+  int timeout)
 {
 fd_set rfds, efds;
 {
 fd_set rfds, efds;
-int max_fd = MAX(pfd[0], tls_out.active) + 1;
+int max_fd = MAX(pfd[0], tls_out.active.sock) + 1;
 int rc, i, fd_bits, nbytes;
 
 close(pfd[1]);
 int rc, i, fd_bits, nbytes;
 
 close(pfd[1]);
@@ -2608,10 +2779,10 @@ if ((rc = fork()))
   _exit(rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
   }
 
   _exit(rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
   }
 
-if (running_in_test_harness) millisleep(100); /* let parent debug out */
+if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
 set_process_info("proxying TLS connection for continued transport");
 FD_ZERO(&rfds);
 set_process_info("proxying TLS connection for continued transport");
 FD_ZERO(&rfds);
-FD_SET(tls_out.active, &rfds);
+FD_SET(tls_out.active.sock, &rfds);
 FD_SET(pfd[0], &rfds);
 
 for (fd_bits = 3; fd_bits; )
 FD_SET(pfd[0], &rfds);
 
 for (fd_bits = 3; fd_bits; )
@@ -2637,21 +2808,21 @@ for (fd_bits = 3; fd_bits; )
       goto done;
       }
 
       goto done;
       }
 
-    if (FD_ISSET(tls_out.active, &efds) || FD_ISSET(pfd[0], &efds))
+    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;
       }
     }
       {
       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, &rfds) || FD_ISSET(pfd[0], &rfds)));
+  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, &rfds))
-    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, &rfds);
+      FD_CLR(tls_out.active.sock, &rfds);
       shutdown(pfd[0], SHUT_WR);
       timeout = 5;
       }
       shutdown(pfd[0], SHUT_WR);
       timeout = 5;
       }
@@ -2661,19 +2832,20 @@ for (fd_bits = 3; fd_bits; )
        if ((i = write(pfd[0], buf + nbytes, rc - nbytes)) < 0) goto done;
       }
   else if (fd_bits & 1)
        if ((i = write(pfd[0], buf + nbytes, rc - nbytes)) < 0) goto done;
       }
   else if (fd_bits & 1)
-    FD_SET(tls_out.active, &rfds);
+    FD_SET(tls_out.active.sock, &rfds);
 
   /* handle outbound data */
   if (FD_ISSET(pfd[0], &rfds))
     if ((rc = read(pfd[0], buf, bsize)) <= 0)
       {
       fd_bits = 0;
 
   /* handle outbound data */
   if (FD_ISSET(pfd[0], &rfds))
     if ((rc = read(pfd[0], buf, bsize)) <= 0)
       {
       fd_bits = 0;
-      tls_close(FALSE, TRUE);
+      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)
+       if ((i = tls_write(ct_ctx, buf + nbytes, rc - nbytes, FALSE)) < 0)
          goto done;
       }
   else if (fd_bits & 2)
          goto done;
       }
   else if (fd_bits & 2)
@@ -2681,7 +2853,7 @@ for (fd_bits = 3; fd_bits; )
   }
 
 done:
   }
 
 done:
-  if (running_in_test_harness) millisleep(100);        /* let logging complete */
+  if (f.running_in_test_harness) millisleep(100);      /* let logging complete */
   exim_exit(0, US"TLS proxy");
 }
 #endif
   exim_exit(0, US"TLS proxy");
 }
 #endif
@@ -2879,7 +3051,7 @@ to send is. */
 if (  !(sx.peer_offered & OPTION_CHUNKING)
    && (sx.ok || (pipelining_active && !mua_wrapper)))
   {
 if (  !(sx.peer_offered & OPTION_CHUNKING)
    && (sx.ok || (pipelining_active && !mua_wrapper)))
   {
-  int count = smtp_write_command(&sx.outblock, SCMD_FLUSH, "DATA\r\n");
+  int count = smtp_write_command(&sx, SCMD_FLUSH, "DATA\r\n");
 
   if (count < 0) goto SEND_FAILED;
   switch(sync_responses(&sx, count, sx.ok ? +1 : -1))
 
   if (count < 0) goto SEND_FAILED;
   switch(sync_responses(&sx, count, sx.ok ? +1 : -1))
@@ -2916,7 +3088,7 @@ if (!(sx.peer_offered & OPTION_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 */
@@ -2965,6 +3137,30 @@ else
   transport_count = 0;
 
 #ifndef DISABLE_DKIM
   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 (!f.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);
@@ -3018,7 +3214,7 @@ else
    * with per non-PRDR. */
   if(sx.prdr_active)
     {
    * with per non-PRDR. */
   if(sx.prdr_active)
     {
-    sx.ok = smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer), '3',
+    sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '3',
       sx.ob->final_timeout);
     if (!sx.ok && errno == 0) switch(sx.buffer[0])
       {
       sx.ob->final_timeout);
     if (!sx.ok && errno == 0) switch(sx.buffer[0])
       {
@@ -3039,7 +3235,7 @@ else
 
   if (!sx.lmtp)
     {
 
   if (!sx.lmtp)
     {
-    sx.ok = smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer), '2',
+    sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2',
       sx.ob->final_timeout);
     if (!sx.ok && errno == 0 && sx.buffer[0] == '4')
       {
       sx.ob->final_timeout);
     if (!sx.ok && errno == 0 && sx.buffer[0] == '4')
       {
@@ -3103,7 +3299,7 @@ else
       if (sx.lmtp)
 #endif
         {
       if (sx.lmtp)
 #endif
         {
-        if (!smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer), '2',
+        if (!smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2',
             sx.ob->final_timeout))
           {
           if (errno != 0 || sx.buffer[0] == 0) goto RESPONSE_FAILED;
             sx.ob->final_timeout))
           {
           if (errno != 0 || sx.buffer[0] == 0) goto RESPONSE_FAILED;
@@ -3147,6 +3343,8 @@ else
       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
       if (sx.prdr_active) setflag(addr, af_prdr_used);
 #endif
 #ifndef DISABLE_PRDR
       if (sx.prdr_active) setflag(addr, af_prdr_used);
 #endif
@@ -3178,9 +3376,12 @@ 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.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2',
           sx.ob->final_timeout);
         if (!sx.ok)
          {
           sx.ob->final_timeout);
         if (!sx.ok)
          {
@@ -3195,7 +3396,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)
            {
@@ -3381,7 +3589,7 @@ hosts_nopass_tls. */
 DEBUG(D_transport)
   debug_printf("ok=%d send_quit=%d send_rset=%d continue_more=%d "
     "yield=%d first_address is %sNULL\n", sx.ok, sx.send_quit,
 DEBUG(D_transport)
   debug_printf("ok=%d send_quit=%d send_rset=%d continue_more=%d "
     "yield=%d first_address is %sNULL\n", sx.ok, sx.send_quit,
-    sx.send_rset, continue_more, yield, sx.first_addr ? "not " : "");
+    sx.send_rset, f.continue_more, yield, sx.first_addr ? "not " : "");
 
 if (sx.completed_addr && sx.ok && sx.send_quit)
   {
 
 if (sx.completed_addr && sx.ok && sx.send_quit)
   {
@@ -3392,11 +3600,11 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
   t_compare.current_sender_address = sender_address;
 
   if (  sx.first_addr != NULL
   t_compare.current_sender_address = sender_address;
 
   if (  sx.first_addr != NULL
-     || continue_more
+     || f.continue_more
      || (
 #ifdef SUPPORT_TLS
      || (
 #ifdef SUPPORT_TLS
-          (  tls_out.active < 0  &&  !continue_proxy_cipher
-           || verify_check_given_host(&sx.ob->hosts_nopass_tls, host) != OK
+          (  tls_out.active.sock < 0  &&  !continue_proxy_cipher
+           || verify_check_given_host(CUSS &sx.ob->hosts_nopass_tls, host) != OK
           )
         &&
 #endif
           )
         &&
 #endif
@@ -3409,13 +3617,13 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
     BOOL pass_message;
 
     if (sx.send_rset)
     BOOL pass_message;
 
     if (sx.send_rset)
-      if (! (sx.ok = smtp_write_command(&sx.outblock, SCMD_FLUSH, "RSET\r\n") >= 0))
+      if (! (sx.ok = smtp_write_command(&sx, SCMD_FLUSH, "RSET\r\n") >= 0))
         {
         msg = US string_sprintf("send() to %s [%s] failed: %s", host->name,
           host->address, strerror(errno));
         sx.send_quit = FALSE;
         }
         {
         msg = US string_sprintf("send() to %s [%s] failed: %s", host->name,
           host->address, strerror(errno));
         sx.send_quit = FALSE;
         }
-      else if (! (sx.ok = smtp_read_response(&sx.inblock, sx.buffer,
+      else if (! (sx.ok = smtp_read_response(&sx, sx.buffer,
                  sizeof(sx.buffer), '2', sx.ob->command_timeout)))
         {
         int code;
                  sizeof(sx.buffer), '2', sx.ob->command_timeout)))
         {
         int code;
@@ -3432,8 +3640,10 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
 
     if (sx.ok)
       {
 
     if (sx.ok)
       {
+#ifdef SUPPORT_TLS
       int pfd[2];
       int pfd[2];
-      int socket_fd = sx.inblock.sock;
+#endif
+      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 */
@@ -3448,9 +3658,9 @@ 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 (  continue_more
-          || verify_check_given_host(&sx.ob->hosts_noproxy_tls, host) == OK)
+      if (tls_out.active.sock >= 0)
+       if (  f.continue_more
+          || verify_check_given_host(CUSS &sx.ob->hosts_noproxy_tls, host) == OK)
          {
          /* Before passing the socket on, or returning to caller with it still
          open, we must shut down TLS.  Not all MTAs allow for the continuation
          {
          /* Before passing the socket on, or returning to caller with it still
          open, we must shut down TLS.  Not all MTAs allow for the continuation
@@ -3458,15 +3668,16 @@ 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_peer_options = smtp_peer_options_wrap;
          sx.ok = !sx.smtps
-           && smtp_write_command(&sx.outblock, SCMD_FLUSH,
-                                     "EHLO %s\r\n", sx.helo_data) >= 0
-           && smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer),
+           && smtp_write_command(&sx, SCMD_FLUSH, "EHLO %s\r\n", sx.helo_data)
+               >= 0
+           && smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer),
                                      '2', sx.ob->command_timeout);
 
                                      '2', sx.ob->command_timeout);
 
-         if (sx.ok && continue_more)
+         if (sx.ok && f.continue_more)
            return yield;               /* More addresses for another run */
          }
        else
            return yield;               /* More addresses for another run */
          }
        else
@@ -3486,7 +3697,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
          }
       else
 #endif
          }
       else
 #endif
-       if (continue_more)
+       if (f.continue_more)
          return yield;                 /* More addresses for another run */
 
       /* If the socket is successfully passed, we mustn't send QUIT (or
          return yield;                 /* More addresses for another run */
 
       /* If the socket is successfully passed, we mustn't send QUIT (or
@@ -3506,14 +3717,14 @@ propagate it from the initial
        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
        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();
          if (pid == 0)         /* child; fork again to disconnect totally */
            {
          {
          int pid = fork();
          if (pid == 0)         /* child; fork again to disconnect totally */
            {
-           if (running_in_test_harness) millisleep(100); /* let parent debug out */
+           if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
            /* does not return */
            /* does not return */
-           smtp_proxy_tls(sx.buffer, sizeof(sx.buffer), pfd,
+           smtp_proxy_tls(sx.cctx.tls_ctx, sx.buffer, sizeof(sx.buffer), pfd,
                            sx.ob->command_timeout);
            }
 
                            sx.ob->command_timeout);
            }
 
@@ -3523,8 +3734,10 @@ propagate it from the initial
            close(pfd[0]);
            /* tidy the inter-proc to disconn the proxy proc */
            waitpid(pid, NULL, 0);
            close(pfd[0]);
            /* tidy the inter-proc to disconn the proxy proc */
            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;
@@ -3564,12 +3777,13 @@ This change is being made on 31-Jul-98. After over a year of trouble-free
 operation, the old commented-out code was removed on 17-Sep-99. */
 
 SEND_QUIT:
 operation, the old commented-out code was removed on 17-Sep-99. */
 
 SEND_QUIT:
-if (sx.send_quit) (void)smtp_write_command(&sx.outblock, SCMD_FLUSH, "QUIT\r\n");
+if (sx.send_quit) (void)smtp_write_command(&sx, 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
@@ -3585,12 +3799,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);
@@ -3626,29 +3840,32 @@ 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;
-smtp_inblock inblock;
-smtp_outblock outblock;
+client_conn_ctx cctx;
+smtp_context sx;
 uschar buffer[256];
 uschar inbuffer[4096];
 uschar outbuffer[16];
 
 uschar buffer[256];
 uschar inbuffer[4096];
 uschar outbuffer[16];
 
-inblock.sock = fileno(stdin);
-inblock.buffer = inbuffer;
-inblock.buffersize = sizeof(inbuffer);
-inblock.ptr = inbuffer;
-inblock.ptrend = inbuffer;
-
-outblock.sock = inblock.sock;
-outblock.buffersize = sizeof(outbuffer);
-outblock.buffer = outbuffer;
-outblock.ptr = outbuffer;
-outblock.cmd_count = 0;
-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)close(inblock.sock);
+/*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;
+
+sx.inblock.cctx = &cctx;
+sx.inblock.buffer = inbuffer;
+sx.inblock.buffersize = sizeof(inbuffer);
+sx.inblock.ptr = inbuffer;
+sx.inblock.ptrend = inbuffer;
+
+sx.outblock.cctx = &cctx;
+sx.outblock.buffersize = sizeof(outbuffer);
+sx.outblock.buffer = outbuffer;
+sx.outblock.ptr = outbuffer;
+sx.outblock.cmd_count = 0;
+sx.outblock.authenticating = FALSE;
+
+(void)smtp_write_command(&sx, SCMD_FLUSH, "QUIT\r\n");
+(void)smtp_read_response(&sx, buffer, sizeof(buffer), '2', ob->command_timeout);
+(void)close(cctx.sock);
 }
 
 
 }
 
 
@@ -3749,7 +3966,7 @@ DEBUG(D_transport)
   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
@@ -3764,6 +3981,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)
@@ -3795,7 +4018,7 @@ if (!hostlist || (ob->hosts_override && ob->hosts))
         {
         addrlist->message = string_sprintf("failed to expand list of hosts "
           "\"%s\" in %s transport: %s", s, tblock->name, expand_string_message);
         {
         addrlist->message = string_sprintf("failed to expand list of hosts "
           "\"%s\" in %s transport: %s", s, tblock->name, expand_string_message);
-        addrlist->transport_return = search_find_defer ? DEFER : PANIC;
+        addrlist->transport_return = f.search_find_defer ? DEFER : PANIC;
         return FALSE;     /* Only top address has status */
         }
       DEBUG(D_transport) debug_printf("expanded list of hosts \"%s\" to "
         return FALSE;     /* Only top address has status */
         }
       DEBUG(D_transport) debug_printf("expanded list of hosts \"%s\" to "
@@ -3992,7 +4215,7 @@ retry_non_continued:
       /* 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;
 
@@ -4083,7 +4306,7 @@ retry_non_continued:
     were not in it. We don't want to hold up all SMTP deliveries! Except when
     doing a two-stage queue run, don't do this if forcing. */
 
     were not in it. We don't want to hold up all SMTP deliveries! Except when
     doing a two-stage queue run, don't do this if forcing. */
 
-    if ((!deliver_force || queue_2stage) && (queue_smtp ||
+    if ((!f.deliver_force || f.queue_2stage) && (f.queue_smtp ||
         match_isinlist(addrlist->domain,
          (const uschar **)&queue_smtp_domains, 0,
           &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK))
         match_isinlist(addrlist->domain,
          (const uschar **)&queue_smtp_domains, 0,
           &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK))
@@ -4214,7 +4437,7 @@ retry_non_continued:
     sending the message down a pre-existing connection. */
 
     if (  !continue_hostname
     sending the message down a pre-existing connection. */
 
     if (  !continue_hostname
-       && verify_check_given_host(&ob->serialize_hosts, host) == OK)
+       && verify_check_given_host(CUSS &ob->serialize_hosts, host) == OK)
       {
       serialize_key = string_sprintf("host-serialize-%s", host->name);
       if (!enq_start(serialize_key, 1))
       {
       serialize_key = string_sprintf("host-serialize-%s", host->name);
       if (!enq_start(serialize_key, 1))
@@ -4247,7 +4470,7 @@ retry_non_continued:
     /* This is not for real; don't do the delivery. If there are
     any remaining hosts, list them. */
 
     /* This is not for real; don't do the delivery. If there are
     any remaining hosts, list them. */
 
-    if (dont_deliver)
+    if (f.dont_deliver)
       {
       host_item *host2;
       set_errno_nohost(addrlist, 0, NULL, OK, FALSE);
       {
       host_item *host2;
       set_errno_nohost(addrlist, 0, NULL, OK, FALSE);
@@ -4355,7 +4578,7 @@ retry_non_continued:
       if (  rc == DEFER
         && first_addr->basic_errno == ERRNO_TLSFAILURE
         && ob->tls_tempfail_tryclear
       if (  rc == DEFER
         && first_addr->basic_errno == ERRNO_TLSFAILURE
         && ob->tls_tempfail_tryclear
-        && verify_check_given_host(&ob->hosts_require_tls, host) != OK
+        && verify_check_given_host(CUSS &ob->hosts_require_tls, host) != OK
         )
         {
         log_write(0, LOG_MAIN,
         )
         {
         log_write(0, LOG_MAIN,
@@ -4525,6 +4748,7 @@ retry_non_continued:
           "hosts_max_try (message older than host's retry time)\n");
         }
       }
           "hosts_max_try (message older than host's retry time)\n");
         }
       }
+    if (f.running_in_test_harness) millisleep(500); /* let server debug out */
     }   /* End of loop for trying multiple hosts. */
 
   /* If we failed to find a matching host in the list, for an already-open
     }   /* End of loop for trying multiple hosts. */
 
   /* If we failed to find a matching host in the list, for an already-open
@@ -4534,21 +4758,27 @@ retry_non_continued:
 
   if (continue_hostname && !continue_host_tried)
     {
 
   if (continue_hostname && !continue_host_tried)
     {
-    int fd = cutthrough.fd >= 0 ? cutthrough.fd : 0;
+    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
 
     DEBUG(D_transport) debug_printf("no hosts match already-open connection\n");
 #ifdef SUPPORT_TLS
-    if (tls_out.active == fd)
+    /* 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(FALSE, US"QUIT\r\n", 6, FALSE);
-      tls_close(FALSE, TRUE);
+      (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);
       }
     else
 #else
       (void) write(fd, US"QUIT\r\n", 6);
 #endif
     (void) close(fd);
-    cutthrough.fd = -1;
+    cutthrough.cctx.sock = -1;
     continue_hostname = NULL;
     goto retry_non_continued;
     }
     continue_hostname = NULL;
     goto retry_non_continued;
     }
@@ -4612,7 +4842,7 @@ for (addr = addrlist; addr; addr = addr->next)
       setflag(addr, af_retry_skipped);
       }
 
       setflag(addr, af_retry_skipped);
       }
 
-  if (queue_smtp)    /* no deliveries attempted */
+  if (f.queue_smtp)    /* no deliveries attempted */
     {
     addr->transport_return = DEFER;
     addr->basic_errno = 0;
     {
     addr->transport_return = DEFER;
     addr->basic_errno = 0;