malware: use sendfile for ClamAV TCP
[exim.git] / src / src / smtp_in.c
index 40fd3083b550cc137e519df140078eb322bace00..13cba2ec21bd1c9ae2187a1daa09695e15d53ddd 100644 (file)
@@ -3,6 +3,7 @@
 *************************************************/
 
 /* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for handling an incoming SMTP call. */
@@ -133,7 +134,7 @@ to the circular buffer that holds a list of the last n received. */
 
 static struct {
   BOOL auth_advertised                 :1;
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   BOOL tls_advertised                  :1;
 #endif
   BOOL dsn_advertised                  :1;
@@ -142,7 +143,7 @@ static struct {
   BOOL helo_verify                     :1;
   BOOL helo_seen                       :1;
   BOOL helo_accept_junk                        :1;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   BOOL pipe_connect_acceptable         :1;
 #endif
   BOOL rcpt_smtp_response_same         :1;
@@ -194,7 +195,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
@@ -226,7 +227,7 @@ static smtp_cmd_list *cmd_list_end =
 /* This list of names is used for performing the smtp_no_mail logging action.
 It must be kept in step with the SCH_xxx enumerations. */
 
-static uschar *smtp_names[] =
+uschar * smtp_names[] =
   {
   US"NONE", US"AUTH", US"DATA", US"BDAT", US"EHLO", US"ETRN", US"EXPN",
   US"HELO", US"HELP", US"MAIL", US"NOOP", US"QUIT", US"RCPT", US"RSET",
@@ -348,7 +349,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,7 +398,7 @@ return TRUE;
 }
 
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
 static BOOL
 pipeline_connect_sends(void)
 {
@@ -433,7 +434,7 @@ if (!sender_address                         /* No transaction in progress */
 
 if (recipients_count > 0)
   {
-  raw_recipients = store_get(recipients_count * sizeof(uschar *));
+  raw_recipients = store_get(recipients_count * sizeof(uschar *), FALSE);
   for (int i = 0; i < recipients_count; i++)
     raw_recipients[i] = recipients_list[i].address;
   raw_recipients_count = recipients_count;
@@ -457,7 +458,7 @@ if (smtp_batched_input)
 smtp_notquit_exit(US"command-timeout", US"421",
   US"%s: SMTP command timeout - closing connection",
   smtp_active_hostname);
-exim_exit(EXIT_FAILURE, US"receiving");
+exim_exit(EXIT_FAILURE);
 }
 
 void
@@ -468,7 +469,7 @@ if (smtp_batched_input)
   moan_smtp_batch(NULL, "421 SIGTERM received");  /* Does not return */
 smtp_notquit_exit(US"signal-exit", US"421",
   US"%s: Service not available - closing connection", smtp_active_hostname);
-exim_exit(EXIT_FAILURE, US"receiving");
+exim_exit(EXIT_FAILURE);
 }
 
 void
@@ -527,8 +528,8 @@ if (rc <= 0)
       smtp_data_sigint_exit();
 
     smtp_had_error = save_errno;
-    smtp_read_error = string_copy_malloc(
-      string_sprintf(" (error: %s)", strerror(save_errno)));
+    smtp_read_error = string_copy_perm(
+      string_sprintf(" (error: %s)", strerror(save_errno)), FALSE);
     }
   else
     smtp_had_eof = 1;
@@ -877,6 +878,8 @@ flush for non-TLS connections. The smtp_fflush() function is available for
 checking that: for convenience, TLS output errors are remembered here so that
 they are also picked up later by smtp_fflush().
 
+This function is exposed to the local_scan API; do not change the signature.
+
 Arguments:
   format      format string
   more       further data expected
@@ -897,7 +900,11 @@ va_end(ap);
 
 /* This is split off so that verify.c:respond_printf() can, in effect, call
 smtp_printf(), bearing in mind that in C a vararg function can't directly
-call another vararg function, only a function which accepts a va_list. */
+call another vararg function, only a function which accepts a va_list.
+
+This function is exposed to the local_scan API; do not change the signature.
+*/
+/*XXX consider passing caller-info in, for string_vformat-onward */
 
 void
 smtp_vprintf(const char *format, BOOL more, va_list ap)
@@ -905,26 +912,27 @@ 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(&gs, FALSE, format, ap);
+/* Use taint-unchecked routines for writing into big_buffer, trusting
+that we'll never expand it. */
+
+yield = !! string_vformat(&gs, SVFMT_TAINT_NOCHK, format, ap);
 string_from_gstring(&gs);
 
 DEBUG(D_receive)
   {
-  void *reset_point = store_get(0);
   uschar *msg_copy, *cr, *end;
   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);
   debug_printf("SMTP>> %s", msg_copy);
-  store_reset(reset_point);
   }
 
 if (!yield)
   {
   log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_printf()");
   smtp_closedown(US"Unexpected error");
-  exim_exit(EXIT_FAILURE, NULL);
+  exim_exit(EXIT_FAILURE);
   }
 
 /* If this is the first output for a (non-batch) RCPT command, see if all RCPTs
@@ -945,16 +953,13 @@ if (fl.rcpt_in_progress)
 
 /* Now write the string */
 
-#ifdef SUPPORT_TLS
-if (tls_in.active.sock >= 0)
-  {
-  if (tls_write(NULL, gs.s, gs.ptr, more) < 0)
-    smtp_write_error = -1;
-  }
-else
+if (
+#ifndef DISABLE_TLS
+    tls_in.active.sock >= 0 ? (tls_write(NULL, gs.s, gs.ptr, more) < 0) :
 #endif
-
-if (fprintf(smtp_out, "%s", gs.s) < 0) smtp_write_error = -1;
+    (fwrite(gs.s, gs.ptr, 1, smtp_out) == 0)
+   )
+    smtp_write_error = -1;
 }
 
 
