summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/doc-txt/ChangeLog4
-rw-r--r--doc/doc-txt/experimental-spec.txt43
-rw-r--r--src/src/config.h.defaults1
-rw-r--r--src/src/deliver.c136
-rw-r--r--src/src/exim.c9
-rw-r--r--src/src/globals.c8
-rw-r--r--src/src/structs.h9
-rw-r--r--src/src/transports/smtp.c209
l---------test/confs/47001
-rw-r--r--test/log/470021
-rw-r--r--test/mail/4700.CALLER131
l---------test/scripts/4700-dsn-info/47001
-rw-r--r--test/scripts/4700-dsn-info/REQUIRES1
-rw-r--r--test/stdout/470034
14 files changed, 498 insertions, 110 deletions
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:<d3@myhost.test.ex>: 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
+ );
}
}
@@ -2938,6 +3019,10 @@ for (addr = addrlist; addr != NULL; addr = addr->next)
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=<CALLER@test.ex> 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 <CALLER@test.ex> 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=<CALLER@test.ex> 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=<CALLER@test.ex> 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 <CALLER@test.ex> 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 <Mailer-Daemon@test.ex>
+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: <E10HmaY-0005vi-00@the.local.host.name>
+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: <CALLER@test.ex>
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@test.ex>)
+ id 10HmaX-0005vi-00
+ for userx@domain1; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaX-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@test.ex>
+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 <Mailer-Daemon@test.ex>
+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: <E10HmbA-0005vi-00@the.local.host.name>
+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: <CALLER@test.ex>
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@test.ex>)
+ id 10HmaZ-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaZ-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@test.ex>
+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