TLS: move from SUPPORT_TLS to DISABLE_TLS macro for the build
[exim.git] / src / src / smtp_in.c
index 4267bc5f75132f8adc51b79fbc6a0e063b70acdd..049f5b5428a79ba96b777bc621588faf092c60cc 100644 (file)
@@ -131,28 +131,35 @@ to the circular buffer that holds a list of the last n received. */
 *                Local static variables          *
 *************************************************/
 
-static auth_instance *authenticated_by;
-static BOOL auth_advertised;
-#ifdef SUPPORT_TLS
-static BOOL tls_advertised;
-# ifdef EXPERIMENTAL_REQUIRETLS
-static BOOL requiretls_advertised;
-# endif
+static struct {
+  BOOL auth_advertised                 :1;
+#ifndef DISABLE_TLS
+  BOOL tls_advertised                  :1;
 #endif
-static BOOL dsn_advertised;
-static BOOL esmtp;
-static BOOL helo_required = FALSE;
-static BOOL helo_verify = FALSE;
-static BOOL helo_seen;
-static BOOL helo_accept_junk;
-static BOOL count_nonmail;
-static BOOL rcpt_smtp_response_same;
-static BOOL rcpt_in_progress;
-static int  nonmail_command_count;
-static BOOL smtp_exit_function_called = 0;
+  BOOL dsn_advertised                  :1;
+  BOOL esmtp                           :1;
+  BOOL helo_required                   :1;
+  BOOL helo_verify                     :1;
+  BOOL helo_seen                       :1;
+  BOOL helo_accept_junk                        :1;
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  BOOL pipe_connect_acceptable         :1;
+#endif
+  BOOL rcpt_smtp_response_same         :1;
+  BOOL rcpt_in_progress                        :1;
+  BOOL smtp_exit_function_called       :1;
 #ifdef SUPPORT_I18N
-static BOOL smtputf8_advertised;
+  BOOL smtputf8_advertised             :1;
 #endif
+} fl = {
+  .helo_required = FALSE,
+  .helo_verify = FALSE,
+  .smtp_exit_function_called = FALSE,
+};
+
+static auth_instance *authenticated_by;
+static int  count_nonmail;
+static int  nonmail_command_count;
 static int  synprot_error_count;
 static int  unknown_command_count;
 static int  sync_cmd_limit;