@@ -965,8 +970,7 @@ if (fprintf(smtp_out, "%s", gs.s) < 0) smtp_write_error = -1;
 
 /* This function isn't currently used within Exim (it detects errors when it
 tries to read the next SMTP input), but is available for use in local_scan().
-For non-TLS connections, it flushes the output and checks for errors. For
-TLS-connections, it checks for a previously-detected TLS write error.
+It flushes the output and checks for errors.
 
 Arguments:  none
 Returns:    0 for no error; -1 after an error
@@ -976,6 +980,15 @@ int
 smtp_fflush(void)
 {
 if (tls_in.active.sock < 0 && fflush(smtp_out) != 0) smtp_write_error = -1;
+
+if (
+#ifndef DISABLE_TLS
+    tls_in.active.sock >= 0 ? (tls_write(NULL, NULL, 0, FALSE) < 0) :
+#endif
+    (fflush(smtp_out) != 0)
+   )
+    smtp_write_error = -1;
+
 return smtp_write_error;
 }
 
@@ -1020,25 +1033,6 @@ had_command_sigterm = sig;
 
 
 #ifdef SUPPORT_PROXY
-/*************************************************
-*     Restore socket timeout to previous value   *
-*************************************************/
-/* If the previous value was successfully retrieved, restore
-it before returning control to the non-proxy routines
-
-Arguments: fd     - File descriptor for input
-           get_ok - Successfully retrieved previous values
-           tvtmp  - Time struct with previous values
-           vslen  - Length of time struct
-Returns:   none
-*/
-static void
-restore_socket_timeout(int fd, int get_ok, struct timeval * tvtmp, socklen_t vslen)
-{
-if (get_ok == 0)
-  (void) setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, CS tvtmp, vslen);
-}
-
 /*************************************************
 *       Check if host is required proxy host     *
 *************************************************/
@@ -1115,7 +1109,7 @@ if (cr != NULL)
 
 while (capacity > 0)
   {
-  do { ret = recv(fd, to, 1, 0); } while (ret == -1 && errno == EINTR);
+  do { ret = read(fd, to, 1); } while (ret == -1 && errno == EINTR && !had_command_timeout);
   if (ret == -1)
     return -1;
   have++;
@@ -1219,20 +1213,11 @@ int size, ret;
 int fd = fileno(smtp_in);
 const char v2sig[12] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
 uschar * iptype;  /* To display debug info */
-struct timeval tv;
-struct timeval tvtmp;
 socklen_t vslen = sizeof(struct timeval);
 BOOL yield = FALSE;
 
-/* Save current socket timeout values */
-get_ok = getsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, CS &tvtmp, &vslen);
-
-/* Proxy Protocol host must send header within a short time
-(default 3 seconds) or it's considered invalid */
-tv.tv_sec  = PROXY_NEGOTIATION_TIMEOUT_SEC;
-tv.tv_usec = PROXY_NEGOTIATION_TIMEOUT_USEC;
-if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, CS &tv, sizeof(tv)) < 0)
-  goto bad;
+os_non_restarting_signal(SIGALRM, command_timeout_handler);
+ALARM(proxy_protocol_timeout);
 
 do
   {
@@ -1240,9 +1225,9 @@ do
   don't do a PEEK into the data, actually slurp up enough to be
   "safe". Can't take it all because TLS-on-connect clients follow
   immediately with TLS handshake. */
-  ret = recv(fd, &hdr, PROXY_INITIAL_READ, 0);
+  ret = read(fd, &hdr, PROXY_INITIAL_READ);
   }
-  while (ret == -1 && errno == EINTR);
+  while (ret == -1 && errno == EINTR && !had_command_timeout);
 
 if (ret == -1)
   goto proxyfail;
@@ -1256,8 +1241,8 @@ if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0))
   /* First get the length fields. */
   do
     {
-    retmore = recv(fd, (uschar*)&hdr + ret, PROXY_V2_HEADER_SIZE - PROXY_INITIAL_READ, 0);
-    } while (retmore == -1 && errno == EINTR);
+    retmore = read(fd, (uschar*)&hdr + ret, PROXY_V2_HEADER_SIZE - PROXY_INITIAL_READ);
+    } while (retmore == -1 && errno == EINTR && !had_command_timeout);
   if (retmore == -1)
     goto proxyfail;
   ret += retmore;
@@ -1293,8 +1278,8 @@ if ((ret == PROXY_INITIAL_READ) && (memcmp(&hdr.v2, v2sig, sizeof(v2sig)) == 0))
     {
     do
       {
-      retmore = recv(fd, (uschar*)&hdr + ret, size-ret, 0);
-      } while (retmore == -1 && errno == EINTR);
+      retmore = read(fd, (uschar*)&hdr + ret, size-ret);
+      } while (retmore == -1 && errno == EINTR && !had_command_timeout);
     if (retmore == -1)
       goto proxyfail;
     ret += retmore;
@@ -1522,7 +1507,8 @@ done:
 should cause a synchronization failure */
 
 proxyfail:
-  restore_socket_timeout(fd, get_ok, &tvtmp, vslen);
+  DEBUG(D_receive) if (had_command_timeout)
+    debug_printf("Timeout while reading proxy header\n");
 
 bad:
   if (yield)
@@ -1538,6 +1524,7 @@ bad:
       debug_printf("Failure to extract proxied host, only QUIT allowed\n");
     }
 
+ALARM(0);
 return;
 }
 #endif
@@ -1720,6 +1707,7 @@ for (;;) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED))
     return;
 
   case QUIT_CMD:
+    f.smtp_in_quit = TRUE;
     smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
     mac_smtp_fflush();
     return;
