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;
/* 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 */
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 */
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;
)
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",
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. */
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;
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);
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 */
) )
{
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
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),
/* 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);
/* 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)
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++;
}
}
#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);
}
? 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);
}
#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)
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; )
{
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;
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
{
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
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
*/
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)
{
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;
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);
}
}
+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
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;
address_item * addr;
sx.peer_offered = smtp_peer_options;
+ sx.pending_MAIL = FALSE;
sx.ok = TRUE;
sx.next_addr = NULL;
if (mua_wrapper)
{
- address_item *badaddr;
- for (badaddr = sx.first_addr; badaddr; badaddr = badaddr->next)
- if (badaddr->transport_return != PENDING_OK)
+ 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, badaddr->message, FAIL,
- testflag(badaddr, af_pass_message));
+ set_errno_nohost(addrlist, 0, a->message, FAIL,
+ testflag(a, af_pass_message));
sx.ok = FALSE;
break;
}
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))
else
{
transport_ctx tctx = {
+ sx.inblock.sock,
tblock,
addrlist,
US".", US"..", /* Escaping strings */
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
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));
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
{
# 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. */
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);
continue_transport = NULL;
continue_hostname = NULL;
return yield;
}
- else if (pid == 0) /* child */
+ 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);
}
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:
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);
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;
/* 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:
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
/* 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:
"%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);