X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Ftransports%2Fsmtp.c;h=23083f5d879c829a4ed5d7d0ff2a851fa696fb5d;hp=5f9d4313c1515c162e3903449cabd847fc54a384;hb=587d831d9428c40f108ac91c1e061096b17a7a3e;hpb=d097cc730a1ab358bad80338b30b49287d1274c3 diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 5f9d4313c..23083f5d8 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -2,7 +2,7 @@ * 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" @@ -24,6 +24,10 @@ optionlist smtp_transport_options[] = { (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, @@ -105,7 +109,7 @@ optionlist smtp_transport_options[] = { { "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 @@ -120,7 +124,7 @@ optionlist smtp_transport_options[] = { (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 @@ -206,10 +210,9 @@ void smtp_transport_closedown(transport_instance *tblock) {} 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, @@ -219,7 +222,7 @@ smtp_transport_options_block smtp_transport_option_defaults = { .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, #endif @@ -287,7 +290,15 @@ smtp_transport_options_block smtp_transport_option_defaults = { .dkim_sign_headers = NULL, .dkim_strict = NULL, .dkim_hash = US"sha256", - .dot_stuffed = FALSE}, + .dot_stuffed = FALSE, + .force_bodyhash = FALSE, +# ifdef EXPERIMENTAL_ARC + .arc_signspec = NULL, +# endif + }, +# ifdef EXPERIMENTAL_ARC + .arc_sign = NULL, +# endif #endif }; @@ -623,34 +634,34 @@ return FALSE; /* This writes to the main log and to the message log. Arguments: - addr the address item containing error information host the current host + detail the current message (addr_item->message) + basic_errno the errno (addr_item->basic_errno) Returns: nothing */ static void -write_logs(address_item *addr, host_item *host) +write_logs(const host_item *host, const uschar *suffix, int basic_errno) { -uschar * message = LOGGING(outgoing_port) + + +uschar *message = LOGGING(outgoing_port) ? string_sprintf("H=%s [%s]:%d", host->name, host->address, host->port == PORT_NONE ? 25 : host->port) : string_sprintf("H=%s [%s]", host->name, host->address); -if (addr->message) +if (suffix) { - message = string_sprintf("%s: %s", message, addr->message); - if (addr->basic_errno > 0) - message = string_sprintf("%s: %s", message, strerror(addr->basic_errno)); - log_write(0, LOG_MAIN, "%s", message); - deliver_msglog("%s %s\n", tod_stamp(tod_log), message); + message = string_sprintf("%s: %s", message, suffix); + if (basic_errno > 0) + message = string_sprintf("%s: %s", message, strerror(basic_errno)); } else - { - const uschar * s = exim_errstr(addr->basic_errno); - log_write(0, LOG_MAIN, "%s %s", message, s); - deliver_msglog("%s %s %s\n", tod_stamp(tod_log), message, s); - } + message = string_sprintf("%s %s", message, exim_errstr(basic_errno)); + +log_write(0, LOG_MAIN, "%s", message); +deliver_msglog("%s %s\n", tod_stamp(tod_log), message); } static void @@ -1190,7 +1201,7 @@ return FALSE; -#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 @@ -1217,14 +1228,15 @@ DEBUG(D_transport) 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; @@ -1489,7 +1501,7 @@ Returns: OK - the connection was made and the delivery attempted; 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; @@ -1511,7 +1523,7 @@ sx->esmtp_sent = 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 @@ -1585,7 +1597,7 @@ if (!continue_hostname) 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; @@ -1606,6 +1618,9 @@ if (!continue_hostname) 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; } } @@ -1614,6 +1629,8 @@ if (!continue_hostname) 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; } } @@ -1935,7 +1952,7 @@ if ( smtp_peer_options & OPTION_TLS 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); @@ -1946,10 +1963,15 @@ if ( smtp_peer_options & OPTION_TLS 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; @@ -2033,7 +2055,7 @@ if (tls_out.active >= 0) 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 @@ -2043,6 +2065,13 @@ else if ( sx->smtps 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*/ @@ -2233,7 +2262,7 @@ if (sx->send_quit) (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 @@ -2607,6 +2636,7 @@ if ((rc = fork())) _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); @@ -2666,7 +2696,7 @@ for (fd_bits = 3; fd_bits; ) if ((rc = read(pfd[0], buf, bsize)) <= 0) { fd_bits = 0; - tls_close(FALSE, TRUE); + tls_close(FALSE, TLS_SHUTDOWN_NOWAIT); } else { @@ -2963,6 +2993,30 @@ 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); @@ -3165,7 +3219,7 @@ else else sprintf(CS sx.buffer, "%.500s\n", addr->unique); - DEBUG(D_deliver) debug_printf("S:journalling %s", sx.buffer); + DEBUG(D_deliver) debug_printf("S:journalling %s\n", sx.buffer); len = Ustrlen(CS sx.buffer); if (write(journal_fd, sx.buffer, len) != len) log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for " @@ -3176,8 +3230,11 @@ else #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) @@ -3193,7 +3250,14 @@ else 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) { @@ -3319,8 +3383,9 @@ if (!sx.ok) set_rc = DEFER; if (save_errno > 0) message = US string_sprintf("%s: %s", message, strerror(save_errno)); - if (host->next != NULL) log_write(0, LOG_MAIN, "%s", message); - msglog_line(host, message); + + write_logs(host, message, sx.first_addr ? sx.first_addr->basic_errno : 0); + *message_defer = TRUE; } } @@ -3455,7 +3520,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit) 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, @@ -3507,9 +3572,12 @@ propagate it from the initial { 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 */ { @@ -3517,7 +3585,7 @@ propagate it from the initial 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; @@ -3563,7 +3631,7 @@ if (sx.send_quit) (void)smtp_write_command(&sx.outblock, SCMD_FLUSH, "QUIT\r\n") 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 @@ -3727,7 +3795,7 @@ uschar *tid = string_sprintf("%s transport", tblock->name); 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) { @@ -3738,7 +3806,7 @@ 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", @@ -3919,7 +3987,9 @@ for (cutoff_retry = 0; { 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 @@ -3984,7 +4054,7 @@ for (cutoff_retry = 0; /* 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; @@ -4052,14 +4122,16 @@ for (cutoff_retry = 0; 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. */ @@ -4325,7 +4397,7 @@ for (cutoff_retry = 0; if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL && first_addr->basic_errno != ERRNO_TLSFAILURE) - write_logs(first_addr, host); + write_logs(host, first_addr->message, first_addr->basic_errno); #ifndef DISABLE_EVENT if (rc == DEFER) @@ -4355,7 +4427,7 @@ for (cutoff_retry = 0; rc = smtp_deliver(addrlist, thost, host_af, defport, interface, tblock, &message_defer, TRUE); if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL) - write_logs(first_addr, host); + write_logs(host, first_addr->message, first_addr->basic_errno); # ifndef DISABLE_EVENT if (rc == DEFER) deferred_event_raise(first_addr, host); @@ -4517,6 +4589,32 @@ for (cutoff_retry = 0; } } /* 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. */