SPDX: license tags (mostly by guesswork)
[exim.git] / src / src / deliver.c
index 67d711b7e014ff3de5c9bdd514e4d6026935a0ed..719fa9d93f52687bd7e8c8e061b2c6dba38a2d44 100644 (file)
@@ -2,9 +2,10 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim Maintainers 2020 - 2022 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-only */
 
 /* The main code for delivering a message. */
 
@@ -74,6 +75,7 @@ static BOOL update_spool;
 static BOOL remove_journal;
 static int  parcount = 0;
 static pardata *parlist = NULL;
+static struct pollfd *parpoll;
 static int  return_count;
 static uschar *frozen_info = US"";
 static uschar *used_return_path = NULL;
@@ -145,7 +147,7 @@ Returns:      a pointer to an initialized address_item
 address_item *
 deliver_make_addr(uschar *address, BOOL copy)
 {
-address_item *addr = store_get(sizeof(address_item), FALSE);
+address_item * addr = store_get(sizeof(address_item), GET_UNTAINTED);
 *addr = address_defaults;
 if (copy) address = string_copy(address);
 addr->address = address;
@@ -334,7 +336,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--)
@@ -460,6 +462,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
@@ -518,8 +523,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;
   }
@@ -789,6 +798,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)
   {
@@ -843,8 +855,18 @@ return g;
 
 
 #ifndef DISABLE_EVENT
+/* Distribute a named event to any listeners.
+
+Args:  action  config option specifying listener
+       event   name of the event
+       ev_data associated data for the event
+       errnop  pointer to errno for modification, or null
+
+Return: string expansion from listener, or NULL
+*/
+
 uschar *
-event_raise(uschar * action, const uschar * event, uschar * ev_data)
+event_raise(uschar * action, const uschar * event, uschar * ev_data, int * errnop)
 {
 uschar * s;
 if (action)
@@ -871,7 +893,8 @@ if (action)
     {
     DEBUG(D_deliver)
       debug_printf("Event(%s): event_action returned \"%s\"\n", event, s);
-    errno = ERRNO_EVENT;
+    if (errnop)
+      *errnop = ERRNO_EVENT;
     return s;
     }
   }
@@ -900,7 +923,7 @@ if (!addr->transport)
      a filter was used which triggered a fail command (in such a case
      a transport isn't needed).  Convert it to an internal fail event. */
 
-    (void) event_raise(event_action, US"msg:fail:internal", addr->message);
+    (void) event_raise(event_action, US"msg:fail:internal", addr->message, NULL);
     }
   }
 else
@@ -912,7 +935,8 @@ else
            || Ustrcmp(addr->transport->driver_name, "smtp") == 0
            || Ustrcmp(addr->transport->driver_name, "lmtp") == 0
            || Ustrcmp(addr->transport->driver_name, "autoreply") == 0
-          ? addr->message : NULL);
+          ? addr->message : NULL,
+          NULL);
   }
 
 deliver_host_port =    save_port;
@@ -930,9 +954,22 @@ router_name = transport_name = NULL;
 
 
 /*************************************************
-*        Generate local prt for logging          *
+*        Generate local part for logging         *
 *************************************************/
 
+static uschar *
+string_get_lpart_sub(const address_item * addr, uschar * s)
+{
+#ifdef SUPPORT_I18N
+if (testflag(addr, af_utf8_downcvt))
+  {
+  uschar * t = string_localpart_utf8_to_alabel(s, NULL);
+  return t ? t : s;    /* t is NULL on a failed conversion */
+  }
+#endif
+return s;
+}
+
 /* This function is a subroutine for use in string_log_address() below.
 
 Arguments:
@@ -947,32 +984,13 @@ string_get_localpart(address_item * addr, gstring * yield)
 {
 uschar * s;
 
-s = addr->prefix;
-if (testflag(addr, af_include_affixes) && s)
-  {
-#ifdef SUPPORT_I18N
-  if (testflag(addr, af_utf8_downcvt))
-    s = string_localpart_utf8_to_alabel(s, NULL);
-#endif
-  yield = string_cat(yield, s);
-  }
+if (testflag(addr, af_include_affixes) && (s = addr->prefix))
+  yield = string_cat(yield, string_get_lpart_sub(addr, s));
 
-s = addr->local_part;
-#ifdef SUPPORT_I18N
-if (testflag(addr, af_utf8_downcvt))
-  s = string_localpart_utf8_to_alabel(s, NULL);
-#endif
-yield = string_cat(yield, s);
+yield = string_cat(yield, string_get_lpart_sub(addr, addr->local_part));
 
-s = addr->suffix;
-if (testflag(addr, af_include_affixes) && s)
-  {
-#ifdef SUPPORT_I18N
-  if (testflag(addr, af_utf8_downcvt))
-    s = string_localpart_utf8_to_alabel(s, NULL);
-#endif
-  yield = string_cat(yield, s);
-  }
+if (testflag(addr, af_include_affixes) && (s = addr->suffix))
+  yield = string_cat(yield, string_get_lpart_sub(addr, s));
 
 return yield;
 }
@@ -1125,7 +1143,7 @@ pointer to a single host item in their host list, for use by the transport. */
 #endif
 
 reset_point = store_mark();
