TLS: variables $tls_(in,out)_tlsver
[exim.git] / src / src / transports / smtp.c
index aed80cae9ca49de05d36dea7f19de8d9681b2eed..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
@@ -106,7 +110,7 @@ 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
@@ -244,7 +248,7 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   .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
@@ -256,7 +260,7 @@ 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,
@@ -353,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                  *
 *************************************************/
@@ -620,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
@@ -810,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)
 {
@@ -1081,7 +1134,7 @@ address_item * addr = sx->sync_addr;
 smtp_transport_options_block * ob = sx->conn_args.ob;
 int yield = 0;
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
 int rc;
 if ((rc = smtp_reap_early_pipe(sx, &count)) != OK)
   return rc == FAIL ? -4 : -5;
@@ -1184,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;
     }
 
@@ -1397,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
@@ -1413,7 +1472,7 @@ if (!regex_AUTH)
 
 if (  sx->esmtp
    &&
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
       sx->early_pipe_active ? authbits
       :
 #endif
@@ -1423,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]);
@@ -1437,7 +1496,7 @@ if (  sx->esmtp
     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
@@ -1555,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)
       {
@@ -1578,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
@@ -1794,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)
@@ -1841,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,
@@ -1850,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. */
@@ -1883,7 +1942,7 @@ if (flags & tc_reap_prev  &&  prev_cmd_count > 0)
 
     case -5: errno = ERRNO_TLSFAILURE;
             return DEFER;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
     case -4:                           /* non-2xx for pipelined banner or EHLO */
 #endif
     case -1:                           /* Timeout on RCPT */
@@ -1953,7 +2012,6 @@ 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
@@ -1977,7 +2035,7 @@ 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;
@@ -2026,6 +2084,7 @@ 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
@@ -2064,6 +2123,7 @@ 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
        )
@@ -2105,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;
@@ -2168,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 */
@@ -2269,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,
@@ -2277,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;
@@ -2310,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)
@@ -2345,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
@@ -2363,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;
@@ -2456,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. */
 
@@ -2533,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;
         }
     }
   }
@@ -2560,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.
@@ -2578,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;
@@ -2601,14 +2674,14 @@ 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
@@ -2673,13 +2746,13 @@ if (continue_hostname == NULL
   {
   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
@@ -2700,7 +2773,7 @@ if (continue_hostname == NULL
        | (ob->size_addition >= 0 ? OPTION_SIZE : 0)
 #endif
       );
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
     if (tls_out.active.sock >= 0)
       sx->ehlo_resp.crypted_features = sx->peer_offered;
 #endif
@@ -2748,7 +2821,7 @@ if (continue_hostname == NULL
     DEBUG(D_transport) debug_printf("%susing DSN\n",
                        sx->peer_offered & OPTION_DSN ? "" : "not ");
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
     if (  sx->early_pipe_ok
        && !sx->early_pipe_active
        && tls_out.active.sock >= 0
@@ -2840,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);
@@ -2897,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
@@ -3004,7 +3101,7 @@ if (  sx->peer_offered & OPTION_UTF8
    && addrlist->prop.utf8_msg
    && !addrlist->prop.utf8_downcvt
    )
-  Ustrcpy(p, " SMTPUTF8"), p += 9;
+  Ustrcpy(p, US" SMTPUTF8"), p += 9;
 #endif
 
 /* check if all addresses have DSN-lasthop flag; do not send RET and ENVID if so */
@@ -3025,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)
     {
@@ -3064,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])
       {
@@ -3240,7 +3337,7 @@ 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
@@ -3271,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
@@ -3288,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);
@@ -3362,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
@@ -3474,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;
@@ -3572,7 +3676,7 @@ if (  !(sx.peer_offered & OPTION_CHUNKING)
 
     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");
@@ -3651,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
     {
@@ -3675,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);
@@ -3718,7 +3831,7 @@ else
 
       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");
@@ -3870,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
@@ -4072,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
@@ -4224,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",
@@ -4261,7 +4374,7 @@ propagate it from the initial
          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);
@@ -4342,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 */
@@ -4448,6 +4562,7 @@ for (address_item * addr = addrlist; addr; addr = addr->next)
     addr->peercert = NULL;
     addr->peerdn = NULL;
     addr->ocsp = OCSP_NOT_REQ;
+    addr->tlsver = NULL;
 #endif
 #ifdef EXPERIMENTAL_DSN_INFO
     addr->smtp_greeting = NULL;
@@ -4560,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. */
@@ -5044,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);
@@ -5285,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