deliver
[exim.git] / src / src / deliver.c
index b89671c572188d031d7a2a2101aab39564655956..a6f68825c9238569ab3deac3be91f9def0b72d4b 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. */
 
 /* The main code for delivering a message. */
@@ -220,7 +221,9 @@ if (!addr->next)
 
   deliver_localpart = addr->local_part;
   deliver_localpart_prefix = addr->prefix;
+  deliver_localpart_prefix_v = addr->prefix_v;
   deliver_localpart_suffix = addr->suffix;
+  deliver_localpart_suffix_v = addr->suffix_v;
 
   for (addr_orig = addr; addr_orig->parent; addr_orig = addr_orig->parent) ;
   deliver_domain_orig = addr_orig->domain;
@@ -260,7 +263,9 @@ if (!addr->next)
       else if (deliver_localpart[0] == '|') address_pipe = addr->local_part;
       deliver_localpart = addr->parent->local_part;
       deliver_localpart_prefix = addr->parent->prefix;
+      deliver_localpart_prefix_v = addr->parent->prefix_v;
       deliver_localpart_suffix = addr->parent->suffix;
+      deliver_localpart_suffix_v = addr->parent->suffix_v;
       }
     }
 
@@ -329,7 +334,7 @@ static int
 open_msglog_file(uschar *filename, int mode, uschar **error)
 {
 if (Ustrstr(filename, US"/../"))
-  log_write(0, LOG_MAIN|LOG_PANIC,
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE,
     "Attempt to open msglog file path with upward-traversal: '%s'\n", filename);
 
 for (int i = 2; i > 0; i--)
@@ -429,7 +434,7 @@ for (address_item * addr2 = addr->next; addr2; addr2 = addr2->next)
   addr2->transport_return = addr->transport_return;
   addr2->basic_errno =     addr->basic_errno;
   addr2->more_errno =      addr->more_errno;
-  addr2->delivery_usec =    addr->delivery_usec;
+  addr2->delivery_time =    addr->delivery_time;
   addr2->special_action =   addr->special_action;
   addr2->message =         addr->message;
   addr2->user_message =            addr->user_message;
@@ -455,6 +460,9 @@ TRUE if the lists refer to the same hosts in the same order, except that
 This enables Exim to use a single SMTP transaction for sending to two entirely
 different domains that happen to end up pointing at the same hosts.
 
+We do not try to batch up different A-record host names that refer to the
+same IP.
+
 Arguments:
   one       points to the first host list
   two       points to the second host list
@@ -513,8 +521,12 @@ while (one && two)
   else if (one->port != two->port)
     return FALSE;
 
-  /* Hosts matched */
+#ifdef SUPPORT_DANE
+  /* DNSSEC equality */
+  if (one->dnssec != two->dnssec) return FALSE;
+#endif
 
+  /* Hosts matched */
   one = one->next;
   two = two->next;
   }
@@ -784,6 +796,9 @@ g = string_append(g, 3, US" [", h->address, US"]");
 if (LOGGING(outgoing_port))
   g = string_fmt_append(g, ":%d", h->port);
 
