X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/a1108b5118d32e969c5fe91b2110944f7483a7cb..5800e3234f2594639d82e5063d9c522c6a881d25:/src/src/transports/smtp.c diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 52940572b..e38ea1502 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -3,7 +3,7 @@ *************************************************/ /* Copyright (c) University of Cambridge 1995 - 2018 */ -/* Copyright (c) The Exim Maintainers 2020 */ +/* Copyright (c) The Exim Maintainers 2020 - 2021 */ /* See the file NOTICE for conditions of use and distribution. */ #include "../exim.h" @@ -84,6 +84,7 @@ optionlist smtp_transport_options[] = { #if !defined(DISABLE_TLS) && !defined(DISABLE_OCSP) { "hosts_request_ocsp", opt_stringptr, LOFF(hosts_request_ocsp) }, #endif + { "hosts_require_alpn", opt_stringptr, LOFF(hosts_require_alpn) }, { "hosts_require_auth", opt_stringptr, LOFF(hosts_require_auth) }, #ifndef DISABLE_TLS # ifdef SUPPORT_DANE @@ -123,6 +124,7 @@ optionlist smtp_transport_options[] = { { "socks_proxy", opt_stringptr, LOFF(socks_proxy) }, #endif #ifndef DISABLE_TLS + { "tls_alpn", opt_stringptr, LOFF(tls_alpn) }, { "tls_certificate", opt_stringptr, LOFF(tls_certificate) }, { "tls_crl", opt_stringptr, LOFF(tls_crl) }, { "tls_dh_min_bits", opt_int, LOFF(tls_dh_min_bits) }, @@ -192,9 +194,7 @@ smtp_transport_options_block smtp_transport_option_defaults = { .keepalive = TRUE, .retry_include_ip_address = TRUE, #ifndef DISABLE_TLS -# if defined(SUPPORT_SYSDEFAULT_CABUNDLE) || !defined(USE_GNUTLS) .tls_verify_certificates = US"system", -# endif .tls_dh_min_bits = EXIM_CLIENT_DH_DEFAULT_MIN_BITS, .tls_tempfail_tryclear = TRUE, .tls_try_verify_hosts = US"*", @@ -237,48 +237,39 @@ 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); +struct list + { + const pcre2_code ** re; + const uschar * string; + } list[] = + { + { ®ex_AUTH, AUTHS_REGEX }, + { ®ex_CHUNKING, US"\\n250[\\s\\-]CHUNKING(\\s|\\n|$)" }, + { ®ex_DSN, US"\\n250[\\s\\-]DSN(\\s|\\n|$)" }, + { ®ex_IGNOREQUOTA, US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)" }, + { ®ex_PIPELINING, US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)" }, + { ®ex_SIZE, US"\\n250[\\s\\-]SIZE(\\s|\\n|$)" }, #ifndef DISABLE_TLS -if (!regex_STARTTLS) regex_STARTTLS = - regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE); + { ®ex_STARTTLS, US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)" }, #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); + { ®ex_PRDR, US"\\n250[\\s\\-]PRDR(\\s|\\n|$)" }, #endif - #ifdef SUPPORT_I18N -if (!regex_UTF8) regex_UTF8 = - regex_must_compile(US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE); + { ®ex_UTF8, US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)" }, #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); + { ®ex_EARLY_PIPE, US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)" }, #endif - #ifdef EXPERIMENTAL_ESMTP_LIMITS -if (!regex_LIMITS) regex_LIMITS = - regex_must_compile(US"\\n250[\\s\\-]LIMITS\\s", FALSE, TRUE); + { ®ex_LIMITS, US"\\n250[\\s\\-]LIMITS\\s" }, #endif + }; + +for (struct list * l = list; l < list + nelem(list); l++) + if (!*l->re) + *l->re = regex_must_compile(l->string, FALSE, TRUE); } @@ -355,6 +346,7 @@ void smtp_transport_init(transport_instance *tblock) { smtp_transport_options_block *ob = SOB tblock->options_block; +int old_pool = store_pool; /* Retry_use_local_part defaults FALSE if unset */ @@ -390,7 +382,9 @@ if (ob->hosts_override && ob->hosts) tblock->overrides_hosts = TRUE; /* If there are any fallback hosts listed, build a chain of host items for them, but do not do any lookups at this time. */ -host_build_hostlist(&(ob->fallback_hostlist), ob->fallback_hosts, FALSE); +store_pool = POOL_PERM; +host_build_hostlist(&ob->fallback_hostlist, ob->fallback_hosts, FALSE); +store_pool = old_pool; } @@ -684,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; @@ -722,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), @@ -755,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; } @@ -774,13 +771,12 @@ This saves us dealing with a duplicate set of values. */ static void ehlo_response_limits_read(smtp_context * sx) { -int ovec[3]; /* results vector for a main-match only */ +uschar * match; /* matches up to just after the first space after the keyword */ -if (pcre_exec(regex_LIMITS, NULL, CS sx->buffer, Ustrlen(sx->buffer), - 0, PCRE_EOPT, ovec, nelem(ovec)) >= 0) - for (const uschar * s = sx->buffer + ovec[1]; *s; ) +if (regex_match(regex_LIMITS, sx->buffer, -1, &match)) + for (const uschar * s = sx->buffer + Ustrlen(match); *s; ) { while (isspace(*s)) s++; if (*s == '\n') break; @@ -853,9 +849,9 @@ return Ustrchr(host->address, ':') /* Cache EHLO-response info for use by early-pipe. Called - During a normal flow on EHLO response (either cleartext or under TLS), - when we are willing to do PIPE_CONNECT and it is offered + when we are willing to do PIPECONNECT and it is offered - During an early-pipe flow on receiving the actual EHLO response and noting - disparity versus the cached info used, when PIPE_CONNECT is still being offered + disparity versus the cached info used, when PIPECONNECT is still being offered We assume that suitable values have been set in the sx.ehlo_resp structure for features and auths; we handle the copy of limits. */ @@ -1437,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 @@ -1806,57 +1809,65 @@ return Ustrcmp(current_local_identity, message_local_identity) == 0; static unsigned ehlo_response(uschar * buf, unsigned checks) { -size_t bsize = Ustrlen(buf); +PCRE2_SIZE bsize = Ustrlen(buf); +pcre2_match_data * md = pcre2_match_data_create(1, pcre_gen_ctx); /* debug_printf("%s: check for 0x%04x\n", __FUNCTION__, checks); */ #ifndef DISABLE_TLS if ( checks & OPTION_TLS - && pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) + && pcre2_match(regex_STARTTLS, + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) #endif checks &= ~OPTION_TLS; if ( checks & OPTION_IGNQ - && pcre_exec(regex_IGNOREQUOTA, NULL, CS buf, bsize, 0, - PCRE_EOPT, NULL, 0) < 0) + && pcre2_match(regex_IGNOREQUOTA, + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) checks &= ~OPTION_IGNQ; if ( checks & OPTION_CHUNKING - && pcre_exec(regex_CHUNKING, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) + && pcre2_match(regex_CHUNKING, + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) checks &= ~OPTION_CHUNKING; #ifndef DISABLE_PRDR if ( checks & OPTION_PRDR - && pcre_exec(regex_PRDR, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) + && pcre2_match(regex_PRDR, + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) #endif checks &= ~OPTION_PRDR; #ifdef SUPPORT_I18N if ( checks & OPTION_UTF8 - && pcre_exec(regex_UTF8, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) + && pcre2_match(regex_UTF8, + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) #endif checks &= ~OPTION_UTF8; if ( checks & OPTION_DSN - && pcre_exec(regex_DSN, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) + && pcre2_match(regex_DSN, + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) checks &= ~OPTION_DSN; if ( checks & OPTION_PIPE - && pcre_exec(regex_PIPELINING, NULL, CS buf, bsize, 0, - PCRE_EOPT, NULL, 0) < 0) + && pcre2_match(regex_PIPELINING, + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) checks &= ~OPTION_PIPE; if ( checks & OPTION_SIZE - && pcre_exec(regex_SIZE, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) + && pcre2_match(regex_SIZE, + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) checks &= ~OPTION_SIZE; #ifndef DISABLE_PIPE_CONNECT if ( checks & OPTION_EARLY_PIPE - && pcre_exec(regex_EARLY_PIPE, NULL, CS buf, bsize, 0, - PCRE_EOPT, NULL, 0) < 0) + && pcre2_match(regex_EARLY_PIPE, + (PCRE2_SPTR)buf, bsize, 0, PCRE_EOPT, md, pcre_mtc_ctx) < 0) #endif checks &= ~OPTION_EARLY_PIPE; +pcre2_match_data_free(md); /* debug_printf("%s: found 0x%04x\n", __FUNCTION__, checks); */ return checks; } @@ -2011,44 +2022,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. */ @@ -2062,9 +2060,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. */ @@ -2118,7 +2113,7 @@ if (continue_hostname && continue_proxy_cipher) { case OK: sx->conn_args.dane = TRUE; ob->tls_tempfail_tryclear = FALSE; /* force TLS */ - ob->tls_sni = sx->first_addr->domain; /* force SNI */ + ob->tls_sni = sx->conn_args.host->name; /* force SNI */ break; case FAIL_FORCED: break; default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, @@ -2128,7 +2123,8 @@ if (continue_hostname && continue_proxy_cipher) # ifndef DISABLE_EVENT (void) event_raise(sx->conn_args.tblock->event_action, US"dane:fail", sx->dane_required - ? US"dane-required" : US"dnssec-invalid"); + ? US"dane-required" : US"dnssec-invalid", + NULL); # endif return rc; } @@ -2159,7 +2155,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; @@ -2205,7 +2201,7 @@ if (!continue_hostname) { case OK: sx->conn_args.dane = TRUE; ob->tls_tempfail_tryclear = FALSE; /* force TLS */ - ob->tls_sni = sx->first_addr->domain; /* force SNI */ + ob->tls_sni = sx->conn_args.host->name; /* force SNI */ break; case FAIL_FORCED: break; default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, @@ -2215,7 +2211,8 @@ if (!continue_hostname) # ifndef DISABLE_EVENT (void) event_raise(sx->conn_args.tblock->event_action, US"dane:fail", sx->dane_required - ? US"dane-required" : US"dnssec-invalid"); + ? US"dane-required" : US"dnssec-invalid", + NULL); # endif return rc; } @@ -2227,7 +2224,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; } @@ -2242,6 +2239,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, @@ -2260,7 +2260,7 @@ if (!continue_hostname) && sx->ehlo_resp.cleartext_features & OPTION_EARLY_PIPE) { DEBUG(D_transport) - debug_printf("Using cached cleartext PIPE_CONNECT\n"); + debug_printf("Using cached cleartext PIPECONNECT\n"); sx->early_pipe_active = TRUE; sx->peer_offered = sx->ehlo_resp.cleartext_features; } @@ -2346,7 +2346,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, @@ -2475,8 +2475,8 @@ goto SEND_QUIT; int n = sizeof(sx->buffer); uschar * rsp = sx->buffer; - if (sx->esmtp_sent && (n = Ustrlen(sx->buffer)) < sizeof(sx->buffer)/2) - { rsp = sx->buffer + n + 1; n = sizeof(sx->buffer) - n; } + if (sx->esmtp_sent && (n = Ustrlen(sx->buffer) + 1) < sizeof(sx->buffer)/2) + { rsp = sx->buffer + n; n = sizeof(sx->buffer) - n; } if (smtp_write_command(sx, SCMD_FLUSH, "HELO %s\r\n", sx->helo_data) < 0) goto SEND_FAILED; @@ -2683,7 +2683,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 @@ -2762,7 +2762,7 @@ if (tls_out.active.sock >= 0) sx->peer_offered = sx->ehlo_resp.crypted_features; if ((sx->early_pipe_active = !!(sx->ehlo_resp.crypted_features & OPTION_EARLY_PIPE))) - DEBUG(D_transport) debug_printf("Using cached crypted PIPE_CONNECT\n"); + DEBUG(D_transport) debug_printf("Using cached crypted PIPECONNECT\n"); } #endif #ifdef EXPERIMMENTAL_ESMTP_LIMITS @@ -2851,7 +2851,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; } @@ -2961,7 +2962,7 @@ if ( !continue_hostname && ( sx->ehlo_resp.cleartext_features | sx->ehlo_resp.crypted_features) & OPTION_EARLY_PIPE) { - DEBUG(D_transport) debug_printf("PIPE_CONNECT usable in future for this IP\n"); + DEBUG(D_transport) debug_printf("PIPECONNECT usable in future for this IP\n"); sx->ehlo_resp.crypted_auths = study_ehlo_auths(sx); write_ehlo_cache_entry(sx); } @@ -3076,7 +3077,7 @@ return OK; SEND_FAILED: code = '4'; - message = US string_sprintf("send() to %s [%s] failed: %s", + message = US string_sprintf("smtp send to %s [%s] failed: %s", sx->conn_args.host->name, sx->conn_args.host->address, strerror(errno)); sx->send_quit = FALSE; yield = DEFER; @@ -3169,9 +3170,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; @@ -3547,8 +3549,8 @@ void 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.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,23 +3559,16 @@ 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); -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; @@ -3586,39 +3581,36 @@ 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; } else - { for (int nbytes = 0; rc - nbytes > 0; nbytes += i) if ((i = write(pfd[0], buf + nbytes, rc - nbytes)) < 0) goto done; - } /* Handle outbound data. We cannot combine payload and the TLS-close - due to the limitations of the (pipe) channel feeding us. */ - if (FD_ISSET(pfd[0], &rfds)) + due to the limitations of the (pipe) channel feeding us. Maybe use a unix-domain + socket? */ + 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)); @@ -3628,15 +3620,11 @@ for (int fd_bits = 3; fd_bits; ) shutdown(tls_out.active.sock, SHUT_WR); } else - { 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); @@ -3708,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; @@ -3723,12 +3711,11 @@ 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; */ gettimeofday(&sx->delivery_start, NULL); sx->sync_addr = sx->first_addr = addrlist; @@ -3779,7 +3766,7 @@ if (tblock->filter_command) if (!transport_set_up_command(&transport_filter_argv, tblock->filter_command, TRUE, DEFER, addrlist, - string_sprintf("%.50s transport", tblock->name), NULL)) + string_sprintf("%.50s transport filter", tblock->name), NULL)) { set_errno_nohost(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER, FALSE, &sx->delivery_start); @@ -4102,7 +4089,7 @@ else sx->send_quit = FALSE; /* avoid sending it later */ #ifndef DISABLE_TLS - if (sx->cctx.tls_ctx) /* need to send TLS Cloe 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)); @@ -4360,6 +4347,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 @@ -4400,7 +4388,7 @@ if (!sx->ok) { save_errno = errno; code = '4'; - message = string_sprintf("send() to %s [%s] failed: %s", + message = string_sprintf("smtp send to %s [%s] failed: %s", host->name, host->address, message ? message : US strerror(save_errno)); sx->send_quit = FALSE; goto FAILED; @@ -4436,6 +4424,22 @@ if (!sx->ok) message_error = Ustrncmp(smtp_command,"end ",4) == 0; break; +#ifndef DISABLE_DKIM + case EACCES: + /* DKIM signing failure: avoid thinking we pipelined quit, + just abandon the message and close the socket. */ + + message_error = FALSE; +# ifndef DISABLE_TLS + if (sx->cctx.tls_ctx) + { + tls_close(sx->cctx.tls_ctx, + sx->send_tlsclose ? TLS_SHUTDOWN_WAIT : TLS_SHUTDOWN_WONLY); + sx->cctx.tls_ctx = NULL; + } +# endif + break; +#endif default: message_error = FALSE; break; @@ -4584,7 +4588,7 @@ if (sx->completed_addr && sx->ok && sx->send_quit) if (sx->send_rset) if (! (sx->ok = smtp_write_command(sx, SCMD_FLUSH, "RSET\r\n") >= 0)) { - msg = US string_sprintf("send() to %s [%s] failed: %s", host->name, + msg = US string_sprintf("smtp send to %s [%s] failed: %s", host->name, host->address, strerror(errno)); sx->send_quit = FALSE; } @@ -4641,7 +4645,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; @@ -4743,7 +4748,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)); @@ -4798,15 +4803,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) @@ -4822,9 +4835,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 @@ -5596,7 +5610,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);