X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/4c2471caf34b901ee481cd1e742b3620e734b16b..d185889f47b9b27088e777f7d382295c51271586:/src/src/transports/smtp.c diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 997281901..477cdac4d 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -186,6 +186,18 @@ address can appear in the tables drtables.c. */ int smtp_transport_options_count = sizeof(smtp_transport_options)/sizeof(optionlist); + +#ifdef MACRO_PREDEF + +/* Dummy values */ +smtp_transport_options_block smtp_transport_option_defaults = {0}; +void smtp_transport_init(transport_instance *tblock) {} +BOOL smtp_transport_entry(transport_instance *tblock, address_item *addr) {return FALSE;} +void smtp_transport_closedown(transport_instance *tblock) {} + +#else /*!MACRO_PREDEF*/ + + /* Default private options block for the smtp transport. */ smtp_transport_options_block smtp_transport_option_defaults = { @@ -1186,20 +1198,24 @@ tlsa_lookup(const host_item * host, dns_answer * dnsa, BOOL dane_required) /* move this out to host.c given the similarity to dns_lookup() ? */ uschar buffer[300]; const uschar * fullname = buffer; +int rc; +BOOL sec; /* TLSA lookup string */ (void)sprintf(CS buffer, "_%d._tcp.%.256s", host->port, host->name); -switch (dns_lookup(dnsa, buffer, T_TLSA, &fullname)) +rc = dns_lookup(dnsa, buffer, T_TLSA, &fullname); +sec = dns_is_secure(dnsa); +DEBUG(D_transport) + debug_printf("TLSA lookup ret %d %sDNSSEC\n", rc, sec ? "" : "not "); + +switch (rc) { case DNS_SUCCEED: - if (!dns_is_secure(dnsa)) - { - log_write(0, LOG_MAIN, "DANE error: TLSA lookup not DNSSEC"); - return DEFER; - } - return OK; + if (sec) return OK; + log_write(0, LOG_MAIN, "DANE error: TLSA lookup not DNSSEC"); + /*FALLTHROUGH*/ case DNS_AGAIN: return DEFER; /* just defer this TLS'd conn */ @@ -1372,13 +1388,14 @@ smtp_context * sx = tctx->smtp_context; int cmd_count = 0; int prev_cmd_count; -/* Write SMTP chunk header command */ +/* Write SMTP chunk header command. If not reaping responses, note that +there may be more writes (like, the chunk data) done soon. */ if (chunk_size > 0) { - if((cmd_count = smtp_write_command(&sx->outblock, FALSE, "BDAT %u%s\r\n", - chunk_size, - flags & tc_chunk_last ? " LAST" : "") + if((cmd_count = smtp_write_command(&sx->outblock, + flags & tc_reap_prev ? SCMD_FLUSH : SCMD_MORE, + "BDAT %u%s\r\n", chunk_size, flags & tc_chunk_last ? " LAST" : "") ) < 0) return ERROR; if (flags & tc_chunk_last) data_command = string_copy(big_buffer); /* Save for later error message */ @@ -1552,34 +1569,17 @@ if (sx->smtps) the initial interaction and HELO/EHLO/LHLO. Connect timeout errors are handled specially so they can be identified for retries. */ -if (continue_hostname == NULL) +if (!continue_hostname) { if (sx->verify) HDEBUG(D_verify) debug_printf("interface=%s port=%d\n", sx->interface, sx->port); - /* This puts port into host->port */ - sx->inblock.sock = sx->outblock.sock = - smtp_connect(sx->host, sx->host_af, sx->port, sx->interface, - sx->ob->connect_timeout, sx->tblock); + /* Get the actual port the connection will use, into sx->host */ - if (sx->inblock.sock < 0) - { - uschar * msg = NULL; - if (sx->verify) - { - msg = US strerror(errno); - HDEBUG(D_verify) debug_printf("connect: %s\n", msg); - } - set_errno_nohost(sx->addrlist, - errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno, - sx->verify ? string_sprintf("could not connect: %s", msg) - : NULL, - DEFER, FALSE); - sx->send_quit = FALSE; - return DEFER; - } + smtp_port_for_connect(sx->host, sx->port); #if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE) + /* Do TLSA lookup for DANE */ { tls_out.dane_verified = FALSE; tls_out.tlsa_usage = 0; @@ -1591,7 +1591,9 @@ if (continue_hostname == NULL) ) switch (rc = tlsa_lookup(sx->host, &tlsa_dnsa, sx->dane_required)) { - case OK: sx->dane = TRUE; break; + case OK: sx->dane = TRUE; + sx->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", @@ -1607,12 +1609,32 @@ if (continue_hostname == NULL) FAIL, FALSE); return FAIL; } - - if (sx->dane) - sx->ob->tls_tempfail_tryclear = FALSE; } #endif /*DANE*/ + /* Make the TCP connection */ + + sx->inblock.sock = sx->outblock.sock = + smtp_connect(sx->host, sx->host_af, sx->interface, + sx->ob->connect_timeout, sx->tblock); + + if (sx->inblock.sock < 0) + { + uschar * msg = NULL; + if (sx->verify) + { + msg = US strerror(errno); + HDEBUG(D_verify) debug_printf("connect: %s\n", msg); + } + set_errno_nohost(sx->addrlist, + errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno, + sx->verify ? string_sprintf("could not connect: %s", msg) + : NULL, + DEFER, FALSE); + sx->send_quit = FALSE; + return DEFER; + } + /* 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. */ @@ -1741,7 +1763,7 @@ goto SEND_QUIT; if (sx->esmtp) { - if (smtp_write_command(&sx->outblock, FALSE, "%s %s\r\n", + if (smtp_write_command(&sx->outblock, SCMD_FLUSH, "%s %s\r\n", sx->lmtp ? "LHLO" : "EHLO", sx->helo_data) < 0) goto SEND_FAILED; sx->esmtp_sent = TRUE; @@ -1774,7 +1796,7 @@ goto SEND_QUIT; if (sx->esmtp_sent && (n = Ustrlen(sx->buffer)) < sizeof(sx->buffer)/2) { rsp = sx->buffer + n + 1; n = sizeof(sx->buffer) - n; } - if (smtp_write_command(&sx->outblock, FALSE, "HELO %s\r\n", sx->helo_data) < 0) + if (smtp_write_command(&sx->outblock, SCMD_FLUSH, "HELO %s\r\n", sx->helo_data) < 0) goto SEND_FAILED; good_response = smtp_read_response(&sx->inblock, rsp, n, '2', sx->ob->command_timeout); @@ -1836,7 +1858,7 @@ else else { sx->inblock.sock = sx->outblock.sock = 0; /* stdin */ - sx->host->port = sx->port; /* Record the port that was used */ + smtp_port_for_connect(sx->host, sx->port); /* Record the port that was used */ } smtp_command = big_buffer; sx->helo_data = NULL; /* ensure we re-expand ob->helo_data */ @@ -1874,7 +1896,7 @@ if ( smtp_peer_options & PEER_OFFERED_TLS ) ) { uschar buffer2[4096]; - if (smtp_write_command(&sx->outblock, FALSE, "STARTTLS\r\n") < 0) + if (smtp_write_command(&sx->outblock, SCMD_FLUSH, "STARTTLS\r\n") < 0) goto SEND_FAILED; /* If there is an I/O error, transmission of this message is deferred. If @@ -1988,7 +2010,7 @@ if (tls_out.active >= 0) debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n"); } - if (smtp_write_command(&sx->outblock, FALSE, "%s %s\r\n", + if (smtp_write_command(&sx->outblock, SCMD_FLUSH, "%s %s\r\n", sx->lmtp ? "LHLO" : greeting_cmd, sx->helo_data) < 0) goto SEND_FAILED; good_response = smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), @@ -2167,32 +2189,41 @@ return OK; /* The failure happened while setting up the call; see if the failure was a 5xx response (this will either be on connection, or following HELO - a 5xx - after EHLO causes it to try HELO). If so, fail all addresses, as this host is - never going to accept them. For other errors during setting up (timeouts or - whatever), defer all addresses, and yield DEFER, so that the host is not - tried again for a while. */ + after EHLO causes it to try HELO). If so, and there are no more hosts to try, + fail all addresses, as this host is never going to accept them. For other + errors during setting up (timeouts or whatever), defer all addresses, and + yield DEFER, so that the host is not tried again for a while. + + XXX This peeking for another host feels like a layering violation. We want + to note the host as unusable, but down here we shouldn't know if this was + the last host to try for the addr(list). Perhaps the upper layer should be + the one to do set_errno() ? The problem is that currently the addr is where + errno etc. are stashed, but until we run out of hosts to try the errors are + host-specific. Maybe we should enhance the host_item definition? */ FAILED: sx->ok = FALSE; /* For when reached by GOTO */ - - yield = code == '5' + set_errno(sx->addrlist, errno, message, + sx->host->next + ? DEFER + : code == '5' #ifdef SUPPORT_I18N - || errno == ERRNO_UTF8_FWD + || errno == ERRNO_UTF8_FWD #endif - ? FAIL : DEFER; - - set_errno(sx->addrlist, errno, message, yield, pass_message, sx->host + ? FAIL : DEFER, + pass_message, sx->host #ifdef EXPERIMENTAL_DSN_INFO , sx->smtp_greeting, sx->helo_response #endif ); + yield = DEFER; } SEND_QUIT: if (sx->send_quit) - (void)smtp_write_command(&sx->outblock, FALSE, "QUIT\r\n"); + (void)smtp_write_command(&sx->outblock, SCMD_FLUSH, "QUIT\r\n"); #ifdef SUPPORT_TLS tls_close(FALSE, TRUE); @@ -2201,12 +2232,7 @@ tls_close(FALSE, TRUE); /* Close the socket, and return the appropriate value, first setting works because the NULL setting is passed back to the calling process, and remote_max_parallel is forced to 1 when delivering over an existing connection, - -If all went well and continue_more is set, we shouldn't actually get here if -there are further addresses, as the return above will be taken. However, -writing RSET might have failed, or there may be other addresses whose hosts are -specified in the transports, and therefore not visible at top level, in which -case continue_more won't get set. */ +*/ HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n"); if (sx->send_quit) @@ -2253,6 +2279,9 @@ included in the count.) */ if (sx->peer_offered & PEER_OFFERED_SIZE) { +/*XXX problem here under spool_files_wireformat? +Or just forget about lines? Or inflate by a fixed proportion? */ + sprintf(CS p, " SIZE=%d", message_size+message_linecount+sx->ob->size_addition); while (*p) p++; } @@ -2422,7 +2451,7 @@ sx->pending_MAIL = TRUE; /* The block starts with MAIL */ } #endif - rc = smtp_write_command(&sx->outblock, pipelining_active, + rc = smtp_write_command(&sx->outblock, pipelining_active ? SCMD_BUFFER : SCMD_FLUSH, "MAIL FROM:<%s>%s\r\n", s, sx->buffer); } @@ -2500,8 +2529,8 @@ for (addr = sx->first_addr, address_count = 0; } #endif - count = smtp_write_command(&sx->outblock, no_flush, "RCPT TO:<%s>%s%s\r\n", - rcpt_addr, sx->igquotstr, sx->buffer); + count = smtp_write_command(&sx->outblock, no_flush ? SCMD_BUFFER : SCMD_FLUSH, + "RCPT TO:<%s>%s%s\r\n", rcpt_addr, sx->igquotstr, sx->buffer); if (count < 0) return -5; if (count > 0) @@ -2551,14 +2580,14 @@ Arguments: void smtp_proxy_tls(uschar * buf, size_t bsize, int proxy_fd, int timeout) { -fd_set fds; +fd_set rfds, efds; int max_fd = MAX(proxy_fd, tls_out.active) + 1; int rc, i, fd_bits, nbytes; set_process_info("proxying TLS connection for continued transport"); -FD_ZERO(&fds); -FD_SET(tls_out.active, &fds); -FD_SET(proxy_fd, &fds); +FD_ZERO(&rfds); +FD_SET(tls_out.active, &rfds); +FD_SET(proxy_fd, &rfds); for (fd_bits = 3; fd_bits; ) { @@ -2566,11 +2595,13 @@ for (fd_bits = 3; fd_bits; ) 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 *)&fds, NULL, NULL, &tv); + rc = select(max_fd, + (SELECT_ARG2_TYPE *)&rfds, NULL, (SELECT_ARG2_TYPE *)&efds, &tv); if (rc < 0 && errno == EINTR) if ((time_left -= time(NULL) - time_start) > 0) continue; @@ -2580,16 +2611,24 @@ for (fd_bits = 3; fd_bits; ) DEBUG(D_transport) if (rc == 0) debug_printf("%s: timed out\n", __FUNCTION__); return; } + + if (FD_ISSET(tls_out.active, &efds) || FD_ISSET(proxy_fd, &efds)) + { + DEBUG(D_transport) debug_printf("select: exceptional cond on %s fd\n", + FD_ISSET(proxy_fd, &efds) ? "proxy" : "tls"); + return; + } } - while (rc < 0 || !(FD_ISSET(tls_out.active, &fds) || FD_ISSET(proxy_fd, &fds))); + while (rc < 0 || !(FD_ISSET(tls_out.active, &rfds) || FD_ISSET(proxy_fd, &rfds))); /* handle inbound data */ - if (FD_ISSET(tls_out.active, &fds)) + if (FD_ISSET(tls_out.active, &rfds)) if ((rc = tls_read(FALSE, buf, bsize)) <= 0) { fd_bits &= ~1; - FD_CLR(tls_out.active, &fds); + FD_CLR(tls_out.active, &rfds); shutdown(proxy_fd, SHUT_WR); + timeout = 5; } else { @@ -2597,23 +2636,23 @@ for (fd_bits = 3; fd_bits; ) if ((i = write(proxy_fd, buf + nbytes, rc - nbytes)) < 0) return; } else if (fd_bits & 1) - FD_SET(tls_out.active, &fds); + FD_SET(tls_out.active, &rfds); /* handle outbound data */ - if (FD_ISSET(proxy_fd, &fds)) + if (FD_ISSET(proxy_fd, &rfds)) if ((rc = read(proxy_fd, buf, bsize)) <= 0) { - fd_bits &= ~2; - FD_CLR(proxy_fd, &fds); - shutdown(tls_out.active, SHUT_WR); + fd_bits = 0; + tls_close(FALSE, TRUE); } else { for (nbytes = 0; rc - nbytes > 0; nbytes += i) - if ((i = tls_write(FALSE, buf + nbytes, rc - nbytes)) < 0) return; + if ((i = tls_write(FALSE, buf + nbytes, rc - nbytes, FALSE)) < 0) + return; } else if (fd_bits & 2) - FD_SET(proxy_fd, &fds); + FD_SET(proxy_fd, &rfds); } } #endif @@ -2642,7 +2681,8 @@ Arguments: failed by one of them. host host to deliver to host_af AF_INET or AF_INET6 - port default TCP/IP port to use, in host byte order + defport default TCP/IP port to use if host does not specify, in host + byte order interface interface to bind to, or NULL tblock transport instance block message_defer set TRUE if yield is OK, but all addresses were deferred @@ -2664,7 +2704,7 @@ Returns: OK - the connection was made and the delivery attempted; */ static int -smtp_deliver(address_item *addrlist, host_item *host, int host_af, int port, +smtp_deliver(address_item *addrlist, host_item *host, int host_af, int defport, uschar *interface, transport_instance *tblock, BOOL *message_defer, BOOL suppress_tls) { @@ -2687,7 +2727,7 @@ suppress_tls = suppress_tls; /* stop compiler warning when no TLS support */ sx.addrlist = addrlist; sx.host = host; sx.host_af = host_af, -sx.port = port; +sx.port = defport; sx.interface = interface; sx.helo_data = NULL; sx.tblock = tblock; @@ -2810,7 +2850,7 @@ to send is. */ if ( !(sx.peer_offered & PEER_OFFERED_CHUNKING) && (sx.ok || (pipelining_active && !mua_wrapper))) { - int count = smtp_write_command(&sx.outblock, FALSE, "DATA\r\n"); + int count = smtp_write_command(&sx.outblock, SCMD_FLUSH, "DATA\r\n"); if (count < 0) goto SEND_FAILED; switch(sync_responses(&sx, count, sx.ok ? +1 : -1)) @@ -2847,6 +2887,7 @@ if (!(sx.peer_offered & PEER_OFFERED_CHUNKING) && !sx.ok) else { transport_ctx tctx = { + sx.inblock.sock, tblock, addrlist, US".", US"..", /* Escaping strings */ @@ -2895,10 +2936,9 @@ else transport_count = 0; #ifndef DISABLE_DKIM - sx.ok = dkim_transport_write_message(sx.inblock.sock, &tctx, &sx.ob->dkim, - CUSS &message); + sx.ok = dkim_transport_write_message(&tctx, &sx.ob->dkim, CUSS &message); #else - sx.ok = transport_write_message(sx.inblock.sock, &tctx, 0); + sx.ok = transport_write_message(&tctx, 0); #endif /* transport_write_message() uses write() because it is called from other @@ -3337,7 +3377,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit) BOOL pass_message; if (sx.send_rset) - if (! (sx.ok = smtp_write_command(&sx.outblock, FALSE, "RSET\r\n") >= 0)) + if (! (sx.ok = smtp_write_command(&sx.outblock, SCMD_FLUSH, "RSET\r\n") >= 0)) { msg = US string_sprintf("send() to %s [%s] failed: %s", host->name, host->address, strerror(errno)); @@ -3369,26 +3409,33 @@ if (sx.completed_addr && sx.ok && sx.send_quit) continue_sequence++; /* Causes * in logging */ goto SEND_MESSAGE; } - if (continue_more) return yield; /* More addresses for another run */ - /* Pass the connection on to a new Exim process. */ + /* Unless caller said it already has more messages listed for this host, + pass the connection on to a new Exim process (below, the call to + transport_pass_socket). If the caller has more ready, just return with + the connection still open. */ + #ifdef SUPPORT_TLS if (tls_out.active >= 0) - if (verify_check_given_host(&sx.ob->hosts_noproxy_tls, host) == OK) + if ( continue_more + || verify_check_given_host(&sx.ob->hosts_noproxy_tls, host) == OK) { - /* Pass the socket, for direct use, to a new Exim process. Before - doing so, we must shut down TLS. Not all MTAs allow for the - continuation of the SMTP session when TLS is shut down. We test for - this by sending a new EHLO. If we don't get a good response, we don't - attempt to pass the socket on. */ + /* Before passing the socket on, or returning to caller with it still + open, we must shut down TLS. Not all MTAs allow for the continuation + of the SMTP session when TLS is shut down. We test for this by sending + a new EHLO. If we don't get a good response, we don't attempt to pass + the socket on. */ tls_close(FALSE, TRUE); smtp_peer_options = smtp_peer_options_wrap; sx.ok = !sx.smtps - && smtp_write_command(&sx.outblock, FALSE, + && smtp_write_command(&sx.outblock, SCMD_FLUSH, "EHLO %s\r\n", sx.helo_data) >= 0 && smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer), '2', sx.ob->command_timeout); + + if (sx.ok && continue_more) + return yield; /* More addresses for another run */ } else { @@ -3405,7 +3452,10 @@ if (sx.completed_addr && sx.ok && sx.send_quit) # endif ); } + else #endif + if (continue_more) + return yield; /* More addresses for another run */ /* If the socket is successfully passed, we mustn't send QUIT (or indeed anything!) from here. */ @@ -3428,6 +3478,8 @@ propagate it from the initial int pid = fork(); if (pid > 0) /* parent */ { + DEBUG(D_transport) debug_printf("proxy-proc inter-pid %d\n", pid); + close(pfd[0]); waitpid(pid, NULL, 0); tls_close(FALSE, FALSE); (void)close(sx.inblock.sock); @@ -3437,8 +3489,12 @@ propagate it from the initial } else if (pid == 0) /* child; fork again to disconnect totally */ { + close(pfd[1]); if ((pid = fork())) + { + DEBUG(D_transport) debug_printf("proxy-prox final-pid %d\n", pid); _exit(pid ? EXIT_FAILURE : EXIT_SUCCESS); + } smtp_proxy_tls(sx.buffer, sizeof(sx.buffer), pfd[0], sx.ob->command_timeout); exim_exit(0); } @@ -3476,7 +3532,7 @@ 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: -if (sx.send_quit) (void)smtp_write_command(&sx.outblock, FALSE, "QUIT\r\n"); +if (sx.send_quit) (void)smtp_write_command(&sx.outblock, SCMD_FLUSH, "QUIT\r\n"); END_OFF: @@ -3557,7 +3613,7 @@ outblock.ptr = outbuffer; outblock.cmd_count = 0; outblock.authenticating = FALSE; -(void)smtp_write_command(&outblock, FALSE, "QUIT\r\n"); +(void)smtp_write_command(&outblock, SCMD_FLUSH, "QUIT\r\n"); (void)smtp_read_response(&inblock, buffer, sizeof(buffer), '2', ob->command_timeout); (void)close(inblock.sock); @@ -3629,7 +3685,7 @@ smtp_transport_entry( address_item *addrlist) /* addresses we are working on */ { int cutoff_retry; -int port; +int defport; int hosts_defer = 0; int hosts_fail = 0; int hosts_looked_up = 0; @@ -3784,7 +3840,7 @@ else if (ob->hosts_randomize && hostlist->mx == MX_NONE && !continue_hostname) /* Sort out the default port. */ -if (!smtp_get_port(ob->port, addrlist, &port, tid)) return FALSE; +if (!smtp_get_port(ob->port, addrlist, &defport, tid)) return FALSE; /* For each host-plus-IP-address on the list: @@ -4023,7 +4079,7 @@ for (cutoff_retry = 0; the default. */ pistring = string_sprintf(":%d", host->port == PORT_NONE - ? port : host->port); + ? defport : host->port); if (Ustrcmp(pistring, ":25") == 0) pistring = US""; /* Select IPv4 or IPv6, and choose an outgoing interface. If the interface @@ -4223,7 +4279,7 @@ for (cutoff_retry = 0; /* Attempt the delivery. */ total_hosts_tried++; - rc = smtp_deliver(addrlist, thost, host_af, port, interface, tblock, + rc = smtp_deliver(addrlist, thost, host_af, defport, interface, tblock, &message_defer, FALSE); /* Yield is one of: @@ -4270,7 +4326,7 @@ for (cutoff_retry = 0; "%s: delivering unencrypted to H=%s [%s] (not in hosts_require_tls)", first_addr->message, host->name, host->address); first_addr = prepare_addresses(addrlist, host); - rc = smtp_deliver(addrlist, thost, host_af, port, interface, tblock, + rc = smtp_deliver(addrlist, thost, host_af, defport, interface, tblock, &message_defer, TRUE); if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL) write_logs(first_addr, host); @@ -4565,6 +4621,7 @@ DEBUG(D_transport) debug_printf("Leaving %s transport\n", tblock->name); return TRUE; /* Each address has its status */ } +#endif /*!MACRO_PREDEF*/ /* vi: aw ai sw=2 */ /* End of transport/smtp.c */