TLS: do decent PIPELINING under TLS, at least with GnuTLS
[exim.git] / src / src / transports / smtp.c
index e28a5bfe67f7c6462b36e19bea3959974b9e9579..454c0f74dbe15b8a9f8b7ee78de05420897e794b 100644 (file)
@@ -1186,20 +1186,24 @@ tlsa_lookup(const host_item * host, dns_answer * dnsa, BOOL dane_required)
 /* 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 */
 
@@ -1553,34 +1557,17 @@ if (sx->smtps)
 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;
@@ -1592,7 +1579,9 @@ if (continue_hostname == NULL)
        )
        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",
@@ -1608,12 +1597,32 @@ if (continue_hostname == NULL)
        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. */
@@ -2202,12 +2211,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)
@@ -2254,6 +2258,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++;
   }
@@ -2611,7 +2618,8 @@ for (fd_bits = 3; fd_bits; )
     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);
@@ -3370,18 +3378,22 @@ 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;
@@ -3390,6 +3402,9 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
                                      "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
          {
@@ -3406,7 +3421,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. */
@@ -3429,6 +3447,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);
@@ -3439,7 +3458,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);
            }