@@ -187,7 +194,7 @@ static smtp_cmd_list cmd_list[] = {
   { "helo",       sizeof("helo")-1,       HELO_CMD, TRUE,  FALSE },
   { "ehlo",       sizeof("ehlo")-1,       EHLO_CMD, TRUE,  FALSE },
   { "auth",       sizeof("auth")-1,       AUTH_CMD, TRUE,  TRUE  },
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   { "starttls",   sizeof("starttls")-1,   STARTTLS_CMD, FALSE, FALSE },
   { "tls_auth",   0,                      TLS_AUTH_CMD, FALSE, FALSE },
 #endif
@@ -257,9 +264,6 @@ enum {
   ENV_MAIL_OPT_RET, ENV_MAIL_OPT_ENVID,
 #ifdef SUPPORT_I18N
   ENV_MAIL_OPT_UTF8,
-#endif
-#ifdef EXPERIMENTAL_REQUIRETLS
-  ENV_MAIL_OPT_REQTLS,
 #endif
   };
 typedef struct {
@@ -279,10 +283,6 @@ static env_mail_type_t env_mail_type_list[] = {
     { US"ENVID",  ENV_MAIL_OPT_ENVID,  TRUE },
 #ifdef SUPPORT_I18N
     { US"SMTPUTF8",ENV_MAIL_OPT_UTF8,  FALSE },                /* rfc6531 */
-#endif
-#ifdef EXPERIMENTAL_REQUIRETLS
-    /* https://tools.ietf.org/html/draft-ietf-uta-smtp-require-tls-03 */
-    { US"REQUIRETLS",ENV_MAIL_OPT_REQTLS,  FALSE },
 #endif
     /* keep this the last entry */
     { US"NULL",   ENV_MAIL_OPT_NULL,   FALSE },
@@ -348,7 +348,7 @@ int fd, rc;
 fd_set fds;
 struct timeval tzero;
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 if (tls_in.active.sock >= 0)
  return !tls_could_read();
 #endif
@@ -397,6 +397,18 @@ return TRUE;
 }
 
 
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+static BOOL
+pipeline_connect_sends(void)
+{
+if (!sender_host_address || f.sender_host_notsocket || !fl.pipe_connect_acceptable)
+  return FALSE;
+
+if (wouldblock_reading()) return FALSE;
+f.smtp_in_early_pipe_used = TRUE;
+return TRUE;
+}
+#endif
 
 /*************************************************
 *          Log incomplete transactions           *
@@ -413,17 +425,16 @@ Returns:    nothing
 static void
 incomplete_transaction_log(uschar *what)
 {
-if (sender_address == NULL ||                 /* No transaction in progress */
-    !LOGGING(smtp_incomplete_transaction))
+if (!sender_address                            /* No transaction in progress */
+   || !LOGGING(smtp_incomplete_transaction))
   return;
 
 /* Build list of recipients for logging */
 
 if (recipients_count > 0)
   {
-  int i;
   raw_recipients = store_get(recipients_count * sizeof(uschar *));
-  for (i = 0; i < recipients_count; i++)
+  for (int i = 0; i < recipients_count; i++)
     raw_recipients[i] = recipients_list[i].address;
   raw_recipients_count = recipients_count;
   }
@@ -492,14 +503,14 @@ smtp_refill(unsigned lim)
 int rc, save_errno;
 if (!smtp_out) return FALSE;
 fflush(smtp_out);
-if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
+if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
 
 /* Limit amount read, so non-message data is not fed to DKIM.
 Take care to not touch the safety NUL at the end of the buffer. */
 
 rc = read(fileno(smtp_in), smtp_inbuffer, MIN(IN_BUFFER_SIZE-1, lim));
 save_errno = errno;
-if (smtp_receive_timeout > 0) alarm(0);
+if (smtp_receive_timeout > 0) ALARM_CLR(0);
 if (rc <= 0)
   {
   /* Must put the error text in fixed store, because this might be during
@@ -891,18 +902,20 @@ call another vararg function, only a function which accepts a va_list. */
 void
 smtp_vprintf(const char *format, BOOL more, va_list ap)
 {
+gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer };
 BOOL yield;
 
-yield = string_vformat(big_buffer, big_buffer_size, format, ap);
+yield = !! string_vformat(&gs, FALSE, format, ap);
+string_from_gstring(&gs);
 
 DEBUG(D_receive)
   {
   void *reset_point = store_get(0);
   uschar *msg_copy, *cr, *end;
-  msg_copy = string_copy(big_buffer);
-  end = msg_copy + Ustrlen(msg_copy);
+  msg_copy = string_copy(gs.s);
+  end = msg_copy + gs.ptr;
   while ((cr = Ustrchr(msg_copy, '\r')) != NULL)   /* lose CRs */
-  memmove(cr, cr + 1, (end--) - cr);
+    memmove(cr, cr + 1, (end--) - cr);
   debug_printf("SMTP>> %s", msg_copy);
   store_reset(reset_point);
   }
@@ -920,28 +933,28 @@ be tidier to have it only in one place, but when it was added, it was easier to
 do it that way, so as not to have to mess with the code for the RCPT command,
 which sometimes uses smtp_printf() and sometimes smtp_respond(). */
 
-if (rcpt_in_progress)
+if (fl.rcpt_in_progress)
   {
   if (rcpt_smtp_response == NULL)
     rcpt_smtp_response = string_copy(big_buffer);
-  else if (rcpt_smtp_response_same &&
+  else if (fl.rcpt_smtp_response_same &&
            Ustrcmp(rcpt_smtp_response, big_buffer) != 0)
-    rcpt_smtp_response_same = FALSE;
-  rcpt_in_progress = FALSE;
+    fl.rcpt_smtp_response_same = FALSE;
+  fl.rcpt_in_progress = FALSE;
   }
 
 /* Now write the string */
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 if (tls_in.active.sock >= 0)
   {
-  if (tls_write(NULL, big_buffer, Ustrlen(big_buffer), more) < 0)
+  if (tls_write(NULL, gs.s, gs.ptr, more) < 0)
     smtp_write_error = -1;
   }
 else
 #endif
 
-if (fprintf(smtp_out, "%s", big_buffer) < 0) smtp_write_error = -1;
+if (fprintf(smtp_out, "%s", gs.s) < 0) smtp_write_error = -1;
 }
 
 
@@ -1557,7 +1570,6 @@ smtp_read_command(BOOL check_sync, unsigned buffer_lim)
 {
 int c;
 int ptr = 0;
-smtp_cmd_list *p;
 BOOL hadnull = FALSE;
 
 had_command_timeout = 0;
@@ -1602,7 +1614,7 @@ if (hadnull) return BADCHAR_CMD;
 to the start of the actual data characters. Check for SMTP synchronization
 if required. */
 
-for (p = cmd_list; p < cmd_list_end; p++)
+for (smtp_cmd_list * p = cmd_list; p < cmd_list_end; p++)
   {
 #ifdef SUPPORT_PROXY
   /* Only allow QUIT command if Proxy Protocol parsing failed */
@@ -1753,7 +1765,7 @@ if (f.sender_host_unknown || f.sender_host_notsocket)
 if (f.is_inetd)
   return string_sprintf("SMTP connection from %s (via inetd)", hostname);
 
-if (LOGGING(incoming_interface) && interface_address != NULL)
+if (LOGGING(incoming_interface) && interface_address)
   return string_sprintf("SMTP connection from %s I=[%s]:%d", hostname,
     interface_address, interface_port);
 
@@ -1762,7 +1774,7 @@ return string_sprintf("SMTP connection from %s", hostname);
 
 
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 /* Append TLS-related information to a log line
 
 Arguments:
@@ -1774,7 +1786,13 @@ static gstring *
 s_tlslog(gstring * g)
 {
 if (LOGGING(tls_cipher) && tls_in.cipher)
+  {
   g = string_append(g, 2, US" X=", tls_in.cipher);
+#ifdef EXPERIMENTAL_TLS_RESUME
+  if (LOGGING(tls_resumption) && tls_in.resumption & RESUME_USED)
+    g = string_catn(g, US"*", 1);
+#endif
+  }
 if (LOGGING(tls_certificate_verified) && tls_in.cipher)
   g = string_append(g, 2, US" CV=", tls_in.certificate_verified? "yes":"no");
 if (LOGGING(tls_peerdn) && tls_in.peerdn)
@@ -1800,7 +1818,6 @@ Returns:     nothing
 void
 smtp_log_no_mail(void)
 {
-int i;
 uschar * sep, * s;
 gstring * g = NULL;
 
@@ -1813,20 +1830,20 @@ if (sender_host_authenticated)
   if (authenticated_id) g = string_append(g, 2, US":", authenticated_id);
   }
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 g = s_tlslog(g);
 #endif
 
 sep = smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE ?  US" C=..." : US" C=";
 
-for (i = smtp_ch_index; i < SMTP_HBUFF_SIZE; i++)
+for (int i = smtp_ch_index; i < SMTP_HBUFF_SIZE; i++)
   if (smtp_connection_had[i] != SCH_NONE)
     {
     g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]);
     sep = US",";
     }
 
-for (i = 0; i < smtp_ch_index; i++)
+for (int i = 0; i < smtp_ch_index; i++)
   {
   g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]);
   sep = US",";
@@ -1835,7 +1852,7 @@ for (i = 0; i < smtp_ch_index; i++)
 if (!(s = string_from_gstring(g))) s = US"";
 
 log_write(0, LOG_MAIN, "no MAIL in %sSMTP connection from %s D=%s%s",
-  f.tcp_in_fastopen ? US"TFO " : US"",
+  f.tcp_in_fastopen ? f.tcp_in_fastopen_data ? US"TFO* " : US"TFO " : US"",
   host_and_ident(FALSE), string_timesince(&smtp_connection_start), s);
 }
 
@@ -1845,15 +1862,14 @@ log_write(0, LOG_MAIN, "no MAIL in %sSMTP connection from %s D=%s%s",
 uschar *
 smtp_cmd_hist(void)
 {
-int  i;
 gstring * list = NULL;
 uschar * s;
 
-for (i = smtp_ch_index; i < SMTP_HBUFF_SIZE; i++)
+for (int i = smtp_ch_index; i < SMTP_HBUFF_SIZE; i++)
   if (smtp_connection_had[i] != SCH_NONE)
     list = string_append_listele(list, ',', smtp_names[smtp_connection_had[i]]);
 
-for (i = 0; i < smtp_ch_index; i++)
+for (int i = 0; i < smtp_ch_index; i++)
   list = string_append_listele(list, ',', smtp_names[smtp_connection_had[i]]);
 
 s = string_from_gstring(list);
@@ -1886,7 +1902,7 @@ check_helo(uschar *s)
 {
 uschar *start = s;
 uschar *end = s + Ustrlen(s);
-BOOL yield = helo_accept_junk;
+BOOL yield = fl.helo_accept_junk;
 
 /* Discard any previous helo name */
 
@@ -2017,8 +2033,8 @@ acl_added_headers = NULL;
 acl_removed_headers = NULL;
 f.queue_only_policy = FALSE;
 rcpt_smtp_response = NULL;
-rcpt_smtp_response_same = TRUE;
-rcpt_in_progress = FALSE;
+fl.rcpt_smtp_response_same = TRUE;
+fl.rcpt_in_progress = FALSE;
 f.deliver_freeze = FALSE;                              /* Can be set by ACL */
 freeze_tell = freeze_tell_config;                    /* Can be set by ACL */
 fake_response = OK;                                  /* Can be set by ACL */
@@ -2060,7 +2076,6 @@ f.dkim_disable_verify = FALSE;
 dkim_collect_input = 0;
 dkim_verify_overall = dkim_verify_status = dkim_verify_reason = NULL;
 dkim_key_length = 0;
-dkim_verify_signers = US"$dkim_signers";
 #endif
 #ifdef EXPERIMENTAL_DMARC
 f.dmarc_has_been_checked = f.dmarc_disable_verify = f.dmarc_enable_forensic = FALSE;
@@ -2360,6 +2375,7 @@ return done - 2;  /* Convert yield values */
 
 
 
+#ifndef DISABLE_TLS
 static BOOL
 smtp_log_tls_fail(uschar * errstr)
 {
@@ -2371,6 +2387,7 @@ if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) conn_info += 5;
 log_write(0, LOG_MAIN, "TLS error on %s %s", conn_info, errstr);
 return FALSE;
 }
+#endif
 
 
 
@@ -2383,13 +2400,20 @@ tfo_in_check(void)
 struct tcp_info tinfo;
 socklen_t len = sizeof(tinfo);
 
-if (  getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0
-   && tinfo.tcpi_state == TCP_SYN_RECV
-   )
-  {
-  DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (state TCP_SYN_RECV)\n");
-  f.tcp_in_fastopen = TRUE;
-  }
+if (getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0)
+#ifdef TCPI_OPT_SYN_DATA       /* FreeBSD 11 does not seem to have this yet */
+  if (tinfo.tcpi_options & TCPI_OPT_SYN_DATA)
+    {
+    DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (ACKd data-on-SYN)\n");
+    f.tcp_in_fastopen_data = f.tcp_in_fastopen = TRUE;
+    }
+  else
+#endif
+    if (tinfo.tcpi_state == TCP_SYN_RECV)
+    {
+    DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (state TCP_SYN_RECV)\n");
+    f.tcp_in_fastopen = TRUE;
+    }
 # endif
 }
 #endif
@@ -2424,16 +2448,16 @@ smtp_ch_index = 0;
 
 /* Default values for certain variables */
 
-helo_seen = esmtp = helo_accept_junk = FALSE;
+fl.helo_seen = fl.esmtp = fl.helo_accept_junk = FALSE;
 smtp_mailcmd_count = 0;
 count_nonmail = TRUE_UNSET;
 synprot_error_count = unknown_command_count = nonmail_command_count = 0;
 smtp_delay_mail = smtp_rlm_base;
-auth_advertised = FALSE;
+fl.auth_advertised = FALSE;
 f.smtp_in_pipelining_advertised = f.smtp_in_pipelining_used = FALSE;
 f.pipelining_enable = TRUE;
 sync_cmd_limit = NON_SYNC_CMD_NON_PIPELINING;
-smtp_exit_function_called = FALSE;    /* For avoiding loop in not-quit exit */
+fl.smtp_exit_function_called = FALSE;    /* For avoiding loop in not-quit exit */
 
 /* If receiving by -bs from a trusted user, or testing with -bh, we allow
 authentication settings from -oMaa to remain in force. */
@@ -2442,19 +2466,16 @@ if (!host_checking && !f.sender_host_notsocket)
   sender_host_auth_pubname = sender_host_authenticated = NULL;
 authenticated_by = NULL;
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 tls_in.cipher = tls_in.peerdn = NULL;
 tls_in.ourcert = tls_in.peercert = NULL;
 tls_in.sni = NULL;
 tls_in.ocsp = OCSP_NOT_REQ;
-tls_advertised = FALSE;
-# ifdef EXPERIMENTAL_REQUIRETLS
-requiretls_advertised = FALSE;
-# endif
+fl.tls_advertised = FALSE;
 #endif
-dsn_advertised = FALSE;
+fl.dsn_advertised = FALSE;
 #ifdef SUPPORT_I18N
-smtputf8_advertised = FALSE;
+fl.smtputf8_advertised = FALSE;
 #endif
 
 /* Reset ACL connection variables */
@@ -2617,7 +2638,7 @@ if (!f.sender_host_unknown)
       {
       uschar *p = big_buffer;
       uschar *pend = big_buffer + big_buffer_size;
-      uschar *opt, *adptr;
+      uschar *adptr;
       int optcount;
       struct in_addr addr;
 
@@ -2634,9 +2655,7 @@ if (!f.sender_host_unknown)
       Ustrcpy(p, "IP options on incoming call:");
       p += Ustrlen(p);
 
-      for (opt = optstart; opt != NULL &&
-           opt < US (ipopt) + optlen;)
-        {
+      for (uschar * opt = optstart; opt && opt < US (ipopt) + optlen; )
         switch (*opt)
           {
           case IPOPT_EOL:
@@ -2684,18 +2703,16 @@ if (!f.sender_host_unknown)
 
           default:
             {
-            int i;
             if (pend - p < 4 + 3*opt[1]) { opt = NULL; break; }
             Ustrcat(p, "[ ");
             p += 2;
-            for (i = 0; i < opt[1]; i++)
+            for (int i = 0; i < opt[1]; i++)
               p += sprintf(CS p, "%2.2x ", opt[i]);
             *p++ = ']';
             }
           opt += opt[1];
           break;
           }
-        }
 
       *p = 0;
       log_write(0, LOG_MAIN, "%s", big_buffer);
@@ -2859,14 +2876,14 @@ if (!f.sender_host_unknown)
   /* Determine whether HELO/EHLO is required for this host. The requirement
   can be hard or soft. */
 
-  helo_required = verify_check_host(&helo_verify_hosts) == OK;
-  if (!helo_required)
-    helo_verify = verify_check_host(&helo_try_verify_hosts) == OK;
+  fl.helo_required = verify_check_host(&helo_verify_hosts) == OK;
+  if (!fl.helo_required)
+    fl.helo_verify = verify_check_host(&helo_try_verify_hosts) == OK;
 
   /* Determine whether this hosts is permitted to send syntactic junk
   after a HELO or EHLO command. */
 
-  helo_accept_junk = verify_check_host(&helo_accept_junk_hosts) == OK;
+  fl.helo_accept_junk = verify_check_host(&helo_accept_junk_hosts) == OK;
   }
 
 /* For batch SMTP input we are now done. */
@@ -2886,7 +2903,7 @@ if (check_proxy_protocol_host())
   /* Start up TLS if tls_on_connect is set. This is for supporting the legacy
   smtps port for use with older style SSL MTAs. */
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   if (tls_in.on_connect)
     {
     if (tls_server_start(tls_require_ciphers, &user_msg) != OK)
@@ -2978,22 +2995,39 @@ while (*p);
 /* Before we write the banner, check that there is no input pending, unless
 this synchronisation check is disabled. */
 
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+fl.pipe_connect_acceptable =
+  sender_host_address && verify_check_host(&pipe_connect_advertise_hosts) == OK;
+
 if (!check_sync())
-  {
-  unsigned n = smtp_inend - smtp_inptr;
-  if (n > 32) n = 32;
-
-  log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol "
-    "synchronization error (input sent without waiting for greeting): "
-    "rejected connection from %s input=\"%s\"", host_and_ident(TRUE),
-    string_printing(string_copyn(smtp_inptr, n)));
-  smtp_printf("554 SMTP synchronization error\r\n", FALSE);
-  return FALSE;
-  }
+  if (fl.pipe_connect_acceptable)
+    f.smtp_in_early_pipe_used = TRUE;
+  else
+#else
+if (!check_sync())
+#endif
+    {
+    unsigned n = smtp_inend - smtp_inptr;
+    if (n > 32) n = 32;
+
+    log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol "
+      "synchronization error (input sent without waiting for greeting): "
+      "rejected connection from %s input=\"%s\"", host_and_ident(TRUE),
+      string_printing(string_copyn(smtp_inptr, n)));
+    smtp_printf("554 SMTP synchronization error\r\n", FALSE);
+    return FALSE;
+    }
 
 /* Now output the banner */
+/*XXX the ehlo-resp code does its own tls/nontls bit.  Maybe subroutine that? */
 
-smtp_printf("%s", FALSE, string_from_gstring(ss));
+smtp_printf("%s",
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  fl.pipe_connect_acceptable && pipeline_connect_sends(),
+#else
+  FALSE,
+#endif
+  string_from_gstring(ss));
 
 /* Attempt to see if we sent the banner before the last ACK of the 3-way
 handshake arrived.  If so we must have managed a TFO. */
@@ -3097,14 +3131,14 @@ be tidier to have it only in one place, but when it was added, it was easier to
 do it that way, so as not to have to mess with the code for the RCPT command,
 which sometimes uses smtp_printf() and sometimes smtp_respond(). */
 
-if (rcpt_in_progress)
+if (fl.rcpt_in_progress)
   {
   if (rcpt_smtp_response == NULL)
     rcpt_smtp_response = string_copy(msg);
-  else if (rcpt_smtp_response_same &&
+  else if (fl.rcpt_smtp_response_same &&
            Ustrcmp(rcpt_smtp_response, msg) != 0)
-    rcpt_smtp_response_same = FALSE;
-  rcpt_in_progress = FALSE;
+    fl.rcpt_smtp_response_same = FALSE;
+  fl.rcpt_in_progress = FALSE;
   }
 
 /* Now output the message, splitting it up into multiple lines if necessary.
@@ -3291,8 +3325,8 @@ unless the sender_verify_fail log selector has been turned off. */
 if (sender_verified_failed &&
     !testflag(sender_verified_failed, af_sverify_told))
   {
-  BOOL save_rcpt_in_progress = rcpt_in_progress;
-  rcpt_in_progress = FALSE;  /* So as not to treat these as the error */
+  BOOL save_rcpt_in_progress = fl.rcpt_in_progress;
+  fl.rcpt_in_progress = FALSE;  /* So as not to treat these as the error */
 
   setflag(sender_verified_failed, af_sverify_told);
 
@@ -3324,7 +3358,7 @@ if (sender_verified_failed &&
         sender_verified_failed->address,
         sender_verified_failed->user_message));
 
-  rcpt_in_progress = save_rcpt_in_progress;
+  fl.rcpt_in_progress = save_rcpt_in_progress;
   }
 
 /* Sort out text for logging */
@@ -3370,7 +3404,7 @@ is closing if required and return 2.  */
 
 if (log_reject_target != 0)
   {
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   gstring * g = s_tlslog(NULL);
   uschar * tls = string_from_gstring(g);
   if (!tls) tls = US"";
@@ -3435,15 +3469,15 @@ int rc;
 uschar *user_msg = NULL;
 uschar *log_msg = NULL;
 
-/* Check for recursive acll */
+/* Check for recursive call */
 
-if (smtp_exit_function_called)
+if (fl.smtp_exit_function_called)
   {
   log_write(0, LOG_PANIC, "smtp_notquit_exit() called more than once (%s)",
     reason);
   return;
   }
-smtp_exit_function_called = TRUE;
+fl.smtp_exit_function_called = TRUE;
 
 /* Call the not-QUIT ACL, if there is one, unless no reason is given. */
 
@@ -3456,10 +3490,11 @@ if (acl_smtp_notquit && reason)
       log_msg);
   }
 