@@ -1774,7 +1762,7 @@ return string_sprintf("SMTP connection from %s", hostname);
 
 
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 /* Append TLS-related information to a log line
 
 Arguments:
@@ -1788,7 +1776,7 @@ 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
+#ifndef DISABLE_TLS_RESUME
   if (LOGGING(tls_resumption) && tls_in.resumption & RESUME_USED)
     g = string_catn(g, US"*", 1);
 #endif
@@ -1798,11 +1786,31 @@ if (LOGGING(tls_certificate_verified) && tls_in.cipher)
 if (LOGGING(tls_peerdn) && tls_in.peerdn)
   g = string_append(g, 3, US" DN=\"", string_printing(tls_in.peerdn), US"\"");
 if (LOGGING(tls_sni) && tls_in.sni)
-  g = string_append(g, 3, US" SNI=\"", string_printing(tls_in.sni), US"\"");
+  g = string_append(g, 2, US" SNI=", string_printing2(tls_in.sni, SP_TAB|SP_SPACE));
 return g;
 }
 #endif
 
+
+
+static gstring *
+s_connhad_log(gstring * g)
+{
+const uschar * sep = smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE
+  ? US" C=..." : US" C=";
+
+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 (int i = 0; i < smtp_ch_index; i++, sep = US",")
+  g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]);
+return g;
+}
+
+
 /*************************************************
 *      Log lack of MAIL if so configured         *
 *************************************************/
@@ -1818,7 +1826,7 @@ Returns:     nothing
 void
 smtp_log_no_mail(void)
 {
-uschar * sep, * s;
+uschar * s;
 gstring * g = NULL;
 
 if (smtp_mailcmd_count > 0 || !LOGGING(smtp_no_mail))
@@ -1830,24 +1838,11 @@ 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 (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 (int i = 0; i < smtp_ch_index; i++)
-  {
-  g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]);
-  sep = US",";
-  }
+g = s_connhad_log(g);
 
 if (!(s = string_from_gstring(g))) s = US"";
 
@@ -1906,11 +1901,7 @@ BOOL yield = fl.helo_accept_junk;
 
 /* Discard any previous helo name */
 
-if (sender_helo_name)
-  {
-  store_free(sender_helo_name);
-  sender_helo_name = NULL;
-  }
+sender_helo_name = NULL;
 
 /* Skip tests if junk is permitted. */
 
@@ -1949,7 +1940,7 @@ if (!yield)
 
 /* Save argument if OK */
 
-if (yield) sender_helo_name = string_copy_malloc(start);
+if (yield) sender_helo_name = string_copy_perm(start, TRUE);
 return yield;
 }
 
@@ -2021,7 +2012,7 @@ Argument:   the stacking pool storage reset point
 Returns:    nothing
 */
 
-void
+void *
 smtp_reset(void *reset_point)
 {
 recipients_list = NULL;
@@ -2029,29 +2020,32 @@ rcpt_count = rcpt_defer_count = rcpt_fail_count =
   raw_recipients_count = recipients_count = recipients_list_max = 0;
 message_linecount = 0;
 message_size = -1;
+message_body = message_body_end = NULL;
 acl_added_headers = NULL;
 acl_removed_headers = NULL;
 f.queue_only_policy = FALSE;
 rcpt_smtp_response = NULL;
 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 */
+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 */
 #ifdef WITH_CONTENT_SCAN
-f.no_mbox_unspool = FALSE;                             /* Can be set by ACL */
+f.no_mbox_unspool = FALSE;                             /* Can be set by ACL */
 #endif
-f.submission_mode = FALSE;                             /* Can be set by ACL */
+f.submission_mode = FALSE;                             /* Can be set by ACL */
 f.suppress_local_fixups = f.suppress_local_fixups_default; /* Can be set by ACL */
-f.active_local_from_check = local_from_check;          /* Can be set by ACL */
-f.active_local_sender_retain = local_sender_retain;    /* Can be set by ACL */
+f.active_local_from_check = local_from_check;          /* Can be set by ACL */
+f.active_local_sender_retain = local_sender_retain;    /* Can be set by ACL */
 sending_ip_address = NULL;
 return_path = sender_address = NULL;
-sender_data = NULL;                                 /* Can be set by ACL */
+deliver_localpart_data = deliver_domain_data =
+recipient_data = sender_data = NULL;                   /* Can be set by ACL */
+recipient_verify_failure = NULL;
 deliver_localpart_parent = deliver_localpart_orig = NULL;
 deliver_domain_parent = deliver_domain_orig = NULL;
 callout_address = NULL;
-submission_name = NULL;                              /* Can be set by ACL */
+submission_name = NULL;                                        /* Can be set by ACL */
 raw_sender = NULL;                  /* After SMTP rewrite, before qualifying */
 sender_address_unrewritten = NULL;  /* Set only after verify rewrite */
 sender_verified_list = NULL;        /* No senders verified */
@@ -2077,13 +2071,14 @@ dkim_collect_input = 0;
 dkim_verify_overall = dkim_verify_status = dkim_verify_reason = NULL;
 dkim_key_length = 0;
 #endif
-#ifdef EXPERIMENTAL_DMARC
+#ifdef SUPPORT_DMARC
 f.dmarc_has_been_checked = f.dmarc_disable_verify = f.dmarc_enable_forensic = FALSE;
 dmarc_domain_policy = dmarc_status = dmarc_status_text =
 dmarc_used_domain = NULL;
 #endif
 #ifdef EXPERIMENTAL_ARC
 arc_state = arc_state_reason = NULL;
+arc_received_instance = 0;
 #endif
 dsn_ret = 0;
 dsn_envid = NULL;
@@ -2104,23 +2099,7 @@ ratelimiters_mail = NULL;           /* Updated by ratelimit ACL condition */
 
 acl_var_m = NULL;
 
-/* The message body variables use malloc store. They may be set if this is
-not the first message in an SMTP session and the previous message caused them
-to be referenced in an ACL. */
-
-if (message_body)
-  {
-  store_free(message_body);
-  message_body = NULL;
-  }
-
-if (message_body_end)
-  {
-  store_free(message_body_end);
-  message_body_end = NULL;
-  }
-
-/* Warning log messages are also saved in malloc store. They are saved to avoid
+/* Warning log messages are saved in malloc store. They are saved to avoid
 repetition in the same message, but it seems right to repeat them for different
 messages. */
 
@@ -2131,6 +2110,7 @@ while (acl_warn_logged)
   store_free(this);
   }
 store_reset(reset_point);
