From 895fbaf26d3450d4eeacbad8fe04c328a77645f0 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Wed, 9 Sep 2015 16:03:38 +0100 Subject: [PATCH] DSN: Under EXPERIMENTAL_DSN_INFO add extras to bounce messages. Bug 1686 --- doc/doc-txt/ChangeLog | 4 + doc/doc-txt/experimental-spec.txt | 43 ++++++ src/src/config.h.defaults | 1 + src/src/deliver.c | 136 ++++++++++++------ src/src/exim.c | 9 +- src/src/globals.c | 8 +- src/src/structs.h | 9 +- src/src/transports/smtp.c | 209 +++++++++++++++++++--------- test/confs/4700 | 1 + test/log/4700 | 21 +++ test/mail/4700.CALLER | 131 +++++++++++++++++ test/scripts/4700-dsn-info/4700 | 1 + test/scripts/4700-dsn-info/REQUIRES | 1 + test/stdout/4700 | 34 +++++ 14 files changed, 498 insertions(+), 110 deletions(-) create mode 120000 test/confs/4700 create mode 100644 test/log/4700 create mode 100644 test/mail/4700.CALLER create mode 120000 test/scripts/4700-dsn-info/4700 create mode 100644 test/scripts/4700-dsn-info/REQUIRES create mode 100644 test/stdout/4700 diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index f24883d92..bc95690b1 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -34,6 +34,10 @@ HS/01 Bug 1671: Fix post transport crash. JH/03 Bug 425: Capture substrings in $regex1, $regex2 etc from regex & mime_regex ACL conditions. +JH/04 Bug 1686: When compiled with EXPERIMENTAL_DSN_INFO: Add extra information + to DSN fail messages (bounces): remote IP, remote greeting, remote response + to HELO, local diagnostic string. + Exim version 4.86 ----------------- diff --git a/doc/doc-txt/experimental-spec.txt b/doc/doc-txt/experimental-spec.txt index 317f40101..cf3c27f6a 100644 --- a/doc/doc-txt/experimental-spec.txt +++ b/doc/doc-txt/experimental-spec.txt @@ -1380,6 +1380,49 @@ must be representable in UTF-16. +DSN extra information +--------------------- +If compiled with EXPERIMENTAL_DSN_INFO extra information will be added +to DSN fail messages ("bounces"), when available. The intent is to aid +tracing of specific failing messages, when presented with a "bounce" +complaint and needing to search logs. + + +The remote MTA IP address, with port number if nonstandard. +Example: + Remote-MTA: X-ip; [127.0.0.1]:587 +Rationale: + Several addresses may correspond to the (already available) + dns name for the remote MTA. + +The remote MTA connect-time greeting. +Example: + X-Remote-MTA-smtp-greeting: X-str; 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +Rationale: + This string sometimes presents the remote MTA's idea of its + own name, and sometimes identifies the MTA software. + +The remote MTA response to HELO or EHLO. +Example: + X-Remote-MTA-helo-response: X-str; 250-the.local.host.name Hello localhost [127.0.0.1] +Limitations: + Only the first line of a multiline response is recorded. +Rationale: + This string sometimes presents the remote MTA's view of + the peer IP connecting to it. + +The reporting MTA detailed diagnostic. +Example: + X-Exim-Diagnostic: X-str; SMTP error from remote mail server after RCPT TO:: 550 hard error +Rationale: + This string somtimes give extra information over the + existing (already available) Diagnostic-Code field. + + +Note that non-RFC-documented field names and data types are used. + + + -------------------------------------------------------------- End of file diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index 596e651f0..6af3b4d43 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -172,6 +172,7 @@ it's a default value. */ #define EXPERIMENTAL_BRIGHTMAIL #define EXPERIMENTAL_DANE #define EXPERIMENTAL_DCC +#define EXPERIMENTAL_DSN_INFO #define EXPERIMENTAL_DMARC #define EXPERIMENTAL_EVENT #define EXPERIMENTAL_INTERNATIONAL diff --git a/src/src/deliver.c b/src/src/deliver.c index b5aa9b9a7..3f22dc91f 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -3223,41 +3223,56 @@ while (!done) break; } - addr->transport_return = *ptr++; - addr->special_action = *ptr++; - memcpy(&(addr->basic_errno), ptr, sizeof(addr->basic_errno)); - ptr += sizeof(addr->basic_errno); - memcpy(&(addr->more_errno), ptr, sizeof(addr->more_errno)); - ptr += sizeof(addr->more_errno); - memcpy(&(addr->flags), ptr, sizeof(addr->flags)); - ptr += sizeof(addr->flags); - addr->message = (*ptr)? string_copy(ptr) : NULL; - while(*ptr++); - addr->user_message = (*ptr)? string_copy(ptr) : NULL; - while(*ptr++); - - /* Always two strings for host information, followed by the port number and DNSSEC mark */ - - if (*ptr != 0) + switch (subid) { - h = store_get(sizeof(host_item)); - h->name = string_copy(ptr); - while (*ptr++); - h->address = string_copy(ptr); - while(*ptr++); - memcpy(&(h->port), ptr, sizeof(h->port)); - ptr += sizeof(h->port); - h->dnssec = *ptr == '2' ? DS_YES - : *ptr == '1' ? DS_NO - : DS_UNK; - ptr++; - addr->host_used = h; - } - else ptr++; +#ifdef EXPERIMENTAL_DSN_INFO + case '1': /* must arrive before A0, and applies to that addr */ + /* Two strings: smtp_greeting and helo_response */ + addr->smtp_greeting = string_copy(ptr); + while(*ptr++); + addr->helo_response = string_copy(ptr); + while(*ptr++); + break; +#endif - /* Finished with this address */ + case '0': + addr->transport_return = *ptr++; + addr->special_action = *ptr++; + memcpy(&(addr->basic_errno), ptr, sizeof(addr->basic_errno)); + ptr += sizeof(addr->basic_errno); + memcpy(&(addr->more_errno), ptr, sizeof(addr->more_errno)); + ptr += sizeof(addr->more_errno); + memcpy(&(addr->flags), ptr, sizeof(addr->flags)); + ptr += sizeof(addr->flags); + addr->message = (*ptr)? string_copy(ptr) : NULL; + while(*ptr++); + addr->user_message = (*ptr)? string_copy(ptr) : NULL; + while(*ptr++); - addr = addr->next; + /* Always two strings for host information, followed by the port number and DNSSEC mark */ + + if (*ptr != 0) + { + h = store_get(sizeof(host_item)); + h->name = string_copy(ptr); + while (*ptr++); + h->address = string_copy(ptr); + while(*ptr++); + memcpy(&(h->port), ptr, sizeof(h->port)); + ptr += sizeof(h->port); + h->dnssec = *ptr == '2' ? DS_YES + : *ptr == '1' ? DS_NO + : DS_UNK; + ptr++; + addr->host_used = h; + } + else ptr++; + + /* Finished with this address */ + + addr = addr->next; + break; + } break; /* Local interface address/port */ @@ -4423,7 +4438,6 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) for (r = addr->retries; r != NULL; r = r->next) { - uschar *ptr; sprintf(CS big_buffer, "%c%.500s", r->flags, r->key); ptr = big_buffer + Ustrlen(big_buffer+2) + 3; memcpy(ptr, &(r->basic_errno), sizeof(r->basic_errno)); @@ -4438,11 +4452,31 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) rmt_dlv_checked_write(fd, 'R', '0', big_buffer, ptr - big_buffer); } - /* The rest of the information goes in an 'A' item. */ +#ifdef EXPERIMENTAL_DSN_INFO +/*um, are they really per-addr? Other per-conn stuff is not (auth, tls). But host_used is! */ + if (addr->smtp_greeting) + { + ptr = big_buffer; + DEBUG(D_deliver) debug_printf("smtp_greeting '%s'\n", addr->smtp_greeting); + sprintf(CS ptr, "%.128s", addr->smtp_greeting); + while(*ptr++); + if (addr->helo_response) + { + DEBUG(D_deliver) debug_printf("helo_response '%s'\n", addr->helo_response); + sprintf(CS ptr, "%.128s", addr->helo_response); + while(*ptr++); + } + else + *ptr++ = '\0'; + rmt_dlv_checked_write(fd, 'A', '1', big_buffer, ptr - big_buffer); + } +#endif + + /* The rest of the information goes in an 'A0' item. */ - ptr = big_buffer + 2; sprintf(CS big_buffer, "%c%c", addr->transport_return, addr->special_action); + ptr = big_buffer + 2; memcpy(ptr, &(addr->basic_errno), sizeof(addr->basic_errno)); ptr += sizeof(addr->basic_errno); memcpy(ptr, &(addr->more_errno), sizeof(addr->more_errno)); @@ -4480,7 +4514,11 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++) } /* Local interface address/port */ +#ifdef EXPERIMENTAL_DSN_INFO + if (sending_ip_address) +#else if (LOGGING(incoming_interface) && sending_ip_address) +#endif { uschar * ptr = big_buffer; sprintf(CS ptr, "%.128s", sending_ip_address); @@ -7209,16 +7247,32 @@ wording. */ 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); - if (addr->host_used && addr->host_used->name) - { - fprintf(f, "Remote-MTA: dns; %s\n", - addr->host_used->name); - print_dsn_diagnostic_code(addr, f); - } + if ((hu = addr->host_used) && hu->name) + { + const uschar * s; + fprintf(f, "Remote-MTA: dns; %s\n", + hu->name); +#ifdef EXPERIMENTAL_DSN_INFO + if (hu->address) + { + uschar * p = hu->port == 25 + ? US"" : string_sprintf(":%d", hu->port); + fprintf(f, "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); + if ((s = addr->helo_response) && *s) + fprintf(f, "X-Remote-MTA-helo-response: X-str; %s\n", s); + if ((s = addr->message) && *s) + fprintf(f, "X-Exim-Diagnostic: X-str; %s\n", s); +#endif + print_dsn_diagnostic_code(addr, f); + } fputc('\n', f); } diff --git a/src/src/exim.c b/src/src/exim.c index 999b94cc1..084d64990 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -847,6 +847,12 @@ fprintf(f, "Support for:"); #ifdef EXPERIMENTAL_DMARC fprintf(f, " Experimental_DMARC"); #endif +#ifdef EXPERIMENTAL_DSN_INFO + fprintf(f, " Experimental_DSN_info"); +#endif +#ifdef EXPERIMENTAL_INTERNATIONAL + fprintf(f, " Experimental_International"); +#endif #ifdef EXPERIMENTAL_PROXY fprintf(f, " Experimental_Proxy"); #endif @@ -859,9 +865,6 @@ fprintf(f, "Support for:"); #ifdef EXPERIMENTAL_SOCKS fprintf(f, " Experimental_SOCKS"); #endif -#ifdef EXPERIMENTAL_INTERNATIONAL - fprintf(f, " Experimental_International"); -#endif fprintf(f, "\n"); fprintf(f, "Lookups (built-in):"); diff --git a/src/src/globals.c b/src/src/globals.c index 8445f001c..f3b6791f3 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -354,13 +354,17 @@ address_item address_defaults = { NULL, /* return_filename */ NULL, /* self_hostname */ NULL, /* shadow_message */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS NULL, /* cipher */ NULL, /* ourcert */ NULL, /* peercert */ NULL, /* peerdn */ OCSP_NOT_REQ, /* ocsp */ - #endif +#endif +#ifdef EXPERIMENTAL_DSN_INFO + NULL, /* smtp_greeting */ + NULL, /* helo_response */ +#endif NULL, /* authenticator */ NULL, /* auth_id */ NULL, /* auth_sndr */ diff --git a/src/src/structs.h b/src/src/structs.h index 438b52168..db9e843ac 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -561,13 +561,18 @@ typedef struct address_item { uschar *self_hostname; /* after self=pass */ uschar *shadow_message; /* info about shadow transporting */ - #ifdef SUPPORT_TLS +#ifdef SUPPORT_TLS uschar *cipher; /* Cipher used for transport */ void *ourcert; /* Certificate offered to peer, binary */ void *peercert; /* Certificate from peer, binary */ uschar *peerdn; /* DN of server's certificate */ int ocsp; /* OCSP status of peer cert */ - #endif +#endif + +#ifdef EXPERIMENTAL_DSN_INFO + const uschar *smtp_greeting; /* peer self-identification */ + const uschar *helo_response; /* peer message */ +#endif uschar *authenticator; /* auth driver name used by transport */ uschar *auth_id; /* auth "login" name used by transport */ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index c93f2ef8f..ac40460f1 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -440,6 +440,8 @@ Arguments: rc to put in each address's transport_return field pass_message if TRUE, set the "pass message" flag in the address host if set, mark addrs as having used this host + smtp_greeting from peer + helo_response from peer If errno_value has the special value ERRNO_CONNECTTIMEOUT, ETIMEDOUT is put in the errno field, and RTEF_CTOUT is ORed into the more_errno field, to indicate @@ -450,7 +452,11 @@ Returns: nothing static void set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc, - BOOL pass_message, host_item * host) + BOOL pass_message, host_item * host +#ifdef EXPERIMENTAL_DSN_INFO + , const uschar * smtp_greeting, const uschar * helo_response +#endif + ) { address_item *addr; int orvalue = 0; @@ -459,7 +465,7 @@ if (errno_value == ERRNO_CONNECTTIMEOUT) errno_value = ETIMEDOUT; orvalue = RTEF_CTOUT; } -for (addr = addrlist; addr != NULL; addr = addr->next) +for (addr = addrlist; addr; addr = addr->next) if (addr->transport_return >= PENDING) { addr->basic_errno = errno_value; @@ -471,10 +477,31 @@ for (addr = addrlist; addr != NULL; addr = addr->next) } addr->transport_return = rc; if (host) + { addr->host_used = host; +#ifdef EXPERIMENTAL_DSN_INFO + if (smtp_greeting) + {uschar * s = Ustrchr(smtp_greeting, '\n'); if (s) *s = '\0';} + addr->smtp_greeting = smtp_greeting; + + if (helo_response) + {uschar * s = Ustrchr(helo_response, '\n'); if (s) *s = '\0';} + addr->helo_response = helo_response; +#endif + } } } +static void +set_errno_nohost(address_item *addrlist, int errno_value, uschar *msg, int rc, + BOOL pass_message) +{ +set_errno(addrlist, errno_value, msg, rc, pass_message, NULL +#ifdef EXPERIMENTAL_DSN_INFO + , NULL, NULL +#endif + ); +} /************************************************* @@ -847,7 +874,7 @@ while (count-- > 0) { uschar *message = string_sprintf("SMTP timeout after RCPT TO:<%s>", transport_rcpt_address(addr, include_affixes)); - set_errno(addrlist, ETIMEDOUT, message, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ETIMEDOUT, message, DEFER, FALSE); retry_add_item(addr, addr->address_retry_key, 0); update_waiting = FALSE; return -1; @@ -1096,8 +1123,8 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1)) /* Internal problem, message in buffer. */ case ERROR: - set_errno(addrlist, ERRNO_AUTHPROB, string_copy(buffer), - DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_AUTHPROB, string_copy(buffer), + DEFER, FALSE); return ERROR; } @@ -1111,9 +1138,9 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1)) if (require_auth == OK && !smtp_authenticated) { - set_errno(addrlist, ERRNO_AUTHFAIL, + set_errno_nohost(addrlist, ERRNO_AUTHFAIL, string_sprintf("authentication required but %s", fail_reason), DEFER, - FALSE, NULL); + FALSE); return DEFER; } @@ -1152,7 +1179,7 @@ if (ob->authenticated_sender != NULL) { uschar *message = string_sprintf("failed to expand " "authenticated_sender: %s", expand_string_message); - set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE); return TRUE; } } @@ -1381,6 +1408,10 @@ smtp_outblock outblock; int max_rcpt = tblock->max_addresses; uschar *igquotstr = US""; +#ifdef EXPERIMENTAL_DSN_INFO +uschar *smtp_greeting = NULL; +uschar *helo_response = NULL; +#endif uschar *helo_data = NULL; uschar *message = NULL; @@ -1432,8 +1463,8 @@ tls_modify_variables(&tls_out); #ifndef SUPPORT_TLS if (smtps) { - set_errno(addrlist, ERRNO_TLSFAILURE, US"TLS support not available", - DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_TLSFAILURE, US"TLS support not available", + DEFER, FALSE); return ERROR; } #endif @@ -1450,8 +1481,8 @@ if (continue_hostname == NULL) if (inblock.sock < 0) { - set_errno(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno, - NULL, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno, + NULL, DEFER, FALSE); return DEFER; } @@ -1469,18 +1500,18 @@ if (continue_hostname == NULL) && dane_required /* do not error on only dane-requested */ ) { - set_errno(addrlist, ERRNO_DNSDEFER, + set_errno_nohost(addrlist, ERRNO_DNSDEFER, string_sprintf("DANE error: tlsa lookup %s", rc == DEFER ? "DEFER" : "FAIL"), - rc, FALSE, NULL); + rc, FALSE); return rc; } } else if (dane_required) { - set_errno(addrlist, ERRNO_DNSDEFER, + set_errno_nohost(addrlist, ERRNO_DNSDEFER, string_sprintf("DANE error: %s lookup not DNSSEC", host->name), - FAIL, FALSE, NULL); + FAIL, FALSE); return FAIL; } @@ -1501,7 +1532,7 @@ if (continue_hostname == NULL) if ((helo_data = string_domain_utf8_to_alabel(helo_data, &errstr)), errstr) { errstr = string_sprintf("failed to expand helo_data: %s", errstr); - set_errno(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE); yield = DEFER; goto SEND_QUIT; } @@ -1514,8 +1545,12 @@ if (continue_hostname == NULL) if (!smtps) { - if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', - ob->command_timeout)) goto RESPONSE_FAILED; + BOOL good_response = smtp_read_response(&inblock, buffer, sizeof(buffer), + '2', ob->command_timeout); +#ifdef EXPERIMENTAL_DSN_INFO + smtp_greeting = string_copy(buffer); +#endif + if (!good_response) goto RESPONSE_FAILED; #ifdef EXPERIMENTAL_EVENT { @@ -1525,9 +1560,9 @@ if (continue_hostname == NULL) s = event_raise(tblock->event_action, US"smtp:connect", buffer); if (s) { - set_errno(addrlist, ERRNO_EXPANDFAIL, + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, string_sprintf("deferred by smtp:connect event expansion: %s", s), - DEFER, FALSE, NULL); + DEFER, FALSE); yield = DEFER; goto SEND_QUIT; } @@ -1541,7 +1576,7 @@ if (continue_hostname == NULL) { uschar *message = string_sprintf("failed to expand helo_data: %s", expand_string_message); - set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE); yield = DEFER; goto SEND_QUIT; } @@ -1606,9 +1641,18 @@ goto SEND_QUIT; if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', ob->command_timeout)) { - if (errno != 0 || buffer[0] == 0 || lmtp) goto RESPONSE_FAILED; + if (errno != 0 || buffer[0] == 0 || lmtp) + { +#ifdef EXPERIMENTAL_DSN_INFO + helo_response = string_copy(buffer); +#endif + goto RESPONSE_FAILED; + } esmtp = FALSE; } +#ifdef EXPERIMENTAL_DSN_INFO + helo_response = string_copy(buffer); +#endif } else { @@ -1618,10 +1662,16 @@ goto SEND_QUIT; if (!esmtp) { + BOOL good_response; + if (smtp_write_command(&outblock, FALSE, "HELO %s\r\n", helo_data) < 0) goto SEND_FAILED; - if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', - ob->command_timeout)) goto RESPONSE_FAILED; + good_response = smtp_read_response(&inblock, buffer, sizeof(buffer), + '2', ob->command_timeout); +#ifdef EXPERIMENTAL_DSN_INFO + helo_response = string_copy(buffer); +#endif + if (!good_response) goto RESPONSE_FAILED; } /* Set IGNOREQUOTA if the response to LHLO specifies support and the @@ -1671,6 +1721,11 @@ error messages. Note that smtp_use_size and smtp_use_pipelining will have been set from the command line if they were set in the process that passed the connection on. */ +/*XXX continue case needs to propagate DSN_INFO, prob. in deliver.c +as the contine goes via transport_pass_socket() and doublefork and exec. +It does not wait. Unclear how we keep separate host's responses +separate - we could match up by host ip+port as a bodge. */ + else { inblock.sock = outblock.sock = fileno(stdin); @@ -1749,7 +1804,7 @@ if ( tls_offered /* TLS session is set up */ - for (addr = addrlist; addr != NULL; addr = addr->next) + for (addr = addrlist; addr; addr = addr->next) if (addr->transport_return == PENDING_DEFER) { addr->cipher = tls_out.cipher; @@ -1774,6 +1829,8 @@ start of the Exim process (in exim.c). */ if (tls_out.active >= 0) { char *greeting_cmd; + BOOL good_response; + if (helo_data == NULL) { helo_data = expand_string(ob->helo_data); @@ -1781,7 +1838,7 @@ if (tls_out.active >= 0) { uschar *message = string_sprintf("failed to expand helo_data: %s", expand_string_message); - set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE); yield = DEFER; goto SEND_QUIT; } @@ -1790,8 +1847,12 @@ if (tls_out.active >= 0) /* For SMTPS we need to wait for the initial OK response. */ if (smtps) { - if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', - ob->command_timeout)) goto RESPONSE_FAILED; + good_response = smtp_read_response(&inblock, buffer, sizeof(buffer), + '2', ob->command_timeout); +#ifdef EXPERIMENTAL_DSN_INFO + smtp_greeting = string_copy(buffer); +#endif + if (!good_response) goto RESPONSE_FAILED; } if (esmtp) @@ -1806,9 +1867,12 @@ if (tls_out.active >= 0) if (smtp_write_command(&outblock, FALSE, "%s %s\r\n", lmtp? "LHLO" : greeting_cmd, helo_data) < 0) goto SEND_FAILED; - if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2', - ob->command_timeout)) - goto RESPONSE_FAILED; + good_response = smtp_read_response(&inblock, buffer, sizeof(buffer), + '2', ob->command_timeout); +#ifdef EXPERIMENTAL_DSN_INFO + helo_response = string_copy(buffer); +#endif + if (!good_response) goto RESPONSE_FAILED; } /* If the host is required to use a secure channel, ensure that we @@ -1935,8 +1999,8 @@ if (tblock->filter_command != NULL) if (!rc) { - set_errno(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER, - FALSE, NULL); + set_errno_nohost(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER, + FALSE); yield = ERROR; goto SEND_QUIT; } @@ -2065,7 +2129,7 @@ pending_MAIL = TRUE; /* The block starts with MAIL */ { if (s = string_address_utf8_to_alabel(return_path, &errstr), errstr) { - set_errno(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE, NULL); + set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE); yield = ERROR; goto SEND_QUIT; } @@ -2217,8 +2281,8 @@ if (mua_wrapper) if (badaddr->transport_return != PENDING_OK) { /*XXX could we find a better errno than 0 here? */ - set_errno(addrlist, 0, badaddr->message, FAIL, - testflag(badaddr, af_pass_message), NULL); + set_errno_nohost(addrlist, 0, badaddr->message, FAIL, + testflag(badaddr, af_pass_message)); ok = FALSE; break; } @@ -2475,7 +2539,7 @@ if (!ok) ok = TRUE; else else sprintf(CS buffer, "%.500s\n", addr->unique); - DEBUG(D_deliver) debug_printf("journalling %s", buffer); + DEBUG(D_deliver) debug_printf("journalling %s\n", buffer); len = Ustrlen(CS buffer); if (write(journal_fd, buffer, len) != len) log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for " @@ -2512,7 +2576,7 @@ if (!ok) ok = TRUE; else else sprintf(CS buffer, "%.500s\n", addr->unique); - DEBUG(D_deliver) debug_printf("journalling(PRDR) %s", buffer); + DEBUG(D_deliver) debug_printf("journalling(PRDR) %s\n", buffer); len = Ustrlen(CS buffer); if (write(journal_fd, buffer, len) != len) log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for " @@ -2542,22 +2606,27 @@ the problem is not related to this specific message. */ if (!ok) { - int code; + int code, set_rc; + uschar * set_message; RESPONSE_FAILED: - save_errno = errno; - message = NULL; - send_quit = check_response(host, &save_errno, addrlist->more_errno, - buffer, &code, &message, &pass_message); - goto FAILED; + { + save_errno = errno; + message = NULL; + send_quit = check_response(host, &save_errno, addrlist->more_errno, + buffer, &code, &message, &pass_message); + goto FAILED; + } SEND_FAILED: - save_errno = errno; - code = '4'; - message = US string_sprintf("send() to %s [%s] failed: %s", - host->name, host->address, strerror(save_errno)); - send_quit = FALSE; - goto FAILED; + { + save_errno = errno; + code = '4'; + message = US string_sprintf("send() to %s [%s] failed: %s", + host->name, host->address, strerror(save_errno)); + send_quit = FALSE; + goto FAILED; + } /* This label is jumped to directly when a TLS negotiation has failed, or was not done for a host for which it is required. Values will be set @@ -2578,16 +2647,14 @@ if (!ok) FAILED: ok = FALSE; /* For when reached by GOTO */ + set_message = message; if (setting_up) { if (code == '5') - set_errno(addrlist, save_errno, message, FAIL, pass_message, host); + set_rc = FAIL; else - { - set_errno(addrlist, save_errno, message, DEFER, pass_message, host); - yield = DEFER; - } + yield = set_rc = DEFER; } /* We want to handle timeouts after MAIL or "." and loss of connection after @@ -2646,14 +2713,15 @@ if (!ok) if (message_error) { if (mua_wrapper) code = '5'; /* Force hard failure in wrapper mode */ - set_errno(addrlist, save_errno, message, (code == '5')? FAIL : DEFER, - pass_message, host); /* If there's an errno, the message contains just the identity of the host. */ - if (code != '5') /* Anything other than 5 is treated as temporary */ + if (code == '5') + set_rc = FAIL; + else /* Anything other than 5 is treated as temporary */ { + set_rc = DEFER; if (save_errno > 0) message = US string_sprintf("%s: %s", message, strerror(save_errno)); if (host->next != NULL) log_write(0, LOG_MAIN, "%s", message); @@ -2670,11 +2738,17 @@ if (!ok) else { + set_rc = DEFER; yield = (save_errno == ERRNO_CHHEADER_FAIL || save_errno == ERRNO_FILTER_FAIL)? ERROR : DEFER; - set_errno(addrlist, save_errno, message, DEFER, pass_message, host); } } + + set_errno(addrlist, save_errno, set_message, set_rc, pass_message, host +#ifdef EXPERIMENTAL_DSN_INFO + , smtp_greeting, helo_response +#endif + ); } @@ -2787,6 +2861,9 @@ if (completed_address && ok && send_quit) /* If the socket is successfully passed, we musn't send QUIT (or indeed anything!) from here. */ +/*XXX DSN_INFO: assume likely to do new HELO; but for greet we'll want to +propagate it from the initial +*/ if (ok && transport_pass_socket(tblock->name, host->name, host->address, new_message_id, inblock.sock)) { @@ -2796,7 +2873,11 @@ if (completed_address && ok && send_quit) /* If RSET failed and there are addresses left, they get deferred. */ - else set_errno(first_addr, errno, msg, DEFER, FALSE, host); + else set_errno(first_addr, errno, msg, DEFER, FALSE, host +#ifdef EXPERIMENTAL_DSN_INFO + , smtp_greeting, helo_response +#endif + ); } } @@ -2937,6 +3018,10 @@ for (addr = addrlist; addr != NULL; addr = addr->next) addr->peercert = NULL; addr->peerdn = NULL; addr->ocsp = OCSP_NOT_REQ; +#endif +#ifdef EXPERIMENTAL_DSN_INFO + addr->smtp_greeting = NULL; + addr->helo_response = NULL; #endif } return first_addr; @@ -3479,7 +3564,7 @@ for (cutoff_retry = 0; expired && if (dont_deliver) { host_item *host2; - set_errno(addrlist, 0, NULL, OK, FALSE, NULL); + set_errno_nohost(addrlist, 0, NULL, OK, FALSE); for (addr = addrlist; addr != NULL; addr = addr->next) { addr->host_used = host; diff --git a/test/confs/4700 b/test/confs/4700 new file mode 120000 index 000000000..bf798671f --- /dev/null +++ b/test/confs/4700 @@ -0,0 +1 @@ +0211 \ No newline at end of file diff --git a/test/log/4700 b/test/log/4700 new file mode 100644 index 000000000..35da72e04 --- /dev/null +++ b/test/log/4700 @@ -0,0 +1,21 @@ +1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss +1999-03-02 09:44:33 10HmaX-0005vi-00 H=localhost4.test.ex [127.0.0.1]: SMTP error from remote mail server after initial connection: 450 I'm busy +1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@domain1 R=others T=smtp defer (0) H=localhost4.test.ex [127.0.0.1]: SMTP error from remote mail server after initial connection: 450 I'm busy +1999-03-02 09:44:33 Start queue run: pid=pppp -qf +1999-03-02 09:44:33 10HmaX-0005vi-00 H=localhost4.test.ex [127.0.0.1]: SMTP error from remote mail server after HELO the.local.host.name: 450 I'm busy +1999-03-02 09:44:33 10HmaX-0005vi-00 == userx@domain1 R=others T=smtp defer (0) H=localhost4.test.ex [127.0.0.1]: SMTP error from remote mail server after HELO the.local.host.name: 450 I'm busy +1999-03-02 09:44:33 End queue run: pid=pppp -qf +1999-03-02 09:44:33 Start queue run: pid=pppp -qf +1999-03-02 09:44:33 10HmaX-0005vi-00 ** userx@domain1 F= R=others T=smtp H=localhost4.test.ex [127.0.0.1]: SMTP error from remote mail server after initial connection: 550 Go away +1999-03-02 09:44:33 10HmaY-0005vi-00 <= <> R=10HmaX-0005vi-00 U=EXIMUSER P=local S=sss +1999-03-02 09:44:33 10HmaY-0005vi-00 => CALLER F=<> R=all T=local_delivery +1999-03-02 09:44:33 10HmaY-0005vi-00 Completed +1999-03-02 09:44:33 10HmaX-0005vi-00 Completed +1999-03-02 09:44:33 End queue run: pid=pppp -qf +1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss +1999-03-02 09:44:33 10HmaZ-0005vi-00 ** userx@domain1 F= R=others T=smtp H=localhost4.test.ex [127.0.0.1]: SMTP error from remote mail server after HELO the.local.host.name: 550 Go away +1999-03-02 09:44:33 10HmaZ-0005vi-00 ** usery@domain2 F= R=others T=smtp H=localhost4.test.ex [127.0.0.1]: SMTP error from remote mail server after HELO the.local.host.name: 550 Go away +1999-03-02 09:44:33 10HmbA-0005vi-00 <= <> R=10HmaZ-0005vi-00 U=EXIMUSER P=local S=sss +1999-03-02 09:44:33 10HmbA-0005vi-00 => CALLER F=<> R=all T=local_delivery +1999-03-02 09:44:33 10HmbA-0005vi-00 Completed +1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed diff --git a/test/mail/4700.CALLER b/test/mail/4700.CALLER new file mode 100644 index 000000000..bde98db7d --- /dev/null +++ b/test/mail/4700.CALLER @@ -0,0 +1,131 @@ +From MAILER-DAEMON Tue Mar 02 09:44:33 1999 +Return-path: <> +Received: from EXIMUSER by the.local.host.name with local (Exim x.yz) + id 10HmaY-0005vi-00 + for CALLER@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 +X-Failed-Recipients: userx@domain1 +Auto-Submitted: auto-replied +From: Mail Delivery System +To: CALLER@test.ex +Content-Type: multipart/report; report-type=delivery-status; boundary=NNNNNNNNNN-eximdsn-MMMMMMMMMM +MIME-Version: 1.0 +Subject: Mail delivery failed: returning message to sender +Message-Id: +Date: Tue, 2 Mar 1999 09:44:33 +0000 + +--NNNNNNNNNN-eximdsn-MMMMMMMMMM +Content-type: text/plain; charset=us-ascii + +This message was created automatically by mail delivery software. + +A message that you sent could not be delivered to one or more of its +recipients. This is a permanent error. The following address(es) failed: + + userx@domain1 + host localhost4.test.ex [127.0.0.1] + SMTP error from remote mail server after initial connection: + 550 Go away + +--NNNNNNNNNN-eximdsn-MMMMMMMMMM +Content-type: message/delivery-status + +Reporting-MTA: dns; the.local.host.name + +Action: failed +Final-Recipient: rfc822;userx@domain1 +Status: 5.0.0 +Remote-MTA: dns; localhost4.test.ex +Remote-MTA: X-ip; [127.0.0.1]:1111 +X-Remote-MTA-smtp-greeting: X-str; 550 Go away +X-Exim-Diagnostic: X-str; SMTP error from remote mail server after initial connection: 550 Go away +Diagnostic-Code: smtp; 550 Go away + +--NNNNNNNNNN-eximdsn-MMMMMMMMMM +Content-type: message/rfc822 + +Return-path: +Received: from CALLER by the.local.host.name with local (Exim x.yz) + (envelope-from ) + id 10HmaX-0005vi-00 + for userx@domain1; Tue, 2 Mar 1999 09:44:33 +0000 +Message-Id: +From: CALLER_NAME +Date: Tue, 2 Mar 1999 09:44:33 +0000 + +Test message 1 + +--NNNNNNNNNN-eximdsn-MMMMMMMMMM-- + +From MAILER-DAEMON Tue Mar 02 09:44:33 1999 +Return-path: <> +Received: from EXIMUSER by the.local.host.name with local (Exim x.yz) + id 10HmbA-0005vi-00 + for CALLER@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 +X-Failed-Recipients: usery@domain2, + userx@domain1 +Auto-Submitted: auto-replied +From: Mail Delivery System +To: CALLER@test.ex +Content-Type: multipart/report; report-type=delivery-status; boundary=NNNNNNNNNN-eximdsn-MMMMMMMMMM +MIME-Version: 1.0 +Subject: Mail delivery failed: returning message to sender +Message-Id: +Date: Tue, 2 Mar 1999 09:44:33 +0000 + +--NNNNNNNNNN-eximdsn-MMMMMMMMMM +Content-type: text/plain; charset=us-ascii + +This message was created automatically by mail delivery software. + +A message that you sent could not be delivered to one or more of its +recipients. This is a permanent error. The following address(es) failed: + + usery@domain2 + host localhost4.test.ex [127.0.0.1] + SMTP error from remote mail server after HELO the.local.host.name: + 550 Go away + userx@domain1 + host localhost4.test.ex [127.0.0.1] + SMTP error from remote mail server after HELO the.local.host.name: + 550 Go away + +--NNNNNNNNNN-eximdsn-MMMMMMMMMM +Content-type: message/delivery-status + +Reporting-MTA: dns; the.local.host.name + +Action: failed +Final-Recipient: rfc822;userx@domain1 +Status: 5.0.0 +Remote-MTA: dns; localhost4.test.ex +Remote-MTA: X-ip; [127.0.0.1]:1111 +X-Remote-MTA-smtp-greeting: X-str; 220 Connected OK +X-Remote-MTA-helo-response: X-str; 550 Go away +X-Exim-Diagnostic: X-str; SMTP error from remote mail server after HELO the.local.host.name: 550 Go away +Diagnostic-Code: smtp; 550 Go away + +Action: failed +Final-Recipient: rfc822;usery@domain2 +Status: 5.0.0 +Remote-MTA: dns; localhost4.test.ex +Remote-MTA: X-ip; [127.0.0.1]:1111 +X-Remote-MTA-smtp-greeting: X-str; 220 Connected OK +X-Remote-MTA-helo-response: X-str; 550 Go away +X-Exim-Diagnostic: X-str; SMTP error from remote mail server after HELO the.local.host.name: 550 Go away +Diagnostic-Code: smtp; 550 Go away + +--NNNNNNNNNN-eximdsn-MMMMMMMMMM +Content-type: message/rfc822 + +Return-path: +Received: from CALLER by the.local.host.name with local (Exim x.yz) + (envelope-from ) + id 10HmaZ-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000 +Message-Id: +From: CALLER_NAME +Date: Tue, 2 Mar 1999 09:44:33 +0000 + +Test message 2 + +--NNNNNNNNNN-eximdsn-MMMMMMMMMM-- + diff --git a/test/scripts/4700-dsn-info/4700 b/test/scripts/4700-dsn-info/4700 new file mode 120000 index 000000000..e480f3494 --- /dev/null +++ b/test/scripts/4700-dsn-info/4700 @@ -0,0 +1 @@ +../0000-Basic/0211 \ No newline at end of file diff --git a/test/scripts/4700-dsn-info/REQUIRES b/test/scripts/4700-dsn-info/REQUIRES new file mode 100644 index 000000000..683fc9019 --- /dev/null +++ b/test/scripts/4700-dsn-info/REQUIRES @@ -0,0 +1 @@ +support Experimental_DSN_info diff --git a/test/stdout/4700 b/test/stdout/4700 new file mode 100644 index 000000000..446203b4f --- /dev/null +++ b/test/stdout/4700 @@ -0,0 +1,34 @@ + +******** SERVER ******** +Listening on port 1224 ... +Connection request from [127.0.0.1] +450 I'm busy +QUIT +250 OK +End of script +Listening on port 1224 ... +Connection request from [127.0.0.1] +220 Connected OK +EHLO the.local.host.name +450 I'm busy +HELO the.local.host.name +450 I'm busy +QUIT +250 OK +End of script +Listening on port 1224 ... +Connection request from [127.0.0.1] +550 Go away +QUIT +250 OK +End of script +Listening on port 1224 ... +Connection request from [127.0.0.1] +220 Connected OK +EHLO the.local.host.name +550 Go away +HELO the.local.host.name +550 Go away +QUIT +250 OK +End of script -- 2.25.1