X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/4833456868dd4370c3ab5c79dc1e1812dbb8a36d..fee1a06ec05e58e0cda8cf04f28240688736f945:/src/src/deliver.c diff --git a/src/src/deliver.c b/src/src/deliver.c index 68152d505..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; } } @@ -284,13 +289,12 @@ to the same pipe or file. */ else { - address_item *addr2; if (testflag(addr, af_pfr)) { if (testflag(addr, af_file)) address_file = addr->local_part; else if (addr->local_part[0] == '|') address_pipe = addr->local_part; } - for (addr2 = addr->next; addr2; addr2 = addr2->next) + for (address_item * addr2 = addr->next; addr2; addr2 = addr2->next) { if (deliver_domain && Ustrcmp(deliver_domain, addr2->domain) != 0) deliver_domain = NULL; @@ -329,11 +333,13 @@ Returns: a file descriptor, or -1 (with errno set) static int open_msglog_file(uschar *filename, int mode, uschar **error) { -int fd, i; +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 (i = 2; i > 0; i--) +for (int i = 2; i > 0; i--) { - fd = Uopen(filename, + int fd = Uopen(filename, #ifdef O_CLOEXEC O_CLOEXEC | #endif @@ -350,7 +356,7 @@ for (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; @@ -370,7 +376,7 @@ for (i = 2; i > 0; i--) MSGLOG_DIRECTORY_MODE, TRUE); } -*error = US"create"; +*error = US"create or open"; return -1; } @@ -422,14 +428,13 @@ Returns: nothing static void replicate_status(address_item *addr) { -address_item *addr2; -for (addr2 = addr->next; addr2; addr2 = addr2->next) +for (address_item * addr2 = addr->next; addr2; addr2 = addr2->next) { addr2->transport = addr->transport; 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 @@ -657,8 +665,6 @@ Returns: nothing static void address_done(address_item *addr, uschar *now) { -address_item *dup; - update_spool = TRUE; /* Ensure spool gets updated */ /* Top-level address */ @@ -685,7 +691,7 @@ else tree_add_nonrecipient(addr->unique); /* Check the list of duplicate addresses and ensure they are now marked done as well. */ -for (dup = addr_duplicate; dup; dup = dup->next) +for (address_item * dup = addr_duplicate; dup; dup = dup->next) if (Ustrcmp(addr->unique, dup->unique) == 0) { tree_add_nonrecipient(dup->unique); @@ -716,9 +722,10 @@ Returns: nothing static void child_done(address_item *addr, uschar *now) { -address_item *aa; while (addr->parent) { + address_item *aa; + addr = addr->parent; if (--addr->child_count > 0) return; /* Incomplete parent */ address_done(addr, now); @@ -761,10 +768,9 @@ d_log_interface(gstring * g) if (LOGGING(incoming_interface) && LOGGING(outgoing_interface) && sending_ip_address) { - g = string_append(g, 2, US" I=[", sending_ip_address); - g = LOGGING(outgoing_port) - ? string_append(g, 2, US"]:", string_sprintf("%d", sending_port)) - : string_catn(g, US"]", 1); + g = string_fmt_append(g, " I=[%s]", sending_ip_address); + if (LOGGING(outgoing_port)) + g = string_fmt_append(g, ":%d", sending_port); } return g; } @@ -784,21 +790,21 @@ if (LOGGING(dnssec) && h->dnssec == DS_YES) g = string_append(g, 3, US" [", h->address, US"]"); if (LOGGING(outgoing_port)) - g = string_append(g, 2, US":", string_sprintf("%d", h->port)); + g = string_fmt_append(g, ":%d", h->port); #ifdef SUPPORT_SOCKS if (LOGGING(proxy) && proxy_local_address) { g = string_append(g, 3, US" PRX=[", proxy_local_address, US"]"); if (LOGGING(outgoing_port)) - g = string_append(g, 2, US":", string_sprintf("%d", proxy_local_port)); + g = string_fmt_append(g, ":%d", proxy_local_port); } #endif g = d_log_interface(g); if (testflag(addr, af_tcp_fastopen)) - g = string_catn(g, US" TFO", 4); + g = string_catn(g, US" TFO*", testflag(addr, af_tcp_fastopen_data) ? 5 : 4); return g; } @@ -807,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 @@ -825,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 @@ -851,7 +863,7 @@ if (action) if (!(s = expand_string(action)) && *expand_string_message) log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand event_action %s in %s: %s\n", - event, transport_name, expand_string_message); + event, transport_name ? transport_name : US"main", expand_string_message); event_name = event_data = NULL; @@ -862,6 +874,7 @@ if (action) { DEBUG(D_deliver) debug_printf("Event(%s): event_action returned \"%s\"\n", event, s); + errno = ERRNO_EVENT; return s; } } @@ -877,20 +890,33 @@ const uschar * save_host = deliver_host; const uschar * save_address = deliver_host_address; const int save_port = deliver_host_port; -if (!addr->transport) - return; - router_name = addr->router ? addr->router->name : NULL; -transport_name = addr->transport->name; deliver_domain = addr->domain; deliver_localpart = addr->local_part; deliver_host = addr->host_used ? addr->host_used->name : NULL; -(void) event_raise(addr->transport->event_action, event, - addr->host_used - || Ustrcmp(addr->transport->driver_name, "smtp") == 0 - || Ustrcmp(addr->transport->driver_name, "lmtp") == 0 - ? addr->message : NULL); +if (!addr->transport) + { + if (Ustrcmp(event, "msg:fail:delivery") == 0) + { + /* An address failed with no transport involved. This happens when + 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); + } + } +else + { + transport_name = addr->transport->name; + + (void) event_raise(addr->transport->event_action, event, + addr->host_used + || Ustrcmp(addr->transport->driver_name, "smtp") == 0 + || Ustrcmp(addr->transport->driver_name, "lmtp") == 0 + || Ustrcmp(addr->transport->driver_name, "autoreply") == 0 + ? addr->message : NULL); + } deliver_host_port = save_port; deliver_host_address = save_address; @@ -1009,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) { @@ -1031,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 @@ -1049,8 +1077,7 @@ if ( (all_parents || testflag(addr, af_pfr)) && addr->parent != topaddr) { uschar *s = US" ("; - address_item *addr2; - for (addr2 = addr->parent; addr2 != topaddr; addr2 = addr2->parent) + for (address_item * addr2 = addr->parent; addr2 != topaddr; addr2 = addr2->parent) { g = string_catn(g, s, 2); g = string_cat (g, addr2->address); @@ -1073,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, "%d.%03ds", (int)diff->tv_sec, (int)diff->tv_usec/1000); -return buf; -} - - -uschar * -string_timesince(struct timeval * then) -{ -struct timeval diff; - -timesince(&diff, then); -return string_timediff(&diff); -} - /******************************************************************************/ @@ -1124,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 @@ -1136,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" "); @@ -1183,8 +1175,7 @@ if (addr->router) g = string_append(g, 2, US" T=", addr->transport->name); if (LOGGING(delivery_size)) - g = string_append(g, 2, US" S=", - string_sprintf("%d", transport_count)); + g = string_fmt_append(g, " S=%d", transport_count); /* Local delivery */ @@ -1204,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 @@ -1219,7 +1210,7 @@ else #endif } -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS g = d_tlslog(g, addr); #endif @@ -1234,6 +1225,16 @@ else } } + if (LOGGING(pipelining)) + { + if (testflag(addr, af_pipelining)) + g = string_catn(g, US" L", 2); +#ifndef DISABLE_PIPE_CONNECT + if (testflag(addr, af_early_pipe)) + g = string_catn(g, US"*", 1); +#endif + } + #ifndef DISABLE_PRDR if (testflag(addr, af_prdr_used)) g = string_catn(g, US" PRDR", 5); @@ -1250,12 +1251,11 @@ if ( LOGGING(smtp_confirmation) && (addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0) ) { - unsigned i; unsigned lim = big_buffer_size < 1024 ? big_buffer_size : 1024; uschar *p = big_buffer; uschar *ss = addr->message; *p++ = '\"'; - for (i = 0; i < lim && ss[i] != 0; i++) /* limit logged amount */ + for (int i = 0; i < lim && ss[i] != 0; i++) /* limit logged amount */ { if (ss[i] == '\"' || ss[i] == '\\') *p++ = '\\'; /* quote \ and " */ *p++ = ss[i]; @@ -1272,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. */ @@ -1296,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. */ @@ -1322,13 +1317,12 @@ if (driver_name) { if (driver_kind[1] == 't' && addr->router) g = string_append(g, 2, US" R=", addr->router->name); - g = string_cat(g, string_sprintf(" %c=%s", toupper(driver_kind[1]), driver_name)); + g = string_fmt_append(g, " %c=%s", toupper(driver_kind[1]), driver_name); } else if (driver_kind) g = string_append(g, 2, US" ", driver_kind); -/*XXX need an s+s+p sprintf */ -g = string_cat(g, string_sprintf(" defer (%d)", addr->basic_errno)); +g = string_fmt_append(g, " defer (%d)", addr->basic_errno); if (addr->basic_errno > 0) g = string_append(g, 2, US": ", @@ -1342,11 +1336,13 @@ if (addr->host_used) if (LOGGING(outgoing_port)) { int port = addr->host_used->port; - g = string_append(g, 2, - US":", port == PORT_NONE ? US"25" : string_sprintf("%d", port)); + g = string_fmt_append(g, ":%d", port == PORT_NONE ? 25 : port); } } +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); @@ -1355,7 +1351,7 @@ if (addr->message) /* Log the deferment in the message log, but don't clutter it up with retry-time defers after the first delivery attempt. */ -if (deliver_firsttime || addr->basic_errno > ERRNO_RETRY_BASE) +if (f.deliver_firsttime || addr->basic_errno > ERRNO_RETRY_BASE) deliver_msglog("%s %s\n", now, g->s); /* Write the main log and reset the store. @@ -1377,8 +1373,18 @@ 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 +later so avoid doing it here. */ + +if ( !addr->prop.ignore_error + && !(addr->dsn_flags & (rf_dsnflags & ~rf_notify_failure)) + ) + msg_event_raise(US"msg:fail:delivery", addr); +#endif /* Build up the log line for the message and main logs */ @@ -1406,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 @@ -1416,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, @@ -1428,10 +1437,6 @@ else log_write(0, LOG_MAIN, "** %s", g->s); -#ifndef DISABLE_EVENT -msg_event_raise(US"msg:fail:delivery", addr); -#endif - store_reset(reset_point); return; } @@ -1475,7 +1480,7 @@ if (driver_type == EXIM_DTYPE_TRANSPORT) { driver_name = addr->transport->name; driver_kind = US" transport"; - disable_logging = addr->transport->disable_logging; + f.disable_logging = addr->transport->disable_logging; } else driver_kind = US"transporting"; } @@ -1485,7 +1490,7 @@ else if (driver_type == EXIM_DTYPE_ROUTER) { driver_name = addr->router->name; driver_kind = US" router"; - disable_logging = addr->router->disable_logging; + f.disable_logging = addr->router->disable_logging; } else driver_kind = US"routing"; } @@ -1610,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; @@ -1626,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; @@ -1659,7 +1666,7 @@ else if (result == DEFER || result == PANIC) if (addr->special_action == SPECIAL_FREEZE) { - deliver_freeze = TRUE; + f.deliver_freeze = TRUE; deliver_frozen_at = time(NULL); update_spool = TRUE; } @@ -1667,7 +1674,7 @@ else if (result == DEFER || result == PANIC) /* If doing a 2-stage queue run, we skip writing to either the message log or the main log for SMTP defers. */ - if (!queue_2stage || addr->basic_errno != 0) + if (!f.queue_2stage || addr->basic_errno != 0) deferral_log(addr, now, logflags, driver_name, driver_kind); } @@ -1700,10 +1707,10 @@ else { frozen_info = addr->special_action == SPECIAL_FREEZE ? US"" - : sender_local && !local_error_message + : f.sender_local && !f.local_error_message ? US" (message created with -f <>)" : US" (delivery error message)"; - deliver_freeze = TRUE; + f.deliver_freeze = TRUE; deliver_frozen_at = time(NULL); update_spool = TRUE; @@ -1728,7 +1735,7 @@ else /* Ensure logging is turned on again in all cases */ -disable_logging = FALSE; +f.disable_logging = FALSE; } @@ -1757,22 +1764,20 @@ Returns: nothing static void common_error(BOOL logit, address_item *addr, int code, uschar *format, ...) { -address_item *addr2; addr->basic_errno = code; if (format) { va_list ap; - uschar buffer[512]; + gstring * g; + va_start(ap, format); - if (!string_vformat(buffer, sizeof(buffer), CS format, ap)) - log_write(0, LOG_MAIN|LOG_PANIC_DIE, - "common_error expansion was longer than " SIZE_T_FMT, sizeof(buffer)); + g = string_vformat(NULL, SVFMT_EXTEND|SVFMT_REBUFFER, CS format, ap); va_end(ap); - addr->message = string_copy(buffer); + addr->message = string_from_gstring(g); } -for (addr2 = addr->next; addr2; addr2 = addr2->next) +for (address_item * addr2 = addr->next; addr2; addr2 = addr2->next) { addr2->basic_errno = code; addr2->message = addr->message; @@ -1802,9 +1807,8 @@ Returns: TRUE if the uid is on the list static BOOL check_never_users(uid_t uid, uid_t *nusers) { -int i; if (!nusers) return FALSE; -for (i = 1; i <= (int)(nusers[0]); i++) if (nusers[i] == uid) return TRUE; +for (int i = 1; i <= (int)(nusers[0]); i++) if (nusers[i] == uid) return TRUE; return FALSE; } @@ -2030,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", @@ -2092,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. @@ -2154,7 +2158,7 @@ if (tp->return_path) uschar *new_return_path = expand_string(tp->return_path); if (!new_return_path) { - if (!expand_string_forcedfail) + if (!f.expand_string_forcedfail) { common_error(TRUE, addr, ERRNO_EXPANDFAIL, US"Failed to expand return path \"%s\" in %s transport: %s", @@ -2268,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; @@ -2349,9 +2353,8 @@ if ((pid = fork()) == 0) DEBUG(D_deliver) { - address_item *batched; debug_printf(" home=%s current=%s\n", deliver_home, working_directory); - for (batched = addr->next; batched; batched = batched->next) + for (address_item * batched = addr->next; batched; batched = batched->next) debug_printf("additional batched address: %s\n", batched->address); } @@ -2415,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 *) @@ -2483,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 *)); @@ -2553,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 (running_in_test_harness) millisleep(300); + testharness_pause_ms(300); DEBUG(D_deliver) debug_printf("journalling %s", big_buffer); len = Ustrlen(big_buffer); @@ -2616,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)) @@ -2713,7 +2716,7 @@ while (addr_local) struct timeval deliver_time; address_item *addr2, *addr3, *nextaddr; int logflags = LOG_MAIN; - int logchar = dont_deliver? '*' : '='; + int logchar = f.dont_deliver? '*' : '='; transport_instance *tp; uschar * serialize_key = NULL; @@ -2731,10 +2734,10 @@ while (addr_local) if (!(tp = addr->transport)) { logflags |= LOG_PANIC; - disable_logging = FALSE; /* Jic */ + 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; } @@ -2749,7 +2752,7 @@ while (addr_local) /* There are weird cases where logging is disabled */ - disable_logging = tp->disable_logging; + f.disable_logging = tp->disable_logging; /* Check for batched addresses and possible amalgamation. Skip all the work if either batch_max <= 1 or there aren't any other addresses for local @@ -2885,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"); @@ -2936,7 +2939,7 @@ while (addr_local) retry_record->expired); } - if (queue_running && !deliver_force) + if (f.queue_running && !f.deliver_force) { ok = (now - retry_record->time_stamp > retry_data_expire) || (now >= retry_record->next_try) @@ -3045,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; @@ -3089,12 +3092,7 @@ while (addr_local) DEBUG(D_deliver|D_transport) debug_printf("%s shadow transport returned %s for %s\n", - stp->name, - sresult == OK ? "OK" : - sresult == DEFER ? "DEFER" : - sresult == FAIL ? "FAIL" : - sresult == PANIC ? "PANIC" : "?", - shadow_addr->address); + stp->name, rc_to_string(sresult), shadow_addr->address); } DEBUG(D_deliver|D_transport) @@ -3123,12 +3121,7 @@ while (addr_local) DEBUG(D_deliver|D_transport) debug_printf("%s transport returned %s for %s\n", - tp->name, - result == OK ? "OK" : - result == DEFER ? "DEFER" : - result == FAIL ? "FAIL" : - result == PANIC ? "PANIC" : "?", - addr2->address); + tp->name, rc_to_string(result), addr2->address); /* If there is a retry_record, or if delivery is deferred, build a retry item for setting a new retry time or deleting the old retry record from @@ -3147,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 @@ -3261,9 +3250,8 @@ while ( *aptr DEBUG(D_deliver) { - address_item *addr; debug_printf("remote addresses after sorting:\n"); - for (addr = addr_remote; addr; addr = addr->next) + for (address_item * addr = addr_remote; addr; addr = addr->next) debug_printf(" %s\n", addr->address); } } @@ -3293,8 +3281,8 @@ small items (less than PIPE_BUF, which seems to be at least 512 in any Unix and often bigger) so even if we are reading while the subprocess is still going, we should never have only a partial item in the buffer. -hs12: This assumption is not true anymore, since we got quit large items (certificate -information and such) +hs12: This assumption is not true anymore, since we get quite large items (certificate +information and such). Argument: poffset the offset of the parlist item @@ -3454,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++; @@ -3495,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); @@ -3533,7 +3523,7 @@ while (!done) } while (*ptr++); break; -#endif /*SUPPORT_TLS*/ +#endif /*DISABLE_TLS*/ case 'C': /* client authenticator information */ switch (*subid) @@ -3551,6 +3541,16 @@ while (!done) break; #endif + case 'L': + switch (*subid) + { +#ifndef DISABLE_PIPE_CONNECT + case 2: setflag(addr, af_early_pipe); /*FALLTHROUGH*/ +#endif + case 1: setflag(addr, af_pipelining); break; + } + break; + case 'K': setflag(addr, af_chunking_used); break; @@ -3558,6 +3558,7 @@ while (!done) case 'T': setflag(addr, af_tcp_fastopen_conn); if (*subid > '0') setflag(addr, af_tcp_fastopen); + if (*subid > '1') setflag(addr, af_tcp_fastopen_data); break; case 'D': @@ -3613,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; @@ -3626,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); @@ -3757,12 +3758,10 @@ static void remote_post_process(address_item *addr, int logflags, uschar *msg, BOOL fallback) { -host_item *h; - /* If any host addresses were found to be unusable, add them to the unusable tree so that subsequent deliveries don't try them. */ -for (h = addr->host_list; h; h = h->next) +for (host_item * h = addr->host_list; h; h = h->next) if (h->address) if (h->status >= hstatus_unusable) tree_add_unusable(h); @@ -4196,7 +4195,6 @@ static BOOL do_remote_deliveries(BOOL fallback) { int parmax; -int delivery_count; int poffset; parcount = 0; /* Number of executing subprocesses */ @@ -4213,14 +4211,14 @@ 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; } /* Now loop for each remote delivery */ -for (delivery_count = 0; addr_remote; delivery_count++) +for (int delivery_count = 0; addr_remote; delivery_count++) { pid_t pid; uid_t uid; @@ -4251,7 +4249,7 @@ for (delivery_count = 0; addr_remote; delivery_count++) if (!(tp = addr->transport)) { - disable_logging = FALSE; /* Jic */ + f.disable_logging = FALSE; /* Jic */ panicmsg = US"No transport set by router"; goto panic_continue; } @@ -4277,6 +4275,10 @@ for (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, @@ -4355,6 +4357,11 @@ for (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 @@ -4446,7 +4453,7 @@ for (delivery_count = 0; addr_remote; delivery_count++) uschar *new_return_path = expand_string(tp->return_path); if (new_return_path) return_path = new_return_path; - else if (!expand_string_forcedfail) + else if (!f.expand_string_forcedfail) { panicmsg = string_sprintf("Failed to expand return path \"%s\": %s", tp->return_path, expand_string_message); @@ -4497,10 +4504,11 @@ for (delivery_count = 0; addr_remote; delivery_count++) we must check that the continue host is on the list. Otherwise, the host is set in the transport. */ - continue_more = FALSE; /* In case got set for the last lot */ + f.continue_more = FALSE; /* In case got set for the last lot */ 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 @@ -4519,9 +4527,8 @@ for (delivery_count = 0; addr_remote; delivery_count++) && addr->host_list ) { - host_item * h; ok = FALSE; - for (h = addr->host_list; h; h = h->next) + for (host_item * h = addr->host_list; h; h = h->next) if (Ustrcmp(h->name, continue_hostname) == 0) /*XXX should also check port here */ { ok = TRUE; break; } @@ -4572,13 +4579,10 @@ for (delivery_count = 0; addr_remote; delivery_count++) connected to is too hard to manage. Perhaps we need a finer-grain interface to the transport. */ - for (next = addr_remote; next && !continue_more; next = next->next) - { - host_item *h; - for (h = next->host_list; h; h = h->next) + for (next = addr_remote; next && !f.continue_more; next = next->next) + for (host_item * h = next->host_list; h; h = h->next) if (Ustrcmp(h->name, continue_hostname) == 0) - { continue_more = TRUE; break; } - } + { f.continue_more = TRUE; break; } } /* The transports set up the process info themselves as they may connect @@ -4656,33 +4660,28 @@ 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; - DEBUG(D_deliver) debug_selector |= D_pid; // hs12 /* Setting this global in the subprocess means we need never clear it */ transport_name = tp->name; /* There are weird circumstances in which logging is disabled */ - disable_logging = tp->disable_logging; + f.disable_logging = tp->disable_logging; /* 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 predictable settings for each delivery process, so do something explicit here rather they rely on the fixed reset in the random number function. */ - random_seed = running_in_test_harness ? 42 + 2*delivery_count : 0; + random_seed = f.running_in_test_harness ? 42 + 2*delivery_count : 0; /* Set close-on-exec on the pipe so that it doesn't get passed on to a new process that may be forked to do another delivery down the same @@ -4781,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; @@ -4811,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; @@ -4820,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; @@ -4833,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) { @@ -4856,19 +4857,29 @@ all pipes, so I do not see a reason to use non-blocking IO here rmt_dlv_checked_write(fd, 'P', '0', NULL, 0); #endif + if (testflag(addr, af_pipelining)) +#ifndef DISABLE_PIPE_CONNECT + if (testflag(addr, af_early_pipe)) + rmt_dlv_checked_write(fd, 'L', '2', NULL, 0); + else +#endif + rmt_dlv_checked_write(fd, 'L', '1', NULL, 0); + if (testflag(addr, af_chunking_used)) rmt_dlv_checked_write(fd, 'K', '0', NULL, 0); if (testflag(addr, af_tcp_fastopen_conn)) rmt_dlv_checked_write(fd, 'T', - testflag(addr, af_tcp_fastopen) ? '1' : '0', NULL, 0); + testflag(addr, af_tcp_fastopen) ? testflag(addr, af_tcp_fastopen_data) + ? '2' : '1' : '0', + NULL, 0); memcpy(big_buffer, &addr->dsn_aware, sizeof(addr->dsn_aware)); rmt_dlv_checked_write(fd, 'D', '0', big_buffer, sizeof(addr->dsn_aware)); /* 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; @@ -4927,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); @@ -4987,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 @@ -5031,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 (running_in_test_harness) millisleep(500); + else testharness_pause_ms(600); continue; @@ -5092,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++; @@ -5135,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; @@ -5219,15 +5231,12 @@ static int continue_closedown(void) { if (continue_transport) - { - transport_instance *t; - for (t = transports; t; t = t->next) + for (transport_instance * t = transports; t; t = t->next) if (Ustrcmp(t->name, continue_transport) == 0) { if (t->info->closedown) (t->info->closedown)(t); break; } - } return DELIVER_NOT_ATTEMPTED; } @@ -5335,7 +5344,7 @@ static void print_address_error(address_item *addr, FILE *f, 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; @@ -5384,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) @@ -5396,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); } @@ -5464,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 * *************************************************/ @@ -5515,8 +5599,13 @@ int process_recipients = RECIP_ACCEPT; open_db dbblock; open_db *dbm_file; extern int acl_where; +uschar *info; -uschar *info = queue_run_pid == (pid_t)0 +#ifdef MEASURE_TIMING +report_time_since(×tamp_startup, US"delivery start"); /* testcase 0022, 2100 */ +#endif + +info = queue_run_pid == (pid_t)0 ? string_sprintf("delivering %s", id) : string_sprintf("delivering %s (queue run pid %d)", id, queue_run_pid); @@ -5556,8 +5645,9 @@ message size. This use of strcpy() is OK because the length id is checked when it is obtained from a command line (the -M or -q options), and otherwise it is known to be a valid message id. */ -Ustrcpy(message_id, id); -deliver_force = forced; +if (id != message_id) + Ustrcpy(message_id, id); +f.deliver_force = forced; return_count = 0; message_size = 0; @@ -5705,7 +5795,7 @@ Otherwise it might be needed again. */ can happen, but in the default situation, unless forced, no delivery is attempted. */ -if (deliver_freeze) +if (f.deliver_freeze) { #ifdef SUPPORT_MOVE_FROZEN_MESSAGES /* Moving to another directory removes the message from Exim's view. Other @@ -5748,8 +5838,8 @@ if (deliver_freeze) || auto_thaw <= 0 || now <= deliver_frozen_at + auto_thaw ) - && ( !forced || !deliver_force_thaw - || !admin_user || continue_hostname + && ( !forced || !f.deliver_force_thaw + || !f.admin_user || continue_hostname ) ) { (void)close(deliver_datafile); @@ -5763,7 +5853,7 @@ if (deliver_freeze) if (forced) { - deliver_manual_thaw = TRUE; + f.deliver_manual_thaw = TRUE; log_write(0, LOG_MAIN, "Unfrozen by forced delivery"); } else log_write(0, LOG_MAIN, "Unfrozen by auto-thaw"); @@ -5771,7 +5861,7 @@ if (deliver_freeze) /* We get here if any of the rules for unfreezing have triggered. */ - deliver_freeze = FALSE; + f.deliver_freeze = FALSE; update_spool = TRUE; } @@ -5841,13 +5931,11 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) ugid.uid_set = ugid.gid_set = TRUE; } else - { ugid.uid_set = ugid.gid_set = FALSE; - } return_path = sender_address; - enable_dollar_recipients = TRUE; /* Permit $recipients in system filter */ - system_filtering = TRUE; + f.enable_dollar_recipients = TRUE; /* Permit $recipients in system filter */ + f.system_filtering = TRUE; /* Any error in the filter file causes a delivery to be abandoned. */ @@ -5895,8 +5983,8 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) /* Reset things. If the filter message is an empty string, which can happen for a filter "fail" or "freeze" command with no text, reset it to NULL. */ - system_filtering = FALSE; - enable_dollar_recipients = FALSE; + f.system_filtering = FALSE; + f.enable_dollar_recipients = FALSE; if (filter_message && filter_message[0] == 0) filter_message = NULL; /* Save the values of the system filter variables so that user filters @@ -5919,9 +6007,9 @@ else if (system_filter && process_recipients != RECIP_FAIL_TIMEOUT) unset "delivered", which is forced by the "freeze" command to make -bF work properly. */ - else if (rc == FF_FREEZE && !deliver_manual_thaw) + else if (rc == FF_FREEZE && !f.deliver_manual_thaw) { - deliver_freeze = TRUE; + f.deliver_freeze = TRUE; deliver_frozen_at = time(NULL); process_recipients = RECIP_DEFER; frozen_info = string_sprintf(" by the system filter%s%s", @@ -5988,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 @@ -6063,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 @@ -6147,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) @@ -6158,19 +6250,19 @@ if (process_recipients != RECIP_IGNORE) /* RECIP_DEFER is set when a system filter freezes a message. */ case RECIP_DEFER: - new->next = addr_defer; - addr_defer = new; - break; + new->next = addr_defer; + addr_defer = new; + break; /* RECIP_FAIL_FILTER is set when a system filter has obeyed a "fail" command. */ case RECIP_FAIL_FILTER: - new->message = - filter_message ? filter_message : US"delivery cancelled"; - setflag(new, af_pass_message); - goto RECIP_QUEUE_FAILED; /* below */ + new->message = + filter_message ? filter_message : US"delivery cancelled"; + setflag(new, af_pass_message); + goto RECIP_QUEUE_FAILED; /* below */ /* RECIP_FAIL_TIMEOUT is set when a message is frozen, but is older @@ -6180,15 +6272,15 @@ if (process_recipients != RECIP_IGNORE) been logged. */ case RECIP_FAIL_TIMEOUT: - new->message = US"delivery cancelled; message timed out"; - goto RECIP_QUEUE_FAILED; /* below */ + new->message = US"delivery cancelled; message timed out"; + goto RECIP_QUEUE_FAILED; /* below */ /* RECIP_FAIL is set when -Mg has been used. */ case RECIP_FAIL: - new->message = US"delivery cancelled by administrator"; - /* Fall through */ + new->message = US"delivery cancelled by administrator"; + /* Fall through */ /* Common code for the failure cases above. If this is not a bounce message, put the address on the failed list so that it is used to @@ -6196,11 +6288,11 @@ if (process_recipients != RECIP_IGNORE) The incident has already been logged. */ RECIP_QUEUE_FAILED: - if (sender_address[0] != 0) - { - new->next = addr_failed; - addr_failed = new; - } + if (sender_address[0]) + { + new->next = addr_failed; + addr_failed = new; + } break; @@ -6209,44 +6301,49 @@ if (process_recipients != RECIP_IGNORE) is a bounce message, it will get frozen. */ case RECIP_FAIL_LOOP: - new->message = US"Too many \"Received\" headers - suspected mail loop"; - post_process_one(new, FAIL, LOG_MAIN, EXIM_DTYPE_ROUTER, 0); - break; + new->message = US"Too many \"Received\" headers - suspected mail loop"; + post_process_one(new, FAIL, LOG_MAIN, EXIM_DTYPE_ROUTER, 0); + break; /* Value should be RECIP_ACCEPT; take this as the safe default. */ default: - if (!addr_new) addr_new = new; else addr_last->next = new; - addr_last = new; - break; + if (!addr_new) addr_new = new; else addr_last->next = new; + addr_last = new; + break; } #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; + uschar * addr = new->address, * errmsg = NULL; + int start, end, dom; - deliver_localpart = expand_string( - string_sprintf("${local_part:%s}", new->address)); - deliver_domain = expand_string( - string_sprintf("${domain:%s}", new->address)); + if (!parse_extract_address(addr, &errmsg, &start, &end, &dom, TRUE)) + log_write(0, LOG_MAIN|LOG_PANIC, + "failed to parse address '%.100s': %s\n", addr, errmsg); + else + { + deliver_localpart = + string_copyn(addr+start, dom ? (dom-1) - start : end - start); + deliver_domain = dom ? CUS string_copyn(addr+dom, end - dom) : CUS""; - (void) event_raise(event_action, - US"msg:fail:internal", new->message); + event_raise(event_action, US"msg:fail:internal", new->message); - deliver_localpart = save_local; - deliver_domain = save_domain; + deliver_localpart = save_local; + deliver_domain = save_domain; + } } #endif } DEBUG(D_deliver) { - address_item *p; debug_printf("Delivery address list:\n"); - for (p = addr_new; p; p = p->next) + for (address_item * p = addr_new; p; p = p->next) debug_printf(" %s %s\n", p->address, p->onetime_parent ? p->onetime_parent : US""); } @@ -6296,7 +6393,7 @@ deliver_out_buffer = store_malloc(DELIVER_OUT_BUFFER_SIZE); . If new addresses have been generated by the routers, da capo. */ -header_rewritten = FALSE; /* No headers rewritten yet */ +f.header_rewritten = FALSE; /* No headers rewritten yet */ while (addr_new) /* Loop until all addresses dealt with */ { address_item *addr, *parent; @@ -6304,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"); @@ -6366,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))) { @@ -6560,13 +6655,21 @@ while (addr_new) /* Loop until all addresses dealt with */ if ( domain_retry_record && now - domain_retry_record->time_stamp > retry_data_expire ) + { + DEBUG(D_deliver|D_retry) + debug_printf("domain retry record present but expired\n"); domain_retry_record = NULL; /* Ignore if too old */ + } address_retry_record = dbfn_read(dbm_file, addr->address_retry_key); if ( address_retry_record && now - address_retry_record->time_stamp > retry_data_expire ) + { + DEBUG(D_deliver|D_retry) + debug_printf("address retry record present but expired\n"); address_retry_record = NULL; /* Ignore if too old */ + } if (!address_retry_record) { @@ -6575,7 +6678,11 @@ while (addr_new) /* Loop until all addresses dealt with */ address_retry_record = dbfn_read(dbm_file, altkey); if ( address_retry_record && now - address_retry_record->time_stamp > retry_data_expire) + { + DEBUG(D_deliver|D_retry) + debug_printf("address retry record present but expired\n"); address_retry_record = NULL; /* Ignore if too old */ + } } } else @@ -6584,9 +6691,18 @@ while (addr_new) /* Loop until all addresses dealt with */ DEBUG(D_deliver|D_retry) { if (!domain_retry_record) - debug_printf("no domain retry record\n"); + debug_printf("no domain retry record\n"); + else + debug_printf("have domain retry record; next_try = now%+d\n", + f.running_in_test_harness ? 0 : + (int)(domain_retry_record->next_try - now)); + if (!address_retry_record) - debug_printf("no address retry record\n"); + debug_printf("no address retry record\n"); + else + debug_printf("have address retry record; next_try = now%+d\n", + f.running_in_test_harness ? 0 : + (int)(address_retry_record->next_try - now)); } /* If we are sending a message down an existing SMTP connection, we must @@ -6608,6 +6724,9 @@ while (addr_new) /* Loop until all addresses dealt with */ addr->message = US"reusing SMTP connection skips previous routing defer"; addr->basic_errno = ERRNO_RRETRY; (void)post_process_one(addr, DEFER, LOG_MAIN, EXIM_DTYPE_ROUTER, 0); + + addr->message = domain_retry_record->text; + setflag(addr, af_pass_message); } /* If we are in a queue run, defer routing unless there is no retry data or @@ -6642,7 +6761,7 @@ while (addr_new) /* Loop until all addresses dealt with */ which keep the retry record fresh, which can lead to us perpetually deferring messages. */ - else if ( ( queue_running && !deliver_force + else if ( ( f.queue_running && !f.deliver_force || continue_hostname ) && ( ( domain_retry_record @@ -6661,6 +6780,16 @@ while (addr_new) /* Loop until all addresses dealt with */ addr->message = US"retry time not reached"; addr->basic_errno = ERRNO_RRETRY; (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 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. */ + + addr->message = domain_retry_record + ? domain_retry_record->text : address_retry_record->text; + setflag(addr, af_pass_message); } /* The domain is OK for routing. Remember if retry data exists so it @@ -6686,7 +6815,7 @@ while (addr_new) /* Loop until all addresses dealt with */ those domains. During queue runs, queue_domains is forced to be unset. Optimize by skipping this pass through the addresses if nothing is set. */ - if (!deliver_force && queue_domains) + if (!f.deliver_force && queue_domains) { address_item *okaddr = NULL; while (addr_route) @@ -6743,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 @@ -6856,22 +6985,21 @@ while (addr_new) /* Loop until all addresses dealt with */ DEBUG(D_deliver|D_retry|D_route) { - address_item *p; debug_printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); debug_printf("After routing:\n Local deliveries:\n"); - for (p = addr_local; p; p = p->next) + for (address_item * p = addr_local; p; p = p->next) debug_printf(" %s\n", p->address); debug_printf(" Remote deliveries:\n"); - for (p = addr_remote; p; p = p->next) + for (address_item * p = addr_remote; p; p = p->next) debug_printf(" %s\n", p->address); debug_printf(" Failed addresses:\n"); - for (p = addr_failed; p; p = p->next) + for (address_item * p = addr_failed; p; p = p->next) debug_printf(" %s\n", p->address); debug_printf(" Deferred addresses:\n"); - for (p = addr_defer; p; p = p->next) + for (address_item * p = addr_defer; p; p = p->next) debug_printf(" %s\n", p->address); } @@ -6951,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; @@ -6980,18 +7118,18 @@ remember them for all subsequent deliveries. This can be delayed till later if there is only address to be delivered - if it succeeds the spool write need not happen. */ -if ( header_rewritten +if ( f.header_rewritten && ( addr_local && (addr_local->next || addr_remote) || addr_remote && addr_remote->next ) ) { /* Panic-dies on error */ (void)spool_write_header(message_id, SW_DELIVERING, NULL); - header_rewritten = FALSE; + f.header_rewritten = FALSE; } -/* If there are any deliveries to be and we do not already have the journal +/* If there are any deliveries to do and we do not already have the journal file, create it. This is used to record successful deliveries as soon as possible after each delivery is known to be complete. A file opened with O_APPEND is used so that several processes can run simultaneously. @@ -7022,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) @@ -7067,13 +7205,13 @@ if (addr_local) DEBUG(D_deliver|D_transport) debug_printf(">>>>>>>>>>>>>>>> Local deliveries >>>>>>>>>>>>>>>>\n"); do_local_deliveries(); - disable_logging = FALSE; + f.disable_logging = FALSE; } /* If queue_run_local is set, we do not want to attempt any remote deliveries, so just queue them all. */ -if (queue_run_local) +if (f.queue_run_local) while (addr_remote) { address_item *addr = addr_remote; @@ -7094,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 @@ -7125,7 +7263,7 @@ if (addr_remote) if (remote_sort_domains) sort_remote_deliveries(); do_remote_deliveries(TRUE); } - disable_logging = FALSE; + f.disable_logging = FALSE; } @@ -7153,8 +7291,8 @@ if (mua_wrapper) { if (addr_defer) { - address_item *addr, *nextaddr; - for (addr = addr_defer; addr; addr = nextaddr) + address_item * nextaddr; + for (address_item * addr = addr_defer; addr; addr = nextaddr) { log_write(0, LOG_MAIN, "** %s mua_wrapper forced failure for deferred " "delivery", addr->address); @@ -7204,45 +7342,43 @@ retry cutoff time has expired for all alternative destinations. Bypass the updating of the database if the -N flag is set, which is a debugging thing that prevents actual delivery. */ -else if (!dont_deliver) +else if (!f.dont_deliver) retry_update(&addr_defer, &addr_failed, &addr_succeed); /* 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_dsnflags - && 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 @@ -7255,27 +7391,27 @@ 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"); } else /* Creation of child succeeded */ { - FILE *f = fdopen(fd, "wb"); + FILE * f = fdopen(fd, "wb"); /* header only as required by RFC. only failure DSN needs to honor RET=FULL */ uschar * bound; 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()); @@ -7284,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" @@ -7296,17 +7435,14 @@ 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) == 1 - ? "via non DSN router" - : addr_dsntmp->dsn_aware == dsn_support_no - ? "to non-DSN-aware mailer" - : "via non \"Remote SMTP\" router" + 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" ); fprintf(f, "--%s\n" @@ -7324,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) == 1 ? "DSN" : "SMTP"); + a->dsn_flags & rf_dsnlasthop ? "DSN" : "SMTP"); } fprintf(f, "--%s\nContent-type: text/rfc822-headers\n\n", bound); @@ -7352,8 +7482,10 @@ if (addr_senddsn) /* Write the original email out */ - tctx.u.fd = fileno(f); + tctx.u.fd = fd; tctx.options = topt_add_return_path | topt_no_body; + /*XXX hmm, FALSE(fail) retval ignored. + Could error for any number of reasons, and they are not handled. */ transport_write_message(&tctx, 0); fflush(f); @@ -7384,9 +7516,9 @@ while (addr_failed) /* There are weird cases when logging is disabled in the transport. However, there may not be a transport (address failed by a router). */ - disable_logging = FALSE; + f.disable_logging = FALSE; if (addr_failed->transport) - disable_logging = addr_failed->transport->disable_logging; + f.disable_logging = addr_failed->transport->disable_logging; DEBUG(D_deliver) debug_printf("processing failed address %s\n", addr_failed->address); @@ -7422,19 +7554,24 @@ while (addr_failed) mark the recipient done. */ if ( addr_failed->prop.ignore_error - || ( addr_failed->dsn_flags & rf_dsnflags - && (addr_failed->dsn_flags & rf_notify_failure) != rf_notify_failure - ) ) + || addr_failed->dsn_flags & rf_dsnflags + && !(addr_failed->dsn_flags & rf_notify_failure) + ) { addr = addr_failed; addr_failed = addr->next; if (addr->return_filename) Uunlink(addr->return_filename); - log_write(0, LOG_MAIN, "%s%s%s%s: error ignored", +#ifndef DISABLE_EVENT + msg_event_raise(US"msg:fail:delivery", addr); +#endif + 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); @@ -7455,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)); @@ -7468,8 +7605,8 @@ while (addr_failed) int filecount = 0; int rcount = 0; uschar *bcc, *emf_text; - FILE *f = fdopen(fd, "wb"); - FILE *emf = NULL; + FILE * fp = fdopen(fd, "wb"); + FILE * emf = NULL; BOOL to_sender = strcmpic(sender_address, bounce_recipient) == 0; int max = (bounce_return_size_limit/DELIVER_IN_BUFFER_SIZE + 1) * DELIVER_IN_BUFFER_SIZE; @@ -7507,10 +7644,10 @@ while (addr_failed) if (testflag(addr, af_hide_child)) continue; if (rcount >= 50) { - fprintf(f, "\n"); + fprintf(fp, "\n"); rcount = 0; } - fprintf(f, "%s%s", + fprintf(fp, "%s%s", rcount++ == 0 ? "X-Failed-Recipients: " : ",\n ", @@ -7518,20 +7655,21 @@ while (addr_failed) ? string_printing(addr->parent->address) : string_printing(addr->address)); } - if (rcount > 0) fprintf(f, "\n"); + if (rcount > 0) fprintf(fp, "\n"); /* Output the standard headers */ if (errors_reply_to) - fprintf(f, "Reply-To: %s\n", errors_reply_to); - fprintf(f, "Auto-Submitted: auto-replied\n"); - moan_write_from(f); - fprintf(f, "To: %s\n", bounce_recipient); + fprintf(fp, "Reply-To: %s\n", errors_reply_to); + 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()); - fprintf(f, "Content-Type: multipart/report;" + fprintf(fp, "Content-Type: multipart/report;" " report-type=delivery-status; boundary=%s\n" "MIME-Version: 1.0\n", bound); @@ -7540,53 +7678,52 @@ 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. */ if ((bcc = moan_check_errorcopy(bounce_recipient))) - fprintf(f, "Bcc: %s\n", bcc); + fprintf(fp, "Bcc: %s\n", bcc); /* The texts for the message can be read from a template file; if there isn't one, or if it is too short, built-in texts are used. The first emf text is a Subject: and any other headers. */ if ((emf_text = next_emf(emf, US"header"))) - fprintf(f, "%s\n", emf_text); + fprintf(fp, "%s\n", emf_text); else - fprintf(f, "Subject: Mail delivery failed%s\n\n", + fprintf(fp, "Subject: Mail delivery failed%s\n\n", to_sender? ": returning message to sender" : ""); /* output human readable part as text/plain section */ - fprintf(f, "--%s\n" + fprintf(fp, "--%s\n" "Content-type: text/plain; charset=us-ascii\n\n", bound); if ((emf_text = next_emf(emf, US"intro"))) - fprintf(f, "%s", CS emf_text); + fprintf(fp, "%s", CS emf_text); else { - fprintf(f, + fprintf(fp, /* This message has been reworded several times. It seems to be confusing to somebody, however it is worded. I have retreated to the original, simple wording. */ "This message was created automatically by mail delivery software.\n"); if (bounce_message_text) - fprintf(f, "%s", CS bounce_message_text); + fprintf(fp, "%s", CS bounce_message_text); if (to_sender) - fprintf(f, + fprintf(fp, "\nA message that you sent could not be delivered to one or more of its\n" "recipients. This is a permanent error. The following address(es) failed:\n"); else - fprintf(f, + fprintf(fp, "\nA message sent by\n\n <%s>\n\n" "could not be delivered to one or more of its recipients. The following\n" "address(es) failed:\n", sender_address); } - fputc('\n', f); + fputc('\n', fp); /* Process the addresses, leaving them on the msgchain if they have a file name for a return message. (There has already been a check in @@ -7597,12 +7734,12 @@ wording. */ paddr = &msgchain; for (addr = msgchain; addr; addr = *paddr) { - if (print_address_information(addr, f, US" ", US"\n ", US"")) - print_address_error(addr, f, US""); + if (print_address_information(addr, fp, US" ", US"\n ", US"")) + print_address_error(addr, fp, US""); /* End the final line for the address */ - fputc('\n', f); + fputc('\n', fp); /* Leave on msgchain if there's a return file. */ @@ -7623,7 +7760,7 @@ wording. */ } } - fputc('\n', f); + fputc('\n', fp); /* Get the next text, whether we need it or not, so as to be positioned for the one after. */ @@ -7642,9 +7779,9 @@ wording. */ address_item *nextaddr; if (emf_text) - fprintf(f, "%s", CS emf_text); + fprintf(fp, "%s", CS emf_text); else - fprintf(f, + fprintf(fp, "The following text was generated during the delivery " "attempt%s:\n", (filecount > 1)? "s" : ""); @@ -7655,24 +7792,24 @@ wording. */ /* List all the addresses that relate to this file */ - fputc('\n', f); + fputc('\n', fp); while(addr) /* Insurance */ { - print_address_information(addr, f, US"------ ", US"\n ", + print_address_information(addr, fp, US"------ ", US"\n ", US" ------\n"); if (addr->return_filename) break; addr = addr->next; } - fputc('\n', f); + fputc('\n', fp); /* Now copy the file */ if (!(fm = Ufopen(addr->return_filename, "rb"))) - fprintf(f, " +++ Exim error... failed to open text file: %s\n", + fprintf(fp, " +++ Exim error... failed to open text file: %s\n", strerror(errno)); else { - while ((ch = fgetc(fm)) != EOF) fputc(ch, f); + while ((ch = fgetc(fm)) != EOF) fputc(ch, fp); (void)fclose(fm); } Uunlink(addr->return_filename); @@ -7684,19 +7821,19 @@ wording. */ addr->next = handled_addr; handled_addr = topaddr; } - fputc('\n', f); + fputc('\n', fp); } /* output machine readable part */ #ifdef SUPPORT_I18N if (message_smtputf8) - fprintf(f, "--%s\n" + fprintf(fp, "--%s\n" "Content-type: message/global-delivery-status\n\n" "Reporting-MTA: dns; %s\n", bound, smtp_active_hostname); else #endif - fprintf(f, "--%s\n" + fprintf(fp, "--%s\n" "Content-type: message/delivery-status\n\n" "Reporting-MTA: dns; %s\n", bound, smtp_active_hostname); @@ -7706,22 +7843,21 @@ wording. */ /* must be decoded from xtext: see RFC 3461:6.3a */ uschar *xdec_envid; if (auth_xtextdecode(dsn_envid, &xdec_envid) > 0) - fprintf(f, "Original-Envelope-ID: %s\n", dsn_envid); + fprintf(fp, "Original-Envelope-ID: %s\n", dsn_envid); else - fprintf(f, "X-Original-Envelope-ID: error decoding xtext formatted ENVID\n"); + fprintf(fp, "X-Original-Envelope-ID: error decoding xtext formatted ENVID\n"); } - fputc('\n', f); + fputc('\n', fp); for (addr = handled_addr; addr; addr = addr->next) { host_item * hu; - fprintf(f, "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(f, "Remote-MTA: dns; %s\n", hu->name); + fprintf(fp, "Remote-MTA: dns; %s\n", hu->name); #ifdef EXPERIMENTAL_DSN_INFO { const uschar * s; @@ -7729,19 +7865,19 @@ wording. */ { uschar * p = hu->port == 25 ? US"" : string_sprintf(":%d", hu->port); - fprintf(f, "Remote-MTA: X-ip; [%s]%s\n", hu->address, p); + fprintf(fp, "Remote-MTA: X-ip; [%s]%s\n", hu->address, p); } if ((s = addr->smtp_greeting) && *s) - fprintf(f, "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(f, "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(f, "X-Exim-Diagnostic: X-str; %s\n", s); + fprintf(fp, "X-Exim-Diagnostic: X-str; %.900s\n", s); } #endif - print_dsn_diagnostic_code(addr, f); + print_dsn_diagnostic_code(addr, fp); } - fputc('\n', f); + fputc('\n', fp); } /* Now copy the message, trying to give an intelligible comment if @@ -7762,7 +7898,7 @@ wording. */ bounce_return_size_limit is always honored. */ - fprintf(f, "--%s\n", bound); + fprintf(fp, "--%s\n", bound); dsnlimitmsg = US"X-Exim-DSN-Information: Due to administrative limits only headers are returned"; dsnnotifyhdr = NULL; @@ -7800,45 +7936,42 @@ wording. */ if (message_smtputf8) fputs(topt & topt_no_body ? "Content-type: message/global-headers\n\n" : "Content-type: message/global\n\n", - f); + fp); else #endif fputs(topt & topt_no_body ? "Content-type: text/rfc822-headers\n\n" : "Content-type: message/rfc822\n\n", - f); + fp); - fflush(f); + fflush(fp); transport_filter_argv = NULL; /* Just in case */ return_path = sender_address; /* In case not previously set */ { /* Dummy transport for headers add */ transport_ctx tctx = {{0}}; transport_instance tb = {0}; - tctx.u.fd = fileno(f); + tctx.u.fd = fileno(fp); tctx.tblock = &tb; tctx.options = topt; tb.add_headers = dsnnotifyhdr; + /*XXX no checking for failure! buggy! */ transport_write_message(&tctx, 0); } - fflush(f); + fflush(fp); /* we never add the final text. close the file */ if (emf) (void)fclose(emf); - fprintf(f, "\n--%s--\n", bound); + fprintf(fp, "\n--%s--\n", bound); /* Close the file, which should send an EOF to the child process that is receiving the message. Wait for it to finish. */ - (void)fclose(f); + (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 (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 @@ -7854,7 +7987,7 @@ wording. */ if (now - received_time.tv_sec < retry_maximum_timeout && !addr_defer) { addr_defer = (address_item *)(+1); - deliver_freeze = TRUE; + f.deliver_freeze = TRUE; deliver_frozen_at = time(NULL); /* Panic-dies on error */ (void)spool_write_header(message_id, SW_DELIVERING, NULL); @@ -7883,7 +8016,7 @@ wording. */ } } -disable_logging = FALSE; /* In case left set */ +f.disable_logging = FALSE; /* In case left set */ /* Come here from the mua_wrapper case if routing goes wrong */ @@ -7941,7 +8074,7 @@ if (!addr_defer) log_write(0, LOG_MAIN, "Completed"); /* Unset deliver_freeze so that we won't try to move the spool files further down */ - deliver_freeze = FALSE; + f.deliver_freeze = FALSE; #ifndef DISABLE_EVENT (void) event_raise(event_action, US"msg:complete", NULL); @@ -7960,6 +8093,8 @@ the parent's domain. If all the deferred addresses have an error number that indicates "retry time not reached", skip sending the warning message, because it won't contain the reason for the delay. It will get sent at the next real delivery attempt. + Exception: for retries caused by a remote peer we use the error message + store in the retry DB as the reason. However, if at least one address has tried, we'd better include all of them in the message. @@ -7977,18 +8112,17 @@ was set just to keep the message on the spool, so there is nothing to do here. else if (addr_defer != (address_item *)(+1)) { - address_item *addr; uschar *recipients = US""; - BOOL delivery_attempted = FALSE; + BOOL want_warning_msg = FALSE; deliver_domain = testflag(addr_defer, af_pfr) ? addr_defer->parent->domain : addr_defer->domain; - for (addr = addr_defer; addr; addr = addr->next) + for (address_item * addr = addr_defer; addr; addr = addr->next) { address_item *otaddr; - if (addr->basic_errno > ERRNO_RETRY_BASE) delivery_attempted = TRUE; + if (addr->basic_errno > ERRNO_WARN_BASE) want_warning_msg = TRUE; if (deliver_domain) { @@ -8059,10 +8193,10 @@ else if (addr_defer != (address_item *)(+1)) is not sent. Another attempt will be made at the next delivery attempt (if it also defers). */ - if ( !queue_2stage - && delivery_attempted - && ( ((addr_defer->dsn_flags & rf_dsnflags) == 0) - || (addr_defer->dsn_flags & rf_notify_delay) == rf_notify_delay + if ( !f.queue_2stage + && want_warning_msg + && ( !(addr_defer->dsn_flags & rf_dsnflags) + || addr_defer->dsn_flags & rf_notify_delay ) && delay_warning[1] > 0 && sender_address[0] != 0 @@ -8076,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 (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 */ @@ -8111,7 +8231,8 @@ else if (addr_defer != (address_item *)(+1)) DEBUG(D_deliver) { - debug_printf("time on queue = %s\n", readconf_printtime(queue_time)); + 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); } @@ -8124,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 @@ -8149,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()); @@ -8206,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); @@ -8255,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); } @@ -8285,6 +8398,7 @@ else if (addr_defer != (address_item *)(+1)) return_path = sender_address; /* In case not previously set */ /* Write the original email out */ + /*XXX no checking for failure! buggy! */ transport_write_message(&tctx, 0); fflush(f); @@ -8312,9 +8426,9 @@ 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 (deliver_firsttime) + if (f.deliver_firsttime) { - deliver_firsttime = FALSE; + f.deliver_firsttime = FALSE; update_spool = TRUE; } @@ -8325,9 +8439,9 @@ else if (addr_defer != (address_item *)(+1)) For the "tell" message, we turn \n back into newline. Also, insert a newline near the start instead of the ": " string. */ - if (deliver_freeze) + if (f.deliver_freeze) { - if (freeze_tell && freeze_tell[0] != 0 && !local_error_message) + if (freeze_tell && freeze_tell[0] != 0 && !f.local_error_message) { uschar *s = string_copy(frozen_info); uschar *ss = Ustrstr(s, " by the system filter: "); @@ -8368,9 +8482,9 @@ else if (addr_defer != (address_item *)(+1)) DEBUG(D_deliver) debug_printf("delivery deferred: update_spool=%d header_rewritten=%d\n", - update_spool, header_rewritten); + update_spool, f.header_rewritten); - if (update_spool || header_rewritten) + if (update_spool || f.header_rewritten) /* Panic-dies on error */ (void)spool_write_header(message_id, SW_DELIVERING, NULL); } @@ -8405,7 +8519,7 @@ if (remove_journal) /* Move the message off the spool if requested */ #ifdef SUPPORT_MOVE_FROZEN_MESSAGES - if (deliver_freeze && move_frozen_messages) + if (f.deliver_freeze && move_frozen_messages) (void)spool_move_message(id, message_subdir, US"", US"F"); #endif } @@ -8417,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 @@ -8432,48 +8549,13 @@ return final_yield; void -deliver_init(void) +tcp_init(void) { #ifdef EXIM_TFO_PROBE tfo_probe(); #else -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(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)", - FALSE, TRUE); - -#ifdef SUPPORT_TLS -if (!regex_STARTTLS) regex_STARTTLS = - regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); +f.tcp_fastopen_ok = TRUE; #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); } @@ -8483,17 +8565,17 @@ deliver_get_sender_address (uschar * id) int rc; uschar * new_sender_address, * save_sender_address; -BOOL save_qr = queue_running; +BOOL save_qr = f.queue_running; uschar * spoolname; /* make spool_open_datafile non-noisy on fail */ -queue_running = TRUE; +f.queue_running = TRUE; /* Side effect: message_subdir is set for the (possibly split) spool directory */ deliver_datafile = spool_open_datafile(id); -queue_running = save_qr; +f.queue_running = save_qr; if (deliver_datafile < 0) return NULL; @@ -8536,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; @@ -8550,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 (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 */ @@ -8579,6 +8660,7 @@ else } return; /* compiler quietening; control does not reach here. */ +#ifndef DISABLE_TLS fail: log_write(0, LOG_MAIN | (exec_type == CEE_EXEC_EXIT ? LOG_PANIC : LOG_PANIC_DIE), @@ -8588,6 +8670,7 @@ fail: Note: this must be _exit(), not exit(). */ _exit(EX_EXECFAILED); +#endif } /* vi: aw ai sw=2