-g = string_get_tainted(256, TRUE);     /* addrs will be tainted, so avoid copy */
+g = string_get_tainted(256, GET_TAINTED);      /* addrs will be tainted, so avoid copy */
 
 if (msg)
   g = string_append(g, 2, host_and_ident(TRUE), US" ");
@@ -1149,11 +1167,6 @@ if (LOGGING(sender_on_delivery) || msg)
 if (*queue_name)
   g = string_append(g, 2, US" Q=", queue_name);
 
-#ifdef EXPERIMENTAL_SRS_ALT
-if(addr->prop.srs_sender)
-  g = string_append(g, 3, US" SRS=<", addr->prop.srs_sender, US">");
-#endif
-
 /* You might think that the return path must always be set for a successful
 delivery; indeed, I did for some time, until this statement crashed. The case
 when it is not set is for a delivery to /dev/null which is optimised by not
@@ -1192,8 +1205,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;
@@ -1265,8 +1276,8 @@ 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))
   g = string_append(g, 2, US" DT=", string_timediff(&addr->delivery_time));
@@ -1322,20 +1333,10 @@ 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));
@@ -1586,6 +1587,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)
@@ -2093,9 +2100,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.
@@ -2125,7 +2132,7 @@ Arguments:
 Returns:     nothing
 */
 
-static void
+void
 deliver_local(address_item *addr, BOOL shadowing)
 {
 BOOL use_initgroups;
@@ -2143,27 +2150,21 @@ 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_ALT
-else if (addr->prop.srs_sender)
-  return_path = addr->prop.srs_sender;
-#endif
 else
   return_path = sender_address;
 
 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
@@ -2370,27 +2371,29 @@ if ((pid = exim_fork(US"delivery-local")) == 0)
     {
     BOOL ok = TRUE;
     set_process_info("delivering %s to %s using %s", message_id,
-     addr->local_part, addr->transport->name);
+     addr->local_part, tp->name);
 
-    /* Setting this global in the subprocess means we need never clear it */
+    /* Setting these globals in the subprocess means we need never clear them */
     transport_name = addr->transport->name;
+    driver_srcfile = tp->srcfile;
+    driver_srcline = tp->srcline;
 
     /* If a transport filter has been specified, set up its argument list.
     Any errors will get put into the address, and FALSE yielded. */
 
-    if (addr->transport->filter_command)
+    if (tp->filter_command)
       {
       ok = transport_set_up_command(&transport_filter_argv,
-        addr->transport->filter_command,
-        TRUE, PANIC, addr, US"transport filter", NULL);
-      transport_filter_timeout = addr->transport->filter_timeout;
+        tp->filter_command,
+        TRUE, PANIC, addr, FALSE, US"transport filter", NULL);
+      transport_filter_timeout = tp->filter_timeout;
       }
     else transport_filter_argv = NULL;
 
     if (ok)
       {
-      debug_print_string(addr->transport->debug_string);
-      replicate = !(addr->transport->info->code)(addr->transport, addr);
+      debug_print_string(tp->debug_string);
+      replicate = !(tp->info->code)(addr->transport, addr);
       }
     }
 
@@ -2410,13 +2413,13 @@ if ((pid = exim_fork(US"delivery-local")) == 0)
     uschar *s;
     int ret;
 
