X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/875512a36119423217802de1f79350e7fce1cd9b..4c2471caf34b901ee481cd1e742b3620e734b16b:/src/src/transports/smtp.c diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 41d0dc1ea..997281901 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -220,8 +220,10 @@ smtp_transport_options_block smtp_transport_option_defaults = { NULL, /* hosts_verify_avoid_tls */ NULL, /* hosts_avoid_pipelining */ NULL, /* hosts_avoid_esmtp */ +#ifdef SUPPORT_TLS NULL, /* hosts_nopass_tls */ US"*", /* hosts_noproxy_tls */ +#endif 5*60, /* command_timeout */ 5*60, /* connect_timeout; shorter system default overrides */ 5*60, /* data timeout */ @@ -794,7 +796,9 @@ with an address by scanning for the next address whose status is PENDING_DEFER. while (count-- > 0) { - while (addr->transport_return != PENDING_DEFER) addr = addr->next; + while (addr->transport_return != PENDING_DEFER) + if (!(addr = addr->next)) + return -2; /* The address was accepted */ addr->host_used = sx->host; @@ -1345,15 +1349,22 @@ return checks; If given a nonzero size, first flush any buffered SMTP commands then emit the command. -Reap previous SMTP command responses if requested. -Reap one SMTP command response if requested. +Reap previous SMTP command responses if requested, and always reap +the response from a previous BDAT command. + +Args: + tctx transport context + chunk_size value for SMTP BDAT command + flags + tc_chunk_last add LAST option to SMTP BDAT command + tc_reap_prev reap response to previous SMTP commands Returns: OK or ERROR */ static int -smtp_chunk_cmd_callback(int fd, transport_ctx * tctx, - unsigned chunk_size, unsigned flags) +smtp_chunk_cmd_callback(transport_ctx * tctx, unsigned chunk_size, + unsigned flags) { smtp_transport_options_block * ob = (smtp_transport_options_block *)(tctx->tblock->options_block); @@ -1801,9 +1812,11 @@ goto SEND_QUIT; } } -/* For continuing deliveries down the same channel, the socket is the standard -input, and we don't need to redo EHLO here (but may need to do so for TLS - see -below). Set up the pointer to where subsequent commands will be left, for +/* For continuing deliveries down the same channel, having re-exec'd the socket +is the standard input; for a socket held open from verify it is recorded +in the cutthrough context block. Either way we don't need to redo EHLO here +(but may need to do so for TLS - see below). +Set up the pointer to where subsequent commands will be left, for error messages. Note that smtp_peer_options will have been set from the command line if they were set in the process that passed the connection on. */ @@ -1815,19 +1828,30 @@ separate - we could match up by host ip+port as a bodge. */ else { - sx->inblock.sock = sx->outblock.sock = 0; /* stdin */ + if (cutthrough.fd >= 0 && cutthrough.callout_hold_only) + { + sx->inblock.sock = sx->outblock.sock = cutthrough.fd; + sx->host->port = sx->port = cutthrough.host.port; + } + else + { + sx->inblock.sock = sx->outblock.sock = 0; /* stdin */ + sx->host->port = sx->port; /* Record the port that was used */ + } smtp_command = big_buffer; - sx->host->port = sx->port; /* Record the port that was used */ sx->helo_data = NULL; /* ensure we re-expand ob->helo_data */ - /* For a continued connection with TLS being proxied for us, nothing - more to do. */ + /* For a continued connection with TLS being proxied for us, or a + held-open verify connection with TLS, nothing more to do. */ - if (continue_proxy) + if ( continue_proxy_cipher + || (cutthrough.fd >= 0 && cutthrough.callout_hold_only && cutthrough.is_tls) + ) { sx->peer_offered = smtp_peer_options; pipelining_active = !!(smtp_peer_options & PEER_OFFERED_PIPE); - HDEBUG(D_transport) debug_printf("continued connection, proxied TLS\n"); + HDEBUG(D_transport) debug_printf("continued connection, %s TLS\n", + continue_proxy_cipher ? "proxied" : "verify conn with"); return OK; } HDEBUG(D_transport) debug_printf("continued connection, no TLS\n"); @@ -2453,7 +2477,8 @@ for (addr = sx->first_addr, address_count = 0; ? dsn_support_yes : dsn_support_no; address_count++; - no_flush = pipelining_active && !sx->verify && (!mua_wrapper || addr->next); + no_flush = pipelining_active && !sx->verify + && (!mua_wrapper || addr->next && address_count < sx->max_rcpt); build_rcptcmd_options(sx, addr); @@ -2511,19 +2536,20 @@ return 0; * Proxy TLS connection for another transport process * ******************************************************/ /* -Use the smtp-context buffer as a staging area, and select on both the slave -process and the TLS'd fd for data to read (per the coding in ip_recv() and +Use the given buffer as a staging area, and select on both the given fd +and the TLS'd client-fd for data to read (per the coding in ip_recv() and fd_ready() this is legitimate). Do blocking full-size writes, and reads under a timeout. Arguments: - sx smtp context block + buf space to use for buffering + bufsiz size of buffer proxy_fd comms to proxied process timeout per-read timeout, seconds */ -static void -smtp_proxy_tls(smtp_context * sx, int proxy_fd, int timeout) +void +smtp_proxy_tls(uschar * buf, size_t bsize, int proxy_fd, int timeout) { fd_set fds; int max_fd = MAX(proxy_fd, tls_out.active) + 1; @@ -2559,7 +2585,7 @@ for (fd_bits = 3; fd_bits; ) /* handle inbound data */ if (FD_ISSET(tls_out.active, &fds)) - if ((rc = tls_read(FALSE, sx->buffer, sizeof(sx->buffer))) <= 0) + if ((rc = tls_read(FALSE, buf, bsize)) <= 0) { fd_bits &= ~1; FD_CLR(tls_out.active, &fds); @@ -2568,14 +2594,14 @@ for (fd_bits = 3; fd_bits; ) else { for (nbytes = 0; rc - nbytes > 0; nbytes += i) - if ((i = write(proxy_fd, sx->buffer + nbytes, rc - nbytes)) < 0) return; + if ((i = write(proxy_fd, buf + nbytes, rc - nbytes)) < 0) return; } else if (fd_bits & 1) FD_SET(tls_out.active, &fds); /* handle outbound data */ if (FD_ISSET(proxy_fd, &fds)) - if ((rc = read(proxy_fd, sx->buffer, sizeof(sx->buffer))) <= 0) + if ((rc = read(proxy_fd, buf, bsize)) <= 0) { fd_bits &= ~2; FD_CLR(proxy_fd, &fds); @@ -2584,7 +2610,7 @@ for (fd_bits = 3; fd_bits; ) else { for (nbytes = 0; rc - nbytes > 0; nbytes += i) - if ((i = tls_write(FALSE, sx->buffer + nbytes, rc - nbytes)) < 0) return; + if ((i = tls_write(FALSE, buf + nbytes, rc - nbytes)) < 0) return; } else if (fd_bits & 2) FD_SET(proxy_fd, &fds); @@ -2678,17 +2704,14 @@ set it up. This cannot be done until the identify of the host is known. */ if (tblock->filter_command) { - BOOL rc; - uschar fbuf[64]; - sprintf(CS fbuf, "%.50s transport", tblock->name); - rc = transport_set_up_command(&transport_filter_argv, tblock->filter_command, - TRUE, DEFER, addrlist, fbuf, NULL); transport_filter_timeout = tblock->filter_timeout; /* On failure, copy the error to all addresses, abandon the SMTP call, and yield ERROR. */ - if (!rc) + if (!transport_set_up_command(&transport_filter_argv, + tblock->filter_command, TRUE, DEFER, addrlist, + string_sprintf("%.50s transport", tblock->name), NULL)) { set_errno_nohost(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER, FALSE); @@ -2707,6 +2730,7 @@ if (tblock->filter_command) } } +sx.first_addr = addrlist; /* For messages that have more than the maximum number of envelope recipients, we want to send several transactions down the same SMTP connection. (See @@ -2718,39 +2742,61 @@ transaction to handle. */ SEND_MESSAGE: sx.from_addr = return_path; -sx.first_addr = sx.sync_addr = addrlist; +sx.sync_addr = sx.first_addr; sx.ok = FALSE; sx.send_rset = TRUE; sx.completed_addr = FALSE; -/* Initiate a message transfer. */ +/* If we are a continued-connection-after-verify the MAIL and RCPT +commands were already sent; do not re-send but do mark the addrs as +having been accepted up to RCPT stage. A traditional cont-conn +always has a sequence number greater than one. */ -switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield)) +if (continue_hostname && continue_sequence == 1) { - case 0: break; - case -1: case -2: goto RESPONSE_FAILED; - case -3: goto END_OFF; - case -4: goto SEND_QUIT; - default: goto SEND_FAILED; - } + address_item * addr; -/* If we are an MUA wrapper, abort if any RCPTs were rejected, either -permanently or temporarily. We should have flushed and synced after the last -RCPT. */ + sx.peer_offered = smtp_peer_options; + sx.pending_MAIL = FALSE; + sx.ok = TRUE; + sx.next_addr = NULL; -if (mua_wrapper) + for (addr = addrlist; addr; addr = addr->next) + addr->transport_return = PENDING_OK; + } +else { - address_item *badaddr; - for (badaddr = sx.first_addr; badaddr; badaddr = badaddr->next) - if (badaddr->transport_return != PENDING_OK) - { - /*XXX could we find a better errno than 0 here? */ - set_errno_nohost(addrlist, 0, badaddr->message, FAIL, - testflag(badaddr, af_pass_message)); - sx.ok = FALSE; - break; - } + /* Initiate a message transfer. */ + + switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield)) + { + case 0: break; + case -1: case -2: goto RESPONSE_FAILED; + case -3: goto END_OFF; + case -4: goto SEND_QUIT; + default: goto SEND_FAILED; + } + + /* If we are an MUA wrapper, abort if any RCPTs were rejected, either + permanently or temporarily. We should have flushed and synced after the last + RCPT. */ + + if (mua_wrapper) + { + address_item * a; + unsigned cnt; + + for (a = sx.first_addr, cnt = 0; a && cnt < sx.max_rcpt; a = a->next, cnt++) + if (a->transport_return != PENDING_OK) + { + /*XXX could we find a better errno than 0 here? */ + set_errno_nohost(addrlist, 0, a->message, FAIL, + testflag(a, af_pass_message)); + sx.ok = FALSE; + break; + } + } } /* If ok is TRUE, we know we have got at least one good recipient, and must now @@ -3050,7 +3096,7 @@ else else sprintf(CS sx.buffer, "%.500s\n", addr->unique); - DEBUG(D_deliver) debug_printf("journalling %s\n", sx.buffer); + DEBUG(D_deliver) debug_printf("S:journalling %s\n", sx.buffer); len = Ustrlen(CS sx.buffer); if (write(journal_fd, sx.buffer, len) != len) log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for " @@ -3277,7 +3323,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit) || continue_more || ( #ifdef SUPPORT_TLS - ( tls_out.active < 0 && !continue_proxy + ( tls_out.active < 0 && !continue_proxy_cipher || verify_check_given_host(&sx.ob->hosts_nopass_tls, host) != OK ) && @@ -3349,7 +3395,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit) /* Set up a pipe for proxying TLS for the new transport process */ smtp_peer_options |= PEER_OFFERED_TLS; - if (sx.ok = (socketpair(AF_LOCAL, SOCK_STREAM, 0, pfd) == 0)) + if (sx.ok = (socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0)) socket_fd = pfd[1]; else set_errno(sx.first_addr, errno, US"internal allocation problem", @@ -3376,24 +3422,28 @@ propagate it from the initial just passed the baton to. Fork a child to to do it, and return to get logging done asap. Which way to place the work makes assumptions about post-fork prioritisation which may not hold on all platforms. */ - +#ifdef SUPPORT_TLS if (tls_out.active >= 0) { int pid = fork(); if (pid > 0) /* parent */ { + waitpid(pid, NULL, 0); tls_close(FALSE, FALSE); (void)close(sx.inblock.sock); continue_transport = NULL; continue_hostname = NULL; return yield; } - else if (pid == 0) /* child */ + else if (pid == 0) /* child; fork again to disconnect totally */ { - smtp_proxy_tls(&sx, pfd[0], sx.ob->command_timeout); + if ((pid = fork())) + _exit(pid ? EXIT_FAILURE : EXIT_SUCCESS); + smtp_proxy_tls(sx.buffer, sizeof(sx.buffer), pfd[0], sx.ob->command_timeout); exim_exit(0); } } +#endif } } @@ -3608,8 +3658,10 @@ DEBUG(D_transport) for (host = hostlist; host; host = host->next) debug_printf(" %s:%d\n", host->name, host->port); } - if (continue_hostname) debug_printf("already connected to %s [%s]\n", - continue_hostname, continue_host_address); + if (continue_hostname) + debug_printf("already connected to %s [%s] (on fd %d)\n", + continue_hostname, continue_host_address, + cutthrough.fd >= 0 ? cutthrough.fd : 0); } /* Set the flag requesting that these hosts be added to the waiting