Fix continue_more on TLS connection. Bug 2104
[exim.git] / src / src / transports / smtp.c
index 14eb76921edf43e175dbdd526012c06a50e8dcbd..758f1143aeb28480eddeb09d4b4d4e7fb13dd5cb 100644 (file)
@@ -796,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;
@@ -1370,13 +1372,14 @@ smtp_context * sx = tctx->smtp_context;
 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 */
@@ -1739,7 +1742,7 @@ goto SEND_QUIT;
 
   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;
@@ -1772,7 +1775,7 @@ goto SEND_QUIT;
     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);
@@ -1872,7 +1875,7 @@ if (  smtp_peer_options & PEER_OFFERED_TLS
    )  )
   {
   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
@@ -1986,7 +1989,7 @@ if (tls_out.active >= 0)
       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),
@@ -2190,7 +2193,7 @@ FAILED:
 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);
@@ -2199,12 +2202,7 @@ 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)
@@ -2251,6 +2249,9 @@ included in the count.) */
 
 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++;
   }
@@ -2420,7 +2421,7 @@ sx->pending_MAIL = TRUE;     /* The block starts with MAIL */
     }
 #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);
   }
 
@@ -2475,7 +2476,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);
 
@@ -2497,8 +2499,8 @@ for (addr = sx->first_addr, address_count = 0;
     }
 #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)
@@ -2701,17 +2703,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);
@@ -2730,6 +2729,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
@@ -2741,7 +2741,7 @@ 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;
@@ -2783,13 +2783,15 @@ else
 
   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;
        }
@@ -2807,7 +2809,7 @@ to send is. */
 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))
@@ -2844,6 +2846,7 @@ if (!(sx.peer_offered & PEER_OFFERED_CHUNKING) && !sx.ok)
 else
   {
   transport_ctx tctx = {
+    sx.inblock.sock,
     tblock,
     addrlist,
     US".", US"..",    /* Escaping strings */
@@ -2892,10 +2895,9 @@ else
   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
@@ -3334,7 +3336,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
     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));
@@ -3366,26 +3368,33 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
         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
          {
@@ -3402,7 +3411,10 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
 # 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. */
@@ -3425,6 +3437,7 @@ propagate it from the initial
          int pid = fork();
          if (pid > 0)          /* parent */
            {
+           DEBUG(D_transport) debug_printf("proxy-proc inter-pid %d\n", pid);
            waitpid(pid, NULL, 0);
            tls_close(FALSE, FALSE);
            (void)close(sx.inblock.sock);
@@ -3435,7 +3448,10 @@ propagate it from the initial
          else if (pid == 0)    /* child; fork again to disconnect totally */
            {
            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);
            }
@@ -3473,7 +3489,7 @@ This change is being made on 31-Jul-98. After over a year of trouble-free
 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:
 
@@ -3554,7 +3570,7 @@ outblock.ptr = outbuffer;
 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);