* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* Copyright (c) University of Cambridge 1995 - 2016 */
/* See the file NOTICE for conditions of use and distribution. */
#include "../exim.h"
(void *)offsetof(smtp_transport_options_block, serialize_hosts) },
{ "size_addition", opt_int,
(void *)offsetof(smtp_transport_options_block, size_addition) }
-#ifdef EXPERIMENTAL_SOCKS
+#ifdef SUPPORT_SOCKS
,{ "socks_proxy", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, socks_proxy) }
#endif
FALSE, /* lmtp_ignore_quota */
NULL, /* expand_retry_include_ip_address */
TRUE /* retry_include_ip_address */
-#ifdef EXPERIMENTAL_SOCKS
+#ifdef SUPPORT_SOCKS
,NULL /* socks_proxy */
#endif
#ifdef SUPPORT_TLS
return FALSE;
}
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
/* Handle lack of advertised SMTPUTF8, for international message */
if (*errno_value == ERRNO_UTF8_FWD)
{
{
uschar * message = string_sprintf("H=%s [%s]", host->name, host->address);
+if (LOGGING(outgoing_port))
+ message = string_sprintf("%s:%d", message,
+ host->port == PORT_NONE ? 25 : host->port);
if (addr->message)
{
message = string_sprintf("%s: %s", message, addr->message);
}
else
{
- if (LOGGING(outgoing_port))
- message = string_sprintf("%s:%d", message,
- host->port == PORT_NONE ? 25 : host->port);
- log_write(0, LOG_MAIN, "%s %s", message, strerror(addr->basic_errno));
- deliver_msglog("%s %s %s\n", tod_stamp(tod_log), message,
- strerror(addr->basic_errno));
+ const uschar * s = exim_errstr(addr->basic_errno);
+ log_write(0, LOG_MAIN, "%s %s", message, s);
+ deliver_msglog("%s %s %s\n", tod_stamp(tod_log), message, s);
}
}
static void
msglog_line(host_item * host, uschar * message)
{
- deliver_msglog("%s H=%s [%s] %s\n", tod_stamp(tod_log),
- host->name, host->address, message);
+deliver_msglog("%s H=%s [%s] %s\n", tod_stamp(tod_log),
+ host->name, host->address, message);
}
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
/*************************************************
* Post-defer action *
*************************************************/
addr->basic_errno = ERRNO_RCPT4XX;
addr->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
+#ifndef DISABLE_EVENT
+ event_defer_errno = addr->more_errno;
+ msg_event_raise(US"msg:rcpt:host:defer", addr);
+#endif
+
/* Log temporary errors if there are more hosts to be tried.
If not, log this last one in the == line. */
if (host->next)
log_write(0, LOG_MAIN, "H=%s [%s]: %s", host->name, host->address, addr->message);
+#ifndef DISABLE_EVENT
+ else
+ msg_event_raise(US"msg:rcpt:defer", addr);
+#endif
+
/* Do not put this message on the list of those waiting for specific
hosts, as otherwise it is likely to be tried too often. */
#ifdef EXPERIMENTAL_DANE
int
-tlsa_lookup(const host_item * host, dns_answer * dnsa,
- BOOL dane_required, BOOL * dane)
+tlsa_lookup(const host_item * host, dns_answer * dnsa, BOOL dane_required)
{
/* move this out to host.c given the similarity to dns_lookup() ? */
uschar buffer[300];
default:
case DNS_FAIL:
- if (dane_required)
- return FAIL;
- break;
+ return dane_required ? FAIL : DEFER;
case DNS_SUCCEED:
if (!dns_is_secure(dnsa))
log_write(0, LOG_MAIN, "DANE error: TLSA lookup not DNSSEC");
return DEFER;
}
- *dane = TRUE;
- break;
+ return OK;
}
-return OK;
}
#endif
+uschar
+ehlo_response(uschar * buf, size_t bsize, uschar checks)
+{
+#ifdef SUPPORT_TLS
+if (checks & PEER_OFFERED_TLS)
+ if (pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+ checks &= ~PEER_OFFERED_TLS;
+#endif
+
+ if ( checks & PEER_OFFERED_IGNQ
+ && pcre_exec(regex_IGNOREQUOTA, NULL, CS buf, bsize, 0,
+ PCRE_EOPT, NULL, 0) < 0)
+ checks &= ~PEER_OFFERED_IGNQ;
+
+#ifndef DISABLE_PRDR
+ if ( checks & PEER_OFFERED_PRDR
+ && pcre_exec(regex_PRDR, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+ checks &= ~PEER_OFFERED_PRDR;
+#endif
+
+#ifdef SUPPORT_I18N
+ if ( checks & PEER_OFFERED_UTF8
+ && pcre_exec(regex_UTF8, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+ checks &= ~PEER_OFFERED_UTF8;
+#endif
+
+ if ( checks & PEER_OFFERED_DSN
+ && pcre_exec(regex_DSN, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+ checks &= ~PEER_OFFERED_DSN;
+
+ if ( checks & PEER_OFFERED_PIPE
+ && pcre_exec(regex_PIPELINING, NULL, CS buf, bsize, 0,
+ PCRE_EOPT, NULL, 0) < 0)
+ checks &= ~PEER_OFFERED_PIPE;
+
+ if ( checks & PEER_OFFERED_SIZE
+ && pcre_exec(regex_SIZE, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+ checks &= ~PEER_OFFERED_SIZE;
+
+return checks;
+}
+
+
/*************************************************
* Deliver address list to given host *
*************************************************/
BOOL esmtp = TRUE;
BOOL pending_MAIL;
BOOL pass_message = FALSE;
+uschar peer_offered = 0; /*XXX should this be handed on cf. tls_offered, smtp_use_dsn ? */
#ifndef DISABLE_PRDR
-BOOL prdr_offered = FALSE;
BOOL prdr_active;
#endif
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
BOOL utf8_needed = FALSE;
-BOOL utf8_offered = FALSE;
#endif
BOOL dsn_all_lasthop = TRUE;
#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
if (host->dnssec == DS_YES)
{
- if( ( dane_required
- || verify_check_given_host(&ob->hosts_try_dane, host) == OK
- )
- && (rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK
- && dane_required /* do not error on only dane-requested */
+ if( dane_required
+ || verify_check_given_host(&ob->hosts_try_dane, host) == OK
)
{
- set_errno_nohost(addrlist, ERRNO_DNSDEFER,
- string_sprintf("DANE error: tlsa lookup %s",
- rc == DEFER ? "DEFER" : "FAIL"),
- rc, FALSE);
- return rc;
+ if ((rc = tlsa_lookup(host, &tlsa_dnsa, dane_required)) != OK)
+ {
+ set_errno_nohost(addrlist, ERRNO_DNSDEFER,
+ string_sprintf("DANE error: tlsa lookup %s",
+ rc == DEFER ? "DEFER" : "FAIL"),
+ rc, FALSE);
+ return rc;
+ }
+ dane = TRUE;
}
}
else if (dane_required)
set_errno_nohost(addrlist, ERRNO_DNSDEFER,
string_sprintf("DANE error: %s lookup not DNSSEC", host->name),
FAIL, FALSE);
- return FAIL;
+ return FAIL;
}
if (dane)
delayed till here so that $sending_interface and $sending_port are set. */
helo_data = expand_string(ob->helo_data);
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
if (helo_data)
{
uschar * errstr = NULL;
#endif
if (!good_response) goto RESPONSE_FAILED;
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
{
uschar * s;
lookup_dnssec_authenticated = host->dnssec==DS_YES ? US"yes"
if (!good_response) goto RESPONSE_FAILED;
}
- /* Set IGNOREQUOTA if the response to LHLO specifies support and the
- lmtp_ignore_quota option was set. */
-
- igquotstr = (lmtp && ob->lmtp_ignore_quota &&
- pcre_exec(regex_IGNOREQUOTA, NULL, CS buffer, Ustrlen(CS buffer), 0,
- PCRE_EOPT, NULL, 0) >= 0)? US" IGNOREQUOTA" : US"";
+ if (esmtp || lmtp)
+ peer_offered = ehlo_response(buffer, Ustrlen(buffer),
+ PEER_OFFERED_TLS
+ | 0 /* IGNQ checked later */
+ | 0 /* PRDR checked later */
+ | 0 /* UTF8 checked later */
+ | 0 /* DSN checked later */
+ | 0 /* PIPE checked later */
+ | 0 /* SIZE checked later */
+ );
/* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
#ifdef SUPPORT_TLS
- tls_offered = esmtp &&
- pcre_exec(regex_STARTTLS, NULL, CS buffer, Ustrlen(buffer), 0,
- PCRE_EOPT, NULL, 0) >= 0;
-#endif
-
-#ifndef DISABLE_PRDR
- prdr_offered = esmtp
- && pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(buffer), 0,
- PCRE_EOPT, NULL, 0) >= 0
- && verify_check_given_host(&ob->hosts_try_prdr, host) == OK;
-
- if (prdr_offered)
- {DEBUG(D_transport) debug_printf("PRDR usable\n");}
-#endif
-
-#ifdef EXPERIMENTAL_INTERNATIONAL
- if (addrlist->prop.utf8_msg)
- {
- utf8_needed = !addrlist->prop.utf8_downcvt
- && !addrlist->prop.utf8_downcvt_maybe;
- DEBUG(D_transport) if (!utf8_needed) debug_printf("utf8: %s downconvert\n",
- addrlist->prop.utf8_downcvt ? "mandatory" : "optional");
-
- utf8_offered = esmtp
- && pcre_exec(regex_UTF8, NULL, CS buffer, Ustrlen(buffer), 0,
- PCRE_EOPT, NULL, 0) >= 0;
- }
+ tls_offered = !!(peer_offered & PEER_OFFERED_TLS);
#endif
}
if (!smtp_read_response(&inblock, buffer2, sizeof(buffer2), '2',
ob->command_timeout))
{
- if (errno != 0 || buffer2[0] == 0 ||
- (buffer2[0] == '4' && !ob->tls_tempfail_tryclear))
+ if ( errno != 0
+ || buffer2[0] == 0
+ || (buffer2[0] == '4' && !ob->tls_tempfail_tryclear)
+ )
{
Ustrncpy(buffer, buffer2, sizeof(buffer));
goto RESPONSE_FAILED;
if (rc != OK)
{
# ifdef EXPERIMENTAL_DANE
- if (rc == DEFER && dane && !dane_required)
+ if (rc == DEFER && dane)
{
- log_write(0, LOG_MAIN, "DANE attempt failed;"
- " trying CA-root TLS to %s [%s] (not in hosts_require_dane)",
+ log_write(0, LOG_MAIN,
+ "DANE attempt failed; no TLS connection to %s [%s]",
host->name, host->address);
- dane = FALSE;
- goto TLS_NEGOTIATE;
}
# endif
#endif
)
{
+ if (esmtp || lmtp)
+ peer_offered = ehlo_response(buffer, Ustrlen(buffer),
+ 0 /* no TLS */
+ | (lmtp && ob->lmtp_ignore_quota ? PEER_OFFERED_IGNQ : 0)
+ | PEER_OFFERED_PRDR
+#ifdef SUPPORT_I18N
+ | (addrlist->prop.utf8_msg ? PEER_OFFERED_UTF8 : 0)
+ /*XXX if we hand peercaps on to continued-conn processes,
+ must not depend on this addr */
+#endif
+ | PEER_OFFERED_DSN
+ | PEER_OFFERED_PIPE
+ | (ob->size_addition >= 0 ? PEER_OFFERED_SIZE : 0)
+ );
+
/* Set for IGNOREQUOTA if the response to LHLO specifies support and the
lmtp_ignore_quota option was set. */
- igquotstr = (lmtp && ob->lmtp_ignore_quota &&
- pcre_exec(regex_IGNOREQUOTA, NULL, CS buffer, Ustrlen(CS buffer), 0,
- PCRE_EOPT, NULL, 0) >= 0)? US" IGNOREQUOTA" : US"";
+ igquotstr = peer_offered & PEER_OFFERED_IGNQ ? US" IGNOREQUOTA" : US"";
/* If the response to EHLO specified support for the SIZE parameter, note
this, provided size_addition is non-negative. */
- smtp_use_size = esmtp && ob->size_addition >= 0 &&
- pcre_exec(regex_SIZE, NULL, CS buffer, Ustrlen(CS buffer), 0,
- PCRE_EOPT, NULL, 0) >= 0;
+ smtp_use_size = !!(peer_offered & PEER_OFFERED_SIZE);
/* Note whether the server supports PIPELINING. If hosts_avoid_esmtp matched
the current host, esmtp will be false, so PIPELINING can never be used. If
the current host matches hosts_avoid_pipelining, don't do it. */
- smtp_use_pipelining = esmtp
- && verify_check_given_host(&ob->hosts_avoid_pipelining, host) != OK
- && pcre_exec(regex_PIPELINING, NULL, CS buffer, Ustrlen(CS buffer), 0,
- PCRE_EOPT, NULL, 0) >= 0;
+ smtp_use_pipelining = peer_offered & PEER_OFFERED_PIPE
+ && verify_check_given_host(&ob->hosts_avoid_pipelining, host) != OK;
DEBUG(D_transport) debug_printf("%susing PIPELINING\n",
- smtp_use_pipelining? "" : "not ");
+ smtp_use_pipelining ? "" : "not ");
#ifndef DISABLE_PRDR
- prdr_offered = esmtp
- && pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(CS buffer), 0,
- PCRE_EOPT, NULL, 0) >= 0
- && verify_check_given_host(&ob->hosts_try_prdr, host) == OK;
+ if ( peer_offered & PEER_OFFERED_PRDR
+ && verify_check_given_host(&ob->hosts_try_prdr, host) != OK)
+ peer_offered &= ~PEER_OFFERED_PRDR;
- if (prdr_offered)
+ if (peer_offered & PEER_OFFERED_PRDR)
{DEBUG(D_transport) debug_printf("PRDR usable\n");}
#endif
-#ifdef EXPERIMENTAL_INTERNATIONAL
- if (addrlist->prop.utf8_msg)
- utf8_offered = esmtp
- && pcre_exec(regex_UTF8, NULL, CS buffer, Ustrlen(buffer), 0,
- PCRE_EOPT, NULL, 0) >= 0;
-#endif
-
/* Note if the server supports DSN */
- smtp_use_dsn = esmtp
- && pcre_exec(regex_DSN, NULL, CS buffer, Ustrlen(CS buffer), 0,
- PCRE_EOPT, NULL, 0) >= 0;
- DEBUG(D_transport) debug_printf("use_dsn=%d\n", smtp_use_dsn);
+ smtp_use_dsn = !!(peer_offered & PEER_OFFERED_DSN);
+ DEBUG(D_transport) debug_printf("%susing DSN\n", smtp_use_dsn ? "" : "not ");
/* Note if the response to EHLO specifies support for the AUTH extension.
If it has, check that this host is one we want to authenticate to, and do
setting_up = FALSE;
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
+if (addrlist->prop.utf8_msg)
+ {
+ utf8_needed = !addrlist->prop.utf8_downcvt
+ && !addrlist->prop.utf8_downcvt_maybe;
+ DEBUG(D_transport) if (!utf8_needed) debug_printf("utf8: %s downconvert\n",
+ addrlist->prop.utf8_downcvt ? "mandatory" : "optional");
+ }
+
/* If this is an international message we need the host to speak SMTPUTF8 */
-if (utf8_needed && !utf8_offered)
+if (utf8_needed && !(peer_offered & PEER_OFFERED_UTF8))
{
errno = ERRNO_UTF8_FWD;
goto RESPONSE_FAILED;
#ifndef DISABLE_PRDR
prdr_active = FALSE;
-if (prdr_offered)
- {
+if (peer_offered & PEER_OFFERED_PRDR)
for (addr = first_addr; addr; addr = addr->next)
if (addr->transport_return == PENDING_DEFER)
{
}
break;
}
- }
#endif
-#ifdef EXPERIMENTAL_INTERNATIONAL
-if (addrlist->prop.utf8_msg && !addrlist->prop.utf8_downcvt && utf8_offered)
+#ifdef SUPPORT_I18N
+if ( addrlist->prop.utf8_msg
+ && !addrlist->prop.utf8_downcvt
+ && peer_offered & PEER_OFFERED_UTF8
+ )
sprintf(CS p, " SMTPUTF8"), p += 9;
#endif
{
uschar * s = return_path;
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
uschar * errstr = NULL;
/* If we must downconvert, do the from-address here. Remember we had to
for the to-addresses (done below), and also (ugly) for re-doing when building
the delivery log line. */
- if (addrlist->prop.utf8_msg && (addrlist->prop.utf8_downcvt || !utf8_offered))
+ if ( addrlist->prop.utf8_msg
+ && (addrlist->prop.utf8_downcvt || !(peer_offered & PEER_OFFERED_UTF8))
+ )
{
if (s = string_address_utf8_to_alabel(return_path, &errstr), errstr)
{
rcpt_addr = transport_rcpt_address(addr, tblock->rcpt_include_affixes);
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
{
uschar * dummy_errstr;
if ( testflag(addrlist, af_utf8_downcvt)
/* Set up confirmation if needed - applies only to SMTP */
if (
-#ifndef EXPERIMENTAL_EVENT
+#ifdef DISABLE_EVENT
LOGGING(smtp_confirmation) &&
#endif
!lmtp
switch(save_errno)
{
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
case ERRNO_UTF8_FWD:
code = '5';
/*FALLTHROUGH*/
specified in the transports, and therefore not visible at top level, in which
case continue_more won't get set. */
+HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP(close)>>\n");
(void)close(inblock.sock);
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
(void) event_raise(tblock->event_action, US"tcp:close", NULL);
#endif
host_item *h;
DEBUG(D_transport)
debug_printf("hosts_max_try limit reached with this host\n");
- for (h = host; h != NULL; h = h->next)
- if (h->mx != host->mx) break;
- if (h != NULL)
- {
- nexthost = h;
- unexpired_hosts_tried--;
- DEBUG(D_transport) debug_printf("however, a higher MX host exists "
- "and will be tried\n");
- }
+ for (h = host; h; h = h->next) if (h->mx != host->mx)
+ {
+ nexthost = h;
+ unexpired_hosts_tried--;
+ DEBUG(D_transport) debug_printf("however, a higher MX host exists "
+ "and will be tried\n");
+ break;
+ }
}
/* Attempt the delivery. */
first_addr->basic_errno != ERRNO_TLSFAILURE)
write_logs(first_addr, host);
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
if (rc == DEFER)
deferred_event_raise(first_addr, host);
#endif
&message_defer, TRUE);
if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL)
write_logs(first_addr, host);
-# ifdef EXPERIMENTAL_EVENT
+# ifndef DISABLE_EVENT
if (rc == DEFER)
deferred_event_raise(first_addr, host);
# endif
else if (expired)
{
setflag(addr, af_pass_message); /* This is not a security risk */
- addr->message = (ob->delay_after_cutoff)?
- US"retry time not reached for any host after a long failure period" :
- US"all hosts have been failing for a long time and were last tried "
+ addr->message = ob->delay_after_cutoff
+ ? US"retry time not reached for any host after a long failure period"
+ : US"all hosts have been failing for a long time and were last tried "
"after this message arrived";
/* If we are already using fallback hosts, or there are no fallback hosts
}
else
{
+ const char * s;
if (hosts_retry == hosts_total)
- addr->message = US"retry time not reached for any host";
+ s = "retry time not reached for any host%s";
else if (hosts_fail == hosts_total)
- addr->message = US"all host address lookups failed permanently";
+ s = "all host address lookups%s failed permanently";
else if (hosts_defer == hosts_total)
- addr->message = US"all host address lookups failed temporarily";
+ s = "all host address lookups%s failed temporarily";
else if (hosts_serial == hosts_total)
- addr->message = US"connection limit reached for all hosts";
+ s = "connection limit reached for all hosts%s";
else if (hosts_fail+hosts_defer == hosts_total)
- addr->message = US"all host address lookups failed";
- else addr->message = US"some host address lookups failed and retry time "
- "not reached for other hosts or connection limit reached";
+ s = "all host address lookups%s failed";
+ else
+ s = "some host address lookups failed and retry time "
+ "not reached for other hosts or connection limit reached%s";
+
+ addr->message = string_sprintf(s,
+ addr->domain ? string_sprintf(" for '%s'", addr->domain) : US"");
}
}
}