.tls_tempfail_tryclear = TRUE,
.tls_try_verify_hosts = US"*",
.tls_verify_cert_hostnames = US"*",
-# ifndef DISABLE_TLS_RESUME
- .host_name_extract = US"${if and {{match{$host}{.outlook.com\\$}} {match{$item}{\\N^250-([\\w.]+)\\s\\N}}} {$1}}",
-# endif
#endif
#ifdef SUPPORT_I18N
.utf8_downconvert = US"-1",
static unsigned ehlo_response(uschar * buf, unsigned checks);
+/* sync_responses() return codes */
+
+#define RESP_BIT_HAD_5XX BIT(1)
+#define RESP_BIT_HAD_2XX BIT(0)
+#define RESP_HAD_2_AND_5 (RESP_BIT_HAD_2XX | RESP_BIT_HAD_5XX)
+#define RESP_NOERROR 0
+#define RESP_RCPT_TIMEO -1
+#define RESP_RCPT_ERROR -2
+#define RESP_MAIL_OR_DATA_ERROR -3
+#define RESP_EPIPE_EHLO_ERR -4
+#define RESP_EHLO_ERR_TLS -5
/******************************************************************************/
#ifndef DISABLE_PIPE_CONNECT
{ ®ex_EARLY_PIPE, US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)" },
#endif
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
{ ®ex_LIMITS, US"\\n250[\\s\\-]LIMITS\\s" },
#endif
};
void
smtp_transport_init(transport_instance *tblock)
{
-smtp_transport_options_block *ob = SOB tblock->options_block;
+smtp_transport_options_block * ob = SOB tblock->options_block;
int old_pool = store_pool;
/* Retry_use_local_part defaults FALSE if unset */
deferred_event_raise(address_item * addr, host_item * host, uschar * evstr)
{
uschar * action = addr->transport->event_action;
-const uschar * save_domain;
-uschar * save_local;
+const uschar * save_domain, * save_local;
if (!action)
return;
}
+#if !defined(DISABLE_TLS) && !defined(DISABLE_TLS_RESUME)
+
/* Grab a string differentiating server behind a loadbalancer, for TLS
resumption when such servers do not share a session-cache */
static void
-ehlo_response_lbserver(smtp_context * sx, smtp_transport_options_block * ob)
+ehlo_response_lbserver(smtp_context * sx, const uschar * name_extract)
{
-#if !defined(DISABLE_TLS) && !defined(DISABLE_TLS_RESUME)
const uschar * s;
uschar * save_item = iterate_item;
if (sx->conn_args.have_lbserver)
return;
iterate_item = sx->buffer;
-s = expand_cstring(ob->host_name_extract);
+s = expand_cstring(name_extract);
iterate_item = save_item;
sx->conn_args.host_lbserver = s && !*s ? NULL : s;
sx->conn_args.have_lbserver = TRUE;
-#endif
}
+#endif
/******************************************************************************/
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
/* If TLS, or TLS not offered, called with the EHLO response in the buffer.
Check it for a LIMITS keyword and parse values into the smtp context structure.
{
open_db dbblock, * dbm_file;
-# ifdef EXPERIMENTAL_ESMTP_LIMITS
+# ifndef DISABLE_ESMTP_LIMITS
sx->ehlo_resp.limit_mail = sx->peer_limit_mail;
sx->ehlo_resp.limit_rcpt = sx->peer_limit_rcpt;
sx->ehlo_resp.limit_rcptdom = sx->peer_limit_rcptdom;
dbdata_ehlo_resp er = { .data = sx->ehlo_resp };
HDEBUG(D_transport)
-# ifdef EXPERIMENTAL_ESMTP_LIMITS
+# ifndef DISABLE_ESMTP_LIMITS
if (sx->ehlo_resp.limit_mail || sx->ehlo_resp.limit_rcpt || sx->ehlo_resp.limit_rcptdom)
debug_printf("writing clr %04x/%04x cry %04x/%04x lim %05d/%05d/%05d\n",
sx->ehlo_resp.cleartext_features, sx->ehlo_resp.cleartext_auths,
else
{
DEBUG(D_transport)
-# ifdef EXPERIMENTAL_ESMTP_LIMITS
+# ifndef DISABLE_ESMTP_LIMITS
if (er->data.limit_mail || er->data.limit_rcpt || er->data.limit_rcptdom)
debug_printf("EHLO response bits from cache:"
" cleartext 0x%04x/0x%04x crypted 0x%04x/0x%04x lim %05d/%05d/%05d\n",
er->data.crypted_features, er->data.crypted_auths);
sx->ehlo_resp = er->data;
-# ifdef EXPERIMENTAL_ESMTP_LIMITS
+# ifndef DISABLE_ESMTP_LIMITS
ehlo_cache_limits_apply(sx);
# endif
dbfn_close(dbm_file);
if (pending_BANNER)
{
+ const uschar * s;
+
DEBUG(D_transport) debug_printf("%s expect banner\n", __FUNCTION__);
(*countp)--;
if (!smtp_reap_banner(sx))
goto fail;
}
/*XXX EXPERIMENTAL_ESMTP_LIMITS ? */
- ehlo_response_lbserver(sx, sx->conn_args.ob);
+
+# ifndef DISABLE_TLS_RESUME
+ GET_OPTION("host_name_extract");
+ s = ((smtp_transport_options_block *)sx->conn_args.ob)->host_name_extract;
+ if (!s) s = HNE_DEFAULT;
+ ehlo_response_lbserver(sx, s);
+# endif
}
if (pending_EHLO)
| OPTION_CHUNKING | OPTION_PRDR | OPTION_DSN | OPTION_PIPE | OPTION_SIZE
| OPTION_UTF8 | OPTION_EARLY_PIPE
);
-# ifdef EXPERIMENTAL_ESMTP_LIMITS
+# ifndef DISABLE_ESMTP_LIMITS
if (tls_out.active.sock >= 0 || !(peer_offered & OPTION_TLS))
ehlo_response_limits_read(sx);
# endif
return OK; /* just carry on */
}
-# ifdef EXPERIMENTAL_ESMTP_LIMITS
+# ifndef DISABLE_ESMTP_LIMITS
/* If we are handling LIMITS, compare the actual EHLO LIMITS values with the
cached values and invalidate cache if different. OK to carry on with
connect since values are advisory. */
#ifndef DISABLE_PIPE_CONNECT
int rc;
if ((rc = smtp_reap_early_pipe(sx, &count)) != OK)
- return rc == FAIL ? -4 : -5;
+ return rc == FAIL ? RESP_EPIPE_EHLO_ERR : RESP_EHLO_ERR_TLS;
#endif
/* Handle the response for a MAIL command. On error, reinstate the original
DEBUG(D_transport) debug_printf("bad response for MAIL\n");
Ustrcpy(big_buffer, mail_command); /* Fits, because it came from there! */
if (errno == ERRNO_TLSFAILURE)
- return -5;
+ return RESP_EHLO_ERR_TLS;
if (errno == 0 && sx->buffer[0] != 0)
{
int save_errno = 0;
addr->host_used = sx->conn_args.host;
addr = addr->next;
}
- return -3;
+ return RESP_MAIL_OR_DATA_ERROR;
}
}
{
while (addr->transport_return != PENDING_DEFER)
if (!(addr = addr->next))
- return -2;
+ return RESP_RCPT_ERROR;
/* The address was accepted */
addr->host_used = sx->conn_args.host;
if (smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
'2', ob->command_timeout))
{
- yield |= 1;
+ yield |= RESP_BIT_HAD_2XX;
addr->transport_return = PENDING_OK;
/* If af_dr_retry_exists is set, there was a routing delay on this address;
/* Error on first TLS read */
else if (errno == ERRNO_TLSFAILURE)
- return -5;
+ return RESP_EHLO_ERR_TLS;
/* Timeout while reading the response */
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;
+ return RESP_RCPT_TIMEO;
}
/* Handle other errors in obtaining an SMTP response by returning -1. This
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;
+ return RESP_RCPT_ERROR;
}
/* Handle SMTP permanent and temporary response codes. */
if (sx->buffer[0] == '5')
{
addr->transport_return = FAIL;
- yield |= 2;
+ yield |= RESP_BIT_HAD_5XX;
}
/* The response was 4xx */
/* If a 452 and we've had at least one 2xx or 5xx, set next_addr to the
start point for another MAIL command. */
- if (addr->more_errno >> 8 == 52 && yield & 3)
+ if (addr->more_errno >> 8 == 52 && yield > 0)
{
if (!sx->RCPT_452) /* initialised at MAIL-ack above */
{
}
}
if (count && !(addr = addr->next))
- return -2;
+ return RESP_RCPT_ERROR;
} /* Loop for next RCPT response */
/* Update where to start at for the next block of responses, unless we
BOOL pass_message;
if (errno == ERRNO_TLSFAILURE) /* Error on first TLS read */
- return -5;
+ return RESP_EHLO_ERR_TLS;
- if (pending_DATA > 0 || (yield & 1) != 0)
+ if (pending_DATA > 0 || yield & RESP_BIT_HAD_2XX)
{
if (errno == 0 && sx->buffer[0] == '4')
{
errno = ERRNO_DATA4XX;
sx->first_addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
}
- return -3;
+ return RESP_MAIL_OR_DATA_ERROR;
}
(void)check_response(sx->conn_args.host, &errno, 0, sx->buffer, &code, &msg, &pass_message);
DEBUG(D_transport) debug_printf("%s\nerror for DATA ignored: pipelining "
authenticated_sender, ob->authenticated_sender, f.smtp_authenticated?"Y":"N");
#endif
+GET_OPTION("authenticated_sender");
if (ob->authenticated_sender)
{
uschar * new = expand_string(ob->authenticated_sender);
typedef struct smtp_compare_s
{
- uschar * current_sender_address;
+ const uschar * current_sender_address;
struct transport_instance * tblock;
} smtp_compare_t;
*/
static uschar *
-smtp_local_identity(uschar * sender, struct transport_instance * tblock)
+smtp_local_identity(const uschar * sender, struct transport_instance * tblock)
{
address_item * addr1;
uschar * if1 = US"";
#ifndef DISABLE_TLS
uschar * tlsc1 = US"";
#endif
-uschar * save_sender_address = sender_address;
+const uschar * save_sender_address = sender_address;
uschar * local_identity = NULL;
smtp_transport_options_block * ob = SOB tblock->options_block;
switch(sync_responses(sx, prev_cmd_count, 0))
{
- case 1: /* 2xx (only) => OK */
- 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 RESP_BIT_HAD_2XX: /* OK */
+ case RESP_HAD_2_AND_5: sx->good_RCPT = TRUE; /* OK & progress made */
+ case RESP_BIT_HAD_5XX: sx->completed_addr = TRUE; /* progress made */
+ case RESP_NOERROR: break; /* No 2xx or 5xx, but no probs */
- case -5: errno = ERRNO_TLSFAILURE;
- return DEFER;
+ case RESP_EHLO_ERR_TLS:errno = ERRNO_TLSFAILURE;
+ return DEFER;
#ifndef DISABLE_PIPE_CONNECT
- case -4: /* non-2xx for pipelined banner or EHLO */
+ case RESP_EPIPE_EHLO_ERR: /* non-2xx for pipelined banner or EHLO */
#endif
- case -1: /* Timeout on RCPT */
- default: return ERROR; /* I/O error, or any MAIL/DATA error */
+ case RESP_RCPT_TIMEO: /* Timeout on RCPT */
+ default: return ERROR;/* I/O error, or any MAIL/DATA error */
}
cmd_count = 1;
if (!sx->pending_BDAT)
sx->cctx.tls_ctx = NULL;
sx->inblock.cctx = sx->outblock.cctx = &sx->cctx;
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
sx->peer_limit_mail = sx->peer_limit_rcpt = sx->peer_limit_rcptdom =
#endif
sx->avoid_option = sx->peer_offered = smtp_peer_options = 0;
For early-pipe, we are ok if binding to a local interface; otherwise (if
$sending_ip_address is seen in helo_data) we disabled early-pipe above. */
+ GET_OPTION("helo_data");
if (sx->helo_data)
if (!(sx->helo_data = expand_string(sx->helo_data)))
if (sx->verify)
#ifndef DISABLE_TLS
if (sx->smtps)
{
+ const uschar * s;
+
smtp_peer_options |= OPTION_TLS;
suppress_tls = FALSE;
ob->tls_tempfail_tryclear = FALSE;
smtp_command = US"SSL-on-connect";
+
+# ifndef DISABLE_TLS_RESUME
+ /* Having no EHLO response yet, cannot peek there for a servername to detect
+ an LB. Call this anyway, so that a dummy host_name_extract option value can
+ force resumption attempts. */
+
+ GET_OPTION("host_name_extract");
+ if (!(s = ob->host_name_extract)) s = US"never-LB";
+ ehlo_response_lbserver(sx, s);
+# endif
goto TLS_NEGOTIATE;
}
#endif
DEBUG(D_transport) debug_printf("may need to auth, so pipeline no further\n");
if (smtp_write_command(sx, SCMD_FLUSH, NULL) < 0)
goto SEND_FAILED;
- if (sync_responses(sx, 2, 0) != 0)
+ if (sync_responses(sx, 2, 0) != RESP_NOERROR)
{
HDEBUG(D_transport)
debug_printf("failed reaping pipelined cmd responses\n");
if (!sx->early_pipe_active)
#endif
{
+ const uschar * s;
+
sx->peer_offered = ehlo_response(sx->buffer,
OPTION_TLS /* others checked later */
#ifndef DISABLE_PIPE_CONNECT
)
#endif
);
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
if (tls_out.active.sock >= 0 || !(sx->peer_offered & OPTION_TLS))
{
ehlo_response_limits_read(sx);
}
}
#endif
- ehlo_response_lbserver(sx, ob);
+#ifndef DISABLE_TLS_RESUME
+ GET_OPTION("host_name_extract");
+ if (!(s = ob->host_name_extract)) s = HNE_DEFAULT;
+ ehlo_response_lbserver(sx, s);
+#endif
}
/* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
sx->inblock.cctx = sx->outblock.cctx = &sx->cctx;
smtp_command = big_buffer;
sx->peer_offered = smtp_peer_options;
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
/* Limits passed by cmdline over exec. */
ehlo_limits_apply(sx,
sx->peer_limit_mail = continue_limit_mail,
if (sx->early_pipe_active)
{
- if (sync_responses(sx, 2, 0) != 0)
+ if (sync_responses(sx, 2, 0) != RESP_NOERROR)
{
HDEBUG(D_transport)
debug_printf("failed reaping pipelined cmd responses\n");
{
uschar * greeting_cmd;
- if (!sx->helo_data && !(sx->helo_data = expand_string(ob->helo_data)))
+ if (!sx->helo_data)
{
- uschar *message = string_sprintf("failed to expand helo_data: %s",
- expand_string_message);
- set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, &sx->delivery_start);
- yield = DEFER;
- goto SEND_QUIT;
+ GET_OPTION("helo_data");
+ if (!(sx->helo_data = expand_string(ob->helo_data)))
+ {
+ uschar *message = string_sprintf("failed to expand helo_data: %s",
+ expand_string_message);
+ set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, &sx->delivery_start);
+ yield = DEFER;
+ goto SEND_QUIT;
+ }
}
#ifndef DISABLE_PIPE_CONNECT
sx->ehlo_resp.crypted_features = sx->peer_offered;
#endif
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
if (tls_out.active.sock >= 0 || !(sx->peer_offered & OPTION_TLS))
{
ehlo_response_limits_read(sx);
/* If the transport sets a downconversion mode it overrides any set by ACL
for the message. */
+ GET_OPTION("utf8_downconvert");
if ((s = ob->utf8_downconvert))
{
if (!(s = expand_string(s)))
-/*
-Return:
- 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
+/* Send MAIL FROM and RCPT TO commands.
+See sw_mrc_t definition for return codes.
*/
-int
+sw_mrc_t
smtp_write_mail_and_rcpt_cmds(smtp_context * sx, int * yield)
{
address_item * addr;
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
address_item * restart_addr = NULL;
#endif
int address_count, pipe_limit;
if (build_mailcmd_options(sx, sx->first_addr) != OK)
{
*yield = ERROR;
- return -4;
+ return sw_mrc_bad_internal;
}
/* From here until we send the DATA command, we can make use of PIPELINING
sx->pending_MAIL = TRUE; /* The block starts with MAIL */
{
- uschar * s = sx->from_addr;
+ const uschar * s = sx->from_addr;
#ifdef SUPPORT_I18N
uschar * errstr = NULL;
{
set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE, &sx->delivery_start);
*yield = ERROR;
- return -4;
+ return sw_mrc_bad_internal;
}
setflag(sx->addrlist, af_utf8_downcvt);
}
switch(rc)
{
case -1: /* Transmission error */
- return -5;
+ return sw_mrc_bad_mail;
case +1: /* Cmd was sent */
if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
errno = ERRNO_MAIL4XX;
sx->addrlist->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
}
- return -1;
+ return sw_mrc_bad_mail;
}
sx->pending_MAIL = FALSE;
break;
{
int cmds_sent;
BOOL no_flush;
- uschar * rcpt_addr;
+ const uschar * rcpt_addr;
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
if ( sx->single_rcpt_domain /* restriction on domains */
&& address_count > 0 /* not first being sent */
&& Ustrcmp(addr->domain, sx->first_addr->domain) != 0 /* dom diff from first */
{
/*XXX could we use a per-address errstr here? Not fail the whole send? */
errno = ERRNO_EXPANDFAIL;
- return -5; /*XXX too harsh? */
+ return sw_mrc_tx_fail; /*XXX too harsh? */
}
#endif
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 (cmds_sent < 0) return -5;
+ if (cmds_sent < 0) return sw_mrc_tx_fail;
if (cmds_sent > 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 */
+ case RESP_HAD_2_AND_5: sx->ok = TRUE; /* OK & progress made */
+ case RESP_BIT_HAD_5XX: sx->completed_addr = TRUE; /* progress made */
break;
- case 1: sx->ok = TRUE; /* 2xx (only) => OK, but if LMTP, */
+ case RESP_BIT_HAD_2XX: sx->ok = TRUE; /* OK, but if LMTP, */
if (!sx->lmtp) /* can't tell about progress yet */
sx->completed_addr = TRUE;
- case 0: /* No 2xx or 5xx, but no probs */
+ case RESP_NOERROR: /* 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. */
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;
+ return sw_mrc_ok;
}
break;
- case -1: return -3; /* Timeout on RCPT */
- case -2: return -2; /* non-MAIL read i/o error */
- default: return -1; /* any MAIL error */
+ case RESP_RCPT_TIMEO: return sw_mrc_nonmail_read_timeo;
+ case RESP_RCPT_ERROR: return sw_mrc_bad_read;
+ default: return sw_mrc_bad_mail; /* any MAIL error */
#ifndef DISABLE_PIPE_CONNECT
- case -4: return -1; /* non-2xx for pipelined banner or EHLO */
- case -5: return -1; /* TLS first-read error */
+ case RESP_EPIPE_EHLO_ERR: return sw_mrc_bad_mail; /* non-2xx for pipelined banner or EHLO */
+ case RESP_EHLO_ERR_TLS: return sw_mrc_bad_mail; /* TLS first-read error */
#endif
}
}
} /* Loop for next address */
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
sx->next_addr = restart_addr ? restart_addr : addr;
#else
sx->next_addr = addr;
#endif
-return 0;
+return sw_mrc_ok;
}
uschar new_message_id[MESSAGE_ID_LENGTH + 1];
smtp_context * sx = store_get(sizeof(*sx), GET_TAINTED); /* tainted, for the data buffers */
BOOL pass_message = FALSE;
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
BOOL mail_limit = FALSE;
#endif
#ifdef SUPPORT_DANE
switch(smtp_write_mail_and_rcpt_cmds(sx, &yield))
{
- case 0: break;
- case -1: case -2: goto RESPONSE_FAILED;
- case -3: goto END_OFF;
- case -4: goto SEND_QUIT;
- default: goto SEND_FAILED;
+ case sw_mrc_ok: break;
+ case sw_mrc_bad_mail: goto RESPONSE_FAILED;
+ case sw_mrc_bad_read: goto RESPONSE_FAILED;
+ case sw_mrc_nonmail_read_timeo: goto END_OFF;
+ case sw_mrc_bad_internal: goto SEND_QUIT;
+ default: goto SEND_FAILED;
}
/* If we are an MUA wrapper, abort if any RCPTs were rejected, either
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))
{
- case 3: sx->ok = TRUE; /* 2xx & 5xx => OK & progress made */
- case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */
- break;
+ case RESP_HAD_2_AND_5: sx->ok = TRUE; /* OK & progress made */
+ case RESP_BIT_HAD_5XX: sx->completed_addr = TRUE; /* 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 0: break; /* No 2xx or 5xx, but no probs */
+ case RESP_BIT_HAD_2XX: sx->ok = TRUE; /* OK, but if LMTP, */
+ if (!sx->lmtp) /* can't tell about progress yet */
+ sx->completed_addr = TRUE;
+ case RESP_NOERROR: break; /* No 2xx or 5xx, but no probs */
- case -1: goto END_OFF; /* Timeout on RCPT */
+ case RESP_RCPT_TIMEO: goto END_OFF;
#ifndef DISABLE_PIPE_CONNECT
case -5: /* TLS first-read error */
/* Reap any outstanding MAIL & RCPT commands, but not a DATA-go-ahead */
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 */
- break;
+ case RESP_HAD_2_AND_5: sx->ok = TRUE; /* OK & progress made */
+ case RESP_BIT_HAD_5XX: sx->completed_addr = TRUE; /* 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 0: break; /* No 2xx or 5xx, but no probs */
+ case RESP_BIT_HAD_2XX: sx->ok = TRUE; /* OK, but if LMTP, */
+ if (!sx->lmtp) /* can't tell about progress yet */
+ sx->completed_addr = TRUE;
+ case RESP_NOERROR: break; /* No 2xx or 5xx, but no probs */
- case -1: goto END_OFF; /* Timeout on RCPT */
+ case RESP_RCPT_TIMEO: goto END_OFF; /* Timeout on RCPT */
#ifndef DISABLE_PIPE_CONNECT
- case -5: /* TLS first-read error */
- case -4: HDEBUG(D_transport)
+ case RESP_EHLO_ERR_TLS: /* TLS first-read error */
+ case RESP_EPIPE_EHLO_ERR: HDEBUG(D_transport)
debug_printf("failed reaping pipelined cmd responses\n");
#endif
- default: goto RESPONSE_FAILED; /* I/O error, or any MAIL/DATA error */
+ default: goto RESPONSE_FAILED; /* I/O error, or any MAIL/DATA error */
}
}
break;
case ERRNO_SMTPCLOSED:
- message_error = Ustrncmp(smtp_command,"end ",4) == 0;
+ /* If the peer closed the TCP connection after end-of-data, but before
+ we could send QUIT, do TLS close, etc - call it a message error.
+ Otherwise, if all the recipients have been dealt with, call a close no
+ error at all; each address_item should have a suitable result already
+ (2xx: PENDING_OK, 4xx: DEFER, 5xx: FAIL) */
+
+ if (!(message_error = Ustrncmp(smtp_command,"end ",4) == 0))
+ {
+ address_item * addr;
+ for (addr = sx->addrlist; addr; addr = addr->next)
+ if (addr->transport_return == PENDING_DEFER)
+ break;
+ if (!addr) /* all rcpts fates determined */
+ {
+ log_write(0, LOG_MAIN, "peer close after all rcpt responses;"
+ " converting i/o-error to no-error");
+ sx->ok = TRUE;
+ goto happy;
+ }
+ }
break;
#ifndef DISABLE_DKIM
break;
}
- /* Handle the cases that are treated as message errors. These are:
+ /* Handle the cases that are treated as message errors (as opposed to
+ host-errors). These are:
(a) negative response or timeout after MAIL
(b) negative response after DATA
can), so we do not pass such a connection on if the host matches
hosts_nopass_tls. */
+happy:
+
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 " : "");
if (sx->completed_addr && sx->ok && sx->send_quit)
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
if (mail_limit = continue_sequence >= sx->max_mail)
{
DEBUG(D_transport)
if (sx->first_addr) /* More addresses still to be sent */
{ /* for this message */
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
/* Any that we marked as skipped, reset to do now */
for (address_item * a = sx->first_addr; a; a = a->next)
if (a->transport_return == SKIP)
*/
if (sx->ok && transport_pass_socket(tblock->name, host->name,
host->address, new_message_id, socket_fd
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
, sx->peer_limit_mail, sx->peer_limit_rcpt, sx->peer_limit_rcptdom
#endif
))
}
#endif
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
if (mail_limit && sx->first_addr)
{
/* Reset the sequence count since we closed the connection. This is flagged
*/
static address_item *
-prepare_addresses(address_item *addrlist, host_item *host)
+prepare_addresses(address_item * addrlist, host_item * host)
{
-address_item *first_addr = NULL;
+address_item * first_addr = NULL;
for (address_item * addr = addrlist; addr; addr = addr->next)
if (addr->transport_return == DEFER)
{
if (!first_addr) first_addr = addr;
addr->transport_return = PENDING_DEFER;
addr->basic_errno = 0;
- addr->more_errno = (host->mx >= 0)? 'M' : 'A';
+ addr->more_errno = host->mx >= 0 ? 'M' : 'A';
addr->message = NULL;
#ifndef DISABLE_TLS
addr->cipher = NULL;
BOOL
smtp_transport_entry(
- transport_instance *tblock, /* data for this instantiation */
- address_item *addrlist) /* addresses we are working on */
+ transport_instance * tblock, /* data for this instantiation */
+ address_item * addrlist) /* addresses we are working on */
{
int defport;
-int hosts_defer = 0;
-int hosts_fail = 0;
-int hosts_looked_up = 0;
-int hosts_retry = 0;
-int hosts_serial = 0;
-int hosts_total = 0;
-int total_hosts_tried = 0;
+int hosts_defer = 0, hosts_fail = 0, hosts_looked_up = 0;
+int hosts_retry = 0, hosts_serial = 0, hosts_total = 0, total_hosts_tried = 0;
BOOL expired = TRUE;
-uschar *expanded_hosts = NULL;
-uschar *pistring;
-uschar *tid = string_sprintf("%s transport", tblock->name);
-smtp_transport_options_block *ob = SOB tblock->options_block;
-host_item *hostlist = addrlist->host_list;
-host_item *host = NULL;
+uschar * expanded_hosts = NULL, * pistring;
+uschar * tid = string_sprintf("%s transport", tblock->name);
+smtp_transport_options_block * ob = SOB tblock->options_block;
+host_item * hostlist = addrlist->host_list, * host = NULL;
DEBUG(D_transport)
{
queue_smtp or a 2-stage queue run. This gets unset for certain
kinds of error, typically those that are specific to the message. */
-update_waiting = TRUE;
+update_waiting = TRUE;
/* If a host list is not defined for the addresses - they must all have the
same one in order to be passed to a single transport - or if the transport has
&& total_hosts_tried < ob->hosts_max_try_hardlimit;
host = nexthost)
{
- int rc;
- int host_af;
- BOOL host_is_expired = FALSE;
- BOOL message_defer = FALSE;
- BOOL some_deferred = FALSE;
- address_item *first_addr = NULL;
- uschar *interface = NULL;
- uschar *retry_host_key = NULL;
- uschar *retry_message_key = NULL;
- uschar *serialize_key = NULL;
+ int rc, host_af;
+ BOOL host_is_expired = FALSE, message_defer = FALSE, some_deferred = FALSE;
+ address_item * first_addr = NULL;
+ uschar * interface = NULL;
+ uschar * retry_host_key = NULL, * retry_message_key = NULL;
+ uschar * serialize_key = NULL;
/* Deal slightly better with a possible Linux kernel bug that results
in intermittent TFO-conn fails deep into the TCP flow. Bug 2907 tracks.
host_af = Ustrchr(host->address, ':') ? AF_INET6 : AF_INET;
{
- uschar * s = ob->interface;
- if (s && *s)
+ uschar * s;
+ GET_OPTION("interface");
+ if ((s = ob->interface) && *s)
{
if (!smtp_get_interface(s, host_af, addrlist, &interface, tid))
return FALSE;
out the result of previous attempts, and finding the first address that
is still to be delivered. */
- first_addr = prepare_addresses(addrlist, host);
+ if (!(first_addr = prepare_addresses(addrlist, host)))
+ {
+ /* Obscure situation; at least one case (bug 3059, fixed) where
+ a previous host try returned DEFER, but having moved all
+ recipients away from DEFER (the waiting-to-be-done state). */
+ DEBUG(D_transport) debug_printf("no pending recipients\n");
+ goto END_TRANSPORT;
+ }
DEBUG(D_transport) debug_printf("delivering %s to %s [%s] (%s%s)\n",
message_id, host->name, host->address, addrlist->address,
{
host_item * thost;
/* Make a copy of the host if it is local to this invocation
- of the transport. */
+ of the transport. */
if (expanded_hosts)
{
if (rc == OK)
for (address_item * addr = addrlist; addr; addr = addr->next)
if (addr->transport_return == DEFER)
- {
- some_deferred = TRUE;
- break;
- }
+ { some_deferred = TRUE; break; }
/* If no addresses deferred or the result was ERROR, return. We do this for
ERROR because a failing filter set-up or add_headers expansion is likely to
fail for any host we try. */
if (rc == ERROR || (rc == OK && !some_deferred))
- {
- DEBUG(D_transport) debug_printf("Leaving %s transport\n", tblock->name);
- return TRUE; /* Each address has its status */
- }
+ goto END_TRANSPORT;
/* If the result was DEFER or some individual addresses deferred, let
the loop run to try other hosts with the deferred addresses, except for the
if ((rc == DEFER || some_deferred) && nexthost)
{
BOOL timedout;
- retry_config *retry = retry_find_config(host->name, NULL, 0, 0);
+ retry_config * retry = retry_find_config(host->name, NULL, 0, 0);
if (retry && retry->rules)
{