-    if(  (ret = write(pfd[pipe_write], &addr2->transport_return, sizeof(int))) != sizeof(int)
+    if(  (i = addr2->transport_return, (ret = write(pfd[pipe_write], &i, sizeof(int))) != sizeof(int))
       || (ret = write(pfd[pipe_write], &transport_count, sizeof(transport_count))) != sizeof(transport_count)
       || (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_time,  sizeof(struct timeval))) != sizeof(struct timeval)
-      || (ret = write(pfd[pipe_write], &addr2->special_action, sizeof(int))) != sizeof(int)
+      || (i = addr2->special_action, (ret = write(pfd[pipe_write], &i, sizeof(int))) != sizeof(int))
       || (ret = write(pfd[pipe_write], &addr2->transport,
         sizeof(transport_instance *))) != sizeof(transport_instance *)
 
@@ -2484,7 +2487,7 @@ for (addr2 = addr; addr2; addr2 = addr2->next)
     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_time,  sizeof(struct timeval));
-    len = read(pfd[pipe_read], &addr2->special_action, sizeof(int));
+    len = read(pfd[pipe_read], &i, sizeof(int)); addr2->special_action = i;
     len = read(pfd[pipe_read], &addr2->transport,
       sizeof(transport_instance *));
 
@@ -2886,10 +2889,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;
@@ -3045,7 +3046,7 @@ while (addr_local)
     else for (addr2 = addr; addr2; addr2 = addr2->next)
       if (addr2->transport_return == OK)
        {
-       addr3 = store_get(sizeof(address_item), FALSE);
+       addr3 = store_get(sizeof(address_item), GET_UNTAINTED);
        *addr3 = *addr2;
        addr3->next = NULL;
        addr3->shadow_message = US &addr2->shadow_message;
@@ -3193,6 +3194,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)))
       )