+return store_mark();
 }
 
 
@@ -2158,7 +2138,7 @@ static int
 smtp_setup_batch_msg(void)
 {
 int done = 0;
-void *reset_point = store_get(0);
+rmark reset_point = store_mark();
 
 /* Save the line count at the start of each transaction - single commands
 like HELO and RSET count as whole transactions. */
@@ -2168,7 +2148,7 @@ bsmtp_transaction_linecount = receive_linecount;
 if ((receive_feof)()) return 0;   /* Treat EOF as QUIT */
 
 cancel_cutthrough_connection(TRUE, US"smtp_setup_batch_msg");
-smtp_reset(reset_point);                /* Reset for start of message */
+reset_point = smtp_reset(reset_point);                /* Reset for start of message */
 
 /* Deal with SMTP commands. This loop is exited by setting done to a POSITIVE
 value. The values are 2 larger than the required yield of the function. */
@@ -2193,7 +2173,7 @@ while (done <= 0)
 
     case RSET_CMD:
       cancel_cutthrough_connection(TRUE, US"RSET received");
-      smtp_reset(reset_point);
+      reset_point = smtp_reset(reset_point);
       bsmtp_transaction_linecount = receive_linecount;
       break;
 
@@ -2217,7 +2197,7 @@ while (done <= 0)
       /* Reset to start of message */
 
       cancel_cutthrough_connection(TRUE, US"MAIL received");
-      smtp_reset(reset_point);
+      reset_point = smtp_reset(reset_point);
 
       /* Apply SMTP rewrite */
 
@@ -2344,8 +2324,9 @@ while (done <= 0)
       break;
 
 
-    case EOF_CMD:
     case QUIT_CMD:
+      f.smtp_in_quit = TRUE;
+    case EOF_CMD:
       done = 2;
       break;
 
@@ -2375,7 +2356,7 @@ return done - 2;  /* Convert yield values */
 
 
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 static BOOL
 smtp_log_tls_fail(uschar * errstr)
 {
@@ -2396,24 +2377,47 @@ return FALSE;
 static void
 tfo_in_check(void)
 {
-# ifdef TCP_INFO
+# ifdef __FreeBSD__
+int is_fastopen;
+socklen_t len = sizeof(is_fastopen);
+
+/* The tinfo TCPOPT_FAST_OPEN bit seems unreliable, and we don't see state
+TCP_SYN_RCV (as of 12.1) so no idea about data-use. */
+
+if (getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_FASTOPEN, &is_fastopen, &len) == 0)
+  {
+  if (is_fastopen)
+    {
+    DEBUG(D_receive)
+      debug_printf("TFO mode connection (TCP_FASTOPEN getsockopt)\n");
+    f.tcp_in_fastopen = TRUE;
+    }
+  }
+else DEBUG(D_receive)
+  debug_printf("TCP_INFO getsockopt: %s\n", strerror(errno));
+
+# elif defined(TCP_INFO)
 struct tcp_info tinfo;
 socklen_t len = sizeof(tinfo);
 
 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 */
+#  ifdef TCPI_OPT_SYN_DATA     /* FreeBSD 11,12 do 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");
+    DEBUG(D_receive)
+      debug_printf("TFO 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)
+#  endif
+    if (tinfo.tcpi_state == TCP_SYN_RECV)      /* Not seen on FreeBSD 12.1 */
     {
-    DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (state TCP_SYN_RECV)\n");
+    DEBUG(D_receive)
+      debug_printf("TFO mode connection (state TCP_SYN_RECV)\n");
     f.tcp_in_fastopen = TRUE;
     }
+else DEBUG(D_receive)
+  debug_printf("TCP_INFO getsockopt: %s\n", strerror(errno));
 # endif
 }
 #endif
@@ -2466,8 +2470,8 @@ if (!host_checking && !f.sender_host_notsocket)
   sender_host_auth_pubname = sender_host_authenticated = NULL;
 authenticated_by = NULL;
 
-#ifdef SUPPORT_TLS
-tls_in.cipher = tls_in.peerdn = NULL;
+#ifndef DISABLE_TLS
+tls_in.ver = tls_in.cipher = tls_in.peerdn = NULL;
 tls_in.ourcert = tls_in.peercert = NULL;
 tls_in.sni = NULL;
 tls_in.ocsp = OCSP_NOT_REQ;
@@ -2482,11 +2486,9 @@ fl.smtputf8_advertised = FALSE;
 
 acl_var_c = NULL;
 
-/* Allow for trailing 0 in the command and data buffers. */
+/* Allow for trailing 0 in the command and data buffers.  Tainted. */
 
-if (!(smtp_cmd_buffer = US malloc(2*SMTP_CMD_BUFFER_SIZE + 2)))
-  log_write(0, LOG_MAIN|LOG_PANIC_DIE,
-    "malloc() failed for SMTP command buffer");
+smtp_cmd_buffer = store_get_perm(2*SMTP_CMD_BUFFER_SIZE + 2, TRUE);
 
 smtp_cmd_buffer[0] = 0;
 smtp_data_buffer = smtp_cmd_buffer + SMTP_CMD_BUFFER_SIZE + 1;
@@ -2597,7 +2599,7 @@ if (!f.sender_host_unknown)
     {
     #if OPTSTYLE == 1
     EXIM_SOCKLEN_T optlen = sizeof(struct ip_options) + MAX_IPOPTLEN;
-    struct ip_options *ipopt = store_get(optlen);
+    struct ip_options *ipopt = store_get(optlen, FALSE);
     #elif OPTSTYLE == 2
     struct ip_opts ipoptblock;
     struct ip_opts *ipopt = &ipoptblock;
@@ -2903,10 +2905,10 @@ 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)
+    if (tls_server_start(&user_msg) != OK)
       return smtp_log_tls_fail(user_msg);
     cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE;
     }
@@ -2995,7 +2997,7 @@ while (*p);
 /* Before we write the banner, check that there is no input pending, unless
 this synchronisation check is disabled. */
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
 fl.pipe_connect_acceptable =
   sender_host_address && verify_check_host(&pipe_connect_advertise_hosts) == OK;
 
@@ -3008,7 +3010,7 @@ if (!check_sync())
 #endif
     {
     unsigned n = smtp_inend - smtp_inptr;
-    if (n > 32) n = 32;
+    if (n > 128) n = 128;
 
     log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol "
       "synchronization error (input sent without waiting for greeting): "
@@ -3022,7 +3024,7 @@ if (!check_sync())
 /*XXX the ehlo-resp code does its own tls/nontls bit.  Maybe subroutine that? */
 
 smtp_printf("%s",
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
   fl.pipe_connect_acceptable && pipeline_connect_sends(),
 #else
   FALSE,
@@ -3033,7 +3035,7 @@ smtp_printf("%s",
 handshake arrived.  If so we must have managed a TFO. */
 
 #ifdef TCP_FASTOPEN
-tfo_in_check();
+if (sender_host_address && !f.sender_host_notsocket) tfo_in_check();
 #endif
 
 return TRUE;
@@ -3069,15 +3071,17 @@ synprot_error(int type, int code, uschar *data, uschar *errmess)
 int yield = -1;
 
 log_write(type, LOG_MAIN, "SMTP %s error in \"%s\" %s %s",
-  (type == L_smtp_syntax_error)? "syntax" : "protocol",
+  type == L_smtp_syntax_error ? "syntax" : "protocol",
   string_printing(smtp_cmd_buffer), host_and_ident(TRUE), errmess);
 
 if (++synprot_error_count > smtp_max_synprot_errors)
   {
   yield = 1;
   log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
-    "syntax or protocol errors (last command was \"%s\")",
-    host_and_ident(FALSE), string_printing(smtp_cmd_buffer));
+    "syntax or protocol errors (last command was \"%s\", %s)",
+    host_and_ident(FALSE), string_printing(smtp_cmd_buffer),
+    string_from_gstring(s_connhad_log(NULL))
+    );
   }
 
 if (code > 0)
@@ -3163,7 +3167,7 @@ for (;;)
     {
     smtp_printf("%.3s-%.*s%.*s\r\n", TRUE, code, esclen, esc, (int)(nl - msg), msg);
     msg = nl + 1;
-    while (isspace(*msg)) msg++;
+    Uskip_whitespace(&msg);
     }
   }
 }
