(void *)offsetof(smtp_transport_options_block, address_retry_include_sender) },
{ "allow_localhost", opt_bool,
(void *)offsetof(smtp_transport_options_block, allow_localhost) },
+#ifdef EXPERIMENTAL_ARC
+ { "arc_sign", opt_stringptr,
+ (void *)offsetof(smtp_transport_options_block, arc_sign) },
+#endif
{ "authenticated_sender", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, authenticated_sender) },
{ "authenticated_sender_force", opt_bool,
(void *)offsetof(smtp_transport_options_block, connect_timeout) },
{ "connection_max_messages", opt_int | opt_public,
(void *)offsetof(transport_instance, connection_max_messages) },
+# ifdef SUPPORT_DANE
+ { "dane_require_tls_ciphers", opt_stringptr,
+ (void *)offsetof(smtp_transport_options_block, dane_require_tls_ciphers) },
+# endif
{ "data_timeout", opt_time,
(void *)offsetof(smtp_transport_options_block, data_timeout) },
{ "delay_after_cutoff", opt_bool,
(void *)offsetof(smtp_transport_options_block, dkim.dkim_sign_headers) },
{ "dkim_strict", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, dkim.dkim_strict) },
+ { "dkim_timestamps", opt_stringptr,
+ (void *)offsetof(smtp_transport_options_block, dkim.dkim_timestamps) },
#endif
{ "dns_qualify_single", opt_bool,
(void *)offsetof(smtp_transport_options_block, dns_qualify_single) },
{ "hosts_require_auth", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_require_auth) },
#ifdef SUPPORT_TLS
-# ifdef EXPERIMENTAL_DANE
+# ifdef SUPPORT_DANE
{ "hosts_require_dane", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_require_dane) },
# endif
(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(EXPERIMENTAL_DANE)
+#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
{ "hosts_try_dane", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, hosts_try_dane) },
#endif
{ "tls_verify_certificates", opt_stringptr,
(void *)offsetof(smtp_transport_options_block, tls_verify_certificates) },
{ "tls_verify_hosts", opt_stringptr,
- (void *)offsetof(smtp_transport_options_block, tls_verify_hosts) }
+ (void *)offsetof(smtp_transport_options_block, tls_verify_hosts) },
+#endif
+#ifdef SUPPORT_I18N
+ { "utf8_downconvert", opt_stringptr,
+ (void *)offsetof(smtp_transport_options_block, utf8_downconvert) },
#endif
};
/* Size of the options list. An extern variable has to be used so that its
address can appear in the tables drtables.c. */
-int smtp_transport_options_count =
- sizeof(smtp_transport_options)/sizeof(optionlist);
+int smtp_transport_options_count = nelem(smtp_transport_options);
#ifdef MACRO_PREDEF
.fallback_hosts = NULL,
.hostlist = NULL,
.fallback_hostlist = NULL,
- .authenticated_sender = NULL,
.helo_data = US"$primary_hostname",
.interface = NULL,
.port = NULL,
.hosts_try_auth = NULL,
.hosts_require_auth = NULL,
.hosts_try_chunking = US"*",
-#ifdef EXPERIMENTAL_DANE
+#ifdef SUPPORT_DANE
.hosts_try_dane = NULL,
.hosts_require_dane = NULL,
+ .dane_require_tls_ciphers = NULL,
#endif
.hosts_try_fastopen = NULL,
#ifndef DISABLE_PRDR
.tls_try_verify_hosts = US"*",
.tls_verify_cert_hostnames = US"*",
#endif
+#ifdef SUPPORT_I18N
+ .utf8_downconvert = NULL,
+#endif
#ifndef DISABLE_DKIM
.dkim =
{.dkim_domain = NULL,
.dkim_sign_headers = NULL,
.dkim_strict = NULL,
.dkim_hash = US"sha256",
- .dot_stuffed = FALSE},
+ .dkim_timestamps = NULL,
+ .dot_stuffed = FALSE,
+ .force_bodyhash = FALSE,
+# ifdef EXPERIMENTAL_ARC
+ .arc_signspec = NULL,
+# endif
+ },
+# ifdef EXPERIMENTAL_ARC
+ .arc_sign = NULL,
+# endif
#endif
};
if (sx->pending_MAIL)
{
+ DEBUG(D_transport) debug_printf("%s expect mail\n", __FUNCTION__);
count--;
if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
'2', ob->command_timeout))
/* The address was accepted */
addr->host_used = sx->host;
+ DEBUG(D_transport) debug_printf("%s expect rcpt\n", __FUNCTION__);
if (smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
'2', ob->command_timeout))
{
/* Handle a response to DATA. If we have not had any good recipients, either
previously or in this block, the response is ignored. */
-if (pending_DATA != 0 &&
- !smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
- '3', ob->command_timeout))
+if (pending_DATA != 0)
{
- int code;
- uschar *msg;
- BOOL pass_message;
- if (pending_DATA > 0 || (yield & 1) != 0)
+ DEBUG(D_transport) debug_printf("%s expect data\n", __FUNCTION__);
+ if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+ '3', ob->command_timeout))
{
- if (errno == 0 && sx->buffer[0] == '4')
+ int code;
+ uschar *msg;
+ BOOL pass_message;
+ if (pending_DATA > 0 || (yield & 1) != 0)
{
- errno = ERRNO_DATA4XX;
- sx->first_addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
+ 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 -3;
+ (void)check_response(sx->host, &errno, 0, sx->buffer, &code, &msg, &pass_message);
+ DEBUG(D_transport) debug_printf("%s\nerror for DATA ignored: pipelining "
+ "is in use and there were no good recipients\n", msg);
}
- (void)check_response(sx->host, &errno, 0, sx->buffer, &code, &msg, &pass_message);
- DEBUG(D_transport) debug_printf("%s\nerror for DATA ignored: pipelining "
- "is in use and there were no good recipients\n", msg);
}
/* All responses read and handled; MAIL (if present) received 2xx and DATA (if
-#ifdef EXPERIMENTAL_DANE
+#ifdef SUPPORT_DANE
/* Lookup TLSA record for host/port.
Return: OK success with dnssec; DANE mode
DEFER Do not use this host now, may retry later
-static uschar
-ehlo_response(uschar * buf, uschar checks)
+static unsigned
+ehlo_response(uschar * buf, unsigned checks)
{
size_t bsize = Ustrlen(buf);
if ( checks & OPTION_TLS
&& pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
checks &= ~OPTION_TLS;
+
+# ifdef EXPERIMENTAL_REQUIRETLS
+if ( checks & OPTION_REQUIRETLS
+ && pcre_exec(regex_REQUIRETLS, NULL, CS buf,bsize, 0, PCRE_EOPT, NULL,0) < 0)
+ checks &= ~OPTION_REQUIRETLS;
+# endif
#endif
if ( checks & OPTION_IGNQ
int
smtp_setup_conn(smtp_context * sx, BOOL suppress_tls)
{
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
dns_answer tlsa_dnsa;
#endif
BOOL pass_message = FALSE;
sx->utf8_needed = FALSE;
#endif
sx->dsn_all_lasthop = TRUE;
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
sx->dane = FALSE;
-sx->dane_required = verify_check_given_host(&sx->ob->hosts_require_dane, sx->host) == OK;
+sx->dane_required =
+ verify_check_given_host(&sx->ob->hosts_require_dane, sx->host) == OK;
#endif
if ((sx->max_rcpt = sx->tblock->max_addresses) == 0) sx->max_rcpt = 999999;
smtp_port_for_connect(sx->host, sx->port);
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
/* Do TLSA lookup for DANE */
{
tls_out.dane_verified = FALSE;
string_sprintf("DANE error: tlsa lookup %s",
rc == DEFER ? "DEFER" : "FAIL"),
rc, FALSE);
+# ifndef DISABLE_EVENT
+ (void) event_raise(sx->tblock->event_action,
+ US"dane:fail", sx->dane_required
+ ? US"dane-required" : US"dnssec-invalid");
+# endif
return rc;
}
}
set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
string_sprintf("DANE error: %s lookup not DNSSEC", sx->host->name),
FAIL, FALSE);
+# ifndef DISABLE_EVENT
+ (void) event_raise(sx->tblock->event_action,
+ US"dane:fail", US"dane-required");
+# endif
return FAIL;
}
}
/* Make the TCP connection */
- sx->inblock.sock = sx->outblock.sock =
+ sx->cctx.sock =
smtp_connect(sx->host, sx->host_af, sx->interface,
sx->ob->connect_timeout, sx->tblock);
+ sx->cctx.tls_ctx = NULL;
+ sx->inblock.cctx = sx->outblock.cctx = &sx->cctx;
- if (sx->inblock.sock < 0)
+ if (sx->cctx.sock < 0)
{
uschar * msg = NULL;
if (sx->verify)
BOOL good_response;
#ifdef TCP_QUICKACK
- (void) setsockopt(sx->inblock.sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
+ (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
#endif
good_response = smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
'2', sx->ob->command_timeout);
else
{
- if (cutthrough.fd >= 0 && cutthrough.callout_hold_only)
+ if (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only)
{
- sx->inblock.sock = sx->outblock.sock = cutthrough.fd;
+ sx->cctx = cutthrough.cctx;
sx->host->port = sx->port = cutthrough.host.port;
}
else
{
- sx->inblock.sock = sx->outblock.sock = 0; /* stdin */
+ sx->cctx.sock = 0; /* stdin */
+ sx->cctx.tls_ctx = NULL;
smtp_port_for_connect(sx->host, sx->port); /* Record the port that was used */
}
+ sx->inblock.cctx = sx->outblock.cctx = &sx->cctx;
smtp_command = big_buffer;
sx->helo_data = NULL; /* ensure we re-expand ob->helo_data */
held-open verify connection with TLS, nothing more to do. */
if ( continue_proxy_cipher
- || (cutthrough.fd >= 0 && cutthrough.callout_hold_only && cutthrough.is_tls)
+ || (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only
+ && cutthrough.is_tls)
)
{
sx->peer_offered = smtp_peer_options;
- pipelining_active = !!(smtp_peer_options & OPTION_PIPE);
+ sx->pipelining_used = pipelining_active = !!(smtp_peer_options & OPTION_PIPE);
HDEBUG(D_transport) debug_printf("continued connection, %s TLS\n",
continue_proxy_cipher ? "proxied" : "verify conn with");
return OK;
{
address_item * addr;
uschar * errstr;
- int rc = tls_client_start(sx->inblock.sock, sx->host, sx->addrlist, sx->tblock,
-# ifdef EXPERIMENTAL_DANE
+ sx->cctx.tls_ctx = tls_client_start(sx->cctx.sock, sx->host,
+ sx->addrlist, sx->tblock,
+# ifdef SUPPORT_DANE
sx->dane ? &tlsa_dnsa : NULL,
# endif
- &errstr);
+ &tls_out, &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. */
-
- if (rc != OK)
+ if (!sx->cctx.tls_ctx)
{
-# ifdef EXPERIMENTAL_DANE
- if (sx->dane) log_write(0, LOG_MAIN,
+ /* 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. */
+
+# ifdef SUPPORT_DANE
+ if (sx->dane)
+ {
+ log_write(0, LOG_MAIN,
"DANE attempt failed; TLS connection to %s [%s]: %s",
sx->host->name, sx->host->address, errstr);
+# ifndef DISABLE_EVENT
+ (void) event_raise(sx->tblock->event_action,
+ US"dane:fail", US"validation-failure"); /* could do with better detail */
+# endif
+ }
# endif
errno = ERRNO_TLSFAILURE;
expand it here. $sending_ip_address and $sending_port are set up right at the
start of the Exim process (in exim.c). */
-if (tls_out.active >= 0)
+if (tls_out.active.sock >= 0)
{
char *greeting_cmd;
BOOL good_response;
have one. */
else if ( sx->smtps
-# ifdef EXPERIMENTAL_DANE
+# ifdef SUPPORT_DANE
|| sx->dane
+# endif
+# ifdef EXPERIMENTAL_REQUIRETLS
+ || tls_requiretls & REQUIRETLS_MSG
# endif
|| verify_check_given_host(&sx->ob->hosts_require_tls, sx->host) == OK
)
{
- errno = ERRNO_TLSREQUIRED;
+ errno =
+# ifdef EXPERIMENTAL_REQUIRETLS
+ tls_requiretls & REQUIRETLS_MSG ? ERRNO_REQUIRETLS :
+# endif
+ 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)
+ (void) event_raise(sx->tblock->event_action, US"dane:fail",
+ smtp_peer_options & OPTION_TLS
+ ? US"validation-failure" /* could do with better detail */
+ : US"starttls-not-supported");
+# endif
goto TLS_FAILED;
}
#endif /*SUPPORT_TLS*/
if (continue_hostname == NULL
#ifdef SUPPORT_TLS
- || tls_out.active >= 0
+ || tls_out.active.sock >= 0
#endif
)
{
| OPTION_DSN
| OPTION_PIPE
| (sx->ob->size_addition >= 0 ? OPTION_SIZE : 0)
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+ | (tls_requiretls & REQUIRETLS_MSG ? OPTION_REQUIRETLS : 0)
+#endif
);
/* Set for IGNOREQUOTA if the response to LHLO specifies support and the
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
+
/* Note if the response to EHLO specifies support for the AUTH extension.
If it has, check that this host is one we want to authenticate to, and do
the business. The host name and address must be available when the
}
}
}
-pipelining_active = !!(smtp_peer_options & OPTION_PIPE);
+sx->pipelining_used = pipelining_active = !!(smtp_peer_options & OPTION_PIPE);
/* The setting up of the SMTP call is now complete. Any subsequent errors are
message-specific. */
#ifdef SUPPORT_I18N
if (sx->addrlist->prop.utf8_msg)
{
+ uschar * s;
+
+ /* If the transport sets a downconversion mode it overrides any set by ACL
+ for the message. */
+
+ if ((s = sx->ob->utf8_downconvert))
+ {
+ if (!(s = expand_string(s)))
+ {
+ message = string_sprintf("failed to expand utf8_downconvert: %s",
+ expand_string_message);
+ set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
+ yield = DEFER;
+ goto SEND_QUIT;
+ }
+ switch (*s)
+ {
+ case '1': sx->addrlist->prop.utf8_downcvt = TRUE;
+ sx->addrlist->prop.utf8_downcvt_maybe = FALSE;
+ break;
+ case '0': sx->addrlist->prop.utf8_downcvt = FALSE;
+ sx->addrlist->prop.utf8_downcvt_maybe = FALSE;
+ break;
+ case '-': if (s[1] == '1')
+ {
+ sx->addrlist->prop.utf8_downcvt = FALSE;
+ sx->addrlist->prop.utf8_downcvt_maybe = TRUE;
+ }
+ break;
+ }
+ }
+
sx->utf8_needed = !sx->addrlist->prop.utf8_downcvt
&& !sx->addrlist->prop.utf8_downcvt_maybe;
DEBUG(D_transport) if (!sx->utf8_needed)
errno = ERRNO_UTF8_FWD;
goto RESPONSE_FAILED;
}
+#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;
message = NULL;
sx->send_quit = check_response(sx->host, &errno, sx->addrlist->more_errno,
sx->buffer, &code, &message, &pass_message);
+ yield = DEFER;
goto FAILED;
SEND_FAILED:
message = US string_sprintf("send() to %s [%s] failed: %s",
sx->host->name, sx->host->address, strerror(errno));
sx->send_quit = FALSE;
+ yield = DEFER;
goto FAILED;
EHLOHELO_FAILED:
message = string_sprintf("Remote host closed connection in response to %s"
" (EHLO response was: %s)", smtp_command, sx->buffer);
sx->send_quit = FALSE;
+ yield = DEFER;
goto FAILED;
/* This label is jumped to directly when a TLS negotiation has failed,
#ifdef SUPPORT_TLS
TLS_FAILED:
- code = '4';
+# 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;
goto FAILED;
#endif
, sx->smtp_greeting, sx->helo_response
#endif
);
- yield = DEFER;
}
(void)smtp_write_command(&sx->outblock, SCMD_FLUSH, "QUIT\r\n");
#ifdef SUPPORT_TLS
-tls_close(FALSE, TRUE);
+if (sx->cctx.tls_ctx)
+ {
+ tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
+ sx->cctx.tls_ctx = NULL;
+ }
#endif
/* Close the socket, and return the appropriate value, first setting
HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n");
if (sx->send_quit)
{
- shutdown(sx->outblock.sock, SHUT_WR);
- if (fcntl(sx->inblock.sock, F_SETFL, O_NONBLOCK) == 0)
- for (rc = 16; read(sx->inblock.sock, sx->inbuffer, sizeof(sx->inbuffer)) > 0 && rc > 0;)
+ shutdown(sx->cctx.sock, SHUT_WR);
+ if (fcntl(sx->cctx.sock, F_SETFL, O_NONBLOCK) == 0)
+ for (rc = 16; read(sx->cctx.sock, sx->inbuffer, sizeof(sx->inbuffer)) > 0 && rc > 0;)
rc--; /* drain socket */
sx->send_quit = FALSE;
}
-(void)close(sx->inblock.sock);
-sx->inblock.sock = sx->outblock.sock = -1;
+(void)close(sx->cctx.sock);
+sx->cctx.sock = -1;
#ifndef DISABLE_EVENT
(void) event_raise(sx->tblock->event_action, US"tcp:close", NULL);
Ustrcpy(p, " SMTPUTF8"), p += 9;
#endif
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+if (tls_requiretls & REQUIRETLS_MSG)
+ Ustrcpy(p, " REQUIRETLS") , p += 11;
+#endif
+
/* check if all addresses have DSN-lasthop flag; do not send RET and ENVID if so */
for (sx->dsn_all_lasthop = TRUE, addr = addrlist, address_count = 0;
addr && address_count < sx->max_rcpt;
channels are closed, exit the process.
Arguments:
+ ct_ctx tls context
buf space to use for buffering
bufsiz size of buffer
pfd pipe filedescriptor array; [0] is comms to proxied process
*/
void
-smtp_proxy_tls(uschar * buf, size_t bsize, int * pfd, int timeout)
+smtp_proxy_tls(void * ct_ctx, uschar * buf, size_t bsize, int * pfd,
+ int timeout)
{
fd_set rfds, efds;
-int max_fd = MAX(pfd[0], tls_out.active) + 1;
+int max_fd = MAX(pfd[0], tls_out.active.sock) + 1;
int rc, i, fd_bits, nbytes;
close(pfd[1]);
if (running_in_test_harness) millisleep(100); /* let parent debug out */
set_process_info("proxying TLS connection for continued transport");
FD_ZERO(&rfds);
-FD_SET(tls_out.active, &rfds);
+FD_SET(tls_out.active.sock, &rfds);
FD_SET(pfd[0], &rfds);
for (fd_bits = 3; fd_bits; )
goto done;
}
- if (FD_ISSET(tls_out.active, &efds) || FD_ISSET(pfd[0], &efds))
+ if (FD_ISSET(tls_out.active.sock, &efds) || FD_ISSET(pfd[0], &efds))
{
DEBUG(D_transport) debug_printf("select: exceptional cond on %s fd\n",
FD_ISSET(pfd[0], &efds) ? "proxy" : "tls");
goto done;
}
}
- while (rc < 0 || !(FD_ISSET(tls_out.active, &rfds) || FD_ISSET(pfd[0], &rfds)));
+ while (rc < 0 || !(FD_ISSET(tls_out.active.sock, &rfds) || FD_ISSET(pfd[0], &rfds)));
/* handle inbound data */
- if (FD_ISSET(tls_out.active, &rfds))
- if ((rc = tls_read(FALSE, buf, bsize)) <= 0)
+ if (FD_ISSET(tls_out.active.sock, &rfds))
+ if ((rc = tls_read(ct_ctx, buf, bsize)) <= 0)
{
fd_bits &= ~1;
- FD_CLR(tls_out.active, &rfds);
+ FD_CLR(tls_out.active.sock, &rfds);
shutdown(pfd[0], SHUT_WR);
timeout = 5;
}
if ((i = write(pfd[0], buf + nbytes, rc - nbytes)) < 0) goto done;
}
else if (fd_bits & 1)
- FD_SET(tls_out.active, &rfds);
+ FD_SET(tls_out.active.sock, &rfds);
/* handle outbound data */
if (FD_ISSET(pfd[0], &rfds))
if ((rc = read(pfd[0], buf, bsize)) <= 0)
{
fd_bits = 0;
- tls_close(FALSE, TRUE);
+ tls_close(ct_ctx, TLS_SHUTDOWN_NOWAIT);
+ ct_ctx = NULL;
}
else
{
for (nbytes = 0; rc - nbytes > 0; nbytes += i)
- if ((i = tls_write(FALSE, buf + nbytes, rc - nbytes, FALSE)) < 0)
+ if ((i = tls_write(ct_ctx, buf + nbytes, rc - nbytes, FALSE)) < 0)
goto done;
}
else if (fd_bits & 2)
else
{
transport_ctx tctx = {
- {sx.inblock.sock},
+ {sx.cctx.sock}, /*XXX will this need TLS info? */
tblock,
addrlist,
US".", US"..", /* Escaping strings */
transport_count = 0;
#ifndef DISABLE_DKIM
+ dkim_exim_sign_init();
+# ifdef EXPERIMENTAL_ARC
+ {
+ uschar * s = sx.ob->arc_sign;
+ if (s)
+ {
+ if (!(sx.ob->dkim.arc_signspec = s = expand_string(s)))
+ {
+ if (!expand_string_forcedfail)
+ {
+ message = US"failed to expand arc_sign";
+ sx.ok = FALSE;
+ goto SEND_FAILED;
+ }
+ }
+ else if (*s)
+ {
+ /* Ask dkim code to hash the body for ARC */
+ (void) arc_ams_setup_sign_bodyhash();
+ sx.ob->dkim.force_bodyhash = TRUE;
+ }
+ }
+ }
+# endif
sx.ok = dkim_transport_write_message(&tctx, &sx.ob->dkim, CUSS &message);
#else
sx.ok = transport_write_message(&tctx, 0);
addr->host_used = host;
addr->special_action = flag;
addr->message = conf;
+
+ if (sx.pipelining_used) setflag(addr, af_pipelining);
#ifndef DISABLE_PRDR
if (sx.prdr_active) setflag(addr, af_prdr_used);
#endif
#ifndef DISABLE_PRDR
if (sx.prdr_active)
{
+ const uschar * overall_message;
+
/* PRDR - get the final, overall response. For any non-success
upgrade all the address statuses. */
+
sx.ok = smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer), '2',
sx.ob->final_timeout);
if (!sx.ok)
goto RESPONSE_FAILED;
}
- /* Update the journal, or setup retry. */
+ /* Append the overall response to the individual PRDR response for logging
+ and update the journal, or setup retry. */
+
+ overall_message = string_printing(sx.buffer);
+ for (addr = addrlist; addr != sx.first_addr; addr = addr->next)
+ if (addr->transport_return == OK)
+ addr->message = string_sprintf("%s\\n%s", addr->message, overall_message);
+
for (addr = addrlist; addr != sx.first_addr; addr = addr->next)
if (addr->transport_return == OK)
{
|| continue_more
|| (
#ifdef SUPPORT_TLS
- ( tls_out.active < 0 && !continue_proxy_cipher
+ ( tls_out.active.sock < 0 && !continue_proxy_cipher
|| verify_check_given_host(&sx.ob->hosts_nopass_tls, host) != OK
)
&&
if (sx.ok)
{
int pfd[2];
- int socket_fd = sx.inblock.sock;
+ int socket_fd = sx.cctx.sock;
if (sx.first_addr != NULL) /* More addresses still to be sent */
the connection still open. */
#ifdef SUPPORT_TLS
- if (tls_out.active >= 0)
+ if (tls_out.active.sock >= 0)
if ( continue_more
|| verify_check_given_host(&sx.ob->hosts_noproxy_tls, host) == OK)
{
a new EHLO. If we don't get a good response, we don't attempt to pass
the socket on. */
- tls_close(FALSE, TRUE);
+ tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_WAIT);
+ sx.cctx.tls_ctx = NULL;
smtp_peer_options = smtp_peer_options_wrap;
sx.ok = !sx.smtps
&& smtp_write_command(&sx.outblock, SCMD_FLUSH,
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
- if (tls_out.active >= 0)
+ if (tls_out.active.sock >= 0)
{
int pid = fork();
if (pid == 0) /* child; fork again to disconnect totally */
{
if (running_in_test_harness) millisleep(100); /* let parent debug out */
/* does not return */
- smtp_proxy_tls(sx.buffer, sizeof(sx.buffer), pfd,
+ smtp_proxy_tls(sx.cctx.tls_ctx, sx.buffer, sizeof(sx.buffer), pfd,
sx.ob->command_timeout);
}
close(pfd[0]);
/* tidy the inter-proc to disconn the proxy proc */
waitpid(pid, NULL, 0);
- tls_close(FALSE, FALSE);
- (void)close(sx.inblock.sock);
+ tls_close(sx.cctx.tls_ctx, TLS_NO_SHUTDOWN);
+ sx.cctx.tls_ctx = NULL;
+ (void)close(sx.cctx.sock);
+ sx.cctx.sock = -1;
continue_transport = NULL;
continue_hostname = NULL;
return yield;
END_OFF:
#ifdef SUPPORT_TLS
-tls_close(FALSE, TRUE);
+tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
+sx.cctx.tls_ctx = NULL;
#endif
/* Close the socket, and return the appropriate value, first setting
HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n");
if (sx.send_quit)
{
- shutdown(sx.outblock.sock, SHUT_WR);
- if (fcntl(sx.inblock.sock, F_SETFL, O_NONBLOCK) == 0)
- for (rc = 16; read(sx.inblock.sock, sx.inbuffer, sizeof(sx.inbuffer)) > 0 && rc > 0;)
+ shutdown(sx.cctx.sock, SHUT_WR);
+ if (fcntl(sx.cctx.sock, F_SETFL, O_NONBLOCK) == 0)
+ for (rc = 16; read(sx.cctx.sock, sx.inbuffer, sizeof(sx.inbuffer)) > 0 && rc > 0;)
rc--; /* drain socket */
}
-(void)close(sx.inblock.sock);
+(void)close(sx.cctx.sock);
#ifndef DISABLE_EVENT
(void) event_raise(tblock->event_action, US"tcp:close", NULL);
{
smtp_transport_options_block *ob =
(smtp_transport_options_block *)tblock->options_block;
+client_conn_ctx cctx;
smtp_inblock inblock;
smtp_outblock outblock;
uschar buffer[256];
uschar inbuffer[4096];
uschar outbuffer[16];
-inblock.sock = fileno(stdin);
+/*XXX really we need an active-smtp-client ctx, rather than assuming stdout */
+cctx.sock = fileno(stdin);
+cctx.tls_ctx = cctx.sock == tls_out.active.sock ? tls_out.active.tls_ctx : NULL;
+
+inblock.cctx = &cctx;
inblock.buffer = inbuffer;
inblock.buffersize = sizeof(inbuffer);
inblock.ptr = inbuffer;
inblock.ptrend = inbuffer;
-outblock.sock = inblock.sock;
+outblock.cctx = &cctx;
outblock.buffersize = sizeof(outbuffer);
outblock.buffer = outbuffer;
outblock.ptr = outbuffer;
(void)smtp_write_command(&outblock, SCMD_FLUSH, "QUIT\r\n");
(void)smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
ob->command_timeout);
-(void)close(inblock.sock);
+(void)close(cctx.sock);
}
if (continue_hostname)
debug_printf("already connected to %s [%s] (on fd %d)\n",
continue_hostname, continue_host_address,
- cutthrough.fd >= 0 ? cutthrough.fd : 0);
+ cutthrough.cctx.sock >= 0 ? cutthrough.cctx.sock : 0);
}
/* Set the flag requesting that these hosts be added to the waiting
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)
if (continue_hostname && !continue_host_tried)
{
- int fd = cutthrough.fd >= 0 ? cutthrough.fd : 0;
+ 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
- if (tls_out.active == fd)
+ /* A TLS conn could be open for a cutthrough, but not for a plain continued-
+ transport */
+/*XXX doublecheck that! */
+
+ if (cutthrough.cctx.sock >= 0 && cutthrough.is_tls)
{
- (void) tls_write(FALSE, US"QUIT\r\n", 6, FALSE);
- tls_close(FALSE, TRUE);
+ (void) tls_write(cutthrough.cctx.tls_ctx, US"QUIT\r\n", 6, FALSE);
+ tls_close(cutthrough.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
+ cutthrough.cctx.tls_ctx = NULL;
+ cutthrough.is_tls = FALSE;
}
else
#else
(void) write(fd, US"QUIT\r\n", 6);
#endif
(void) close(fd);
- cutthrough.fd = -1;
+ cutthrough.cctx.sock = -1;
continue_hostname = NULL;
goto retry_non_continued;
}