GnuTLS: repeat lowlevel read and write operations while they request retry
[exim.git] / src / src / smtp_in.c
index 00c842760525b1e49632ab71bc5c78071132d1de..b3cc76c64dc298de6bd5bb5b5a432f6527d5017d 100644 (file)
@@ -145,6 +145,9 @@ static struct {
   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;
@@ -404,6 +407,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           *
@@ -420,17 +435,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;
   }
@@ -499,14 +513,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
@@ -898,18 +912,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);
   }
@@ -942,13 +958,13 @@ if (fl.rcpt_in_progress)
 #ifdef SUPPORT_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;
 }
 
 
@@ -1564,7 +1580,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;
@@ -1609,7 +1624,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 */
@@ -1760,7 +1775,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);
 
@@ -1807,7 +1822,6 @@ Returns:     nothing
 void
 smtp_log_no_mail(void)
 {
-int i;
 uschar * sep, * s;
 gstring * g = NULL;
 
@@ -1826,14 +1840,14 @@ g = s_tlslog(g);
 
 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",";
@@ -1842,7 +1856,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);
 }
 
@@ -1852,15 +1866,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);
@@ -2392,13 +2405,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
@@ -2626,7 +2646,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;
 
@@ -2643,9 +2663,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:
@@ -2693,18 +2711,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);
@@ -2987,22 +3003,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. */
@@ -3444,7 +3477,7 @@ int rc;
 uschar *user_msg = NULL;
 uschar *log_msg = NULL;
 
-/* Check for recursive acll */
+/* Check for recursive call */
 
 if (fl.smtp_exit_function_called)
   {
@@ -3465,10 +3498,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)
   {
@@ -3476,13 +3510,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();
   }
@@ -3587,33 +3621,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;
           }
     }
@@ -3656,7 +3682,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
@@ -3670,14 +3696,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
@@ -3907,7 +3933,6 @@ while (done <= 0)
   int start, end, sender_domain, recipient_domain;
   int rc;
   int c;
-  auth_instance *au;
   uschar *orcpt = NULL;
   int dsn_flags;
   gstring * g;
@@ -3922,7 +3947,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
@@ -3950,7 +3975,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
@@ -4028,23 +4059,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 */
 
@@ -4121,9 +4155,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
@@ -4183,7 +4216,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
@@ -4207,20 +4245,15 @@ while (done <= 0)
       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
@@ -4258,11 +4291,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);
@@ -4312,13 +4342,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
          }
 
 
@@ -4339,9 +4378,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)
@@ -4439,7 +4477,14 @@ while (done <= 0)
       has been seen. */
 
 #ifdef SUPPORT_TLS
-      if (tls_in.active.sock >= 0) (void)tls_write(NULL, g->s, g->ptr, FALSE); else
+      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
 
        {
@@ -5244,7 +5289,10 @@ 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 (fl.rcpt_smtp_response_same && rcpt_smtp_response != NULL)
@@ -5616,7 +5664,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)
          );