X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/fc243e944ec00b59b75f41d07494116f925d58b4..fee1a06ec05e58e0cda8cf04f28240688736f945:/src/src/deliver.c diff --git a/src/src/deliver.c b/src/src/deliver.c index 506470070..f5f065e63 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -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. */ @@ -66,7 +67,6 @@ static address_item *addr_new = NULL; static address_item *addr_remote = NULL; static address_item *addr_route = NULL; static address_item *addr_succeed = NULL; -static address_item *addr_dsntmp = NULL; static address_item *addr_senddsn = NULL; static FILE *message_log = NULL; @@ -108,21 +108,21 @@ Returns: the amount of bytes read static ssize_t readn(int fd, void * buffer, size_t len) { - void * next = buffer; - void * end = buffer + len; +uschar * next = buffer; +uschar * end = next + len; - while (next < end) - { - ssize_t got = read(fd, next, end - next); +while (next < end) + { + ssize_t got = read(fd, next, end - next); - /* I'm not sure if there are signals that can interrupt us, - for now I assume the worst */ - if (got == -1 && errno == EINTR) continue; - if (got <= 0) return next - buffer; - next += got; - } + /* I'm not sure if there are signals that can interrupt us, + for now I assume the worst */ + if (got == -1 && errno == EINTR) continue; + if (got <= 0) return next - US buffer; + next += got; + } - return len; +return len; } @@ -145,7 +145,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)); +address_item *addr = store_get(sizeof(address_item), FALSE); *addr = address_defaults; if (copy) address = string_copy(address); addr->address = address; @@ -199,6 +199,7 @@ deliver_recipients = addr; deliver_address_data = addr->prop.address_data; deliver_domain_data = addr->prop.domain_data; deliver_localpart_data = addr->prop.localpart_data; +router_var = addr->prop.variables; /* These may be unset for multiple addresses */ @@ -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; } } @@ -328,6 +333,10 @@ Returns: a file descriptor, or -1 (with errno set) static int open_msglog_file(uschar *filename, int mode, uschar **error) { +if (Ustrstr(filename, US"/../")) + 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--) { int fd = Uopen(filename, @@ -347,7 +356,7 @@ for (int i = 2; i > 0; i--) #ifndef O_CLOEXEC (void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); #endif - if (fchown(fd, exim_uid, exim_gid) < 0) + if (exim_fchown(fd, exim_uid, exim_gid, filename) < 0) { *error = US"chown"; return -1; @@ -367,7 +376,7 @@ for (int i = 2; i > 0; i--) MSGLOG_DIRECTORY_MODE, TRUE); } -*error = US"create"; +*error = US"create or open"; return -1; } @@ -425,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; @@ -451,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 @@ -758,7 +770,7 @@ if (LOGGING(incoming_interface) && LOGGING(outgoing_interface) { g = string_fmt_append(g, " I=[%s]", sending_ip_address); if (LOGGING(outgoing_port)) - g = string_fmt_append(g, "%d", sending_port); + g = string_fmt_append(g, ":%d", sending_port); } return g; } @@ -801,14 +813,20 @@ return g; -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS static gstring * -d_tlslog(gstring * s, address_item * addr) +d_tlslog(gstring * g, address_item * addr) { if (LOGGING(tls_cipher) && addr->cipher) - s = string_append(s, 2, US" X=", addr->cipher); + { + g = string_append(g, 2, US" X=", addr->cipher); +#ifdef EXPERIMENTAL_TLS_RESUME + if (LOGGING(tls_resumption) && testflag(addr, af_tls_resume)) + g = string_catn(g, US"*", 1); +#endif + } if (LOGGING(tls_certificate_verified) && addr->cipher) - s = string_append(s, 2, US" CV=", + g = string_append(g, 2, US" CV=", testflag(addr, af_cert_verified) ? #ifdef SUPPORT_DANE @@ -819,8 +837,8 @@ if (LOGGING(tls_certificate_verified) && addr->cipher) "yes" : "no"); if (LOGGING(tls_peerdn) && addr->peerdn) - s = string_append(s, 3, US" DN=\"", string_printing(addr->peerdn), US"\""); -return s; + g = string_append(g, 3, US" DN=\"", string_printing(addr->peerdn), US"\""); +return g; } #endif @@ -1017,7 +1035,8 @@ splitting is done; in those cases use the original field. */ else { - uschar * cmp = g->s + g->ptr; + uschar * cmp; + int off = g->ptr; /* start of the "full address" */ if (addr->local_part) { @@ -1039,6 +1058,7 @@ else of all, do a caseless comparison; if this succeeds, do a caseful comparison on the local parts. */ + cmp = g->s + off; /* only now, as rebuffer likely done */ string_from_gstring(g); /* ensure nul-terminated */ if ( strcmpic(cmp, topaddr->address) == 0 && Ustrncmp(cmp, topaddr->address, Ustrchr(cmp, '@') - cmp) == 0 @@ -1080,42 +1100,6 @@ return g; -void -timesince(struct timeval * diff, struct timeval * then) -{ -gettimeofday(diff, NULL); -diff->tv_sec -= then->tv_sec; -if ((diff->tv_usec -= then->tv_usec) < 0) - { - diff->tv_sec--; - diff->tv_usec += 1000*1000; - } -} - - - -uschar * -string_timediff(struct timeval * diff) -{ -static uschar buf[sizeof("0.000s")]; - -if (diff->tv_sec >= 5 || !LOGGING(millisec)) - return readconf_printtime((int)diff->tv_sec); - -sprintf(CS buf, "%u.%03us", (uint)diff->tv_sec, (uint)diff->tv_usec/1000); -return buf; -} - - -uschar * -string_timesince(struct timeval * then) -{ -struct timeval diff; - -timesince(&diff, then); -return string_timediff(&diff); -} - /******************************************************************************/ @@ -1131,7 +1115,7 @@ void delivery_log(int flags, address_item * addr, int logchar, uschar * msg) { gstring * g; /* Used for a temporary, expanding buffer, for building log lines */ -void * reset_point; /* released afterwards. */ +rmark reset_point; /* Log the delivery on the main log. We use an extensible string to build up the log line, and reset the store afterwards. Remote deliveries should always @@ -1143,7 +1127,8 @@ pointer to a single host item in their host list, for use by the transport. */ lookup_dnssec_authenticated = NULL; #endif -g = reset_point = string_get(256); +reset_point = store_mark(); +g = string_get_tainted(256, TRUE); /* addrs will be tainted, so avoid copy */ if (msg) g = string_append(g, 2, host_and_ident(TRUE), US" "); @@ -1210,7 +1195,7 @@ else if (addr->host_used) { g = d_hostlog(g, addr); - if (continue_sequence > 1) + 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); #ifndef DISABLE_EVENT @@ -1225,7 +1210,7 @@ else #endif } -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS g = d_tlslog(g, addr); #endif @@ -1244,7 +1229,7 @@ else { if (testflag(addr, af_pipelining)) g = string_catn(g, US" L", 2); -#ifdef EXPERIMENTAL_PIPE_CONNECT +#ifndef DISABLE_PIPE_CONNECT if (testflag(addr, af_early_pipe)) g = string_catn(g, US"*", 1); #endif @@ -1287,10 +1272,7 @@ if (LOGGING(queue_time)) string_timesince(&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. */ @@ -1311,14 +1293,12 @@ static void deferral_log(address_item * addr, uschar * now, int logflags, uschar * driver_name, uschar * driver_kind) { -gstring * g; -void * reset_point; +rmark reset_point = store_mark(); +gstring * g = string_get(256); /* Build up the line that is used for both the message log and the main log. */ -g = reset_point = string_get(256); - /* Create the address string for logging. Must not do this earlier, because an OK result may be changed to FAIL when a pipe returns text. */ @@ -1360,6 +1340,9 @@ if (addr->host_used) } } +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); @@ -1390,8 +1373,8 @@ return; static void failure_log(address_item * addr, uschar * driver_kind, uschar * now) { -void * reset_point; -gstring * g = reset_point = string_get(256); +rmark reset_point = store_mark(); +gstring * g = string_get(256); #ifndef DISABLE_EVENT /* Message failures for which we will send a DSN get their event raised @@ -1429,7 +1412,7 @@ if (addr->transport) if (addr->host_used) g = d_hostlog(g, addr); -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS g = d_tlslog(g, addr); #endif @@ -1439,6 +1422,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, @@ -1629,12 +1615,13 @@ if (result == OK) } /* Certificates for logging (via events) */ -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS tls_out.ourcert = addr->ourcert; addr->ourcert = NULL; 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; @@ -1645,9 +1632,10 @@ if (result == OK) delivery_log(LOG_MAIN, addr, logchar, NULL); -#ifdef SUPPORT_TLS +#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; @@ -1784,7 +1772,7 @@ if (format) gstring * g; va_start(ap, format); - g = string_vformat(NULL, TRUE, CS format, ap); + g = string_vformat(NULL, SVFMT_EXTEND|SVFMT_REBUFFER, CS format, ap); va_end(ap); addr->message = string_from_gstring(g); } @@ -2046,10 +2034,10 @@ Returns: TRUE if previously delivered by the transport static BOOL previously_transported(address_item *addr, BOOL testing) { -(void)string_format(big_buffer, big_buffer_size, "%s/%s", +uschar * s = string_sprintf("%s/%s", addr->unique + (testflag(addr, af_homonym)? 3:0), addr->transport->name); -if (tree_search(tree_nonrecipients, big_buffer) != 0) +if (tree_search(tree_nonrecipients, s) != 0) { DEBUG(D_deliver|D_route|D_transport) debug_printf("%s was previously delivered (%s transport): discarded\n", @@ -2108,9 +2096,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. @@ -2284,7 +2272,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; @@ -2430,7 +2418,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 *) @@ -2498,7 +2486,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 *)); @@ -2568,7 +2556,7 @@ if (!shadowing) /* In the test harness, wait just a bit to let the subprocess finish off any debug output etc first. */ - if (f.running_in_test_harness) millisleep(300); + testharness_pause_ms(300); DEBUG(D_deliver) debug_printf("journalling %s", big_buffer); len = Ustrlen(big_buffer); @@ -2631,7 +2619,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)) @@ -2749,7 +2737,7 @@ while (addr_local) f.disable_logging = FALSE; /* Jic */ addr->message = addr->router ? string_sprintf("No transport set by %s router", addr->router->name) - : string_sprintf("No transport set by system filter"); + : US"No transport set by system filter"; post_process_one(addr, DEFER, logflags, EXIM_DTYPE_TRANSPORT, 0); continue; } @@ -2900,7 +2888,7 @@ while (addr_local) of these checks, rather than for all local deliveries, because some local deliveries (e.g. to pipes) can take a substantial time. */ - if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE))) + 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"); @@ -3060,7 +3048,7 @@ while (addr_local) else for (addr2 = addr; addr2; addr2 = addr2->next) if (addr2->transport_return == OK) { - addr3 = store_get(sizeof(address_item)); + addr3 = store_get(sizeof(address_item), FALSE); *addr3 = *addr2; addr3->next = NULL; addr3->shadow_message = US &addr2->shadow_message; @@ -3152,11 +3140,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 @@ -3458,7 +3442,7 @@ while (!done) if (!r || !(*ptr & rf_delete)) { - r = store_get(sizeof(retry_item)); + r = store_get(sizeof(retry_item), FALSE); r->next = addr->retries; addr->retries = r; r->flags = *ptr++; @@ -3499,17 +3483,19 @@ while (!done) it in with the other info, in order to keep each message short enough to guarantee it won't be split in the pipe. */ -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS case 'X': if (!addr) goto ADDR_MISMATCH; /* Below, in 'A' handler */ 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); @@ -3537,7 +3523,7 @@ while (!done) } while (*ptr++); break; -#endif /*SUPPORT_TLS*/ +#endif /*DISABLE_TLS*/ case 'C': /* client authenticator information */ switch (*subid) @@ -3558,7 +3544,7 @@ while (!done) case 'L': switch (*subid) { -#ifdef EXPERIMENTAL_PIPE_CONNECT +#ifndef DISABLE_PIPE_CONNECT case 2: setflag(addr, af_early_pipe); /*FALLTHROUGH*/ #endif case 1: setflag(addr, af_pipelining); break; @@ -3628,8 +3614,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; @@ -3641,7 +3627,7 @@ while (!done) if (*ptr) { - h = store_get(sizeof(host_item)); + h = store_get(sizeof(host_item), FALSE); h->name = string_copy(ptr); while (*ptr++); h->address = string_copy(ptr); @@ -4225,7 +4211,7 @@ set up, do so. */ if (!parlist) { - parlist = store_get(remote_max_parallel * sizeof(pardata)); + parlist = store_get(remote_max_parallel * sizeof(pardata), FALSE); for (poffset = 0; poffset < remote_max_parallel; poffset++) parlist[poffset].pid = 0; } @@ -4289,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, @@ -4367,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 @@ -4513,6 +4508,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 @@ -4664,7 +4660,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; @@ -4678,10 +4674,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 @@ -4787,16 +4780,18 @@ all pipes, so I do not see a reason to use non-blocking IO here for(; addr; addr = addr->next) { uschar *ptr; - retry_item *r; /* The certificate verification status goes into the flags */ if (tls_out.certificate_verified) setflag(addr, af_cert_verified); #ifdef SUPPORT_DANE if (tls_out.dane_verified) setflag(addr, af_dane_verified); #endif +# ifdef EXPERIMENTAL_TLS_RESUME + if (tls_out.resumption & RESUME_USED) setflag(addr, af_tls_resume); +# endif /* Use an X item only if there's something to send */ -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS if (addr->cipher) { ptr = big_buffer + sprintf(CS big_buffer, "%.128s", addr->cipher) + 1; @@ -4817,7 +4812,7 @@ all pipes, so I do not see a reason to use non-blocking IO here if (addr->peercert) { ptr = big_buffer; - if (!tls_export_cert(ptr, big_buffer_size-2, addr->peercert)) + if (tls_export_cert(ptr, big_buffer_size-2, addr->peercert)) while(*ptr++); else *ptr++ = 0; @@ -4826,7 +4821,7 @@ all pipes, so I do not see a reason to use non-blocking IO here if (addr->ourcert) { ptr = big_buffer; - if (!tls_export_cert(ptr, big_buffer_size-2, addr->ourcert)) + if (tls_export_cert(ptr, big_buffer_size-2, addr->ourcert)) while(*ptr++); else *ptr++ = 0; @@ -4839,7 +4834,7 @@ all pipes, so I do not see a reason to use non-blocking IO here rmt_dlv_checked_write(fd, 'X', '4', big_buffer, ptr - big_buffer); } # endif -#endif /*SUPPORT_TLS*/ +#endif /*DISABLE_TLS*/ if (client_authenticator) { @@ -4863,7 +4858,7 @@ all pipes, so I do not see a reason to use non-blocking IO here #endif if (testflag(addr, af_pipelining)) -#ifdef EXPERIMENTAL_PIPE_CONNECT +#ifndef DISABLE_PIPE_CONNECT if (testflag(addr, af_early_pipe)) rmt_dlv_checked_write(fd, 'L', '2', NULL, 0); else @@ -4884,7 +4879,7 @@ all pipes, so I do not see a reason to use non-blocking IO here /* Retry information: for most success cases this will be null. */ - for (r = addr->retries; r; r = r->next) + for (retry_item * r = addr->retries; r; r = r->next) { sprintf(CS big_buffer, "%c%.500s", r->flags, r->key); ptr = big_buffer + Ustrlen(big_buffer+2) + 3; @@ -4943,8 +4938,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); @@ -5003,7 +4998,7 @@ all pipes, so I do not see a reason to use non-blocking IO here if (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only) { -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS if (cutthrough.is_tls) tls_close(cutthrough.cctx.tls_ctx, TLS_NO_SHUTDOWN); #endif @@ -5047,9 +5042,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 if (f.running_in_test_harness) millisleep(500); + else testharness_pause_ms(600); continue; @@ -5108,7 +5104,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); +t = addr->cc_local_part = store_get(len+1, is_tainted(address)); while(len-- > 0) { int c = *address++; @@ -5151,7 +5147,7 @@ if (percent_hack_domains) if (new_address) { - address_item *new_parent = store_get(sizeof(address_item)); + address_item *new_parent = store_get(sizeof(address_item), FALSE); *new_parent = *addr; addr->parent = new_parent; new_parent->child_count = 1; @@ -5397,7 +5393,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) @@ -5409,19 +5406,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); } @@ -5477,6 +5487,67 @@ while ((addr = *anchor)) +/************************************************/ + +static void +print_dsn_addr_action(FILE * f, address_item * addr, + uschar * action, uschar * status) +{ +address_item * pa; + +if (addr->dsn_orcpt) + fprintf(f,"Original-Recipient: %s\n", addr->dsn_orcpt); + +for (pa = addr; pa->parent; ) pa = pa->parent; +fprintf(f, "Action: %s\n" + "Final-Recipient: rfc822;%s\n" + "Status: %s\n", + action, pa->address, status); +} + + + +/* 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 != '/' || is_tainted(s)) + log_write(0, LOG_MAIN|LOG_PANIC, + "%s is not %s after expansion: '%s'\n", + varname, *s == '/' ? "untainted" : "absolute", 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 * *************************************************/ @@ -5528,8 +5599,13 @@ int process_recipients = RECIP_ACCEPT; open_db dbblock; open_db *dbm_file; extern int acl_where; +uschar *info; + +#ifdef MEASURE_TIMING +report_time_since(×tamp_startup, US"delivery start"); /* testcase 0022, 2100 */ +#endif -uschar *info = queue_run_pid == (pid_t)0 +info = queue_run_pid == (pid_t)0 ? string_sprintf("delivering %s", id) : string_sprintf("delivering %s (queue run pid %d)", id, queue_run_pid); @@ -6000,8 +6076,8 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) if (addr_new) { - int uid = (system_filter_uid_set)? system_filter_uid : geteuid(); - int gid = (system_filter_gid_set)? system_filter_gid : getegid(); + int uid = system_filter_uid_set ? system_filter_uid : geteuid(); + int gid = system_filter_gid_set ? system_filter_gid : getegid(); /* The text "system-filter" is tested in transport_set_up_command() and in set_up_shell_command() in the pipe transport, to enable them to permit @@ -6075,6 +6151,9 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) 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); tpname = tmp; } else @@ -6159,10 +6238,11 @@ 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: %d\n", + DEBUG(D_deliver) debug_printf("DSN: set orcpt: %s flags: 0x%x\n", new->dsn_orcpt ? new->dsn_orcpt : US"", new->dsn_flags); switch (process_recipients) @@ -6235,7 +6315,7 @@ if (process_recipients != RECIP_IGNORE) } #ifndef DISABLE_EVENT - if (process_recipients != RECIP_ACCEPT) + if (process_recipients != RECIP_ACCEPT && event_action) { uschar * save_local = deliver_localpart; const uschar * save_domain = deliver_domain; @@ -6321,7 +6401,7 @@ while (addr_new) /* Loop until all addresses dealt with */ /* Failure to open the retry database is treated the same as if it does not exist. In both cases, dbm_file is NULL. */ - if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE))) + if (!(dbm_file = dbfn_open(US"retry", O_RDONLY, &dbblock, FALSE, TRUE))) DEBUG(D_deliver|D_retry|D_route|D_hints_lookup) debug_printf("no retry data available\n"); @@ -6383,10 +6463,8 @@ while (addr_new) /* Loop until all addresses dealt with */ keep piling '>' characters on the front. */ if (addr->address[0] == '>') - { while (tree_search(tree_duplicates, addr->unique)) addr->unique = string_sprintf(">%s", addr->unique); - } else if ((tnode = tree_search(tree_duplicates, addr->unique))) { @@ -6704,7 +6782,7 @@ while (addr_new) /* Loop until all addresses dealt with */ (void)post_process_one(addr, DEFER, LOG_MAIN, EXIM_DTYPE_ROUTER, 0); /* For remote-retry errors (here and just above) that we've not yet - hit the rery time, use the error recorded in the retry database + hit the retry time, use the error recorded in the retry database as info in the warning message. This lets us send a message even when we're not failing on a fresh attempt. We assume that this info is not sensitive. */ @@ -6794,8 +6872,8 @@ while (addr_new) /* Loop until all addresses dealt with */ &addr_succeed, v_none)) == DEFER) retry_add_item(addr, addr->router->retry_use_local_part - ? string_sprintf("R:%s@%s", addr->local_part, addr->domain) - : string_sprintf("R:%s", addr->domain), + ? string_sprintf("R:%s@%s", addr->local_part, addr->domain) + : string_sprintf("R:%s", addr->domain), 0); /* Otherwise, if there is an existing retry record in the database, add @@ -7001,10 +7079,20 @@ 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; @@ -7072,7 +7160,7 @@ if (addr_local || addr_remote) that the mode is correct - the group setting doesn't always seem to get set automatically. */ - if( fchown(journal_fd, exim_uid, exim_gid) + if( exim_fchown(journal_fd, exim_uid, exim_gid, fname) || fchmod(journal_fd, SPOOL_MODE) #ifndef O_CLOEXEC || fcntl(journal_fd, F_SETFD, fcntl(journal_fd, F_GETFD) | FD_CLOEXEC) @@ -7144,7 +7232,7 @@ if (addr_remote) /* Precompile some regex that are used to recognize parameters in response to an EHLO command, if they aren't already compiled. */ - deliver_init(); + smtp_deliver_init(); /* Now sort the addresses if required, and do the deliveries. The yield of do_remote_deliveries is FALSE when mua_wrapper is set and all addresses @@ -7260,38 +7348,37 @@ else if (!f.dont_deliver) /* Send DSN for successful messages if requested */ addr_senddsn = NULL; -for (addr_dsntmp = addr_succeed; addr_dsntmp; addr_dsntmp = addr_dsntmp->next) +for (address_item * a = addr_succeed; a; a = a->next) { /* af_ignore_error not honored here. it's not an error */ DEBUG(D_deliver) debug_printf("DSN: processing router : %s\n" "DSN: processing successful delivery address: %s\n" "DSN: Sender_address: %s\n" - "DSN: orcpt: %s flags: %d\n" + "DSN: orcpt: %s flags: 0x%x\n" "DSN: envid: %s ret: %d\n" "DSN: Final recipient: %s\n" "DSN: Remote SMTP server supports DSN: %d\n", - addr_dsntmp->router ? addr_dsntmp->router->name : US"(unknown)", - addr_dsntmp->address, + a->router ? a->router->name : US"(unknown)", + a->address, sender_address, - addr_dsntmp->dsn_orcpt ? addr_dsntmp->dsn_orcpt : US"NULL", - addr_dsntmp->dsn_flags, + a->dsn_orcpt ? a->dsn_orcpt : US"NULL", + a->dsn_flags, dsn_envid ? dsn_envid : US"NULL", dsn_ret, - addr_dsntmp->address, - addr_dsntmp->dsn_aware + a->address, + a->dsn_aware ); /* send report if next hop not DSN aware or a router flagged "last DSN hop" - and a report was requested */ - if ( ( addr_dsntmp->dsn_aware != dsn_support_yes - || addr_dsntmp->dsn_flags & rf_dsnlasthop - ) - && addr_dsntmp->dsn_flags & rf_notify_success + and a report was requested */ + + if ( (a->dsn_aware != dsn_support_yes || a->dsn_flags & rf_dsnlasthop) + && a->dsn_flags & rf_notify_success ) { /* 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)); - *addr_senddsn = *addr_dsntmp; + addr_senddsn = store_get(sizeof(address_item), FALSE); + *addr_senddsn = *a; addr_senddsn->next = addr_next; } else @@ -7304,14 +7391,14 @@ 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); if (pid < 0) /* Creation of child failed */ { log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %d (parent %d) failed to " - "create child process to send failure message: %s", getpid(), + "create child process to send success-dsn message: %s", getpid(), getppid(), strerror(errno)); DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n"); @@ -7324,7 +7411,7 @@ if (addr_senddsn) transport_ctx tctx = {{0}}; DEBUG(D_deliver) - debug_printf("sending error message to: %s\n", sender_address); + debug_printf("sending success-dsn to: %s\n", sender_address); /* build unique id for MIME boundary */ bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); @@ -7333,11 +7420,14 @@ if (addr_senddsn) if (errors_reply_to) fprintf(f, "Reply-To: %s\n", errors_reply_to); + moan_write_from(f); fprintf(f, "Auto-Submitted: auto-generated\n" - "From: Mail Delivery System \n" "To: %s\n" - "Subject: Delivery Status Notification\n" - "Content-Type: multipart/report; report-type=delivery-status; boundary=%s\n" + "Subject: Delivery Status Notification\n", + sender_address); + moan_write_references(f, NULL); + fprintf(f, "Content-Type: multipart/report;" + " report-type=delivery-status; boundary=%s\n" "MIME-Version: 1.0\n\n" "--%s\n" @@ -7345,14 +7435,13 @@ if (addr_senddsn) "This message was created automatically by mail delivery software.\n" " ----- The following addresses had successful delivery notifications -----\n", - qualify_domain_sender, sender_address, bound, bound); + bound, bound); - for (addr_dsntmp = addr_senddsn; addr_dsntmp; - addr_dsntmp = addr_dsntmp->next) + for (address_item * a = addr_senddsn; a; a = a->next) fprintf(f, "<%s> (relayed %s)\n\n", - addr_dsntmp->address, - addr_dsntmp->dsn_flags & rf_dsnlasthop ? "via non DSN router" - : addr_dsntmp->dsn_aware == dsn_support_no ? "to non-DSN-aware mailer" + a->address, + a->dsn_flags & rf_dsnlasthop ? "via non DSN router" + : a->dsn_aware == dsn_support_no ? "to non-DSN-aware mailer" : "via non \"Remote SMTP\" router" ); @@ -7371,24 +7460,18 @@ if (addr_senddsn) } fputc('\n', f); - for (addr_dsntmp = addr_senddsn; - addr_dsntmp; - addr_dsntmp = addr_dsntmp->next) + for (address_item * a = addr_senddsn; a; a = a->next) { - if (addr_dsntmp->dsn_orcpt) - fprintf(f,"Original-Recipient: %s\n", addr_dsntmp->dsn_orcpt); + host_item * hu; - fprintf(f, "Action: delivered\n" - "Final-Recipient: rfc822;%s\n" - "Status: 2.0.0\n", - addr_dsntmp->address); + print_dsn_addr_action(f, a, US"delivered", US"2.0.0"); - if (addr_dsntmp->host_used && addr_dsntmp->host_used->name) + if ((hu = a->host_used) && hu->name) fprintf(f, "Remote-MTA: dns; %s\nDiagnostic-Code: smtp; 250 Ok\n\n", - addr_dsntmp->host_used->name); + hu->name); else fprintf(f, "Diagnostic-Code: X-Exim; relayed via non %s router\n\n", - addr_dsntmp->dsn_flags & rf_dsnlasthop ? "DSN" : "SMTP"); + a->dsn_flags & rf_dsnlasthop ? "DSN" : "SMTP"); } fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", bound); @@ -7471,7 +7554,8 @@ while (addr_failed) mark the recipient done. */ if ( addr_failed->prop.ignore_error - || addr_failed->dsn_flags & (rf_dsnflags & ~rf_notify_failure) + || addr_failed->dsn_flags & rf_dsnflags + && !(addr_failed->dsn_flags & rf_notify_failure) ) { addr = addr_failed; @@ -7481,11 +7565,13 @@ while (addr_failed) #ifndef DISABLE_EVENT msg_event_raise(US"msg:fail:delivery", addr); #endif - log_write(0, LOG_MAIN, "%s%s%s%s: error ignored", + log_write(0, LOG_MAIN, "%s%s%s%s: error ignored%s", addr->address, !addr->parent ? US"" : US" <", !addr->parent ? US"" : addr->parent->address, - !addr->parent ? US"" : US">"); + !addr->parent ? US"" : US">", + addr->prop.ignore_error + ? US"" : US": RFC 3461 DSN, failure notify not requested"); address_done(addr, logtod); child_done(addr, logtod); @@ -7506,7 +7592,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)); @@ -7578,6 +7664,7 @@ while (addr_failed) fprintf(fp, "Auto-Submitted: auto-replied\n"); moan_write_from(fp); fprintf(fp, "To: %s\n", bounce_recipient); + moan_write_references(fp, NULL); /* generate boundary string and output MIME-Headers */ bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); @@ -7591,9 +7678,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. */ @@ -7766,10 +7852,9 @@ wording. */ for (addr = handled_addr; addr; addr = addr->next) { host_item * hu; - fprintf(fp, "Action: failed\n" - "Final-Recipient: rfc822;%s\n" - "Status: 5.0.0\n", - addr->address); + + print_dsn_addr_action(fp, addr, US"failed", US"5.0.0"); + if ((hu = addr->host_used) && hu->name) { fprintf(fp, "Remote-MTA: dns; %s\n", hu->name); @@ -7783,11 +7868,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); @@ -7887,10 +7972,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. */ - - if (f.running_in_test_harness) millisleep(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 @@ -8129,21 +8210,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 */ @@ -8164,7 +8231,8 @@ else if (addr_defer != (address_item *)(+1)) DEBUG(D_deliver) { - debug_printf("time on queue = %s id %s addr %s\n", readconf_printtime(queue_time), message_id, addr_defer->address); + debug_printf("time on queue = %s id %s addr %s\n", + readconf_printtime(queue_time), message_id, addr_defer->address); debug_printf("warning counts: required %d done %d\n", count, warning_count); } @@ -8177,20 +8245,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 @@ -8202,6 +8269,7 @@ else if (addr_defer != (address_item *)(+1)) fprintf(f, "Auto-Submitted: auto-replied\n"); moan_write_from(f); fprintf(f, "To: %s\n", recipients); + moan_write_references(f, NULL); /* generated boundary string and output MIME-Headers */ bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand()); @@ -8259,13 +8327,9 @@ else if (addr_defer != (address_item *)(+1)) /* List the addresses, with error information if allowed */ - /* store addr_defer for machine readable part */ - address_item *addr_dsndefer = addr_defer; fputc('\n', f); - while (addr_defer) + for (address_item * addr = addr_defer; addr; addr = addr->next) { - address_item *addr = addr_defer; - addr_defer = addr->next; if (print_address_information(addr, f, US" ", US"\n ", US"")) print_address_error(addr, f, US"Delay reason: "); fputc('\n', f); @@ -8308,20 +8372,16 @@ else if (addr_defer != (address_item *)(+1)) } fputc('\n', f); - for ( ; addr_dsndefer; addr_dsndefer = addr_dsndefer->next) + for (address_item * addr = addr_defer; addr; addr = addr->next) { - if (addr_dsndefer->dsn_orcpt) - fprintf(f, "Original-Recipient: %s\n", addr_dsndefer->dsn_orcpt); - - fprintf(f, "Action: delayed\n" - "Final-Recipient: rfc822;%s\n" - "Status: 4.0.0\n", - addr_dsndefer->address); - if (addr_dsndefer->host_used && addr_dsndefer->host_used->name) + host_item * hu; + + print_dsn_addr_action(f, addr, US"delayed", US"4.0.0"); + + if ((hu = addr->host_used) && hu->name) { - fprintf(f, "Remote-MTA: dns; %s\n", - addr_dsndefer->host_used->name); - print_dsn_diagnostic_code(addr_dsndefer, f); + fprintf(f, "Remote-MTA: dns; %s\n", hu->name); + print_dsn_diagnostic_code(addr, f); } fputc('\n', f); } @@ -8471,6 +8531,9 @@ to try delivery. */ (void)close(deliver_datafile); deliver_datafile = -1; DEBUG(D_deliver) debug_printf("end delivery of %s\n", id); +#ifdef MEASURE_TIMING +report_time_since(×tamp_startup, US"delivery end"); /* testcase 0005 */ +#endif /* It is unlikely that there will be any cached resources, since they are released after routing, and in the delivery subprocesses. However, it's @@ -8486,57 +8549,13 @@ return final_yield; void -deliver_init(void) +tcp_init(void) { #ifdef EXIM_TFO_PROBE tfo_probe(); #else f.tcp_fastopen_ok = TRUE; #endif - - -if (!regex_PIPELINING) regex_PIPELINING = - regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE); - -if (!regex_SIZE) regex_SIZE = - regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE); - -if (!regex_AUTH) regex_AUTH = - regex_must_compile(AUTHS_REGEX, FALSE, TRUE); - -#ifdef SUPPORT_TLS -if (!regex_STARTTLS) regex_STARTTLS = - regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); - -# ifdef EXPERIMENTAL_REQUIRETLS -if (!regex_REQUIRETLS) regex_REQUIRETLS = - regex_must_compile(US"\\n250[\\s\\-]REQUIRETLS(\\s|\\n|$)", FALSE, TRUE); -# endif -#endif - -if (!regex_CHUNKING) regex_CHUNKING = - regex_must_compile(US"\\n250[\\s\\-]CHUNKING(\\s|\\n|$)", FALSE, TRUE); - -#ifndef DISABLE_PRDR -if (!regex_PRDR) regex_PRDR = - regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE); -#endif - -#ifdef SUPPORT_I18N -if (!regex_UTF8) regex_UTF8 = - regex_must_compile(US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE); -#endif - -if (!regex_DSN) regex_DSN = - regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE); - -if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA = - regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE); - -#ifdef EXPERIMENTAL_PIPE_CONNECT -if (!regex_EARLY_PIPE) regex_EARLY_PIPE = - regex_must_compile(US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)", FALSE, TRUE); -#endif } @@ -8599,7 +8618,7 @@ if (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only) smtp_peer_options = cutthrough.peer_options; continue_sequence = 0; -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS if (cutthrough.is_tls) { int pfd[2], pid; @@ -8613,18 +8632,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 */ { - if (f.running_in_test_harness) millisleep(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 */ @@ -8642,7 +8660,7 @@ else } return; /* compiler quietening; control does not reach here. */ -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS fail: log_write(0, LOG_MAIN | (exec_type == CEE_EXEC_EXIT ? LOG_PANIC : LOG_PANIC_DIE),