X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/87abcb247b4444bab5fd0bcb212ddb26d5fd9191..759502e5af0acfb310b8571f056d2dbf59adb1d3:/src/src/deliver.c?ds=sidebyside diff --git a/src/src/deliver.c b/src/src/deliver.c index ada042a24..5c5167b3a 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -66,7 +66,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 +107,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 +144,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 +198,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 +220,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 +262,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 +332,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, + "Attempt to open msglog file path with upward-traversal: '%s'\n", filename); + for (int i = 2; i > 0; i--) { int fd = Uopen(filename, @@ -425,7 +433,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; @@ -1023,7 +1031,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) { @@ -1045,6 +1054,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 @@ -1086,42 +1096,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); -} - /******************************************************************************/ @@ -1137,7 +1111,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 @@ -1149,7 +1123,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" "); @@ -1250,7 +1225,7 @@ else { if (testflag(addr, af_pipelining)) g = string_catn(g, US" L", 2); -#ifdef SUPPORT_PIPE_CONNECT +#ifndef DISABLE_PIPE_CONNECT if (testflag(addr, af_early_pipe)) g = string_catn(g, US"*", 1); #endif @@ -1293,10 +1268,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. */ @@ -1317,14 +1289,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. */ @@ -1366,6 +1336,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); @@ -1396,8 +1369,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 @@ -1445,6 +1418,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, @@ -1641,6 +1617,7 @@ if (result == OK) tls_out.peercert = addr->peercert; addr->peercert = NULL; + tls_out.ver = addr->tlsver; tls_out.cipher = addr->cipher; tls_out.peerdn = addr->peerdn; tls_out.ocsp = addr->ocsp; @@ -1654,6 +1631,7 @@ if (result == OK) #ifndef DISABLE_TLS tls_free_cert(&tls_out.ourcert); tls_free_cert(&tls_out.peercert); + tls_out.ver = NULL; tls_out.cipher = NULL; tls_out.peerdn = NULL; tls_out.ocsp = OCSP_NOT_REQ; @@ -1790,7 +1768,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); } @@ -2052,10 +2030,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", @@ -2436,7 +2414,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 *) @@ -2504,7 +2482,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 *)); @@ -2574,7 +2552,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); @@ -2755,7 +2733,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; } @@ -3066,7 +3044,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; @@ -3158,11 +3136,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 @@ -3464,7 +3438,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++; @@ -3511,11 +3485,13 @@ while (!done) switch (*subid) { case '1': - addr->cipher = NULL; - addr->peerdn = NULL; + addr->tlsver = addr->cipher = addr->peerdn = NULL; if (*ptr) + { addr->cipher = string_copy(ptr); + addr->tlsver = string_copyn(ptr, Ustrchr(ptr, ':') - ptr); + } while (*ptr++); if (*ptr) addr->peerdn = string_copy(ptr); @@ -3564,7 +3540,7 @@ while (!done) case 'L': switch (*subid) { -#ifdef SUPPORT_PIPE_CONNECT +#ifndef DISABLE_PIPE_CONNECT case 2: setflag(addr, af_early_pipe); /*FALLTHROUGH*/ #endif case 1: setflag(addr, af_pipelining); break; @@ -3634,8 +3610,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; @@ -3647,7 +3623,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); @@ -4231,7 +4207,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; } @@ -4670,6 +4646,7 @@ all pipes, so I do not see a reason to use non-blocking IO here search_tidyup(); + DEBUG(D_deliver) debug_printf("forking transport process\n"); if ((pid = fork()) == 0) { int fd = pfd[pipe_write]; @@ -4793,7 +4770,6 @@ 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); @@ -4826,7 +4802,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; @@ -4835,7 +4811,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; @@ -4872,7 +4848,7 @@ all pipes, so I do not see a reason to use non-blocking IO here #endif if (testflag(addr, af_pipelining)) -#ifdef SUPPORT_PIPE_CONNECT +#ifndef DISABLE_PIPE_CONNECT if (testflag(addr, af_early_pipe)) rmt_dlv_checked_write(fd, 'L', '2', NULL, 0); else @@ -4893,7 +4869,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; @@ -4952,8 +4928,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); @@ -5001,6 +4977,7 @@ all pipes, so I do not see a reason to use non-blocking IO here (void)close(fd); exit(EXIT_SUCCESS); } + DEBUG(D_deliver) debug_printf("forked transport process (%d)\n", pid); /* Back in the mainline: close the unwanted half of the pipe. */ @@ -5056,9 +5033,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; @@ -5117,7 +5095,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++; @@ -5160,7 +5138,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; @@ -5486,6 +5464,45 @@ 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; +} + /************************************************* * Deliver one message * *************************************************/ @@ -5537,8 +5554,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); @@ -6009,8 +6031,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 @@ -6084,6 +6106,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 @@ -6168,10 +6193,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) @@ -6392,10 +6418,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))) { @@ -6713,7 +6737,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. */ @@ -6803,8 +6827,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 @@ -7153,7 +7177,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 @@ -7269,38 +7293,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 @@ -7320,7 +7343,7 @@ if (addr_senddsn) 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"); @@ -7333,7 +7356,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()); @@ -7345,8 +7368,11 @@ if (addr_senddsn) moan_write_from(f); fprintf(f, "Auto-Submitted: auto-generated\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" @@ -7354,14 +7380,13 @@ if (addr_senddsn) "This message was created automatically by mail delivery software.\n" " ----- The following addresses had successful delivery notifications -----\n", - 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" ); @@ -7380,24 +7405,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); @@ -7480,7 +7499,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; @@ -7490,11 +7510,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); @@ -7587,6 +7609,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()); @@ -7775,10 +7798,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); @@ -7898,7 +7920,7 @@ wording. */ /* In the test harness, let the child do it's thing first. */ - if (f.running_in_test_harness) millisleep(500); + testharness_pause_ms(500); /* If the process failed, there was some disaster in setting up the error message. Unless the message is very old, ensure that addr_defer @@ -8138,21 +8160,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 */ @@ -8173,7 +8181,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); } @@ -8211,6 +8220,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()); @@ -8268,13 +8278,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); @@ -8317,20 +8323,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); } @@ -8480,6 +8482,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 @@ -8495,52 +8500,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); - -#ifndef DISABLE_TLS -if (!regex_STARTTLS) regex_STARTTLS = - regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, 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); - -#ifdef SUPPORT_PIPE_CONNECT -if (!regex_EARLY_PIPE) regex_EARLY_PIPE = - regex_must_compile(US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)", FALSE, TRUE); -#endif } @@ -8622,7 +8588,7 @@ if (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only) else if (pid == 0) /* child: fork again to totally disconnect */ { - if (f.running_in_test_harness) millisleep(100); /* let parent debug out */ + testharness_pause_ms(100); /* let parent debug out */ /* does not return */ smtp_proxy_tls(cutthrough.cctx.tls_ctx, big_buffer, big_buffer_size, pfd, 5*60);