summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2016-12-24 21:04:20 +0000
committerJeremy Harris <jgh146exb@wizmail.org>2017-01-21 21:49:50 +0000
commit02b41d7106c67bbe862d6a44373034684a03c00e (patch)
tree956b81751853900a447f140ddb43fdaa5cdb64bb /src
parent9be4572b6242b4d297f8b030bdd54bd345a72c06 (diff)
Use smtp_setup_conn() for verify callout
Diffstat (limited to 'src')
-rw-r--r--src/src/smtp_in.c4
-rw-r--r--src/src/transports/smtp.c142
-rw-r--r--src/src/transports/smtp.h59
-rw-r--r--src/src/verify.c587
4 files changed, 250 insertions, 542 deletions
diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
index 04cbbe465..79a1ee02b 100644
--- a/src/src/smtp_in.c
+++ b/src/src/smtp_in.c
@@ -2917,7 +2917,7 @@ we have not sent a response about it yet, do so now, as a preliminary line for
failures, but not defers. However, always log it for defer, and log it for fail
unless the sender_verify_fail log selector has been turned off. */
-if (sender_verified_failed != NULL &&
+if (sender_verified_failed &&
!testflag(sender_verified_failed, af_sverify_told))
{
BOOL save_rcpt_in_progress = rcpt_in_progress;
@@ -2933,7 +2933,7 @@ if (sender_verified_failed != NULL &&
(sender_verified_failed->message == NULL)? US"" :
string_sprintf(": %s", sender_verified_failed->message));
- if (rc == FAIL && sender_verified_failed->user_message != NULL)
+ if (rc == FAIL && sender_verified_failed->user_message)
smtp_respond(smtp_code, codelen, FALSE, string_sprintf(
testflag(sender_verified_failed, af_verify_pmfail)?
"Postmaster verification failed while checking <%s>\n%s\n"
diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
index f3247245d..1ce0cbe11 100644
--- a/src/src/transports/smtp.c
+++ b/src/src/transports/smtp.c
@@ -8,12 +8,6 @@
#include "../exim.h"
#include "smtp.h"
-#define PENDING 256
-#define PENDING_DEFER (PENDING + DEFER)
-#define PENDING_OK (PENDING + OK)
-
-#define DELIVER_BUFFER_SIZE 4096
-
/* Options specific to the smtp transport. This transport also supports LMTP
over TCP/IP. The options must be in alphabetic order (note that "_" comes
@@ -327,7 +321,7 @@ gid = gid;
/* Pass back options if required. This interface is getting very messy. */
-if (tf != NULL)
+if (tf)
{
tf->interface = ob->interface;
tf->port = ob->port;
@@ -346,11 +340,8 @@ host lists, provided that the local host wasn't present in the original host
list. */
if (!testflag(addrlist, af_local_host_removed))
- {
- for (; addrlist != NULL; addrlist = addrlist->next)
- if (addrlist->fallback_hosts == NULL)
- addrlist->fallback_hosts = ob->fallback_hostlist;
- }
+ for (; addrlist; addrlist = addrlist->next)
+ if (!addrlist->fallback_hosts) addrlist->fallback_hosts = ob->fallback_hostlist;
return OK;
}
@@ -458,7 +449,7 @@ for (addr = addrlist; addr; addr = addr->next)
{
addr->basic_errno = errno_value;
addr->more_errno |= orvalue;
- if (msg != NULL)
+ if (msg)
{
addr->message = msg;
if (pass_message) setflag(addr, af_pass_message);
@@ -610,7 +601,7 @@ if (*errno_value == 0 || *errno_value == ECONNRESET)
{
*errno_value = ERRNO_SMTPCLOSED;
*message = US string_sprintf("Remote host closed connection "
- "in response to %s%s", pl, smtp_command);
+ "in response to %s%s", pl, smtp_command);
}
else *message = US string_sprintf("%s [%s]", host->name, host->address);
@@ -1471,61 +1462,9 @@ return OK;
* Make connection for given message *
*************************************************/
-typedef struct {
- address_item * addrlist;
- host_item * host;
- int host_af;
- int port;
- uschar * interface;
-
- BOOL lmtp:1;
- BOOL smtps:1;
- BOOL ok:1;
- BOOL send_rset:1;
- BOOL send_quit:1;
- BOOL setting_up:1;
- BOOL esmtp:1;
- BOOL esmtp_sent:1;
- BOOL pending_MAIL:1;
-#ifndef DISABLE_PRDR
- BOOL prdr_active:1;
-#endif
-#ifdef SUPPORT_I18N
- BOOL utf8_needed:1;
-#endif
- BOOL dsn_all_lasthop:1;
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
- BOOL dane:1;
- BOOL dane_required:1;
-#endif
-
- int max_rcpt;
-
- uschar peer_offered;
- uschar * igquotstr;
- uschar * helo_data;
-#ifdef EXPERIMENTAL_DSN_INFO
- uschar * smtp_greeting;
- uschar * helo_response;
-#endif
-
- smtp_inblock inblock;
- smtp_outblock outblock;
- uschar buffer[DELIVER_BUFFER_SIZE];
- uschar inbuffer[4096];
- uschar outbuffer[4096];
-
- transport_instance * tblock;
- smtp_transport_options_block * ob;
-} smtp_context;
-
/*
Arguments:
ctx connection context
- message_defer return set TRUE if yield is OK, but all addresses were deferred
- because of a non-recipient, non-host failure, that is, a
- 4xx response to MAIL FROM, DATA, or ".". This is a defer
- that is specific to the message.
suppress_tls if TRUE, don't attempt a TLS connection - this is set for
a second attempt after TLS initialization fails
verify TRUE if connection is for a verify callout, FALSE for
@@ -1541,8 +1480,7 @@ Returns: OK - the connection was made and the delivery attempted;
to expand
*/
int
-smtp_setup_conn(smtp_context * sx, BOOL * message_defer, BOOL suppress_tls,
- BOOL verify)
+smtp_setup_conn(smtp_context * sx, BOOL suppress_tls, BOOL verify)
{
#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
dns_answer tlsa_dnsa;
@@ -1554,7 +1492,7 @@ int save_errno;
int yield = OK;
int rc;
-sx->ob = (smtp_transport_options_block *)(sx->tblock->options_block);
+sx->ob = (smtp_transport_options_block *) sx->tblock->options_block;
sx->lmtp = strcmpic(sx->ob->protocol, US"lmtp") == 0;
sx->smtps = strcmpic(sx->ob->protocol, US"smtps") == 0;
@@ -1574,15 +1512,14 @@ sx->dane_required = verify_check_given_host(&sx->ob->hosts_require_dane, sx->hos
#endif
if ((sx->max_rcpt = sx->tblock->max_addresses) == 0) sx->max_rcpt = 999999;
-sx->helo_data = NULL;
sx->peer_offered = 0;
sx->igquotstr = US"";
+if (!sx->helo_data) sx->helo_data = sx->ob->helo_data;
#ifdef EXPERIMENTAL_DSN_INFO
sx->smtp_greeting = NULL;
sx->helo_response = NULL;
#endif
-*message_defer = FALSE;
smtp_command = US"initial connection";
sx->buffer[0] = '\0';
@@ -1615,7 +1552,8 @@ tls_out.ocsp = OCSP_NOT_REQ;
/* 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
-afterward as we're in a subprocess. */
+afterward (when being used by a transport) as we're in a subprocess.
+For verify, unflipped once the callout is dealt with */
tls_modify_variables(&tls_out);
@@ -1634,6 +1572,9 @@ specially so they can be identified for retries. */
if (continue_hostname == NULL)
{
+ if (verify)
+ HDEBUG(D_verify) debug_printf("interface=%s port=%d\n", sx->interface, sx->port);
+
/* This puts port into host->port */
sx->inblock.sock = sx->outblock.sock =
smtp_connect(sx->host, sx->host_af, sx->port, sx->interface,
@@ -1641,8 +1582,19 @@ if (continue_hostname == NULL)
if (sx->inblock.sock < 0)
{
- set_errno_nohost(sx->addrlist, errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
- NULL, DEFER, FALSE);
+ uschar * msg = NULL;
+ int save_errno = errno;
+ if (verify)
+ {
+ msg = strerror(errno);
+ HDEBUG(D_verify) debug_printf("connect: %s\n", msg);
+ }
+ set_errno_nohost(sx->addrlist,
+ save_errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : save_errno,
+ verify ? string_sprintf("could not connect: %s", msg)
+ : NULL,
+ DEFER, FALSE);
+ sx->send_quit = FALSE;
return DEFER;
}
@@ -1684,18 +1636,26 @@ if (continue_hostname == NULL)
sense if helo_data contains ${lookup dnsdb ...} stuff). The expansion is
delayed till here so that $sending_interface and $sending_port are set. */
- sx->helo_data = expand_string(sx->ob->helo_data);
+ if (sx->helo_data)
+ if (!(sx->helo_data = expand_string(sx->helo_data)))
+ if (verify)
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "<%s>: failed to expand transport's helo_data value for callout: %s",
+ sx->addrlist->address, expand_string_message);
+
#ifdef SUPPORT_I18N
if (sx->helo_data)
{
- uschar * errstr = NULL;
- if ((sx->helo_data = string_domain_utf8_to_alabel(sx->helo_data, &errstr)), errstr)
- {
- errstr = string_sprintf("failed to expand helo_data: %s", errstr);
- set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE);
- yield = DEFER;
- goto SEND_QUIT;
- }
+ expand_string_message = NULL;
+ if ((sx->helo_data = string_domain_utf8_to_alabel(sx->helo_data,
+ &expand_string_message)),
+ expand_string_message)
+ if (verify)
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "<%s>: failed to expand transport's helo_data value for callout: %s",
+ sx->addrlist->address, expand_string_message);
+ else
+ sx->helo_data = NULL;
}
#endif
@@ -1892,6 +1852,7 @@ else
sx->inblock.sock = sx->outblock.sock = fileno(stdin);
smtp_command = big_buffer;
sx->host->port = sx->port; /* Record the port that was used */
+ sx->helo_data = NULL; /* ensure we re-expand ob->helo_data */
}
/* If TLS is available on this connection, whether continued or not, attempt to
@@ -1905,7 +1866,10 @@ for error analysis. */
#ifdef SUPPORT_TLS
if ( smtp_peer_options & PEER_OFFERED_TLS
&& !suppress_tls
- && verify_check_given_host(&sx->ob->hosts_avoid_tls, sx->host) != OK)
+ && verify_check_given_host(&sx->ob->hosts_avoid_tls, sx->host) != OK
+ && ( !verify
+ || verify_check_given_host(&sx->ob->hosts_verify_avoid_tls, sx->host) != OK
+ ) )
{
uschar buffer2[4096];
if (smtp_write_command(&sx->outblock, FALSE, "STARTTLS\r\n") < 0)
@@ -2226,8 +2190,6 @@ SEND_QUIT:
if (sx->send_quit)
(void)smtp_write_command(&sx->outblock, FALSE, "QUIT\r\n");
-/*END_OFF:*/
-
#ifdef SUPPORT_TLS
tls_close(FALSE, TRUE);
#endif
@@ -2249,8 +2211,10 @@ if (sx->send_quit)
if (fcntl(sx->inblock.sock, F_SETFL, O_NONBLOCK) == 0)
for (rc = 16; read(sx->inblock.sock, sx->inbuffer, sizeof(sx->inbuffer)) > 0 && rc > 0;)
rc--; /* drain socket */
+ sx->send_quit = FALSE;
}
(void)close(sx->inblock.sock);
+sx->inblock.sock = sx->outblock.sock = -1;
#ifndef DISABLE_EVENT
(void) event_raise(sx->tblock->event_action, US"tcp:close", NULL);
@@ -2468,18 +2432,20 @@ uschar *p;
smtp_context sx;
suppress_tls = suppress_tls; /* stop compiler warning when no TLS support */
+*message_defer = FALSE;
sx.addrlist = addrlist;
sx.host = host;
sx.host_af = host_af,
sx.port = port;
sx.interface = interface;
+sx.helo_data = NULL;
sx.tblock = tblock;
/* Get the channel set up ready for a message (MAIL FROM being the next
SMTP command to send */
-if ((rc = smtp_setup_conn(&sx, message_defer, suppress_tls, FALSE)) != OK)
+if ((rc = smtp_setup_conn(&sx, suppress_tls, FALSE)) != OK)
return rc;
/* If there is a filter command specified for this transport, we can now
@@ -3386,7 +3352,7 @@ void
smtp_transport_closedown(transport_instance *tblock)
{
smtp_transport_options_block *ob =
- (smtp_transport_options_block *)(tblock->options_block);
+ (smtp_transport_options_block *)tblock->options_block;
smtp_inblock inblock;
smtp_outblock outblock;
uschar buffer[256];
diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h
index c8df38ab4..e12c0ab94 100644
--- a/src/src/transports/smtp.h
+++ b/src/src/transports/smtp.h
@@ -5,6 +5,13 @@
/* Copyright (c) University of Cambridge 1995 - 2015 */
/* See the file NOTICE for conditions of use and distribution. */
+#define DELIVER_BUFFER_SIZE 4096
+
+#define PENDING 256
+#define PENDING_DEFER (PENDING + DEFER)
+#define PENDING_OK (PENDING + OK)
+
+
/* Private structure for the private options and other private data. */
typedef struct {
@@ -82,6 +89,58 @@ typedef struct {
#endif
} smtp_transport_options_block;
+/* smtp connect context */
+typedef struct {
+ address_item * addrlist;
+ host_item * host;
+ int host_af;
+ int port;
+ uschar * interface;
+
+ BOOL lmtp:1;
+ BOOL smtps:1;
+ BOOL ok:1;
+ BOOL send_rset:1;
+ BOOL send_quit:1;
+ BOOL setting_up:1;
+ BOOL esmtp:1;
+ BOOL esmtp_sent:1;
+ BOOL pending_MAIL:1;
+#ifndef DISABLE_PRDR
+ BOOL prdr_active:1;
+#endif
+#ifdef SUPPORT_I18N
+ BOOL utf8_needed:1;
+#endif
+ BOOL dsn_all_lasthop:1;
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+ BOOL dane:1;
+ BOOL dane_required:1;
+#endif
+
+ int max_rcpt;
+
+ uschar peer_offered;
+ uschar * igquotstr;
+ uschar * helo_data;
+#ifdef EXPERIMENTAL_DSN_INFO
+ uschar * smtp_greeting;
+ uschar * helo_response;
+#endif
+
+ smtp_inblock inblock;
+ smtp_outblock outblock;
+ uschar buffer[DELIVER_BUFFER_SIZE];
+ uschar inbuffer[4096];
+ uschar outbuffer[4096];
+
+ transport_instance * tblock;
+ smtp_transport_options_block * ob;
+} smtp_context;
+
+extern int smtp_setup_conn(smtp_context *, BOOL, BOOL);
+
+
/* Data for reading the private options. */
extern optionlist smtp_transport_options[];
diff --git a/src/src/verify.c b/src/src/verify.c
index 0e31ee8b5..4af863c0b 100644
--- a/src/src/verify.c
+++ b/src/src/verify.c
@@ -171,7 +171,6 @@ dbdata_callout_cache new_domain_record;
dbdata_callout_cache_address new_address_record;
host_item *host;
time_t callout_start_time;
-uschar peer_offered = 0;
new_domain_record.result = ccache_unknown;
new_domain_record.postmaster_result = ccache_unknown;
@@ -508,7 +507,7 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
}
}
}
- break;
+ break; /* host_list */
}
if (!done)
cancel_cutthrough_connection("incompatible connection");
@@ -519,26 +518,11 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
for (host = host_list; host && !done; host = host->next)
{
- smtp_inblock inblock;
- smtp_outblock outblock;
int host_af;
int port = 25;
- BOOL send_quit = TRUE;
- uschar *active_hostname = smtp_active_hostname;
- BOOL lmtp;
- BOOL smtps;
- BOOL esmtp;
- BOOL suppress_tls = FALSE;
uschar *interface = NULL; /* Outgoing interface to use; NULL => any */
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
- BOOL dane = FALSE;
- BOOL dane_required;
- dns_answer tlsa_dnsa;
-#endif
- uschar inbuffer[4096];
- uschar outbuffer[1024];
+ smtp_context sx;
uschar responsebuffer[4096];
- uschar * size_str;
clearflag(addr, af_verify_pmfail); /* postmaster callout flag */
clearflag(addr, af_verify_nsfail); /* null sender callout flag */
@@ -583,403 +567,92 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
addr->message);
- /* Set HELO string according to the protocol */
- lmtp= Ustrcmp(tf->protocol, "lmtp") == 0;
- smtps= Ustrcmp(tf->protocol, "smtps") == 0;
-
-
- HDEBUG(D_verify) debug_printf("interface=%s port=%d\n", interface, port);
-
- /* Set up the buffer for reading SMTP response packets. */
-
- inblock.buffer = inbuffer;
- inblock.buffersize = sizeof(inbuffer);
- inblock.ptr = inbuffer;
- inblock.ptrend = inbuffer;
-
- /* Set up the buffer for holding SMTP commands while pipelining */
-
- outblock.buffer = outbuffer;
- outblock.buffersize = sizeof(outbuffer);
- outblock.ptr = outbuffer;
- outblock.cmd_count = 0;
- outblock.authenticating = FALSE;
-
- /* Connect to the host; on failure, just loop for the next one, but we
- set the error for the last one. Use the callout_connect timeout. */
-
- tls_retry_connection:
-
- /* Reset the parameters of a TLS session */
- tls_out.cipher = tls_out.peerdn = tls_out.peercert = NULL;
-
- inblock.sock = outblock.sock =
- smtp_connect(host, host_af, port, interface, callout_connect,
- addr->transport);
- if (inblock.sock < 0)
+ sx.addrlist = addr;
+ sx.host = host;
+ sx.host_af = host_af,
+ sx.port = port;
+ sx.interface = interface;
+ sx.helo_data = tf->helo_data;
+ sx.tblock = addr->transport;
+
+tls_retry_connection:
+ /* Set the address state so that errors are recorded in it */
+
+ addr->transport_return = PENDING_DEFER;
+ ob->connect_timeout = callout_connect;
+ ob->command_timeout = callout;
+
+ /* Get the channel set up ready for a message (MAIL FROM being the next
+ SMTP command to send. If we tried TLS but it failed, try again without
+ if permitted */
+
+ if ( (yield = smtp_setup_conn(&sx, FALSE, TRUE)) == DEFER
+ && addr->basic_errno == ERRNO_TLSFAILURE
+ && ob->tls_tempfail_tryclear
+ && verify_check_given_host(&ob->hosts_require_tls, host) != OK
+ )
{
- HDEBUG(D_verify) debug_printf("connect: %s\n", strerror(errno));
- addr->message = string_sprintf("could not connect to %s [%s]: %s",
- host->name, host->address, strerror(errno));
+ log_write(0, LOG_MAIN, "TLS session failure:"
+ " callout unencrypted to %s [%s] (not in hosts_require_tls)",
+ host->name, host->address);
+ yield = smtp_setup_conn(&sx, TRUE, TRUE);
+ }
+ if (yield != OK)
+ {
+ if (addr->message) addr->message = string_sprintf("%s [%s] %s",
+ host->name, host->address, addr->message);
+ errno = addr->basic_errno;
transport_name = NULL;
deliver_host = deliver_host_address = NULL;
deliver_domain = save_deliver_domain;
- continue;
- }
-
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
- {
- int rc;
-
- tls_out.dane_verified = FALSE;
- tls_out.tlsa_usage = 0;
- dane_required =
- verify_check_given_host(&ob->hosts_require_dane, host) == OK;
-
- if (host->dnssec == DS_YES)
- {
- if( dane_required
- || verify_check_given_host(&ob->hosts_try_dane, host) == OK
- )
- {
- if ((rc = tlsa_lookup(host, &tlsa_dnsa, dane_required)) != OK)
- return rc;
- dane = TRUE;
- }
- }
- else if (dane_required)
- {
- log_write(0, LOG_MAIN, "DANE error: %s lookup not DNSSEC", host->name);
- return FAIL;
- }
-
- if (dane)
- ob->tls_tempfail_tryclear = FALSE;
- }
-#endif /*DANE*/
-
- /* Expand the helo_data string to find the host name to use. */
-
- if (tf->helo_data)
- {
- uschar * s = expand_string(tf->helo_data);
- if (!s)
- log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: failed to expand transport's "
- "helo_data value for callout: %s", addr->address,
- expand_string_message);
- else active_hostname = s;
- }
-
- /* Wait for initial response, and send HELO. The smtp_write_command()
- function leaves its command in big_buffer. This is used in error responses.
- Initialize it in case the connection is rejected. */
-
- Ustrcpy(big_buffer, "initial connection");
-
- /* Unless ssl-on-connect, wait for the initial greeting */
- smtps_redo_greeting:
-
-#ifdef SUPPORT_TLS
- if (!smtps || (smtps && tls_out.active >= 0))
-#endif
- {
-#ifdef TCP_QUICKACK
- (void) setsockopt(inblock.sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
-#endif
- if (!(done= smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout)))
- goto RESPONSE_FAILED;
+ /* Failure to accept HELO is cached; this blocks the whole domain for all
+ senders. I/O errors and defer responses are not cached. */
-#ifndef DISABLE_EVENT
- lookup_dnssec_authenticated = host->dnssec==DS_YES ? US"yes"
- : host->dnssec==DS_NO ? US"no" : NULL;
- if (event_raise(addr->transport->event_action,
- US"smtp:connect", responsebuffer))
+ if (yield == FAIL && (errno == 0 || errno == ERRNO_SMTPCLOSED))
{
- lookup_dnssec_authenticated = NULL;
- /* Logging? Debug? */
- goto RESPONSE_FAILED;
+ setflag(addr, af_verify_nsfail);
+ new_domain_record.result = ccache_reject;
+ done = TRUE;
}
- lookup_dnssec_authenticated = NULL;
-#endif
- }
-
- /* Not worth checking greeting line for ESMTP support */
- if (!(esmtp = verify_check_given_host(&ob->hosts_avoid_esmtp, host) != OK))
- DEBUG(D_transport)
- debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
-
- tls_redo_helo:
-
-#ifdef SUPPORT_TLS
- if (smtps && tls_out.active < 0) /* ssl-on-connect, first pass */
- {
- peer_offered &= ~PEER_OFFERED_TLS;
- ob->tls_tempfail_tryclear = FALSE;
- }
- else /* all other cases */
-#endif
-
- { esmtp_retry:
-
- if (!(done= smtp_write_command(&outblock, FALSE, "%s %s\r\n",
- !esmtp? "HELO" : lmtp? "LHLO" : "EHLO", active_hostname) >= 0))
- goto SEND_FAILED;
- if (!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout))
- {
- if (errno != 0 || responsebuffer[0] == 0 || lmtp || !esmtp || tls_out.active >= 0)
- {
- done= FALSE;
- goto RESPONSE_FAILED;
- }
-#ifdef SUPPORT_TLS
- peer_offered &= ~PEER_OFFERED_TLS;
-#endif
- esmtp = FALSE;
- goto esmtp_retry; /* fallback to HELO */
- }
-
- /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
-
- peer_offered = esmtp
- ? ehlo_response(responsebuffer, sizeof(responsebuffer),
- (!suppress_tls && tls_out.active < 0 ? PEER_OFFERED_TLS : 0)
- | 0 /* no IGNQ */
- | 0 /* no PRDR */
-#ifdef SUPPORT_I18N
- | (addr->prop.utf8_msg && !addr->prop.utf8_downcvt
- ? PEER_OFFERED_UTF8 : 0)
-#endif
- | 0 /* no DSN */
- | 0 /* no PIPE */
-
- /* only care about SIZE if we have size from inbound */
- | (message_size > 0 && ob->size_addition >= 0
- ? PEER_OFFERED_SIZE : 0)
- )
- : 0;
- }
-
- size_str = options & vopt_is_recipient && peer_offered & PEER_OFFERED_SIZE
- ? string_sprintf(" SIZE=%d", message_size + ob->size_addition) : US"";
-
-#ifdef SUPPORT_TLS
- smtp_peer_options |= peer_offered & PEER_OFFERED_TLS;
-#endif
-
- /* If TLS is available on this connection attempt to
- start up a TLS session, unless the host is in hosts_avoid_tls. If successful,
- send another EHLO - the server may give a different answer in secure mode. We
- use a separate buffer for reading the response to STARTTLS so that if it is
- negative, the original EHLO data is available for subsequent analysis, should
- the client not be required to use TLS. If the response is bad, copy the buffer
- for error analysis. */
-
-#ifdef SUPPORT_TLS
- if ( peer_offered & PEER_OFFERED_TLS
- && verify_check_given_host(&ob->hosts_avoid_tls, host) != OK
- && verify_check_given_host(&ob->hosts_verify_avoid_tls, host) != OK
- )
- {
- uschar buffer2[4096];
- if ( !smtps
- && !(done= smtp_write_command(&outblock, FALSE, "STARTTLS\r\n") >= 0))
- goto SEND_FAILED;
-
- /* If there is an I/O error, transmission of this message is deferred. If
- there is a temporary rejection of STARRTLS and tls_tempfail_tryclear is
- false, we also defer. However, if there is a temporary rejection of STARTTLS
- and tls_tempfail_tryclear is true, or if there is an outright rejection of
- STARTTLS, we carry on. This means we will try to send the message in clear,
- unless the host is in hosts_require_tls (tested below). */
-
- if (!smtps && !smtp_read_response(&inblock, buffer2, sizeof(buffer2), '2',
- ob->command_timeout))
- {
- if ( errno != 0
- || buffer2[0] == 0
- || buffer2[0] == '4' && !ob->tls_tempfail_tryclear
- )
- {
- Ustrncpy(responsebuffer, buffer2, sizeof(responsebuffer));
- done= FALSE;
- goto RESPONSE_FAILED;
- }
- }
-
- /* STARTTLS accepted or ssl-on-connect: try to negotiate a TLS session. */
else
- {
- int oldtimeout = ob->command_timeout;
- int rc;
-
- ob->command_timeout = callout;
- rc = tls_client_start(inblock.sock, host, addr, addr->transport
-# ifdef EXPERIMENTAL_DANE
- , dane ? &tlsa_dnsa : NULL
-# endif
- );
- ob->command_timeout = oldtimeout;
-
- /* TLS negotiation failed; give an error. Try in clear on a new
- connection, if the options permit it for this host. */
- if (rc != OK)
- {
- HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP(close)>>\n");
- (void)close(inblock.sock);
-# ifndef DISABLE_EVENT
- (void) event_raise(addr->transport->event_action,
- US"tcp:close", NULL);
-# endif
- if ( ob->tls_tempfail_tryclear
- && !smtps
- && verify_check_given_host(&ob->hosts_require_tls, host) != OK
- )
- {
- log_write(0, LOG_MAIN, "TLS session failure:"
- " callout unencrypted to %s [%s] (not in hosts_require_tls)",
- host->name, host->address);
- suppress_tls = TRUE;
- goto tls_retry_connection;
- }
-
- /*save_errno = ERRNO_TLSFAILURE;*/
- /*message = US"failure while setting up TLS session";*/
- send_quit = FALSE;
- done= FALSE;
- goto TLS_FAILED;
- }
-
- /* TLS session is set up. Copy info for logging. */
- addr->cipher = tls_out.cipher;
- addr->peerdn = tls_out.peerdn;
-
- /* For SMTPS we need to wait for the initial OK response, then do HELO. */
- if (smtps)
- goto smtps_redo_greeting;
-
- /* For STARTTLS we need to redo EHLO */
- goto tls_redo_helo;
- }
- }
-
- /* If the host is required to use a secure channel, ensure that we have one. */
- if (tls_out.active < 0)
- if (
-# ifdef EXPERIMENTAL_DANE
- dane ||
-# endif
- verify_check_given_host(&ob->hosts_require_tls, host) == OK
- )
- {
- /*save_errno = ERRNO_TLSREQUIRED;*/
- log_write(0, LOG_MAIN,
- "H=%s [%s]: a TLS session is required for this host, but %s",
- host->name, host->address,
- peer_offered & PEER_OFFERED_TLS
- ? "an attempt to start TLS failed"
- : "the server did not offer TLS support");
- done= FALSE;
- goto TLS_FAILED;
- }
-
-#endif /*SUPPORT_TLS*/
-
- done = TRUE; /* so far so good; have response to HELO */
-
- /* For now, transport_filter by cutthrough-delivery is not supported */
- /* Need proper integration with the proper transport mechanism. */
- if (cutthrough.delivery)
- {
-#ifndef DISABLE_DKIM
- uschar * s;
-#endif
- if (addr->transport->filter_command)
- {
- cutthrough.delivery = FALSE;
- HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of transport filter\n");
- }
-#ifndef DISABLE_DKIM
- else if ((s = ob->dkim.dkim_domain) && (s = expand_string(s)) && *s)
- {
- cutthrough.delivery = FALSE;
- HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of DKIM signing\n");
- }
-#endif
- }
-
- SEND_FAILED:
- RESPONSE_FAILED:
- TLS_FAILED:
- ;
- /* Clear down of the TLS, SMTP and TCP layers on error is handled below. */
-
- /* Failure to accept HELO is cached; this blocks the whole domain for all
- senders. I/O errors and defer responses are not cached. */
-
- if (!done)
- {
- *failure_ptr = US"mail"; /* At or before MAIL */
- if (errno == 0 && responsebuffer[0] == '5')
- {
- setflag(addr, af_verify_nsfail);
- new_domain_record.result = ccache_reject;
- }
- }
-
-#ifdef SUPPORT_I18N
- else if ( addr->prop.utf8_msg
- && !addr->prop.utf8_downcvt
- && !(peer_offered & PEER_OFFERED_UTF8)
- )
- {
- HDEBUG(D_acl|D_v) debug_printf("utf8 required but not offered\n");
- errno = ERRNO_UTF8_FWD;
- setflag(addr, af_verify_nsfail);
- done = FALSE;
- }
- else if ( addr->prop.utf8_msg
- && (addr->prop.utf8_downcvt || !(peer_offered & PEER_OFFERED_UTF8))
- && !(setflag(addr, af_utf8_downcvt),
- from_address = string_address_utf8_to_alabel(from_address,
- &addr->message)
- ) )
- {
- errno = ERRNO_EXPANDFAIL;
- setflag(addr, af_verify_nsfail);
- done = FALSE;
+ done = FALSE;
+ goto no_conn;
}
-#endif
-
- /* If we haven't authenticated, but are required to, give up. */
- /* Try to AUTH */
- else done = smtp_auth(responsebuffer, sizeof(responsebuffer),
- addr, host, ob, esmtp, &inblock, &outblock) == OK &&
+ /* If we needed to authenticate, smtp_setup_conn() did that. Copy
+ the AUTH info for logging */
- /* Copy AUTH info for logging */
- ( (addr->authenticator = client_authenticator),
- (addr->auth_id = client_authenticated_id),
+ addr->authenticator = client_authenticator;
+ addr->auth_id = client_authenticated_id;
/* Build a mail-AUTH string (re-using responsebuffer for convenience */
- !smtp_mail_auth_str(responsebuffer, sizeof(responsebuffer), addr, ob)
- ) &&
- ( (addr->auth_sndr = client_authenticated_sender),
+ done =
+ !smtp_mail_auth_str(responsebuffer, sizeof(responsebuffer), addr, ob)
+ && (
+ (addr->auth_sndr = client_authenticated_sender),
/* Send the MAIL command */
- (smtp_write_command(&outblock, FALSE,
+
+ (smtp_write_command(&sx.outblock, FALSE,
#ifdef SUPPORT_I18N
- addr->prop.utf8_msg && !addr->prop.utf8_downcvt
- ? "MAIL FROM:<%s>%s%s SMTPUTF8\r\n"
- :
+ addr->prop.utf8_msg && !addr->prop.utf8_downcvt
+ ? "MAIL FROM:<%s>%s%s SMTPUTF8\r\n"
+ :
#endif
- "MAIL FROM:<%s>%s%s\r\n",
- from_address, responsebuffer, size_str) >= 0)
- ) &&
+ "MAIL FROM:<%s>%s%s\r\n",
+ from_address,
+ responsebuffer,
+ options & vopt_is_recipient && sx.peer_offered & PEER_OFFERED_SIZE
+ ? string_sprintf(" SIZE=%d", message_size + ob->size_addition)
+ : US""
+
+ ) >= 0)
+ )
- smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
- '2', callout);
+ && smtp_read_response(&sx.inblock, responsebuffer, sizeof(responsebuffer),
+ '2', callout);
deliver_host = deliver_host_address = NULL;
deliver_domain = save_deliver_domain;
@@ -1040,10 +713,10 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
{
uschar randombuffer[1024];
BOOL random_ok =
- smtp_write_command(&outblock, FALSE,
+ smtp_write_command(&sx.outblock, FALSE,
"RCPT TO:<%.1000s@%.1000s>\r\n", random_local_part,
rcpt_domain) >= 0 &&
- smtp_read_response(&inblock, randombuffer,
+ smtp_read_response(&sx.inblock, randombuffer,
sizeof(randombuffer), '2', callout);
/* Remember when we last did a random test */
@@ -1072,11 +745,11 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
new_domain_record.random_result = ccache_reject;
done =
- smtp_write_command(&outblock, FALSE, "RSET\r\n") >= 0 &&
- smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
+ smtp_write_command(&sx.outblock, FALSE, "RSET\r\n") >= 0 &&
+ smtp_read_response(&sx.inblock, responsebuffer, sizeof(responsebuffer),
'2', callout) &&
- smtp_write_command(&outblock, FALSE,
+ smtp_write_command(&sx.outblock, FALSE,
#ifdef SUPPORT_I18N
addr->prop.utf8_msg && !addr->prop.utf8_downcvt
? "MAIL FROM:<%s> SMTPUTF8\r\n"
@@ -1084,7 +757,7 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
#endif
"MAIL FROM:<%s>\r\n",
from_address) >= 0 &&
- smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
+ smtp_read_response(&sx.inblock, responsebuffer, sizeof(responsebuffer),
'2', callout);
if (!done)
@@ -1096,7 +769,7 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
tls_close(FALSE, TRUE);
#endif
HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP(close)>>\n");
- (void)close(inblock.sock);
+ (void)close(sx.inblock.sock);
#ifndef DISABLE_EVENT
(void) event_raise(addr->transport->event_action,
US"tcp:close", NULL);
@@ -1132,9 +805,9 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
#endif
done =
- smtp_write_command(&outblock, FALSE, "RCPT TO:<%.1000s>\r\n",
+ smtp_write_command(&sx.outblock, FALSE, "RCPT TO:<%.1000s>\r\n",
rcpt) >= 0 &&
- smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
+ smtp_read_response(&sx.inblock, responsebuffer, sizeof(responsebuffer),
'2', callout);
if (done)
@@ -1148,7 +821,7 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
/* Do postmaster check if requested; if a full check is required, we
check for RCPT TO:<postmaster> (no domain) in accordance with RFC 821. */
- if (done && pm_mailfrom != NULL)
+ if (done && pm_mailfrom)
{
/* Could possibly shift before main verify, just above, and be ok
for cutthrough. But no way to handle a subsequent rcpt, so just
@@ -1157,21 +830,21 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of postmaster verify\n");
done =
- smtp_write_command(&outblock, FALSE, "RSET\r\n") >= 0 &&
- smtp_read_response(&inblock, responsebuffer,
+ smtp_write_command(&sx.outblock, FALSE, "RSET\r\n") >= 0 &&
+ smtp_read_response(&sx.inblock, responsebuffer,
sizeof(responsebuffer), '2', callout) &&
- smtp_write_command(&outblock, FALSE,
+ smtp_write_command(&sx.outblock, FALSE,
"MAIL FROM:<%s>\r\n", pm_mailfrom) >= 0 &&
- smtp_read_response(&inblock, responsebuffer,
+ smtp_read_response(&sx.inblock, responsebuffer,
sizeof(responsebuffer), '2', callout) &&
/* First try using the current domain */
((
- smtp_write_command(&outblock, FALSE,
+ smtp_write_command(&sx.outblock, FALSE,
"RCPT TO:<postmaster@%.1000s>\r\n", rcpt_domain) >= 0 &&
- smtp_read_response(&inblock, responsebuffer,
+ smtp_read_response(&sx.inblock, responsebuffer,
sizeof(responsebuffer), '2', callout)
)
@@ -1182,9 +855,9 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
(
(options & vopt_callout_fullpm) != 0 &&
- smtp_write_command(&outblock, FALSE,
+ smtp_write_command(&sx.outblock, FALSE,
"RCPT TO:<postmaster>\r\n") >= 0 &&
- smtp_read_response(&inblock, responsebuffer,
+ smtp_read_response(&sx.inblock, responsebuffer,
sizeof(responsebuffer), '2', callout)
));
@@ -1214,50 +887,55 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
don't give the IP address because this may be an internal host whose identity
is not to be widely broadcast. */
- if (!done)
+no_conn:
+ if (!done) switch(errno)
{
- if (errno == ETIMEDOUT)
- {
- HDEBUG(D_verify) debug_printf("SMTP timeout\n");
- send_quit = FALSE;
- }
+ case ETIMEDOUT:
+ HDEBUG(D_verify) debug_printf("SMTP timeout\n");
+ sx.send_quit = FALSE;
+ break;
+
#ifdef SUPPORT_I18N
- else if (errno == ERRNO_UTF8_FWD)
+ case ERRNO_UTF8_FWD:
{
extern int acl_where; /* src/acl.c */
errno = 0;
addr->message = string_sprintf(
- "response to \"%s\" from %s [%s] did not include SMTPUTF8",
- big_buffer, host->name, host->address);
- addr->user_message = acl_where == ACL_WHERE_RCPT
- ? US"533 mailbox name not allowed"
+ "response to \"EHLO\" from %s [%s] did not include SMTPUTF8",
+ host->name, host->address);
+ addr->user_message = acl_where == ACL_WHERE_RCPT
+ ? US"533 no support for internationalised mailbox name"
: US"550 mailbox unavailable";
yield = FAIL;
done = TRUE;
}
+ break;
#endif
- else if (errno == 0)
- {
- if (*responsebuffer == 0) Ustrcpy(responsebuffer, US"connection dropped");
+ case ECONNREFUSED:
+ sx.send_quit = FALSE;
+ break;
+
+ case 0:
+ if (*responsebuffer == 0) Ustrcpy(responsebuffer, US"connection dropped");
- addr->message =
- string_sprintf("response to \"%s\" from %s [%s] was: %s",
- big_buffer, host->name, host->address,
- string_printing(responsebuffer));
+ addr->message =
+ string_sprintf("response to \"%s\" from %s [%s] was: %s",
+ big_buffer, host->name, host->address,
+ string_printing(responsebuffer));
- addr->user_message = options & vopt_is_recipient
+ addr->user_message = options & vopt_is_recipient
? string_sprintf("Callout verification failed:\n%s", responsebuffer)
- : string_sprintf("Called: %s\nSent: %s\nResponse: %s",
- host->address, big_buffer, responsebuffer);
+ : string_sprintf("Called: %s\nSent: %s\nResponse: %s",
+ host->address, big_buffer, responsebuffer);
- /* Hard rejection ends the process */
+ /* Hard rejection ends the process */
- if (responsebuffer[0] == '5') /* Address rejected */
- {
- yield = FAIL;
- done = TRUE;
- }
- }
+ if (responsebuffer[0] == '5') /* Address rejected */
+ {
+ yield = FAIL;
+ done = TRUE;
+ }
+ break;
}
/* End the SMTP conversation and close the connection. */
@@ -1274,12 +952,12 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
&& !random_local_part
&& !pm_mailfrom
&& cutthrough.fd < 0
- && !lmtp
+ && !sx.lmtp
)
{
HDEBUG(D_acl|D_v) debug_printf("holding verify callout open for cutthrough delivery\n");
- cutthrough.fd = outblock.sock; /* We assume no buffer in use in the outblock */
+ cutthrough.fd = sx.outblock.sock; /* We assume no buffer in use in the outblock */
cutthrough.nrcpt = 1;
cutthrough.interface = interface;
cutthrough.host = *host;
@@ -1300,23 +978,27 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
/* Ensure no cutthrough on multiple address verifies */
if (options & vopt_callout_recipsender)
cancel_cutthrough_connection("not usable for cutthrough");
- if (send_quit)
+ if (sx.send_quit)
{
- (void) smtp_write_command(&outblock, FALSE, "QUIT\r\n");
+ (void) smtp_write_command(&sx.outblock, FALSE, "QUIT\r\n");
/* Wait a short time for response, and discard it */
- smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer),
+ smtp_read_response(&sx.inblock, responsebuffer, sizeof(responsebuffer),
'2', 1);
}
+ if (sx.inblock.sock >= 0)
+ {
#ifdef SUPPORT_TLS
- tls_close(FALSE, TRUE);
+ tls_close(FALSE, TRUE);
#endif
- HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP(close)>>\n");
- (void)close(inblock.sock);
+ HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP(close)>>\n");
+ (void)close(sx.inblock.sock);
+ sx.inblock.sock = sx.outblock.sock = -1;
#ifndef DISABLE_EVENT
- (void) event_raise(addr->transport->event_action, US"tcp:close", NULL);
+ (void) event_raise(addr->transport->event_action, US"tcp:close", NULL);
#endif
+ }
}
} /* Loop through all hosts, while !done */
@@ -1388,8 +1070,8 @@ else /* !done */
options & vopt_is_recipient ? "recipient" : "sender");
yield = DEFER;
- if (host_list->next || !addr->message)
- addr->message = dullmsg;
+ addr->message = host_list->next || !addr->message
+ ? dullmsg : string_sprintf("%s: %s", dullmsg, addr->message);
addr->user_message = smtp_return_error_details
? string_sprintf("%s for <%s>.\n"
@@ -1412,6 +1094,7 @@ else /* !done */
END_CALLOUT:
if (dbm_file) dbfn_close(dbm_file);
+tls_modify_variables(&tls_in);
return yield;
}