#include "../exim.h"
#include "smtp.h"
+#if defined(SUPPORT_DANE) && defined(DISABLE_TLS)
+# error TLS is required for DANE
+#endif
+
/* Options specific to the smtp transport. This transport also supports LMTP
over TCP/IP. The options must be in alphabetic order (note that "_" comes
(void *)offsetof(smtp_transport_options_block, hosts_avoid_esmtp) },
{ "hosts_avoid_pipelining", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_avoid_pipelining) },
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
{ "hosts_avoid_tls", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_avoid_tls) },
#endif
(void *)offsetof(smtp_transport_options_block, hosts_max_try) },
{ "hosts_max_try_hardlimit", opt_int,
(void *)offsetof(smtp_transport_options_block, hosts_max_try_hardlimit) },
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
{ "hosts_nopass_tls", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_nopass_tls) },
{ "hosts_noproxy_tls", opt_stringptr,
#endif
{ "hosts_override", opt_bool,
(void *)offsetof(smtp_transport_options_block, hosts_override) },
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
{ "hosts_pipe_connect", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_pipe_connect) },
#endif
{ "hosts_randomize", opt_bool,
(void *)offsetof(smtp_transport_options_block, hosts_randomize) },
-#if defined(SUPPORT_TLS) && !defined(DISABLE_OCSP)
+#if !defined(DISABLE_TLS) && !defined(DISABLE_OCSP)
{ "hosts_request_ocsp", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_request_ocsp) },
#endif
{ "hosts_require_auth", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_require_auth) },
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
# ifdef SUPPORT_DANE
{ "hosts_require_dane", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_require_dane) },
(void *)offsetof(smtp_transport_options_block, hosts_try_auth) },
{ "hosts_try_chunking", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_try_chunking) },
-#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
+#ifdef SUPPORT_DANE
{ "hosts_try_dane", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_try_dane) },
#endif
{ "hosts_try_prdr", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_try_prdr) },
#endif
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
{ "hosts_verify_avoid_tls", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_verify_avoid_tls) },
#endif
{ "socks_proxy", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, socks_proxy) },
#endif
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
{ "tls_certificate", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_certificate) },
{ "tls_crl", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_privatekey) },
{ "tls_require_ciphers", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_require_ciphers) },
+# ifdef EXPERIMENTAL_TLS_RESUME
+ { "tls_resumption_hosts", opt_stringptr,
+ (void *)offsetof(smtp_transport_options_block, tls_resumption_hosts) },
+# endif
{ "tls_sni", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_sni) },
{ "tls_tempfail_tryclear", opt_bool,
.hosts_require_auth = NULL,
.hosts_try_chunking = US"*",
#ifdef SUPPORT_DANE
- .hosts_try_dane = NULL,
+ .hosts_try_dane = US"*",
.hosts_require_dane = NULL,
.dane_require_tls_ciphers = NULL,
#endif
- .hosts_try_fastopen = NULL,
+ .hosts_try_fastopen = US"*",
#ifndef DISABLE_PRDR
.hosts_try_prdr = US"*",
#endif
.hosts_avoid_tls = NULL,
.hosts_verify_avoid_tls = NULL,
.hosts_avoid_pipelining = NULL,
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
.hosts_pipe_connect = NULL,
#endif
.hosts_avoid_esmtp = NULL,
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
.hosts_nopass_tls = NULL,
- .hosts_noproxy_tls = US"*",
+ .hosts_noproxy_tls = NULL,
#endif
.command_timeout = 5*60,
.connect_timeout = 5*60,
#ifdef SUPPORT_SOCKS
.socks_proxy = NULL,
#endif
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
.tls_certificate = NULL,
.tls_crl = NULL,
.tls_privatekey = NULL,
.tls_verify_certificates = US"system",
.tls_dh_min_bits = EXIM_CLIENT_DH_DEFAULT_MIN_BITS,
.tls_tempfail_tryclear = TRUE,
+# ifdef EXPERIMENTAL_TLS_RESUME
+ .tls_resumption_hosts = NULL,
+# endif
.tls_verify_hosts = NULL,
.tls_try_verify_hosts = US"*",
.tls_verify_cert_hostnames = US"*",
static unsigned ehlo_response(uschar * buf, unsigned checks);
+/******************************************************************************/
+
+void
+smtp_deliver_init(void)
+{
+if (!regex_PIPELINING) regex_PIPELINING =
+ regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE);
+
+if (!regex_SIZE) regex_SIZE =
+ regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE);
+
+if (!regex_AUTH) regex_AUTH =
+ regex_must_compile(AUTHS_REGEX, FALSE, TRUE);
+
+#ifndef DISABLE_TLS
+if (!regex_STARTTLS) regex_STARTTLS =
+ regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
+#endif
+
+if (!regex_CHUNKING) regex_CHUNKING =
+ regex_must_compile(US"\\n250[\\s\\-]CHUNKING(\\s|\\n|$)", FALSE, TRUE);
+
+#ifndef DISABLE_PRDR
+if (!regex_PRDR) regex_PRDR =
+ regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE);
+#endif
+
+#ifdef SUPPORT_I18N
+if (!regex_UTF8) regex_UTF8 =
+ regex_must_compile(US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE);
+#endif
+
+if (!regex_DSN) regex_DSN =
+ regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE);
+
+if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA =
+ regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE);
+
+#ifndef DISABLE_PIPE_CONNECT
+if (!regex_EARLY_PIPE) regex_EARLY_PIPE =
+ regex_must_compile(US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)", FALSE, TRUE);
+#endif
+}
+
+
/*************************************************
* Setup entry point *
*************************************************/
pl, smtp_command, s);
return FALSE;
+ case ERRNO_TLSFAILURE: /* Handle bad first read; can happen with
+ GnuTLS and TLS1.3 */
+ *message = US"bad first read from TLS conn";
+ return TRUE;
+
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",
return FALSE;
case ERRNO_WRITEINCOMPLETE: /* failure to write a complete data block */
- *message = string_sprintf("failed to write a data block");
+ *message = US"failed to write a data block";
return FALSE;
#ifdef SUPPORT_I18N
#ifdef EXPERIMENTAL_DSN_INFO
sx->helo_response = string_copy(sx->buffer);
#endif
+#ifndef DISABLE_EVENT
+(void) event_raise(sx->conn_args.tblock->event_action,
+ US"smtp:ehlo", sx->buffer);
+#endif
return TRUE;
}
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
static uschar *
ehlo_cache_key(const smtp_context * sx)
{
{
open_db dbblock, * dbm_file;
-if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE)))
+if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE)))
{
uschar * ehlo_resp_key = ehlo_cache_key(sx);
dbdata_ehlo_resp er = { .data = sx->ehlo_resp };
+ HDEBUG(D_transport) debug_printf("writing clr %04x/%04x cry %04x/%04x\n",
+ sx->ehlo_resp.cleartext_features, sx->ehlo_resp.cleartext_auths,
+ sx->ehlo_resp.crypted_features, sx->ehlo_resp.crypted_auths);
+
dbfn_write(dbm_file, ehlo_resp_key, &er, (int)sizeof(er));
dbfn_close(dbm_file);
}
open_db dbblock, * dbm_file;
if ( sx->early_pipe_active
- && (dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE)))
+ && (dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE)))
{
uschar * ehlo_resp_key = ehlo_cache_key(sx);
dbfn_delete(dbm_file, ehlo_resp_key);
open_db dbblock;
open_db * dbm_file;
-if (!(dbm_file = dbfn_open(US"misc", O_RDONLY, &dbblock, FALSE)))
+if (!(dbm_file = dbfn_open(US"misc", O_RDONLY, &dbblock, FALSE, TRUE)))
{ DEBUG(D_transport) debug_printf("ehlo-cache: no misc DB\n"); }
else
{
{
DEBUG(D_transport) debug_printf("ehlo-resp record too old\n");
dbfn_close(dbm_file);
- if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE)))
+ if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE)))
dbfn_delete(dbm_file, ehlo_resp_key);
}
else
Return:
OK all well
+ DEFER error on first read of TLS'd conn
FAIL SMTP error in response
*/
int
{
BOOL pending_BANNER = sx->pending_BANNER;
BOOL pending_EHLO = sx->pending_EHLO;
+int rc = FAIL;
sx->pending_BANNER = FALSE; /* clear early to avoid recursion */
sx->pending_EHLO = FALSE;
if (!smtp_reap_banner(sx))
{
DEBUG(D_transport) debug_printf("bad banner\n");
+ if (tls_out.active.sock >= 0) rc = DEFER;
goto fail;
}
}
if (!smtp_reap_ehlo(sx))
{
DEBUG(D_transport) debug_printf("bad response for EHLO\n");
+ if (tls_out.active.sock >= 0) rc = DEFER;
goto fail;
}
? &sx->ehlo_resp.cleartext_auths : &sx->ehlo_resp.crypted_auths;
peer_offered = ehlo_response(sx->buffer,
- (tls_out.active.sock < 0 ? OPTION_TLS : OPTION_REQUIRETLS)
+ (tls_out.active.sock < 0 ? OPTION_TLS : 0)
| OPTION_CHUNKING | OPTION_PRDR | OPTION_DSN | OPTION_PIPE | OPTION_SIZE
| OPTION_UTF8 | OPTION_EARLY_PIPE
);
|| (authbits = study_ehlo_auths(sx)) != *ap)
{
HDEBUG(D_transport)
- debug_printf("EHLO extensions changed, 0x%04x/0x%04x -> 0x%04x/0x%04x\n",
+ debug_printf("EHLO %s extensions changed, 0x%04x/0x%04x -> 0x%04x/0x%04x\n",
+ tls_out.active.sock < 0 ? "cleartext" : "crypted",
sx->peer_offered, *ap, peer_offered, authbits);
+ *(tls_out.active.sock < 0
+ ? &sx->ehlo_resp.cleartext_features : &sx->ehlo_resp.crypted_features) = peer_offered;
*ap = authbits;
if (peer_offered & OPTION_EARLY_PIPE)
write_ehlo_cache_entry(sx);
fail:
invalidate_ehlo_cache_entry(sx);
(void) smtp_discard_responses(sx, sx->conn_args.ob, *countp);
- return FAIL;
+ return rc;
}
#endif
-2 I/O or other non-response error for RCPT
-3 DATA or MAIL failed - errno and buffer set
-4 banner or EHLO failed (early-pipelining)
+ -5 banner or EHLO failed (early-pipelining, TLS)
*/
static int
smtp_transport_options_block * ob = sx->conn_args.ob;
int yield = 0;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
-if (smtp_reap_early_pipe(sx, &count) != OK)
- return -4;
+#ifndef DISABLE_PIPE_CONNECT
+int rc;
+if ((rc = smtp_reap_early_pipe(sx, &count)) != OK)
+ return rc == FAIL ? -4 : -5;
#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;
if (errno == 0 && sx->buffer[0] != 0)
{
int save_errno = 0;
}
}
+ /* Error on first TLS read */
+
+ else if (errno == ERRNO_TLSFAILURE)
+ return -5;
+
/* Timeout while reading the response */
else if (errno == ETIMEDOUT)
else if (errno != 0 || sx->buffer[0] == 0)
{
- string_format(big_buffer, big_buffer_size, "RCPT TO:<%s>",
+ gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer }, * g = &gs;
+
+ /* Use taint-unchecked routines for writing into big_buffer, trusting
+ that we'll never expand it. */
+
+ g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "RCPT TO:<%s>",
transport_rcpt_address(addr, sx->conn_args.tblock->rcpt_include_affixes));
+ string_from_gstring(g);
return -2;
}
int code;
uschar *msg;
BOOL pass_message;
+
+ if (errno == ERRNO_TLSFAILURE) /* Error on first TLS read */
+ return -5;
+
if (pending_DATA > 0 || (yield & 1) != 0)
{
if (errno == 0 && sx->buffer[0] == '4')
host_item * host = sx->conn_args.host; /* host to deliver to */
smtp_transport_options_block * ob = sx->conn_args.ob; /* transport options */
int require_auth = verify_check_given_host(CUSS &ob->hosts_require_auth, host);
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
unsigned short authbits = tls_out.active.sock >= 0
? sx->ehlo_resp.crypted_auths : sx->ehlo_resp.cleartext_auths;
#endif
if ( sx->esmtp
&&
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
sx->early_pipe_active ? authbits
:
#endif
uschar * names = NULL;
expand_nmax = -1; /* reset */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (!sx->early_pipe_active)
#endif
names = string_copyn(expand_nstring[1], expand_nlength[1]);
if ( require_auth == OK
|| verify_check_given_host(CUSS &ob->hosts_try_auth, host) == OK)
{
- auth_instance * au;
-
DEBUG(D_transport) debug_printf("scanning authentication mechanisms\n");
fail_reason = US"no common mechanisms were found";
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (sx->early_pipe_active)
{
/* Scan our authenticators (which support use by a client and were offered
client function. We are limited to supporting up to 16 authenticator
public-names by the number of bits in a short. */
+ auth_instance * au;
uschar bitnum;
int rc;
Return True on error, otherwise buffer has (possibly empty) terminated string
*/
-BOOL
+static BOOL
smtp_mail_auth_str(uschar *buffer, unsigned bufsize, address_item *addrlist,
smtp_transport_options_block *ob)
{
-uschar *local_authenticated_sender = authenticated_sender;
+uschar * local_authenticated_sender = authenticated_sender;
#ifdef notdef
debug_printf("smtp_mail_auth_str: as<%s> os<%s> SA<%s>\n", authenticated_sender, ob->authenticated_sender, f.smtp_authenticated?"Y":"N");
#endif
-if (ob->authenticated_sender != NULL)
+if (ob->authenticated_sender)
{
uschar *new = expand_string(ob->authenticated_sender);
- if (new == NULL)
+ if (!new)
{
if (!f.expand_string_forcedfail)
{
return TRUE;
}
}
- else if (new[0] != 0) local_authenticated_sender = new;
+ else if (*new) local_authenticated_sender = new;
}
/* Add the authenticated sender address if present */
-if ((f.smtp_authenticated || ob->authenticated_sender_force) &&
- local_authenticated_sender != NULL)
+if ( (f.smtp_authenticated || ob->authenticated_sender_force)
+ && local_authenticated_sender)
{
- string_format(buffer, bufsize, " AUTH=%s",
+ string_format_nt(buffer, bufsize, " AUTH=%s",
auth_xtextencode(local_authenticated_sender,
- Ustrlen(local_authenticated_sender)));
+ Ustrlen(local_authenticated_sender)));
client_authenticated_sender = string_copy(local_authenticated_sender);
}
else
address_item * addr1;
uschar * if1 = US"";
uschar * helo1 = US"";
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
uschar * tlsc1 = US"";
#endif
uschar * save_sender_address = sender_address;
if (ob->helo_data)
helo1 = expand_string(ob->helo_data);
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (ob->tls_certificate)
tlsc1 = expand_string(ob->tls_certificate);
local_identity = string_sprintf ("%s^%s^%s", if1, helo1, tlsc1);
/* debug_printf("%s: check for 0x%04x\n", __FUNCTION__, checks); */
-#ifdef SUPPORT_TLS
-# ifdef EXPERIMENTAL_REQUIRETLS
-if ( checks & OPTION_REQUIRETLS
- && pcre_exec(regex_REQUIRETLS, NULL, CS buf,bsize, 0, PCRE_EOPT, NULL,0) < 0)
-# endif
- checks &= ~OPTION_REQUIRETLS;
-
+#ifndef DISABLE_TLS
if ( checks & OPTION_TLS
&& pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
#endif
&& pcre_exec(regex_SIZE, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
checks &= ~OPTION_SIZE;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if ( checks & OPTION_EARLY_PIPE
&& pcre_exec(regex_EARLY_PIPE, NULL, CS buf, bsize, 0,
PCRE_EOPT, NULL, 0) < 0)
tc_chunk_last add LAST option to SMTP BDAT command
tc_reap_prev reap response to previous SMTP commands
-Returns: OK or ERROR
+Returns:
+ OK or ERROR
+ DEFER TLS error on first read (EHLO-resp); errno set
*/
static int
if (chunk_size > 0)
{
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
BOOL new_conn = !!(sx->outblock.conn_args);
#endif
if((cmd_count = smtp_write_command(sx,
) < 0) return ERROR;
if (flags & tc_chunk_last)
data_command = string_copy(big_buffer); /* Save for later error message */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
/* That command write could have been the one that made the connection.
Copy the fd from the client conn ctx (smtp transport specific) to the
generic transport ctx. */
case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */
case 0: break; /* No 2xx or 5xx, but no probs */
- case -1: /* Timeout on RCPT */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+ case -5: errno = ERRNO_TLSFAILURE;
+ return DEFER;
+#ifndef DISABLE_PIPE_CONNECT
case -4: /* non-2xx for pipelined banner or EHLO */
#endif
+ case -1: /* Timeout on RCPT */
default: return ERROR; /* I/O error, or any MAIL/DATA error */
}
cmd_count = 1;
int
smtp_setup_conn(smtp_context * sx, BOOL suppress_tls)
{
-#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
-dns_answer tlsa_dnsa;
-#endif
smtp_transport_options_block * ob = sx->conn_args.tblock->options_block;
BOOL pass_message = FALSE;
uschar * message = NULL;
int yield = OK;
-int rc;
+#ifndef DISABLE_TLS
+uschar * tls_errstr;
+#endif
sx->conn_args.ob = ob;
sx->utf8_needed = FALSE;
#endif
sx->dsn_all_lasthop = TRUE;
-#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
-sx->dane = FALSE;
+#ifdef SUPPORT_DANE
+sx->conn_args.dane = FALSE;
sx->dane_required =
verify_check_given_host(CUSS &ob->hosts_require_dane, sx->conn_args.host) == OK;
#endif
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
sx->early_pipe_active = sx->early_pipe_ok = FALSE;
sx->ehlo_resp.cleartext_features = sx->ehlo_resp.crypted_features = 0;
sx->pending_BANNER = sx->pending_EHLO = FALSE;
tls_out.ourcert = NULL;
tls_out.peercert = NULL;
tls_out.peerdn = NULL;
-#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
+#ifdef USE_OPENSSL
tls_out.sni = NULL;
#endif
tls_out.ocsp = OCSP_NOT_REQ;
+#ifdef EXPERIMENTAL_TLS_RESUME
+tls_out.resumption = 0;
+#endif
+tls_out.ver = NULL;
/* Flip the legacy TLS-related variables over to the outbound set in case
they're used in the context of the transport. Don't bother resetting
tls_modify_variables(&tls_out);
-#ifndef SUPPORT_TLS
+#ifdef DISABLE_TLS
if (sx->smtps)
{
set_errno_nohost(sx->addrlist, ERRNO_TLSFAILURE, US"TLS support not available",
smtp_port_for_connect(sx->conn_args.host, sx->port);
-#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
+#ifdef SUPPORT_DANE
/* Do TLSA lookup for DANE */
{
tls_out.dane_verified = FALSE;
if (sx->conn_args.host->dnssec == DS_YES)
{
+ int rc;
if( sx->dane_required
|| verify_check_given_host(CUSS &ob->hosts_try_dane, sx->conn_args.host) == OK
)
- switch (rc = tlsa_lookup(sx->conn_args.host, &tlsa_dnsa, sx->dane_required))
+ switch (rc = tlsa_lookup(sx->conn_args.host, &sx->conn_args.tlsa_dnsa, sx->dane_required))
{
- case OK: sx->dane = TRUE;
+ case OK: sx->conn_args.dane = TRUE;
ob->tls_tempfail_tryclear = FALSE;
break;
case FAIL_FORCED: break;
default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
string_sprintf("DANE error: tlsa lookup %s",
- rc == DEFER ? "DEFER" : "FAIL"),
+ rc_to_string(rc)),
rc, FALSE);
# ifndef DISABLE_EVENT
(void) event_raise(sx->conn_args.tblock->event_action,
sx->inblock.cctx = sx->outblock.cctx = &sx->cctx;
sx->avoid_option = sx->peer_offered = smtp_peer_options = 0;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
- if (verify_check_given_host(CUSS &ob->hosts_pipe_connect, sx->conn_args.host) == OK)
- {
- sx->early_pipe_ok = TRUE;
- if ( read_ehlo_cache_entry(sx)
- && sx->ehlo_resp.cleartext_features & OPTION_EARLY_PIPE)
+#ifndef DISABLE_PIPE_CONNECT
+ if ( verify_check_given_host(CUSS &ob->hosts_pipe_connect,
+ sx->conn_args.host) == OK)
+
+ /* We don't find out the local ip address until the connect, so if
+ the helo string might use it avoid doing early-pipelining. */
+
+ if ( !sx->helo_data
+ || !Ustrstr(sx->helo_data, "$sending_ip_address")
+ || Ustrstr(sx->helo_data, "def:sending_ip_address")
+ )
{
- DEBUG(D_transport) debug_printf("Using cached cleartext PIPE_CONNECT\n");
- sx->early_pipe_active = TRUE;
- sx->peer_offered = sx->ehlo_resp.cleartext_features;
+ sx->early_pipe_ok = TRUE;
+ if ( read_ehlo_cache_entry(sx)
+ && sx->ehlo_resp.cleartext_features & OPTION_EARLY_PIPE)
+ {
+ DEBUG(D_transport)
+ debug_printf("Using cached cleartext PIPE_CONNECT\n");
+ sx->early_pipe_active = TRUE;
+ sx->peer_offered = sx->ehlo_resp.cleartext_features;
+ }
}
- }
+ else DEBUG(D_transport)
+ debug_printf("helo needs $sending_ip_address\n");
if (sx->early_pipe_active)
sx->outblock.conn_args = &sx->conn_args;
{
if ((sx->cctx.sock = smtp_connect(&sx->conn_args, NULL)) < 0)
{
- uschar * msg = NULL;
- if (sx->verify)
- {
- msg = US strerror(errno);
- HDEBUG(D_verify) debug_printf("connect: %s\n", msg);
- }
set_errno_nohost(sx->addrlist,
errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
- sx->verify ? string_sprintf("could not connect: %s", msg)
- : NULL,
+ sx->verify ? US strerror(errno) : NULL,
DEFER, FALSE);
sx->send_quit = FALSE;
return DEFER;
if (!sx->smtps)
{
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (sx->early_pipe_active)
{
sx->pending_BANNER = TRUE; /* sync_responses() must eventually handle */
/* Alas; be careful, since this goto is not an error-out, so conceivably
we might set data between here and the target which we assume to exist
and be usable. I can see this coming back to bite us. */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (sx->smtps)
{
smtp_peer_options |= OPTION_TLS;
if (sx->esmtp)
{
if (smtp_write_command(sx,
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
sx->early_pipe_active ? SCMD_BUFFER :
#endif
SCMD_FLUSH,
goto SEND_FAILED;
sx->esmtp_sent = TRUE;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (sx->early_pipe_active)
{
sx->pending_EHLO = TRUE;
DEBUG(D_transport)
debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (!sx->early_pipe_active)
#endif
if (!sx->esmtp)
if (sx->esmtp || sx->lmtp)
{
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (!sx->early_pipe_active)
#endif
{
sx->peer_offered = ehlo_response(sx->buffer,
OPTION_TLS /* others checked later */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
| (sx->early_pipe_ok
? OPTION_IGNQ
| OPTION_CHUNKING | OPTION_PRDR | OPTION_DSN | OPTION_PIPE | OPTION_SIZE
)
#endif
);
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (sx->early_pipe_ok)
{
sx->ehlo_resp.cleartext_features = sx->peer_offered;
/* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
smtp_peer_options |= sx->peer_offered & OPTION_TLS;
#endif
}
the client not be required to use TLS. If the response is bad, copy the buffer
for error analysis. */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if ( smtp_peer_options & OPTION_TLS
&& !suppress_tls
&& verify_check_given_host(CUSS &ob->hosts_avoid_tls, sx->conn_args.host) != OK
if (smtp_write_command(sx, SCMD_FLUSH, "STARTTLS\r\n") < 0)
goto SEND_FAILED;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
/* If doing early-pipelining reap the banner and EHLO-response but leave
the response for the STARTTLS we just sent alone. */
else
TLS_NEGOTIATE:
{
- address_item * addr;
- uschar * errstr;
- sx->cctx.tls_ctx = tls_client_start(sx->cctx.sock, sx->conn_args.host,
- sx->addrlist, sx->conn_args.tblock,
-# ifdef SUPPORT_DANE
- sx->dane ? &tlsa_dnsa : NULL,
-# endif
- &tls_out, &errstr);
-
- if (!sx->cctx.tls_ctx)
+ if (!tls_client_start(&sx->cctx, &sx->conn_args, sx->addrlist, &tls_out, &tls_errstr))
{
/* TLS negotiation failed; give an error. From outside, this function may
be called again to try in clear on a new connection, if the options permit
it for this host. */
- DEBUG(D_tls) debug_printf("TLS session fail: %s\n", errstr);
+#ifdef USE_GNUTLS
+ GNUTLS_CONN_FAILED:
+#endif
+ DEBUG(D_tls) debug_printf("TLS session fail: %s\n", tls_errstr);
# ifdef SUPPORT_DANE
- if (sx->dane)
+ if (sx->conn_args.dane)
{
log_write(0, LOG_MAIN,
"DANE attempt failed; TLS connection to %s [%s]: %s",
- sx->conn_args.host->name, sx->conn_args.host->address, errstr);
+ sx->conn_args.host->name, sx->conn_args.host->address, tls_errstr);
# ifndef DISABLE_EVENT
(void) event_raise(sx->conn_args.tblock->event_action,
US"dane:fail", US"validation-failure"); /* could do with better detail */
# endif
errno = ERRNO_TLSFAILURE;
- message = string_sprintf("TLS session: %s", errstr);
+ message = string_sprintf("TLS session: %s", tls_errstr);
sx->send_quit = FALSE;
goto TLS_FAILED;
}
addr->peercert = tls_out.peercert;
addr->peerdn = tls_out.peerdn;
addr->ocsp = tls_out.ocsp;
+ addr->tlsver = tls_out.ver;
}
}
}
goto SEND_QUIT;
}
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
/* For SMTPS there is no cleartext early-pipe; use the crypted permission bit.
We're unlikely to get the group sent and delivered before the server sends its
banner, but it's still worth sending as a group.
/* For SMTPS we need to wait for the initial OK response. */
if (sx->smtps)
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (sx->early_pipe_active)
{
sx->pending_BANNER = TRUE;
}
if (smtp_write_command(sx,
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
sx->early_pipe_active ? SCMD_BUFFER :
#endif
SCMD_FLUSH,
"%s %s\r\n", greeting_cmd, sx->helo_data) < 0)
goto SEND_FAILED;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (sx->early_pipe_active)
sx->pending_EHLO = TRUE;
else
#endif
{
if (!smtp_reap_ehlo(sx))
+#ifdef USE_GNUTLS
+ {
+ /* The GnuTLS layer in Exim only spots a server-rejection of a client
+ cert late, under TLS1.3 - which means here; the first time we try to
+ receive crypted data. Treat it as if it was a connect-time failure.
+ See also the early-pipe equivalent... which will be hard; every call
+ to sync_responses will need to check the result.
+ It would be nicer to have GnuTLS check the cert during the handshake.
+ Can it do that, with all the flexibility we need? */
+
+ tls_errstr = US"error on first read";
+ goto GNUTLS_CONN_FAILED;
+ }
+#else
goto RESPONSE_FAILED;
+#endif
smtp_peer_options = 0;
}
}
else if ( sx->smtps
# ifdef SUPPORT_DANE
- || sx->dane
-# endif
-# ifdef EXPERIMENTAL_REQUIRETLS
- || tls_requiretls & REQUIRETLS_MSG
+ || sx->conn_args.dane
# endif
|| verify_check_given_host(CUSS &ob->hosts_require_tls, sx->conn_args.host) == OK
)
{
- errno =
-# ifdef EXPERIMENTAL_REQUIRETLS
- tls_requiretls & REQUIRETLS_MSG ? ERRNO_REQUIRETLS :
-# endif
- ERRNO_TLSREQUIRED;
+ errno = ERRNO_TLSREQUIRED;
message = string_sprintf("a TLS session is required, but %s",
smtp_peer_options & OPTION_TLS
? "an attempt to start TLS failed" : "the server did not offer TLS support");
# if defined(SUPPORT_DANE) && !defined(DISABLE_EVENT)
- if (sx->dane)
+ if (sx->conn_args.dane)
(void) event_raise(sx->conn_args.tblock->event_action, US"dane:fail",
smtp_peer_options & OPTION_TLS
? US"validation-failure" /* could do with better detail */
# endif
goto TLS_FAILED;
}
-#endif /*SUPPORT_TLS*/
+#endif /*DISABLE_TLS*/
/* If TLS is active, we have just started it up and re-done the EHLO command,
so its response needs to be analyzed. If TLS is not active and this is a
we skip this. */
if (continue_hostname == NULL
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
|| tls_out.active.sock >= 0
#endif
)
{
if (sx->esmtp || sx->lmtp)
{
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (!sx->early_pipe_active)
#endif
{
sx->peer_offered = ehlo_response(sx->buffer,
0 /* no TLS */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
| (sx->lmtp && ob->lmtp_ignore_quota ? OPTION_IGNQ : 0)
| OPTION_DSN | OPTION_PIPE | OPTION_SIZE
- | OPTION_CHUNKING | OPTION_PRDR | OPTION_UTF8 | OPTION_REQUIRETLS
+ | OPTION_CHUNKING | OPTION_PRDR | OPTION_UTF8
| (tls_out.active.sock >= 0 ? OPTION_EARLY_PIPE : 0) /* not for lmtp */
#else
| OPTION_DSN
| OPTION_PIPE
| (ob->size_addition >= 0 ? OPTION_SIZE : 0)
-# if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
- | (tls_requiretls & REQUIRETLS_MSG ? OPTION_REQUIRETLS : 0)
-# endif
#endif
);
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (tls_out.active.sock >= 0)
sx->ehlo_resp.crypted_features = sx->peer_offered;
#endif
DEBUG(D_transport) debug_printf("%susing DSN\n",
sx->peer_offered & OPTION_DSN ? "" : "not ");
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
- if (sx->peer_offered & OPTION_REQUIRETLS)
- {
- smtp_peer_options |= OPTION_REQUIRETLS;
- DEBUG(D_transport) debug_printf(
- tls_requiretls & REQUIRETLS_MSG
- ? "using REQUIRETLS\n" : "REQUIRETLS offered\n");
- }
-#endif
-
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if ( sx->early_pipe_ok
&& !sx->early_pipe_active
&& tls_out.active.sock >= 0
}
#endif /*SUPPORT_I18N*/
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
- /*XXX should tls_requiretls actually be per-addr? */
-
-if ( tls_requiretls & REQUIRETLS_MSG
- && !(sx->peer_offered & OPTION_REQUIRETLS)
- )
- {
- sx->setting_up = TRUE;
- errno = ERRNO_REQUIRETLS;
- message = US"REQUIRETLS support is required from the server"
- " but it was not offered";
- DEBUG(D_transport) debug_printf("%s\n", message);
- goto TLS_FAILED;
- }
-#endif
-
return OK;
int code;
RESPONSE_FAILED:
+ if (errno == ECONNREFUSED) /* first-read error on a TFO conn */
+ {
+ /* There is a testing facility for simulating a connection timeout, as I
+ can't think of any other way of doing this. It converts a connection
+ refused into a timeout if the timeout is set to 999999. This is done for
+ a 3whs connection in ip_connect(), but a TFO connection does not error
+ there - instead it gets ECONNREFUSED on the first data read. Tracking
+ that a TFO really was done is too hard, or we would set a
+ sx->pending_conn_done bit and test that in smtp_reap_banner() and
+ smtp_reap_ehlo(). That would let us also add the conn-timeout to the
+ cmd-timeout. */
+
+ if (f.running_in_test_harness && ob->connect_timeout == 999999)
+ errno = ETIMEDOUT;
+ set_errno_nohost(sx->addrlist,
+ errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
+ sx->verify ? US strerror(errno) : NULL,
+ DEFER, FALSE);
+ sx->send_quit = FALSE;
+ return DEFER;
+ }
+
+ /* really an error on an SMTP read */
message = NULL;
sx->send_quit = check_response(sx->conn_args.host, &errno, sx->addrlist->more_errno,
sx->buffer, &code, &message, &pass_message);
in message and errno, and setting_up will always be true. Treat as
a temporary error. */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
TLS_FAILED:
-# ifdef EXPERIMENTAL_REQUIRETLS
- if (errno == ERRNO_REQUIRETLS)
- code = '5', yield = FAIL;
- /*XXX DSN will be labelled 500; prefer 530 5.7.4 */
- else
-# endif
- code = '4', yield = DEFER;
+ code = '4', yield = DEFER;
goto FAILED;
#endif
|| errno == ERRNO_UTF8_FWD
#endif
? FAIL : DEFER,
- pass_message, sx->conn_args.host
+ pass_message,
+ errno == ECONNREFUSED ? NULL : sx->conn_args.host
#ifdef EXPERIMENTAL_DSN_INFO
, sx->smtp_greeting, sx->helo_response
#endif
if (sx->send_quit)
(void)smtp_write_command(sx, SCMD_FLUSH, "QUIT\r\n");
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (sx->cctx.tls_ctx)
{
tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
}
#ifndef DISABLE_PRDR
-/* If it supports Per-Recipient Data Reponses, and we have omre than one recipient,
+/* If it supports Per-Recipient Data Responses, and we have more than one recipient,
request that */
sx->prdr_active = FALSE;
&& addrlist->prop.utf8_msg
&& !addrlist->prop.utf8_downcvt
)
- Ustrcpy(p, " SMTPUTF8"), p += 9;
-#endif
-
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
-if (tls_requiretls & REQUIRETLS_MSG)
- Ustrcpy(p, " REQUIRETLS") , p += 11;
+ Ustrcpy(p, US" SMTPUTF8"), p += 9;
#endif
/* check if all addresses have DSN-lasthop flag; do not send RET and ENVID if so */
if (sx->peer_offered & OPTION_DSN && !sx->dsn_all_lasthop)
{
if (dsn_ret == dsn_ret_hdrs)
- { Ustrcpy(p, " RET=HDRS"); p += 9; }
+ { Ustrcpy(p, US" RET=HDRS"); p += 9; }
else if (dsn_ret == dsn_ret_full)
- { Ustrcpy(p, " RET=FULL"); p += 9; }
+ { Ustrcpy(p, US" RET=FULL"); p += 9; }
if (dsn_envid)
{
{
BOOL first = TRUE;
- Ustrcpy(p, " NOTIFY=");
+ Ustrcpy(p, US" NOTIFY=");
while (*p) p++;
for (int i = 0; i < nelem(rf_list); i++) if (addr->dsn_flags & rf_list[i])
{
case -2: return -2; /* non-MAIL read i/o error */
default: return -1; /* any MAIL error */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
case -4: return -1; /* non-2xx for pipelined banner or EHLO */
+ case -5: return -1; /* TLS first-read error */
#endif
}
sx->pending_MAIL = FALSE; /* Dealt with MAIL */
}
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
/*****************************************************
* Proxy TLS connection for another transport process *
******************************************************/
bufsiz size of buffer
pfd pipe filedescriptor array; [0] is comms to proxied process
timeout per-read timeout, seconds
+
+Does not return.
*/
void
_exit(rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
-if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
+testharness_pause_ms(100); /* let parent debug out */
set_process_info("proxying TLS connection for continued transport");
FD_ZERO(&rfds);
FD_SET(tls_out.active.sock, &rfds);
}
done:
- if (f.running_in_test_harness) millisleep(100); /* let logging complete */
+ testharness_pause_ms(100); /* let logging complete */
exim_exit(0, US"TLS proxy");
}
#endif
uschar *interface, transport_instance *tblock,
BOOL *message_defer, BOOL suppress_tls)
{
-address_item *addr;
smtp_transport_options_block * ob = SOB tblock->options_block;
int yield = OK;
int save_errno;
&& *transport_filter_argv
&& **transport_filter_argv
&& sx.peer_offered & OPTION_CHUNKING
+#ifndef DISABLE_DKIM
+ /* When dkim signing, chunking is handled even with a transport-filter */
+ && !(ob->dkim.dkim_private_key && ob->dkim.dkim_domain && ob->dkim.dkim_selector)
+ && !ob->dkim.force_bodyhash
+#endif
)
{
sx.peer_offered &= ~OPTION_CHUNKING;
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 0: break; /* No 2xx or 5xx, but no probs */
- case -1: goto END_OFF; /* Timeout on RCPT */
+ case -1: goto END_OFF; /* Timeout on RCPT */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
+ case -5: /* TLS first-read error */
case -4: HDEBUG(D_transport)
debug_printf("failed reaping pipelined cmd responses\n");
#endif
transport_count = 0;
#ifndef DISABLE_DKIM
+ {
+# ifdef MEASURE_TIMING
+ struct timeval t0;
+ gettimeofday(&t0, NULL);
+# endif
dkim_exim_sign_init();
# ifdef EXPERIMENTAL_ARC
{
}
}
# endif
+# ifdef MEASURE_TIMING
+ report_time_since(&t0, US"dkim_exim_sign_init (delta)");
+# endif
+ }
sx.ok = dkim_transport_write_message(&tctx, &ob->dkim, CUSS &message);
#else
sx.ok = transport_write_message(&tctx, 0);
{
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, */
+ 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 0: break; /* No 2xx or 5xx, but no probs */
- case -1: goto END_OFF; /* Timeout on RCPT */
+ case -1: goto END_OFF; /* Timeout on RCPT */
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
+ case -5: /* TLS first-read error */
case -4: HDEBUG(D_transport)
debug_printf("failed reaping pipelined cmd responses\n");
#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 */
}
}
if (tcp_out_fastopen >= TFO_USED_DATA) setflag(addr, af_tcp_fastopen_data);
}
if (sx.pipelining_used) setflag(addr, af_pipelining);
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (sx.early_pipe_active) setflag(addr, af_early_pipe);
#endif
#ifndef DISABLE_PRDR
else
{
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
/* If we were early-pipelinng and the actual EHLO response did not match
the cached value we assumed, we could have detected it and passed a
custom errno through to here. It would be nice to RSET and retry right
many as to hit the configured limit. The function transport_check_waiting looks
for a waiting message and returns its id. Then transport_pass_socket tries to
set up a continued delivery by passing the socket on to another process. The
-variable send_rset is FALSE if a message has just been successfully transfered.
+variable send_rset is FALSE if a message has just been successfully transferred.
If we are already sending down a continued channel, there may be further
addresses not yet delivered that are aimed at the same host, but which have not
if ( sx.first_addr != NULL
|| f.continue_more
|| (
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
( tls_out.active.sock < 0 && !continue_proxy_cipher
|| verify_check_given_host(CUSS &ob->hosts_nopass_tls, host) != OK
)
if (sx.ok)
{
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
int pfd[2];
#endif
int socket_fd = sx.cctx.sock;
transport_pass_socket). If the caller has more ready, just return with
the connection still open. */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (tls_out.active.sock >= 0)
if ( f.continue_more
|| verify_check_given_host(CUSS &ob->hosts_noproxy_tls, host) == OK)
/* Set up a pipe for proxying TLS for the new transport process */
smtp_peer_options |= OPTION_TLS;
- if (sx.ok = (socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0))
+ if ((sx.ok = socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0))
socket_fd = pfd[1];
else
set_errno(sx.first_addr, errno, US"internal allocation problem",
just passed the baton to. Fork a child to to do it, and return to
get logging done asap. Which way to place the work makes assumptions
about post-fork prioritisation which may not hold on all platforms. */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (tls_out.active.sock >= 0)
{
int pid = fork();
if (pid == 0) /* child; fork again to disconnect totally */
{
- if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
+ testharness_pause_ms(100); /* let parent debug out */
/* does not return */
smtp_proxy_tls(sx.cctx.tls_ctx, sx.buffer, sizeof(sx.buffer), pfd,
ob->command_timeout);
END_OFF:
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
sx.cctx.tls_ctx = NULL;
#endif
if (sx.send_quit)
{
shutdown(sx.cctx.sock, SHUT_WR);
- millisleep(f.running_in_test_harness ? 200 : 20);
+ millisleep(20);
+ testharness_pause_ms(200);
if (fcntl(sx.cctx.sock, F_SETFL, O_NONBLOCK) == 0)
for (int i = 16; read(sx.cctx.sock, sx.inbuffer, sizeof(sx.inbuffer)) > 0 && i > 0;)
i--; /* drain socket */
addr->basic_errno = 0;
addr->more_errno = (host->mx >= 0)? 'M' : 'A';
addr->message = NULL;
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
addr->cipher = NULL;
addr->ourcert = NULL;
addr->peercert = NULL;
addr->peerdn = NULL;
addr->ocsp = OCSP_NOT_REQ;
+ addr->tlsver = NULL;
#endif
#ifdef EXPERIMENTAL_DSN_INFO
addr->smtp_greeting = NULL;
int hosts_serial = 0;
int hosts_total = 0;
int total_hosts_tried = 0;
-address_item *addr;
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;
+host_item *host = NULL;
DEBUG(D_transport)
{
a host list with hosts_override set, use the host list supplied with the
transport. It is an error for this not to exist. */
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
-if (tls_requiretls & REQUIRETLS_MSG)
- ob->tls_tempfail_tryclear = FALSE; /*XXX surely we should have a local for this
- rather than modifying the transport? */
-#endif
-
if (!hostlist || (ob->hosts_override && ob->hosts))
{
if (!ob->hosts)
else
if (ob->hosts_randomize) s = expanded_hosts = string_copy(s);
+ if (is_tainted(s))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "attempt to use tainted host list '%s' from '%s' in transport %s",
+ s, ob->hosts, tblock->name);
+ /* Avoid leaking info to an attacker */
+ addrlist->message = US"internal configuration error";
+ addrlist->transport_return = PANIC;
+ return FALSE;
+ }
+
host_build_hostlist(&hostlist, s, ob->hosts_randomize);
/* Check that the expansion yielded something useful. */
{
int rc;
int host_af;
- uschar *rs;
BOOL host_is_expired = FALSE;
BOOL message_defer = FALSE;
BOOL some_deferred = FALSE;
treated separately. */
host_af = Ustrchr(host->address, ':') == NULL ? AF_INET : AF_INET6;
- if ((rs = ob->interface) && *rs)
{
- if (!smtp_get_interface(rs, host_af, addrlist, &interface, tid))
- return FALSE;
- pistring = string_sprintf("%s/%s", pistring, interface);
+ uschar * s = ob->interface;
+ if (s && *s)
+ {
+ if (!smtp_get_interface(s, host_af, addrlist, &interface, tid))
+ return FALSE;
+ pistring = string_sprintf("%s/%s", pistring, interface);
+ }
}
/* The first time round the outer loop, check the status of the host by
if (expanded_hosts)
{
- thost = store_get(sizeof(host_item));
+ thost = store_get(sizeof(host_item), FALSE);
*thost = *host;
thost->name = string_copy(host->name);
thost->address = string_copy(host->address);
session, so the in-clear transmission after those errors, if permitted,
happens inside smtp_deliver().] */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if ( rc == DEFER
&& first_addr->basic_errno == ERRNO_TLSFAILURE
&& ob->tls_tempfail_tryclear
deferred_event_raise(first_addr, host);
# endif
}
-#endif /*SUPPORT_TLS*/
+#endif /*DISABLE_TLS*/
}
/* Delivery attempt finished */
- rs = rc == OK ? US"OK"
- : rc == DEFER ? US"DEFER"
- : rc == ERROR ? US"ERROR"
- : US"?";
-
set_process_info("delivering %s: just tried %s [%s]%s for %s%s: result %s",
message_id, host->name, host->address, pistring, addrlist->address,
- addrlist->next ? " (& others)" : "", rs);
+ addrlist->next ? " (& others)" : "", rc_to_string(rc));
/* Release serialization if set up */
ob->hosts_max_try_hardlimit);
}
- if (f.running_in_test_harness) millisleep(500); /* let server debug out */
+ testharness_pause_ms(500); /* let server debug out */
} /* End of loop for trying multiple hosts. */
/* If we failed to find a matching host in the list, for an already-open
int fd = cutthrough.cctx.sock >= 0 ? cutthrough.cctx.sock : 0;
DEBUG(D_transport) debug_printf("no hosts match already-open connection\n");
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
/* A TLS conn could be open for a cutthrough, but not for a plain continued-
transport */
/*XXX doublecheck that! */