* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) The Exim Maintainers 2020 - 2022 */
+/* Copyright (c) The Exim Maintainers 2020 - 2023 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "../exim.h"
#include "smtp.h"
{ "interface", opt_stringptr, LOFF(interface) },
{ "keepalive", opt_bool, LOFF(keepalive) },
{ "lmtp_ignore_quota", opt_bool, LOFF(lmtp_ignore_quota) },
- { "max_rcpt", opt_int | opt_public,
+ { "max_rcpt", opt_stringptr | opt_public,
OPT_OFF(transport_instance, max_addresses) },
{ "message_linelength_limit", opt_int, LOFF(message_linelength_limit) },
{ "multi_domain", opt_expand_bool | opt_public,
.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",
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 */
else
message = string_fmt_append(message, " %s", exim_errstr(basic_errno));
-log_write(0, LOG_MAIN, "%s", string_from_gstring(message));
-deliver_msglog("%s %s\n", tod_stamp(tod_log), message->s);
+log_write(0, LOG_MAIN, "%Y", message);
+deliver_msglog("%s %.*s\n", tod_stamp(tod_log), message->ptr, message->s);
}
static void
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;
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;
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;
&& (dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE)))
{
uschar * ehlo_resp_key = ehlo_cache_key(sx);
+ HDEBUG(D_transport)
+ {
+ dbdata_ehlo_resp * er;
+
+ if (!(er = dbfn_read_enforce_length(dbm_file, ehlo_resp_key, sizeof(dbdata_ehlo_resp))))
+ debug_printf("no ehlo-resp record!\n");
+ else
+ debug_printf("ehlo-resp record is %d seconds old\n", time(NULL) - er->time_stamp);
+ }
+
dbfn_delete(dbm_file, ehlo_resp_key);
dbfn_close(dbm_file);
}
/* Return an auths bitmap for the set of AUTH methods offered by the server
-which match our authenticators. */
+which match our client-side authenticators. */
static unsigned short
study_ehlo_auths(smtp_context * sx)
}
DEBUG(D_transport)
- debug_printf("server offers %s AUTH, methods '%s', bitmap 0x%04x\n",
+ debug_printf("server offers %s AUTH, methods '%s', usable-bitmap 0x%04x\n",
tls_out.active.sock >= 0 ? "crypted" : "plaintext", names, authbits);
if (tls_out.active.sock >= 0)
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);
+
+ s = ((smtp_transport_options_block *)sx->conn_args.ob)->host_name_extract;
+ if (!s) s = HNE_DEFAULT;
+ ehlo_response_lbserver(sx, s);
}
if (pending_EHLO)
write_ehlo_cache_entry(sx);
}
else
+ {
invalidate_ehlo_cache_entry(sx);
+ sx->early_pipe_active = FALSE; /* cancel further early-pipe on this conn */
+ }
return OK; /* just carry on */
}
sx->buffer, sizeof(sx->buffer));
sx->outblock.authenticating = FALSE;
driver_srcfile = authenticator_name = NULL; driver_srcline = 0;
-DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n", au->name, rc);
+DEBUG(D_transport) debug_printf("%s authenticator yielded %s\n", au->name, rc_names[rc]);
/* A temporary authentication failure must hold up delivery to
this host. After a permanent authentication failure, we carry on
/* Failure after reading a response */
case FAIL:
+ {
+ uschar * logmsg = NULL;
+
if (errno != 0 || sx->buffer[0] != '5') return FAIL;
- log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s",
- au->name, host->name, host->address, sx->buffer);
+#ifndef DISABLE_EVENT
+ {
+ uschar * save_name = sender_host_authenticated;
+ sender_host_authenticated = au->name;
+ if ((logmsg = event_raise(sx->conn_args.tblock->event_action, US"auth:fail",
+ sx->buffer, NULL)))
+ log_write(0, LOG_MAIN, "%s", logmsg);
+ sender_host_authenticated = save_name;
+ }
+#endif
+ if (!logmsg)
+ log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s",
+ au->name, host->name, host->address, sx->buffer);
break;
+ }
/* Failure by some other means. In effect, the authenticator
decided it wasn't prepared to handle this case. Typically this
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;
verify_check_given_host(CUSS &ob->hosts_require_dane, sx->conn_args.host) == OK;
#endif
-if ((sx->max_mail = sx->conn_args.tblock->connection_max_messages) == 0) sx->max_mail = 999999;
-if ((sx->max_rcpt = sx->conn_args.tblock->max_addresses) == 0) sx->max_rcpt = 999999;
+if ((sx->max_mail = sx->conn_args.tblock->connection_max_messages) == 0)
+ sx->max_mail = UNLIMITED_ADDRS;
+sx->max_rcpt = expand_max_rcpt(sx->conn_args.tblock->max_addresses);
sx->igquotstr = US"";
if (!sx->helo_data) sx->helo_data = ob->helo_data;
#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";
+
+ /* 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. */
+
+ if (!(s = ob->host_name_extract)) s = US"never-LB";
+ ehlo_response_lbserver(sx, s);
+
goto TLS_NEGOTIATE;
}
#endif
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
- ehlo_response_lbserver(sx, ob);
+ if (!(s = ob->host_name_extract)) s = HNE_DEFAULT;
+ ehlo_response_lbserver(sx, s);
}
/* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
#ifdef EXPERIMMENTAL_ESMTP_LIMITS
/* As we are about to send another EHLO, forget any LIMITS received so far. */
sx->peer_limit_mail = sx->peer_limit_rcpt = sx->peer_limit_rcptdom = 0;
- if ((sx->max_mail = sx->conn_args.tblock->connection_max_message) == 0) sx->max_mail = 999999;
- if ((sx->max_rcpt = sx->conn_args.tblock->max_addresses) == 0) sx->max_rcpt = 999999;
+ if ((sx->max_mail = sx->conn_args.tblock->connection_max_message) == 0)
+ sx->max_mail = UNLIMITED_ADDRS;
+ sx->max_rcpt = expand_max_rcpt(sx->conn_args.tblock->max_addresses);
sx->single_rcpt_domain = FALSE;
#endif
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;
{
int cmds_sent;
BOOL no_flush;
- uschar * rcpt_addr;
+ const uschar * rcpt_addr;
#ifdef EXPERIMENTAL_ESMTP_LIMITS
if ( sx->single_rcpt_domain /* restriction on domains */
yield ERROR. */
if (!transport_set_up_command(&transport_filter_argv,
- tblock->filter_command, TRUE, DEFER, addrlist, FALSE,
+ tblock->filter_command, TSUC_EXPAND_ARGS, DEFER, addrlist,
string_sprintf("%.50s transport filter", tblock->name), NULL))
{
set_errno_nohost(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER,
open, we must shut down TLS. Not all MTAs allow for the continuation
of the SMTP session when TLS is shut down. We test for this by sending
a new EHLO. If we don't get a good response, we don't attempt to pass
- the socket on. */
+ the socket on.
+ NB: TLS close is *required* per RFC 9266 when tls-exporter info has
+ been used, which we do under TLSv1.3 for the gsasl SCRAM*PLUS methods.
+ But we were always doing it anyway. */
tls_close(sx->cctx.tls_ctx,
sx->send_tlsclose ? TLS_SHUTDOWN_WAIT : TLS_SHUTDOWN_WONLY);
{
smtp_transport_options_block * ob = SOB tblock->options_block;
client_conn_ctx cctx;
-smtp_context sx;
+smtp_context sx = {0};
uschar buffer[256];
uschar inbuffer[4096];
uschar outbuffer[16];
uschar *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.
+ Hack: Clear TFO option for any further hosts on this tpt run. */
+
+ if (total_hosts_tried > 0)
+ {
+ DEBUG(D_transport|D_acl|D_v)
+ debug_printf("Clearing TFO as not first host for message\n");
+ ob->hosts_try_fastopen = US"";
+ }
+
/* Default next host is next host. :-) But this can vary if the
hosts_max_try limit is hit (see below). It may also be reset if a host
address is looked up here (in case the host was multihomed). */