* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2016 */
+/* Copyright (c) University of Cambridge 1995 - 2017 */
/* See the file NOTICE for conditions of use and distribution. */
#include "../exim.h"
uschar *buffer, int *yield, uschar **message, BOOL *pass_message)
{
uschar * pl = pipelining_active ? US"pipelined " : US"";
+const uschar * s;
*yield = '4'; /* Default setting is to give a temporary error */
-/* Handle response timeout */
-
-if (*errno_value == ETIMEDOUT)
- {
- *message = US string_sprintf("SMTP timeout after %s%s",
- pl, smtp_command);
- if (transport_count > 0)
- *message = US string_sprintf("%s (%d bytes written)", *message,
- transport_count);
- return FALSE;
- }
-
-/* Handle malformed SMTP response */
-
-if (*errno_value == ERRNO_SMTPFORMAT)
+switch(*errno_value)
{
- const uschar *malfresp = string_printing(buffer);
- while (isspace(*malfresp)) malfresp++;
- *message = *malfresp == 0
- ? string_sprintf("Malformed SMTP reply (an empty line) "
- "in response to %s%s", pl, smtp_command)
- : string_sprintf("Malformed SMTP reply in response to %s%s: %s",
- pl, smtp_command, malfresp);
- return FALSE;
- }
-
-/* Handle a failed filter process error; can't send QUIT as we mustn't
-end the DATA. */
-
-if (*errno_value == ERRNO_FILTER_FAIL)
- {
- *message = US string_sprintf("transport filter process failed (%d)%s",
- more_errno,
- (more_errno == EX_EXECFAILED)? ": unable to execute command" : "");
- return FALSE;
- }
-
-/* Handle a failed add_headers expansion; can't send QUIT as we mustn't
-end the DATA. */
-
-if (*errno_value == ERRNO_CHHEADER_FAIL)
- {
- *message =
- US string_sprintf("failed to expand headers_add or headers_remove: %s",
- expand_string_message);
- return FALSE;
- }
-
-/* Handle failure to write a complete data block */
-
-if (*errno_value == ERRNO_WRITEINCOMPLETE)
- {
- *message = US string_sprintf("failed to write a data block");
- return FALSE;
- }
+ case ETIMEDOUT: /* Handle response timeout */
+ *message = US string_sprintf("SMTP timeout after %s%s",
+ pl, smtp_command);
+ if (transport_count > 0)
+ *message = US string_sprintf("%s (%d bytes written)", *message,
+ transport_count);
+ return FALSE;
+
+ case ERRNO_SMTPFORMAT: /* Handle malformed SMTP response */
+ s = string_printing(buffer);
+ while (isspace(*s)) s++;
+ *message = *s == 0
+ ? string_sprintf("Malformed SMTP reply (an empty line) "
+ "in response to %s%s", pl, smtp_command)
+ : string_sprintf("Malformed SMTP reply in response to %s%s: %s",
+ pl, smtp_command, s);
+ return FALSE;
+
+ case ERRNO_FILTER_FAIL: /* Handle a failed filter process error;
+ can't send QUIT as we mustn't end the DATA. */
+ *message = string_sprintf("transport filter process failed (%d)%s",
+ more_errno,
+ more_errno == EX_EXECFAILED ? ": unable to execute command" : "");
+ return FALSE;
+
+ case ERRNO_CHHEADER_FAIL: /* Handle a failed add_headers expansion;
+ can't send QUIT as we mustn't end the DATA. */
+ *message =
+ string_sprintf("failed to expand headers_add or headers_remove: %s",
+ expand_string_message);
+ return FALSE;
+
+ case ERRNO_WRITEINCOMPLETE: /* failure to write a complete data block */
+ *message = string_sprintf("failed to write a data block");
+ return FALSE;
#ifdef SUPPORT_I18N
-/* Handle lack of advertised SMTPUTF8, for international message */
-if (*errno_value == ERRNO_UTF8_FWD)
- {
- *message = US"utf8 support required but not offered for forwarding";
- DEBUG(D_deliver|D_transport) debug_printf("%s\n", *message);
- return TRUE;
- }
+ case ERRNO_UTF8_FWD: /* no advertised SMTPUTF8, for international message */
+ *message = US"utf8 support required but not offered for forwarding";
+ DEBUG(D_deliver|D_transport) debug_printf("%s\n", *message);
+ return TRUE;
#endif
+ }
/* Handle error responses from the remote mailer. */
if (buffer[0] != 0)
{
- const uschar *s = string_printing(buffer);
- *message = US string_sprintf("SMTP error from remote mail server after %s%s: "
- "%s", pl, smtp_command, s);
+ *message = string_sprintf("SMTP error from remote mail server after %s%s: "
+ "%s", pl, smtp_command, s = string_printing(buffer));
*pass_message = TRUE;
*yield = buffer[0];
return TRUE;
*message = US string_sprintf("Remote host closed connection "
"in response to %s%s", pl, smtp_command);
}
-else *message = US string_sprintf("%s [%s]", host->name, host->address);
+else
+ *message = US string_sprintf("%s [%s]", host->name, host->address);
return FALSE;
}
sync_responses(smtp_context * sx, int count, int pending_DATA)
{
address_item *addr = sx->sync_addr;
+smtp_transport_options_block *ob =
+ (smtp_transport_options_block *)sx->tblock->options_block;
int yield = 0;
/* Handle the response for a MAIL command. On error, reinstate the original
if (sx->pending_MAIL)
{
count--;
- if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), '2', ((smtp_transport_options_block *)sx->tblock->options_block)->command_timeout))
+ if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+ '2', ob->command_timeout))
{
DEBUG(D_transport) debug_printf("bad response for MAIL\n");
Ustrcpy(big_buffer, mail_command); /* Fits, because it came from there! */
while (count-- > 0)
{
if (!smtp_read_response(&sx->inblock, flushbuffer, sizeof(flushbuffer),
- '2', ((smtp_transport_options_block *)sx->tblock->options_block)->command_timeout)
+ '2', ob->command_timeout)
&& (errno != 0 || flushbuffer[0] == 0))
break;
}
while (count-- > 0)
{
- while (addr->transport_return != PENDING_DEFER) addr = addr->next;
+ while (addr->transport_return != PENDING_DEFER)
+ if (!(addr = addr->next))
+ return -2;
/* The address was accepted */
addr->host_used = sx->host;
- if (smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), '2', ((smtp_transport_options_block *)sx->tblock->options_block)->command_timeout))
+ if (smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+ '2', ob->command_timeout))
{
yield |= 1;
addr->transport_return = PENDING_OK;
else if (errno == ETIMEDOUT)
{
uschar *message = string_sprintf("SMTP timeout after RCPT TO:<%s>",
- transport_rcpt_address(addr, sx->tblock->rcpt_include_affixes));
+ transport_rcpt_address(addr, sx->tblock->rcpt_include_affixes));
set_errno_nohost(sx->first_addr, ETIMEDOUT, message, DEFER, FALSE);
retry_add_item(addr, addr->address_retry_key, 0);
update_waiting = FALSE;
"%s", transport_rcpt_address(addr, sx->tblock->rcpt_include_affixes),
string_printing(sx->buffer));
setflag(addr, af_pass_message);
- msglog_line(sx->host, addr->message);
+ if (!sx->verify)
+ msglog_line(sx->host, addr->message);
/* The response was 5xx */
addr->basic_errno = ERRNO_RCPT4XX;
addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
+ if (!sx->verify)
+ {
#ifndef DISABLE_EVENT
- event_defer_errno = addr->more_errno;
- msg_event_raise(US"msg:rcpt:host:defer", addr);
+ event_defer_errno = addr->more_errno;
+ msg_event_raise(US"msg:rcpt:host:defer", addr);
#endif
- /* Log temporary errors if there are more hosts to be tried.
- If not, log this last one in the == line. */
+ /* Log temporary errors if there are more hosts to be tried.
+ If not, log this last one in the == line. */
- if (sx->host->next)
- log_write(0, LOG_MAIN, "H=%s [%s]: %s", sx->host->name, sx->host->address, addr->message);
+ if (sx->host->next)
+ log_write(0, LOG_MAIN, "H=%s [%s]: %s",
+ sx->host->name, sx->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. */
- if (((smtp_transport_options_block *)sx->tblock->options_block)->address_retry_include_sender)
- {
- uschar *altkey = string_sprintf("%s:<%s>", addr->address_retry_key,
- sender_address);
- retry_add_item(addr, altkey, 0);
- }
- else retry_add_item(addr, 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);
+ }
}
}
} /* Loop for next RCPT response */
/* Update where to start at for the next block of responses, unless we
have already handled all the addresses. */
-if (addr != NULL) sx->sync_addr = addr->next;
+if (addr) sx->sync_addr = addr->next;
/* Handle a response to DATA. If we have not had any good recipients, either
previously or in this block, the response is ignored. */
if (pending_DATA != 0 &&
- !smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), '3', ((smtp_transport_options_block *)sx->tblock->options_block)->command_timeout))
+ !smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+ '3', ob->command_timeout))
{
int code;
uschar *msg;
-uschar
-ehlo_response(uschar * buf, size_t bsize, uschar checks)
+static uschar
+ehlo_response(uschar * buf, uschar checks)
{
+size_t bsize = Ustrlen(buf);
+
#ifdef SUPPORT_TLS
-if ( checks & PEER_OFFERED_TLS
+if ( checks & OPTION_TLS
&& pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
- checks &= ~PEER_OFFERED_TLS;
+ checks &= ~OPTION_TLS;
#endif
-if ( checks & PEER_OFFERED_IGNQ
+if ( checks & OPTION_IGNQ
&& pcre_exec(regex_IGNOREQUOTA, NULL, CS buf, bsize, 0,
PCRE_EOPT, NULL, 0) < 0)
- checks &= ~PEER_OFFERED_IGNQ;
+ checks &= ~OPTION_IGNQ;
-if ( checks & PEER_OFFERED_CHUNKING
+if ( checks & OPTION_CHUNKING
&& pcre_exec(regex_CHUNKING, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
- checks &= ~PEER_OFFERED_CHUNKING;
+ checks &= ~OPTION_CHUNKING;
#ifndef DISABLE_PRDR
-if ( checks & PEER_OFFERED_PRDR
+if ( checks & OPTION_PRDR
&& pcre_exec(regex_PRDR, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
- checks &= ~PEER_OFFERED_PRDR;
+ checks &= ~OPTION_PRDR;
#endif
#ifdef SUPPORT_I18N
-if ( checks & PEER_OFFERED_UTF8
+if ( checks & OPTION_UTF8
&& pcre_exec(regex_UTF8, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
- checks &= ~PEER_OFFERED_UTF8;
+ checks &= ~OPTION_UTF8;
#endif
-if ( checks & PEER_OFFERED_DSN
+if ( checks & OPTION_DSN
&& pcre_exec(regex_DSN, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
- checks &= ~PEER_OFFERED_DSN;
+ checks &= ~OPTION_DSN;
-if ( checks & PEER_OFFERED_PIPE
+if ( checks & OPTION_PIPE
&& pcre_exec(regex_PIPELINING, NULL, CS buf, bsize, 0,
PCRE_EOPT, NULL, 0) < 0)
- checks &= ~PEER_OFFERED_PIPE;
+ checks &= ~OPTION_PIPE;
-if ( checks & PEER_OFFERED_SIZE
+if ( checks & OPTION_SIZE
&& pcre_exec(regex_SIZE, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
- checks &= ~PEER_OFFERED_SIZE;
+ checks &= ~OPTION_SIZE;
return checks;
}
{
smtp_transport_options_block * ob =
(smtp_transport_options_block *)(tctx->tblock->options_block);
+smtp_context * sx = tctx->smtp_context;
int cmd_count = 0;
int prev_cmd_count;
-uschar * buffer = tctx->buffer;
-smtp_context sx;
-
/* Write SMTP chunk header command */
if (chunk_size > 0)
{
- if((cmd_count = smtp_write_command(tctx->outblock, FALSE, "BDAT %u%s\r\n",
+ if((cmd_count = smtp_write_command(&sx->outblock, FALSE, "BDAT %u%s\r\n",
chunk_size,
flags & tc_chunk_last ? " LAST" : "")
) < 0) return ERROR;
data_command = string_copy(big_buffer); /* Save for later error message */
}
-prev_cmd_count = cmd_count += tctx->cmd_count;
+prev_cmd_count = cmd_count += sx->cmd_count;
/* Reap responses for any previous, but not one we just emitted */
if (chunk_size > 0)
prev_cmd_count--;
-if (tctx->pending_BDAT)
+if (sx->pending_BDAT)
prev_cmd_count--;
if (flags & tc_reap_prev && prev_cmd_count > 0)
{
- sx.first_addr = tctx->first_addr;
- sx.tblock = tctx->tblock;
- sx.sync_addr = *tctx->sync_addr;
- sx.host = tctx->host;
- sx.pending_MAIL = tctx->pending_MAIL;
- sx.inblock = *tctx->inblock;
-
DEBUG(D_transport) debug_printf("look for %d responses"
" for previous pipelined cmds\n", prev_cmd_count);
- switch(sync_responses(&sx, prev_cmd_count, 0))
+ switch(sync_responses(sx, prev_cmd_count, 0))
{
case 1: /* 2xx (only) => OK */
- case 3: tctx->good_RCPT = TRUE; /* 2xx & 5xx => OK & progress made */
- case 2: *tctx->completed_address = TRUE; /* 5xx (only) => progress made */
+ case 3: sx->good_RCPT = TRUE; /* 2xx & 5xx => OK & progress made */
+ case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */
case 0: break; /* No 2xx or 5xx, but no probs */
case -1: /* Timeout on RCPT */
default: return ERROR; /* I/O error, or any MAIL/DATA error */
}
- *tctx->sync_addr = sx.sync_addr;
cmd_count = 1;
- if (!tctx->pending_BDAT)
+ if (!sx->pending_BDAT)
pipelining_active = FALSE;
}
/* Reap response for an outstanding BDAT */
-if (tctx->pending_BDAT)
+if (sx->pending_BDAT)
{
DEBUG(D_transport) debug_printf("look for one response for BDAT\n");
- if (!smtp_read_response(tctx->inblock, sx.buffer, sizeof(sx.buffer), '2',
+ if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), '2',
ob->command_timeout))
{
- if (errno == 0 && sx.buffer[0] == '4')
+ if (errno == 0 && sx->buffer[0] == '4')
{
errno = ERRNO_DATA4XX; /*XXX does this actually get used? */
- tctx->first_addr->more_errno |=
- ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8;
+ sx->addrlist->more_errno |=
+ ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
}
return ERROR;
}
cmd_count--;
- tctx->pending_BDAT = FALSE;
+ sx->pending_BDAT = FALSE;
pipelining_active = FALSE;
}
else if (chunk_size > 0)
- tctx->pending_BDAT = TRUE;
+ sx->pending_BDAT = TRUE;
-tctx->cmd_count = cmd_count;
+sx->cmd_count = cmd_count;
return OK;
}
ctx connection context
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
- a delivery attempt
Returns: OK - the connection was made and the delivery attempted;
fd is set in the conn context, tls_out set up.
to expand
*/
int
-smtp_setup_conn(smtp_context * sx, BOOL suppress_tls, BOOL verify)
+smtp_setup_conn(smtp_context * sx, BOOL suppress_tls)
{
#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
dns_answer tlsa_dnsa;
#endif
BOOL pass_message = FALSE;
-
uschar * message = NULL;
-int save_errno;
int yield = OK;
int rc;
if ((sx->max_rcpt = sx->tblock->max_addresses) == 0) sx->max_rcpt = 999999;
sx->peer_offered = 0;
+sx->avoid_option = 0;
sx->igquotstr = US"";
if (!sx->helo_data) sx->helo_data = sx->ob->helo_data;
#ifdef EXPERIMENTAL_DSN_INFO
if (continue_hostname == NULL)
{
- if (verify)
+ if (sx->verify)
HDEBUG(D_verify) debug_printf("interface=%s port=%d\n", sx->interface, sx->port);
/* This puts port into host->port */
if (sx->inblock.sock < 0)
{
uschar * msg = NULL;
- int save_errno = errno;
- if (verify)
+ if (sx->verify)
{
- msg = strerror(errno);
+ msg = US 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)
+ errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
+ sx->verify ? string_sprintf("could not connect: %s", msg)
: NULL,
DEFER, FALSE);
sx->send_quit = FALSE;
if (sx->helo_data)
if (!(sx->helo_data = expand_string(sx->helo_data)))
- if (verify)
+ if (sx->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);
if ((sx->helo_data = string_domain_utf8_to_alabel(sx->helo_data,
&expand_string_message)),
expand_string_message)
- if (verify)
+ if (sx->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_TLS
if (sx->smtps)
{
- smtp_peer_options |= PEER_OFFERED_TLS;
+ smtp_peer_options |= OPTION_TLS;
suppress_tls = FALSE;
sx->ob->tls_tempfail_tryclear = FALSE;
smtp_command = US"SSL-on-connect";
if (rsp != sx->buffer && rsp[0] == 0 && (errno == 0 || errno == ECONNRESET))
{
- sx->send_quit = FALSE;
- save_errno = ERRNO_SMTPCLOSED;
- message = string_sprintf("Remote host closed connection "
- "in response to %s (EHLO response was: %s)",
- smtp_command, sx->buffer);
- goto FAILED;
+ errno = ERRNO_SMTPCLOSED;
+ goto EHLOHELO_FAILED;
}
Ustrncpy(sx->buffer, rsp, sizeof(sx->buffer)/2);
goto RESPONSE_FAILED;
}
}
- sx->peer_offered = smtp_peer_options = 0;
+ sx->avoid_option = sx->peer_offered = smtp_peer_options = 0;
if (sx->esmtp || sx->lmtp)
{
- sx->peer_offered = ehlo_response(sx->buffer, Ustrlen(sx->buffer),
- PEER_OFFERED_TLS /* others checked later */
+ sx->peer_offered = ehlo_response(sx->buffer,
+ OPTION_TLS /* others checked later */
);
/* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
#ifdef SUPPORT_TLS
- smtp_peer_options |= sx->peer_offered & PEER_OFFERED_TLS;
+ smtp_peer_options |= sx->peer_offered & OPTION_TLS;
#endif
}
}
for error analysis. */
#ifdef SUPPORT_TLS
-if ( smtp_peer_options & PEER_OFFERED_TLS
+if ( smtp_peer_options & OPTION_TLS
&& !suppress_tls
&& verify_check_given_host(&sx->ob->hosts_avoid_tls, sx->host) != OK
- && ( !verify
+ && ( !sx->verify
|| verify_check_given_host(&sx->ob->hosts_verify_avoid_tls, sx->host) != OK
) )
{
)
{
Ustrncpy(sx->buffer, buffer2, sizeof(sx->buffer));
+ sx->buffer[sizeof(sx->buffer)-1] = '\0';
goto RESPONSE_FAILED;
}
}
sx->host->name, sx->host->address);
# endif
- save_errno = ERRNO_TLSFAILURE;
+ errno = ERRNO_TLSFAILURE;
message = US"failure while setting up TLS session";
sx->send_quit = FALSE;
goto TLS_FAILED;
|| verify_check_given_host(&sx->ob->hosts_require_tls, sx->host) == OK
)
{
- save_errno = ERRNO_TLSREQUIRED;
+ errno = ERRNO_TLSREQUIRED;
message = string_sprintf("a TLS session is required, but %s",
- smtp_peer_options & PEER_OFFERED_TLS
+ smtp_peer_options & OPTION_TLS
? "an attempt to start TLS failed" : "the server did not offer TLS support");
goto TLS_FAILED;
}
{
if (sx->esmtp || sx->lmtp)
{
- sx->peer_offered = ehlo_response(sx->buffer, Ustrlen(sx->buffer),
+ sx->peer_offered = ehlo_response(sx->buffer,
0 /* no TLS */
- | (sx->lmtp && sx->ob->lmtp_ignore_quota ? PEER_OFFERED_IGNQ : 0)
- | PEER_OFFERED_CHUNKING
- | PEER_OFFERED_PRDR
+ | (sx->lmtp && sx->ob->lmtp_ignore_quota ? OPTION_IGNQ : 0)
+ | OPTION_CHUNKING
+ | OPTION_PRDR
#ifdef SUPPORT_I18N
- | (sx->addrlist->prop.utf8_msg ? PEER_OFFERED_UTF8 : 0)
+ | (sx->addrlist->prop.utf8_msg ? OPTION_UTF8 : 0)
/*XXX if we hand peercaps on to continued-conn processes,
must not depend on this addr */
#endif
- | PEER_OFFERED_DSN
- | PEER_OFFERED_PIPE
- | (sx->ob->size_addition >= 0 ? PEER_OFFERED_SIZE : 0)
+ | OPTION_DSN
+ | OPTION_PIPE
+ | (sx->ob->size_addition >= 0 ? OPTION_SIZE : 0)
);
/* Set for IGNOREQUOTA if the response to LHLO specifies support and the
lmtp_ignore_quota option was set. */
- sx->igquotstr = sx->peer_offered & PEER_OFFERED_IGNQ ? US" IGNOREQUOTA" : US"";
+ sx->igquotstr = sx->peer_offered & OPTION_IGNQ ? US" IGNOREQUOTA" : US"";
/* If the response to EHLO specified support for the SIZE parameter, note
this, provided size_addition is non-negative. */
- smtp_peer_options |= sx->peer_offered & PEER_OFFERED_SIZE;
+ smtp_peer_options |= sx->peer_offered & OPTION_SIZE;
/* Note whether the server supports PIPELINING. If hosts_avoid_esmtp matched
the current host, esmtp will be false, so PIPELINING can never be used. If
the current host matches hosts_avoid_pipelining, don't do it. */
- if ( sx->peer_offered & PEER_OFFERED_PIPE
+ if ( sx->peer_offered & OPTION_PIPE
&& verify_check_given_host(&sx->ob->hosts_avoid_pipelining, sx->host) != OK)
- smtp_peer_options |= PEER_OFFERED_PIPE;
+ smtp_peer_options |= OPTION_PIPE;
DEBUG(D_transport) debug_printf("%susing PIPELINING\n",
- smtp_peer_options & PEER_OFFERED_PIPE ? "" : "not ");
+ smtp_peer_options & OPTION_PIPE ? "" : "not ");
- if ( sx->peer_offered & PEER_OFFERED_CHUNKING
+ if ( sx->peer_offered & OPTION_CHUNKING
&& verify_check_given_host(&sx->ob->hosts_try_chunking, sx->host) != OK)
- sx->peer_offered &= ~PEER_OFFERED_CHUNKING;
+ sx->peer_offered &= ~OPTION_CHUNKING;
- if (sx->peer_offered & PEER_OFFERED_CHUNKING)
+ if (sx->peer_offered & OPTION_CHUNKING)
{DEBUG(D_transport) debug_printf("CHUNKING usable\n");}
#ifndef DISABLE_PRDR
- if ( sx->peer_offered & PEER_OFFERED_PRDR
+ if ( sx->peer_offered & OPTION_PRDR
&& verify_check_given_host(&sx->ob->hosts_try_prdr, sx->host) != OK)
- sx->peer_offered &= ~PEER_OFFERED_PRDR;
+ sx->peer_offered &= ~OPTION_PRDR;
- if (sx->peer_offered & PEER_OFFERED_PRDR)
+ if (sx->peer_offered & OPTION_PRDR)
{DEBUG(D_transport) debug_printf("PRDR usable\n");}
#endif
/* Note if the server supports DSN */
- smtp_peer_options |= sx->peer_offered & PEER_OFFERED_DSN;
+ smtp_peer_options |= sx->peer_offered & OPTION_DSN;
DEBUG(D_transport) debug_printf("%susing DSN\n",
- sx->peer_offered & PEER_OFFERED_DSN ? "" : "not ");
+ sx->peer_offered & OPTION_DSN ? "" : "not ");
/* Note if the response to EHLO specifies support for the AUTH extension.
If it has, check that this host is one we want to authenticate to, and do
}
}
}
-pipelining_active = !!(smtp_peer_options & PEER_OFFERED_PIPE);
+pipelining_active = !!(smtp_peer_options & OPTION_PIPE);
/* The setting up of the SMTP call is now complete. Any subsequent errors are
message-specific. */
}
/* If this is an international message we need the host to speak SMTPUTF8 */
-if (sx->utf8_needed && !(sx->peer_offered & PEER_OFFERED_UTF8))
+if (sx->utf8_needed && !(sx->peer_offered & OPTION_UTF8))
{
errno = ERRNO_UTF8_FWD;
goto RESPONSE_FAILED;
{
int code;
- uschar * set_message;
RESPONSE_FAILED:
- {
- save_errno = errno;
message = NULL;
- sx->send_quit = check_response(sx->host, &save_errno, sx->addrlist->more_errno,
+ sx->send_quit = check_response(sx->host, &errno, sx->addrlist->more_errno,
sx->buffer, &code, &message, &pass_message);
goto FAILED;
- }
SEND_FAILED:
- {
- save_errno = errno;
code = '4';
message = US string_sprintf("send() to %s [%s] failed: %s",
- sx->host->name, sx->host->address, strerror(save_errno));
+ sx->host->name, sx->host->address, strerror(errno));
sx->send_quit = FALSE;
goto FAILED;
- }
/* This label is jumped to directly when a TLS negotiation has failed,
or was not done for a host for which it is required. Values will be set
- in message and save_errno, and setting_up will always be true. Treat as
+ in message and errno, and setting_up will always be true. Treat as
a temporary error. */
+ EHLOHELO_FAILED:
+ code = '4';
+ message = string_sprintf("Remote host closed connection in response to %s"
+ " (EHLO response was: %s)", smtp_command, sx->buffer);
+ sx->send_quit = FALSE;
+ goto FAILED;
+
#ifdef SUPPORT_TLS
TLS_FAILED:
- code = '4';
+ code = '4';
+ goto FAILED;
#endif
/* The failure happened while setting up the call; see if the failure was
whatever), defer all addresses, and yield DEFER, so that the host is not
tried again for a while. */
- FAILED:
+FAILED:
sx->ok = FALSE; /* For when reached by GOTO */
- set_message = message;
yield = code == '5'
#ifdef SUPPORT_I18N
#endif
? FAIL : DEFER;
- set_errno(sx->addrlist, save_errno, set_message, yield, pass_message, sx->host
+ set_errno(sx->addrlist, errno, message, yield, pass_message, sx->host
#ifdef EXPERIMENTAL_DSN_INFO
, sx->smtp_greeting, sx->helo_response
#endif
specified in the transports, and therefore not visible at top level, in which
case continue_more won't get set. */
-HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP(close)>>\n");
+HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n");
if (sx->send_quit)
{
shutdown(sx->outblock.sock, SHUT_WR);
*p = 0;
-/* If we know the receiving MTA supports the SIZE qualification,
+/* If we know the receiving MTA supports the SIZE qualification, and we know it,
send it, adding something to the message size to allow for imprecision
and things that get added en route. Exim keeps the number of lines
in a message, so we can give an accurate value for the original message, but we
need some additional to handle added headers. (Double "." characters don't get
included in the count.) */
-if (sx->peer_offered & PEER_OFFERED_SIZE)
+if ( message_size > 0
+ && sx->peer_offered & OPTION_SIZE && !(sx->avoid_option & OPTION_SIZE))
{
sprintf(CS p, " SIZE=%d", message_size+message_linecount+sx->ob->size_addition);
while (*p) p++;
request that */
sx->prdr_active = FALSE;
-if (sx->peer_offered & PEER_OFFERED_PRDR)
+if (sx->peer_offered & OPTION_PRDR)
for (addr = addrlist; addr; addr = addr->next)
if (addr->transport_return == PENDING_DEFER)
{
/* If it supports internationalised messages, and this meesage need that,
request it */
-if ( sx->peer_offered & PEER_OFFERED_UTF8
+if ( sx->peer_offered & OPTION_UTF8
&& addrlist->prop.utf8_msg
&& !addrlist->prop.utf8_downcvt
)
/* Add any DSN flags to the mail command */
-if (sx->peer_offered & PEER_OFFERED_DSN && !sx->dsn_all_lasthop)
+if (sx->peer_offered & OPTION_DSN && !sx->dsn_all_lasthop)
{
if (dsn_ret == dsn_ret_hdrs)
{ Ustrcpy(p, " RET=HDRS"); p += 9; }
/* Add any DSN flags to the rcpt command */
-if (sx->peer_offered & PEER_OFFERED_DSN && !(addr->dsn_flags & rf_dsnlasthop))
+if (sx->peer_offered & OPTION_DSN && !(addr->dsn_flags & rf_dsnlasthop))
{
if (addr->dsn_flags & rf_dsnflags)
{
/*
Return:
- 0 good
- -1 MAIL response error, any read i/o error
- -2 non-MAIL response timeout
- -3 internal error; channel still usable
- -4 transmit failed
+ 0 good, rcpt results in addr->transport_return (PENDING_OK, DEFER, FAIL)
+ -1 MAIL response error
+ -2 any non-MAIL read i/o error
+ -3 non-MAIL response timeout
+ -4 internal error; channel still usable
+ -5 transmit failed
*/
int
if (build_mailcmd_options(sx, sx->first_addr) != OK)
{
*yield = ERROR;
- return -3;
+ return -4;
}
/* From here until we send the DATA command, we can make use of PIPELINING
the delivery log line. */
if ( sx->addrlist->prop.utf8_msg
- && (sx->addrlist->prop.utf8_downcvt || !(sx->peer_offered & PEER_OFFERED_UTF8))
+ && (sx->addrlist->prop.utf8_downcvt || !(sx->peer_offered & OPTION_UTF8))
)
{
if (s = string_address_utf8_to_alabel(s, &errstr), errstr)
{
set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE);
*yield = ERROR;
- return -3;
+ return -4;
}
setflag(sx->addrlist, af_utf8_downcvt);
}
switch(rc)
{
case -1: /* Transmission error */
- return -4;
+ return -5;
case +1: /* Cmd was sent */
if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), '2',
}
sx->pending_MAIL = FALSE;
break;
+
+ /* otherwise zero: command queued for pipeline */
}
/* Pass over all the relevant recipient addresses for this host, which are the
last address because we want to abort if any recipients have any kind of
problem, temporary or permanent. We know that all recipient addresses will have
the PENDING_DEFER status, because only one attempt is ever made, and we know
-that max_rcpt will be large, so all addresses will be done at once. */
+that max_rcpt will be large, so all addresses will be done at once.
+
+For verify we flush the pipeline after any (the only) rcpt address. */
for (addr = sx->first_addr, address_count = 0;
addr && address_count < sx->max_rcpt;
BOOL no_flush;
uschar * rcpt_addr;
- addr->dsn_aware = sx->peer_offered & PEER_OFFERED_DSN
+ addr->dsn_aware = sx->peer_offered & OPTION_DSN
? dsn_support_yes : dsn_support_no;
address_count++;
- no_flush = pipelining_active && (!mua_wrapper || addr->next);
+ no_flush = pipelining_active && !sx->verify
+ && (!mua_wrapper || addr->next && address_count < sx->max_rcpt);
build_rcptcmd_options(sx, addr);
{
/*XXX could we use a per-address errstr here? Not fail the whole send? */
errno = ERRNO_EXPANDFAIL;
- return -4; /*XXX too harsh? */
+ return -5; /*XXX too harsh? */
}
#endif
count = smtp_write_command(&sx->outblock, no_flush, "RCPT TO:<%s>%s%s\r\n",
rcpt_addr, sx->igquotstr, sx->buffer);
- if (count < 0) return -4;
+ if (count < 0) return -5;
if (count > 0)
{
switch(sync_responses(sx, count, 0))
{
case 3: sx->ok = TRUE; /* 2xx & 5xx => OK & progress made */
case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */
- break;
+ break;
case 1: sx->ok = TRUE; /* 2xx (only) => OK, but if LMTP, */
if (!sx->lmtp) /* can't tell about progress yet */
case 0: /* No 2xx or 5xx, but no probs */
break;
- case -1: return -2; /* Timeout on RCPT */
- default: return -1; /* I/O error, or any MAIL error */
+ case -1: return -3; /* Timeout on RCPT */
+ case -2: return -2; /* non-MAIL read i/o error */
+ default: return -1; /* any MAIL error */
}
sx->pending_MAIL = FALSE; /* Dealt with MAIL */
}
sx.interface = interface;
sx.helo_data = NULL;
sx.tblock = tblock;
+sx.verify = FALSE;
/* 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, FALSE)) != OK)
+if ((rc = smtp_setup_conn(&sx, suppress_tls)) != OK)
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. */
-if (tblock->filter_command != NULL)
+if (tblock->filter_command)
{
BOOL rc;
uschar fbuf[64];
if ( transport_filter_argv
&& *transport_filter_argv
&& **transport_filter_argv
- && sx.peer_offered & PEER_OFFERED_CHUNKING
+ && sx.peer_offered & OPTION_CHUNKING
)
{
- sx.peer_offered &= ~PEER_OFFERED_CHUNKING;
+ sx.peer_offered &= ~OPTION_CHUNKING;
DEBUG(D_transport) debug_printf("CHUNKING not usable due to transport filter\n");
}
}
switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield))
{
- case 0: break;
- case -1: goto RESPONSE_FAILED;
- case -2: goto END_OFF;
- case -3: goto SEND_QUIT;
- default: goto SEND_FAILED;
+ case 0: break;
+ case -1: case -2: goto RESPONSE_FAILED;
+ case -3: goto END_OFF;
+ case -4: goto SEND_QUIT;
+ default: goto SEND_FAILED;
}
/* If we are an MUA wrapper, abort if any RCPTs were rejected, either
if (mua_wrapper)
{
- address_item *badaddr;
- for (badaddr = sx.first_addr; badaddr; badaddr = badaddr->next)
- if (badaddr->transport_return != PENDING_OK)
+ address_item * a;
+ unsigned 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, badaddr->message, FAIL,
- testflag(badaddr, af_pass_message));
+ set_errno_nohost(addrlist, 0, a->message, FAIL,
+ testflag(a, af_pass_message));
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 & PEER_OFFERED_CHUNKING)
+if ( !(sx.peer_offered & OPTION_CHUNKING)
&& (sx.ok || (pipelining_active && !mua_wrapper)))
{
int count = smtp_write_command(&sx.outblock, FALSE, "DATA\r\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 & PEER_OFFERED_CHUNKING) && !sx.ok)
+if (!(sx.peer_offered & OPTION_CHUNKING) && !sx.ok)
{
/* Save the first address of the next batch. */
sx.first_addr = sx.next_addr;
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 & PEER_OFFERED_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;
- tctx.inblock = &sx.inblock;
- tctx.outblock = &sx.outblock;
- tctx.host = host;
- tctx.first_addr = sx.first_addr;
- tctx.sync_addr = &sx.sync_addr;
- tctx.pending_MAIL = sx.pending_MAIL;
- tctx.pending_BDAT = FALSE;
- tctx.good_RCPT = sx.ok;
- tctx.completed_address = &sx.completed_addr;
- tctx.cmd_count = 0;
- tctx.buffer = sx.buffer;
+ sx.pending_BDAT = FALSE;
+ sx.good_RCPT = sx.ok;
+ sx.cmd_count = 0;
+ tctx.smtp_context = &sx;
}
else
tctx.options |= topt_end_dot;
transport_write_timeout = sx.ob->data_timeout;
smtp_command = US"sending data block"; /* For error messages */
DEBUG(D_transport|D_v)
- if (sx.peer_offered & PEER_OFFERED_CHUNKING)
+ if (sx.peer_offered & OPTION_CHUNKING)
debug_printf(" will write message using CHUNKING\n");
else
debug_printf(" SMTP>> writing message and terminating \".\"\n");
smtp_command = US"end of data";
- if (sx.peer_offered & PEER_OFFERED_CHUNKING && tctx.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, tctx.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 */
#ifndef DISABLE_PRDR
if (sx.prdr_active) addr->flags |= af_prdr_used;
#endif
- if (sx.peer_offered & PEER_OFFERED_CHUNKING) addr->flags |= af_chunking_used;
+ if (sx.peer_offered & OPTION_CHUNKING) addr->flags |= af_chunking_used;
flag = '-';
#ifndef DISABLE_PRDR
if (! (sx.ok = smtp_write_command(&sx.outblock, FALSE, "RSET\r\n") >= 0))
{
msg = US string_sprintf("send() to %s [%s] failed: %s", host->name,
- host->address, strerror(save_errno));
+ host->address, strerror(errno));
sx.send_quit = FALSE;
}
else if (! (sx.ok = smtp_read_response(&sx.inblock, sx.buffer,
specified in the transports, and therefore not visible at top level, in which
case continue_more won't get set. */
-HDEBUG(D_transport|D_acl|D_v) debug_printf(" SMTP(close)>>\n");
+HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n");
if (sx.send_quit)
{
shutdown(sx.outblock.sock, SHUT_WR);