+/* If the connection was dropped, we certainly are no longer talking TLS */
+tls_in.active.sock = -1;
+
 /* Write an SMTP response if we are expected to give one. As the default
-responses are all internal, they should always fit in the buffer, but code a
-warning, just in case. Note that string_vformat() still leaves a complete
-string, even if it is incomplete. */
+responses are all internal, they should be reasonable size. */
 
 if (code && defaultrespond)
   {
@@ -3467,13 +3502,13 @@ if (code && defaultrespond)
     smtp_respond(code, 3, TRUE, user_msg);
   else
     {
-    uschar buffer[128];
+    gstring * g;
     va_list ap;
+
     va_start(ap, defaultrespond);
-    if (!string_vformat(buffer, sizeof(buffer), CS defaultrespond, ap))
-      log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_notquit_exit()");
-    smtp_printf("%s %s\r\n", FALSE, code, buffer);
+    g = string_vformat(NULL, TRUE, CS defaultrespond, ap);
     va_end(ap);
+    smtp_printf("%s %s\r\n", FALSE, code, string_from_gstring(g));
     }
   mac_smtp_fflush();
   }
@@ -3578,33 +3613,25 @@ else
   if (!f.helo_verified)
     {
     int rc;
-    host_item h;
-    dnssec_domains d;
-    host_item *hh;
-
-    h.name = sender_helo_name;
-    h.address = NULL;
-    h.mx = MX_NONE;
-    h.next = NULL;
-    d.request = US"*";
-    d.require = US"";
+    host_item h =
+      {.name = sender_helo_name, .address = NULL, .mx = MX_NONE, .next = NULL};
+    dnssec_domains d =
+      {.request = US"*", .require = US""};
 
     HDEBUG(D_receive) debug_printf("getting IP address for %s\n",
       sender_helo_name);
     rc = host_find_bydns(&h, NULL, HOST_FIND_BY_A | HOST_FIND_BY_AAAA,
                          NULL, NULL, NULL, &d, NULL, NULL);
     if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL)
