*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
/* See the file NOTICE for conditions of use and distribution. */
#include "../exim.h"
#include "smtp.h"
+#if defined(SUPPORT_DANE) && defined(DISABLE_TLS)
+# error TLS is required for DANE
+#endif
+
/* Options specific to the smtp transport. This transport also supports LMTP
over TCP/IP. The options must be in alphabetic order (note that "_" comes
before the lower case letters). Some live in the transport_instance block so as
to be publicly visible; these are flagged with opt_public. */
+#define LOFF(field) OPT_OFF(smtp_transport_options_block, field)
+
optionlist smtp_transport_options[] = {
{ "*expand_multi_domain", opt_stringptr | opt_hidden | opt_public,
- (void *)offsetof(transport_instance, expand_multi_domain) },
+ OPT_OFF(transport_instance, expand_multi_domain) },
{ "*expand_retry_include_ip_address", opt_stringptr | opt_hidden,
- (void *)(offsetof(smtp_transport_options_block, expand_retry_include_ip_address)) },
+ LOFF(expand_retry_include_ip_address) },
{ "address_retry_include_sender", opt_bool,
- (void *)offsetof(smtp_transport_options_block, address_retry_include_sender) },
- { "allow_localhost", opt_bool,
- (void *)offsetof(smtp_transport_options_block, allow_localhost) },
+ LOFF(address_retry_include_sender) },
+ { "allow_localhost", opt_bool, LOFF(allow_localhost) },
#ifdef EXPERIMENTAL_ARC
- { "arc_sign", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, arc_sign) },
-#endif
- { "authenticated_sender", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, authenticated_sender) },
- { "authenticated_sender_force", opt_bool,
- (void *)offsetof(smtp_transport_options_block, authenticated_sender_force) },
- { "command_timeout", opt_time,
- (void *)offsetof(smtp_transport_options_block, command_timeout) },
- { "connect_timeout", opt_time,
- (void *)offsetof(smtp_transport_options_block, connect_timeout) },
+ { "arc_sign", opt_stringptr, LOFF(arc_sign) },
+#endif
+ { "authenticated_sender", opt_stringptr, LOFF(authenticated_sender) },
+ { "authenticated_sender_force", opt_bool, LOFF(authenticated_sender_force) },
+ { "command_timeout", opt_time, LOFF(command_timeout) },
+ { "connect_timeout", opt_time, LOFF(connect_timeout) },
{ "connection_max_messages", opt_int | opt_public,
- (void *)offsetof(transport_instance, connection_max_messages) },
+ OPT_OFF(transport_instance, connection_max_messages) },
# ifdef SUPPORT_DANE
- { "dane_require_tls_ciphers", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dane_require_tls_ciphers) },
+ { "dane_require_tls_ciphers", opt_stringptr, LOFF(dane_require_tls_ciphers) },
# endif
- { "data_timeout", opt_time,
- (void *)offsetof(smtp_transport_options_block, data_timeout) },
- { "delay_after_cutoff", opt_bool,
- (void *)offsetof(smtp_transport_options_block, delay_after_cutoff) },
+ { "data_timeout", opt_time, LOFF(data_timeout) },
+ { "delay_after_cutoff", opt_bool, LOFF(delay_after_cutoff) },
#ifndef DISABLE_DKIM
- { "dkim_canon", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dkim.dkim_canon) },
- { "dkim_domain", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dkim.dkim_domain) },
- { "dkim_hash", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dkim.dkim_hash) },
- { "dkim_identity", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dkim.dkim_identity) },
- { "dkim_private_key", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dkim.dkim_private_key) },
- { "dkim_selector", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dkim.dkim_selector) },
- { "dkim_sign_headers", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dkim.dkim_sign_headers) },
- { "dkim_strict", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dkim.dkim_strict) },
- { "dkim_timestamps", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dkim.dkim_timestamps) },
-#endif
- { "dns_qualify_single", opt_bool,
- (void *)offsetof(smtp_transport_options_block, dns_qualify_single) },
- { "dns_search_parents", opt_bool,
- (void *)offsetof(smtp_transport_options_block, dns_search_parents) },
- { "dnssec_request_domains", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dnssec.request) },
- { "dnssec_require_domains", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dnssec.require) },
- { "dscp", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, dscp) },
- { "fallback_hosts", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, fallback_hosts) },
- { "final_timeout", opt_time,
- (void *)offsetof(smtp_transport_options_block, final_timeout) },
- { "gethostbyname", opt_bool,
- (void *)offsetof(smtp_transport_options_block, gethostbyname) },
- { "helo_data", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, helo_data) },
- { "hosts", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts) },
- { "hosts_avoid_esmtp", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts_avoid_esmtp) },
- { "hosts_avoid_pipelining", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts_avoid_pipelining) },
+ { "dkim_canon", opt_stringptr, LOFF(dkim.dkim_canon) },
+ { "dkim_domain", opt_stringptr, LOFF(dkim.dkim_domain) },
+ { "dkim_hash", opt_stringptr, LOFF(dkim.dkim_hash) },
+ { "dkim_identity", opt_stringptr, LOFF(dkim.dkim_identity) },
+ { "dkim_private_key", opt_stringptr, LOFF(dkim.dkim_private_key) },
+ { "dkim_selector", opt_stringptr, LOFF(dkim.dkim_selector) },
+ { "dkim_sign_headers", opt_stringptr, LOFF(dkim.dkim_sign_headers) },
+ { "dkim_strict", opt_stringptr, LOFF(dkim.dkim_strict) },
+ { "dkim_timestamps", opt_stringptr, LOFF(dkim.dkim_timestamps) },
+#endif
+ { "dns_qualify_single", opt_bool, LOFF(dns_qualify_single) },
+ { "dns_search_parents", opt_bool, LOFF(dns_search_parents) },
+ { "dnssec_request_domains", opt_stringptr, LOFF(dnssec.request) },
+ { "dnssec_require_domains", opt_stringptr, LOFF(dnssec.require) },
+ { "dscp", opt_stringptr, LOFF(dscp) },
+ { "fallback_hosts", opt_stringptr, LOFF(fallback_hosts) },
+ { "final_timeout", opt_time, LOFF(final_timeout) },
+ { "gethostbyname", opt_bool, LOFF(gethostbyname) },
+ { "helo_data", opt_stringptr, LOFF(helo_data) },
+ { "hosts", opt_stringptr, LOFF(hosts) },
+ { "hosts_avoid_esmtp", opt_stringptr, LOFF(hosts_avoid_esmtp) },
+ { "hosts_avoid_pipelining", opt_stringptr, LOFF(hosts_avoid_pipelining) },
#ifndef DISABLE_TLS
- { "hosts_avoid_tls", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts_avoid_tls) },
+ { "hosts_avoid_tls", opt_stringptr, LOFF(hosts_avoid_tls) },
#endif
- { "hosts_max_try", opt_int,
- (void *)offsetof(smtp_transport_options_block, hosts_max_try) },
- { "hosts_max_try_hardlimit", opt_int,
- (void *)offsetof(smtp_transport_options_block, hosts_max_try_hardlimit) },
+ { "hosts_max_try", opt_int, LOFF(hosts_max_try) },
+ { "hosts_max_try_hardlimit", opt_int, LOFF(hosts_max_try_hardlimit) },
#ifndef DISABLE_TLS
- { "hosts_nopass_tls", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts_nopass_tls) },
- { "hosts_noproxy_tls", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts_noproxy_tls) },
-#endif
- { "hosts_override", opt_bool,
- (void *)offsetof(smtp_transport_options_block, hosts_override) },
-#ifdef EXPERIMENTAL_PIPE_CONNECT
- { "hosts_pipe_connect", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts_pipe_connect) },
-#endif
- { "hosts_randomize", opt_bool,
- (void *)offsetof(smtp_transport_options_block, hosts_randomize) },
+ { "hosts_nopass_tls", opt_stringptr, LOFF(hosts_nopass_tls) },
+ { "hosts_noproxy_tls", opt_stringptr, LOFF(hosts_noproxy_tls) },
+#endif
+ { "hosts_override", opt_bool, LOFF(hosts_override) },
+#ifndef DISABLE_PIPE_CONNECT
+ { "hosts_pipe_connect", opt_stringptr, LOFF(hosts_pipe_connect) },
+#endif
+ { "hosts_randomize", opt_bool, LOFF(hosts_randomize) },
#if !defined(DISABLE_TLS) && !defined(DISABLE_OCSP)
- { "hosts_request_ocsp", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts_request_ocsp) },
+ { "hosts_request_ocsp", opt_stringptr, LOFF(hosts_request_ocsp) },
#endif
- { "hosts_require_auth", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts_require_auth) },
+ { "hosts_require_auth", opt_stringptr, LOFF(hosts_require_auth) },
#ifndef DISABLE_TLS
# ifdef SUPPORT_DANE
- { "hosts_require_dane", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts_require_dane) },
+ { "hosts_require_dane", opt_stringptr, LOFF(hosts_require_dane) },
# endif
# ifndef DISABLE_OCSP
- { "hosts_require_ocsp", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts_require_ocsp) },
+ { "hosts_require_ocsp", opt_stringptr, LOFF(hosts_require_ocsp) },
# endif
- { "hosts_require_tls", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts_require_tls) },
+ { "hosts_require_tls", opt_stringptr, LOFF(hosts_require_tls) },
#endif
- { "hosts_try_auth", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts_try_auth) },
- { "hosts_try_chunking", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts_try_chunking) },
+ { "hosts_try_auth", opt_stringptr, LOFF(hosts_try_auth) },
+ { "hosts_try_chunking", opt_stringptr, LOFF(hosts_try_chunking) },
#ifdef SUPPORT_DANE
- { "hosts_try_dane", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts_try_dane) },
+ { "hosts_try_dane", opt_stringptr, LOFF(hosts_try_dane) },
#endif
- { "hosts_try_fastopen", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts_try_fastopen) },
+ { "hosts_try_fastopen", opt_stringptr, LOFF(hosts_try_fastopen) },
#ifndef DISABLE_PRDR
- { "hosts_try_prdr", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts_try_prdr) },
+ { "hosts_try_prdr", opt_stringptr, LOFF(hosts_try_prdr) },
#endif
#ifndef DISABLE_TLS
- { "hosts_verify_avoid_tls", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, hosts_verify_avoid_tls) },
-#endif
- { "interface", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, interface) },
- { "keepalive", opt_bool,
- (void *)offsetof(smtp_transport_options_block, keepalive) },
- { "lmtp_ignore_quota", opt_bool,
- (void *)offsetof(smtp_transport_options_block, lmtp_ignore_quota) },
+ { "hosts_verify_avoid_tls", opt_stringptr, LOFF(hosts_verify_avoid_tls) },
+#endif
+ { "interface", opt_stringptr, LOFF(interface) },
+ { "keepalive", opt_bool, LOFF(keepalive) },
+ { "lmtp_ignore_quota", opt_bool, LOFF(lmtp_ignore_quota) },
{ "max_rcpt", opt_int | opt_public,
- (void *)offsetof(transport_instance, max_addresses) },
+ OPT_OFF(transport_instance, max_addresses) },
+ { "message_linelength_limit", opt_int, LOFF(message_linelength_limit) },
{ "multi_domain", opt_expand_bool | opt_public,
- (void *)offsetof(transport_instance, multi_domain) },
- { "port", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, port) },
- { "protocol", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, protocol) },
- { "retry_include_ip_address", opt_expand_bool,
- (void *)offsetof(smtp_transport_options_block, retry_include_ip_address) },
- { "serialize_hosts", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, serialize_hosts) },
- { "size_addition", opt_int,
- (void *)offsetof(smtp_transport_options_block, size_addition) },
+ OPT_OFF(transport_instance, multi_domain) },
+ { "port", opt_stringptr, LOFF(port) },
+ { "protocol", opt_stringptr, LOFF(protocol) },
+ { "retry_include_ip_address", opt_expand_bool, LOFF(retry_include_ip_address) },
+ { "serialize_hosts", opt_stringptr, LOFF(serialize_hosts) },
+ { "size_addition", opt_int, LOFF(size_addition) },
#ifdef SUPPORT_SOCKS
- { "socks_proxy", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, socks_proxy) },
+ { "socks_proxy", opt_stringptr, LOFF(socks_proxy) },
#endif
#ifndef DISABLE_TLS
- { "tls_certificate", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, tls_certificate) },
- { "tls_crl", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, tls_crl) },
- { "tls_dh_min_bits", opt_int,
- (void *)offsetof(smtp_transport_options_block, tls_dh_min_bits) },
- { "tls_privatekey", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, tls_privatekey) },
- { "tls_require_ciphers", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, tls_require_ciphers) },
-# ifdef EXPERIMENTAL_TLS_RESUME
- { "tls_resumption_hosts", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, tls_resumption_hosts) },
+ { "tls_certificate", opt_stringptr, LOFF(tls_certificate) },
+ { "tls_crl", opt_stringptr, LOFF(tls_crl) },
+ { "tls_dh_min_bits", opt_int, LOFF(tls_dh_min_bits) },
+ { "tls_privatekey", opt_stringptr, LOFF(tls_privatekey) },
+ { "tls_require_ciphers", opt_stringptr, LOFF(tls_require_ciphers) },
+# ifndef DISABLE_TLS_RESUME
+ { "tls_resumption_hosts", opt_stringptr, LOFF(tls_resumption_hosts) },
# endif
- { "tls_sni", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, tls_sni) },
- { "tls_tempfail_tryclear", opt_bool,
- (void *)offsetof(smtp_transport_options_block, tls_tempfail_tryclear) },
- { "tls_try_verify_hosts", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, tls_try_verify_hosts) },
- { "tls_verify_cert_hostnames", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block,tls_verify_cert_hostnames)},
- { "tls_verify_certificates", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, tls_verify_certificates) },
- { "tls_verify_hosts", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, tls_verify_hosts) },
+ { "tls_sni", opt_stringptr, LOFF(tls_sni) },
+ { "tls_tempfail_tryclear", opt_bool, LOFF(tls_tempfail_tryclear) },
+ { "tls_try_verify_hosts", opt_stringptr, LOFF(tls_try_verify_hosts) },
+ { "tls_verify_cert_hostnames", opt_stringptr, LOFF(tls_verify_cert_hostnames)},
+ { "tls_verify_certificates", opt_stringptr, LOFF(tls_verify_certificates) },
+ { "tls_verify_hosts", opt_stringptr, LOFF(tls_verify_hosts) },
#endif
#ifdef SUPPORT_I18N
- { "utf8_downconvert", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, utf8_downconvert) },
+ { "utf8_downconvert", opt_stringptr, LOFF(utf8_downconvert) },
#endif
};
.hosts_require_dane = NULL,
.dane_require_tls_ciphers = NULL,
#endif
- .hosts_try_fastopen = NULL,
+ .hosts_try_fastopen = US"*",
#ifndef DISABLE_PRDR
.hosts_try_prdr = US"*",
#endif
.hosts_avoid_tls = NULL,
.hosts_verify_avoid_tls = NULL,
.hosts_avoid_pipelining = NULL,
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
.hosts_pipe_connect = NULL,
#endif
.hosts_avoid_esmtp = NULL,
.size_addition = 1024,
.hosts_max_try = 5,
.hosts_max_try_hardlimit = 50,
+ .message_linelength_limit = 998,
.address_retry_include_sender = TRUE,
.allow_localhost = FALSE,
.authenticated_sender_force = FALSE,
.gethostbyname = FALSE,
.dns_qualify_single = TRUE,
.dns_search_parents = FALSE,
- .dnssec = { .request=NULL, .require=NULL },
+ .dnssec = { .request= US"*", .require=NULL },
.delay_after_cutoff = TRUE,
.hosts_override = FALSE,
.hosts_randomize = FALSE,
.tls_verify_certificates = US"system",
.tls_dh_min_bits = EXIM_CLIENT_DH_DEFAULT_MIN_BITS,
.tls_tempfail_tryclear = TRUE,
-# ifdef EXPERIMENTAL_TLS_RESUME
+# ifndef DISABLE_TLS_RESUME
.tls_resumption_hosts = NULL,
# endif
.tls_verify_hosts = NULL,
.tls_verify_cert_hostnames = US"*",
#endif
#ifdef SUPPORT_I18N
- .utf8_downconvert = NULL,
+ .utf8_downconvert = US"-1",
#endif
#ifndef DISABLE_DKIM
.dkim =
static unsigned ehlo_response(uschar * buf, unsigned checks);
+/******************************************************************************/
+
+void
+smtp_deliver_init(void)
+{
+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);
+
+#ifndef DISABLE_PIPE_CONNECT
+if (!regex_EARLY_PIPE) regex_EARLY_PIPE =
+ regex_must_compile(US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)", FALSE, TRUE);
+#endif
+}
+
+
/*************************************************
* Setup entry point *
*************************************************/
{
smtp_transport_options_block *ob = SOB tblock->options_block;
-errmsg = errmsg; /* Keep picky compilers happy */
-uid = uid;
-gid = gid;
-
/* Pass back options if required. This interface is getting very messy. */
if (tf)
host if set, mark addrs as having used this host
smtp_greeting from peer
helo_response from peer
+ start points to timestamp of delivery start
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
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
+ const uschar * smtp_greeting, const uschar * helo_response,
#endif
+ struct timeval * start
)
{
int orvalue = 0;
+struct timeval deliver_time;
+
if (errno_value == ERRNO_CONNECTTIMEOUT)
{
errno_value = ETIMEDOUT;
orvalue = RTEF_CTOUT;
}
+timesince(&deliver_time, start);
+
for (address_item * addr = addrlist; addr; addr = addr->next)
if (addr->transport_return >= PENDING)
{
addr->basic_errno = errno_value;
addr->more_errno |= orvalue;
+ addr->delivery_time = deliver_time;
if (msg)
{
addr->message = msg;
}
static void
-set_errno_nohost(address_item *addrlist, int errno_value, uschar *msg, int rc,
- BOOL pass_message)
+set_errno_nohost(address_item * addrlist, int errno_value, uschar * msg, int rc,
+ BOOL pass_message, struct timeval * start)
{
-set_errno(addrlist, errno_value, msg, rc, pass_message, NULL
+set_errno(addrlist, errno_value, msg, rc, pass_message, NULL,
#ifdef EXPERIMENTAL_DSN_INFO
- , NULL, NULL
+ NULL, NULL,
#endif
- );
+ start);
}
transport_count);
return FALSE;
- case ECONNREFUSED: /* First-read error on a TFO conn */
- if (verify_mode) *message = US strerror(*errno_value);
- return FALSE; /* nonverify, do not set message */
-
case ERRNO_SMTPFORMAT: /* Handle malformed SMTP response */
s = string_printing(buffer);
while (isspace(*s)) s++;
return FALSE;
case ERRNO_WRITEINCOMPLETE: /* failure to write a complete data block */
- *message = string_sprintf("failed to write a data block");
+ *message = US"failed to write a data block";
return FALSE;
#ifdef SUPPORT_I18N
Arguments:
addr the address item containing error information
host the current host
+ evstr the event
Returns: nothing
*/
static void
-deferred_event_raise(address_item *addr, host_item *host)
+deferred_event_raise(address_item * addr, host_item * host, uschar * evstr)
{
uschar * action = addr->transport->event_action;
const uschar * save_domain;
deliver_domain = addr->domain;
deliver_localpart = addr->local_part;
-(void) event_raise(action, US"msg:host:defer",
+(void) event_raise(action, evstr,
addr->message
? addr->basic_errno > 0
? string_sprintf("%s: %s", addr->message, strerror(addr->basic_errno))
#ifdef EXPERIMENTAL_DSN_INFO
sx->helo_response = string_copy(sx->buffer);
#endif
+#ifndef DISABLE_EVENT
+(void) event_raise(sx->conn_args.tblock->event_action,
+ US"smtp:ehlo", sx->buffer);
+#endif
return TRUE;
}
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
static uschar *
ehlo_cache_key(const smtp_context * sx)
{
sx->ehlo_resp = er->data;
dbfn_close(dbm_file);
DEBUG(D_transport) debug_printf(
- "EHLO response bits from cache: cleartext 0x%04x crypted 0x%04x\n",
- er->data.cleartext_features, er->data.crypted_features);
+ "EHLO response bits from cache: cleartext 0x%04x/0x%04x crypted 0x%04x/0x%04x\n",
+ er->data.cleartext_features, er->data.cleartext_auths,
+ er->data.crypted_features, er->data.crypted_auths);
return TRUE;
}
dbfn_close(dbm_file);
for (au = auths, authnum = 0; au; au = au->next, authnum++) if (au->client)
{
const uschar * list = names;
- int sep = ' ';
- uschar name[32];
-
- while (string_nextinlist(&list, &sep, name, sizeof(name)))
- if (strcmpic(au->public_name, name) == 0)
+ uschar * s;
+ for (int sep = ' '; s = string_nextinlist(&list, &sep, NULL, 0); )
+ if (strcmpic(au->public_name, s) == 0)
{ authbits |= BIT(authnum); break; }
}
smtp_transport_options_block * ob = sx->conn_args.ob;
int yield = 0;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
int rc;
if ((rc = smtp_reap_early_pipe(sx, &count)) != OK)
return rc == FAIL ? -4 : -5;
{
DEBUG(D_transport) debug_printf("%s expect mail\n", __FUNCTION__);
count--;
+ sx->pending_MAIL = FALSE;
if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
'2', ob->command_timeout))
{
{
uschar *message = string_sprintf("SMTP timeout after RCPT TO:<%s>",
transport_rcpt_address(addr, sx->conn_args.tblock->rcpt_include_affixes));
- set_errno_nohost(sx->first_addr, ETIMEDOUT, message, DEFER, FALSE);
+ set_errno_nohost(sx->first_addr, ETIMEDOUT, message, DEFER, FALSE, &sx->delivery_start);
retry_add_item(addr, addr->address_retry_key, 0);
update_waiting = FALSE;
return -1;
else if (errno != 0 || sx->buffer[0] == 0)
{
- string_format(big_buffer, big_buffer_size, "RCPT TO:<%s>",
+ gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer }, * g = &gs;
+
+ /* Use taint-unchecked routines for writing into big_buffer, trusting
+ that we'll never expand it. */
+
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "RCPT TO:<%s>",
transport_rcpt_address(addr, sx->conn_args.tblock->rcpt_include_affixes));
+ string_from_gstring(g);
return -2;
}
event_defer_errno = addr->more_errno;
msg_event_raise(US"msg:rcpt:host:defer", addr);
#endif
+ /* If a 452 and we've had at least one 2xx or 5xx, set next_addr to the
+ start point for another MAIL command. */
- /* Log temporary errors if there are more hosts to be tried.
- If not, log this last one in the == line. */
-
- if (sx->conn_args.host->next)
- if (LOGGING(outgoing_port))
- log_write(0, LOG_MAIN, "H=%s [%s]:%d %s", sx->conn_args.host->name,
- sx->conn_args.host->address,
- sx->port == PORT_NONE ? 25 : sx->port, addr->message);
- else
- log_write(0, LOG_MAIN, "H=%s [%s]: %s", sx->conn_args.host->name,
- sx->conn_args.host->address, addr->message);
+ if (addr->more_errno >> 8 == 52 && yield & 3)
+ {
+ if (!sx->RCPT_452)
+ {
+ DEBUG(D_transport)
+ debug_printf("%s: seen first 452 too-many-rcpts\n", __FUNCTION__);
+ sx->RCPT_452 = TRUE;
+ sx->next_addr = addr;
+ }
+ addr->transport_return = PENDING_DEFER;
+ addr->basic_errno = 0;
+ }
+ else
+ {
+ /* Log temporary errors if there are more hosts to be tried.
+ If not, log this last one in the == line. */
+
+ if (sx->conn_args.host->next)
+ if (LOGGING(outgoing_port))
+ log_write(0, LOG_MAIN, "H=%s [%s]:%d %s", sx->conn_args.host->name,
+ sx->conn_args.host->address,
+ sx->port == PORT_NONE ? 25 : sx->port, addr->message);
+ else
+ log_write(0, LOG_MAIN, "H=%s [%s]: %s", sx->conn_args.host->name,
+ sx->conn_args.host->address, addr->message);
#ifndef DISABLE_EVENT
- else
- msg_event_raise(US"msg:rcpt:defer", addr);
+ 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. */
+ /* Do not put this message on the list of those waiting for specific
+ hosts, as otherwise it is likely to be tried too often. */
- update_waiting = FALSE;
+ update_waiting = FALSE;
- /* Add a retry item for the address so that it doesn't get tried again
- too soon. If address_retry_include_sender is true, add the sender address
- to the retry key. */
+ /* Add a retry item for the address so that it doesn't get tried again
+ too soon. If address_retry_include_sender is true, add the sender address
+ to the retry key. */
- retry_add_item(addr,
- ob->address_retry_include_sender
- ? string_sprintf("%s:<%s>", addr->address_retry_key, sender_address)
- : addr->address_retry_key,
- 0);
+ retry_add_item(addr,
+ ob->address_retry_include_sender
+ ? string_sprintf("%s:<%s>", addr->address_retry_key, sender_address)
+ : addr->address_retry_key,
+ 0);
+ }
}
}
}
case ERROR:
set_errno_nohost(sx->addrlist, ERRNO_AUTHPROB, string_copy(sx->buffer),
- DEFER, FALSE);
+ DEFER, FALSE, &sx->delivery_start);
return ERROR;
}
return OK;
host_item * host = sx->conn_args.host; /* host to deliver to */
smtp_transport_options_block * ob = sx->conn_args.ob; /* transport options */
int require_auth = verify_check_given_host(CUSS &ob->hosts_require_auth, host);
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
unsigned short authbits = tls_out.active.sock >= 0
? sx->ehlo_resp.crypted_auths : sx->ehlo_resp.cleartext_auths;
#endif
if ( sx->esmtp
&&
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
sx->early_pipe_active ? authbits
:
#endif
uschar * names = NULL;
expand_nmax = -1; /* reset */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (!sx->early_pipe_active)
#endif
names = string_copyn(expand_nstring[1], expand_nlength[1]);
DEBUG(D_transport) debug_printf("scanning authentication mechanisms\n");
fail_reason = US"no common mechanisms were found";
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (sx->early_pipe_active)
{
/* Scan our authenticators (which support use by a client and were offered
if (require_auth == OK && !f.smtp_authenticated)
{
+ invalidate_ehlo_cache_entry(sx);
set_errno_nohost(sx->addrlist, ERRNO_AUTHFAIL,
string_sprintf("authentication required but %s", fail_reason), DEFER,
- FALSE);
+ FALSE, &sx->delivery_start);
return DEFER;
}
/* Construct AUTH appendix string for MAIL TO */
/*
Arguments
- buffer to build string
+ sx context for smtp connection
+ p point in sx->buffer to build string
addrlist chain of potential addresses to deliver
- ob transport options
Globals f.smtp_authenticated
client_authenticated_sender
Return True on error, otherwise buffer has (possibly empty) terminated string
*/
-BOOL
-smtp_mail_auth_str(uschar *buffer, unsigned bufsize, address_item *addrlist,
- smtp_transport_options_block *ob)
+static BOOL
+smtp_mail_auth_str(smtp_context * sx, uschar * p, address_item * addrlist)
{
-uschar *local_authenticated_sender = authenticated_sender;
+smtp_transport_options_block * ob = sx->conn_args.ob;
+uschar * local_authenticated_sender = authenticated_sender;
#ifdef notdef
- debug_printf("smtp_mail_auth_str: as<%s> os<%s> SA<%s>\n", authenticated_sender, ob->authenticated_sender, f.smtp_authenticated?"Y":"N");
+ debug_printf("smtp_mail_auth_str: as<%s> os<%s> SA<%s>\n",
+ authenticated_sender, ob->authenticated_sender, f.smtp_authenticated?"Y":"N");
#endif
-if (ob->authenticated_sender != NULL)
+if (ob->authenticated_sender)
{
- uschar *new = expand_string(ob->authenticated_sender);
- if (new == NULL)
+ uschar * new = expand_string(ob->authenticated_sender);
+ if (!new)
{
if (!f.expand_string_forcedfail)
{
uschar *message = string_sprintf("failed to expand "
"authenticated_sender: %s", expand_string_message);
- set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
+ set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, &sx->delivery_start);
return TRUE;
}
}
- else if (new[0] != 0) local_authenticated_sender = new;
+ else if (*new)
+ local_authenticated_sender = new;
}
/* Add the authenticated sender address if present */
-if ((f.smtp_authenticated || ob->authenticated_sender_force) &&
- local_authenticated_sender != NULL)
+if ( (f.smtp_authenticated || ob->authenticated_sender_force)
+ && local_authenticated_sender)
{
- string_format(buffer, bufsize, " AUTH=%s",
+ string_format_nt(p, sizeof(sx->buffer) - (p-sx->buffer), " AUTH=%s",
auth_xtextencode(local_authenticated_sender,
- Ustrlen(local_authenticated_sender)));
+ Ustrlen(local_authenticated_sender)));
client_authenticated_sender = string_copy(local_authenticated_sender);
}
else
- *buffer= 0;
+ *p = 0;
return FALSE;
}
-#ifdef SUPPORT_DANE
-/* Lookup TLSA record for host/port.
-Return: OK success with dnssec; DANE mode
- DEFER Do not use this host now, may retry later
- FAIL_FORCED No TLSA record; DANE not usable
- FAIL Do not use this connection
-*/
-
-int
-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];
-const uschar * fullname = buffer;
-int rc;
-BOOL sec;
-
-/* TLSA lookup string */
-(void)sprintf(CS buffer, "_%d._tcp.%.256s", host->port, host->name);
-
-rc = dns_lookup(dnsa, buffer, T_TLSA, &fullname);
-sec = dns_is_secure(dnsa);
-DEBUG(D_transport)
- debug_printf("TLSA lookup ret %d %sDNSSEC\n", rc, sec ? "" : "not ");
-
-switch (rc)
- {
- case DNS_AGAIN:
- return DEFER; /* just defer this TLS'd conn */
-
- case DNS_SUCCEED:
- if (sec)
- {
- DEBUG(D_transport)
- {
- dns_scan dnss;
- for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
- rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
- if (rr->type == T_TLSA && rr->size > 3)
- {
- uint16_t payload_length = rr->size - 3;
- uschar s[MAX_TLSA_EXPANDED_SIZE], * sp = s, * p = US rr->data;
-
- sp += sprintf(CS sp, "%d ", *p++); /* usage */
- sp += sprintf(CS sp, "%d ", *p++); /* selector */
- sp += sprintf(CS sp, "%d ", *p++); /* matchtype */
- while (payload_length-- > 0 && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4))
- sp += sprintf(CS sp, "%02x", *p++);
-
- debug_printf(" %s\n", s);
- }
- }
- return OK;
- }
- log_write(0, LOG_MAIN,
- "DANE error: TLSA lookup for %s not DNSSEC", host->name);
- /*FALLTRHOUGH*/
-
- case DNS_NODATA: /* no TLSA RR for this lookup */
- case DNS_NOMATCH: /* no records at all for this lookup */
- return dane_required ? FAIL : FAIL_FORCED;
-
- default:
- case DNS_FAIL:
- return dane_required ? FAIL : DEFER;
- }
-}
-#endif
-
-
-
typedef struct smtp_compare_s
{
uschar *current_sender_address;
smtp_local_identity(s_compare->current_sender_address, s_compare->tblock);
if (!(new_sender_address = deliver_get_sender_address(message_id)))
- return 0;
+ return FALSE;
message_local_identity =
smtp_local_identity(new_sender_address, s_compare->tblock);
&& pcre_exec(regex_SIZE, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
checks &= ~OPTION_SIZE;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if ( checks & OPTION_EARLY_PIPE
&& pcre_exec(regex_EARLY_PIPE, NULL, CS buf, bsize, 0,
PCRE_EOPT, NULL, 0) < 0)
if (chunk_size > 0)
{
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
BOOL new_conn = !!(sx->outblock.conn_args);
#endif
if((cmd_count = smtp_write_command(sx,
) < 0) return ERROR;
if (flags & tc_chunk_last)
data_command = string_copy(big_buffer); /* Save for later error message */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
/* That command write could have been the one that made the connection.
Copy the fd from the client conn ctx (smtp transport specific) to the
generic transport ctx. */
case -5: errno = ERRNO_TLSFAILURE;
return DEFER;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
case -4: /* non-2xx for pipelined banner or EHLO */
#endif
case -1: /* Timeout on RCPT */
BOOL pass_message = FALSE;
uschar * message = NULL;
int yield = OK;
-int rc;
#ifndef DISABLE_TLS
uschar * tls_errstr;
#endif
sx->lmtp = strcmpic(ob->protocol, US"lmtp") == 0;
sx->smtps = strcmpic(ob->protocol, US"smtps") == 0;
-sx->ok = FALSE;
+/* sx->ok = FALSE; */
sx->send_rset = TRUE;
sx->send_quit = TRUE;
sx->setting_up = TRUE;
sx->esmtp = TRUE;
-sx->esmtp_sent = FALSE;
+/* sx->esmtp_sent = FALSE; */
#ifdef SUPPORT_I18N
-sx->utf8_needed = FALSE;
+/* sx->utf8_needed = FALSE; */
#endif
sx->dsn_all_lasthop = TRUE;
#ifdef SUPPORT_DANE
-sx->conn_args.dane = FALSE;
+/* sx->conn_args.dane = FALSE; */
sx->dane_required =
verify_check_given_host(CUSS &ob->hosts_require_dane, sx->conn_args.host) == OK;
#endif
-#ifdef EXPERIMENTAL_PIPE_CONNECT
-sx->early_pipe_active = sx->early_pipe_ok = FALSE;
-sx->ehlo_resp.cleartext_features = sx->ehlo_resp.crypted_features = 0;
-sx->pending_BANNER = sx->pending_EHLO = FALSE;
+#ifndef DISABLE_PIPE_CONNECT
+/* sx->early_pipe_active = sx->early_pipe_ok = FALSE; */
+/* sx->ehlo_resp.cleartext_features = sx->ehlo_resp.crypted_features = 0; */
+/* sx->pending_BANNER = sx->pending_EHLO = sx->pending_MAIL = FALSE; */
#endif
if ((sx->max_rcpt = sx->conn_args.tblock->max_addresses) == 0) sx->max_rcpt = 999999;
-sx->peer_offered = 0;
-sx->avoid_option = 0;
+/* sx->peer_offered = 0; */
+/* sx->avoid_option = 0; */
sx->igquotstr = US"";
if (!sx->helo_data) sx->helo_data = ob->helo_data;
#ifdef EXPERIMENTAL_DSN_INFO
-sx->smtp_greeting = NULL;
-sx->helo_response = NULL;
+/* sx->smtp_greeting = NULL; */
+/* sx->helo_response = NULL; */
#endif
smtp_command = US"initial connection";
-sx->buffer[0] = '\0';
+/* sx->buffer[0] = '\0'; */
/* Set up the buffer for reading SMTP response packets. */
sx->outblock.buffer = sx->outbuffer;
sx->outblock.buffersize = sizeof(sx->outbuffer);
sx->outblock.ptr = sx->outbuffer;
-sx->outblock.cmd_count = 0;
-sx->outblock.authenticating = FALSE;
-sx->outblock.conn_args = NULL;
+/* sx->outblock.cmd_count = 0; */
+/* sx->outblock.authenticating = FALSE; */
+/* sx->outblock.conn_args = NULL; */
/* Reset the parameters of a TLS session. */
tls_out.sni = NULL;
#endif
tls_out.ocsp = OCSP_NOT_REQ;
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifndef DISABLE_TLS_RESUME
tls_out.resumption = 0;
#endif
+tls_out.ver = NULL;
/* Flip the legacy TLS-related variables over to the outbound set in case
they're used in the context of the transport. Don't bother resetting
if (sx->smtps)
{
set_errno_nohost(sx->addrlist, ERRNO_TLSFAILURE, US"TLS support not available",
- DEFER, FALSE);
+ DEFER, FALSE, &sx->delivery_start);
return ERROR;
}
#endif
if (sx->conn_args.host->dnssec == DS_YES)
{
+ int rc;
if( sx->dane_required
|| verify_check_given_host(CUSS &ob->hosts_try_dane, sx->conn_args.host) == OK
)
default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
string_sprintf("DANE error: tlsa lookup %s",
rc_to_string(rc)),
- rc, FALSE);
+ rc, FALSE, &sx->delivery_start);
# ifndef DISABLE_EVENT
(void) event_raise(sx->conn_args.tblock->event_action,
US"dane:fail", sx->dane_required
{
set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
string_sprintf("DANE error: %s lookup not DNSSEC", sx->conn_args.host->name),
- FAIL, FALSE);
+ FAIL, FALSE, &sx->delivery_start);
# ifndef DISABLE_EVENT
(void) event_raise(sx->conn_args.tblock->event_action,
US"dane:fail", US"dane-required");
sx->inblock.cctx = sx->outblock.cctx = &sx->cctx;
sx->avoid_option = sx->peer_offered = smtp_peer_options = 0;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
- if (verify_check_given_host(CUSS &ob->hosts_pipe_connect, sx->conn_args.host) == OK)
- {
- sx->early_pipe_ok = TRUE;
- if ( read_ehlo_cache_entry(sx)
- && sx->ehlo_resp.cleartext_features & OPTION_EARLY_PIPE)
+#ifndef DISABLE_PIPE_CONNECT
+ if ( verify_check_given_host(CUSS &ob->hosts_pipe_connect,
+ sx->conn_args.host) == OK)
+
+ /* We don't find out the local ip address until the connect, so if
+ the helo string might use it avoid doing early-pipelining. */
+
+ if ( !sx->helo_data
+ || !Ustrstr(sx->helo_data, "$sending_ip_address")
+ || Ustrstr(sx->helo_data, "def:sending_ip_address")
+ )
{
- DEBUG(D_transport) debug_printf("Using cached cleartext PIPE_CONNECT\n");
- sx->early_pipe_active = TRUE;
- sx->peer_offered = sx->ehlo_resp.cleartext_features;
+ sx->early_pipe_ok = TRUE;
+ if ( read_ehlo_cache_entry(sx)
+ && sx->ehlo_resp.cleartext_features & OPTION_EARLY_PIPE)
+ {
+ DEBUG(D_transport)
+ debug_printf("Using cached cleartext PIPE_CONNECT\n");
+ sx->early_pipe_active = TRUE;
+ sx->peer_offered = sx->ehlo_resp.cleartext_features;
+ }
}
- }
+ else DEBUG(D_transport)
+ debug_printf("helo needs $sending_ip_address\n");
+PIPE_CONNECT_RETRY:
if (sx->early_pipe_active)
sx->outblock.conn_args = &sx->conn_args;
else
set_errno_nohost(sx->addrlist,
errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
sx->verify ? US strerror(errno) : NULL,
- DEFER, FALSE);
+ DEFER, FALSE, &sx->delivery_start);
sx->send_quit = FALSE;
return DEFER;
}
if (!sx->smtps)
{
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (sx->early_pipe_active)
{
sx->pending_BANNER = TRUE; /* sync_responses() must eventually handle */
{
set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL,
string_sprintf("deferred by smtp:connect event expansion: %s", s),
- DEFER, FALSE);
+ DEFER, FALSE, &sx->delivery_start);
yield = DEFER;
goto SEND_QUIT;
}
{
message = string_sprintf("failed to expand helo_data: %s",
expand_string_message);
- set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
+ set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, &sx->delivery_start);
yield = DEFER;
goto SEND_QUIT;
}
if (sx->esmtp)
{
if (smtp_write_command(sx,
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
sx->early_pipe_active ? SCMD_BUFFER :
#endif
SCMD_FLUSH,
goto SEND_FAILED;
sx->esmtp_sent = TRUE;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (sx->early_pipe_active)
{
sx->pending_EHLO = TRUE;
DEBUG(D_transport)
debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (!sx->early_pipe_active)
#endif
if (!sx->esmtp)
if (sx->esmtp || sx->lmtp)
{
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (!sx->early_pipe_active)
#endif
{
sx->peer_offered = ehlo_response(sx->buffer,
OPTION_TLS /* others checked later */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
| (sx->early_pipe_ok
? OPTION_IGNQ
| OPTION_CHUNKING | OPTION_PRDR | OPTION_DSN | OPTION_PIPE | OPTION_SIZE
)
#endif
);
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (sx->early_pipe_ok)
{
sx->ehlo_resp.cleartext_features = sx->peer_offered;
}
sx->inblock.cctx = sx->outblock.cctx = &sx->cctx;
smtp_command = big_buffer;
+ sx->peer_offered = smtp_peer_options;
sx->helo_data = NULL; /* ensure we re-expand ob->helo_data */
/* For a continued connection with TLS being proxied for us, or a
&& cutthrough.is_tls)
)
{
- sx->peer_offered = smtp_peer_options;
sx->pipelining_used = pipelining_active = !!(smtp_peer_options & OPTION_PIPE);
HDEBUG(D_transport) debug_printf("continued connection, %s TLS\n",
continue_proxy_cipher ? "proxied" : "verify conn with");
if (smtp_write_command(sx, SCMD_FLUSH, "STARTTLS\r\n") < 0)
goto SEND_FAILED;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
/* If doing early-pipelining reap the banner and EHLO-response but leave
the response for the STARTTLS we just sent alone. */
{
HDEBUG(D_transport)
debug_printf("failed reaping pipelined cmd responses\n");
- goto RESPONSE_FAILED;
+ close(sx->cctx.sock);
+ sx->cctx.sock = -1;
+ sx->early_pipe_active = FALSE;
+ goto PIPE_CONNECT_RETRY;
}
#endif
addr->peercert = tls_out.peercert;
addr->peerdn = tls_out.peerdn;
addr->ocsp = tls_out.ocsp;
+ addr->tlsver = tls_out.ver;
}
}
}
{
uschar *message = string_sprintf("failed to expand helo_data: %s",
expand_string_message);
- set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
+ set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, &sx->delivery_start);
yield = DEFER;
goto SEND_QUIT;
}
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
/* For SMTPS there is no cleartext early-pipe; use the crypted permission bit.
We're unlikely to get the group sent and delivered before the server sends its
banner, but it's still worth sending as a group.
/* For SMTPS we need to wait for the initial OK response. */
if (sx->smtps)
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (sx->early_pipe_active)
{
sx->pending_BANNER = TRUE;
}
if (smtp_write_command(sx,
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
sx->early_pipe_active ? SCMD_BUFFER :
#endif
SCMD_FLUSH,
"%s %s\r\n", greeting_cmd, sx->helo_data) < 0)
goto SEND_FAILED;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (sx->early_pipe_active)
sx->pending_EHLO = TRUE;
else
{
if (sx->esmtp || sx->lmtp)
{
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (!sx->early_pipe_active)
#endif
{
sx->peer_offered = ehlo_response(sx->buffer,
0 /* no TLS */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
| (sx->lmtp && ob->lmtp_ignore_quota ? OPTION_IGNQ : 0)
| OPTION_DSN | OPTION_PIPE | OPTION_SIZE
| OPTION_CHUNKING | OPTION_PRDR | OPTION_UTF8
| (ob->size_addition >= 0 ? OPTION_SIZE : 0)
#endif
);
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (tls_out.active.sock >= 0)
sx->ehlo_resp.crypted_features = sx->peer_offered;
#endif
DEBUG(D_transport) debug_printf("%susing DSN\n",
sx->peer_offered & OPTION_DSN ? "" : "not ");
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if ( sx->early_pipe_ok
&& !sx->early_pipe_active
&& tls_out.active.sock >= 0
{
message = string_sprintf("failed to expand utf8_downconvert: %s",
expand_string_message);
- set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
+ set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, &sx->delivery_start);
yield = DEFER;
goto SEND_QUIT;
}
int code;
RESPONSE_FAILED:
+ if (errno == ECONNREFUSED) /* first-read error on a TFO conn */
+ {
+ /* There is a testing facility for simulating a connection timeout, as I
+ can't think of any other way of doing this. It converts a connection
+ refused into a timeout if the timeout is set to 999999. This is done for
+ a 3whs connection in ip_connect(), but a TFO connection does not error
+ there - instead it gets ECONNREFUSED on the first data read. Tracking
+ that a TFO really was done is too hard, or we would set a
+ sx->pending_conn_done bit and test that in smtp_reap_banner() and
+ smtp_reap_ehlo(). That would let us also add the conn-timeout to the
+ cmd-timeout. */
+
+ if (f.running_in_test_harness && ob->connect_timeout == 999999)
+ errno = ETIMEDOUT;
+ set_errno_nohost(sx->addrlist,
+ errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
+ sx->verify ? US strerror(errno) : NULL,
+ DEFER, FALSE, &sx->delivery_start);
+ sx->send_quit = FALSE;
+ return DEFER;
+ }
+
+ /* really an error on an SMTP read */
message = NULL;
sx->send_quit = check_response(sx->conn_args.host, &errno, sx->addrlist->more_errno,
sx->buffer, &code, &message, &pass_message);
#endif
? FAIL : DEFER,
pass_message,
- errno == ECONNREFUSED ? NULL : sx->conn_args.host
+ errno == ECONNREFUSED ? NULL : sx->conn_args.host,
#ifdef EXPERIMENTAL_DSN_INFO
- , sx->smtp_greeting, sx->helo_response
+ sx->smtp_greeting, sx->helo_response,
#endif
- );
+ &sx->delivery_start);
}
&& addrlist->prop.utf8_msg
&& !addrlist->prop.utf8_downcvt
)
- Ustrcpy(p, " SMTPUTF8"), p += 9;
+ Ustrcpy(p, US" SMTPUTF8"), p += 9;
#endif
/* check if all addresses have DSN-lasthop flag; do not send RET and ENVID if so */
if (sx->peer_offered & OPTION_DSN && !sx->dsn_all_lasthop)
{
if (dsn_ret == dsn_ret_hdrs)
- { Ustrcpy(p, " RET=HDRS"); p += 9; }
+ { Ustrcpy(p, US" RET=HDRS"); p += 9; }
else if (dsn_ret == dsn_ret_full)
- { Ustrcpy(p, " RET=FULL"); p += 9; }
+ { Ustrcpy(p, US" RET=FULL"); p += 9; }
if (dsn_envid)
{
otherwise no check - this feature is expected to be used with LMTP and other
cases where non-standard addresses (e.g. without domains) might be required. */
-if (smtp_mail_auth_str(p, sizeof(sx->buffer) - (p-sx->buffer), addrlist, sx->conn_args.ob))
- return ERROR;
-
-return OK;
+return smtp_mail_auth_str(sx, p, addrlist) ? ERROR : OK;
}
{
BOOL first = TRUE;
- Ustrcpy(p, " NOTIFY=");
+ Ustrcpy(p, US" NOTIFY=");
while (*p) p++;
for (int i = 0; i < nelem(rf_list); i++) if (addr->dsn_flags & rf_list[i])
{
smtp_write_mail_and_rcpt_cmds(smtp_context * sx, int * yield)
{
address_item * addr;
-int address_count;
+int address_count, pipe_limit;
int rc;
if (build_mailcmd_options(sx, sx->first_addr) != OK)
{
if (s = string_address_utf8_to_alabel(s, &errstr), errstr)
{
- set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE);
+ set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE, &sx->delivery_start);
*yield = ERROR;
return -4;
}
For verify we flush the pipeline after any (the only) rcpt address. */
-for (addr = sx->first_addr, address_count = 0;
+for (addr = sx->first_addr, address_count = 0, pipe_limit = 100;
addr && address_count < sx->max_rcpt;
addr = addr->next) if (addr->transport_return == PENDING_DEFER)
{
- int count;
+ int cmds_sent;
BOOL no_flush;
uschar * rcpt_addr;
? dsn_support_yes : dsn_support_no;
address_count++;
- no_flush = pipelining_active && !sx->verify
+ if (pipe_limit-- <= 0)
+ { no_flush = FALSE; pipe_limit = 100; }
+ else
+ no_flush = pipelining_active && !sx->verify
&& (!mua_wrapper || addr->next && address_count < sx->max_rcpt);
build_rcptcmd_options(sx, addr);
}
#endif
- count = smtp_write_command(sx, no_flush ? SCMD_BUFFER : SCMD_FLUSH,
+ cmds_sent = smtp_write_command(sx, no_flush ? SCMD_BUFFER : SCMD_FLUSH,
"RCPT TO:<%s>%s%s\r\n", rcpt_addr, sx->igquotstr, sx->buffer);
- if (count < 0) return -5;
- if (count > 0)
+ if (cmds_sent < 0) return -5;
+ if (cmds_sent > 0)
{
- switch(sync_responses(sx, count, 0))
+ switch(sync_responses(sx, cmds_sent, 0))
{
case 3: sx->ok = TRUE; /* 2xx & 5xx => OK & progress made */
case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */
if (!sx->lmtp) /* can't tell about progress yet */
sx->completed_addr = TRUE;
case 0: /* No 2xx or 5xx, but no probs */
+ /* If any RCPT got a 452 response then next_addr has been updated
+ for restarting with a new MAIL on the same connection. Send no more
+ RCPTs for this MAIL. */
+
+ if (sx->RCPT_452)
+ {
+ DEBUG(D_transport) debug_printf("seen 452 too-many-rcpts\n");
+ sx->RCPT_452 = FALSE;
+ /* sx->next_addr has been reset for fast_retry */
+ return 0;
+ }
break;
case -1: return -3; /* Timeout on RCPT */
case -2: return -2; /* non-MAIL read i/o error */
default: return -1; /* any MAIL error */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
case -4: return -1; /* non-2xx for pipelined banner or EHLO */
case -5: return -1; /* TLS first-read error */
#endif
}
- sx->pending_MAIL = FALSE; /* Dealt with MAIL */
}
} /* Loop for next address */
bufsiz size of buffer
pfd pipe filedescriptor array; [0] is comms to proxied process
timeout per-read timeout, seconds
+
+Does not return.
*/
void
int rc, i;
close(pfd[1]);
-if ((rc = fork()))
- {
- DEBUG(D_transport) debug_printf("proxy-proc final-pid %d\n", rc);
+if ((rc = exim_fork(US"tls-proxy")))
_exit(rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
- }
-if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
set_process_info("proxying TLS connection for continued transport");
FD_ZERO(&rfds);
FD_SET(tls_out.active.sock, &rfds);
}
done:
- if (f.running_in_test_harness) millisleep(100); /* let logging complete */
- exim_exit(0, US"TLS proxy");
+ testharness_pause_ms(100); /* let logging complete */
+ exim_exit(EXIT_SUCCESS);
}
#endif
/* If continue_hostname is not null, we get here only when continuing to
deliver down an existing channel. The channel was passed as the standard
-input. TLS is never active on a passed channel; the previous process always
-closes it down before passing the connection on.
+input. TLS is never active on a passed channel; the previous process either
+closes it down before passing the connection on, or inserts a TLS-proxy
+process and passes on a cleartext conection.
Otherwise, we have to make a connection to the remote host, and do the
initial protocol exchange.
and there was a problem setting it up; OR helo_data
or add_headers or authenticated_sender is specified
for this transport, and the string failed to expand
+
+ For all non-OK returns the first addr of the list carries the
+ time taken for the attempt.
*/
static int
int yield = OK;
int save_errno;
int rc;
-struct timeval start_delivery_time;
BOOL pass_message = FALSE;
uschar *message = NULL;
uschar new_message_id[MESSAGE_ID_LENGTH + 1];
+smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted, for the data buffers */
-smtp_context sx;
-
-gettimeofday(&start_delivery_time, NULL);
suppress_tls = suppress_tls; /* stop compiler warning when no TLS support */
*message_defer = FALSE;
-sx.addrlist = addrlist;
-sx.conn_args.host = host;
-sx.conn_args.host_af = host_af,
-sx.port = defport;
-sx.conn_args.interface = interface;
-sx.helo_data = NULL;
-sx.conn_args.tblock = tblock;
-sx.verify = FALSE;
-sx.sync_addr = sx.first_addr = addrlist;
+memset(sx, 0, sizeof(*sx));
+sx->addrlist = addrlist;
+sx->conn_args.host = host;
+sx->conn_args.host_af = host_af,
+sx->port = defport;
+sx->conn_args.interface = interface;
+sx->helo_data = NULL;
+sx->conn_args.tblock = tblock;
+/* sx->verify = FALSE; */
+gettimeofday(&sx->delivery_start, NULL);
+sx->sync_addr = sx->first_addr = addrlist;
/* Get the channel set up ready for a message (MAIL FROM being the next
SMTP command to send */
-if ((rc = smtp_setup_conn(&sx, suppress_tls)) != OK)
+if ((rc = smtp_setup_conn(sx, suppress_tls)) != OK)
+ {
+ timesince(&addrlist->delivery_time, &sx->delivery_start);
return rc;
+ }
/* If there is a filter command specified for this transport, we can now
-set it up. This cannot be done until the identify of the host is known. */
+set it up. This cannot be done until the identity of the host is known. */
if (tblock->filter_command)
{
string_sprintf("%.50s transport", tblock->name), NULL))
{
set_errno_nohost(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER,
- FALSE);
+ FALSE, &sx->delivery_start);
yield = ERROR;
goto SEND_QUIT;
}
if ( transport_filter_argv
&& *transport_filter_argv
&& **transport_filter_argv
- && sx.peer_offered & OPTION_CHUNKING
+ && sx->peer_offered & OPTION_CHUNKING
+#ifndef DISABLE_DKIM
+ /* When dkim signing, chunking is handled even with a transport-filter */
+ && !(ob->dkim.dkim_private_key && ob->dkim.dkim_domain && ob->dkim.dkim_selector)
+ && !ob->dkim.force_bodyhash
+#endif
)
{
- sx.peer_offered &= ~OPTION_CHUNKING;
+ sx->peer_offered &= ~OPTION_CHUNKING;
DEBUG(D_transport) debug_printf("CHUNKING not usable due to transport filter\n");
}
}
transaction to handle. */
SEND_MESSAGE:
-sx.from_addr = return_path;
-sx.sync_addr = sx.first_addr;
-sx.ok = FALSE;
-sx.send_rset = TRUE;
-sx.completed_addr = FALSE;
+sx->from_addr = return_path;
+sx->sync_addr = sx->first_addr;
+sx->ok = FALSE;
+sx->send_rset = TRUE;
+sx->completed_addr = FALSE;
/* If we are a continued-connection-after-verify the MAIL and RCPT
if (continue_hostname && continue_sequence == 1)
{
- sx.peer_offered = smtp_peer_options;
- sx.pending_MAIL = FALSE;
- sx.ok = TRUE;
- sx.next_addr = NULL;
+ /* sx->pending_MAIL = FALSE; */
+ sx->ok = TRUE;
+ /* sx->next_addr = NULL; */
for (address_item * addr = addrlist; addr; addr = addr->next)
addr->transport_return = PENDING_OK;
{
/* Initiate a message transfer. */
- switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield))
+ switch(smtp_write_mail_and_rcpt_cmds(sx, &yield))
{
case 0: break;
case -1: case -2: goto RESPONSE_FAILED;
address_item * a;
unsigned cnt;
- for (a = sx.first_addr, cnt = 0; a && cnt < sx.max_rcpt; a = a->next, cnt++)
+ for (a = sx->first_addr, cnt = 0; a && cnt < sx->max_rcpt; a = a->next, cnt++)
if (a->transport_return != PENDING_OK)
{
/*XXX could we find a better errno than 0 here? */
set_errno_nohost(addrlist, 0, a->message, FAIL,
- testflag(a, af_pass_message));
- sx.ok = FALSE;
+ testflag(a, af_pass_message), &sx->delivery_start);
+ sx->ok = FALSE;
break;
}
}
If using CHUNKING, do not send a BDAT until we know how big a chunk we want
to send is. */
-if ( !(sx.peer_offered & OPTION_CHUNKING)
- && (sx.ok || (pipelining_active && !mua_wrapper)))
+if ( !(sx->peer_offered & OPTION_CHUNKING)
+ && (sx->ok || (pipelining_active && !mua_wrapper)))
{
- int count = smtp_write_command(&sx, SCMD_FLUSH, "DATA\r\n");
+ int count = smtp_write_command(sx, SCMD_FLUSH, "DATA\r\n");
if (count < 0) goto SEND_FAILED;
- switch(sync_responses(&sx, count, sx.ok ? +1 : -1))
+ switch(sync_responses(sx, count, sx->ok ? +1 : -1))
{
- case 3: sx.ok = TRUE; /* 2xx & 5xx => OK & progress made */
- case 2: sx.completed_addr = TRUE; /* 5xx (only) => progress made */
+ case 3: sx->ok = TRUE; /* 2xx & 5xx => OK & progress made */
+ case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */
break;
- case 1: sx.ok = TRUE; /* 2xx (only) => OK, but if LMTP, */
- if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */
+ case 1: sx->ok = TRUE; /* 2xx (only) => OK, but if LMTP, */
+ if (!sx->lmtp) sx->completed_addr = TRUE; /* can't tell about progress yet */
case 0: break; /* No 2xx or 5xx, but no probs */
case -1: goto END_OFF; /* Timeout on RCPT */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
case -5: /* TLS first-read error */
case -4: HDEBUG(D_transport)
debug_printf("failed reaping pipelined cmd responses\n");
well as body. Set the appropriate timeout value to be used for each chunk.
(Haven't been able to make it work using select() for writing yet.) */
-if (!(sx.peer_offered & OPTION_CHUNKING) && !sx.ok)
+if ( !sx->ok
+ && (!(sx->peer_offered & OPTION_CHUNKING) || !pipelining_active))
{
/* Save the first address of the next batch. */
- sx.first_addr = sx.next_addr;
+ sx->first_addr = sx->next_addr;
- sx.ok = TRUE;
+ sx->ok = TRUE;
}
else
{
transport_ctx tctx = {
- .u = {.fd = sx.cctx.sock}, /*XXX will this need TLS info? */
+ .u = {.fd = sx->cctx.sock}, /*XXX will this need TLS info? */
.tblock = tblock,
.addr = addrlist,
.check_string = US".",
of responses. The callback needs a whole bunch of state so set up
a transport-context structure to be passed around. */
- if (sx.peer_offered & OPTION_CHUNKING)
+ if (sx->peer_offered & OPTION_CHUNKING)
{
tctx.check_string = tctx.escape_string = NULL;
tctx.options |= topt_use_bdat;
tctx.chunk_cb = smtp_chunk_cmd_callback;
- sx.pending_BDAT = FALSE;
- sx.good_RCPT = sx.ok;
- sx.cmd_count = 0;
- tctx.smtp_context = &sx;
+ sx->pending_BDAT = FALSE;
+ sx->good_RCPT = sx->ok;
+ sx->cmd_count = 0;
+ tctx.smtp_context = sx;
}
else
tctx.options |= topt_end_dot;
/* Save the first address of the next batch. */
- sx.first_addr = sx.next_addr;
+ sx->first_addr = sx->next_addr;
/* Responses from CHUNKING commands go in buffer. Otherwise,
there has not been a response. */
- sx.buffer[0] = 0;
+ sx->buffer[0] = 0;
sigalrm_seen = FALSE;
transport_write_timeout = ob->data_timeout;
smtp_command = US"sending data block"; /* For error messages */
DEBUG(D_transport|D_v)
- if (sx.peer_offered & OPTION_CHUNKING)
+ if (sx->peer_offered & OPTION_CHUNKING)
debug_printf(" will write message using CHUNKING\n");
else
debug_printf(" SMTP>> writing message and terminating \".\"\n");
transport_count = 0;
#ifndef DISABLE_DKIM
+ {
+# ifdef MEASURE_TIMING
+ struct timeval t0;
+ gettimeofday(&t0, NULL);
+# endif
dkim_exim_sign_init();
# ifdef EXPERIMENTAL_ARC
{
if (!f.expand_string_forcedfail)
{
message = US"failed to expand arc_sign";
- sx.ok = FALSE;
+ sx->ok = FALSE;
goto SEND_FAILED;
}
}
}
}
# endif
- sx.ok = dkim_transport_write_message(&tctx, &ob->dkim, CUSS &message);
+# ifdef MEASURE_TIMING
+ report_time_since(&t0, US"dkim_exim_sign_init (delta)");
+# endif
+ }
+ sx->ok = dkim_transport_write_message(&tctx, &ob->dkim, CUSS &message);
#else
- sx.ok = transport_write_message(&tctx, 0);
+ sx->ok = transport_write_message(&tctx, 0);
#endif
/* transport_write_message() uses write() because it is called from other
or the failure of a transport filter or the expansion of added headers.
Or, when CHUNKING, it can be a protocol-detected failure. */
- if (!sx.ok)
+ if (!sx->ok)
if (message) goto SEND_FAILED;
else goto RESPONSE_FAILED;
smtp_command = US"end of data";
- if (sx.peer_offered & OPTION_CHUNKING && sx.cmd_count > 1)
+ if (sx->peer_offered & OPTION_CHUNKING && sx->cmd_count > 1)
{
/* Reap any outstanding MAIL & RCPT commands, but not a DATA-go-ahead */
- switch(sync_responses(&sx, sx.cmd_count-1, 0))
+ switch(sync_responses(sx, sx->cmd_count-1, 0))
{
- case 3: sx.ok = TRUE; /* 2xx & 5xx => OK & progress made */
- case 2: sx.completed_addr = TRUE; /* 5xx (only) => progress made */
+ case 3: sx->ok = TRUE; /* 2xx & 5xx => OK & progress made */
+ case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */
break;
- case 1: sx.ok = TRUE; /* 2xx (only) => OK, but if LMTP, */
- if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */
+ case 1: sx->ok = TRUE; /* 2xx (only) => OK, but if LMTP, */
+ if (!sx->lmtp) sx->completed_addr = TRUE; /* can't tell about progress yet */
case 0: break; /* No 2xx or 5xx, but no probs */
case -1: goto END_OFF; /* Timeout on RCPT */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
case -5: /* TLS first-read error */
case -4: HDEBUG(D_transport)
debug_printf("failed reaping pipelined cmd responses\n");
individual responses, before going on with the overall response. If we don't
get the warning then deal with per non-PRDR. */
- if(sx.prdr_active)
+ if(sx->prdr_active)
{
- sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '3', ob->final_timeout);
- if (!sx.ok && errno == 0) switch(sx.buffer[0])
+ sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '3', ob->final_timeout);
+ if (!sx->ok && errno == 0) switch(sx->buffer[0])
{
- case '2': sx.prdr_active = FALSE;
- sx.ok = TRUE;
+ case '2': sx->prdr_active = FALSE;
+ sx->ok = TRUE;
break;
case '4': errno = ERRNO_DATA4XX;
addrlist->more_errno |=
- ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8;
+ ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
break;
}
}
/* For non-PRDR SMTP, we now read a single response that applies to the
whole message. If it is OK, then all the addresses have been delivered. */
- if (!sx.lmtp)
+ if (!sx->lmtp)
{
- sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2',
+ sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
ob->final_timeout);
- if (!sx.ok && errno == 0 && sx.buffer[0] == '4')
+ if (!sx->ok && errno == 0 && sx->buffer[0] == '4')
{
errno = ERRNO_DATA4XX;
- addrlist->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8;
+ addrlist->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
}
}
software before the spool gets updated. Also record the final SMTP
confirmation if needed (for SMTP only). */
- if (sx.ok)
+ if (sx->ok)
{
int flag = '=';
struct timeval delivery_time;
int len;
uschar * conf = NULL;
- timesince(&delivery_time, &start_delivery_time);
- sx.send_rset = FALSE;
+ timesince(&delivery_time, &sx->delivery_start);
+ sx->send_rset = FALSE;
pipelining_active = FALSE;
/* Set up confirmation if needed - applies only to SMTP */
#ifdef DISABLE_EVENT
LOGGING(smtp_confirmation) &&
#endif
- !sx.lmtp
+ !sx->lmtp
)
{
- const uschar *s = string_printing(sx.buffer);
+ const uschar *s = string_printing(sx->buffer);
/* deconst cast ok here as string_printing was checked to have alloc'n'copied */
- conf = (s == sx.buffer)? US string_copy(s) : US s;
+ conf = (s == sx->buffer)? US string_copy(s) : US s;
}
/* Process all transported addresses - for LMTP or PRDR, read a status for
each one. */
- for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next)
+ for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next)
{
if (addr->transport_return != PENDING_OK) continue;
it doesn't get tried again too soon. */
#ifndef DISABLE_PRDR
- if (sx.lmtp || sx.prdr_active)
+ if (sx->lmtp || sx->prdr_active)
#else
- if (sx.lmtp)
+ if (sx->lmtp)
#endif
{
- if (!smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2',
+ if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
ob->final_timeout))
{
- if (errno != 0 || sx.buffer[0] == 0) goto RESPONSE_FAILED;
+ if (errno != 0 || sx->buffer[0] == 0) goto RESPONSE_FAILED;
addr->message = string_sprintf(
#ifndef DISABLE_PRDR
- "%s error after %s: %s", sx.prdr_active ? "PRDR":"LMTP",
+ "%s error after %s: %s", sx->prdr_active ? "PRDR":"LMTP",
#else
"LMTP error after %s: %s",
#endif
- data_command, string_printing(sx.buffer));
+ data_command, string_printing(sx->buffer));
setflag(addr, af_pass_message); /* Allow message to go to user */
- if (sx.buffer[0] == '5')
+ if (sx->buffer[0] == '5')
addr->transport_return = FAIL;
else
{
errno = ERRNO_DATA4XX;
- addr->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8;
+ addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
addr->transport_return = DEFER;
#ifndef DISABLE_PRDR
- if (!sx.prdr_active)
+ if (!sx->prdr_active)
#endif
retry_add_item(addr, addr->address_retry_key, 0);
}
continue;
}
- sx.completed_addr = TRUE; /* NOW we can set this flag */
+ sx->completed_addr = TRUE; /* NOW we can set this flag */
if (LOGGING(smtp_confirmation))
{
- const uschar *s = string_printing(sx.buffer);
+ const uschar *s = string_printing(sx->buffer);
/* deconst cast ok here as string_printing was checked to have alloc'n'copied */
- conf = (s == sx.buffer) ? US string_copy(s) : US s;
+ conf = (s == sx->buffer) ? US string_copy(s) : US s;
}
}
actual host that was used. */
addr->transport_return = OK;
- addr->more_errno = delivery_time.tv_sec;
- addr->delivery_usec = delivery_time.tv_usec;
addr->host_used = host;
+ addr->delivery_time = delivery_time;
addr->special_action = flag;
addr->message = conf;
if (tcp_out_fastopen >= TFO_USED_NODATA) setflag(addr, af_tcp_fastopen);
if (tcp_out_fastopen >= TFO_USED_DATA) setflag(addr, af_tcp_fastopen_data);
}
- if (sx.pipelining_used) setflag(addr, af_pipelining);
-#ifdef EXPERIMENTAL_PIPE_CONNECT
- if (sx.early_pipe_active) setflag(addr, af_early_pipe);
+ if (sx->pipelining_used) setflag(addr, af_pipelining);
+#ifndef DISABLE_PIPE_CONNECT
+ if (sx->early_pipe_active) setflag(addr, af_early_pipe);
#endif
#ifndef DISABLE_PRDR
- if (sx.prdr_active) setflag(addr, af_prdr_used);
+ if (sx->prdr_active) setflag(addr, af_prdr_used);
#endif
- if (sx.peer_offered & OPTION_CHUNKING) setflag(addr, af_chunking_used);
+ if (sx->peer_offered & OPTION_CHUNKING) setflag(addr, af_chunking_used);
flag = '-';
#ifndef DISABLE_PRDR
- if (!sx.prdr_active)
+ if (!sx->prdr_active)
#endif
{
/* Update the journal. For homonymic addresses, use the base address plus
write error, as it may prove possible to update the spool file later. */
if (testflag(addr, af_homonym))
- sprintf(CS sx.buffer, "%.500s/%s\n", addr->unique + 3, tblock->name);
+ sprintf(CS sx->buffer, "%.500s/%s\n", addr->unique + 3, tblock->name);
else
- sprintf(CS sx.buffer, "%.500s\n", addr->unique);
+ sprintf(CS sx->buffer, "%.500s\n", addr->unique);
- DEBUG(D_deliver) debug_printf("S:journalling %s\n", sx.buffer);
- len = Ustrlen(CS sx.buffer);
- if (write(journal_fd, sx.buffer, len) != len)
+ DEBUG(D_deliver) debug_printf("S:journalling %s\n", sx->buffer);
+ len = Ustrlen(CS sx->buffer);
+ if (write(journal_fd, sx->buffer, len) != len)
log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
- "%s: %s", sx.buffer, strerror(errno));
+ "%s: %s", sx->buffer, strerror(errno));
}
}
#ifndef DISABLE_PRDR
- if (sx.prdr_active)
- {
- const uschar * overall_message;
+ if (sx->prdr_active)
+ {
+ const uschar * overall_message;
- /* PRDR - get the final, overall response. For any non-success
- upgrade all the address statuses. */
+ /* PRDR - get the final, overall response. For any non-success
+ upgrade all the address statuses. */
- sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2',
- ob->final_timeout);
- if (!sx.ok)
+ sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
+ ob->final_timeout);
+ if (!sx->ok)
+ {
+ if(errno == 0 && sx->buffer[0] == '4')
{
- if(errno == 0 && sx.buffer[0] == '4')
- {
- errno = ERRNO_DATA4XX;
- addrlist->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8;
- }
- for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next)
- if (sx.buffer[0] == '5' || addr->transport_return == OK)
- addr->transport_return = PENDING_OK; /* allow set_errno action */
- goto RESPONSE_FAILED;
+ errno = ERRNO_DATA4XX;
+ addrlist->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
}
+ for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next)
+ if (sx->buffer[0] == '5' || addr->transport_return == OK)
+ addr->transport_return = PENDING_OK; /* allow set_errno action */
+ goto RESPONSE_FAILED;
+ }
- /* Append the overall response to the individual PRDR response for logging
- and update the journal, or setup retry. */
+ /* Append the overall response to the individual PRDR response for logging
+ and update the journal, or setup retry. */
- overall_message = string_printing(sx.buffer);
- for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next)
- if (addr->transport_return == OK)
- addr->message = string_sprintf("%s\\n%s", addr->message, overall_message);
+ overall_message = string_printing(sx->buffer);
+ for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next)
+ if (addr->transport_return == OK)
+ addr->message = string_sprintf("%s\\n%s", addr->message, overall_message);
- for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next)
- if (addr->transport_return == OK)
- {
- if (testflag(addr, af_homonym))
- sprintf(CS sx.buffer, "%.500s/%s\n", addr->unique + 3, tblock->name);
- else
- sprintf(CS sx.buffer, "%.500s\n", addr->unique);
+ for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next)
+ if (addr->transport_return == OK)
+ {
+ if (testflag(addr, af_homonym))
+ sprintf(CS sx->buffer, "%.500s/%s\n", addr->unique + 3, tblock->name);
+ else
+ sprintf(CS sx->buffer, "%.500s\n", addr->unique);
- DEBUG(D_deliver) debug_printf("journalling(PRDR) %s\n", sx.buffer);
- len = Ustrlen(CS sx.buffer);
- if (write(journal_fd, sx.buffer, len) != len)
- log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
- "%s: %s", sx.buffer, strerror(errno));
- }
- else if (addr->transport_return == DEFER)
- retry_add_item(addr, addr->address_retry_key, -2);
- }
+ DEBUG(D_deliver) debug_printf("journalling(PRDR) %s\n", sx->buffer);
+ len = Ustrlen(CS sx->buffer);
+ if (write(journal_fd, sx->buffer, len) != len)
+ log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
+ "%s: %s", sx->buffer, strerror(errno));
+ }
+ else if (addr->transport_return == DEFER)
+ retry_add_item(addr, addr->address_retry_key, -2);
+ }
#endif
/* Ensure the journal file is pushed out to disk. */
here during the setting up phase (i.e. before MAIL FROM) then always defer, as
the problem is not related to this specific message. */
-if (!sx.ok)
+if (!sx->ok)
{
int code, set_rc;
uschar * set_message;
{
save_errno = errno;
message = NULL;
- sx.send_quit = check_response(host, &save_errno, addrlist->more_errno,
- sx.buffer, &code, &message, &pass_message);
+ sx->send_quit = check_response(host, &save_errno, addrlist->more_errno,
+ sx->buffer, &code, &message, &pass_message);
goto FAILED;
}
code = '4';
message = string_sprintf("send() to %s [%s] failed: %s",
host->name, host->address, message ? message : US strerror(save_errno));
- sx.send_quit = FALSE;
+ sx->send_quit = FALSE;
goto FAILED;
}
{
BOOL message_error;
- sx.ok = FALSE; /* For when reached by GOTO */
+ sx->ok = FALSE; /* For when reached by GOTO */
set_message = message;
/* We want to handle timeouts after MAIL or "." and loss of connection after
if (save_errno > 0)
message = US string_sprintf("%s: %s", message, strerror(save_errno));
- write_logs(host, message, sx.first_addr ? sx.first_addr->basic_errno : 0);
+ write_logs(host, message, sx->first_addr ? sx->first_addr->basic_errno : 0);
*message_defer = TRUE;
}
else
{
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
/* If we were early-pipelinng and the actual EHLO response did not match
the cached value we assumed, we could have detected it and passed a
custom errno through to here. It would be nice to RSET and retry right
}
}
- set_errno(addrlist, save_errno, set_message, set_rc, pass_message, host
+ set_errno(addrlist, save_errno, set_message, set_rc, pass_message, host,
#ifdef EXPERIMENTAL_DSN_INFO
- , sx.smtp_greeting, sx.helo_response
+ sx->smtp_greeting, sx->helo_response,
#endif
- );
+ &sx->delivery_start);
}
-
/* If all has gone well, send_quit will be set TRUE, implying we can end the
SMTP session tidily. However, if there were too many addresses to send in one
message (indicated by first_addr being non-NULL) we want to carry on with the
DEBUG(D_transport)
debug_printf("ok=%d send_quit=%d send_rset=%d continue_more=%d "
- "yield=%d first_address is %sNULL\n", sx.ok, sx.send_quit,
- sx.send_rset, f.continue_more, yield, sx.first_addr ? "not " : "");
+ "yield=%d first_address is %sNULL\n", sx->ok, sx->send_quit,
+ sx->send_rset, f.continue_more, yield, sx->first_addr ? "not " : "");
-if (sx.completed_addr && sx.ok && sx.send_quit)
+if (sx->completed_addr && sx->ok && sx->send_quit)
{
BOOL more;
smtp_compare_t t_compare;
t_compare.tblock = tblock;
t_compare.current_sender_address = sender_address;
- if ( sx.first_addr != NULL
+ if ( sx->first_addr != NULL
|| f.continue_more
|| (
#ifndef DISABLE_TLS
uschar *msg;
BOOL pass_message;
- if (sx.send_rset)
- if (! (sx.ok = smtp_write_command(&sx, SCMD_FLUSH, "RSET\r\n") >= 0))
+ if (sx->send_rset)
+ if (! (sx->ok = smtp_write_command(sx, SCMD_FLUSH, "RSET\r\n") >= 0))
{
msg = US string_sprintf("send() to %s [%s] failed: %s", host->name,
host->address, strerror(errno));
- sx.send_quit = FALSE;
+ sx->send_quit = FALSE;
}
- else if (! (sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer),
+ else if (! (sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
'2', ob->command_timeout)))
{
int code;
- sx.send_quit = check_response(host, &errno, 0, sx.buffer, &code, &msg,
+ sx->send_quit = check_response(host, &errno, 0, sx->buffer, &code, &msg,
&pass_message);
- if (!sx.send_quit)
+ if (!sx->send_quit)
{
DEBUG(D_transport) debug_printf("H=%s [%s] %s\n",
host->name, host->address, msg);
/* Either RSET was not needed, or it succeeded */
- if (sx.ok)
+ if (sx->ok)
{
#ifndef DISABLE_TLS
int pfd[2];
#endif
- int socket_fd = sx.cctx.sock;
+ int socket_fd = sx->cctx.sock;
- if (sx.first_addr != NULL) /* More addresses still to be sent */
- { /* in this run of the transport */
- continue_sequence++; /* Causes * in logging */
+ if (sx->first_addr != NULL) /* More addresses still to be sent */
+ { /* in this run of the transport */
+ continue_sequence++; /* Causes * in logging */
+ pipelining_active = sx->pipelining_used; /* was cleared at DATA */
goto SEND_MESSAGE;
}
a new EHLO. If we don't get a good response, we don't attempt to pass
the socket on. */
- tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_WAIT);
- sx.cctx.tls_ctx = NULL;
+ tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_WAIT);
+ sx->cctx.tls_ctx = NULL;
smtp_peer_options = smtp_peer_options_wrap;
- sx.ok = !sx.smtps
- && smtp_write_command(&sx, SCMD_FLUSH, "EHLO %s\r\n", sx.helo_data)
+ sx->ok = !sx->smtps
+ && smtp_write_command(sx, SCMD_FLUSH, "EHLO %s\r\n", sx->helo_data)
>= 0
- && smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer),
+ && smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
'2', ob->command_timeout);
- if (sx.ok && f.continue_more)
+ if (sx->ok && f.continue_more)
return yield; /* More addresses for another run */
}
else
/* Set up a pipe for proxying TLS for the new transport process */
smtp_peer_options |= OPTION_TLS;
- if (sx.ok = (socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0))
+ if ((sx->ok = socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0))
socket_fd = pfd[1];
else
- set_errno(sx.first_addr, errno, US"internal allocation problem",
- DEFER, FALSE, host
+ set_errno(sx->first_addr, errno, US"internal allocation problem",
+ DEFER, FALSE, host,
# ifdef EXPERIMENTAL_DSN_INFO
- , sx.smtp_greeting, sx.helo_response
+ sx->smtp_greeting, sx->helo_response,
# endif
- );
+ &sx->delivery_start);
}
else
#endif
/*XXX DSN_INFO: assume likely to do new HELO; but for greet we'll want to
propagate it from the initial
*/
- if (sx.ok && transport_pass_socket(tblock->name, host->name,
+ if (sx->ok && transport_pass_socket(tblock->name, host->name,
host->address, new_message_id, socket_fd))
{
- sx.send_quit = FALSE;
+ sx->send_quit = FALSE;
/* We have passed the client socket to a fresh transport process.
If TLS is still active, we need to proxy it for the transport we
#ifndef DISABLE_TLS
if (tls_out.active.sock >= 0)
{
- int pid = fork();
+ int pid = exim_fork(US"tls-proxy-interproc");
if (pid == 0) /* child; fork again to disconnect totally */
{
- if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
/* does not return */
- smtp_proxy_tls(sx.cctx.tls_ctx, sx.buffer, sizeof(sx.buffer), pfd,
+ smtp_proxy_tls(sx->cctx.tls_ctx, sx->buffer, sizeof(sx->buffer), pfd,
ob->command_timeout);
}
if (pid > 0) /* parent */
{
- DEBUG(D_transport) debug_printf("proxy-proc inter-pid %d\n", pid);
close(pfd[0]);
/* tidy the inter-proc to disconn the proxy proc */
waitpid(pid, NULL, 0);
- tls_close(sx.cctx.tls_ctx, TLS_NO_SHUTDOWN);
- sx.cctx.tls_ctx = NULL;
- (void)close(sx.cctx.sock);
- sx.cctx.sock = -1;
+ tls_close(sx->cctx.tls_ctx, TLS_NO_SHUTDOWN);
+ sx->cctx.tls_ctx = NULL;
+ (void)close(sx->cctx.sock);
+ sx->cctx.sock = -1;
continue_transport = NULL;
continue_hostname = NULL;
return yield;
/* If RSET failed and there are addresses left, they get deferred. */
else
- set_errno(sx.first_addr, errno, msg, DEFER, FALSE, host
+ set_errno(sx->first_addr, errno, msg, DEFER, FALSE, host,
#ifdef EXPERIMENTAL_DSN_INFO
- , sx.smtp_greeting, sx.helo_response
+ sx->smtp_greeting, sx->helo_response,
#endif
- );
+ &sx->delivery_start);
}
}
SEND_QUIT:
#ifdef TCP_CORK
-(void) setsockopt(sx.cctx.sock, IPPROTO_TCP, TCP_CORK, US &on, sizeof(on));
+(void) setsockopt(sx->cctx.sock, IPPROTO_TCP, TCP_CORK, US &on, sizeof(on));
#endif
-if (sx.send_quit) (void)smtp_write_command(&sx, SCMD_FLUSH, "QUIT\r\n");
+if (sx->send_quit) (void)smtp_write_command(sx, SCMD_FLUSH, "QUIT\r\n");
END_OFF:
#ifndef DISABLE_TLS
-tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
-sx.cctx.tls_ctx = NULL;
+tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
+sx->cctx.tls_ctx = NULL;
#endif
/* Close the socket, and return the appropriate value, first setting
case continue_more won't get set. */
HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n");
-if (sx.send_quit)
+if (sx->send_quit)
{
- shutdown(sx.cctx.sock, SHUT_WR);
- millisleep(f.running_in_test_harness ? 200 : 20);
- if (fcntl(sx.cctx.sock, F_SETFL, O_NONBLOCK) == 0)
- for (int i = 16; read(sx.cctx.sock, sx.inbuffer, sizeof(sx.inbuffer)) > 0 && i > 0;)
+ shutdown(sx->cctx.sock, SHUT_WR);
+ millisleep(20);
+ testharness_pause_ms(200);
+ if (fcntl(sx->cctx.sock, F_SETFL, O_NONBLOCK) == 0)
+ for (int i = 16; read(sx->cctx.sock, sx->inbuffer, sizeof(sx->inbuffer)) > 0 && i > 0;)
i--; /* drain socket */
}
-(void)close(sx.cctx.sock);
+(void)close(sx->cctx.sock);
#ifndef DISABLE_EVENT
(void) event_raise(tblock->event_action, US"tcp:close", NULL);
addr->peercert = NULL;
addr->peerdn = NULL;
addr->ocsp = OCSP_NOT_REQ;
+ addr->tlsver = NULL;
#endif
#ifdef EXPERIMENTAL_DSN_INFO
addr->smtp_greeting = NULL;
cutthrough.cctx.sock >= 0 ? cutthrough.cctx.sock : 0);
}
+/* Check the restrictions on line length */
+
+if (max_received_linelength > ob->message_linelength_limit)
+ {
+ struct timeval now;
+ gettimeofday(&now, NULL);
+
+ for (address_item * addr = addrlist; addr; addr = addr->next)
+ if (addr->transport_return == DEFER)
+ addr->transport_return = PENDING_DEFER;
+
+ set_errno_nohost(addrlist, ERRNO_SMTPFORMAT,
+ US"message has lines too long for transport", FAIL, TRUE, &now);
+ goto END_TRANSPORT;
+ }
+
/* Set the flag requesting that these hosts be added to the waiting
database if the delivery fails temporarily or if we are running with
queue_smtp or a 2-stage queue run. This gets unset for certain
else
if (ob->hosts_randomize) s = expanded_hosts = string_copy(s);
+ if (is_tainted(s))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "attempt to use tainted host list '%s' from '%s' in transport %s",
+ s, ob->hosts, tblock->name);
+ /* Avoid leaking info to an attacker */
+ addrlist->message = US"internal configuration error";
+ addrlist->transport_return = PANIC;
+ return FALSE;
+ }
+
host_build_hostlist(&hostlist, s, ob->hosts_randomize);
/* Check that the expansion yielded something useful. */
{
for (address_item * addr = addrlist; addr; addr = addr->next)
{
- addr->basic_errno = 0;
+ addr->basic_errno = ERRNO_HOST_IS_LOCAL;
addr->message = string_sprintf("%s transport found host %s to be "
"local", tblock->name, host->name);
}
were not in it. We don't want to hold up all SMTP deliveries! Except when
doing a two-stage queue run, don't do this if forcing. */
- if ((!f.deliver_force || f.queue_2stage) && (f.queue_smtp ||
- match_isinlist(addrlist->domain,
- (const uschar **)&queue_smtp_domains, 0,
- &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK))
+ if ( (!f.deliver_force || f.queue_2stage)
+ && ( f.queue_smtp
+ || match_isinlist(addrlist->domain,
+ CUSS &queue_smtp_domains, 0,
+ &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK)
+ )
{
+ DEBUG(D_transport) debug_printf("first-pass routing only\n");
expired = FALSE;
for (address_item * addr = addrlist; addr; addr = addr->next)
if (addr->transport_return == DEFER)
- addr->message = US"domain matches queue_smtp_domains, or -odqs set";
+ addr->message = US"first-pass only routing due to -odqs, "
+ "queue_smtp_domains or control=queue";
continue; /* With next host */
}
if (f.dont_deliver)
{
- set_errno_nohost(addrlist, 0, NULL, OK, FALSE);
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ set_errno_nohost(addrlist, 0, NULL, OK, FALSE, &now);
for (address_item * addr = addrlist; addr; addr = addr->next)
{
addr->host_used = host;
if (expanded_hosts)
{
- thost = store_get(sizeof(host_item));
+ thost = store_get(sizeof(host_item), FALSE);
*thost = *host;
thost->name = string_copy(host->name);
thost->address = string_copy(host->address);
#ifndef DISABLE_EVENT
if (rc == DEFER)
- deferred_event_raise(first_addr, host);
+ deferred_event_raise(first_addr, host, US"msg:host:defer");
#endif
/* If STARTTLS was accepted, but there was a failure in setting up the
if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL)
write_logs(host, first_addr->message, first_addr->basic_errno);
# ifndef DISABLE_EVENT
- if (rc == DEFER)
- deferred_event_raise(first_addr, host);
+ if (rc == DEFER)
+ deferred_event_raise(first_addr, host, US"msg:host:defer");
# endif
}
#endif /*DISABLE_TLS*/
+
+#ifndef DISABLE_EVENT
+ /* If the last host gave a defer raise a per-message event */
+
+ if ( !( nexthost
+ && unexpired_hosts_tried < ob->hosts_max_try
+ && total_hosts_tried < ob->hosts_max_try_hardlimit
+ )
+ && (message_defer || rc == DEFER)
+ )
+ deferred_event_raise(first_addr, host, US"msg:defer");
+#endif
}
/* Delivery attempt finished */
ob->hosts_max_try_hardlimit);
}
- if (f.running_in_test_harness) millisleep(500); /* let server debug out */
+ testharness_pause_ms(500); /* let server debug out */
} /* End of loop for trying multiple hosts. */
/* If we failed to find a matching host in the list, for an already-open