X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/06f37c802cbe477b2db358a0e76388d2df84691f..b10c87b38c2345d15d30da5c18c823355ac506a9:/src/src/transports/smtp.c diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 96d694fcc..f76b4f730 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -183,6 +183,10 @@ optionlist smtp_transport_options[] = { (void *)offsetof(smtp_transport_options_block, tls_privatekey) }, { "tls_require_ciphers", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_require_ciphers) }, +# ifdef EXPERIMENTAL_TLS_RESUME + { "tls_resumption_hosts", opt_stringptr, + (void *)offsetof(smtp_transport_options_block, tls_resumption_hosts) }, +# endif { "tls_sni", opt_stringptr, (void *)offsetof(smtp_transport_options_block, tls_sni) }, { "tls_tempfail_tryclear", opt_bool, @@ -293,6 +297,9 @@ smtp_transport_options_block smtp_transport_option_defaults = { .tls_verify_certificates = US"system", .tls_dh_min_bits = EXIM_CLIENT_DH_DEFAULT_MIN_BITS, .tls_tempfail_tryclear = TRUE, +# ifdef EXPERIMENTAL_TLS_RESUME + .tls_resumption_hosts = NULL, +# endif .tls_verify_hosts = NULL, .tls_try_verify_hosts = US"*", .tls_verify_cert_hostnames = US"*", @@ -497,14 +504,13 @@ set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc, #endif ) { -address_item *addr; int orvalue = 0; if (errno_value == ERRNO_CONNECTTIMEOUT) { errno_value = ETIMEDOUT; orvalue = RTEF_CTOUT; } -for (addr = addrlist; addr; addr = addr->next) +for (address_item * addr = addrlist; addr; addr = addr->next) if (addr->transport_return >= PENDING) { addr->basic_errno = errno_value; @@ -594,6 +600,11 @@ switch(*errno_value) pl, smtp_command, s); return FALSE; + case ERRNO_TLSFAILURE: /* Handle bad first read; can happen with + GnuTLS and TLS1.3 */ + *message = US"bad first read from TLS conn"; + return TRUE; + case ERRNO_FILTER_FAIL: /* Handle a failed filter process error; can't send QUIT as we mustn't end the DATA. */ *message = string_sprintf("transport filter process failed (%d)%s", @@ -821,11 +832,15 @@ write_ehlo_cache_entry(const smtp_context * sx) { open_db dbblock, * dbm_file; -if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE))) +if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE))) { uschar * ehlo_resp_key = ehlo_cache_key(sx); dbdata_ehlo_resp er = { .data = sx->ehlo_resp }; + HDEBUG(D_transport) debug_printf("writing clr %04x/%04x cry %04x/%04x\n", + sx->ehlo_resp.cleartext_features, sx->ehlo_resp.cleartext_auths, + sx->ehlo_resp.crypted_features, sx->ehlo_resp.crypted_auths); + dbfn_write(dbm_file, ehlo_resp_key, &er, (int)sizeof(er)); dbfn_close(dbm_file); } @@ -837,7 +852,7 @@ invalidate_ehlo_cache_entry(smtp_context * sx) open_db dbblock, * dbm_file; if ( sx->early_pipe_active - && (dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE))) + && (dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE))) { uschar * ehlo_resp_key = ehlo_cache_key(sx); dbfn_delete(dbm_file, ehlo_resp_key); @@ -851,7 +866,7 @@ read_ehlo_cache_entry(smtp_context * sx) open_db dbblock; open_db * dbm_file; -if (!(dbm_file = dbfn_open(US"misc", O_RDONLY, &dbblock, FALSE))) +if (!(dbm_file = dbfn_open(US"misc", O_RDONLY, &dbblock, FALSE, TRUE))) { DEBUG(D_transport) debug_printf("ehlo-cache: no misc DB\n"); } else { @@ -864,7 +879,7 @@ else { DEBUG(D_transport) debug_printf("ehlo-resp record too old\n"); dbfn_close(dbm_file); - if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE))) + if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE))) dbfn_delete(dbm_file, ehlo_resp_key); } else @@ -938,6 +953,7 @@ Arguments: Return: OK all well + DEFER error on first read of TLS'd conn FAIL SMTP error in response */ int @@ -945,6 +961,7 @@ smtp_reap_early_pipe(smtp_context * sx, int * countp) { BOOL pending_BANNER = sx->pending_BANNER; BOOL pending_EHLO = sx->pending_EHLO; +int rc = FAIL; sx->pending_BANNER = FALSE; /* clear early to avoid recursion */ sx->pending_EHLO = FALSE; @@ -956,6 +973,7 @@ if (pending_BANNER) if (!smtp_reap_banner(sx)) { DEBUG(D_transport) debug_printf("bad banner\n"); + if (tls_out.active.sock >= 0) rc = DEFER; goto fail; } } @@ -970,6 +988,7 @@ if (pending_EHLO) if (!smtp_reap_ehlo(sx)) { DEBUG(D_transport) debug_printf("bad response for EHLO\n"); + if (tls_out.active.sock >= 0) rc = DEFER; goto fail; } @@ -980,7 +999,7 @@ if (pending_EHLO) ? &sx->ehlo_resp.cleartext_auths : &sx->ehlo_resp.crypted_auths; peer_offered = ehlo_response(sx->buffer, - (tls_out.active.sock < 0 ? OPTION_TLS : OPTION_REQUIRETLS) + (tls_out.active.sock < 0 ? OPTION_TLS : 0) | OPTION_CHUNKING | OPTION_PRDR | OPTION_DSN | OPTION_PIPE | OPTION_SIZE | OPTION_UTF8 | OPTION_EARLY_PIPE ); @@ -988,8 +1007,11 @@ if (pending_EHLO) || (authbits = study_ehlo_auths(sx)) != *ap) { HDEBUG(D_transport) - debug_printf("EHLO extensions changed, 0x%04x/0x%04x -> 0x%04x/0x%04x\n", + debug_printf("EHLO %s extensions changed, 0x%04x/0x%04x -> 0x%04x/0x%04x\n", + tls_out.active.sock < 0 ? "cleartext" : "crypted", sx->peer_offered, *ap, peer_offered, authbits); + *(tls_out.active.sock < 0 + ? &sx->ehlo_resp.cleartext_features : &sx->ehlo_resp.crypted_features) = peer_offered; *ap = authbits; if (peer_offered & OPTION_EARLY_PIPE) write_ehlo_cache_entry(sx); @@ -1004,7 +1026,7 @@ return OK; fail: invalidate_ehlo_cache_entry(sx); (void) smtp_discard_responses(sx, sx->conn_args.ob, *countp); - return FAIL; + return rc; } #endif @@ -1049,6 +1071,7 @@ Returns: 3 if at least one address had 2xx and one had 5xx -2 I/O or other non-response error for RCPT -3 DATA or MAIL failed - errno and buffer set -4 banner or EHLO failed (early-pipelining) + -5 banner or EHLO failed (early-pipelining, TLS) */ static int @@ -1059,8 +1082,9 @@ smtp_transport_options_block * ob = sx->conn_args.ob; int yield = 0; #ifdef EXPERIMENTAL_PIPE_CONNECT -if (smtp_reap_early_pipe(sx, &count) != OK) - return -4; +int rc; +if ((rc = smtp_reap_early_pipe(sx, &count)) != OK) + return rc == FAIL ? -4 : -5; #endif /* Handle the response for a MAIL command. On error, reinstate the original @@ -1076,6 +1100,8 @@ if (sx->pending_MAIL) { DEBUG(D_transport) debug_printf("bad response for MAIL\n"); Ustrcpy(big_buffer, mail_command); /* Fits, because it came from there! */ + if (errno == ERRNO_TLSFAILURE) + return -5; if (errno == 0 && sx->buffer[0] != 0) { int save_errno = 0; @@ -1134,6 +1160,11 @@ while (count-- > 0) } } + /* Error on first TLS read */ + + else if (errno == ERRNO_TLSFAILURE) + return -5; + /* Timeout while reading the response */ else if (errno == ETIMEDOUT) @@ -1246,6 +1277,10 @@ if (pending_DATA != 0) int code; uschar *msg; BOOL pass_message; + + if (errno == ERRNO_TLSFAILURE) /* Error on first TLS read */ + return -5; + if (pending_DATA > 0 || (yield & 1) != 0) { if (errno == 0 && sx->buffer[0] == '4') @@ -1399,8 +1434,6 @@ if ( sx->esmtp if ( require_auth == OK || verify_check_given_host(CUSS &ob->hosts_try_auth, host) == OK) { - auth_instance * au; - DEBUG(D_transport) debug_printf("scanning authentication mechanisms\n"); fail_reason = US"no common mechanisms were found"; @@ -1413,6 +1446,7 @@ if ( sx->esmtp client function. We are limited to supporting up to 16 authenticator public-names by the number of bits in a short. */ + auth_instance * au; uschar bitnum; int rc; @@ -1448,7 +1482,7 @@ if ( sx->esmtp If one is found, attempt to authenticate by calling its client function. */ - for (au = auths; !f.smtp_authenticated && au; au = au->next) + for (auth_instance * au = auths; !f.smtp_authenticated && au; au = au->next) { uschar *p = names; @@ -1601,8 +1635,7 @@ switch (rc) DEBUG(D_transport) { dns_scan dnss; - dns_record * rr; - for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; + for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == T_TLSA && rr->size > 3) { @@ -1722,12 +1755,6 @@ size_t bsize = Ustrlen(buf); /* debug_printf("%s: check for 0x%04x\n", __FUNCTION__, checks); */ #ifdef SUPPORT_TLS -# ifdef EXPERIMENTAL_REQUIRETLS -if ( checks & OPTION_REQUIRETLS - && pcre_exec(regex_REQUIRETLS, NULL, CS buf,bsize, 0, PCRE_EOPT, NULL,0) < 0) -# endif - checks &= ~OPTION_REQUIRETLS; - if ( checks & OPTION_TLS && pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0) #endif @@ -1795,7 +1822,9 @@ Args: tc_chunk_last add LAST option to SMTP BDAT command tc_reap_prev reap response to previous SMTP commands -Returns: OK or ERROR +Returns: + OK or ERROR + DEFER TLS error on first read (EHLO-resp); errno set */ static int @@ -1852,10 +1881,12 @@ if (flags & tc_reap_prev && prev_cmd_count > 0) case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */ case 0: break; /* No 2xx or 5xx, but no probs */ - case -1: /* Timeout on RCPT */ + case -5: errno = ERRNO_TLSFAILURE; + return DEFER; #ifdef EXPERIMENTAL_PIPE_CONNECT case -4: /* non-2xx for pipelined banner or EHLO */ #endif + case -1: /* Timeout on RCPT */ default: return ERROR; /* I/O error, or any MAIL/DATA error */ } cmd_count = 1; @@ -1918,14 +1949,14 @@ 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(SUPPORT_DANE) -dns_answer tlsa_dnsa; -#endif smtp_transport_options_block * ob = sx->conn_args.tblock->options_block; BOOL pass_message = FALSE; uschar * message = NULL; int yield = OK; int rc; +#ifdef SUPPORT_TLS +uschar * tls_errstr; +#endif sx->conn_args.ob = ob; @@ -1942,7 +1973,7 @@ sx->utf8_needed = FALSE; #endif sx->dsn_all_lasthop = TRUE; #if defined(SUPPORT_TLS) && defined(SUPPORT_DANE) -sx->dane = FALSE; +sx->conn_args.dane = FALSE; sx->dane_required = verify_check_given_host(CUSS &ob->hosts_require_dane, sx->conn_args.host) == OK; #endif @@ -1992,6 +2023,9 @@ tls_out.peerdn = NULL; tls_out.sni = NULL; #endif tls_out.ocsp = OCSP_NOT_REQ; +#ifdef EXPERIMENTAL_TLS_RESUME +tls_out.resumption = 0; +#endif /* Flip the legacy TLS-related variables over to the outbound set in case they're used in the context of the transport. Don't bother resetting @@ -2033,15 +2067,15 @@ if (!continue_hostname) 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, &tlsa_dnsa, sx->dane_required)) + switch (rc = tlsa_lookup(sx->conn_args.host, &sx->conn_args.tlsa_dnsa, sx->dane_required)) { - case OK: sx->dane = TRUE; + case OK: sx->conn_args.dane = TRUE; ob->tls_tempfail_tryclear = FALSE; break; case FAIL_FORCED: break; default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, string_sprintf("DANE error: tlsa lookup %s", - rc == DEFER ? "DEFER" : "FAIL"), + rc_to_string(rc)), rc, FALSE); # ifndef DISABLE_EVENT (void) event_raise(sx->conn_args.tblock->event_action, @@ -2466,28 +2500,22 @@ if ( smtp_peer_options & OPTION_TLS else TLS_NEGOTIATE: { - address_item * addr; - uschar * errstr; - sx->cctx.tls_ctx = tls_client_start(sx->cctx.sock, sx->conn_args.host, - sx->addrlist, sx->conn_args.tblock, -# ifdef SUPPORT_DANE - sx->dane ? &tlsa_dnsa : NULL, -# endif - &tls_out, &errstr); - - if (!sx->cctx.tls_ctx) + 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 be called again to try in clear on a new connection, if the options permit it for this host. */ - DEBUG(D_tls) debug_printf("TLS session fail: %s\n", errstr); +#ifdef USE_GNUTLS + GNUTLS_CONN_FAILED: +#endif + DEBUG(D_tls) debug_printf("TLS session fail: %s\n", tls_errstr); # ifdef SUPPORT_DANE - if (sx->dane) + if (sx->conn_args.dane) { log_write(0, LOG_MAIN, "DANE attempt failed; TLS connection to %s [%s]: %s", - sx->conn_args.host->name, sx->conn_args.host->address, errstr); + 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 */ @@ -2496,7 +2524,7 @@ if ( smtp_peer_options & OPTION_TLS # endif errno = ERRNO_TLSFAILURE; - message = string_sprintf("TLS session: %s", errstr); + message = string_sprintf("TLS session: %s", tls_errstr); sx->send_quit = FALSE; goto TLS_FAILED; } @@ -2504,7 +2532,7 @@ if ( smtp_peer_options & OPTION_TLS /* TLS session is set up */ smtp_peer_options_wrap = smtp_peer_options; - for (addr = sx->addrlist; addr; addr = addr->next) + for (address_item * addr = sx->addrlist; addr; addr = addr->next) if (addr->transport_return == PENDING_DEFER) { addr->cipher = tls_out.cipher; @@ -2594,7 +2622,22 @@ if (tls_out.active.sock >= 0) #endif { if (!smtp_reap_ehlo(sx)) +#ifdef USE_GNUTLS + { + /* The GnuTLS layer in Exim only spots a server-rejection of a client + cert late, under TLS1.3 - which means here; the first time we try to + receive crypted data. Treat it as if it was a connect-time failure. + See also the early-pipe equivalent... which will be hard; every call + to sync_responses will need to check the result. + It would be nicer to have GnuTLS check the cert during the handshake. + Can it do that, with all the flexibility we need? */ + + tls_errstr = US"error on first read"; + goto GNUTLS_CONN_FAILED; + } +#else goto RESPONSE_FAILED; +#endif smtp_peer_options = 0; } } @@ -2604,24 +2647,17 @@ have one. */ else if ( sx->smtps # ifdef SUPPORT_DANE - || sx->dane -# endif -# ifdef EXPERIMENTAL_REQUIRETLS - || tls_requiretls & REQUIRETLS_MSG + || sx->conn_args.dane # endif || verify_check_given_host(CUSS &ob->hosts_require_tls, sx->conn_args.host) == OK ) { - errno = -# ifdef EXPERIMENTAL_REQUIRETLS - tls_requiretls & REQUIRETLS_MSG ? ERRNO_REQUIRETLS : -# endif - ERRNO_TLSREQUIRED; + errno = 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) + if (sx->conn_args.dane) (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 */ @@ -2653,7 +2689,7 @@ if (continue_hostname == NULL #ifdef EXPERIMENTAL_PIPE_CONNECT | (sx->lmtp && ob->lmtp_ignore_quota ? OPTION_IGNQ : 0) | OPTION_DSN | OPTION_PIPE | OPTION_SIZE - | OPTION_CHUNKING | OPTION_PRDR | OPTION_UTF8 | OPTION_REQUIRETLS + | OPTION_CHUNKING | OPTION_PRDR | OPTION_UTF8 | (tls_out.active.sock >= 0 ? OPTION_EARLY_PIPE : 0) /* not for lmtp */ #else @@ -2669,9 +2705,6 @@ if (continue_hostname == NULL | OPTION_DSN | OPTION_PIPE | (ob->size_addition >= 0 ? OPTION_SIZE : 0) -# if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS) - | (tls_requiretls & REQUIRETLS_MSG ? OPTION_REQUIRETLS : 0) -# endif #endif ); #ifdef EXPERIMENTAL_PIPE_CONNECT @@ -2722,16 +2755,6 @@ if (continue_hostname == NULL 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 - #ifdef EXPERIMENTAL_PIPE_CONNECT if ( sx->early_pipe_ok && !sx->early_pipe_active @@ -2817,22 +2840,6 @@ if (sx->utf8_needed && !(sx->peer_offered & OPTION_UTF8)) } #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; @@ -2869,13 +2876,7 @@ return OK; #ifdef SUPPORT_TLS TLS_FAILED: -# 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; + code = '4', yield = DEFER; goto FAILED; #endif @@ -2934,8 +2935,8 @@ if (sx->send_quit) { 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 */ + for (int i = 16; read(sx->cctx.sock, sx->inbuffer, sizeof(sx->inbuffer)) > 0 && i > 0;) + i--; /* drain socket */ sx->send_quit = FALSE; } (void)close(sx->cctx.sock); @@ -2983,12 +2984,12 @@ Or just forget about lines? Or inflate by a fixed proportion? */ } #ifndef DISABLE_PRDR -/* If it supports Per-Recipient Data Reponses, and we have omre than one recipient, +/* If it supports Per-Recipient Data Responses, and we have more than one recipient, request that */ sx->prdr_active = FALSE; if (sx->peer_offered & OPTION_PRDR) - for (addr = addrlist; addr; addr = addr->next) + for (address_item * addr = addrlist; addr; addr = addr->next) if (addr->transport_return == PENDING_DEFER) { for (addr = addr->next; addr; addr = addr->next) @@ -3013,11 +3014,6 @@ if ( sx->peer_offered & OPTION_UTF8 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; @@ -3073,12 +3069,11 @@ if (sx->peer_offered & OPTION_DSN && !(addr->dsn_flags & rf_dsnlasthop)) { if (addr->dsn_flags & rf_dsnflags) { - int i; BOOL first = TRUE; Ustrcpy(p, " NOTIFY="); while (*p) p++; - for (i = 0; i < nelem(rf_list); i++) if (addr->dsn_flags & rf_list[i]) + for (int i = 0; i < nelem(rf_list); i++) if (addr->dsn_flags & rf_list[i]) { if (!first) *p++ = ','; first = FALSE; @@ -3254,6 +3249,7 @@ for (addr = sx->first_addr, address_count = 0; #ifdef EXPERIMENTAL_PIPE_CONNECT case -4: return -1; /* non-2xx for pipelined banner or EHLO */ + case -5: return -1; /* TLS first-read error */ #endif } sx->pending_MAIL = FALSE; /* Dealt with MAIL */ @@ -3290,7 +3286,7 @@ smtp_proxy_tls(void * ct_ctx, uschar * buf, size_t bsize, int * pfd, { fd_set rfds, efds; int max_fd = MAX(pfd[0], tls_out.active.sock) + 1; -int rc, i, fd_bits, nbytes; +int rc, i; close(pfd[1]); if ((rc = fork())) @@ -3305,7 +3301,7 @@ FD_ZERO(&rfds); FD_SET(tls_out.active.sock, &rfds); FD_SET(pfd[0], &rfds); -for (fd_bits = 3; fd_bits; ) +for (int fd_bits = 3; fd_bits; ) { time_t time_left = timeout; time_t time_start = time(NULL); @@ -3348,7 +3344,7 @@ for (fd_bits = 3; fd_bits; ) } else { - for (nbytes = 0; rc - nbytes > 0; nbytes += i) + for (int nbytes = 0; rc - nbytes > 0; nbytes += i) if ((i = write(pfd[0], buf + nbytes, rc - nbytes)) < 0) goto done; } else if (fd_bits & 1) @@ -3364,7 +3360,7 @@ for (fd_bits = 3; fd_bits; ) } else { - for (nbytes = 0; rc - nbytes > 0; nbytes += i) + for (int nbytes = 0; rc - nbytes > 0; nbytes += i) if ((i = tls_write(ct_ctx, buf + nbytes, rc - nbytes, FALSE)) < 0) goto done; } @@ -3429,7 +3425,6 @@ smtp_deliver(address_item *addrlist, host_item *host, int host_af, int defport, uschar *interface, transport_instance *tblock, BOOL *message_defer, BOOL suppress_tls) { -address_item *addr; smtp_transport_options_block * ob = SOB tblock->options_block; int yield = OK; int save_errno; @@ -3516,14 +3511,12 @@ always has a sequence number greater than one. */ if (continue_hostname && continue_sequence == 1) { - address_item * addr; - sx.peer_offered = smtp_peer_options; sx.pending_MAIL = FALSE; sx.ok = TRUE; sx.next_addr = NULL; - for (addr = addrlist; addr; addr = addr->next) + for (address_item * addr = addrlist; addr; addr = addr->next) addr->transport_return = PENDING_OK; } else @@ -3582,11 +3575,12 @@ if ( !(sx.peer_offered & OPTION_CHUNKING) case 1: sx.ok = TRUE; /* 2xx (only) => OK, but if LMTP, */ if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */ - case 0: break; /* No 2xx or 5xx, but no probs */ + case 0: break; /* No 2xx or 5xx, but no probs */ - case -1: goto END_OFF; /* Timeout on RCPT */ + case -1: goto END_OFF; /* Timeout on RCPT */ #ifdef EXPERIMENTAL_PIPE_CONNECT + case -5: /* TLS first-read error */ case -4: HDEBUG(D_transport) debug_printf("failed reaping pipelined cmd responses\n"); #endif @@ -3723,19 +3717,20 @@ else { case 3: sx.ok = TRUE; /* 2xx & 5xx => OK & progress made */ case 2: sx.completed_addr = TRUE; /* 5xx (only) => progress made */ - break; + break; - case 1: sx.ok = TRUE; /* 2xx (only) => OK, but if LMTP, */ + case 1: sx.ok = TRUE; /* 2xx (only) => OK, but if LMTP, */ if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */ - case 0: break; /* No 2xx or 5xx, but no probs */ + case 0: break; /* No 2xx or 5xx, but no probs */ - case -1: goto END_OFF; /* Timeout on RCPT */ + case -1: goto END_OFF; /* Timeout on RCPT */ #ifdef EXPERIMENTAL_PIPE_CONNECT + case -5: /* TLS first-read error */ case -4: HDEBUG(D_transport) debug_printf("failed reaping pipelined cmd responses\n"); #endif - default: goto RESPONSE_FAILED; /* I/O error, or any MAIL/DATA error */ + default: goto RESPONSE_FAILED; /* I/O error, or any MAIL/DATA error */ } } @@ -3815,7 +3810,7 @@ else /* Process all transported addresses - for LMTP or PRDR, read a status for each one. */ - for (addr = addrlist; addr != sx.first_addr; addr = addr->next) + for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next) { if (addr->transport_return != PENDING_OK) continue; @@ -3930,7 +3925,7 @@ else errno = ERRNO_DATA4XX; addrlist->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8; } - for (addr = addrlist; addr != sx.first_addr; addr = addr->next) + for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next) if (sx.buffer[0] == '5' || addr->transport_return == OK) addr->transport_return = PENDING_OK; /* allow set_errno action */ goto RESPONSE_FAILED; @@ -3940,11 +3935,11 @@ else and update the journal, or setup retry. */ overall_message = string_printing(sx.buffer); - for (addr = addrlist; addr != sx.first_addr; addr = addr->next) + for (address_item * 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) + for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next) if (addr->transport_return == OK) { if (testflag(addr, af_homonym)) @@ -4117,7 +4112,7 @@ connection if there are several waiting, provided we haven't already sent so many as to hit the configured limit. The function transport_check_waiting looks for a waiting message and returns its id. Then transport_pass_socket tries to set up a continued delivery by passing the socket on to another process. The -variable send_rset is FALSE if a message has just been successfully transfered. +variable send_rset is FALSE if a message has just been successfully transferred. If we are already sending down a continued channel, there may be further addresses not yet delivered that are aimed at the same host, but which have not @@ -4328,6 +4323,9 @@ This change is being made on 31-Jul-98. After over a year of trouble-free operation, the old commented-out code was removed on 17-Sep-99. */ SEND_QUIT: +#ifdef TCP_CORK +(void) setsockopt(sx.cctx.sock, IPPROTO_TCP, TCP_CORK, US &on, sizeof(on)); +#endif if (sx.send_quit) (void)smtp_write_command(&sx, SCMD_FLUSH, "QUIT\r\n"); END_OFF: @@ -4353,8 +4351,8 @@ if (sx.send_quit) shutdown(sx.cctx.sock, SHUT_WR); millisleep(f.running_in_test_harness ? 200 : 20); 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 */ + for (int i = 16; read(sx.cctx.sock, sx.inbuffer, sizeof(sx.inbuffer)) > 0 && i > 0;) + i--; /* drain socket */ } (void)close(sx.cctx.sock); @@ -4443,8 +4441,7 @@ static address_item * prepare_addresses(address_item *addrlist, host_item *host) { address_item *first_addr = NULL; -address_item *addr; -for (addr = addrlist; addr; addr = addr->next) +for (address_item * addr = addrlist; addr; addr = addr->next) if (addr->transport_return == DEFER) { if (!first_addr) first_addr = addr; @@ -4484,7 +4481,6 @@ smtp_transport_entry( transport_instance *tblock, /* data for this instantiation */ address_item *addrlist) /* addresses we are working on */ { -int cutoff_retry; int defport; int hosts_defer = 0; int hosts_fail = 0; @@ -4493,24 +4489,23 @@ int hosts_retry = 0; int hosts_serial = 0; int hosts_total = 0; int total_hosts_tried = 0; -address_item *addr; BOOL expired = TRUE; uschar *expanded_hosts = NULL; uschar *pistring; uschar *tid = string_sprintf("%s transport", tblock->name); smtp_transport_options_block *ob = SOB tblock->options_block; host_item *hostlist = addrlist->host_list; -host_item *host; +host_item *host = NULL; DEBUG(D_transport) { debug_printf("%s transport entered\n", tblock->name); - for (addr = addrlist; addr; addr = addr->next) + for (address_item * addr = addrlist; addr; addr = addr->next) debug_printf(" %s\n", addr->address); if (hostlist) { debug_printf("hostlist:\n"); - for (host = hostlist; host; host = host->next) + for (host_item * host = hostlist; host; host = host->next) debug_printf(" '%s' IP %s port %d\n", host->name, host->address, host->port); } if (continue_hostname) @@ -4531,12 +4526,6 @@ same one in order to be passed to a single transport - or if the transport has 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) @@ -4692,7 +4681,7 @@ the current message. To cope with this, we have to go round the loop a second time. After that, set the status and error data for any addresses that haven't had it set already. */ -for (cutoff_retry = 0; +for (int cutoff_retry = 0; expired && cutoff_retry < (ob->delay_after_cutoff ? 1 : 2); cutoff_retry++) { @@ -4709,7 +4698,6 @@ retry_non_continued: { int rc; int host_af; - uschar *rs; BOOL host_is_expired = FALSE; BOOL message_defer = FALSE; BOOL some_deferred = FALSE; @@ -4742,7 +4730,6 @@ retry_non_continued: if (!host->address) { int new_port, flags; - host_item *hh; if (host->status >= hstatus_unusable) { @@ -4779,7 +4766,7 @@ retry_non_continued: /* Update the host (and any additional blocks, resulting from multihoming) with a host-specific port, if any. */ - for (hh = host; hh != nexthost; hh = hh->next) hh->port = new_port; + for (host_item * hh = host; hh != nexthost; hh = hh->next) hh->port = new_port; /* Failure to find the host at this time (usually DNS temporary failure) is really a kind of routing failure rather than a transport failure. @@ -4798,7 +4785,7 @@ retry_non_continued: "HOST_FIND_AGAIN" : "HOST_FIND_FAILED", host->name); host->status = hstatus_unusable; - for (addr = addrlist; addr; addr = addr->next) + for (address_item * addr = addrlist; addr; addr = addr->next) { if (addr->transport_return != DEFER) continue; addr->basic_errno = ERRNO_UNKNOWNHOST; @@ -4817,7 +4804,7 @@ retry_non_continued: if (rc == HOST_FOUND_LOCAL && !ob->allow_localhost) { - for (addr = addrlist; addr; addr = addr->next) + for (address_item * addr = addrlist; addr; addr = addr->next) { addr->basic_errno = 0; addr->message = string_sprintf("%s transport found host %s to be " @@ -4862,7 +4849,7 @@ retry_non_continued: &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK)) { expired = FALSE; - for (addr = addrlist; addr; addr = addr->next) + for (address_item * addr = addrlist; addr; addr = addr->next) if (addr->transport_return == DEFER) addr->message = US"domain matches queue_smtp_domains, or -odqs set"; continue; /* With next host */ @@ -4898,11 +4885,14 @@ retry_non_continued: treated separately. */ host_af = Ustrchr(host->address, ':') == NULL ? AF_INET : AF_INET6; - if ((rs = ob->interface) && *rs) { - if (!smtp_get_interface(rs, host_af, addrlist, &interface, tid)) - return FALSE; - pistring = string_sprintf("%s/%s", pistring, interface); + uschar * s = ob->interface; + if (s && *s) + { + if (!smtp_get_interface(s, host_af, addrlist, &interface, tid)) + return FALSE; + pistring = string_sprintf("%s/%s", pistring, interface); + } } /* The first time round the outer loop, check the status of the host by @@ -4927,10 +4917,10 @@ retry_non_continued: incl_ip, &retry_host_key, &retry_message_key); DEBUG(D_transport) debug_printf("%s [%s]%s retry-status = %s\n", host->name, - (host->address == NULL)? US"" : host->address, pistring, - (host->status == hstatus_usable)? "usable" : - (host->status == hstatus_unusable)? "unusable" : - (host->status == hstatus_unusable_expired)? "unusable (expired)" : "?"); + host->address ? host->address : US"", pistring, + host->status == hstatus_usable ? "usable" + : host->status == hstatus_unusable ? "unusable" + : host->status == hstatus_unusable_expired ? "unusable (expired)" : "?"); /* Skip this address if not usable at this time, noting if it wasn't actually expired, both locally and in the address. */ @@ -5022,9 +5012,8 @@ retry_non_continued: if (f.dont_deliver) { - host_item *host2; set_errno_nohost(addrlist, 0, NULL, OK, FALSE); - for (addr = addrlist; addr; addr = addr->next) + for (address_item * addr = addrlist; addr; addr = addr->next) { addr->host_used = host; addr->special_action = '*'; @@ -5034,7 +5023,7 @@ retry_non_continued: { debug_printf("*** delivery by %s transport bypassed by -N option\n" "*** host and remaining hosts:\n", tblock->name); - for (host2 = host; host2; host2 = host2->next) + for (host_item * host2 = host; host2; host2 = host2->next) debug_printf(" %s [%s]\n", host2->name, host2->address ? host2->address : US"unset"); } @@ -5072,10 +5061,9 @@ retry_non_continued: if (!host_is_expired && ++unexpired_hosts_tried >= ob->hosts_max_try) { - host_item *h; DEBUG(D_transport) debug_printf("hosts_max_try limit reached with this host\n"); - for (h = host; h; h = h->next) if (h->mx != host->mx) + for (host_item * h = host; h; h = h->next) if (h->mx != host->mx) { nexthost = h; unexpired_hosts_tried--; @@ -5149,14 +5137,9 @@ retry_non_continued: /* Delivery attempt finished */ - rs = rc == OK ? US"OK" - : rc == DEFER ? US"DEFER" - : rc == ERROR ? US"ERROR" - : US"?"; - set_process_info("delivering %s: just tried %s [%s]%s for %s%s: result %s", message_id, host->name, host->address, pistring, addrlist->address, - addrlist->next ? " (& others)" : "", rs); + addrlist->next ? " (& others)" : "", rc_to_string(rc)); /* Release serialization if set up */ @@ -5244,7 +5227,7 @@ retry_non_continued: case, see if any of them are deferred. */ if (rc == OK) - for (addr = addrlist; addr; addr = addr->next) + for (address_item * addr = addrlist; addr; addr = addr->next) if (addr->transport_return == DEFER) { some_deferred = TRUE; @@ -5363,7 +5346,7 @@ specific failures. Force the delivery status for all addresses to FAIL. */ if (mua_wrapper) { - for (addr = addrlist; addr; addr = addr->next) + for (address_item * addr = addrlist; addr; addr = addr->next) addr->transport_return = FAIL; goto END_TRANSPORT; } @@ -5380,7 +5363,7 @@ If queue_smtp is set, or this transport was called to send a subsequent message down an existing TCP/IP connection, and something caused the host not to be found, we end up here, but can detect these cases and handle them specially. */ -for (addr = addrlist; addr; addr = addr->next) +for (address_item * addr = addrlist; addr; addr = addr->next) { /* If host is not NULL, it means that we stopped processing the host list because of hosts_max_try or hosts_max_try_hardlimit. In the former case, this