X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsrc%2Ftransports%2Fsmtp.c;h=3558fa65c7c73ddf5d8e61abe33ad73bc1a5df91;hb=b66fecb428871a3eb274d9370671f1eaf8c5ccec;hp=576b7c546c3a3443891f89d2cd480e0d09f7f719;hpb=ee8b809061baea861fc87c41bcb72a62d76b0047;p=user%2Fhenk%2Fcode%2Fexim.git diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 576b7c546..3558fa65c 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -497,14 +497,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 +593,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", @@ -668,22 +672,22 @@ Returns: nothing static void write_logs(const host_item *host, const uschar *suffix, int basic_errno) { -uschar *message = LOGGING(outgoing_port) - ? string_sprintf("H=%s [%s]:%d", host->name, host->address, +gstring * message = LOGGING(outgoing_port) + ? string_fmt_append(NULL, "H=%s [%s]:%d", host->name, host->address, host->port == PORT_NONE ? 25 : host->port) - : string_sprintf("H=%s [%s]", host->name, host->address); + : string_fmt_append(NULL, "H=%s [%s]", host->name, host->address); if (suffix) { - message = string_sprintf("%s: %s", message, suffix); + message = string_fmt_append(message, ": %s", suffix); if (basic_errno > 0) - message = string_sprintf("%s: %s", message, strerror(basic_errno)); + message = string_fmt_append(message, ": %s", strerror(basic_errno)); } else - message = string_sprintf("%s %s", message, exim_errstr(basic_errno)); + message = string_fmt_append(message, " %s", exim_errstr(basic_errno)); -log_write(0, LOG_MAIN, "%s", message); -deliver_msglog("%s %s\n", tod_stamp(tod_log), message); +log_write(0, LOG_MAIN, "%s", string_from_gstring(message)); +deliver_msglog("%s %s\n", tod_stamp(tod_log), message->s); } static void @@ -826,6 +830,10 @@ if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, 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); } @@ -938,6 +946,7 @@ Arguments: Return: OK all well + DEFER error on first read of TLS'd conn FAIL SMTP error in response */ int @@ -945,6 +954,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 +966,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 +981,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 +992,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 +1000,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 +1019,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 +1064,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 @@ -1057,10 +1073,11 @@ sync_responses(smtp_context * sx, int count, int pending_DATA) address_item * addr = sx->sync_addr; smtp_transport_options_block * ob = sx->conn_args.ob; int yield = 0; +int rc; #ifdef EXPERIMENTAL_PIPE_CONNECT -if (smtp_reap_early_pipe(sx, &count) != OK) - return -4; +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 +1093,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 +1153,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) @@ -1197,8 +1221,13 @@ while (count-- > 0) If not, log this last one in the == line. */ if (sx->conn_args.host->next) - log_write(0, LOG_MAIN, "H=%s [%s]: %s", - sx->conn_args.host->name, sx->conn_args.host->address, addr->message); + if (LOGGING(outgoing_port)) + log_write(0, LOG_MAIN, "H=%s [%s]:%d %s", sx->conn_args.host->name, + sx->conn_args.host->address, + sx->port == PORT_NONE ? 25 : sx->port, addr->message); + else + log_write(0, LOG_MAIN, "H=%s [%s]: %s", sx->conn_args.host->name, + sx->conn_args.host->address, addr->message); #ifndef DISABLE_EVENT else @@ -1241,6 +1270,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') @@ -1443,7 +1476,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; @@ -1596,8 +1629,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) { @@ -1717,12 +1749,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 @@ -1790,7 +1816,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 @@ -1847,10 +1875,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; @@ -1913,14 +1943,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; @@ -1937,7 +1967,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 @@ -2028,15 +2058,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, @@ -2461,28 +2491,20 @@ 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); +GNUTLS_CONN_FAILED: + 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 */ @@ -2491,7 +2513,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; } @@ -2499,7 +2521,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; @@ -2589,7 +2611,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; } } @@ -2599,24 +2636,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 */ @@ -2648,7 +2678,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 @@ -2664,9 +2694,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 @@ -2717,16 +2744,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 @@ -2812,22 +2829,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; @@ -2864,13 +2865,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 @@ -2929,8 +2924,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); @@ -2978,12 +2973,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) @@ -3008,11 +3003,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; @@ -3068,12 +3058,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; @@ -3249,6 +3238,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 */ @@ -3285,7 +3275,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())) @@ -3300,7 +3290,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); @@ -3343,7 +3333,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) @@ -3359,7 +3349,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; } @@ -3424,7 +3414,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; @@ -3449,9 +3438,7 @@ sx.conn_args.interface = interface; sx.helo_data = NULL; sx.conn_args.tblock = tblock; sx.verify = FALSE; -#ifdef EXPERIMENTAL_PIPE_CONNECT sx.sync_addr = sx.first_addr = addrlist; -#endif /* Get the channel set up ready for a message (MAIL FROM being the next SMTP command to send */ @@ -3513,14 +3500,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 @@ -3579,11 +3564,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 @@ -3720,19 +3706,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 */ } } @@ -3812,7 +3799,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; @@ -3927,7 +3914,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; @@ -3937,11 +3924,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)) @@ -4114,7 +4101,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 @@ -4325,6 +4312,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: @@ -4350,8 +4340,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); @@ -4440,8 +4430,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; @@ -4481,7 +4470,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; @@ -4490,24 +4478,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) @@ -4528,12 +4515,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) @@ -4689,7 +4670,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++) { @@ -4706,7 +4687,6 @@ retry_non_continued: { int rc; int host_af; - uschar *rs; BOOL host_is_expired = FALSE; BOOL message_defer = FALSE; BOOL some_deferred = FALSE; @@ -4739,7 +4719,6 @@ retry_non_continued: if (!host->address) { int new_port, flags; - host_item *hh; if (host->status >= hstatus_unusable) { @@ -4776,7 +4755,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. @@ -4795,7 +4774,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; @@ -4814,7 +4793,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 " @@ -4859,7 +4838,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 */ @@ -4895,11 +4874,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 @@ -4924,10 +4906,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. */ @@ -5010,8 +4992,8 @@ retry_non_continued: message_id, host->name, host->address, addrlist->address, addrlist->next ? ", ..." : ""); - set_process_info("delivering %s to %s [%s] (%s%s)", - message_id, host->name, host->address, addrlist->address, + set_process_info("delivering %s to %s [%s]%s (%s%s)", + message_id, host->name, host->address, pistring, addrlist->address, addrlist->next ? ", ..." : ""); /* This is not for real; don't do the delivery. If there are @@ -5019,9 +5001,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 = '*'; @@ -5031,7 +5012,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"); } @@ -5069,10 +5050,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--; @@ -5146,14 +5126,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] for %s%s: result %s", - message_id, host->name, host->address, addrlist->address, - addrlist->next ? " (& others)" : "", rs); + 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)" : "", rc_to_string(rc)); /* Release serialization if set up */ @@ -5241,7 +5216,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; @@ -5295,6 +5270,17 @@ retry_non_continued: "hosts_max_try (message older than host's retry time)\n"); } } + + DEBUG(D_transport) + { + if (unexpired_hosts_tried >= ob->hosts_max_try) + debug_printf("reached transport hosts_max_try limit %d\n", + ob->hosts_max_try); + if (total_hosts_tried >= ob->hosts_max_try_hardlimit) + debug_printf("reached transport hosts_max_try_hardlimit limit %d\n", + ob->hosts_max_try_hardlimit); + } + if (f.running_in_test_harness) millisleep(500); /* let server debug out */ } /* End of loop for trying multiple hosts. */ @@ -5349,7 +5335,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; } @@ -5366,7 +5352,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