@@ -3404,7 +3408,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"";
@@ -3506,7 +3510,7 @@ if (code && defaultrespond)
     va_list ap;
 
     va_start(ap, defaultrespond);
-    g = string_vformat(NULL, TRUE, CS defaultrespond, ap);
+    g = string_vformat(NULL, SVFMT_EXTEND|SVFMT_REBUFFER, CS defaultrespond, ap);
     va_end(ap);
     smtp_printf("%s %s\r\n", FALSE, code, string_from_gstring(g));
     }
@@ -3717,60 +3721,60 @@ if (rc != OK)
 switch(rc)
   {
   case OK:
-  if (!au->set_id || set_id)    /* Complete success */
-    {
-    if (set_id) authenticated_id = string_copy_malloc(set_id);
-    sender_host_authenticated = au->name;
-    sender_host_auth_pubname  = au->public_name;
-    authentication_failed = FALSE;
-    authenticated_fail_id = NULL;   /* Impossible to already be set? */
-
-    received_protocol =
-      (sender_host_address ? protocols : protocols_local)
-       [pextend + pauthed + (tls_in.active.sock >= 0 ? pcrpted:0)];
-    *s = *ss = US"235 Authentication succeeded";
-    authenticated_by = au;
-    break;
-    }
+    if (!au->set_id || set_id)    /* Complete success */
+      {
+      if (set_id) authenticated_id = string_copy_perm(set_id, TRUE);
+      sender_host_authenticated = au->name;
+      sender_host_auth_pubname  = au->public_name;
+      authentication_failed = FALSE;
+      authenticated_fail_id = NULL;   /* Impossible to already be set? */
+
+      received_protocol =
+       (sender_host_address ? protocols : protocols_local)
+         [pextend + pauthed + (tls_in.active.sock >= 0 ? pcrpted:0)];
+      *s = *ss = US"235 Authentication succeeded";
+      authenticated_by = au;
+      break;
+      }
 
-  /* Authentication succeeded, but we failed to expand the set_id string.
-  Treat this as a temporary error. */
+    /* Authentication succeeded, but we failed to expand the set_id string.
+    Treat this as a temporary error. */
 
-  auth_defer_msg = expand_string_message;
-  /* Fall through */
+    auth_defer_msg = expand_string_message;
+    /* Fall through */
 
   case DEFER:
-  if (set_id) authenticated_fail_id = string_copy_malloc(set_id);
-  *s = string_sprintf("435 Unable to authenticate at present%s",
-    auth_defer_user_msg);
-  *ss = string_sprintf("435 Unable to authenticate at present%s: %s",
-    set_id, auth_defer_msg);
-  break;
+    if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
+    *s = string_sprintf("435 Unable to authenticate at present%s",
+      auth_defer_user_msg);
+    *ss = string_sprintf("435 Unable to authenticate at present%s: %s",
+      set_id, auth_defer_msg);
+    break;
 
   case BAD64:
-  *s = *ss = US"501 Invalid base64 data";
-  break;
+    *s = *ss = US"501 Invalid base64 data";
+    break;
 
   case CANCELLED:
-  *s = *ss = US"501 Authentication cancelled";
-  break;
+    *s = *ss = US"501 Authentication cancelled";
+    break;
 
   case UNEXPECTED:
-  *s = *ss = US"553 Initial data not expected";
-  break;
+    *s = *ss = US"553 Initial data not expected";
+    break;
 
   case FAIL:
-  if (set_id) authenticated_fail_id = string_copy_malloc(set_id);
-  *s = US"535 Incorrect authentication data";
-  *ss = string_sprintf("535 Incorrect authentication data%s", set_id);
-  break;
+    if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
+    *s = US"535 Incorrect authentication data";
+    *ss = string_sprintf("535 Incorrect authentication data%s", set_id);
+    break;
 
   default:
-  if (set_id) authenticated_fail_id = string_copy_malloc(set_id);
-  *s = US"435 Internal error";
-  *ss = string_sprintf("435 Internal error%s: return %d from authentication "
-    "check", set_id, rc);
-  break;
+    if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
+    *s = US"435 Internal error";
+    *ss = string_sprintf("435 Internal error%s: return %d from authentication "
+      "check", set_id, rc);
+    break;
   }
 
 return rc;
