TLS: variables $tls_(in,out)_tlsver
[exim.git] / src / src / transports / smtp.c
index 98ab002821f4dacd90a163803bf9fa3debb73588..dee546ce1baa2a9d3ed6244b4dc33b7ebd85971c 100644 (file)
@@ -8,6 +8,10 @@
 #include "../exim.h"
 #include "smtp.h"
 
+#if defined(SUPPORT_DANE) && defined(DISABLE_TLS)
+# error TLS is required for DANE
+#endif
+
 
 /* Options specific to the smtp transport. This transport also supports LMTP
 over TCP/IP. The options must be in alphabetic order (note that "_" comes
@@ -90,7 +94,7 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, hosts_avoid_esmtp) },
   { "hosts_avoid_pipelining", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_avoid_pipelining) },
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   { "hosts_avoid_tls",      opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_avoid_tls) },
 #endif
@@ -98,7 +102,7 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, hosts_max_try) },
   { "hosts_max_try_hardlimit", opt_int,
       (void *)offsetof(smtp_transport_options_block, hosts_max_try_hardlimit) },
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   { "hosts_nopass_tls",     opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_nopass_tls) },
   { "hosts_noproxy_tls",    opt_stringptr,
@@ -106,19 +110,19 @@ optionlist smtp_transport_options[] = {
 #endif
   { "hosts_override",       opt_bool,
       (void *)offsetof(smtp_transport_options_block, hosts_override) },
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   { "hosts_pipe_connect",   opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_pipe_connect) },
 #endif
   { "hosts_randomize",      opt_bool,
       (void *)offsetof(smtp_transport_options_block, hosts_randomize) },
-#if defined(SUPPORT_TLS) && !defined(DISABLE_OCSP)
+#if !defined(DISABLE_TLS) && !defined(DISABLE_OCSP)
   { "hosts_request_ocsp",   opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_request_ocsp) },
 #endif
   { "hosts_require_auth",   opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_require_auth) },
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 # ifdef SUPPORT_DANE
   { "hosts_require_dane",   opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_require_dane) },
@@ -134,7 +138,7 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, hosts_try_auth) },
   { "hosts_try_chunking",   opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_try_chunking) },
-#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
+#ifdef SUPPORT_DANE
   { "hosts_try_dane",       opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_try_dane) },
 #endif
@@ -144,7 +148,7 @@ optionlist smtp_transport_options[] = {
   { "hosts_try_prdr",       opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_try_prdr) },
 #endif
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   { "hosts_verify_avoid_tls", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_verify_avoid_tls) },
 #endif
@@ -172,7 +176,7 @@ optionlist smtp_transport_options[] = {
   { "socks_proxy",          opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, socks_proxy) },
 #endif
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   { "tls_certificate",      opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_certificate) },
   { "tls_crl",              opt_stringptr,
@@ -183,6 +187,10 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, tls_privatekey) },
   { "tls_require_ciphers",  opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_require_ciphers) },
+# ifdef EXPERIMENTAL_TLS_RESUME
+  { "tls_resumption_hosts", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, tls_resumption_hosts) },
+# endif
   { "tls_sni",              opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_sni) },
   { "tls_tempfail_tryclear", opt_bool,
@@ -236,11 +244,11 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   .hosts_require_auth =                NULL,
   .hosts_try_chunking =                US"*",
 #ifdef SUPPORT_DANE
-  .hosts_try_dane =            NULL,
+  .hosts_try_dane =            US"*",
   .hosts_require_dane =                NULL,
   .dane_require_tls_ciphers =  NULL,
 #endif
-  .hosts_try_fastopen =                NULL,
+  .hosts_try_fastopen =                US"*",
 #ifndef DISABLE_PRDR
   .hosts_try_prdr =            US"*",
 #endif
@@ -252,13 +260,13 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   .hosts_avoid_tls =           NULL,
   .hosts_verify_avoid_tls =    NULL,
   .hosts_avoid_pipelining =    NULL,
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   .hosts_pipe_connect =                NULL,
 #endif
   .hosts_avoid_esmtp =         NULL,
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   .hosts_nopass_tls =          NULL,
-  .hosts_noproxy_tls =         US"*",
+  .hosts_noproxy_tls =         NULL,
 #endif
   .command_timeout =           5*60,
   .connect_timeout =           5*60,
@@ -284,7 +292,7 @@ smtp_transport_options_block smtp_transport_option_defaults = {
 #ifdef SUPPORT_SOCKS
   .socks_proxy =               NULL,
 #endif
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   .tls_certificate =           NULL,
   .tls_crl =                   NULL,
   .tls_privatekey =            NULL,
@@ -293,6 +301,9 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   .tls_verify_certificates =   US"system",
   .tls_dh_min_bits =           EXIM_CLIENT_DH_DEFAULT_MIN_BITS,
   .tls_tempfail_tryclear =     TRUE,
+# ifdef EXPERIMENTAL_TLS_RESUME
+  .tls_resumption_hosts =      NULL,
+# endif
   .tls_verify_hosts =          NULL,
   .tls_try_verify_hosts =      US"*",
   .tls_verify_cert_hostnames = US"*",
@@ -346,6 +357,51 @@ static BOOL    pipelining_active;  /* current transaction is in pipe mode */
 static unsigned ehlo_response(uschar * buf, unsigned checks);
 
 
+/******************************************************************************/
+
+void
+smtp_deliver_init(void)
+{
+if (!regex_PIPELINING) regex_PIPELINING =
+  regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE);
+
+if (!regex_SIZE) regex_SIZE =
+  regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE);
+
+if (!regex_AUTH) regex_AUTH =
+  regex_must_compile(AUTHS_REGEX, FALSE, TRUE);
+
+#ifndef DISABLE_TLS
+if (!regex_STARTTLS) regex_STARTTLS =
+  regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
+#endif
+
+if (!regex_CHUNKING) regex_CHUNKING =
+  regex_must_compile(US"\\n250[\\s\\-]CHUNKING(\\s|\\n|$)", FALSE, TRUE);
+
+#ifndef DISABLE_PRDR
+if (!regex_PRDR) regex_PRDR =
+  regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE);
+#endif
+
+#ifdef SUPPORT_I18N
+if (!regex_UTF8) regex_UTF8 =
+  regex_must_compile(US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE);
+#endif
+
+if (!regex_DSN) regex_DSN  =
+  regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE);
+
+if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA =
+  regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE);
+
+#ifndef DISABLE_PIPE_CONNECT
+if (!regex_EARLY_PIPE) regex_EARLY_PIPE =
+  regex_must_compile(US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)", FALSE, TRUE);
+#endif
+}
+
+
 /*************************************************
 *             Setup entry point                  *
 *************************************************/
@@ -593,6 +649,11 @@ switch(*errno_value)
          pl, smtp_command, s);
     return FALSE;
 
