+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+sx->next_addr = restart_addr ? restart_addr : addr;
+#else
+sx->next_addr = addr;
+#endif
+return 0;
+}
+
+
+#ifndef DISABLE_TLS
+/*****************************************************
+* Proxy TLS connection for another transport process *
+******************************************************/
+/*
+Close the unused end of the pipe, fork once more, then 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. Once both input
+channels are closed, exit the process.
+
+Arguments:
+ ct_ctx tls context
+ buf space to use for buffering
+ bufsiz size of buffer
+ pfd pipe filedescriptor array; [0] is comms to proxied process
+ timeout per-read timeout, seconds
+
+Does not return.
+*/
+
+void
+smtp_proxy_tls(void * ct_ctx, uschar * buf, size_t bsize, int * pfd,
+ int timeout)
+{
+fd_set rfds, efds;
+int max_fd = MAX(pfd[0], tls_out.active.sock) + 1;
+int rc, i;
+BOOL send_tls_shutdown = TRUE;
+
+close(pfd[1]);
+if ((rc = exim_fork(US"tls-proxy")))
+ _exit(rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+
+set_process_info("proxying TLS connection for continued transport");
+FD_ZERO(&rfds);
+FD_SET(tls_out.active.sock, &rfds);
+FD_SET(pfd[0], &rfds);
+
+for (int fd_bits = 3; fd_bits; )
+ {
+ time_t time_left = timeout;
+ 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 *)&rfds, NULL, (SELECT_ARG2_TYPE *)&efds, &tv);
+
+ if (rc < 0 && errno == EINTR)
+ if ((time_left -= time(NULL) - time_start) > 0) continue;
+
+ if (rc <= 0)
+ {
+ DEBUG(D_transport) if (rc == 0) debug_printf("%s: timed out\n", __FUNCTION__);
+ goto done;
+ }
+
+ /* For errors where not readable, bomb out */
+
+ if (FD_ISSET(tls_out.active.sock, &efds) || FD_ISSET(pfd[0], &efds))
+ {
+ DEBUG(D_transport) debug_printf("select: exceptional cond on %s fd\n",
+ FD_ISSET(pfd[0], &efds) ? "proxy" : "tls");
+ if (!(FD_ISSET(tls_out.active.sock, &rfds) || FD_ISSET(pfd[0], &rfds)))
+ goto done;
+ DEBUG(D_transport) debug_printf("- but also readable; no exit yet\n");
+ }
+ }
+ while (rc < 0 || !(FD_ISSET(tls_out.active.sock, &rfds) || FD_ISSET(pfd[0], &rfds)));
+
+ /* handle inbound data */
+ if (FD_ISSET(tls_out.active.sock, &rfds))
+ if ((rc = tls_read(ct_ctx, buf, bsize)) <= 0) /* Expect -1 for EOF; */
+ { /* that reaps the TLS Close Notify record */
+ fd_bits &= ~1;
+ FD_CLR(tls_out.active.sock, &rfds);
+ shutdown(pfd[0], SHUT_WR);
+ timeout = 5;
+ }
+ else
+ for (int nbytes = 0; rc - nbytes > 0; nbytes += i)
+ if ((i = write(pfd[0], buf + nbytes, rc - nbytes)) < 0) goto done;
+
+ /* Handle outbound data. We cannot combine payload and the TLS-close
+ due to the limitations of the (pipe) channel feeding us. Maybe use a unix-domain
+ socket? */
+ if (FD_ISSET(pfd[0], &rfds))
+ if ((rc = read(pfd[0], buf, bsize)) <= 0)
+ {
+ fd_bits &= ~2;
+ FD_CLR(pfd[0], &rfds);
+
+# ifdef EXIM_TCP_CORK /* Use _CORK to get TLS Close Notify in FIN segment */
+ (void) setsockopt(tls_out.active.sock, IPPROTO_TCP, EXIM_TCP_CORK, US &on, sizeof(on));
+# endif
+ tls_shutdown_wr(ct_ctx);
+ send_tls_shutdown = FALSE;
+ shutdown(tls_out.active.sock, SHUT_WR);
+ }
+ else
+ for (int nbytes = 0; rc - nbytes > 0; nbytes += i)
+ if ((i = tls_write(ct_ctx, buf + nbytes, rc - nbytes, FALSE)) < 0)
+ goto done;
+
+ if (fd_bits & 1) FD_SET(tls_out.active.sock, &rfds);
+ if (fd_bits & 2) FD_SET(pfd[0], &rfds);
+ }
+
+done:
+ if (send_tls_shutdown) tls_close(ct_ctx, TLS_SHUTDOWN_NOWAIT);
+ ct_ctx = NULL;
+ testharness_pause_ms(100); /* let logging complete */
+ exim_exit(EXIT_SUCCESS);
+}
+#endif
+
+
+/*************************************************
+* Deliver address list to given host *
+*************************************************/
+
+/* If continue_hostname is not null, we get here only when continuing to
+deliver down an existing channel. The channel was passed as the standard
+input. TLS is never active on a passed channel; the previous process either
+closes it down before passing the connection on, or inserts a TLS-proxy
+process and passes on a cleartext conection.
+
+Otherwise, we have to make a connection to the remote host, and do the
+initial protocol exchange.
+
+When running as an MUA wrapper, if the sender or any recipient is rejected,
+temporarily or permanently, we force failure for all recipients.
+
+Arguments:
+ addrlist chain of potential addresses to deliver; only those whose
+ transport_return field is set to PENDING_DEFER are currently
+ being processed; others should be skipped - they have either
+ been delivered to an earlier host or IP address, or been
+ failed by one of them.
+ host host to deliver to
+ host_af AF_INET or AF_INET6
+ 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
+ because of a non-recipient, non-host failure, that is, a
+ 4xx response to MAIL FROM, DATA, or ".". This is a defer
+ that is specific to the message.
+ suppress_tls if TRUE, don't attempt a TLS connection - this is set for
+ a second attempt after TLS initialization fails
+
+Returns: OK - the connection was made and the delivery attempted;
+ the result for each address is in its data block.
+ DEFER - the connection could not be made, or something failed
+ while setting up the SMTP session, or there was a
+ non-message-specific error, such as a timeout.
+ ERROR - a filter command is specified for this transport,
+ and there was a problem setting it up; OR helo_data
+ or add_headers or authenticated_sender is specified
+ for this transport, and the string failed to expand
+
+ For all non-OK returns the first addr of the list carries the
+ time taken for the attempt.
+*/
+
+static int
+smtp_deliver(address_item *addrlist, host_item *host, int host_af, int defport,
+ uschar *interface, transport_instance *tblock,
+ BOOL *message_defer, BOOL suppress_tls)
+{
+smtp_transport_options_block * ob = SOB tblock->options_block;
+int yield = OK;
+int save_errno;
+int rc;
+
+uschar *message = NULL;
+uschar new_message_id[MESSAGE_ID_LENGTH + 1];
+smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted, for the data buffers */
+BOOL pass_message = FALSE;
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+BOOL mail_limit = FALSE;
+#endif
+#ifdef SUPPORT_DANE
+BOOL dane_held;
+#endif
+BOOL tcw_done = FALSE, tcw = FALSE;
+
+*message_defer = FALSE;
+
+memset(sx, 0, sizeof(*sx));
+sx->addrlist = addrlist;
+sx->conn_args.host = host;
+sx->conn_args.host_af = host_af,
+sx->port = defport;
+sx->conn_args.interface = interface;
+sx->helo_data = NULL;
+sx->conn_args.tblock = tblock;
+/* sx->verify = FALSE; */
+gettimeofday(&sx->delivery_start, NULL);
+sx->sync_addr = sx->first_addr = addrlist;
+
+REPEAT_CONN:
+#ifdef SUPPORT_DANE
+dane_held = FALSE;
+#endif
+
+/* Get the channel set up ready for a message, MAIL FROM being the next
+SMTP command to send. */
+
+if ((rc = smtp_setup_conn(sx, suppress_tls)) != OK)
+ {
+ timesince(&addrlist->delivery_time, &sx->delivery_start);
+ yield = rc;
+ goto TIDYUP;
+ }
+
+#ifdef SUPPORT_DANE
+/* If the connection used DANE, ignore for now any addresses with incompatible
+domains. The SNI has to be the domain. Arrange a whole new TCP conn later,
+just in case only TLS isn't enough. */
+
+if (sx->conn_args.dane)
+ {
+ const uschar * dane_domain = sx->first_addr->domain;