@@ -3807,17 +3811,16 @@ static void
 smtp_quit_handler(uschar ** user_msgp, uschar ** log_msgp)
 {
 HAD(SCH_QUIT);
+f.smtp_in_quit = TRUE;
 incomplete_transaction_log(US"QUIT");
-if (acl_smtp_quit)
-  {
-  int rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, user_msgp, log_msgp);
-  if (rc == ERROR)
+if (  acl_smtp_quit
+   && acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, user_msgp, log_msgp)
+       == ERROR)
     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));
+#ifdef EXIM_TCP_CORK
+(void) setsockopt(fileno(smtp_out), IPPROTO_TCP, EXIM_TCP_CORK, US &on, sizeof(on));
 #endif
 
 if (*user_msgp)
@@ -3825,7 +3828,7 @@ if (*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
 
@@ -3878,7 +3881,7 @@ BOOL toomany = FALSE;
 BOOL discarded = FALSE;
 BOOL last_was_rej_mail = FALSE;
 BOOL last_was_rcpt = FALSE;
-void *reset_point = store_get(0);
+rmark reset_point = store_mark();
 
 DEBUG(D_receive) debug_printf("smtp_setup_msg entered\n");
 
@@ -3888,7 +3891,7 @@ message. Ditto for EHLO/HELO and for STARTTLS, to allow for going in and out of
 TLS between messages (an Exim client may do this if it has messages queued up
 for the host). Note: we do NOT reset AUTH at this point. */
 
-smtp_reset(reset_point);
+reset_point = smtp_reset(reset_point);
 message_ended = END_NOTSTARTED;
 
 chunking_state = f.chunking_offered ? CHUNKING_OFFERED : CHUNKING_NOT_OFFERED;
@@ -3896,7 +3899,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
 
@@ -3973,7 +3976,7 @@ while (done <= 0)
 #endif
 
   switch(smtp_read_command(
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
          !fl.pipe_connect_acceptable,
 #else
          TRUE,
@@ -4125,8 +4128,10 @@ while (done <= 0)
        if (++synprot_error_count > smtp_max_synprot_errors)
          {
          log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
-           "syntax or protocol errors (last command was \"%s\")",
-           host_and_ident(FALSE), string_printing(smtp_cmd_buffer));
+           "syntax or protocol errors (last command was \"%s\", %s)",
+           host_and_ident(FALSE), string_printing(smtp_cmd_buffer),
+           string_from_gstring(s_connhad_log(NULL))
+           );
          done = 1;
          }
 
@@ -4194,7 +4199,7 @@ while (done <= 0)
 
 #ifdef SUPPORT_SPF
       /* set up SPF context */
-      spf_init(sender_helo_name, sender_host_address);
+      spf_conn_init(sender_helo_name, sender_host_address);
 #endif
 
       /* Apply an ACL check if one is defined; afterwards, recheck
@@ -4205,15 +4210,11 @@ while (done <= 0)
                  &user_msg, &log_msg)) != OK)
          {
          done = smtp_handle_acl_fail(ACL_WHERE_HELO, rc, user_msg, log_msg);
-         if (sender_helo_name)
-           {
-           store_free(sender_helo_name);
-           sender_helo_name = NULL;
-           }
+         sender_helo_name = NULL;
          host_build_sender_fullhost();  /* Rebuild */
          break;
          }
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
        else if (!fl.pipe_connect_acceptable && !check_sync())
 #else
        else if (!check_sync())
@@ -4228,7 +4229,7 @@ while (done <= 0)
 
       fl.auth_advertised = FALSE;
       f.smtp_in_pipelining_advertised = FALSE;
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
       fl.tls_advertised = FALSE;
 #endif
       fl.dsn_advertised = FALSE;