+  case ERRNO_TLSFAILURE:       /* Handle bad first read; can happen with
+                               GnuTLS and TLS1.3 */
+    *message = US"bad first read from TLS conn";
+    return TRUE;
+
   case ERRNO_FILTER_FAIL:      /* Handle a failed filter process error;
                          can't send QUIT as we mustn't end the DATA. */
     *message = string_sprintf("transport filter process failed (%d)%s",
@@ -608,7 +669,7 @@ switch(*errno_value)
     return FALSE;
 
   case ERRNO_WRITEINCOMPLETE:  /* failure to write a complete data block */
-    *message = string_sprintf("failed to write a data block");
+    *message = US"failed to write a data block";
     return FALSE;
 
 #ifdef SUPPORT_I18N
@@ -798,12 +859,16 @@ if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
 #ifdef EXPERIMENTAL_DSN_INFO
 sx->helo_response = string_copy(sx->buffer);
 #endif
+#ifndef DISABLE_EVENT
+(void) event_raise(sx->conn_args.tblock->event_action,
+  US"smtp:ehlo", sx->buffer);
+#endif
 return TRUE;
 }
 
 
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
 static uschar *
 ehlo_cache_key(const smtp_context * sx)
 {
@@ -820,7 +885,7 @@ write_ehlo_cache_entry(const smtp_context * sx)
 {
 open_db dbblock, * dbm_file;
 
-if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE)))
+if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE)))
   {
   uschar * ehlo_resp_key = ehlo_cache_key(sx);
   dbdata_ehlo_resp er = { .data = sx->ehlo_resp };
@@ -840,7 +905,7 @@ invalidate_ehlo_cache_entry(smtp_context * sx)
 open_db dbblock, * dbm_file;
 
 if (  sx->early_pipe_active
-   && (dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE)))
+   && (dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE)))
   {
   uschar * ehlo_resp_key = ehlo_cache_key(sx);
   dbfn_delete(dbm_file, ehlo_resp_key);
@@ -854,7 +919,7 @@ read_ehlo_cache_entry(smtp_context * sx)
 open_db dbblock;
 open_db * dbm_file;
 
-if (!(dbm_file = dbfn_open(US"misc", O_RDONLY, &dbblock, FALSE)))
+if (!(dbm_file = dbfn_open(US"misc", O_RDONLY, &dbblock, FALSE, TRUE)))
   { DEBUG(D_transport) debug_printf("ehlo-cache: no misc DB\n"); }
 else
   {
@@ -867,7 +932,7 @@ else
     {
     DEBUG(D_transport) debug_printf("ehlo-resp record too old\n");
     dbfn_close(dbm_file);
-    if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE)))
+    if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE)))
       dbfn_delete(dbm_file, ehlo_resp_key);
     }
   else
@@ -941,6 +1006,7 @@ Arguments:
 
 Return:
  OK    all well
+ DEFER error on first read of TLS'd conn
  FAIL  SMTP error in response
 */
 int
@@ -948,6 +1014,7 @@ smtp_reap_early_pipe(smtp_context * sx, int * countp)
 {
 BOOL pending_BANNER = sx->pending_BANNER;
 BOOL pending_EHLO = sx->pending_EHLO;
+int rc = FAIL;
 
 sx->pending_BANNER = FALSE;    /* clear early to avoid recursion */
 sx->pending_EHLO = FALSE;
@@ -959,6 +1026,7 @@ if (pending_BANNER)
   if (!smtp_reap_banner(sx))
     {
     DEBUG(D_transport) debug_printf("bad banner\n");
+    if (tls_out.active.sock >= 0) rc = DEFER;
     goto fail;
     }
   }
@@ -973,6 +1041,7 @@ if (pending_EHLO)
   if (!smtp_reap_ehlo(sx))
     {
     DEBUG(D_transport) debug_printf("bad response for EHLO\n");
+    if (tls_out.active.sock >= 0) rc = DEFER;
     goto fail;
     }
 
@@ -983,7 +1052,7 @@ if (pending_EHLO)
       ? &sx->ehlo_resp.cleartext_auths : &sx->ehlo_resp.crypted_auths;
 
   peer_offered = ehlo_response(sx->buffer,
-         (tls_out.active.sock < 0 ?  OPTION_TLS : OPTION_REQUIRETLS)
+         (tls_out.active.sock < 0 ?  OPTION_TLS : 0)
        | OPTION_CHUNKING | OPTION_PRDR | OPTION_DSN | OPTION_PIPE | OPTION_SIZE
        | OPTION_UTF8 | OPTION_EARLY_PIPE
        );
@@ -1010,7 +1079,7 @@ return OK;
 fail:
   invalidate_ehlo_cache_entry(sx);
   (void) smtp_discard_responses(sx, sx->conn_args.ob, *countp);
-  return FAIL;
+  return rc;
 }
 #endif
 
@@ -1055,6 +1124,7 @@ Returns:      3 if at least one address had 2xx and one had 5xx
              -2 I/O or other non-response error for RCPT
              -3 DATA or MAIL failed - errno and buffer set
             -4 banner or EHLO failed (early-pipelining)
+            -5 banner or EHLO failed (early-pipelining, TLS)
 */
 
 static int
@@ -1064,9 +1134,10 @@ address_item * addr = sx->sync_addr;
 smtp_transport_options_block * ob = sx->conn_args.ob;
 int yield = 0;
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
-if (smtp_reap_early_pipe(sx, &count) != OK)
-  return -4;
+#ifndef DISABLE_PIPE_CONNECT
+int rc;
+if ((rc = smtp_reap_early_pipe(sx, &count)) != OK)
+  return rc == FAIL ? -4 : -5;
 #endif
 
 /* Handle the response for a MAIL command. On error, reinstate the original
@@ -1082,6 +1153,8 @@ if (sx->pending_MAIL)
     {
     DEBUG(D_transport) debug_printf("bad response for MAIL\n");
     Ustrcpy(big_buffer, mail_command);  /* Fits, because it came from there! */
+    if (errno == ERRNO_TLSFAILURE)
+      return -5;
     if (errno == 0 && sx->buffer[0] != 0)
       {
       int save_errno = 0;
@@ -1140,6 +1213,11 @@ while (count-- > 0)
       }
     }
 
+  /* Error on first TLS read */
+
+  else if (errno == ERRNO_TLSFAILURE)
+    return -5;
+
   /* Timeout while reading the response */
 
   else if (errno == ETIMEDOUT)
@@ -1159,8 +1237,14 @@ while (count-- > 0)
 
   else if (errno != 0 || sx->buffer[0] == 0)
     {
-    string_format(big_buffer, big_buffer_size, "RCPT TO:<%s>",
+    gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer }, * g = &gs;
+
+    /* Use taint-unchecked routines for writing into big_buffer, trusting
+    that we'll never expand it. */
+
+    g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "RCPT TO:<%s>",
       transport_rcpt_address(addr, sx->conn_args.tblock->rcpt_include_affixes));
