* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2017 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
#include "../exim.h"
(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
smtp_transport_options_block smtp_transport_option_defaults = {
.hosts = NULL,
- .hosts = NULL,
+ .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
.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
};
-#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
switch (rc)
{
- case DNS_SUCCEED:
- if (sec) return OK;
-
- log_write(0, LOG_MAIN, "DANE error: TLSA lookup not DNSSEC");
- /*FALLTHROUGH*/
case DNS_AGAIN:
return DEFER; /* just defer this TLS'd conn */
+ case DNS_SUCCEED:
+ if (sec) return OK;
+ log_write(0, LOG_MAIN,
+ "DANE error: TLSA lookup for %s not DNSSEC", host->name);
+ /*FALLTRHOUGH*/
+
case DNS_NODATA: /* no TLSA RR for this lookup */
case DNS_NOMATCH: /* no records at all for this lookup */
return dane_required ? FAIL : FAIL_FORCED;
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;
#endif
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);
+ (void) event_raise(sx->tblock->event_action,
+ US"dane:fail", sx->dane_required
+ ? US"dane-required" : US"dnssec-invalid");
return rc;
}
}
set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
string_sprintf("DANE error: %s lookup not DNSSEC", sx->host->name),
FAIL, FALSE);
+ (void) event_raise(sx->tblock->event_action,
+ US"dane:fail", US"dane-required");
return FAIL;
}
}
address_item * addr;
uschar * errstr;
int rc = tls_client_start(sx->inblock.sock, sx->host, sx->addrlist, sx->tblock,
-# ifdef EXPERIMENTAL_DANE
+# ifdef SUPPORT_DANE
sx->dane ? &tlsa_dnsa : NULL,
# endif
&errstr);
if (rc != OK)
{
-# ifdef EXPERIMENTAL_DANE
- if (sx->dane) log_write(0, LOG_MAIN,
+# 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);
+ (void) event_raise(sx->tblock->event_action,
+ US"dane:fail", US"validation-failure"); /* could do with better detail */
+ }
# endif
errno = ERRNO_TLSFAILURE;
have one. */
else if ( sx->smtps
-# ifdef EXPERIMENTAL_DANE
+# ifdef SUPPORT_DANE
|| sx->dane
# endif
|| verify_check_given_host(&sx->ob->hosts_require_tls, sx->host) == OK
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");
+# ifdef SUPPORT_DANE
+ 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*/
(void)smtp_write_command(&sx->outblock, SCMD_FLUSH, "QUIT\r\n");
#ifdef SUPPORT_TLS
-tls_close(FALSE, TRUE);
+tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
#endif
/* Close the socket, and return the appropriate value, first setting
_exit(rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
}
+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);
if ((rc = read(pfd[0], buf, bsize)) <= 0)
{
fd_bits = 0;
- tls_close(FALSE, TRUE);
+ tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
}
else
{
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);
#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)
{
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(FALSE, TLS_SHUTDOWN_WAIT);
smtp_peer_options = smtp_peer_options_wrap;
sx.ok = !sx.smtps
&& smtp_write_command(&sx.outblock, SCMD_FLUSH,
{
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,
sx.ob->command_timeout);
+ }
if (pid > 0) /* parent */
{
close(pfd[0]);
/* tidy the inter-proc to disconn the proxy proc */
waitpid(pid, NULL, 0);
- tls_close(FALSE, FALSE);
+ tls_close(FALSE, TLS_NO_SHUTDOWN);
(void)close(sx.inblock.sock);
continue_transport = NULL;
continue_hostname = NULL;
END_OFF:
#ifdef SUPPORT_TLS
-tls_close(FALSE, TRUE);
+tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
#endif
/* Close the socket, and return the appropriate value, first setting
smtp_transport_options_block *ob =
(smtp_transport_options_block *)(tblock->options_block);
host_item *hostlist = addrlist->host_list;
-host_item *host = NULL;
+host_item *host;
DEBUG(D_transport)
{
{
debug_printf("hostlist:\n");
for (host = hostlist; host; host = host->next)
- debug_printf(" %s:%d\n", host->name, host->port);
+ debug_printf(" '%s' IP %s port %d\n", host->name, host->address, host->port);
}
if (continue_hostname)
debug_printf("already connected to %s [%s] (on fd %d)\n",
{
host_item *nexthost = NULL;
int unexpired_hosts_tried = 0;
+ BOOL continue_host_tried = FALSE;
+retry_non_continued:
for (host = hostlist;
host
&& unexpired_hosts_tried < ob->hosts_max_try
/* Find by name if so configured, or if it's an IP address. We don't
just copy the IP address, because we need the test-for-local to happen. */
- flags = HOST_FIND_BY_A;
+ flags = HOST_FIND_BY_A | HOST_FIND_BY_AAAA;
if (ob->dns_qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE;
if (ob->dns_search_parents) flags |= HOST_FIND_SEARCH_PARENTS;
result of the lookup. Set expired FALSE, to save the outer loop executing
twice. */
- if ( continue_hostname
- && ( Ustrcmp(continue_hostname, host->name) != 0
- || Ustrcmp(continue_host_address, host->address) != 0
- ) )
- {
- expired = FALSE;
- continue; /* With next host */
- }
+ if (continue_hostname)
+ if ( Ustrcmp(continue_hostname, host->name) != 0
+ || Ustrcmp(continue_host_address, host->address) != 0
+ )
+ {
+ expired = FALSE;
+ continue; /* With next host */
+ }
+ else
+ continue_host_tried = TRUE;
/* Reset the default next host in case a multihomed host whose addresses
are not looked up till just above added to the host list. */
}
} /* End of loop for trying multiple hosts. */
+ /* If we failed to find a matching host in the list, for an already-open
+ connection, just close it and start over with the list. This can happen
+ for routing that changes from run to run, or big multi-IP sites with
+ round-robin DNS. */
+
+ if (continue_hostname && !continue_host_tried)
+ {
+ int fd = cutthrough.fd >= 0 ? cutthrough.fd : 0;
+
+ DEBUG(D_transport) debug_printf("no hosts match already-open connection\n");
+#ifdef SUPPORT_TLS
+ if (tls_out.active == fd)
+ {
+ (void) tls_write(FALSE, US"QUIT\r\n", 6, FALSE);
+ tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
+ }
+ else
+#else
+ (void) write(fd, US"QUIT\r\n", 6);
+#endif
+ (void) close(fd);
+ cutthrough.fd = -1;
+ continue_hostname = NULL;
+ goto retry_non_continued;
+ }
+
/* This is the end of the loop that repeats iff expired is TRUE and
ob->delay_after_cutoff is FALSE. The second time round we will
try those hosts that haven't been tried since the message arrived. */