+
+ /* otherwise zero: command queued for pipeline */
+ }
+
+/* Pass over all the relevant recipient addresses for this host, which are the
+ones that have status PENDING_DEFER. If we are using PIPELINING, we can send
+several before we have to read the responses for those seen so far. This
+checking is done by a subroutine because it also needs to be done at the end.
+Send only up to max_rcpt addresses at a time, leaving next_addr pointing to
+the next one if not all are sent.
+
+In the MUA wrapper situation, we want to flush the PIPELINING buffer for the
+last address because we want to abort if any recipients have any kind of
+problem, temporary or permanent. We know that all recipient addresses will have
+the PENDING_DEFER status, because only one attempt is ever made, and we know
+that max_rcpt will be large, so all addresses will be done at once.
+
+For verify we flush the pipeline after any (the only) rcpt address. */
+
+for (addr = sx->first_addr, address_count = 0;
+ addr && address_count < sx->max_rcpt;
+ addr = addr->next) if (addr->transport_return == PENDING_DEFER)
+ {
+ int count;
+ BOOL no_flush;
+ uschar * rcpt_addr;
+
+ if (tcp_out_fastopen && !tcp_out_fastopen_logged)
+ {
+ setflag(addr, af_tcp_fastopen_conn);
+ if (tcp_out_fastopen > 1) setflag(addr, af_tcp_fastopen);
+ }
+
+ addr->dsn_aware = sx->peer_offered & OPTION_DSN
+ ? dsn_support_yes : dsn_support_no;
+
+ address_count++;
+ no_flush = pipelining_active && !sx->verify
+ && (!mua_wrapper || addr->next && address_count < sx->max_rcpt);
+
+ build_rcptcmd_options(sx, addr);
+
+ /* Now send the RCPT command, and process outstanding responses when
+ necessary. After a timeout on RCPT, we just end the function, leaving the
+ yield as OK, because this error can often mean that there is a problem with
+ just one address, so we don't want to delay the host. */
+
+ rcpt_addr = transport_rcpt_address(addr, sx->tblock->rcpt_include_affixes);
+
+#ifdef SUPPORT_I18N
+ if ( testflag(sx->addrlist, af_utf8_downcvt)
+ && !(rcpt_addr = string_address_utf8_to_alabel(rcpt_addr, NULL))
+ )
+ {
+ /*XXX could we use a per-address errstr here? Not fail the whole send? */
+ errno = ERRNO_EXPANDFAIL;
+ return -5; /*XXX too harsh? */
+ }
+#endif
+
+ 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)
+ {
+ switch(sync_responses(sx, count, 0))
+ {
+ case 3: sx->ok = TRUE; /* 2xx & 5xx => OK & progress made */
+ case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */
+ break;
+
+ case 1: sx->ok = TRUE; /* 2xx (only) => OK, but if LMTP, */
+ if (!sx->lmtp) /* can't tell about progress yet */
+ sx->completed_addr = TRUE;
+ case 0: /* No 2xx or 5xx, but no probs */
+ break;
+
+ case -1: return -3; /* Timeout on RCPT */
+ case -2: return -2; /* non-MAIL read i/o error */
+ default: return -1; /* any MAIL error */
+ }
+ sx->pending_MAIL = FALSE; /* Dealt with MAIL */
+ }
+ } /* Loop for next address */
+
+tcp_out_fastopen_logged = TRUE;
+sx->next_addr = addr;
+return 0;
+}
+
+
+#ifdef SUPPORT_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
+*/
+
+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, fd_bits, nbytes;
+
+close(pfd[1]);
+if ((rc = fork()))
+ {
+ DEBUG(D_transport) debug_printf("proxy-proc final-pid %d\n", rc);
+ _exit(rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+ }
+
+if (running_in_test_harness) millisleep(100); /* let parent debug out */
+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 (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;
+ }
+
+ 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");
+ goto done;
+ }
+ }
+ 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)
+ {
+ fd_bits &= ~1;
+ FD_CLR(tls_out.active.sock, &rfds);
+ shutdown(pfd[0], SHUT_WR);
+ timeout = 5;
+ }
+ else
+ {
+ for (nbytes = 0; rc - nbytes > 0; nbytes += i)
+ if ((i = write(pfd[0], buf + nbytes, rc - nbytes)) < 0) goto done;
+ }
+ else if (fd_bits & 1)
+ FD_SET(tls_out.active.sock, &rfds);
+
+ /* handle outbound data */
+ if (FD_ISSET(pfd[0], &rfds))
+ if ((rc = read(pfd[0], buf, bsize)) <= 0)
+ {
+ fd_bits = 0;
+ tls_close(ct_ctx, TLS_SHUTDOWN_NOWAIT);
+ ct_ctx = NULL;
+ }
+ else
+ {
+ for (nbytes = 0; rc - nbytes > 0; nbytes += i)
+ if ((i = tls_write(ct_ctx, buf + nbytes, rc - nbytes, FALSE)) < 0)
+ goto done;
+ }
+ else if (fd_bits & 2)
+ FD_SET(pfd[0], &rfds);