DMARC: promote the support from Experimental to mainline
[users/jgh/exim.git] / src / src / smtp_in.c
index a579b8fafcc507706ab0b9345100a2e2642cd5d1..67179864191c82f8e3a0d5fa3e3d94d1177adf3a 100644 (file)
@@ -133,11 +133,8 @@ 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;
-# ifdef EXPERIMENTAL_REQUIRETLS
-  BOOL requiretls_advertised           :1;
-# endif
 #endif
   BOOL dsn_advertised                  :1;
   BOOL esmtp                           :1;
@@ -145,7 +142,7 @@ static struct {
   BOOL helo_verify                     :1;
   BOOL helo_seen                       :1;
   BOOL helo_accept_junk                        :1;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifdef SUPPORT_PIPE_CONNECT
   BOOL pipe_connect_acceptable         :1;
 #endif
   BOOL rcpt_smtp_response_same         :1;
@@ -197,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
@@ -267,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 {
@@ -289,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 },
@@ -358,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
@@ -407,7 +397,7 @@ return TRUE;
 }
 
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifdef SUPPORT_PIPE_CONNECT
 static BOOL
 pipeline_connect_sends(void)
 {
@@ -435,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++)
+  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;
   }
@@ -538,8 +527,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;
@@ -909,24 +898,28 @@ 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. */
+/*XXX consider passing caller-info in, for string_vformat-onward */
 
 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);
+/* 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(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);
   }
 
 if (!yield)
@@ -954,16 +947,16 @@ if (fl.rcpt_in_progress)
 
 /* 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;
 }
 
 
@@ -1579,7 +1572,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;
@@ -1624,7 +1616,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 */
@@ -1775,7 +1767,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);
 
@@ -1784,7 +1776,7 @@ return string_sprintf("SMTP connection from %s", hostname);
 
 
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 /* Append TLS-related information to a log line
 
 Arguments:
@@ -1796,7 +1788,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)
@@ -1822,7 +1820,6 @@ Returns:     nothing
 void
 smtp_log_no_mail(void)
 {
-int i;
 uschar * sep, * s;
 gstring * g = NULL;
 
@@ -1835,20 +1832,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",";
@@ -1867,15 +1864,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);
@@ -1912,11 +1908,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. */
 
@@ -1955,7 +1947,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;
 }
 
@@ -2027,7 +2019,7 @@ Argument:   the stacking pool storage reset point
 Returns:    nothing
 */
 
-void
+void *
 smtp_reset(void *reset_point)
 {
 recipients_list = NULL;
@@ -2082,9 +2074,8 @@ 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
+#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;
@@ -2138,6 +2129,7 @@ while (acl_warn_logged)
   store_free(this);
   }
 store_reset(reset_point);
+return store_mark();
 }
 
 
@@ -2165,7 +2157,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. */
@@ -2175,7 +2167,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. */
@@ -2200,7 +2192,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;
 
@@ -2224,7 +2216,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 */
 
@@ -2382,7 +2374,7 @@ return done - 2;  /* Convert yield values */
 
 
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 static BOOL
 smtp_log_tls_fail(uschar * errstr)
 {
@@ -2408,12 +2400,15 @@ 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 */
   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 if (tinfo.tcpi_state == TCP_SYN_RECV)
+  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;
@@ -2470,15 +2465,12 @@ 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;
 fl.tls_advertised = FALSE;
-# ifdef EXPERIMENTAL_REQUIRETLS
-fl.requiretls_advertised = FALSE;
-# endif
 #endif
 fl.dsn_advertised = FALSE;
 #ifdef SUPPORT_I18N
@@ -2489,11 +2481,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;
@@ -2604,7 +2594,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;
@@ -2645,7 +2635,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;
 
@@ -2662,9 +2652,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:
@@ -2712,18 +2700,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);
@@ -2914,7 +2900,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)
@@ -3006,7 +2992,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
+#ifdef SUPPORT_PIPE_CONNECT
 fl.pipe_connect_acceptable =
   sender_host_address && verify_check_host(&pipe_connect_advertise_hosts) == OK;
 
@@ -3033,7 +3019,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
+#ifdef SUPPORT_PIPE_CONNECT
   fl.pipe_connect_acceptable && pipeline_connect_sends(),
 #else
   FALSE,
@@ -3415,7 +3401,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"";
@@ -3480,7 +3466,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)
   {
@@ -3505,9 +3491,7 @@ if (acl_smtp_notquit && reason)
 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)
   {
@@ -3515,13 +3499,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, SVFMT_EXTEND|SVFMT_REBUFFER, CS defaultrespond, ap);
     va_end(ap);