-      for (hh = &h; hh; hh = hh->next)
+      for (host_item * hh = &h; hh; hh = hh->next)
         if (Ustrcmp(hh->address, sender_host_address) == 0)
           {
           f.helo_verified = TRUE;
          if (h.dnssec == DS_YES) sender_helo_dnssec = TRUE;
           HDEBUG(D_receive)
-           {
             debug_printf("IP address for %s matches calling address\n"
              "Forward DNS security status: %sverified\n",
               sender_helo_name, sender_helo_dnssec ? "" : "un");
-           }
           break;
           }
     }
@@ -3647,7 +3674,7 @@ static int
 smtp_in_auth(auth_instance *au, uschar ** s, uschar ** ss)
 {
 const uschar *set_id = NULL;
-int rc, i;
+int rc;
 
 /* Run the checking code, passing the remainder of the command line as
 data. Initials the $auth<n> variables as empty. Initialize $0 empty and set
@@ -3661,14 +3688,14 @@ userid. On success, require set_id to expand and exist, and put it in
 authenticated_id. Save this in permanent store, as the working store gets
 reset at HELO, RSET, etc. */
 
-for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
+for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
 expand_nmax = 0;
 expand_nlength[0] = 0;   /* $0 contains nothing */
 
 rc = (au->info->servercode)(au, smtp_cmd_data);
 if (au->set_id) set_id = expand_string(au->set_id);
 expand_nmax = -1;        /* Reset numeric variables */
-for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;   /* Reset $auth<n> */
+for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;   /* Reset $auth<n> */
 
 /* The value of authenticated_id is stored in the spool file and printed in
 log lines. It must not contain binary zeros or newline characters. In
@@ -3788,12 +3815,17 @@ if (acl_smtp_quit)
     log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s",
       *log_msgp);
   }
+
+#ifdef TCP_CORK
+(void) setsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_CORK, US &on, sizeof(on));
+#endif
+
 if (*user_msgp)
   smtp_respond(US"221", 3, TRUE, *user_msgp);
 else
   smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 tls_close(NULL, TLS_SHUTDOWN_NOWAIT);
 #endif
 
@@ -3864,7 +3896,7 @@ chunking_state = f.chunking_offered ? CHUNKING_OFFERED : CHUNKING_NOT_OFFERED;
 cmd_list[CMD_LIST_RSET].is_mail_cmd = TRUE;
 cmd_list[CMD_LIST_HELO].is_mail_cmd = TRUE;
 cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE;
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE;
 #endif
 
@@ -3898,9 +3930,8 @@ while (done <= 0)
   int start, end, sender_domain, recipient_domain;
   int rc;
   int c;
-  auth_instance *au;
   uschar *orcpt = NULL;
-  int flags;
+  int dsn_flags;
   gstring * g;
 
 #ifdef AUTH_TLS
@@ -3913,7 +3944,7 @@ while (done <= 0)
     {
     cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = FALSE;
 
-    for (au = auths; au; au = au->next)
+    for (auth_instance * au = auths; au; au = au->next)
       if (strcmpic(US"tls", au->driver_name) == 0)
        {
        if (  acl_smtp_auth
@@ -3941,7 +3972,13 @@ while (done <= 0)
            US &off, sizeof(off));
 #endif
 
-  switch(smtp_read_command(TRUE, GETC_BUFFER_UNLIMITED))
+  switch(smtp_read_command(
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+         !fl.pipe_connect_acceptable,
+#else
+         TRUE,
+#endif
+         GETC_BUFFER_UNLIMITED))
     {
     /* The AUTH command is not permitted to occur inside a transaction, and may
     occur successfully only once per connection. Actually, that isn't quite
@@ -3962,7 +3999,7 @@ while (done <= 0)
       authentication_failed = TRUE;
       cmd_list[CMD_LIST_AUTH].is_mail_cmd = FALSE;
 
-      if (!auth_advertised && !f.allow_auth_unadvertised)
+      if (!fl.auth_advertised && !f.allow_auth_unadvertised)
        {
        done = synprot_error(L_smtp_protocol_error, 503, NULL,
          US"AUTH command used when not advertised");
@@ -4019,23 +4056,26 @@ while (done <= 0)
       as a server and which has been advertised (unless, sigh, allow_auth_
       unadvertised is set). */
 
-      for (au = auths; au; au = au->next)
-       if (strcmpic(s, au->public_name) == 0 && au->server &&
-           (au->advertised || f.allow_auth_unadvertised))
-         break;
-
-      if (au)
        {
-       c = smtp_in_auth(au, &s, &ss);
+       auth_instance * au;
+       for (au = auths; au; au = au->next)
+         if (strcmpic(s, au->public_name) == 0 && au->server &&
+             (au->advertised || f.allow_auth_unadvertised))
+           break;
 
-       smtp_printf("%s\r\n", FALSE, s);
-       if (c != OK)
-         log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s",
-           au->name, host_and_ident(FALSE), ss);
+       if (au)
+         {
+         c = smtp_in_auth(au, &s, &ss);
+
+         smtp_printf("%s\r\n", FALSE, s);
+         if (c != OK)
+           log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s",
+             au->name, host_and_ident(FALSE), ss);
+         }
+       else
+         done = synprot_error(L_smtp_protocol_error, 504, NULL,
+           string_sprintf("%s authentication mechanism not supported", s));
        }