@@ -4239,7 +4240,9 @@ while (done <= 0)
       smtp_code = US"250 ";        /* Default response code plus space*/
       if (!user_msg)
        {
-       g = string_fmt_append(NULL, "%.3s %s Hello %s%s%s",
+       /* sender_host_name below will be tainted, so save on copy when we hit it */
+       g = string_get_tainted(24, TRUE);
+       g = string_fmt_append(g, "%.3s %s Hello %s%s%s",
          smtp_code,
          smtp_active_hostname,
          sender_ident ? sender_ident : US"",
@@ -4344,7 +4347,7 @@ while (done <= 0)
          sync_cmd_limit = NON_SYNC_CMD_PIPELINING;
          f.smtp_in_pipelining_advertised = TRUE;
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
          if (fl.pipe_connect_acceptable)
            {
            f.smtp_in_early_pipe_advertised = TRUE;
@@ -4379,8 +4382,8 @@ while (done <= 0)
            if (au->server)
              {
              DEBUG(D_auth+D_expand) debug_printf_indent(
-               "Evaluating advertise_condition for %s athenticator\n",
-               au->public_name);
+               "Evaluating advertise_condition for %s %s athenticator\n",
+               au->name, au->public_name);
              if (  !au->advertise_condition
                 || expand_check_condition(au->advertise_condition, au->name,
                        US"authenticator")
@@ -4421,7 +4424,7 @@ 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)
          {
@@ -4459,20 +4462,18 @@ while (done <= 0)
       /* Terminate the string (for debug), write it, and note that HELO/EHLO
       has been seen. */
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
       if (tls_in.active.sock >= 0)
        (void)tls_write(NULL, g->s, g->ptr,
-# ifdef EXPERIMENTAL_PIPE_CONNECT
+# ifndef DISABLE_PIPE_CONNECT
                        fl.pipe_connect_acceptable && pipeline_connect_sends());
 # else
                        FALSE);
 # endif
       else
 #endif
+       (void) fwrite(g->s, 1, g->ptr, smtp_out);
 
-       {
-       int i = fwrite(g->s, 1, g->ptr, smtp_out); i = i; /* compiler quietening */
-       }
       DEBUG(D_receive)
        {
        uschar *cr;
@@ -4493,7 +4494,7 @@ while (done <= 0)
          + (tls_in.active.sock >= 0 ? pcrpted : 0)
          ];
       cancel_cutthrough_connection(TRUE, US"sent EHLO response");
-      smtp_reset(reset_point);
+      reset_point = smtp_reset(reset_point);
       toomany = FALSE;
       break;   /* HELO/EHLO */
 
@@ -4548,7 +4549,7 @@ while (done <= 0)
       obviously need to throw away any previous data. */
 
       cancel_cutthrough_connection(TRUE, US"MAIL received");
-      smtp_reset(reset_point);
+      reset_point = smtp_reset(reset_point);
       toomany = FALSE;
       sender_data = recipient_data = NULL;
 
@@ -4832,8 +4833,8 @@ while (done <= 0)
       and EXPN etc. to be used when space is short. */
 
       if (!receive_check_fs(
-          (smtp_check_spool_space && message_size >= 0)?
-             message_size + 5000 : 0))
+          smtp_check_spool_space && message_size >= 0
+             message_size + 5000 : 0))
        {
        smtp_printf("452 Space shortage, please try later\r\n", FALSE);
        sender_address = NULL;
@@ -5142,9 +5143,9 @@ while (done <= 0)
        recipients_list[recipients_count-1].orcpt = orcpt;
        recipients_list[recipients_count-1].dsn_flags = dsn_flags;
 
-       DEBUG(D_receive) debug_printf("DSN: orcpt: %s  flags: %d\n",
+       /* DEBUG(D_receive) debug_printf("DSN: orcpt: %s  flags: %d\n",
          recipients_list[recipients_count-1].orcpt,
-         recipients_list[recipients_count-1].dsn_flags);
+         recipients_list[recipients_count-1].dsn_flags); */
        }
 
       /* The recipient was discarded */
@@ -5159,8 +5160,8 @@ while (done <= 0)
        discarded = TRUE;
        log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> RCPT %s: "
          "discarded by %s ACL%s%s", host_and_ident(TRUE),
-         sender_address_unrewritten? sender_address_unrewritten : sender_address,
-         smtp_cmd_argument, f.recipients_discarded? "MAIL" : "RCPT",
+         sender_address_unrewritten ? sender_address_unrewritten : sender_address,
+         smtp_cmd_argument, f.recipients_discarded ? "MAIL" : "RCPT",
          log_msg ? US": " : US"", log_msg ? log_msg : US"");
        }
 
@@ -5240,7 +5241,7 @@ while (done <= 0)
       f.dot_ends = TRUE;
 
     DATA_BDAT:         /* Common code for DATA and BDAT */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
       fl.pipe_connect_acceptable = FALSE;
 #endif
       if (!discarded && recipients_count <= 0)
@@ -5258,10 +5259,10 @@ while (done <= 0)
          }
        if (f.smtp_in_pipelining_advertised && last_was_rcpt)
          smtp_printf("503 Valid RCPT command must precede %s\r\n", FALSE,
-           smtp_names[smtp_connection_had[smtp_ch_index-1]]);
+           smtp_names[smtp_connection_had[SMTP_HBUFF_PREV(smtp_ch_index)]]);
        else
          done = synprot_error(L_smtp_protocol_error, 503, NULL,
-           smtp_connection_had[smtp_ch_index-1] == SCH_DATA
+           smtp_connection_had[SMTP_HBUFF_PREV(smtp_ch_index)] == SCH_DATA
            ? US"valid RCPT command must precede DATA"
            : US"valid RCPT command must precede BDAT");
 
@@ -5395,7 +5396,7 @@ while (done <= 0)
       break;
 
 
-    #ifdef SUPPORT_TLS
+    #ifndef DISABLE_TLS
 
     case STARTTLS_CMD:
       HAD(SCH_STARTTLS);
@@ -5424,7 +5425,7 @@ while (done <= 0)
 
       incomplete_transaction_log(US"STARTTLS");
       cancel_cutthrough_connection(TRUE, US"STARTTLS received");