+    smtp_printf("%s %s\r\n", FALSE, code, string_from_gstring(g));
     }
   mac_smtp_fflush();
   }
@@ -3626,33 +3610,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;
           }
     }
@@ -3695,7 +3671,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
@@ -3709,14 +3685,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
@@ -3740,7 +3716,7 @@ switch(rc)
   case OK:
   if (!au->set_id || set_id)    /* Complete success */
     {
-    if (set_id) authenticated_id = string_copy_malloc(set_id);
+    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;
@@ -3761,7 +3737,7 @@ switch(rc)
   /* Fall through */
 
   case DEFER:
-  if (set_id) authenticated_fail_id = string_copy_malloc(set_id);
+  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",
@@ -3781,13 +3757,13 @@ switch(rc)
   break;
 
   case FAIL:
-  if (set_id) authenticated_fail_id = string_copy_malloc(set_id);
+  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);
+  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);
@@ -3836,12 +3812,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
 
@@ -3894,7 +3875,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");
 
@@ -3904,7 +3885,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;
@@ -3912,7 +3893,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
 
@@ -3946,7 +3927,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;
@@ -3961,7 +3941,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
@@ -3990,7 +3970,7 @@ while (done <= 0)
 #endif
 
   switch(smtp_read_command(
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifdef SUPPORT_PIPE_CONNECT
          !fl.pipe_connect_acceptable,
 #else
          TRUE,
@@ -4073,23 +4053,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 */
 
@@ -4166,9 +4149,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
@@ -4209,7 +4191,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
@@ -4220,15 +4202,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
+#ifdef SUPPORT_PIPE_CONNECT
        else if (!fl.pipe_connect_acceptable && !check_sync())
 #else
        else if (!check_sync())
@@ -4243,11 +4221,8 @@ while (done <= 0)
 
       fl.auth_advertised = FALSE;
       f.smtp_in_pipelining_advertised = FALSE;
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
       fl.tls_advertised = FALSE;
-# ifdef EXPERIMENTAL_REQUIRETLS
-      fl.requiretls_advertised = FALSE;
-# endif
 #endif
       fl.dsn_advertised = FALSE;
 #ifdef SUPPORT_I18N
@@ -4257,20 +4232,17 @@ while (done <= 0)
       smtp_code = US"250 ";        /* Default response code plus space*/
       if (!user_msg)
        {
-       s = string_sprintf("%.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"",
          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
@@ -4308,11 +4280,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);
@@ -4370,7 +4339,7 @@ while (done <= 0)
          sync_cmd_limit = NON_SYNC_CMD_PIPELINING;
          f.smtp_in_pipelining_advertised = TRUE;
 
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifdef SUPPORT_PIPE_CONNECT
          if (fl.pipe_connect_acceptable)
            {
            f.smtp_in_early_pipe_advertised = TRUE;
@@ -4398,9 +4367,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)
@@ -4448,7 +4416,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)
          {
@@ -4456,17 +4424,6 @@ while (done <= 0)
          g = string_catn(g, US"-STARTTLS\r\n", 11);
          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);
-         fl.requiretls_advertised = TRUE;
-         }
-# endif
 #endif
 
 #ifndef DISABLE_PRDR
@@ -4497,10 +4454,10 @@ 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
+# ifdef SUPPORT_PIPE_CONNECT
                        fl.pipe_connect_acceptable && pipeline_connect_sends());
 # else
                        FALSE);
@@ -4531,7 +4488,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 */
 
@@ -4586,7 +4543,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;
 
@@ -4791,28 +4748,6 @@ while (done <= 0)
            break;
 #endif
 
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
-         case ENV_MAIL_OPT_REQTLS:
-           {
-           uschar * r, * t;
-
-           if (!fl.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.
@@ -4830,17 +4765,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. */
 
@@ -5311,7 +5235,7 @@ while (done <= 0)
       f.dot_ends = TRUE;
 
     DATA_BDAT:         /* Common code for DATA and BDAT */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifdef SUPPORT_PIPE_CONNECT
       fl.pipe_connect_acceptable = FALSE;
 #endif
       if (!discarded && recipients_count <= 0)
@@ -5466,7 +5390,7 @@ while (done <= 0)
       break;
 
 
-    #ifdef SUPPORT_TLS
+    #ifndef DISABLE_TLS
 
     case STARTTLS_CMD:
       HAD(SCH_STARTTLS);
@@ -5495,7 +5419,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;
 
@@ -5543,7 +5467,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",
@@ -5634,7 +5557,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;
 
@@ -5656,17 +5579,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;
@@ -5842,7 +5765,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
@@ -5856,10 +5779,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;