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 = {
/* 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 */
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. */
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 */
/* 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;
}
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 (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);
}
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);
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);
return TRUE; /* Each address has its status */
}
+#endif /*!MACRO_PREDEF*/
/* vi: aw ai sw=2
*/
/* End of transport/smtp.c */