@@ -3305,7 +3307,7 @@ BOOL done = p->done;
 
 /* Loop through all items, reading from the pipe when necessary. The pipe
 used to be non-blocking. But I do not see a reason for using non-blocking I/O
-here, as the preceding select() tells us, if data is available for reading.
+here, as the preceding poll() tells us, if data is available for reading.
 
 A read() on a "selected" handle should never block, but(!) it may return
 less data then we expected. (The buffer size we pass to read() shouldn't be
@@ -3439,7 +3441,7 @@ while (!done)
 
       if (!r || !(*ptr & rf_delete))
        {
-       r = store_get(sizeof(retry_item), FALSE);
+       r = store_get(sizeof(retry_item), GET_UNTAINTED);
        r->next = addr->retries;
        addr->retries = r;
        r->flags = *ptr++;
@@ -3578,7 +3580,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)
@@ -3591,9 +3599,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);
@@ -3601,7 +3609,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);
@@ -3624,7 +3632,7 @@ while (!done)
 
          if (*ptr)
            {
-           h = store_get(sizeof(host_item), FALSE);
+           h = store_get(sizeof(host_item), GET_UNTAINTED);
            h->name = string_copy(ptr);
            while (*ptr++);
            h->address = string_copy(ptr);
@@ -3833,7 +3841,7 @@ static address_item *
 par_wait(void)
 {
 int poffset, status;
-address_item *addr, *addrlist;
+address_item * addr, * addrlist;
 pid_t pid;
 
 set_process_info("delivering %s: waiting for a remote delivery subprocess "
@@ -3843,18 +3851,18 @@ set_process_info("delivering %s: waiting for a remote delivery subprocess "
 existence - in which case give an error return. We cannot proceed just by
 waiting for a completion, because a subprocess may have filled up its pipe, and
 be waiting for it to be emptied. Therefore, if no processes have finished, we
-wait for one of the pipes to acquire some data by calling select(), with a
+wait for one of the pipes to acquire some data by calling poll(), with a
 timeout just in case.
 
 The simple approach is just to iterate after reading data from a ready pipe.
 This leads to non-ideal behaviour when the subprocess has written its final Z
 item, closed the pipe, and is in the process of exiting (the common case). A
-call to waitpid() yields nothing completed, but select() shows the pipe ready -
+call to waitpid() yields nothing completed, but poll() shows the pipe ready -
 reading it yields EOF, so you end up with busy-waiting until the subprocess has
 actually finished.
 
 To avoid this, if all the data that is needed has been read from a subprocess
-after select(), an explicit wait() for it is done. We know that all it is doing
+after poll(), an explicit wait() for it is done. We know that all it is doing
 is writing to the pipe and then exiting, so the wait should not be long.
 
 The non-blocking waitpid() is to some extent just insurance; if we could
@@ -3874,9 +3882,7 @@ for (;;)   /* Normally we do not repeat this loop */
   {
   while ((pid = waitpid(-1, &status, WNOHANG)) <= 0)
     {
-    struct timeval tv;
-    fd_set select_pipes;
-    int maxpipe, readycount;
+    int readycount;
 
     /* A return value of -1 can mean several things. If errno != ECHILD, it
     either means invalid options (which we discount), or that this process was
@@ -3900,7 +3906,7 @@ for (;;)   /* Normally we do not repeat this loop */
     subprocesses are still in existence. If kill() gives an OK return, we know
     it must be for one of our processes - it can't be for a re-use of the pid,
     because if our process had finished, waitpid() would have found it. If any
-    of our subprocesses are in existence, we proceed to use select() as if
+    of our subprocesses are in existence, we proceed to use poll() as if
     waitpid() had returned zero. I think this is safe. */
 
     if (pid < 0)
@@ -3924,7 +3930,7 @@ for (;;)   /* Normally we do not repeat this loop */
       if (poffset >= remote_max_parallel)
         {
         DEBUG(D_deliver) debug_printf("*** no delivery children found\n");
-        return NULL;   /* This is the error return */
+       return NULL;    /* This is the error return */
         }
       }
 
@@ -3933,28 +3939,23 @@ for (;;)   /* Normally we do not repeat this loop */
     subprocess, but there are no completed subprocesses. See if any pipes are
     ready with any data for reading. */
 
-    DEBUG(D_deliver) debug_printf("selecting on subprocess pipes\n");
+    DEBUG(D_deliver) debug_printf("polling subprocess pipes\n");
 
-    maxpipe = 0;
-    FD_ZERO(&select_pipes);
     for (poffset = 0; poffset < remote_max_parallel; poffset++)
       if (parlist[poffset].pid != 0)
-        {
-        int fd = parlist[poffset].fd;
-        FD_SET(fd, &select_pipes);
-        if (fd > maxpipe) maxpipe = fd;
-        }
+       {
+       parpoll[poffset].fd = parlist[poffset].fd;
+       parpoll[poffset].events = POLLIN;
+       }
+      else
+       parpoll[poffset].fd = -1;
 
     /* Stick in a 60-second timeout, just in case. */
 
-    tv.tv_sec = 60;
-    tv.tv_usec = 0;
-
-    readycount = select(maxpipe + 1, (SELECT_ARG2_TYPE *)&select_pipes,
-         NULL, NULL, &tv);
+    readycount = poll(parpoll, remote_max_parallel, 60 * 1000);
 
     /* Scan through the pipes and read any that are ready; use the count
-    returned by select() to stop when there are no more. Select() can return
+    returned by poll() to stop when there are no more. Select() can return
     with no processes (e.g. if interrupted). This shouldn't matter.
 
     If par_read_pipe() returns TRUE, it means that either the terminating Z was
@@ -3971,7 +3972,7 @@ for (;;)   /* Normally we do not repeat this loop */
          poffset++)
       {
       if (  (pid = parlist[poffset].pid) != 0
-         && FD_ISSET(parlist[poffset].fd, &select_pipes)
+        && parpoll[poffset].revents
         )
         {
         readycount--;
@@ -4009,7 +4010,7 @@ for (;;)   /* Normally we do not repeat this loop */
     "transport process list", pid);
   }  /* End of the "for" loop */
 
-/* Come here when all the data was completely read after a select(), and
+/* Come here when all the data was completely read after a poll(), and
 the process in pid has been wait()ed for. */
 
 PROCESS_DONE:
@@ -4044,7 +4045,7 @@ if ((status & 0xffff) != 0)
     "%s %d",
     addrlist->transport->driver_name,
     status,
-    (msb == 0)? "terminated by signal" : "exit code",
+    msb == 0 ? "terminated by signal" : "exit code",
     code);
 
   if (msb != 0 || (code != SIGTERM && code != SIGKILL && code != SIGQUIT))
