#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
#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_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,
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 *
*************************************************/
transport_count);
return FALSE;
- case ECONNREFUSED: /* First-read error on a TFO conn */
- if (verify_mode) *message = US strerror(*errno_value);
- return FALSE; /* nonverify, do not set message */
-
case ERRNO_SMTPFORMAT: /* Handle malformed SMTP response */
s = string_printing(buffer);
while (isspace(*s)) 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)
{
smtp_transport_options_block * ob = sx->conn_args.ob;
int yield = 0;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
int rc;
if ((rc = smtp_reap_early_pipe(sx, &count)) != OK)
return rc == FAIL ? -4 : -5;
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;
}
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]);
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
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
&& 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)
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 -5: errno = ERRNO_TLSFAILURE;
return DEFER;
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
case -4: /* non-2xx for pipelined banner or EHLO */
#endif
case -1: /* Timeout on RCPT */
BOOL pass_message = FALSE;
uschar * message = NULL;
int yield = OK;
-int rc;
#ifndef DISABLE_TLS
uschar * tls_errstr;
#endif
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;
#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
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
)
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->smtps)
{
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (sx->early_pipe_active)
{
sx->pending_BANNER = TRUE; /* sync_responses() must eventually handle */
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;
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. */
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
{
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
| (ob->size_addition >= 0 ? OPTION_SIZE : 0)
#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 ");
-#ifdef EXPERIMENTAL_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if ( sx->early_pipe_ok
&& !sx->early_pipe_active
&& tls_out.active.sock >= 0
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);
&& addrlist->prop.utf8_msg
&& !addrlist->prop.utf8_downcvt
)
- Ustrcpy(p, " SMTPUTF8"), p += 9;
+ 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
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
&& *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: 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");
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 -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");
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
/* 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",
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);
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->peercert = NULL;
addr->peerdn = NULL;
addr->ocsp = OCSP_NOT_REQ;
+ addr->tlsver = NULL;
#endif
#ifdef EXPERIMENTAL_DSN_INFO
addr->smtp_greeting = NULL;
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. */
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);
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