+    string_from_gstring(g);
     return -2;
     }
 
@@ -1252,6 +1336,10 @@ if (pending_DATA != 0)
     int code;
     uschar *msg;
     BOOL pass_message;
+
+    if (errno == ERRNO_TLSFAILURE)     /* Error on first TLS read */
+      return -5;
+
     if (pending_DATA > 0 || (yield & 1) != 0)
       {
       if (errno == 0 && sx->buffer[0] == '4')
@@ -1368,7 +1456,7 @@ smtp_auth(smtp_context * sx)
 host_item * host = sx->conn_args.host;                 /* host to deliver to */
 smtp_transport_options_block * ob = sx->conn_args.ob;  /* transport options */
 int require_auth = verify_check_given_host(CUSS &ob->hosts_require_auth, host);
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
 unsigned short authbits = tls_out.active.sock >= 0
       ? sx->ehlo_resp.crypted_auths : sx->ehlo_resp.cleartext_auths;
 #endif
@@ -1384,7 +1472,7 @@ if (!regex_AUTH)
 
 if (  sx->esmtp
    &&
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
       sx->early_pipe_active ? authbits
       :
 #endif
@@ -1394,7 +1482,7 @@ if (  sx->esmtp
   uschar * names = NULL;
   expand_nmax = -1;                          /* reset */
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   if (!sx->early_pipe_active)
 #endif
     names = string_copyn(expand_nstring[1], expand_nlength[1]);
@@ -1405,12 +1493,10 @@ if (  sx->esmtp
   if (  require_auth == OK
      || verify_check_given_host(CUSS &ob->hosts_try_auth, host) == OK)
     {
-    auth_instance * au;
-
     DEBUG(D_transport) debug_printf("scanning authentication mechanisms\n");
     fail_reason = US"no common mechanisms were found";
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
     if (sx->early_pipe_active)
       {
       /* Scan our authenticators (which support use by a client and were offered
@@ -1419,6 +1505,7 @@ if (  sx->esmtp
       client function.  We are limited to supporting up to 16 authenticator
       public-names by the number of bits in a short. */
 
+      auth_instance * au;
       uschar bitnum;
       int rc;
 
@@ -1527,20 +1614,20 @@ Globals         f.smtp_authenticated
 Return True on error, otherwise buffer has (possibly empty) terminated string
 */
 
-BOOL
+static BOOL
 smtp_mail_auth_str(uschar *buffer, unsigned bufsize, address_item *addrlist,
                    smtp_transport_options_block *ob)
 {
-uschar *local_authenticated_sender = authenticated_sender;
+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, f.smtp_authenticated?"Y":"N");
 #endif
 
-if (ob->authenticated_sender != NULL)
+if (ob->authenticated_sender)
   {
   uschar *new = expand_string(ob->authenticated_sender);
-  if (new == NULL)
+  if (!new)
     {
     if (!f.expand_string_forcedfail)
       {
@@ -1550,17 +1637,17 @@ if (ob->authenticated_sender != NULL)
       return TRUE;
       }
     }
-  else if (new[0] != 0) local_authenticated_sender = new;
+  else if (*new) local_authenticated_sender = new;
   }
 
 /* Add the authenticated sender address if present */
 
-if ((f.smtp_authenticated || ob->authenticated_sender_force) &&
-    local_authenticated_sender != NULL)
+if (  (f.smtp_authenticated || ob->authenticated_sender_force)
+   && local_authenticated_sender)
   {
-  string_format(buffer, bufsize, " AUTH=%s",
+  string_format_nt(buffer, bufsize, " AUTH=%s",
     auth_xtextencode(local_authenticated_sender,
-    Ustrlen(local_authenticated_sender)));
+      Ustrlen(local_authenticated_sender)));
   client_authenticated_sender = string_copy(local_authenticated_sender);
   }
 else
@@ -1659,7 +1746,7 @@ smtp_local_identity(uschar * sender, struct transport_instance * tblock)
 address_item * addr1;
 uschar * if1 = US"";
 uschar * helo1 = US"";
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 uschar * tlsc1 = US"";
 #endif
 uschar * save_sender_address = sender_address;
@@ -1677,7 +1764,7 @@ if (ob->interface)
 if (ob->helo_data)
   helo1 = expand_string(ob->helo_data);
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 if (ob->tls_certificate)
   tlsc1 = expand_string(ob->tls_certificate);
 local_identity = string_sprintf ("%s^%s^%s", if1, helo1, tlsc1);
@@ -1726,13 +1813,7 @@ size_t bsize = Ustrlen(buf);
 
 /* debug_printf("%s: check for 0x%04x\n", __FUNCTION__, checks); */
 
-#ifdef SUPPORT_TLS
-# ifdef EXPERIMENTAL_REQUIRETLS
-if (  checks & OPTION_REQUIRETLS
-   && pcre_exec(regex_REQUIRETLS, NULL, CS buf,bsize, 0, PCRE_EOPT, NULL,0) < 0)
-# endif
-  checks &= ~OPTION_REQUIRETLS;
-
+#ifndef DISABLE_TLS
 if (  checks & OPTION_TLS
    && pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
 #endif
@@ -1772,7 +1853,7 @@ if (  checks & OPTION_SIZE
    && pcre_exec(regex_SIZE, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
   checks &= ~OPTION_SIZE;
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
 if (  checks & OPTION_EARLY_PIPE
    && pcre_exec(regex_EARLY_PIPE, NULL, CS buf, bsize, 0,
                PCRE_EOPT, NULL, 0) < 0)
@@ -1800,7 +1881,9 @@ Args:
    tc_chunk_last       add LAST option to SMTP BDAT command
    tc_reap_prev                reap response to previous SMTP commands
 
-Returns:       OK or ERROR
+Returns:
+  OK or ERROR
+  DEFER                        TLS error on first read (EHLO-resp); errno set
 */
 
 static int
@@ -1817,7 +1900,7 @@ there may be more writes (like, the chunk data) done soon. */
 
 if (chunk_size > 0)
   {
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   BOOL new_conn = !!(sx->outblock.conn_args);
 #endif
   if((cmd_count = smtp_write_command(sx,
@@ -1826,7 +1909,7 @@ if (chunk_size > 0)
      ) < 0) return ERROR;
   if (flags & tc_chunk_last)
     data_command = string_copy(big_buffer);  /* Save for later error message */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   /* That command write could have been the one that made the connection.
   Copy the fd from the client conn ctx (smtp transport specific) to the
   generic transport ctx. */
@@ -1857,10 +1940,12 @@ if (flags & tc_reap_prev  &&  prev_cmd_count > 0)
     case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */
     case 0: break;                     /* No 2xx or 5xx, but no probs */
 
-    case -1:                           /* Timeout on RCPT */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+    case -5: errno = ERRNO_TLSFAILURE;
+            return DEFER;
+#ifndef DISABLE_PIPE_CONNECT
     case -4:                           /* non-2xx for pipelined banner or EHLO */
 #endif
+    case -1:                           /* Timeout on RCPT */
     default: return ERROR;             /* I/O error, or any MAIL/DATA error */
     }
   cmd_count = 1;
@@ -1923,14 +2008,13 @@ Returns:          OK    - the connection was made and the delivery attempted;
 int
 smtp_setup_conn(smtp_context * sx, BOOL suppress_tls)
 {
-#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
-dns_answer tlsa_dnsa;
-#endif
 smtp_transport_options_block * ob = sx->conn_args.tblock->options_block;
 BOOL pass_message = FALSE;
 uschar * message = NULL;
 int yield = OK;
-int rc;
+#ifndef DISABLE_TLS
+uschar * tls_errstr;
+#endif
 
 sx->conn_args.ob = ob;
 
@@ -1946,12 +2030,12 @@ sx->esmtp_sent = FALSE;
 sx->utf8_needed = FALSE;
 #endif
 sx->dsn_all_lasthop = TRUE;
-#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
-sx->dane = FALSE;
+#ifdef SUPPORT_DANE
+sx->conn_args.dane = FALSE;
 sx->dane_required =
   verify_check_given_host(CUSS &ob->hosts_require_dane, sx->conn_args.host) == OK;
 #endif
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
 sx->early_pipe_active = sx->early_pipe_ok = FALSE;
 sx->ehlo_resp.cleartext_features = sx->ehlo_resp.crypted_features = 0;
 sx->pending_BANNER = sx->pending_EHLO = FALSE;
@@ -1993,10 +2077,14 @@ tls_out.cipher = NULL;  /* the one we may use for this transport */
 tls_out.ourcert = NULL;
 tls_out.peercert = NULL;
 tls_out.peerdn = NULL;
-#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
+#ifdef USE_OPENSSL
 tls_out.sni = NULL;
 #endif
 tls_out.ocsp = OCSP_NOT_REQ;
+#ifdef EXPERIMENTAL_TLS_RESUME
+tls_out.resumption = 0;
+#endif
+tls_out.ver = NULL;
 
 /* Flip the legacy TLS-related variables over to the outbound set in case
 they're used in the context of the transport.  Don't bother resetting
@@ -2005,7 +2093,7 @@ For verify, unflipped once the callout is dealt with */
 
 tls_modify_variables(&tls_out);
 
-#ifndef SUPPORT_TLS
+#ifdef DISABLE_TLS
 if (sx->smtps)
   {
   set_errno_nohost(sx->addrlist, ERRNO_TLSFAILURE, US"TLS support not available",
@@ -2027,7 +2115,7 @@ if (!continue_hostname)
 
   smtp_port_for_connect(sx->conn_args.host, sx->port);
 
-#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
+#ifdef SUPPORT_DANE
     /* Do TLSA lookup for DANE */
     {
     tls_out.dane_verified = FALSE;
@@ -2035,18 +2123,19 @@ if (!continue_hostname)
 
     if (sx->conn_args.host->dnssec == DS_YES)
       {
+      int rc;
       if(  sx->dane_required
        || verify_check_given_host(CUSS &ob->hosts_try_dane, sx->conn_args.host) == OK
        )
-       switch (rc = tlsa_lookup(sx->conn_args.host, &tlsa_dnsa, sx->dane_required))
+       switch (rc = tlsa_lookup(sx->conn_args.host, &sx->conn_args.tlsa_dnsa, sx->dane_required))
          {
-         case OK:              sx->dane = TRUE;
+         case OK:              sx->conn_args.dane = TRUE;
                                ob->tls_tempfail_tryclear = FALSE;
                                break;
          case FAIL_FORCED:     break;
          default:              set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
                                  string_sprintf("DANE error: tlsa lookup %s",
-                                   rc == DEFER ? "DEFER" : "FAIL"),
+                                   rc_to_string(rc)),
                                  rc, FALSE);
 # ifndef DISABLE_EVENT
                                (void) event_raise(sx->conn_args.tblock->event_action,
@@ -2076,18 +2165,30 @@ if (!continue_hostname)
   sx->inblock.cctx = sx->outblock.cctx = &sx->cctx;
   sx->avoid_option = sx->peer_offered = smtp_peer_options = 0;
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
-  if (verify_check_given_host(CUSS &ob->hosts_pipe_connect, sx->conn_args.host) == OK)
-    {
-    sx->early_pipe_ok = TRUE;
-    if (  read_ehlo_cache_entry(sx)
-       && sx->ehlo_resp.cleartext_features & OPTION_EARLY_PIPE)
+#ifndef DISABLE_PIPE_CONNECT
+  if (  verify_check_given_host(CUSS &ob->hosts_pipe_connect,
+                                           sx->conn_args.host) == OK)
+
+    /* We don't find out the local ip address until the connect, so if
+    the helo string might use it avoid doing early-pipelining. */
+
+    if (  !sx->helo_data
+       || !Ustrstr(sx->helo_data, "$sending_ip_address")
+       || Ustrstr(sx->helo_data, "def:sending_ip_address")
+       )
       {
-      DEBUG(D_transport) debug_printf("Using cached cleartext PIPE_CONNECT\n");
-      sx->early_pipe_active = TRUE;
-      sx->peer_offered = sx->ehlo_resp.cleartext_features;
+      sx->early_pipe_ok = TRUE;
+      if (  read_ehlo_cache_entry(sx)
+        && sx->ehlo_resp.cleartext_features & OPTION_EARLY_PIPE)
+       {
+       DEBUG(D_transport)
+         debug_printf("Using cached cleartext PIPE_CONNECT\n");
+       sx->early_pipe_active = TRUE;
+       sx->peer_offered = sx->ehlo_resp.cleartext_features;
+       }
       }
-    }
+    else DEBUG(D_transport)
+      debug_printf("helo needs $sending_ip_address\n");
 
   if (sx->early_pipe_active)
     sx->outblock.conn_args = &sx->conn_args;
@@ -2096,16 +2197,9 @@ if (!continue_hostname)
     {
     if ((sx->cctx.sock = smtp_connect(&sx->conn_args, NULL)) < 0)
       {
-      uschar * msg = NULL;
-      if (sx->verify)
-       {
-       msg = US strerror(errno);
-       HDEBUG(D_verify) debug_printf("connect: %s\n", msg);
-       }
       set_errno_nohost(sx->addrlist,
        errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
-       sx->verify ? string_sprintf("could not connect: %s", msg)
-              : NULL,
+       sx->verify ? US strerror(errno) : NULL,
        DEFER, FALSE);
       sx->send_quit = FALSE;
       return DEFER;
@@ -2146,7 +2240,7 @@ will be?  Somehow I doubt it. */
 
   if (!sx->smtps)
     {
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
     if (sx->early_pipe_active)
       {
       sx->pending_BANNER = TRUE;       /* sync_responses() must eventually handle */
@@ -2233,7 +2327,7 @@ goto SEND_QUIT;
   /* 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
   and be usable.  I can see this coming back to bite us. */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   if (sx->smtps)
     {
     smtp_peer_options |= OPTION_TLS;
@@ -2247,7 +2341,7 @@ goto SEND_QUIT;
   if (sx->esmtp)
     {
     if (smtp_write_command(sx,
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
          sx->early_pipe_active ? SCMD_BUFFER :
 #endif
            SCMD_FLUSH,
@@ -2255,7 +2349,7 @@ goto SEND_QUIT;
       goto SEND_FAILED;
     sx->esmtp_sent = TRUE;
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
     if (sx->early_pipe_active)
       {
       sx->pending_EHLO = TRUE;
@@ -2288,7 +2382,7 @@ goto SEND_QUIT;
     DEBUG(D_transport)
       debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   if (!sx->early_pipe_active)
 #endif
     if (!sx->esmtp)
@@ -2323,13 +2417,13 @@ goto SEND_QUIT;
 
   if (sx->esmtp || sx->lmtp)
     {
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
     if (!sx->early_pipe_active)
 #endif
       {
       sx->peer_offered = ehlo_response(sx->buffer,
        OPTION_TLS      /* others checked later */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
        | (sx->early_pipe_ok
          ?   OPTION_IGNQ
            | OPTION_CHUNKING | OPTION_PRDR | OPTION_DSN | OPTION_PIPE | OPTION_SIZE
@@ -2341,7 +2435,7 @@ goto SEND_QUIT;
          )
 #endif
        );
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
       if (sx->early_pipe_ok)
        {
        sx->ehlo_resp.cleartext_features = sx->peer_offered;
@@ -2359,7 +2453,7 @@ goto SEND_QUIT;
 
   /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
     smtp_peer_options |= sx->peer_offered & OPTION_TLS;
 #endif
     }
@@ -2421,7 +2515,7 @@ negative, the original EHLO data is available for subsequent analysis, should
 the client not be required to use TLS. If the response is bad, copy the buffer
 for error analysis. */
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 if (  smtp_peer_options & OPTION_TLS
    && !suppress_tls
    && verify_check_given_host(CUSS &ob->hosts_avoid_tls, sx->conn_args.host) != OK
@@ -2434,7 +2528,7 @@ if (  smtp_peer_options & OPTION_TLS
   if (smtp_write_command(sx, SCMD_FLUSH, "STARTTLS\r\n") < 0)
     goto SEND_FAILED;
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   /* If doing early-pipelining reap the banner and EHLO-response but leave
   the response for the STARTTLS we just sent alone. */
 
@@ -2471,28 +2565,22 @@ if (  smtp_peer_options & OPTION_TLS
   else
   TLS_NEGOTIATE:
     {
-    address_item * addr;
-    uschar * errstr;
-    sx->cctx.tls_ctx = tls_client_start(sx->cctx.sock, sx->conn_args.host,
-                           sx->addrlist, sx->conn_args.tblock,
-# ifdef SUPPORT_DANE
-                            sx->dane ? &tlsa_dnsa : NULL,
-# endif
-                            &tls_out, &errstr);
-
-    if (!sx->cctx.tls_ctx)
+    if (!tls_client_start(&sx->cctx, &sx->conn_args, sx->addrlist, &tls_out, &tls_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. */
-      DEBUG(D_tls) debug_printf("TLS session fail: %s\n", errstr);
+#ifdef USE_GNUTLS
+  GNUTLS_CONN_FAILED:
+#endif
+      DEBUG(D_tls) debug_printf("TLS session fail: %s\n", tls_errstr);
 
 # ifdef SUPPORT_DANE
-      if (sx->dane)
+      if (sx->conn_args.dane)
         {
        log_write(0, LOG_MAIN,
          "DANE attempt failed; TLS connection to %s [%s]: %s",
-         sx->conn_args.host->name, sx->conn_args.host->address, errstr);
+         sx->conn_args.host->name, sx->conn_args.host->address, tls_errstr);
 #  ifndef DISABLE_EVENT
        (void) event_raise(sx->conn_args.tblock->event_action,
          US"dane:fail", US"validation-failure");       /* could do with better detail */
@@ -2501,7 +2589,7 @@ if (  smtp_peer_options & OPTION_TLS
 # endif
 
       errno = ERRNO_TLSFAILURE;
-      message = string_sprintf("TLS session: %s", errstr);
+      message = string_sprintf("TLS session: %s", tls_errstr);
       sx->send_quit = FALSE;
       goto TLS_FAILED;
       }
@@ -2517,6 +2605,7 @@ if (  smtp_peer_options & OPTION_TLS
         addr->peercert = tls_out.peercert;
         addr->peerdn = tls_out.peerdn;
        addr->ocsp = tls_out.ocsp;
+        addr->tlsver = tls_out.ver;
         }
     }
   }
@@ -2544,7 +2633,7 @@ if (tls_out.active.sock >= 0)
     goto SEND_QUIT;
     }
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   /* For SMTPS there is no cleartext early-pipe; use the crypted permission bit.
   We're unlikely to get the group sent and delivered before the server sends its
   banner, but it's still worth sending as a group.
@@ -2562,7 +2651,7 @@ if (tls_out.active.sock >= 0)
 
   /* For SMTPS we need to wait for the initial OK response. */
   if (sx->smtps)
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
     if (sx->early_pipe_active)
       {
       sx->pending_BANNER = TRUE;
@@ -2585,21 +2674,36 @@ if (tls_out.active.sock >= 0)
     }
 
   if (smtp_write_command(sx,
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
        sx->early_pipe_active ? SCMD_BUFFER :
 #endif
          SCMD_FLUSH,
        "%s %s\r\n", greeting_cmd, sx->helo_data) < 0)
     goto SEND_FAILED;
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   if (sx->early_pipe_active)
     sx->pending_EHLO = TRUE;
   else
 #endif
     {
     if (!smtp_reap_ehlo(sx))
+#ifdef USE_GNUTLS
+      {
+      /* The GnuTLS layer in Exim only spots a server-rejection of a client
+      cert late, under TLS1.3 - which means here; the first time we try to
+      receive crypted data.  Treat it as if it was a connect-time failure.
+      See also the early-pipe equivalent... which will be hard; every call
+      to sync_responses will need to check the result.
+      It would be nicer to have GnuTLS check the cert during the handshake.
+      Can it do that, with all the flexibility we need? */
+
+      tls_errstr = US"error on first read";
+      goto GNUTLS_CONN_FAILED;
+      }
+#else
       goto RESPONSE_FAILED;
+#endif
     smtp_peer_options = 0;
     }
   }
@@ -2609,24 +2713,17 @@ have one. */
 
 else if (  sx->smtps
 # ifdef SUPPORT_DANE
-       || sx->dane
-# endif
-# ifdef EXPERIMENTAL_REQUIRETLS
-       || tls_requiretls & REQUIRETLS_MSG
+       || sx->conn_args.dane
 # endif
        || verify_check_given_host(CUSS &ob->hosts_require_tls, sx->conn_args.host) == OK
        )
   {
-  errno =
-# ifdef EXPERIMENTAL_REQUIRETLS
-      tls_requiretls & REQUIRETLS_MSG ? ERRNO_REQUIRETLS :
-# endif
-      ERRNO_TLSREQUIRED;
+  errno = 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");
 # if defined(SUPPORT_DANE) && !defined(DISABLE_EVENT)
-  if (sx->dane)
+  if (sx->conn_args.dane)
     (void) event_raise(sx->conn_args.tblock->event_action, US"dane:fail",
       smtp_peer_options & OPTION_TLS
       ? US"validation-failure"         /* could do with better detail */
@@ -2634,7 +2731,7 @@ else if (  sx->smtps
 # endif
   goto TLS_FAILED;
   }
-#endif /*SUPPORT_TLS*/
+#endif /*DISABLE_TLS*/
 
 /* If TLS is active, we have just started it up and re-done the EHLO command,
 so its response needs to be analyzed. If TLS is not active and this is a
@@ -2642,23 +2739,23 @@ continued session down a previously-used socket, we haven't just done EHLO, so
 we skip this. */
 
 if (continue_hostname == NULL
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
     || tls_out.active.sock >= 0
 #endif
     )
   {
   if (sx->esmtp || sx->lmtp)
     {
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   if (!sx->early_pipe_active)
 #endif
     {
     sx->peer_offered = ehlo_response(sx->buffer,
        0 /* no TLS */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
        | (sx->lmtp && ob->lmtp_ignore_quota ? OPTION_IGNQ : 0)
        | OPTION_DSN | OPTION_PIPE | OPTION_SIZE
-       | OPTION_CHUNKING | OPTION_PRDR | OPTION_UTF8 | OPTION_REQUIRETLS
+       | OPTION_CHUNKING | OPTION_PRDR | OPTION_UTF8
        | (tls_out.active.sock >= 0 ? OPTION_EARLY_PIPE : 0) /* not for lmtp */
 
 #else
@@ -2674,12 +2771,9 @@ if (continue_hostname == NULL
        | OPTION_DSN
        | OPTION_PIPE
        | (ob->size_addition >= 0 ? OPTION_SIZE : 0)
-# if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
-       | (tls_requiretls & REQUIRETLS_MSG ? OPTION_REQUIRETLS : 0)
-# endif
 #endif
       );
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
     if (tls_out.active.sock >= 0)
       sx->ehlo_resp.crypted_features = sx->peer_offered;
 #endif
@@ -2727,17 +2821,7 @@ if (continue_hostname == NULL
     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
-
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
     if (  sx->early_pipe_ok
        && !sx->early_pipe_active
        && tls_out.active.sock >= 0
@@ -2822,22 +2906,6 @@ if (sx->utf8_needed && !(sx->peer_offered & OPTION_UTF8))
   }
 #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;
 
 
@@ -2845,6 +2913,29 @@ return OK;
   int code;
 
   RESPONSE_FAILED:
+    if (errno == ECONNREFUSED) /* first-read error on a TFO conn */
+      {
+      /* There is a testing facility for simulating a connection timeout, as I
+      can't think of any other way of doing this. It converts a connection
+      refused into a timeout if the timeout is set to 999999.  This is done for
+      a 3whs connection in ip_connect(), but a TFO connection does not error
+      there - instead it gets ECONNREFUSED on the first data read.  Tracking
+      that a TFO really was done is too hard, or we would set a
+      sx->pending_conn_done bit and test that in smtp_reap_banner() and
+      smtp_reap_ehlo().  That would let us also add the conn-timeout to the
+      cmd-timeout. */
+
+      if (f.running_in_test_harness && ob->connect_timeout == 999999)
+       errno = ETIMEDOUT;
+      set_errno_nohost(sx->addrlist,
+       errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
+       sx->verify ? US strerror(errno) : NULL,
+       DEFER, FALSE);
+      sx->send_quit = FALSE;
+      return DEFER;
+      }
+
+    /* really an error on an SMTP read */
     message = NULL;
     sx->send_quit = check_response(sx->conn_args.host, &errno, sx->addrlist->more_errno,
       sx->buffer, &code, &message, &pass_message);
@@ -2872,15 +2963,9 @@ return OK;
   in message and errno, and setting_up will always be true. Treat as
   a temporary error. */
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   TLS_FAILED:
-# 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;
+    code = '4', yield = DEFER;
     goto FAILED;
 #endif
 
@@ -2908,7 +2993,8 @@ FAILED:
                        || errno == ERRNO_UTF8_FWD
 #endif
            ? FAIL : DEFER,
-           pass_message, sx->conn_args.host
+           pass_message,
+           errno == ECONNREFUSED ? NULL : sx->conn_args.host
 #ifdef EXPERIMENTAL_DSN_INFO
            , sx->smtp_greeting, sx->helo_response
 #endif
@@ -2921,7 +3007,7 @@ SEND_QUIT:
 if (sx->send_quit)
   (void)smtp_write_command(sx, SCMD_FLUSH, "QUIT\r\n");
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 if (sx->cctx.tls_ctx)
   {
   tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
@@ -3015,12 +3101,7 @@ if (  sx->peer_offered & OPTION_UTF8
    && 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;
+  Ustrcpy(p, US" SMTPUTF8"), p += 9;
 #endif
 
 /* check if all addresses have DSN-lasthop flag; do not send RET and ENVID if so */
@@ -3041,9 +3122,9 @@ for (sx->dsn_all_lasthop = TRUE, addr = addrlist, address_count = 0;
 if (sx->peer_offered & OPTION_DSN && !sx->dsn_all_lasthop)
   {
   if (dsn_ret == dsn_ret_hdrs)
-    { Ustrcpy(p, " RET=HDRS"); p += 9; }
+    { Ustrcpy(p, US" RET=HDRS"); p += 9; }
   else if (dsn_ret == dsn_ret_full)
-    { Ustrcpy(p, " RET=FULL"); p += 9; }
+    { Ustrcpy(p, US" RET=FULL"); p += 9; }
 
   if (dsn_envid)
     {
@@ -3080,7 +3161,7 @@ if (sx->peer_offered & OPTION_DSN && !(addr->dsn_flags & rf_dsnlasthop))
     {
     BOOL first = TRUE;
 
-    Ustrcpy(p, " NOTIFY=");
+    Ustrcpy(p, US" NOTIFY=");
     while (*p) p++;
     for (int i = 0; i < nelem(rf_list); i++) if (addr->dsn_flags & rf_list[i])
       {
@@ -3256,8 +3337,9 @@ for (addr = sx->first_addr, address_count = 0;
       case -2: return -2;                      /* non-MAIL read i/o error */
       default: return -1;                      /* any MAIL error */
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
       case -4: return -1;                      /* non-2xx for pipelined banner or EHLO */
+      case -5: return -1;                      /* TLS first-read error */
 #endif
       }
     sx->pending_MAIL = FALSE;            /* Dealt with MAIL */
@@ -3269,7 +3351,7 @@ return 0;
 }
 
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 /*****************************************************
 * Proxy TLS connection for another transport process *
 ******************************************************/
@@ -3286,6 +3368,8 @@ Arguments:
   bufsiz       size of buffer
   pfd          pipe filedescriptor array; [0] is comms to proxied process
   timeout      per-read timeout, seconds
+
+Does not return.
 */
 
 void
@@ -3303,7 +3387,7 @@ if ((rc = fork()))
   _exit(rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
   }
 
-if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
+testharness_pause_ms(100); /* let parent debug out */
 set_process_info("proxying TLS connection for continued transport");
 FD_ZERO(&rfds);
 FD_SET(tls_out.active.sock, &rfds);
@@ -3377,7 +3461,7 @@ for (int fd_bits = 3; fd_bits; )
   }
 
 done:
-  if (f.running_in_test_harness) millisleep(100);      /* let logging complete */
+  testharness_pause_ms(100);   /* let logging complete */
   exim_exit(0, US"TLS proxy");
 }
 #endif
@@ -3433,7 +3517,6 @@ smtp_deliver(address_item *addrlist, host_item *host, int host_af, int defport,
   uschar *interface, transport_instance *tblock,
   BOOL *message_defer, BOOL suppress_tls)
 {
-address_item *addr;
 smtp_transport_options_block * ob = SOB tblock->options_block;
 int yield = OK;
 int save_errno;
@@ -3490,6 +3573,11 @@ if (tblock->filter_command)
      && *transport_filter_argv
      && **transport_filter_argv
      && sx.peer_offered & OPTION_CHUNKING
+#ifndef DISABLE_DKIM
+    /* When dkim signing, chunking is handled even with a transport-filter */
+     && !(ob->dkim.dkim_private_key && ob->dkim.dkim_domain && ob->dkim.dkim_selector)
+     && !ob->dkim.force_bodyhash
+#endif
      )
     {
     sx.peer_offered &= ~OPTION_CHUNKING;
@@ -3584,11 +3672,12 @@ if (  !(sx.peer_offered & OPTION_CHUNKING)
 
     case 1: sx.ok = TRUE;            /* 2xx (only) => OK, but if LMTP, */
     if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */
-    case 0: break;                       /* No 2xx or 5xx, but no probs */
+    case 0: break;                     /* No 2xx or 5xx, but no probs */
 
-    case -1: goto END_OFF;               /* Timeout on RCPT */
+    case -1: goto END_OFF;             /* Timeout on RCPT */
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
+    case -5:                           /* TLS first-read error */
     case -4:  HDEBUG(D_transport)
                debug_printf("failed reaping pipelined cmd responses\n");
 #endif
@@ -3666,6 +3755,11 @@ else
   transport_count = 0;
 
 #ifndef DISABLE_DKIM
+  {
+# ifdef MEASURE_TIMING
+  struct timeval t0;
+  gettimeofday(&t0, NULL);
+# endif
   dkim_exim_sign_init();
 # ifdef EXPERIMENTAL_ARC
     {
@@ -3690,6 +3784,10 @@ else
       }
     }
 # endif
+# ifdef MEASURE_TIMING
+  report_time_since(&t0, US"dkim_exim_sign_init (delta)");
+# endif
+  }
   sx.ok = dkim_transport_write_message(&tctx, &ob->dkim, CUSS &message);
 #else
   sx.ok = transport_write_message(&tctx, 0);
@@ -3725,19 +3823,20 @@ else
       {
       case 3: sx.ok = TRUE;            /* 2xx & 5xx => OK & progress made */
       case 2: sx.completed_addr = TRUE;    /* 5xx (only) => progress made */
-      break;
+             break;
 
-      case 1: sx.ok = TRUE;            /* 2xx (only) => OK, but if LMTP, */
+      case 1: sx.ok = TRUE;            /* 2xx (only) => OK, but if LMTP, */
       if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */
-      case 0: break;                       /* No 2xx or 5xx, but no probs */
+      case 0: break;                   /* No 2xx or 5xx, but no probs */
 
-      case -1: goto END_OFF;               /* Timeout on RCPT */
+      case -1: goto END_OFF;           /* Timeout on RCPT */
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
+      case -5:                         /* TLS first-read error */
       case -4:  HDEBUG(D_transport)
                  debug_printf("failed reaping pipelined cmd responses\n");
 #endif
-      default: goto RESPONSE_FAILED;       /* I/O error, or any MAIL/DATA error */
+      default: goto RESPONSE_FAILED;   /* I/O error, or any MAIL/DATA error */
       }
     }
 
@@ -3884,7 +3983,7 @@ else
        if (tcp_out_fastopen >= TFO_USED_DATA) setflag(addr, af_tcp_fastopen_data);
        }
       if (sx.pipelining_used) setflag(addr, af_pipelining);
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
       if (sx.early_pipe_active) setflag(addr, af_early_pipe);
 #endif
 #ifndef DISABLE_PRDR
@@ -4086,7 +4185,7 @@ if (!sx.ok)
 
     else
       {
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
       /* If we were early-pipelinng and the actual EHLO response did not match
       the cached value we assumed, we could have detected it and passed a
       custom errno through to here.  It would be nice to RSET and retry right
@@ -4155,7 +4254,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
   if (  sx.first_addr != NULL
      || f.continue_more
      || (
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
           (  tls_out.active.sock < 0  &&  !continue_proxy_cipher
            || verify_check_given_host(CUSS &ob->hosts_nopass_tls, host) != OK
           )
@@ -4193,7 +4292,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
 
     if (sx.ok)
       {
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
       int pfd[2];
 #endif
       int socket_fd = sx.cctx.sock;
@@ -4210,7 +4309,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
       transport_pass_socket).  If the caller has more ready, just return with
       the connection still open. */
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
       if (tls_out.active.sock >= 0)
        if (  f.continue_more
           || verify_check_given_host(CUSS &ob->hosts_noproxy_tls, host) == OK)
@@ -4238,7 +4337,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
          /* Set up a pipe for proxying TLS for the new transport process */
 
          smtp_peer_options |= OPTION_TLS;
-         if (sx.ok = (socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0))
+         if ((sx.ok = socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0))
            socket_fd = pfd[1];
          else
            set_errno(sx.first_addr, errno, US"internal allocation problem",
@@ -4269,13 +4368,13 @@ propagate it from the initial
        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
+#ifndef DISABLE_TLS
        if (tls_out.active.sock >= 0)
          {
          int pid = fork();
          if (pid == 0)         /* child; fork again to disconnect totally */
            {
-           if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
+           testharness_pause_ms(100); /* let parent debug out */
            /* does not return */
            smtp_proxy_tls(sx.cctx.tls_ctx, sx.buffer, sizeof(sx.buffer), pfd,
                            ob->command_timeout);
@@ -4330,11 +4429,14 @@ 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:
+#ifdef TCP_CORK
+(void) setsockopt(sx.cctx.sock, IPPROTO_TCP, TCP_CORK, US &on, sizeof(on));
+#endif
 if (sx.send_quit) (void)smtp_write_command(&sx, SCMD_FLUSH, "QUIT\r\n");
 
 END_OFF:
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
 sx.cctx.tls_ctx = NULL;
 #endif
@@ -4353,7 +4455,8 @@ HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
 if (sx.send_quit)
   {
   shutdown(sx.cctx.sock, SHUT_WR);
-  millisleep(f.running_in_test_harness ? 200 : 20);
+  millisleep(20);
+  testharness_pause_ms(200);
   if (fcntl(sx.cctx.sock, F_SETFL, O_NONBLOCK) == 0)
     for (int i = 16; read(sx.cctx.sock, sx.inbuffer, sizeof(sx.inbuffer)) > 0 && i > 0;)
       i--;                             /* drain socket */
@@ -4453,12 +4556,13 @@ for (address_item * addr = addrlist; addr; addr = addr->next)
     addr->basic_errno = 0;
     addr->more_errno = (host->mx >= 0)? 'M' : 'A';
     addr->message = NULL;
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
     addr->cipher = NULL;
     addr->ourcert = NULL;
     addr->peercert = NULL;
     addr->peerdn = NULL;
     addr->ocsp = OCSP_NOT_REQ;
+    addr->tlsver = NULL;
 #endif
 #ifdef EXPERIMENTAL_DSN_INFO
     addr->smtp_greeting = NULL;
@@ -4493,14 +4597,13 @@ int hosts_retry = 0;
 int hosts_serial = 0;
 int hosts_total = 0;
 int total_hosts_tried = 0;
-address_item *addr;
 BOOL expired = TRUE;
 uschar *expanded_hosts = NULL;
 uschar *pistring;
 uschar *tid = string_sprintf("%s transport", tblock->name);
 smtp_transport_options_block *ob = SOB tblock->options_block;
 host_item *hostlist = addrlist->host_list;
-host_item *host;
+host_item *host = NULL;
 
 DEBUG(D_transport)
   {
@@ -4531,12 +4634,6 @@ 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. */
 
-#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)
@@ -4578,6 +4675,17 @@ if (!hostlist || (ob->hosts_override && ob->hosts))
     else
       if (ob->hosts_randomize) s = expanded_hosts = string_copy(s);
 
+    if (is_tainted(s))
+      {
+      log_write(0, LOG_MAIN|LOG_PANIC,
+       "attempt to use tainted host list '%s' from '%s' in transport %s",
+       s, ob->hosts, tblock->name);
+      /* Avoid leaking info to an attacker */
+      addrlist->message = US"internal configuration error";
+      addrlist->transport_return = PANIC;
+      return FALSE;
+      }
+
     host_build_hostlist(&hostlist, s, ob->hosts_randomize);
 
     /* Check that the expansion yielded something useful. */
@@ -4709,7 +4817,6 @@ retry_non_continued:
     {
     int rc;
     int host_af;
-    uschar *rs;
     BOOL host_is_expired = FALSE;
     BOOL message_defer = FALSE;
     BOOL some_deferred = FALSE;
@@ -4897,11 +5004,14 @@ retry_non_continued:
     treated separately. */
 
     host_af = Ustrchr(host->address, ':') == NULL ? AF_INET : AF_INET6;
-    if ((rs = ob->interface) && *rs)
       {
-      if (!smtp_get_interface(rs, host_af, addrlist, &interface, tid))
-       return FALSE;
-      pistring = string_sprintf("%s/%s", pistring, interface);
+      uschar * s = ob->interface;
+      if (s && *s)
+       {
+       if (!smtp_get_interface(s, host_af, addrlist, &interface, tid))
+         return FALSE;
+       pistring = string_sprintf("%s/%s", pistring, interface);
+       }
       }
 
     /* The first time round the outer loop, check the status of the host by
@@ -5060,7 +5170,7 @@ retry_non_continued:
 
       if (expanded_hosts)
        {
-       thost = store_get(sizeof(host_item));
+       thost = store_get(sizeof(host_item), FALSE);
        *thost = *host;
        thost->name = string_copy(host->name);
        thost->address = string_copy(host->address);
@@ -5121,7 +5231,7 @@ retry_non_continued:
       session, so the in-clear transmission after those errors, if permitted,
       happens inside smtp_deliver().] */
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
       if (  rc == DEFER
         && first_addr->basic_errno == ERRNO_TLSFAILURE
         && ob->tls_tempfail_tryclear
@@ -5141,19 +5251,14 @@ retry_non_continued:
           deferred_event_raise(first_addr, host);
 # endif
         }
-#endif /*SUPPORT_TLS*/
+#endif /*DISABLE_TLS*/
       }
 
     /* Delivery attempt finished */
 
-    rs = rc == OK ? US"OK"
-       : rc == DEFER ? US"DEFER"
-       : rc == ERROR ? US"ERROR"
-       : US"?";
-
     set_process_info("delivering %s: just tried %s [%s]%s for %s%s: result %s",
       message_id, host->name, host->address, pistring, addrlist->address,
-      addrlist->next ? " (& others)" : "", rs);
+      addrlist->next ? " (& others)" : "", rc_to_string(rc));
 
     /* Release serialization if set up */
 
@@ -5306,7 +5411,7 @@ retry_non_continued:
          ob->hosts_max_try_hardlimit);
       }
 
-    if (f.running_in_test_harness) millisleep(500); /* let server debug out */
+    testharness_pause_ms(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
@@ -5319,7 +5424,7 @@ retry_non_continued:
     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
+#ifndef DISABLE_TLS
     /* A TLS conn could be open for a cutthrough, but not for a plain continued-
     transport */
 /*XXX doublecheck that! */