X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/e3e281ccf9d8777d0df98ddd644720573e0343d1..3d040d098384c48be39e47862d55cac1bc0c578c:/src/src/transports/smtp.c diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 6f4e7ab71..f9e319c79 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -678,7 +678,8 @@ deliver_localpart = addr->local_part; : string_copy(addr->message) : addr->basic_errno > 0 ? string_copy(US strerror(addr->basic_errno)) - : NULL); + : NULL, + NULL); deliver_localpart = save_local; deliver_domain = save_domain; @@ -716,9 +717,11 @@ BOOL good_response; { /* Hack to get QUICKACK disabled; has to be right after 3whs, and has to on->off */ int sock = sx->cctx.sock; struct pollfd p = {.fd = sock, .events = POLLOUT}; - int rc = poll(&p, 1, 1000); - (void) setsockopt(sock, IPPROTO_TCP, TCP_QUICKACK, US &on, sizeof(on)); - (void) setsockopt(sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off)); + if (poll(&p, 1, 1000) >= 0) /* retval test solely for compiler quitening */ + { + (void) setsockopt(sock, IPPROTO_TCP, TCP_QUICKACK, US &on, sizeof(on)); + (void) setsockopt(sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off)); + } } #endif good_response = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), @@ -749,7 +752,7 @@ 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); + US"smtp:ehlo", sx->buffer, NULL); #endif return TRUE; } @@ -1430,10 +1433,17 @@ smtp_transport_options_block * ob = sx->conn_args.ob; /* transport options */ host_item * host = sx->conn_args.host; /* host to deliver to */ int rc; +/* Set up globals for error messages */ + +authenticator_name = au->name; +driver_srcfile = au->srcfile; +driver_srcline = au->srcline; + sx->outblock.authenticating = TRUE; rc = (au->info->clientcode)(au, sx, ob->command_timeout, sx->buffer, sizeof(sx->buffer)); sx->outblock.authenticating = FALSE; +driver_srcfile = authenticator_name = NULL; driver_srcline = 0; DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n", au->name, rc); /* A temporary authentication failure must hold up delivery to @@ -1982,6 +1992,38 @@ return OK; +#ifdef SUPPORT_DANE +static int +check_force_dane_conn(smtp_context * sx, smtp_transport_options_block * ob) +{ +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, &sx->conn_args.tlsa_dnsa, sx->dane_required)) + { + case OK: sx->conn_args.dane = TRUE; + ob->tls_tempfail_tryclear = FALSE; /* force TLS */ + ob->tls_sni = sx->conn_args.host->name; /* force SNI */ + break; + case FAIL_FORCED: break; + default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, + string_sprintf("DANE error: tlsa lookup %s", + rc_to_string(rc)), + rc, FALSE, &sx->delivery_start); +# ifndef DISABLE_EVENT + (void) event_raise(sx->conn_args.tblock->event_action, + US"dane:fail", sx->dane_required + ? US"dane-required" : US"dnssec-invalid", + NULL); +# endif + return rc; + } +return OK; +} +#endif + + /************************************************* * Make connection for given message * *************************************************/ @@ -2012,44 +2054,31 @@ int yield = OK; uschar * tls_errstr; #endif +/* Many lines of clearing individual elements of *sx that used to +be here have been replaced by a full memset to zero (de41aff051). +There are two callers, this file and verify.c . Now we only set +up nonzero elements. */ + sx->conn_args.ob = ob; sx->lmtp = strcmpic(ob->protocol, US"lmtp") == 0; sx->smtps = strcmpic(ob->protocol, US"smtps") == 0; -/* sx->ok = FALSE; */ sx->send_rset = TRUE; sx->send_quit = TRUE; sx->setting_up = TRUE; sx->esmtp = TRUE; -/* sx->esmtp_sent = FALSE; */ -#ifdef SUPPORT_I18N -/* sx->utf8_needed = FALSE; */ -#endif sx->dsn_all_lasthop = TRUE; #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 -#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 = sx->pending_MAIL = FALSE; */ -#endif if ((sx->max_mail = sx->conn_args.tblock->connection_max_messages) == 0) sx->max_mail = 999999; if ((sx->max_rcpt = sx->conn_args.tblock->max_addresses) == 0) sx->max_rcpt = 999999; -/* sx->peer_offered = 0; */ -/* sx->avoid_option = 0; */ sx->igquotstr = US""; if (!sx->helo_data) sx->helo_data = ob->helo_data; -#ifdef EXPERIMENTAL_DSN_INFO -/* sx->smtp_greeting = NULL; */ -/* sx->helo_response = NULL; */ -#endif smtp_command = US"initial connection"; -/* sx->buffer[0] = '\0'; */ /* Set up the buffer for reading SMTP response packets. */ @@ -2063,9 +2092,6 @@ sx->inblock.ptrend = sx->inbuffer; sx->outblock.buffer = sx->outbuffer; sx->outblock.buffersize = sizeof(sx->outbuffer); sx->outblock.ptr = sx->outbuffer; -/* sx->outblock.cmd_count = 0; */ -/* sx->outblock.authenticating = FALSE; */ -/* sx->outblock.conn_args = NULL; */ /* Reset the parameters of a TLS session. */ @@ -2107,32 +2133,14 @@ if (continue_hostname && continue_proxy_cipher) const uschar * sni = US""; # ifdef SUPPORT_DANE - /* Check if the message will be DANE-verified; if so force its SNI */ + /* Check if the message will be DANE-verified; if so force TLS and its SNI */ tls_out.dane_verified = FALSE; smtp_port_for_connect(sx->conn_args.host, sx->port); if ( sx->conn_args.host->dnssec == DS_YES - && ( 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, &sx->conn_args.tlsa_dnsa, sx->dane_required)) - { - case OK: sx->conn_args.dane = TRUE; - ob->tls_tempfail_tryclear = FALSE; /* force TLS */ - ob->tls_sni = sx->conn_args.host->name; /* force SNI */ - break; - case FAIL_FORCED: break; - default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, - string_sprintf("DANE error: tlsa lookup %s", - rc_to_string(rc)), - rc, FALSE, &sx->delivery_start); -# ifndef DISABLE_EVENT - (void) event_raise(sx->conn_args.tblock->event_action, - US"dane:fail", sx->dane_required - ? US"dane-required" : US"dnssec-invalid"); -# endif - return rc; - } + && (rc = check_force_dane_conn(sx, ob)) != OK + ) + return rc; # endif /* If the SNI or the DANE status required for the new message differs from the @@ -2160,7 +2168,7 @@ if (continue_hostname && continue_proxy_cipher) DEBUG(D_transport) debug_printf("Closing proxied-TLS connection due to SNI mismatch\n"); - HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP>> QUIT\n"); + smtp_debug_cmd(US"QUIT", 0); write(0, "QUIT\r\n", 6); close(0); continue_hostname = continue_proxy_cipher = NULL; @@ -2199,27 +2207,8 @@ if (!continue_hostname) 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, &sx->conn_args.tlsa_dnsa, sx->dane_required)) - { - case OK: sx->conn_args.dane = TRUE; - ob->tls_tempfail_tryclear = FALSE; /* force TLS */ - ob->tls_sni = sx->conn_args.host->name; /* force SNI */ - break; - case FAIL_FORCED: break; - default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, - string_sprintf("DANE error: tlsa lookup %s", - rc_to_string(rc)), - rc, FALSE, &sx->delivery_start); -# ifndef DISABLE_EVENT - (void) event_raise(sx->conn_args.tblock->event_action, - US"dane:fail", sx->dane_required - ? US"dane-required" : US"dnssec-invalid"); -# endif - return rc; - } + if ((rc = check_force_dane_conn(sx, ob)) != OK) + return rc; } else if (sx->dane_required) { @@ -2228,7 +2217,7 @@ if (!continue_hostname) FAIL, FALSE, &sx->delivery_start); # ifndef DISABLE_EVENT (void) event_raise(sx->conn_args.tblock->event_action, - US"dane:fail", US"dane-required"); + US"dane:fail", US"dane-required", NULL); # endif return FAIL; } @@ -2243,6 +2232,9 @@ if (!continue_hostname) sx->peer_limit_mail = sx->peer_limit_rcpt = sx->peer_limit_rcptdom = #endif sx->avoid_option = sx->peer_offered = smtp_peer_options = 0; +#ifndef DISABLE_CLIENT_CMD_LOG + client_cmd_log = NULL; +#endif #ifndef DISABLE_PIPE_CONNECT if ( verify_check_given_host(CUSS &ob->hosts_pipe_connect, @@ -2252,6 +2244,7 @@ if (!continue_hostname) the helo string might use it avoid doing early-pipelining. */ if ( !sx->helo_data + || sx->conn_args.interface || !Ustrstr(sx->helo_data, "$sending_ip_address") || Ustrstr(sx->helo_data, "def:sending_ip_address") ) @@ -2267,11 +2260,14 @@ if (!continue_hostname) } } else DEBUG(D_transport) - debug_printf("helo needs $sending_ip_address\n"); + debug_printf("helo needs $sending_ip_address; avoid early-pipelining\n"); PIPE_CONNECT_RETRY: if (sx->early_pipe_active) + { sx->outblock.conn_args = &sx->conn_args; + (void) smtp_boundsock(&sx->conn_args); + } else #endif { @@ -2296,9 +2292,10 @@ PIPE_CONNECT_RETRY: } /* Expand the greeting message while waiting for the initial response. (Makes sense if helo_data contains ${lookup dnsdb ...} stuff). The expansion is - delayed till here so that $sending_interface and $sending_port are set. */ -/*XXX early-pipe: they still will not be. Is there any way to find out what they -will be? Somehow I doubt it. */ + delayed till here so that $sending_ip_address and $sending_port are set. + Those will be known even for a TFO lazy-connect, having been set by the bind(). + For early-pipe, we are ok if binding to a local interface; otherwise (if + $sending_ip_address is seen in helo_data) we disabled early-pipe above. */ if (sx->helo_data) if (!(sx->helo_data = expand_string(sx->helo_data))) @@ -2347,7 +2344,7 @@ will be? Somehow I doubt it. */ uschar * s; lookup_dnssec_authenticated = sx->conn_args.host->dnssec==DS_YES ? US"yes" : sx->conn_args.host->dnssec==DS_NO ? US"no" : NULL; - s = event_raise(sx->conn_args.tblock->event_action, US"smtp:connect", sx->buffer); + s = event_raise(sx->conn_args.tblock->event_action, US"smtp:connect", sx->buffer, NULL); if (s) { set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, @@ -2668,6 +2665,7 @@ if ( smtp_peer_options & OPTION_TLS else TLS_NEGOTIATE: { + sx->conn_args.sending_ip_address = sending_ip_address; 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 @@ -2684,7 +2682,7 @@ if ( smtp_peer_options & OPTION_TLS 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 */ + US"dane:fail", US"validation-failure", NULL); /* could do with better detail */ # endif } # endif @@ -2852,7 +2850,8 @@ else if ( sx->smtps (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 */ - : US"starttls-not-supported"); + : US"starttls-not-supported", + NULL); # endif goto TLS_FAILED; } @@ -2934,18 +2933,18 @@ if ( !continue_hostname smtp_peer_options & OPTION_PIPE ? "" : "not "); if ( sx->peer_offered & OPTION_CHUNKING - && verify_check_given_host(CUSS &ob->hosts_try_chunking, sx->conn_args.host) != OK) - sx->peer_offered &= ~OPTION_CHUNKING; + && verify_check_given_host(CUSS &ob->hosts_try_chunking, sx->conn_args.host) == OK) + smtp_peer_options |= OPTION_CHUNKING; - if (sx->peer_offered & OPTION_CHUNKING) + if (smtp_peer_options & OPTION_CHUNKING) DEBUG(D_transport) debug_printf("CHUNKING usable\n"); #ifndef DISABLE_PRDR if ( sx->peer_offered & OPTION_PRDR - && verify_check_given_host(CUSS &ob->hosts_try_prdr, sx->conn_args.host) != OK) - sx->peer_offered &= ~OPTION_PRDR; + && verify_check_given_host(CUSS &ob->hosts_try_prdr, sx->conn_args.host) == OK) + smtp_peer_options |= OPTION_PRDR; - if (sx->peer_offered & OPTION_PRDR) + if (smtp_peer_options & OPTION_PRDR) DEBUG(D_transport) debug_printf("PRDR usable\n"); #endif @@ -3170,9 +3169,10 @@ if (sx->send_quit) sx->cctx.sock = -1; #ifndef DISABLE_EVENT -(void) event_raise(sx->conn_args.tblock->event_action, US"tcp:close", NULL); +(void) event_raise(sx->conn_args.tblock->event_action, US"tcp:close", NULL, NULL); #endif +smtp_debug_cmd_report(); continue_transport = NULL; continue_hostname = NULL; return yield; @@ -3215,7 +3215,7 @@ Or just forget about lines? Or inflate by a fixed proportion? */ request that */ sx->prdr_active = FALSE; -if (sx->peer_offered & OPTION_PRDR) +if (smtp_peer_options & OPTION_PRDR) for (address_item * addr = addrlist; addr; addr = addr->next) if (addr->transport_return == PENDING_DEFER) { @@ -3540,16 +3540,17 @@ Arguments: bufsiz size of buffer pfd pipe filedescriptor array; [0] is comms to proxied process timeout per-read timeout, seconds + host hostname of remote Does not return. */ void smtp_proxy_tls(void * ct_ctx, uschar * buf, size_t bsize, int * pfd, - int timeout) + int timeout, const uschar * host) { -fd_set rfds, efds; -int max_fd = MAX(pfd[0], tls_out.active.sock) + 1; +struct pollfd p[2] = {{.fd = tls_out.active.sock, .events = POLLIN}, + {.fd = pfd[0], .events = POLLIN}}; int rc, i; BOOL send_tls_shutdown = TRUE; @@ -3557,24 +3558,17 @@ close(pfd[1]); if ((rc = exim_fork(US"tls-proxy"))) _exit(rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS); -set_process_info("proxying TLS connection for continued transport"); -FD_ZERO(&rfds); -FD_SET(tls_out.active.sock, &rfds); -FD_SET(pfd[0], &rfds); +set_process_info("proxying TLS connection for continued transport to %s\n", host); -for (int fd_bits = 3; fd_bits; ) +do { time_t time_left = timeout; time_t time_start = time(NULL); /* wait for data */ - efds = rfds; do { - struct timeval tv = { time_left, 0 }; - - rc = select(max_fd, - (SELECT_ARG2_TYPE *)&rfds, NULL, (SELECT_ARG2_TYPE *)&efds, &tv); + rc = poll(p, 2, time_left * 1000); if (rc < 0 && errno == EINTR) if ((time_left -= time(NULL) - time_start) > 0) continue; @@ -3587,23 +3581,22 @@ for (int fd_bits = 3; fd_bits; ) /* For errors where not readable, bomb out */ - if (FD_ISSET(tls_out.active.sock, &efds) || FD_ISSET(pfd[0], &efds)) + if (p[0].revents & POLLERR || p[1].revents & POLLERR) { DEBUG(D_transport) debug_printf("select: exceptional cond on %s fd\n", - FD_ISSET(pfd[0], &efds) ? "proxy" : "tls"); - if (!(FD_ISSET(tls_out.active.sock, &rfds) || FD_ISSET(pfd[0], &rfds))) + p[0].revents & POLLERR ? "tls" : "proxy"); + if (!(p[0].revents & POLLIN || p[1].events & POLLIN)) goto done; DEBUG(D_transport) debug_printf("- but also readable; no exit yet\n"); } } - while (rc < 0 || !(FD_ISSET(tls_out.active.sock, &rfds) || FD_ISSET(pfd[0], &rfds))); + while (rc < 0 || !(p[0].revents & POLLIN || p[1].revents & POLLIN)); /* handle inbound data */ - if (FD_ISSET(tls_out.active.sock, &rfds)) + if (p[0].revents & POLLIN) if ((rc = tls_read(ct_ctx, buf, bsize)) <= 0) /* Expect -1 for EOF; */ { /* that reaps the TLS Close Notify record */ - fd_bits &= ~1; - FD_CLR(tls_out.active.sock, &rfds); + p[0].fd = -1; shutdown(pfd[0], SHUT_WR); timeout = 5; } @@ -3614,11 +3607,10 @@ for (int fd_bits = 3; fd_bits; ) /* Handle outbound data. We cannot combine payload and the TLS-close due to the limitations of the (pipe) channel feeding us. Maybe use a unix-domain socket? */ - if (FD_ISSET(pfd[0], &rfds)) + if (p[1].revents & POLLIN) if ((rc = read(pfd[0], buf, bsize)) <= 0) { - fd_bits &= ~2; - FD_CLR(pfd[0], &rfds); + p[1].fd = -1; # ifdef EXIM_TCP_CORK /* Use _CORK to get TLS Close Notify in FIN segment */ (void) setsockopt(tls_out.active.sock, IPPROTO_TCP, EXIM_TCP_CORK, US &on, sizeof(on)); @@ -3631,10 +3623,8 @@ for (int fd_bits = 3; fd_bits; ) for (int nbytes = 0; rc - nbytes > 0; nbytes += i) if ((i = tls_write(ct_ctx, buf + nbytes, rc - nbytes, FALSE)) < 0) goto done; - - if (fd_bits & 1) FD_SET(tls_out.active.sock, &rfds); - if (fd_bits & 2) FD_SET(pfd[0], &rfds); } +while (p[0].fd >= 0 || p[1].fd >= 0); done: if (send_tls_shutdown) tls_close(ct_ctx, TLS_SHUTDOWN_NOWAIT); @@ -3706,7 +3696,7 @@ int rc; uschar *message = NULL; uschar new_message_id[MESSAGE_ID_LENGTH + 1]; -smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted, for the data buffers */ +smtp_context * sx = store_get(sizeof(*sx), GET_TAINTED); /* tainted, for the data buffers */ BOOL pass_message = FALSE; #ifdef EXPERIMENTAL_ESMTP_LIMITS BOOL mail_limit = FALSE; @@ -3721,12 +3711,12 @@ BOOL tcw_done = FALSE, tcw = FALSE; memset(sx, 0, sizeof(*sx)); sx->addrlist = addrlist; sx->conn_args.host = host; -sx->conn_args.host_af = host_af, +sx->conn_args.host_af = host_af; sx->port = defport; sx->conn_args.interface = interface; sx->helo_data = NULL; sx->conn_args.tblock = tblock; -/* sx->verify = FALSE; */ +sx->conn_args.sock = -1; gettimeofday(&sx->delivery_start, NULL); sx->sync_addr = sx->first_addr = addrlist; @@ -3776,8 +3766,8 @@ if (tblock->filter_command) yield ERROR. */ if (!transport_set_up_command(&transport_filter_argv, - tblock->filter_command, TRUE, DEFER, addrlist, - string_sprintf("%.50s transport", tblock->name), NULL)) + tblock->filter_command, TRUE, DEFER, addrlist, FALSE, + string_sprintf("%.50s transport filter", tblock->name), NULL)) { set_errno_nohost(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER, FALSE, &sx->delivery_start); @@ -3788,7 +3778,7 @@ if (tblock->filter_command) if ( transport_filter_argv && *transport_filter_argv && **transport_filter_argv - && sx->peer_offered & OPTION_CHUNKING + && smtp_peer_options & 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) @@ -3796,7 +3786,7 @@ if (tblock->filter_command) #endif ) { - sx->peer_offered &= ~OPTION_CHUNKING; + smtp_peer_options &= ~OPTION_CHUNKING; DEBUG(D_transport) debug_printf("CHUNKING not usable due to transport filter\n"); } } @@ -3873,7 +3863,7 @@ are pipelining. The responses are all handled by sync_responses(). If using CHUNKING, do not send a BDAT until we know how big a chunk we want to send is. */ -if ( !(sx->peer_offered & OPTION_CHUNKING) +if ( !(smtp_peer_options & OPTION_CHUNKING) && (sx->ok || (pipelining_active && !mua_wrapper))) { int count = smtp_write_command(sx, SCMD_FLUSH, "DATA\r\n"); @@ -3910,7 +3900,7 @@ well as body. Set the appropriate timeout value to be used for each chunk. (Haven't been able to make it work using select() for writing yet.) */ if ( !sx->ok - && (!(sx->peer_offered & OPTION_CHUNKING) || !pipelining_active)) + && (!(smtp_peer_options & OPTION_CHUNKING) || !pipelining_active)) { /* Save the first address of the next batch. */ sx->first_addr = sx->next_addr; @@ -3939,7 +3929,7 @@ else of responses. The callback needs a whole bunch of state so set up a transport-context structure to be passed around. */ - if (sx->peer_offered & OPTION_CHUNKING) + if (smtp_peer_options & OPTION_CHUNKING) { tctx.check_string = tctx.escape_string = NULL; tctx.options |= topt_use_bdat; @@ -3964,7 +3954,7 @@ else transport_write_timeout = ob->data_timeout; smtp_command = US"sending data block"; /* For error messages */ DEBUG(D_transport|D_v) - if (sx->peer_offered & OPTION_CHUNKING) + if (smtp_peer_options & OPTION_CHUNKING) debug_printf(" will write message using CHUNKING\n"); else debug_printf(" SMTP>> writing message and terminating \".\"\n"); @@ -4016,7 +4006,7 @@ else If we can, we want the message-write to not flush (the tail end of) its data out. */ if ( sx->pipelining_used - && (sx->ok && sx->completed_addr || sx->peer_offered & OPTION_CHUNKING) + && (sx->ok && sx->completed_addr || smtp_peer_options & OPTION_CHUNKING) && sx->send_quit && !(sx->first_addr || f.continue_more) && f.deliver_firsttime @@ -4100,7 +4090,7 @@ else sx->send_quit = FALSE; /* avoid sending it later */ #ifndef DISABLE_TLS - if (sx->cctx.tls_ctx) /* need to send TLS Close Notify */ + if (sx->cctx.tls_ctx && sx->send_tlsclose) /* need to send TLS Close Notify */ { # ifdef EXIM_TCP_CORK /* Use _CORK to get Close Notify in FIN segment */ (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, EXIM_TCP_CORK, US &on, sizeof(on)); @@ -4114,7 +4104,7 @@ else } } - if (sx->peer_offered & OPTION_CHUNKING && sx->cmd_count > 1) + if (smtp_peer_options & OPTION_CHUNKING && sx->cmd_count > 1) { /* Reap any outstanding MAIL & RCPT commands, but not a DATA-go-ahead */ switch(sync_responses(sx, sx->cmd_count-1, 0)) @@ -4287,7 +4277,7 @@ else #ifndef DISABLE_PRDR if (sx->prdr_active) setflag(addr, af_prdr_used); #endif - if (sx->peer_offered & OPTION_CHUNKING) setflag(addr, af_chunking_used); + if (smtp_peer_options & OPTION_CHUNKING) setflag(addr, af_chunking_used); flag = '-'; #ifndef DISABLE_PRDR @@ -4358,6 +4348,7 @@ else "%s: %s", sx->buffer, strerror(errno)); } else if (addr->transport_return == DEFER) + /*XXX magic value -2 ? maybe host+message ? */ retry_add_item(addr, addr->address_retry_key, -2); } #endif @@ -4443,7 +4434,8 @@ if (!sx->ok) # ifndef DISABLE_TLS if (sx->cctx.tls_ctx) { - tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_WAIT); + tls_close(sx->cctx.tls_ctx, + sx->send_tlsclose ? TLS_SHUTDOWN_WAIT : TLS_SHUTDOWN_WONLY); sx->cctx.tls_ctx = NULL; } # endif @@ -4654,7 +4646,8 @@ 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(sx->cctx.tls_ctx, TLS_SHUTDOWN_WAIT); + tls_close(sx->cctx.tls_ctx, + sx->send_tlsclose ? TLS_SHUTDOWN_WAIT : TLS_SHUTDOWN_WONLY); sx->send_tlsclose = FALSE; sx->cctx.tls_ctx = NULL; tls_out.active.sock = -1; @@ -4716,7 +4709,7 @@ if (sx->completed_addr && sx->ok && sx->send_quit) { /* does not return */ smtp_proxy_tls(sx->cctx.tls_ctx, sx->buffer, sizeof(sx->buffer), pfd, - ob->command_timeout); + ob->command_timeout, host->name); } if (pid > 0) /* parent */ @@ -4756,7 +4749,7 @@ if (sx->send_quit) { /* Use _MORE to get QUIT in FIN segment */ (void)smtp_write_command(sx, SCMD_MORE, "QUIT\r\n"); #ifndef DISABLE_TLS - if (sx->cctx.tls_ctx) + if (sx->cctx.tls_ctx && sx->send_tlsclose) { # ifdef EXIM_TCP_CORK /* Use _CORK to get TLS Close Notify in FIN segment */ (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, EXIM_TCP_CORK, US &on, sizeof(on)); @@ -4811,15 +4804,23 @@ if (sx->send_quit || tcw_done && !tcw) while (!sigalrm_seen && n > 0); ALARM_CLR(0); + if (sx->send_tlsclose) + { # ifdef EXIM_TCP_CORK - (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, EXIM_TCP_CORK, US &on, sizeof(on)); + (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, EXIM_TCP_CORK, US &on, sizeof(on)); # endif - tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_WAIT); + tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_WAIT); + } + else + tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_WONLY); sx->cctx.tls_ctx = NULL; } #endif - millisleep(20); - if (fcntl(sx->cctx.sock, F_SETFL, O_NONBLOCK) == 0) + + /* Drain any trailing data from the socket before close, to avoid sending a RST */ + + if ( poll_one_fd(sx->cctx.sock, POLLIN, 20) != 0 /* 20ms */ + && fcntl(sx->cctx.sock, F_SETFL, O_NONBLOCK) == 0) for (int i = 16, n; /* drain socket */ (n = read(sx->cctx.sock, sx->inbuffer, sizeof(sx->inbuffer))) > 0 && i > 0; i--) HDEBUG(D_transport|D_acl|D_v) @@ -4835,9 +4836,10 @@ HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n"); sx->cctx.sock = -1; continue_transport = NULL; continue_hostname = NULL; +smtp_debug_cmd_report(); #ifndef DISABLE_EVENT -(void) event_raise(tblock->event_action, US"tcp:close", NULL); +(void) event_raise(tblock->event_action, US"tcp:close", NULL, NULL); #endif #ifdef SUPPORT_DANE @@ -5108,8 +5110,11 @@ if (!hostlist || (ob->hosts_override && ob->hosts)) else if (ob->hosts_randomize) s = expanded_hosts = string_copy(s); - if (is_tainted2(s, LOG_MAIN|LOG_PANIC, "Tainted host list '%s' from '%s' in transport %s", s, ob->hosts, tblock->name)) + 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; @@ -5606,7 +5611,7 @@ retry_non_continued: if (expanded_hosts) { - thost = store_get(sizeof(host_item), FALSE); + thost = store_get(sizeof(host_item), GET_UNTAINTED); *thost = *host; thost->name = string_copy(host->name); thost->address = string_copy(host->address);