-      else
-       done = synprot_error(L_smtp_protocol_error, 504, NULL,
-         string_sprintf("%s authentication mechanism not supported", s));
 
       break;  /* AUTH_CMD */
 
@@ -4058,13 +4098,13 @@ while (done <= 0)
     case HELO_CMD:
       HAD(SCH_HELO);
       hello = US"HELO";
-      esmtp = FALSE;
+      fl.esmtp = FALSE;
       goto HELO_EHLO;
 
     case EHLO_CMD:
       HAD(SCH_EHLO);
       hello = US"EHLO";
-      esmtp = TRUE;
+      fl.esmtp = TRUE;
 
     HELO_EHLO:      /* Common code for HELO and EHLO */
       cmd_list[CMD_LIST_HELO].is_mail_cmd = FALSE;
@@ -4112,9 +4152,8 @@ while (done <= 0)
        because otherwise the log can be confusing. */
 
        if (  !sender_host_name
-          && (deliver_domain = sender_helo_name,  /* set $domain */
-              match_isinlist(sender_helo_name, CUSS &helo_lookup_domains, 0,
-               &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) == OK)
+          && match_isinlist(sender_helo_name, CUSS &helo_lookup_domains, 0,
+               &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK)
          (void)host_name_lookup();
 
        /* Rebuild the fullhost info to include the HELO name (and the real name
@@ -4132,12 +4171,12 @@ while (done <= 0)
        at ACL time. */
 
        f.helo_verified = f.helo_verify_failed = sender_helo_dnssec = FALSE;
-       if (helo_required || helo_verify)
+       if (fl.helo_required || fl.helo_verify)
          {
          BOOL tempfail = !smtp_verify_helo();
          if (!f.helo_verified)
            {
-           if (helo_required)
+           if (fl.helo_required)
              {
              smtp_printf("%d %s argument does not match calling host\r\n", FALSE,
                tempfail? 451 : 550, hello);
@@ -4174,7 +4213,12 @@ while (done <= 0)
          host_build_sender_fullhost();  /* Rebuild */
          break;
          }
-       else if (!check_sync()) goto SYNC_FAILURE;
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+       else if (!fl.pipe_connect_acceptable && !check_sync())
+#else
+       else if (!check_sync())
+#endif
+         goto SYNC_FAILURE;
 
       /* Generate an OK reply. The default string includes the ident if present,
       and also the IP address if present. Reflecting back the ident is intended
@@ -4182,36 +4226,28 @@ while (done <= 0)
       some broken systems expect each response to be in a single packet, arrange
       that the entire reply is sent in one write(). */
 
-      auth_advertised = FALSE;
+      fl.auth_advertised = FALSE;
       f.smtp_in_pipelining_advertised = FALSE;
-#ifdef SUPPORT_TLS
-      tls_advertised = FALSE;
-# ifdef EXPERIMENTAL_REQUIRETLS
-      requiretls_advertised = FALSE;
-# endif
+#ifndef DISABLE_TLS
+      fl.tls_advertised = FALSE;
 #endif
-      dsn_advertised = FALSE;
+      fl.dsn_advertised = FALSE;
 #ifdef SUPPORT_I18N
-      smtputf8_advertised = FALSE;
+      fl.smtputf8_advertised = FALSE;
 #endif
 
       smtp_code = US"250 ";        /* Default response code plus space*/
       if (!user_msg)
        {
-       s = string_sprintf("%.3s %s Hello %s%s%s",
+       g = string_fmt_append(NULL, "%.3s %s Hello %s%s%s",
          smtp_code,
          smtp_active_hostname,
          sender_ident ? sender_ident : US"",
          sender_ident ? US" at " : US"",
          sender_host_name ? sender_host_name : sender_helo_name);
-       g = string_cat(NULL, s);
 
        if (sender_host_address)
-         {
-         g = string_catn(g, US" [", 2);
-         g = string_cat (g, sender_host_address);
-         g = string_catn(g, US"]", 1);
-         }
+         g = string_fmt_append(g, " [%s]", sender_host_address);
        }
 
       /* A user-supplied EHLO greeting may not contain more than one line. Note
@@ -4238,7 +4274,7 @@ while (done <= 0)
       /* If we received EHLO, we must create a multiline response which includes
       the functions supported. */
 
-      if (esmtp)
+      if (fl.esmtp)
        {
        g->s[3] = '-';
 
@@ -4249,11 +4285,8 @@ while (done <= 0)
        till then, VRFY and EXPN can be used after EHLO when space is short. */
 
        if (thismessage_size_limit > 0)
-         {
-         sprintf(CS big_buffer, "%.3s-SIZE %d\r\n", smtp_code,
+         g = string_fmt_append(g, "%.3s-SIZE %d\r\n", smtp_code,
            thismessage_size_limit);
-         g = string_cat(g, big_buffer);
-         }
        else
          {
          g = string_catn(g, smtp_code, 3);
@@ -4278,7 +4311,7 @@ while (done <= 0)
          {
          g = string_catn(g, smtp_code, 3);
          g = string_catn(g, US"-DSN\r\n", 6);
-         dsn_advertised = TRUE;
+         fl.dsn_advertised = TRUE;
          }
 
        /* Advertise ETRN/VRFY/EXPN if there's are ACL checking whether a host is
@@ -4303,13 +4336,22 @@ while (done <= 0)
        /* Exim is quite happy with pipelining, so let the other end know that
        it is safe to use it, unless advertising is disabled. */
 
-       if (f.pipelining_enable &&
-           verify_check_host(&pipelining_advertise_hosts) == OK)
+       if (  f.pipelining_enable
+          && verify_check_host(&pipelining_advertise_hosts) == OK)
          {
          g = string_catn(g, smtp_code, 3);
          g = string_catn(g, US"-PIPELINING\r\n", 13);
          sync_cmd_limit = NON_SYNC_CMD_PIPELINING;
          f.smtp_in_pipelining_advertised = TRUE;
+
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+         if (fl.pipe_connect_acceptable)
+           {
+           f.smtp_in_early_pipe_advertised = TRUE;
+           g = string_catn(g, smtp_code, 3);
+           g = string_catn(g, US"-" EARLY_PIPE_FEATURE_NAME "\r\n", EARLY_PIPE_FEATURE_LEN+3);
+           }
+#endif
          }
 
 
@@ -4330,9 +4372,8 @@ while (done <= 0)
           && verify_check_host(&auth_advertise_hosts) == OK
           )
          {
-         auth_instance *au;
          BOOL first = TRUE;
-         for (au = auths; au; au = au->next)
+         for (auth_instance * au = auths; au; au = au->next)
            {
            au->advertised = FALSE;
            if (au->server)
@@ -4351,7 +4392,7 @@ while (done <= 0)
                  g = string_catn(g, smtp_code, 3);
                  g = string_catn(g, US"-AUTH", 5);
                  first = FALSE;
-                 auth_advertised = TRUE;
+                 fl.auth_advertised = TRUE;
                  }
                saveptr = g->ptr;
                g = string_catn(g, US" ", 1);
@@ -4380,25 +4421,14 @@ while (done <= 0)
        tls_advertise_hosts. We must *not* advertise if we are already in a
        secure connection. */
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
        if (tls_in.active.sock < 0 &&
            verify_check_host(&tls_advertise_hosts) != FAIL)
          {
          g = string_catn(g, smtp_code, 3);
          g = string_catn(g, US"-STARTTLS\r\n", 11);
-         tls_advertised = TRUE;
+         fl.tls_advertised = TRUE;
          }
-
-# ifdef EXPERIMENTAL_REQUIRETLS
-       /* Advertise REQUIRETLS only once we are in a secure connection */
-       if (  tls_in.active.sock >= 0
-          && verify_check_host(&tls_advertise_requiretls) != FAIL)
-         {
-         g = string_catn(g, smtp_code, 3);
-         g = string_catn(g, US"-REQUIRETLS\r\n", 13);
-         requiretls_advertised = TRUE;
-         }
-# endif
 #endif
 
 #ifndef DISABLE_PRDR
@@ -4416,7 +4446,7 @@ while (done <= 0)
          {
          g = string_catn(g, smtp_code, 3);
          g = string_catn(g, US"-SMTPUTF8\r\n", 11);
-         smtputf8_advertised = TRUE;
+         fl.smtputf8_advertised = TRUE;
          }
 #endif
 
@@ -4429,8 +4459,15 @@ while (done <= 0)
       /* Terminate the string (for debug), write it, and note that HELO/EHLO
       has been seen. */
 
-#ifdef SUPPORT_TLS
-      if (tls_in.active.sock >= 0) (void)tls_write(NULL, g->s, g->ptr, FALSE); else
+#ifndef DISABLE_TLS
+      if (tls_in.active.sock >= 0)
+       (void)tls_write(NULL, g->s, g->ptr,
+# ifdef EXPERIMENTAL_PIPE_CONNECT
+                       fl.pipe_connect_acceptable && pipeline_connect_sends());
+# else
+                       FALSE);
+# endif
+      else
 #endif
 
        {
@@ -4445,12 +4482,12 @@ while (done <= 0)
          memmove(cr, cr + 1, (g->ptr--) - (cr - g->s));
        debug_printf("SMTP>> %s", g->s);
        }
-      helo_seen = TRUE;
+      fl.helo_seen = TRUE;
 
       /* Reset the protocol and the state, abandoning any previous message. */
       received_protocol =
        (sender_host_address ? protocols : protocols_local)
-         [ (esmtp
+         [ (fl.esmtp
            ? pextend + (sender_host_authenticated ? pauthed : 0)
            : pnormal)
          + (tls_in.active.sock >= 0 ? pcrpted : 0)
@@ -4473,7 +4510,7 @@ while (done <= 0)
       was_rej_mail = TRUE;               /* Reset if accepted */
       env_mail_type_t * mail_args;       /* Sanity check & validate args */
 
-      if (helo_required && !helo_seen)
+      if (fl.helo_required && !fl.helo_seen)
        {
        smtp_printf("503 HELO or EHLO required\r\n", FALSE);
        log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL from %s: no "
@@ -4517,7 +4554,7 @@ while (done <= 0)
 
       /* Loop, checking for ESMTP additions to the MAIL FROM command. */
 
-      if (esmtp) for(;;)
+      if (fl.esmtp) for(;;)
        {
        uschar *name, *value, *end;
        unsigned long int size;
@@ -4580,7 +4617,7 @@ while (done <= 0)
          is included only if configured in at build time. */
 
          case ENV_MAIL_OPT_RET:
-           if (dsn_advertised)
+           if (fl.dsn_advertised)
              {
              /* Check if RET has already been set */
              if (dsn_ret > 0)
@@ -4605,7 +4642,7 @@ while (done <= 0)
              }
            break;
          case ENV_MAIL_OPT_ENVID:
-           if (dsn_advertised)
+           if (fl.dsn_advertised)
              {
              /* Check if the dsn envid has been already set */
              if (dsn_envid)
@@ -4697,7 +4734,7 @@ while (done <= 0)
 
 #ifdef SUPPORT_I18N
          case ENV_MAIL_OPT_UTF8:
-           if (!smtputf8_advertised)
+           if (!fl.smtputf8_advertised)
              {
              done = synprot_error(L_smtp_syntax_error, 501, NULL,
                US"SMTPUTF8 used when not advertised");
@@ -4716,28 +4753,6 @@ while (done <= 0)
            break;
 #endif
 
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
-         case ENV_MAIL_OPT_REQTLS:
-           {
-           uschar * r, * t;
-
-           if (!requiretls_advertised)
-             {
-             done = synprot_error(L_smtp_syntax_error, 555, NULL,
-               US"unadvertised MAIL option: REQUIRETLS");
-             goto COMMAND_LOOP;
-             }
-
-           DEBUG(D_receive) debug_printf("requiretls requested\n");
-           tls_requiretls = REQUIRETLS_MSG;
-
-           r = string_copy_malloc(received_protocol);
-           if ((t = Ustrrchr(r, 's'))) *t = 'S';
-           received_protocol = r;
-           }
-           break;
-#endif
-
          /* No valid option. Stick back the terminator characters and break
          the loop.  Do the name-terminator second as extract_option sets
          value==name when it found no equal-sign.
@@ -4755,17 +4770,6 @@ while (done <= 0)
        if (arg_error) break;
        }
 
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
-      if (tls_requiretls & REQUIRETLS_MSG)
-       {
-       /* Ensure headers-only bounces whether a RET option was given or not. */
-
-       DEBUG(D_receive) if (dsn_ret == dsn_ret_full)
-         debug_printf("requiretls override: dsn_ret_full -> dsn_ret_hdrs\n");
-       dsn_ret = dsn_ret_hdrs;
-       }
-#endif
-
       /* If we have passed the threshold for rate limiting, apply the current
       delay, and update it for next time, provided this is a limited host. */
 
@@ -4917,7 +4921,7 @@ while (done <= 0)
     case RCPT_CMD:
       HAD(SCH_RCPT);
       rcpt_count++;
-      was_rcpt = rcpt_in_progress = TRUE;
+      was_rcpt = fl.rcpt_in_progress = TRUE;
 
       /* There must be a sender address; if the sender was rejected and
       pipelining was advertised, we assume the client was pipelining, and do not
@@ -4953,16 +4957,16 @@ while (done <= 0)
 
       /* Set the DSN flags orcpt and dsn_flags from the session*/
       orcpt = NULL;
-      flags = 0;
+      dsn_flags = 0;
 
-      if (esmtp) for(;;)
+      if (fl.esmtp) for(;;)
        {
        uschar *name, *value;
 
        if (!extract_option(&name, &value))
          break;
 
-       if (dsn_advertised && strcmpic(name, US"ORCPT") == 0)
+       if (fl.dsn_advertised && strcmpic(name, US"ORCPT") == 0)
          {
          /* Check whether orcpt has been already set */
          if (orcpt)
@@ -4975,17 +4979,17 @@ while (done <= 0)
          DEBUG(D_receive) debug_printf("DSN orcpt: %s\n", orcpt);
          }
 
-       else if (dsn_advertised && strcmpic(name, US"NOTIFY") == 0)
+       else if (fl.dsn_advertised && strcmpic(name, US"NOTIFY") == 0)
          {
          /* Check if the notify flags have been already set */
-         if (flags > 0)
+         if (dsn_flags > 0)
            {
            done = synprot_error(L_smtp_syntax_error, 501, NULL,
                US"NOTIFY can be specified once only");
            goto COMMAND_LOOP;
            }
          if (strcmpic(value, US"NEVER") == 0)
-           flags |= rf_notify_never;
+           dsn_flags |= rf_notify_never;
          else
            {
            uschar *p = value;
@@ -4997,17 +5001,17 @@ while (done <= 0)
              if (strcmpic(p, US"SUCCESS") == 0)
                {
                DEBUG(D_receive) debug_printf("DSN: Setting notify success\n");
-               flags |= rf_notify_success;
+               dsn_flags |= rf_notify_success;
                }
              else if (strcmpic(p, US"FAILURE") == 0)
                {
                DEBUG(D_receive) debug_printf("DSN: Setting notify failure\n");
-               flags |= rf_notify_failure;
+               dsn_flags |= rf_notify_failure;
                }
              else if (strcmpic(p, US"DELAY") == 0)
                {
                DEBUG(D_receive) debug_printf("DSN: Setting notify delay\n");
-               flags |= rf_notify_delay;
+               dsn_flags |= rf_notify_delay;
                }
              else
                {
@@ -5018,7 +5022,7 @@ while (done <= 0)
                }
              p = pp;
              }
-             DEBUG(D_receive) debug_printf("DSN Flags: %x\n", flags);
+             DEBUG(D_receive) debug_printf("DSN Flags: %x\n", dsn_flags);
            }
          }
 
@@ -5136,7 +5140,7 @@ while (done <= 0)
 
        /* Set the dsn flags in the recipients_list */
        recipients_list[recipients_count-1].orcpt = orcpt;
-       recipients_list[recipients_count-1].dsn_flags = flags;
+       recipients_list[recipients_count-1].dsn_flags = dsn_flags;
 
        DEBUG(D_receive) debug_printf("DSN: orcpt: %s  flags: %d\n",
          recipients_list[recipients_count-1].orcpt,
@@ -5235,10 +5239,13 @@ while (done <= 0)
       HAD(SCH_DATA);
       f.dot_ends = TRUE;
 
-      DATA_BDAT:               /* Common code for DATA and BDAT */
+    DATA_BDAT:         /* Common code for DATA and BDAT */
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+      fl.pipe_connect_acceptable = FALSE;
+#endif
       if (!discarded && recipients_count <= 0)
        {
-       if (rcpt_smtp_response_same && rcpt_smtp_response != NULL)
+       if (fl.rcpt_smtp_response_same && rcpt_smtp_response != NULL)
          {
          uschar *code = US"503";
          int len = Ustrlen(rcpt_smtp_response);
@@ -5388,11 +5395,11 @@ while (done <= 0)
       break;
 
 
-    #ifdef SUPPORT_TLS
+    #ifndef DISABLE_TLS
 
     case STARTTLS_CMD:
       HAD(SCH_STARTTLS);
-      if (!tls_advertised)
+      if (!fl.tls_advertised)
        {
        done = synprot_error(L_smtp_protocol_error, 503, NULL,
          US"STARTTLS command used when not advertised");
@@ -5459,7 +5466,7 @@ while (done <= 0)
       if ((rc = tls_server_start(tls_require_ciphers, &s)) == OK)
        {
        if (!tls_remember_esmtp)
-         helo_seen = esmtp = auth_advertised = f.smtp_in_pipelining_advertised = FALSE;
+         fl.helo_seen = fl.esmtp = fl.auth_advertised = f.smtp_in_pipelining_advertised = FALSE;
        cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE;
        cmd_list[CMD_LIST_AUTH].is_mail_cmd = TRUE;
        cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE;
@@ -5473,7 +5480,7 @@ while (done <= 0)
          }
        received_protocol =
          (sender_host_address ? protocols : protocols_local)
-           [ (esmtp
+           [ (fl.esmtp
              ? pextend + (sender_host_authenticated ? pauthed : 0)
              : pnormal)
            + (tls_in.active.sock >= 0 ? pcrpted : 0)
@@ -5499,7 +5506,9 @@ while (done <= 0)
 
       /* Hard failure. Reject everything except QUIT or closed connection. One
       cause for failure is a nested STARTTLS, in which case tls_in.active remains
-      set, but we must still reject all incoming commands. */
+      set, but we must still reject all incoming commands.  Another is a handshake
+      failure - and there may some encrypted data still in the pipe to us, which we
+      see as garbage commands. */
 
       DEBUG(D_tls) debug_printf("TLS failed to start\n");
       while (done <= 0) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED))
@@ -5577,7 +5586,7 @@ while (done <= 0)
        uschar buffer[256];
        buffer[0] = 0;
        Ustrcat(buffer, " AUTH");
-       #ifdef SUPPORT_TLS
+       #ifndef DISABLE_TLS
        if (tls_in.active.sock < 0 &&
            verify_check_host(&tls_advertise_hosts) != FAIL)
          Ustrcat(buffer, " STARTTLS");
@@ -5605,7 +5614,11 @@ while (done <= 0)
        log_write(L_lost_incoming_connection, LOG_MAIN,
          "unexpected %s while reading SMTP command from %s%s%s D=%s",
          f.sender_host_unknown ? "EOF" : "disconnection",
-         f.tcp_in_fastopen && !f.tcp_in_fastopen_logged ? US"TFO " : US"",
+         f.tcp_in_fastopen_logged
+         ? US""
+         : f.tcp_in_fastopen
+         ? f.tcp_in_fastopen_data ? US"TFO* " : US"TFO "
+         : US"",
          host_and_ident(FALSE), smtp_read_error,
          string_timesince(&smtp_connection_start)
          );