-      smtp_reset(reset_point);
+      reset_point = smtp_reset(reset_point);
       toomany = FALSE;
       cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = FALSE;
 
@@ -5463,7 +5464,7 @@ while (done <= 0)
       STARTTLS that don't add to the nonmail command count. */
 
       s = NULL;
-      if ((rc = tls_server_start(tls_require_ciphers, &s)) == OK)
+      if ((rc = tls_server_start(&s)) == OK)
        {
        if (!tls_remember_esmtp)
          fl.helo_seen = fl.esmtp = fl.auth_advertised = f.smtp_in_pipelining_advertised = FALSE;
@@ -5472,7 +5473,6 @@ while (done <= 0)
        cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE;
        if (sender_helo_name)
          {
-         store_free(sender_helo_name);
          sender_helo_name = NULL;
          host_build_sender_fullhost();  /* Rebuild */
          set_process_info("handling incoming TLS connection from %s",
@@ -5526,6 +5526,7 @@ while (done <= 0)
        some sense is perhaps "right". */
 
        case QUIT_CMD:
+         f.smtp_in_quit = TRUE;
          user_msg = NULL;
          if (  acl_smtp_quit
             && ((rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, &user_msg,
@@ -5563,7 +5564,7 @@ while (done <= 0)
     case RSET_CMD:
       smtp_rset_handler();
       cancel_cutthrough_connection(TRUE, US"RSET received");
-      smtp_reset(reset_point);
+      reset_point = smtp_reset(reset_point);
       toomany = FALSE;
       break;
 
@@ -5585,17 +5586,17 @@ while (done <= 0)
        {
        uschar buffer[256];
        buffer[0] = 0;
-       Ustrcat(buffer, " AUTH");
-       #ifdef SUPPORT_TLS
+       Ustrcat(buffer, US" AUTH");
+       #ifndef DISABLE_TLS
        if (tls_in.active.sock < 0 &&
            verify_check_host(&tls_advertise_hosts) != FAIL)
-         Ustrcat(buffer, " STARTTLS");
+         Ustrcat(buffer, US" STARTTLS");
        #endif
-       Ustrcat(buffer, " HELO EHLO MAIL RCPT DATA BDAT");
-       Ustrcat(buffer, " NOOP QUIT RSET HELP");
-       if (acl_smtp_etrn != NULL) Ustrcat(buffer, " ETRN");
-       if (acl_smtp_expn != NULL) Ustrcat(buffer, " EXPN");
-       if (acl_smtp_vrfy != NULL) Ustrcat(buffer, " VRFY");
+       Ustrcat(buffer, US" HELO EHLO MAIL RCPT DATA BDAT");
+       Ustrcat(buffer, US" NOOP QUIT RSET HELP");
+       if (acl_smtp_etrn) Ustrcat(buffer, US" ETRN");
+       if (acl_smtp_expn) Ustrcat(buffer, US" EXPN");
+       if (acl_smtp_vrfy) Ustrcat(buffer, US" VRFY");
        smtp_printf("214%s\r\n", FALSE, buffer);
        }
       break;
@@ -5731,7 +5732,7 @@ while (done <= 0)
 
       oldsignal = signal(SIGCHLD, SIG_IGN);
 
-      if ((pid = fork()) == 0)
+      if ((pid = exim_fork(US"etrn-command")) == 0)
        {
        smtp_input = FALSE;       /* This process is not associated with the */
        (void)fclose(smtp_in);    /* SMTP call any more. */
@@ -5742,7 +5743,8 @@ while (done <= 0)
        /* If not serializing, do the exec right away. Otherwise, fork down
        into another process. */
 
-       if (!smtp_etrn_serialize || (pid = fork()) == 0)
+       if (  !smtp_etrn_serialize
+          || (pid = exim_fork(US"etrn-serialised-command")) == 0)
          {
          DEBUG(D_exec) debug_print_argv(argv);
          exim_nullstd();                   /* Ensure std{in,out,err} exist */
@@ -5771,7 +5773,7 @@ while (done <= 0)
          }
 
        enq_end(etrn_serialize_key);
-       _exit(EXIT_SUCCESS);
+       exim_underbar_exit(EXIT_SUCCESS);
        }
 
       /* Back in the top level SMTP process. Check that we started a subprocess
@@ -5785,10 +5787,10 @@ while (done <= 0)
        if (smtp_etrn_serialize) enq_end(etrn_serialize_key);
        }
       else
-       {
-       if (user_msg == NULL) smtp_printf("250 OK\r\n", FALSE);
-         else smtp_user_msg(US"250", user_msg);
-       }
+       if (!user_msg)
+         smtp_printf("250 OK\r\n", FALSE);
+       else
+         smtp_user_msg(US"250", user_msg);
 
       signal(SIGCHLD, oldsignal);
       break;
@@ -5890,12 +5892,14 @@ if (!sender_host_authenticated)
 
 g = string_append(g, 2, US";\n\tauth=pass (", sender_host_auth_pubname);
 
-if (Ustrcmp(sender_host_auth_pubname, "tls") != 0)
-  g = string_append(g, 2, US") smtp.auth=", authenticated_id);
-else if (authenticated_id)
-  g = string_append(g, 2, US") x509.auth=", authenticated_id);
+if (Ustrcmp(sender_host_auth_pubname, "tls") == 0)
+  g = authenticated_id
+    ? string_append(g, 2, US") x509.auth=", authenticated_id)
+    : string_cat(g, US") reason=x509.auth");
 else
-  g = string_catn(g, US") reason=x509.auth", 17);
+  g = authenticated_id
+    ? string_append(g, 2, US") smtp.auth=", authenticated_id)
+    : string_cat(g, US", no id saved)");
 
 if (authenticated_sender)
   g = string_append(g, 2, US" smtp.mailfrom=", authenticated_sender);