@@ -4062,7 +4063,8 @@ if ((status & 0xffff) != 0)
 /* Else complete reading the pipe to get the result of the delivery, if all
 the data has not yet been obtained. */
 
-else if (!parlist[poffset].done) (void)par_read_pipe(poffset, TRUE);
+else if (!parlist[poffset].done)
+  (void) par_read_pipe(poffset, TRUE);
 
 /* Put the data count and return path into globals, mark the data slot unused,
 decrement the count of subprocesses, and return the address chain. */
@@ -4208,9 +4210,10 @@ set up, do so. */
 
 if (!parlist)
   {
-  parlist = store_get(remote_max_parallel * sizeof(pardata), FALSE);
+  parlist = store_get(remote_max_parallel * sizeof(pardata), GET_UNTAINTED);
   for (poffset = 0; poffset < remote_max_parallel; poffset++)
     parlist[poffset].pid = 0;
+  parpoll = store_get(remote_max_parallel * sizeof(struct pollfd), GET_UNTAINTED);
   }
 
 /* Now loop for each remote delivery */
@@ -4272,6 +4275,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,
@@ -4350,6 +4357,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
@@ -4429,10 +4441,6 @@ for (int delivery_count = 0; addr_remote; delivery_count++)
 
   if(addr->prop.errors_address)
     return_path = addr->prop.errors_address;
-#ifdef EXPERIMENTAL_SRS_ALT
-  else if(addr->prop.srs_sender)
-    return_path = addr->prop.srs_sender;
-#endif
   else
     return_path = sender_address;
 
@@ -4496,6 +4504,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
@@ -4596,7 +4605,7 @@ for (int delivery_count = 0; addr_remote; delivery_count++)
     that it can use either of them, though it prefers O_NONBLOCK, which
     distinguishes between EOF and no-more-data. */
 
-/* The data appears in a timely manner and we already did a select on
+/* The data appears in a timely manner and we already did a poll on
 all pipes, so I do not see a reason to use non-blocking IO here
 
 #ifdef O_NONBLOCK
@@ -4652,8 +4661,10 @@ all pipes, so I do not see a reason to use non-blocking IO here
     int fd = pfd[pipe_write];
     host_item *h;
 
-    /* Setting this global in the subprocess means we need never clear it */
-    transport_name = tp->name;
+    /* Setting these globals in the subprocess means we need never clear them */
+    transport_name = addr->transport->name;
+    driver_srcfile = tp->srcfile;
+    driver_srcline = tp->srcline;
 
     /* There are weird circumstances in which logging is disabled */
     f.disable_logging = tp->disable_logging;