+if (continue_sequence > 1)             /*XXX this is wrong for a dropped proxyconn.  Would have to pass back from transport */
+  g = string_catn(g, US"*", 1);
+
 #ifdef SUPPORT_SOCKS
 if (LOGGING(proxy) && proxy_local_address)
   {
@@ -812,7 +827,7 @@ d_tlslog(gstring * g, address_item * addr)
 if (LOGGING(tls_cipher) && addr->cipher)
   {
   g = string_append(g, 2, US" X=", addr->cipher);
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifndef DISABLE_TLS_RESUME
   if (LOGGING(tls_resumption) && testflag(addr, af_tls_resume))
     g = string_catn(g, US"*", 1);
 #endif
@@ -1144,7 +1159,7 @@ if (LOGGING(sender_on_delivery) || msg)
 if (*queue_name)
   g = string_append(g, 2, US" Q=", queue_name);
 
-#ifdef EXPERIMENTAL_SRS
+#ifdef EXPERIMENTAL_SRS_ALT
 if(addr->prop.srs_sender)
   g = string_append(g, 3, US" SRS=<", addr->prop.srs_sender, US">");
 #endif
@@ -1187,8 +1202,6 @@ else
   if (addr->host_used)
     {
     g = d_hostlog(g, addr);
-    if (continue_sequence > 1)
-      g = string_catn(g, US"*", 1);
 
 #ifndef DISABLE_EVENT
     deliver_host_address = addr->host_used->address;
@@ -1260,14 +1273,11 @@ if (  LOGGING(smtp_confirmation)
 /* Time on queue and actual time taken to deliver */
 
 if (LOGGING(queue_time))
-  g = string_append(g, 2, US" QT=",
-    string_timesince(&received_time));
+  g = string_append(g, 2, US" QT=", string_timesince(
+    LOGGING(queue_time_exclusive) ? &received_time_complete : &received_time));
 
 if (LOGGING(deliver_time))
-  {
-  struct timeval diff = {.tv_sec = addr->more_errno, .tv_usec = addr->delivery_usec};
-  g = string_append(g, 2, US" DT=", string_timediff(&diff));
-  }
+  g = string_append(g, 2, US" DT=", string_timediff(&addr->delivery_time));
 
 /* string_cat() always leaves room for the terminator. Release the
 store we used to build the line after writing it. */
@@ -1320,20 +1330,13 @@ else if (driver_kind)
 g = string_fmt_append(g, " defer (%d)", addr->basic_errno);
 
 if (addr->basic_errno > 0)
-  g = string_append(g, 2, US": ",
-    US strerror(addr->basic_errno));
+  g = string_append(g, 2, US": ", US strerror(addr->basic_errno));
 
 if (addr->host_used)
-  {
-  g = string_append(g, 5,
-                   US" H=", addr->host_used->name,
-                   US" [",  addr->host_used->address, US"]");
-  if (LOGGING(outgoing_port))
-    {
-    int port = addr->host_used->port;
-    g = string_fmt_append(g, ":%d", port == PORT_NONE ? 25 : port);
-    }
-  }
+  g = d_hostlog(g, addr);
+
+if (LOGGING(deliver_time))
+  g = string_append(g, 2, US" DT=", string_timediff(&addr->delivery_time));
 
 if (addr->message)
   g = string_append(g, 2, US": ", addr->message);
@@ -1414,6 +1417,9 @@ if (addr->basic_errno > 0)
 if (addr->message)
   g = string_append(g, 2, US": ", addr->message);
 
+if (LOGGING(deliver_time))
+  g = string_append(g, 2, US" DT=", string_timediff(&addr->delivery_time));
+
 (void) string_from_gstring(g);
 
 /* Do the logging. For the message log, "routing failed" for those cases,
@@ -1578,6 +1584,12 @@ if (addr->return_file >= 0 && addr->return_filename)
   (void)close(addr->return_file);
   }
 
+/* Check if the transport notifed continue-conn status explicitly, and
+update our knowlege. */
+
+if (testflag(addr, af_new_conn))       continue_sequence = 1;
+else if (testflag(addr, af_cont_conn)) continue_sequence++;
+
 /* The success case happens only after delivery by a transport. */
 
 if (result == OK)
@@ -1610,6 +1622,7 @@ if (result == OK)
   tls_out.peercert = addr->peercert;
   addr->peercert = NULL;
 
+  tls_out.ver = addr->tlsver;
   tls_out.cipher = addr->cipher;
   tls_out.peerdn = addr->peerdn;
   tls_out.ocsp = addr->ocsp;
@@ -1623,6 +1636,7 @@ if (result == OK)
 #ifndef DISABLE_TLS
   tls_free_cert(&tls_out.ourcert);
   tls_free_cert(&tls_out.peercert);
+  tls_out.ver = NULL;
   tls_out.cipher = NULL;
   tls_out.peerdn = NULL;
   tls_out.ocsp = OCSP_NOT_REQ;
@@ -2083,9 +2097,9 @@ return FALSE;
 
 /* Each local delivery is performed in a separate process which sets its
 uid and gid as specified. This is a safer way than simply changing and
-restoring using seteuid(); there is a body of opinion that seteuid() cannot be
-used safely. From release 4, Exim no longer makes any use of it. Besides, not
-all systems have seteuid().
+restoring using seteuid(); there is a body of opinion that seteuid()
+cannot be used safely. From release 4, Exim no longer makes any use of
+it for delivery. Besides, not all systems have seteuid().
 
 If the uid/gid are specified in the transport_instance, they are used; the
 transport initialization must ensure that either both or neither are set.
@@ -2115,7 +2129,7 @@ Arguments:
 Returns:     nothing
 */
 
-static void
+void
 deliver_local(address_item *addr, BOOL shadowing)
 {
 BOOL use_initgroups;
@@ -2133,7 +2147,7 @@ has its own return path setting, expand it and replace the existing value. */
 
 if(addr->prop.errors_address)
   return_path = addr->prop.errors_address;
-#ifdef EXPERIMENTAL_SRS
+#ifdef EXPERIMENTAL_SRS_ALT
 else if (addr->prop.srs_sender)
   return_path = addr->prop.srs_sender;
 #endif
@@ -2142,18 +2156,16 @@ else
 
 if (tp->return_path)
   {
-  uschar *new_return_path = expand_string(tp->return_path);
-  if (!new_return_path)
-    {
-    if (!f.expand_string_forcedfail)
-      {
-      common_error(TRUE, addr, ERRNO_EXPANDFAIL,
-        US"Failed to expand return path \"%s\" in %s transport: %s",
-        tp->return_path, tp->name, expand_string_message);
-      return;
-      }
+  uschar * new_return_path = expand_string(tp->return_path);
+  if (new_return_path)
+    return_path = new_return_path;
+  else if (!f.expand_string_forcedfail)
+    {
+    common_error(TRUE, addr, ERRNO_EXPANDFAIL,
+      US"Failed to expand return path \"%s\" in %s transport: %s",
+      tp->return_path, tp->name, expand_string_message);
+    return;
     }
-  else return_path = new_return_path;
   }
 
 /* For local deliveries, one at a time, the value used for logging can just be
@@ -2259,7 +2271,7 @@ a clean slate and doesn't interfere with the parent process. */
 
 search_tidyup();
 
-if ((pid = fork()) == 0)
+if ((pid = exim_fork(US"delivery-local")) == 0)
   {
   BOOL replicate = TRUE;
 
@@ -2405,7 +2417,7 @@ if ((pid = fork()) == 0)
       || (ret = write(pfd[pipe_write], &addr2->flags, sizeof(addr2->flags))) != sizeof(addr2->flags)
       || (ret = write(pfd[pipe_write], &addr2->basic_errno,    sizeof(int))) != sizeof(int)
       || (ret = write(pfd[pipe_write], &addr2->more_errno,     sizeof(int))) != sizeof(int)
-      || (ret = write(pfd[pipe_write], &addr2->delivery_usec,  sizeof(int))) != sizeof(int)
+      || (ret = write(pfd[pipe_write], &addr2->delivery_time,  sizeof(struct timeval))) != sizeof(struct timeval)
       || (ret = write(pfd[pipe_write], &addr2->special_action, sizeof(int))) != sizeof(int)
       || (ret = write(pfd[pipe_write], &addr2->transport,
         sizeof(transport_instance *))) != sizeof(transport_instance *)
@@ -2473,7 +2485,7 @@ for (addr2 = addr; addr2; addr2 = addr2->next)
     len = read(pfd[pipe_read], &addr2->flags, sizeof(addr2->flags));
     len = read(pfd[pipe_read], &addr2->basic_errno,    sizeof(int));
     len = read(pfd[pipe_read], &addr2->more_errno,     sizeof(int));
-    len = read(pfd[pipe_read], &addr2->delivery_usec,  sizeof(int));
+    len = read(pfd[pipe_read], &addr2->delivery_time,  sizeof(struct timeval));
     len = read(pfd[pipe_read], &addr2->special_action, sizeof(int));
     len = read(pfd[pipe_read], &addr2->transport,
       sizeof(transport_instance *));
@@ -2606,7 +2618,7 @@ if (addr->special_action == SPECIAL_WARN && addr->transport->warn_message)
       "message for %s transport): %s", addr->transport->warn_message,
       addr->transport->name, expand_string_message);
 
-  else if ((pid = child_open_exim(&fd)) > 0)
+  else if ((pid = child_open_exim(&fd, US"tpt-warning-message")) > 0)
     {
     FILE *f = fdopen(fd, "wb");
     if (errors_reply_to && !contains_header(US"Reply-To", warn_message))
@@ -2876,10 +2888,8 @@ while (addr_local)
   deliveries (e.g. to pipes) can take a substantial time. */
 
   if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE, TRUE)))
-    {
     DEBUG(D_deliver|D_retry|D_hints_lookup)
       debug_printf("no retry data available\n");
-    }
 
   addr2 = addr;
   addr3 = NULL;
@@ -3127,11 +3137,7 @@ while (addr_local)
 
     /* Done with this address */
 
-    if (result == OK)
-      {
-      addr2->more_errno = deliver_time.tv_sec;
-      addr2->delivery_usec = deliver_time.tv_usec;
-      }
+    addr2->delivery_time = deliver_time;
     post_process_one(addr2, result, logflags, EXIM_DTYPE_TRANSPORT, logchar);
 
     /* If a pipe delivery generated text to be sent back, the result may be
@@ -3187,6 +3193,7 @@ const uschar *listptr = remote_sort_domains;
 uschar *pattern;
 uschar patbuf[256];
 
+/*XXX The list is used before expansion. Not sure how that ties up with the docs */
 while (  *aptr
       && (pattern = string_nextinlist(&listptr, &sep, patbuf, sizeof(patbuf)))
       )
@@ -3480,11 +3487,13 @@ while (!done)
       switch (*subid)
        {
        case '1':
-         addr->cipher = NULL;
-         addr->peerdn = NULL;
+         addr->tlsver = addr->cipher = addr->peerdn = NULL;
 
          if (*ptr)
+           {
            addr->cipher = string_copy(ptr);
+           addr->tlsver = string_copyn(ptr, Ustrchr(ptr, ':') - ptr);
+           }
          while (*ptr++);
          if (*ptr)
            addr->peerdn = string_copy(ptr);
@@ -3570,7 +3579,13 @@ while (!done)
 
       switch (*subid)
        {
-  #ifdef SUPPORT_SOCKS
+       case 3:         /* explicit notification of continued-connection (non)use;
+                       overrides caller's knowlege. */
+         if (*ptr & BIT(1))      setflag(addr, af_new_conn);
+         else if (*ptr & BIT(2)) setflag(addr, af_cont_conn);
+         break;
+
+#ifdef SUPPORT_SOCKS
        case '2':       /* proxy information; must arrive before A0 and applies to that addr XXX oops*/
          proxy_session = TRUE; /*XXX should this be cleared somewhere? */
          if (*ptr == 0)
@@ -3583,9 +3598,9 @@ while (!done)
            ptr += sizeof(proxy_local_port);
            }
          break;
-  #endif
+#endif
 
-  #ifdef EXPERIMENTAL_DSN_INFO
+#ifdef EXPERIMENTAL_DSN_INFO
        case '1':       /* must arrive before A0, and applies to that addr */
                        /* Two strings: smtp_greeting and helo_response */
          addr->smtp_greeting = string_copy(ptr);
@@ -3593,7 +3608,7 @@ while (!done)
          addr->helo_response = string_copy(ptr);
          while(*ptr++);
          break;
-  #endif
+#endif
 
        case '0':
          DEBUG(D_deliver) debug_printf("A0 %s tret %d\n", addr->address, *ptr);
@@ -3603,8 +3618,8 @@ while (!done)
          ptr += sizeof(addr->basic_errno);
          memcpy(&addr->more_errno, ptr, sizeof(addr->more_errno));
          ptr += sizeof(addr->more_errno);
-         memcpy(&addr->delivery_usec, ptr, sizeof(addr->delivery_usec));
-         ptr += sizeof(addr->delivery_usec);
+         memcpy(&addr->delivery_time, ptr, sizeof(addr->delivery_time));
+         ptr += sizeof(addr->delivery_time);
          memcpy(&addr->flags, ptr, sizeof(addr->flags));
          ptr += sizeof(addr->flags);
          addr->message = *ptr ? string_copy(ptr) : NULL;
@@ -4264,6 +4279,10 @@ for (int delivery_count = 0; addr_remote; delivery_count++)
       }
     }
 
+/*XXX need to defeat this when DANE is used - but we don't know that yet.
+So look out for the place it gets used.
+*/
+
   /* Get the flag which specifies whether the transport can handle different
   domains that nevertheless resolve to the same set of hosts. If it needs
   expanding, get variables set: $address_data, $domain_data, $localpart_data,
@@ -4342,6 +4361,11 @@ for (int delivery_count = 0; addr_remote; delivery_count++)
   /************************************************************************/
 
 
+/*XXX don't know yet if DANE will be used.  So tpt will have to
+check at the point if gets next addr from list, and skip/defer any
+nonmatch domains
+*/
+
   /* Pick off all addresses which have the same transport, errors address,
   destination, and extra headers. In some cases they point to the same host
   list, but we also need to check for identical host lists generated from
@@ -4421,7 +4445,7 @@ for (int delivery_count = 0; addr_remote; delivery_count++)
 
   if(addr->prop.errors_address)
     return_path = addr->prop.errors_address;
-#ifdef EXPERIMENTAL_SRS
+#ifdef EXPERIMENTAL_SRS_ALT
   else if(addr->prop.srs_sender)
     return_path = addr->prop.srs_sender;
 #endif
@@ -4488,6 +4512,7 @@ for (int delivery_count = 0; addr_remote; delivery_count++)
   if (continue_transport)
     {
     BOOL ok = Ustrcmp(continue_transport, tp->name) == 0;
+/*XXX do we need to check for a DANEd conn vs. a change of domain? */
 
     /* If the transport is about to override the host list do not check
     it here but take the cost of running the transport process to discover
@@ -4639,7 +4664,7 @@ all pipes, so I do not see a reason to use non-blocking IO here
 
   search_tidyup();
 
-  if ((pid = fork()) == 0)
+  if ((pid = exim_fork(US"transport")) == 0)
     {
     int fd = pfd[pipe_write];
     host_item *h;
@@ -4653,10 +4678,7 @@ all pipes, so I do not see a reason to use non-blocking IO here
     /* Show pids on debug output if parallelism possible */
 
     if (parmax > 1 && (parcount > 0 || addr_remote))
-      {
       DEBUG(D_any|D_v) debug_selector |= D_pid;
-      DEBUG(D_deliver) debug_printf("Remote delivery process started\n");
-      }
 
     /* Reset the random number generator, so different processes don't all
     have the same sequence. In the test harness we want different, but
@@ -4768,7 +4790,7 @@ all pipes, so I do not see a reason to use non-blocking IO here
 #ifdef SUPPORT_DANE
       if (tls_out.dane_verified)        setflag(addr, af_dane_verified);
 #endif
-# ifdef EXPERIMENTAL_TLS_RESUME
+# ifndef DISABLE_TLS_RESUME
       if (tls_out.resumption & RESUME_USED) setflag(addr, af_tls_resume);
 # endif
 
@@ -4877,6 +4899,14 @@ all pipes, so I do not see a reason to use non-blocking IO here
         rmt_dlv_checked_write(fd, 'R', '0', big_buffer, ptr - big_buffer);
         }
 
+      if (testflag(addr, af_new_conn) || testflag(addr, af_cont_conn))
+       {
+       DEBUG(D_deliver) debug_printf("%scontinued-connection\n",
+         testflag(addr, af_new_conn) ? "non-" : "");
+       big_buffer[0] = testflag(addr, af_new_conn) ? BIT(1) : BIT(2);
+        rmt_dlv_checked_write(fd, 'A', '3', big_buffer, 1);
+       }
+
 #ifdef SUPPORT_SOCKS
       if (LOGGING(proxy) && proxy_session)
        {
@@ -4920,8 +4950,8 @@ all pipes, so I do not see a reason to use non-blocking IO here
       ptr += sizeof(addr->basic_errno);
       memcpy(ptr, &addr->more_errno, sizeof(addr->more_errno));
       ptr += sizeof(addr->more_errno);
-      memcpy(ptr, &addr->delivery_usec, sizeof(addr->delivery_usec));
-      ptr += sizeof(addr->delivery_usec);
+      memcpy(ptr, &addr->delivery_time, sizeof(addr->delivery_time));
+      ptr += sizeof(addr->delivery_time);
       memcpy(ptr, &addr->flags, sizeof(addr->flags));
       ptr += sizeof(addr->flags);
 
@@ -5024,9 +5054,10 @@ all pipes, so I do not see a reason to use non-blocking IO here
 
   /* Otherwise, if we are running in the test harness, wait a bit, to let the
   newly created process get going before we create another process. This should
-  ensure repeatability in the tests. We only need to wait a tad. */
+  ensure repeatability in the tests. Wait long enough for most cases to complete
+  the transport. */
 
-  else testharness_pause_ms(500);
+  else testharness_pause_ms(600);
 
   continue;
 
@@ -5374,7 +5405,8 @@ Returns:       nothing
 static void
 print_dsn_diagnostic_code(const address_item *addr, FILE *f)
 {
-uschar *s = testflag(addr, af_pass_message) ? addr->message : NULL;
+uschar * s = testflag(addr, af_pass_message) ? addr->message : NULL;
+unsigned cnt;
 
 /* af_pass_message and addr->message set ? print remote host answer */
 if (s)
@@ -5386,19 +5418,32 @@ if (s)
   if (!(s = Ustrstr(addr->message, ": ")))
     return;                            /* not found, bail out */
   s += 2;  /* skip ": " */
-  fprintf(f, "Diagnostic-Code: smtp; ");
+  cnt = fprintf(f, "Diagnostic-Code: smtp; ");
   }
 /* no message available. do nothing */
 else return;
 
 while (*s)
+  {
+  if (cnt > 950)       /* RFC line length limit: 998 */
+    {
+    DEBUG(D_deliver) debug_printf("print_dsn_diagnostic_code() truncated line\n");
+    fputs("[truncated]", f);
+    break;
+    }
+
   if (*s == '\\' && s[1] == 'n')
     {
     fputs("\n ", f);    /* as defined in RFC 3461 */
     s += 2;
+    cnt += 2;
     }
   else
+    {
     fputc(*s++, f);
+    cnt++;
+    }
+  }
 
 fputc('\n', f);
 }
@@ -5473,6 +5518,49 @@ fprintf(f, "Action: %s\n"
 }
 
 
+
+/* When running in the test harness, there's an option that allows us to
+fudge this time so as to get repeatability of the tests. Take the first
+time off the list. In queue runs, the list pointer gets updated in the
+calling process. */
+
+int
+test_harness_fudged_queue_time(int actual_time)
+{
+int qt;
+if (  f.running_in_test_harness && *fudged_queue_times
+   && (qt = readconf_readtime(fudged_queue_times, '/', FALSE)) >= 0)
+  {
+  DEBUG(D_deliver) debug_printf("fudged queue_times = %s\n",
+    fudged_queue_times);
+  return qt;
+  }
+return actual_time;
+}
+
+/************************************************/
+
+static FILE *
+expand_open(const uschar * filename,
+  const uschar * varname, const uschar * reason)
+{
+const uschar * s = expand_cstring(filename);
+FILE * fp = NULL;
+
+if (!s || !*s)
+  log_write(0, LOG_MAIN|LOG_PANIC,
+    "Failed to expand %s: '%s'\n", varname, filename);
+else if (*s != '/')
+  log_write(0, LOG_MAIN|LOG_PANIC, "%s is not absolute after expansion: '%s'\n",
+    varname, s);
+else if (is_tainted2(s, LOG_MAIN|LOG_PANIC, "Tainted %s after expansion: '%s'\n", varname, s))
+  ;
+else if (!(fp = Ufopen(s, "rb")))
+  log_write(0, LOG_MAIN|LOG_PANIC, "Failed to open %s for %s "
+    "message texts: %s", s, reason, strerror(errno));
+return fp;
+}
+
 /*************************************************
 *              Deliver one message               *
 *************************************************/
@@ -6073,12 +6161,13 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT)
           {
           uschar *tmp = expand_string(tpname);
           address_file = address_pipe = NULL;
+          uschar *m;
           if (!tmp)
             p->message = string_sprintf("failed to expand \"%s\" as a "
               "system filter transport name", tpname);
-         if (is_tainted(tmp))
-            p->message = string_sprintf("attempt to used tainted value '%s' for"
-             "transport '%s' as a system filter", tmp, tpname);
+         if (is_tainted2(tmp, 0, m = string_sprintf("Tainted values '%s' "
+              "for transport '%s' as a system filter", tmp, tpname)))
+            p->message = m;
           tpname = tmp;
           }
         else
@@ -6163,7 +6252,8 @@ if (process_recipients != RECIP_IGNORE)
         new->onetime_parent = recipients_list[r->pno].address;
 
       /* If DSN support is enabled, set the dsn flags and the original receipt
-         to be passed on to other DSN enabled MTAs */
+      to be passed on to other DSN enabled MTAs */
+
       new->dsn_flags = r->dsn_flags & rf_dsnflags;
       new->dsn_orcpt = r->orcpt;
       DEBUG(D_deliver) debug_printf("DSN: set orcpt: %s  flags: 0x%x\n",
@@ -6748,7 +6838,7 @@ while (addr_new)           /* Loop until all addresses dealt with */
       addr_route = addr->next;
 
       deliver_domain = addr->domain;  /* set $domain */
-      if ((rc = match_isinlist(addr->domain, (const uschar **)&queue_domains, 0,
+      if ((rc = match_isinlist(addr->domain, CUSS &queue_domains, 0,
             &domainlist_anchor, addr->domain_cache, MCL_DOMAIN, TRUE, NULL))
               != OK)
         if (rc == DEFER)
@@ -7003,13 +7093,23 @@ if (  mua_wrapper
 
 
 /* If this is a run to continue deliveries to an external channel that is
-already set up, defer any local deliveries. */
+already set up, defer any local deliveries.
 
-if (continue_transport)
+jgh 2020/12/20: I don't see why; locals should be quick.
+The defer goes back to version 1.62 in 1997.  A local being still deliverable
+during a continued run might result from something like a defer during the
+original delivery, eg. in a DB lookup.  Unlikely but possible.
+
+To avoid delaying a local when combined with a callout-hold for a remote
+delivery, test continue_sequence rather than continue_transport. */
+
+if (continue_sequence > 1 && addr_local)
   {
+  DEBUG(D_deliver|D_retry|D_route)
+    debug_printf("deferring local deliveries due to continued-transport\n");
   if (addr_defer)
     {
-    address_item *addr = addr_defer;
+    address_item * addr = addr_defer;
     while (addr->next) addr = addr->next;
     addr->next = addr_local;
     }
@@ -7283,10 +7383,9 @@ for (address_item * a = addr_succeed; a; a = a->next)
       );
 
   /* send report if next hop not DSN aware or a router flagged "last DSN hop"
-     and a report was requested */
-  if (  (  a->dsn_aware != dsn_support_yes
-       || a->dsn_flags & rf_dsnlasthop
-        )
+  and a report was requested */
+
+  if (  (a->dsn_aware != dsn_support_yes || a->dsn_flags & rf_dsnlasthop)
      && a->dsn_flags & rf_notify_success
      )
     {
@@ -7306,7 +7405,7 @@ if (addr_senddsn)
   int fd;
 
   /* create exim process to send message */
-  pid = child_open_exim(&fd);
+  pid = child_open_exim(&fd, US"DSN");
 
   DEBUG(D_deliver) debug_printf("DSN: child_open_exim returns: %d\n", pid);
 
@@ -7507,7 +7606,7 @@ while (addr_failed)
 
     /* Make a subprocess to send a message */
 
-    if ((pid = child_open_exim(&fd)) < 0)
+    if ((pid = child_open_exim(&fd, US"bounce-message")) < 0)
       log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %d (parent %d) failed to "
         "create child process to send failure message: %s", getpid(),
         getppid(), strerror(errno));
@@ -7593,9 +7692,8 @@ while (addr_failed)
       carry on - default texts will be used. */
 
       if (bounce_message_file)
-        if (!(emf = Ufopen(bounce_message_file, "rb")))
-          log_write(0, LOG_MAIN|LOG_PANIC, "Failed to open %s for error "
-            "message texts: %s", bounce_message_file, strerror(errno));
+       emf = expand_open(bounce_message_file,
+               US"bounce_message_file", US"error");
 
       /* Quietly copy to configured additional addresses if required. */
 
@@ -7784,11 +7882,11 @@ wording. */
            fprintf(fp, "Remote-MTA: X-ip; [%s]%s\n", hu->address, p);
            }
          if ((s = addr->smtp_greeting) && *s)
-           fprintf(fp, "X-Remote-MTA-smtp-greeting: X-str; %s\n", s);
+           fprintf(fp, "X-Remote-MTA-smtp-greeting: X-str; %.900s\n", s);
          if ((s = addr->helo_response) && *s)
-           fprintf(fp, "X-Remote-MTA-helo-response: X-str; %s\n", s);
+           fprintf(fp, "X-Remote-MTA-helo-response: X-str; %.900s\n", s);
          if ((s = addr->message) && *s)
-           fprintf(fp, "X-Exim-Diagnostic: X-str; %s\n", s);
+           fprintf(fp, "X-Exim-Diagnostic: X-str; %.900s\n", s);
          }
 #endif
          print_dsn_diagnostic_code(addr, fp);
@@ -7888,10 +7986,6 @@ wording. */
       (void)fclose(fp);
       rc = child_close(pid, 0);     /* Waits for child to close, no timeout */
 
-      /* In the test harness, let the child do it's thing first. */
-
-      testharness_pause_ms(500);
-
       /* If the process failed, there was some disaster in setting up the
       error message. Unless the message is very old, ensure that addr_defer
       is non-null, which will have the effect of leaving the message on the
@@ -8130,21 +8224,7 @@ else if (addr_defer != (address_item *)(+1))
     int show_time;
     int queue_time = time(NULL) - received_time.tv_sec;
 
-    /* When running in the test harness, there's an option that allows us to
-    fudge this time so as to get repeatability of the tests. Take the first
-    time off the list. In queue runs, the list pointer gets updated in the
-    calling process. */
-
-    if (f.running_in_test_harness && fudged_queue_times[0] != 0)
-      {
-      int qt = readconf_readtime(fudged_queue_times, '/', FALSE);
-      if (qt >= 0)
-        {
-        DEBUG(D_deliver) debug_printf("fudged queue_times = %s\n",
-          fudged_queue_times);
-        queue_time = qt;
-        }
-      }
+    queue_time = test_harness_fudged_queue_time(queue_time);
 
     /* See how many warnings we should have sent by now */
 
@@ -8179,20 +8259,19 @@ else if (addr_defer != (address_item *)(+1))
       {
       header_line *h;
       int fd;
-      pid_t pid = child_open_exim(&fd);
+      pid_t pid = child_open_exim(&fd, US"delay-warning-message");
 
       if (pid > 0)
         {
-        uschar *wmf_text;
-        FILE *wmf = NULL;
-        FILE *f = fdopen(fd, "wb");
+        uschar * wmf_text;
+        FILE * wmf = NULL;
+        FILE * f = fdopen(fd, "wb");
        uschar * bound;
        transport_ctx tctx = {{0}};
 
         if (warn_message_file)
-          if (!(wmf = Ufopen(warn_message_file, "rb")))
-            log_write(0, LOG_MAIN|LOG_PANIC, "Failed to open %s for warning "
-              "message texts: %s", warn_message_file, strerror(errno));
+         wmf = expand_open(warn_message_file,
+                 US"warn_message_file", US"warning");
 
         warnmsg_recipients = recipients;
         warnmsg_delay = queue_time < 120*60
@@ -8361,7 +8440,7 @@ else if (addr_defer != (address_item *)(+1))
   /* If this was a first delivery attempt, unset the first time flag, and
   ensure that the spool gets updated. */
 
-  if (f.deliver_firsttime)
+  if (f.deliver_firsttime && !f.queue_2stage)
     {
     f.deliver_firsttime = FALSE;
     update_spool = TRUE;
@@ -8494,52 +8573,9 @@ f.tcp_fastopen_ok = TRUE;
 }
 
 
-uschar *
-deliver_get_sender_address (uschar * id)
-{
-int rc;
-uschar * new_sender_address,
-       * save_sender_address;
-BOOL save_qr = f.queue_running;
-uschar * spoolname;
-
-/* make spool_open_datafile non-noisy on fail */
-
-f.queue_running = TRUE;
-
-/* Side effect: message_subdir is set for the (possibly split) spool directory */
-
-deliver_datafile = spool_open_datafile(id);
-f.queue_running = save_qr;
-if (deliver_datafile < 0)
-  return NULL;
-
-/* Save and restore the global sender_address.  I'm not sure if we should
-not save/restore all the other global variables too, because
-spool_read_header() may change all of them. But OTOH, when this
-deliver_get_sender_address() gets called, the current message is done
-already and nobody needs the globals anymore. (HS12, 2015-08-21) */
-
-spoolname = string_sprintf("%s-H", id);
-save_sender_address = sender_address;
-
-rc = spool_read_header(spoolname, TRUE, TRUE);
-
-new_sender_address = sender_address;
-sender_address = save_sender_address;
-
-if (rc != spool_read_OK)
-  return NULL;
-
-assert(new_sender_address);
-
-(void)close(deliver_datafile);
-deliver_datafile = -1;
-
-return new_sender_address;
-}
-
 
+/* Called from a commandline, or from the daemon, to do a delivery.
+We need to regain privs; do this by exec of the exim binary. */
 
 void
 delivery_re_exec(int exec_type)
@@ -8567,18 +8603,17 @@ if (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only)
       goto fail;
 
     where = US"fork";
-    if ((pid = fork()) < 0)
+    testharness_pause_ms(150);
+    if ((pid = exim_fork(US"tls-proxy-interproc")) < 0)
       goto fail;
 
-    else if (pid == 0)         /* child: fork again to totally disconnect */
+    if (pid == 0)      /* child: will fork again to totally disconnect */
       {
-      testharness_pause_ms(100); /* let parent debug out */
-      /* does not return */
       smtp_proxy_tls(cutthrough.cctx.tls_ctx, big_buffer, big_buffer_size,
                      pfd, 5*60);
+      /* does not return */
       }
 
-    DEBUG(D_transport) debug_printf("proxy-proc inter-pid %d\n", pid);
     close(pfd[0]);
     waitpid(pid, NULL, 0);
     (void) close(channel_fd);  /* release the client socket */