@@ -4882,6 +4893,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)
        {
@@ -5091,7 +5110,7 @@ where they are locally interpreted. [The new draft "821" is more explicit on
 this, Jan 1999.] We know the syntax is valid, so this can be done by simply
 removing quoting backslashes and any unquoted doublequotes. */
 
-t = addr->cc_local_part = store_get(len+1, is_tainted(address));
+t = addr->cc_local_part = store_get(len+1, address);
 while(len-- > 0)
   {
   int c = *address++;
@@ -5134,7 +5153,7 @@ if (percent_hack_domains)
 
   if (new_address)
     {
-    address_item *new_parent = store_get(sizeof(address_item), FALSE);
+    address_item * new_parent = store_get(sizeof(address_item), GET_UNTAINTED);
     *new_parent = *addr;
     addr->parent = new_parent;
     new_parent->child_count = 1;
@@ -5328,10 +5347,10 @@ Returns:       nothing
 */
 
 static void
-print_address_error(address_item *addr, FILE *f, uschar *t)
+print_address_error(address_item * addr, FILE * f, const uschar * t)
 {
 int count = Ustrlen(t);
-uschar *s = testflag(addr, af_pass_message) ? addr->message : NULL;
+uschar * s = testflag(addr, af_pass_message) ? addr->message : NULL;
 
 if (!s && !(s = addr->user_message))
   return;
@@ -5871,7 +5890,7 @@ if (message_logs)
     return continue_closedown();   /* yields DELIVER_NOT_ATTEMPTED */
     }
 
-  /* Make a C stream out of it. */
+  /* Make a stdio stream out of it. */
 
   if (!(message_log = fdopen(fd, "a")))
     {
@@ -6318,7 +6337,7 @@ if (process_recipients != RECIP_IGNORE)
            string_copyn(addr+start, dom ? (dom-1) - start : end - start);
          deliver_domain = dom ? CUS string_copyn(addr+dom, end - dom) : CUS"";
 
-         event_raise(event_action, US"msg:fail:internal", new->message);
+         (void) event_raise(event_action, US"msg:fail:internal", new->message, NULL);
 
          deliver_localpart = save_local;
          deliver_domain = save_domain;
@@ -6398,10 +6417,8 @@ while (addr_new)           /* Loop until all addresses dealt with */
   while (addr_new)
     {
     int rc;
-    uschar *p;
-    tree_node *tnode;
-    dbdata_retry *domain_retry_record;
-    dbdata_retry *address_retry_record;
+    tree_node * tnode;
+    dbdata_retry * domain_retry_record, * address_retry_record;
 
     addr = addr_new;
     addr_new = addr->next;
@@ -6527,14 +6544,19 @@ while (addr_new)           /* Loop until all addresses dealt with */
 
       /* Treat /dev/null as a special case and abandon the delivery. This
       avoids having to specify a uid on the transport just for this case.
-      Arrange for the transport name to be logged as "**bypassed**". */
+      Arrange for the transport name to be logged as "**bypassed**".
+      Copy the transport for this fairly unusual case rather than having
+      to make all transports mutable. */
 
       if (Ustrcmp(addr->address, "/dev/null") == 0)
         {
-        uschar *save = addr->transport->name;
-        addr->transport->name = US"**bypassed**";
+       transport_instance * save_t = addr->transport;
+       transport_instance * t = store_get(sizeof(*t), save_t);
+       *t = *save_t;
+       t->name = US"**bypassed**";
+       addr->transport = t;
         (void)post_process_one(addr, OK, LOG_MAIN, EXIM_DTYPE_TRANSPORT, '=');
-        addr->transport->name = save;
+        addr->transport= save_t;
         continue;   /* with the next new address */
         }
 
@@ -6613,8 +6635,7 @@ while (addr_new)           /* Loop until all addresses dealt with */
     /* Ensure that the domain in the unique field is lower cased, because
     domains are always handled caselessly. */
 
-    p = Ustrrchr(addr->unique, '@');
-    while (*p != 0) { *p = tolower(*p); p++; }
+    for (uschar * p = Ustrrchr(addr->unique, '@'); *p; p++) *p = tolower(*p);
 
     DEBUG(D_deliver|D_route) debug_printf("unique = %s\n", addr->unique);
 
@@ -6811,7 +6832,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)
@@ -7066,13 +7087,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;
     }
@@ -7173,7 +7204,7 @@ local and remote LMTP deliveries. */
 
 if (!regex_IGNOREQUOTA)
   regex_IGNOREQUOTA =
-    regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE);
+    regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", MCS_NOFLAGS, TRUE);
 
 /* Handle local deliveries */
 
@@ -7354,7 +7385,7 @@ for (address_item * a = addr_succeed; a; a = a->next)
     {
     /* copy and relink address_item and send report with all of them at once later */
     address_item * addr_next = addr_senddsn;
-    addr_senddsn = store_get(sizeof(address_item), FALSE);
+    addr_senddsn = store_get(sizeof(address_item), GET_UNTAINTED);
     *addr_senddsn = *a;
     addr_senddsn->next = addr_next;
     }
@@ -8054,7 +8085,7 @@ if (!addr_defer)
   f.deliver_freeze = FALSE;
 
 #ifndef DISABLE_EVENT
-  (void) event_raise(event_action, US"msg:complete", NULL);
+  (void) event_raise(event_action, US"msg:complete", NULL, NULL);
 #endif
   }
 
@@ -8403,7 +8434,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;
@@ -8536,52 +8567,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)
@@ -8616,7 +8604,7 @@ if (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only)
     if (pid == 0)      /* child: will fork again to totally disconnect */
       {
       smtp_proxy_tls(cutthrough.cctx.tls_ctx, big_buffer, big_buffer_size,
-                     pfd, 5*60);
+                     pfd, 5*60, cutthrough.host.name);
       /* does not return */
       }