TLS: do decent PIPELINING under TLS, at least with GnuTLS
authorJeremy Harris <jgh146exb@wizmail.org>
Wed, 17 May 2017 16:32:30 +0000 (17:32 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Wed, 17 May 2017 16:32:30 +0000 (17:32 +0100)
I've not found an equivalent in OpenSSL of gnutls_record_cork() nor gnutls_record_check_pending() yet.

20 files changed:
doc/doc-txt/ChangeLog
src/src/auths/get_data.c
src/src/auths/get_no64_data.c
src/src/daemon.c
src/src/dkim_transport.c
src/src/functions.h
src/src/local_scan.h
src/src/receive.c
src/src/smtp_in.c
src/src/smtp_out.c
src/src/tls-gnu.c
src/src/tls-openssl.c
src/src/transport.c
src/src/transports/smtp.c
src/src/verify.c
test/confs/2007
test/scripts/2000-GnuTLS/2090
test/scripts/2100-OpenSSL/2190
test/stdout/2090
test/stdout/2190

index 4bc5235..12c1ff3 100644 (file)
@@ -86,6 +86,12 @@ PP/04 Add commandline_checks_require_admin option.
       AND make fixes to the calling application, such as using `--` to stop
       processing options.
 
+JH/13 Do pipelining under TLS, with GnuTLS.  Previously, although safe, no
+      advantage was taken.  Now take care to pack both (client) MAIL,RCPT,DATA,
+      and (server) responses to those, into a single TLS record each way (this
+      usually means a single packet).  As a side issue, we can now detect
+      over-eager senders in non-pipelined mode.
+
 
 Exim version 4.89
 -----------------
index 11bc581..3857e07 100644 (file)
@@ -30,7 +30,7 @@ auth_get_data(uschar **aptr, uschar *challenge, int challen)
 {
 int c;
 int p = 0;
-smtp_printf("334 %s\r\n", b64encode(challenge, challen));
+smtp_printf("334 %s\r\n", FALSE, b64encode(challenge, challen));
 while ((c = receive_getc(GETC_BUFFER_UNLIMITED)) != '\n' && c != EOF)
   {
   if (p >= big_buffer_size - 1) return BAD64;
index 71e7139..1bb611c 100644 (file)
@@ -31,7 +31,7 @@ auth_get_no64_data(uschar **aptr, uschar *challenge)
 {
 int c;
 int p = 0;
-smtp_printf("334 %s\r\n", challenge);
+smtp_printf("334 %s\r\n", FALSE, challenge);
 while ((c = receive_getc(GETC_BUFFER_UNLIMITED)) != '\n' && c != EOF)
   {
   if (p >= big_buffer_size - 1) return BAD64;
index af50154..06c2b25 100644 (file)
@@ -109,7 +109,7 @@ never_error(uschar *log_msg, uschar *smtp_msg, int was_errno)
 uschar *emsg = (was_errno <= 0)? US"" :
   string_sprintf(": %s", strerror(was_errno));
 log_write(0, LOG_MAIN|LOG_PANIC, "%s%s", log_msg, emsg);
-if (smtp_out != NULL) smtp_printf("421 %s\r\n", smtp_msg);
+if (smtp_out != NULL) smtp_printf("421 %s\r\n", FALSE, smtp_msg);
 }
 
 
@@ -189,7 +189,7 @@ if (getsockname(accept_socket, (struct sockaddr *)(&interface_sockaddr),
   {
   log_write(0, LOG_MAIN | ((errno == ECONNRESET)? 0 : LOG_PANIC),
     "getsockname() failed: %s", strerror(errno));
-  smtp_printf("421 Local problem: getsockname() failed; please try again later\r\n");
+  smtp_printf("421 Local problem: getsockname() failed; please try again later\r\n", FALSE);
   goto ERROR_RETURN;
   }
 
@@ -222,7 +222,7 @@ if (smtp_accept_max > 0 && smtp_accept_count >= smtp_accept_max)
   DEBUG(D_any) debug_printf("rejecting SMTP connection: count=%d max=%d\n",
     smtp_accept_count, smtp_accept_max);
   smtp_printf("421 Too many concurrent SMTP connections; "
-    "please try again later.\r\n");
+    "please try again later.\r\n", FALSE);
   log_write(L_connection_reject,
             LOG_MAIN, "Connection from %s refused: too many connections",
     whofrom);
@@ -241,7 +241,7 @@ if (smtp_load_reserve >= 0)
     {
     DEBUG(D_any) debug_printf("rejecting SMTP connection: load average = %.2f\n",
       (double)load_average/1000.0);
-    smtp_printf("421 Too much load; please try again later.\r\n");
+    smtp_printf("421 Too much load; please try again later.\r\n", FALSE);
     log_write(L_connection_reject,
               LOG_MAIN, "Connection from %s refused: load average = %.2f",
       whofrom, (double)load_average/1000.0);
@@ -312,7 +312,7 @@ if ((max_for_this_host > 0) &&
       "IP address: count=%d max=%d\n",
       host_accept_count, max_for_this_host);
     smtp_printf("421 Too many concurrent SMTP connections "
-      "from this IP address; please try again later.\r\n");
+      "from this IP address; please try again later.\r\n", FALSE);
     log_write(L_connection_reject,
               LOG_MAIN, "Connection from %s refused: too many connections "
       "from that IP address", whofrom);
@@ -396,7 +396,7 @@ if (pid == 0)
           "(smtp_active_hostname): %s", raw_active_hostname,
           expand_string_message);
         smtp_printf("421 Local configuration error; "
-          "please try again later.\r\n");
+          "please try again later.\r\n", FALSE);
         mac_smtp_fflush();
         search_tidyup();
         _exit(EXIT_FAILURE);
index 95e750e..4d26f92 100644 (file)
@@ -77,7 +77,7 @@ else
       {
 #ifdef SUPPORT_TLS
       wwritten = tls_out.active == out_fd
-       ? tls_write(FALSE, p, sread)
+       ? tls_write(FALSE, p, sread, FALSE)
        : write(out_fd, CS p, sread);
 #else
       wwritten = write(out_fd, CS p, sread);
index 963f48e..4be375d 100644 (file)
@@ -51,6 +51,7 @@ extern int     tls_client_start(int, host_item *, address_item *,
 # endif
                uschar **);
 extern void    tls_close(BOOL, BOOL);
+extern BOOL    tls_could_read(void);
 extern int     tls_export_cert(uschar *, size_t, void *);
 extern int     tls_feof(void);
 extern int     tls_ferror(void);
@@ -63,7 +64,7 @@ extern int     tls_read(BOOL, uschar *, size_t);
 extern int     tls_server_start(const uschar *, uschar **);
 extern BOOL    tls_smtp_buffered(void);
 extern int     tls_ungetc(int);
-extern int     tls_write(BOOL, const uschar *, size_t);
+extern int     tls_write(BOOL, const uschar *, size_t, BOOL);
 extern uschar *tls_validate_require_cipher(void);
 extern void    tls_version_report(FILE *);
 # ifndef USE_GNUTLS
index bc4fc8e..c03d870 100644 (file)
@@ -186,8 +186,8 @@ extern void    receive_add_recipient(uschar *, int);
 extern BOOL    receive_remove_recipient(uschar *);
 extern uschar *rfc2047_decode(uschar *, BOOL, uschar *, int, int *, uschar **);
 extern int     smtp_fflush(void);
-extern void    smtp_printf(const char *, ...) PRINTF_FUNCTION(1,2);
-extern void    smtp_vprintf(const char *, va_list);
+extern void    smtp_printf(const char *, BOOL, ...) PRINTF_FUNCTION(1,3);
+extern void    smtp_vprintf(const char *, BOOL, va_list);
 extern uschar *string_copy(const uschar *);
 extern uschar *string_copyn(const uschar *, int);
 extern uschar *string_sprintf(const char *, ...) ALMOST_PRINTF(1,2);
index 6316ff9..9561a4b 100644 (file)
@@ -2194,7 +2194,7 @@ for (;;)
       sender_address,
       sender_fullhost ? " H=" : "", sender_fullhost ? sender_fullhost : US"",
       sender_ident ? " U=" : "",    sender_ident ? sender_ident : US"");
-    smtp_printf("552 Message header not CRLF terminated\r\n");
+    smtp_printf("552 Message header not CRLF terminated\r\n", FALSE);
     bdat_flush_data();
     smtp_reply = US"";
     goto TIDYUP;                             /* Skip to end of function */
@@ -3501,7 +3501,7 @@ else
       int all_pass = OK;
       int all_fail = FAIL;
 
-      smtp_printf("353 PRDR content analysis beginning\r\n");
+      smtp_printf("353 PRDR content analysis beginning\r\n", TRUE);
       /* Loop through recipients, responses must be in same order received */
       for (c = 0; recipients_count > c; c++)
         {
@@ -4302,13 +4302,12 @@ if (smtp_input)
 
       else if (chunking_state > CHUNKING_OFFERED)
        {
-/*XXX rethink for spool_wireformat */
-        smtp_printf("250- %u byte chunk, total %d\r\n250 OK id=%s\r\n",
+        smtp_printf("250- %u byte chunk, total %d\r\n250 OK id=%s\r\n", FALSE,
            chunking_datasize, message_size+message_linecount, message_id);
        chunking_state = CHUNKING_OFFERED;
        }
       else
-        smtp_printf("250 OK id=%s\r\n", message_id);
+        smtp_printf("250 OK id=%s\r\n", FALSE, message_id);
 
       if (host_checking)
         fprintf(stdout,
@@ -4322,7 +4321,7 @@ if (smtp_input)
         smtp_respond((fake_response == DEFER)? US"450" : US"550", 3, TRUE,
           fake_response_text);
       else
-        smtp_printf("%.1024s\r\n", smtp_reply);
+        smtp_printf("%.1024s\r\n", FALSE, smtp_reply);
 
     switch (cutthrough_done)
       {
index e5885e4..5ff1b7f 100644 (file)
@@ -314,10 +314,10 @@ static void smtp_rset_handler(void);
 *************************************************/
 
 /* Synchronization checks can never be perfect because a packet may be on its
-way but not arrived when the check is done. Such checks can in any case only be
-done when TLS is not in use. Normally, the checks happen when commands are
-read: Exim ensures that there is no more input in the input buffer. In normal
-cases, the response to the command will be fast, and there is no further check.
+way but not arrived when the check is done.  Normally, the checks happen when
+commands are read: Exim ensures that there is no more input in the input buffer.
+In normal cases, the response to the command will be fast, and there is no
+further check.
 
 However, for some commands an ACL is run, and that can include delays. In those
 cases, it is useful to do another check on the input just before sending the
@@ -333,15 +333,14 @@ Returns:   TRUE if all is well; FALSE if there is input pending
 */
 
 static BOOL
-check_sync(void)
+wouldblock_reading(void)
 {
 int fd, rc;
 fd_set fds;
 struct timeval tzero;
 
-if (!smtp_enforce_sync || sender_host_address == NULL ||
-    sender_host_notsocket || tls_in.active >= 0)
-  return TRUE;
+if (tls_in.active >= 0 && tls_could_read())
+  return FALSE;
 
 if (smtp_inptr < smtp_inend)
   return FALSE;
@@ -364,6 +363,29 @@ smtp_inptr[rc] = 0;
 return FALSE;
 }
 
+static BOOL
+check_sync(void)
+{
+if (!smtp_enforce_sync || sender_host_address == NULL || sender_host_notsocket)
+  return TRUE;
+
+return wouldblock_reading();
+}
+
+
+/* If there's input waiting (and we're doing pipelineing) then we can pipeline
+a reponse with the one following. */
+
+static BOOL
+pipeline_response(void)
+{
+if (  !smtp_enforce_sync || !sender_host_address
+   || sender_host_notsocket || !pipelining_advertised)
+  return FALSE;
+
+return !wouldblock_reading();
+}
+
 
 
 /*************************************************
@@ -560,7 +582,7 @@ for(;;)
     return EOD;
     }
 
-  smtp_printf("250 %u byte chunk received\r\n", chunking_datasize);
+  smtp_printf("250 %u byte chunk received\r\n", FALSE, chunking_datasize);
   chunking_state = CHUNKING_OFFERED;
   DEBUG(D_receive) debug_printf("chunking state %d\n", (int)chunking_state);
 
@@ -598,7 +620,7 @@ next_cmd:
 
     case NOOP_CMD:
       HAD(SCH_NOOP);
-      smtp_printf("250 OK\r\n");
+      smtp_printf("250 OK\r\n", FALSE);
       goto next_cmd;
 
     case BDAT_CMD:
@@ -774,18 +796,19 @@ they are also picked up later by smtp_fflush().
 
 Arguments:
   format      format string
+  more       further data expected
   ...         optional arguments
 
 Returns:      nothing
 */
 
 void
-smtp_printf(const char *format, ...)
+smtp_printf(const char *format, BOOL more, ...)
 {
 va_list ap;
 
-va_start(ap, format);
-smtp_vprintf(format, ap);
+va_start(ap, more);
+smtp_vprintf(format, more, ap);
 va_end(ap);
 }
 
@@ -794,7 +817,7 @@ smtp_printf(), bearing in mind that in C a vararg function can't directly
 call another vararg function, only a function which accepts a va_list. */
 
 void
-smtp_vprintf(const char *format, va_list ap)
+smtp_vprintf(const char *format, BOOL more, va_list ap)
 {
 BOOL yield;
 
@@ -840,7 +863,7 @@ if (rcpt_in_progress)
 #ifdef SUPPORT_TLS
 if (tls_in.active >= 0)
   {
-  if (tls_write(TRUE, big_buffer, Ustrlen(big_buffer)) < 0)
+  if (tls_write(TRUE, big_buffer, Ustrlen(big_buffer), more) < 0)
     smtp_write_error = -1;
   }
 else
@@ -1619,7 +1642,7 @@ smtp_closedown(uschar *message)
 {
 if (smtp_in == NULL || smtp_batched_input) return;
 receive_swallow_smtp();
-smtp_printf("421 %s\r\n", message);
+smtp_printf("421 %s\r\n", FALSE, message);
 
 for (;;) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED))
   {
@@ -1627,16 +1650,16 @@ for (;;) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED))
   return;
 
   case QUIT_CMD:
-  smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);
+  smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
   mac_smtp_fflush();
   return;
 
   case RSET_CMD:
-  smtp_printf("250 Reset OK\r\n");
+  smtp_printf("250 Reset OK\r\n", FALSE);
   break;
 
   default:
-  smtp_printf("421 %s\r\n", message);
+  smtp_printf("421 %s\r\n", FALSE, message);
   break;
   }
 }
@@ -2490,7 +2513,7 @@ if (!sender_host_unknown)
         {
         log_write(0, LOG_MAIN, "getsockopt() failed from %s: %s",
           host_and_ident(FALSE), strerror(errno));
-        smtp_printf("451 SMTP service not available\r\n");
+        smtp_printf("451 SMTP service not available\r\n", FALSE);
         return FALSE;
         }
       }
@@ -2595,7 +2618,7 @@ if (!sender_host_unknown)
       log_write(0, LOG_MAIN|LOG_REJECT,
         "connection from %s refused (IP options)", host_and_ident(FALSE));
 
-      smtp_printf("554 SMTP service not available\r\n");
+      smtp_printf("554 SMTP service not available\r\n", FALSE);
       return FALSE;
       }
 
@@ -2647,7 +2670,7 @@ if (!sender_host_unknown)
     {
     log_write(L_connection_reject, LOG_MAIN|LOG_REJECT, "refused connection "
       "from %s (host_reject_connection)", host_and_ident(FALSE));
-    smtp_printf("554 SMTP service not available\r\n");
+    smtp_printf("554 SMTP service not available\r\n", FALSE);
     return FALSE;
     }
 
@@ -2678,7 +2701,7 @@ if (!sender_host_unknown)
       log_write(L_connection_reject,
                 LOG_MAIN|LOG_REJECT, "refused connection from %s "
                 "(tcp wrappers)", host_and_ident(FALSE));
-      smtp_printf("554 SMTP service not available\r\n");
+      smtp_printf("554 SMTP service not available\r\n", FALSE);
       }
     else
       {
@@ -2688,7 +2711,7 @@ if (!sender_host_unknown)
       log_write(L_connection_reject,
                 LOG_MAIN|LOG_REJECT, "temporarily refused connection from %s "
                 "(tcp wrappers errno=%d)", host_and_ident(FALSE), save_errno);
-      smtp_printf("451 Temporary local problem - please try later\r\n");
+      smtp_printf("451 Temporary local problem - please try later\r\n", FALSE);
       }
     return FALSE;
     }
@@ -2708,7 +2731,7 @@ if (!sender_host_unknown)
         host_and_ident(FALSE), smtp_accept_count - 1, smtp_accept_max,
         smtp_accept_reserve, (rc == DEFER)? " (lookup deferred)" : "");
       smtp_printf("421 %s: Too many concurrent SMTP connections; "
-        "please try again later\r\n", smtp_active_hostname);
+        "please try again later\r\n", FALSE, smtp_active_hostname);
       return FALSE;
       }
     reserved_host = TRUE;
@@ -2729,7 +2752,7 @@ if (!sender_host_unknown)
       LOG_MAIN, "temporarily refused connection from %s: not in "
       "reserve list and load average = %.2f", host_and_ident(FALSE),
       (double)load_average/1000.0);
-    smtp_printf("421 %s: Too much load; please try again later\r\n",
+    smtp_printf("421 %s: Too much load; please try again later\r\n", FALSE,
       smtp_active_hostname);
     return FALSE;
     }
@@ -2876,13 +2899,13 @@ if (!check_sync())
     "synchronization error (input sent without waiting for greeting): "
     "rejected connection from %s input=\"%s\"", host_and_ident(TRUE),
     string_printing(string_copyn(smtp_inptr, n)));
-  smtp_printf("554 SMTP synchronization error\r\n");
+  smtp_printf("554 SMTP synchronization error\r\n", FALSE);
   return FALSE;
   }
 
 /* Now output the banner */
 
-smtp_printf("%s", ss);
+smtp_printf("%s", FALSE, ss);
 return TRUE;
 }
 
@@ -2929,10 +2952,10 @@ if (++synprot_error_count > smtp_max_synprot_errors)
 
 if (code > 0)
   {
-  smtp_printf("%d%c%s%s%s\r\n", code, (yield == 1)? '-' : ' ',
-    (data == NULL)? US"" : data, (data == NULL)? US"" : US": ", errmess);
+  smtp_printf("%d%c%s%s%s\r\n", FALSE, code, yield == 1 ? '-' : ' ',
+    data ? data : US"", data ? US": " : US"", errmess);
   if (yield == 1)
-    smtp_printf("%d Too many syntax or protocol errors\r\n", code);
+    smtp_printf("%d Too many syntax or protocol errors\r\n", FALSE, code);
   }
 
 return yield;
@@ -2988,25 +3011,27 @@ if (rcpt_in_progress)
   rcpt_in_progress = FALSE;
   }
 
-/* Not output the message, splitting it up into multiple lines if necessary. */
+/* Not output the message, splitting it up into multiple lines if necessary.
+We only handle pipelining these responses as far as nonfinal/final groups,
+not the whole MAIL/RCPT/DATA response set. */
 
 for (;;)
   {
   uschar *nl = Ustrchr(msg, '\n');
   if (nl == NULL)
     {
-    smtp_printf("%.3s%c%.*s%s\r\n", code, final? ' ':'-', esclen, esc, msg);
+    smtp_printf("%.3s%c%.*s%s\r\n", !final, code, final ? ' ':'-', esclen, esc, msg);
     return;
     }
   else if (nl[1] == 0 || no_multiline_responses)
     {
-    smtp_printf("%.3s%c%.*s%.*s\r\n", code, final? ' ':'-', esclen, esc,
+    smtp_printf("%.3s%c%.*s%.*s\r\n", !final, code, final ? ' ':'-', esclen, esc,
       (int)(nl - msg), msg);
     return;
     }
   else
     {
-    smtp_printf("%.3s-%.*s%.*s\r\n", code, esclen, esc, (int)(nl - msg), msg);
+    smtp_printf("%.3s-%.*s%.*s\r\n", TRUE, code, esclen, esc, (int)(nl - msg), msg);
     msg = nl + 1;
     while (isspace(*msg)) msg++;
     }
@@ -3350,7 +3375,7 @@ if (code && defaultrespond)
     va_start(ap, defaultrespond);
     if (!string_vformat(buffer, sizeof(buffer), CS defaultrespond, ap))
       log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_notquit_exit()");
-    smtp_printf("%s %s\r\n", code, buffer);
+    smtp_printf("%s %s\r\n", FALSE, code, buffer);
     va_end(ap);
     }
   mac_smtp_fflush();
@@ -3642,7 +3667,7 @@ if (allow_unqualified_recipient || strcmpic(*recipient, US"postmaster") == 0)
   *recipient = rewrite_address_qualify(*recipient, TRUE);
   return rd;
   }
-smtp_printf("501 %s: recipient address must contain a domain\r\n",
+smtp_printf("501 %s: recipient address must contain a domain\r\n", FALSE,
   smtp_cmd_data);
 log_write(L_smtp_syntax_error,
   LOG_MAIN|LOG_REJECT, "unqualified %s rejected: <%s> %s%s",
@@ -3668,7 +3693,7 @@ if (acl_smtp_quit)
 if (*user_msgp)
   smtp_respond(US"221", 3, TRUE, *user_msgp);
 else
-  smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);
+  smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
 
 #ifdef SUPPORT_TLS
 tls_close(TRUE, TRUE);
@@ -3684,7 +3709,7 @@ smtp_rset_handler(void)
 {
 HAD(SCH_RSET);
 incomplete_transaction_log(US"RSET");
-smtp_printf("250 Reset OK\r\n");
+smtp_printf("250 Reset OK\r\n", FALSE);
 cmd_list[CMD_LIST_RSET].is_mail_cmd = FALSE;
 }
 
@@ -3904,7 +3929,7 @@ while (done <= 0)
       {
       c = smtp_in_auth(au, &s, &ss);
 
-      smtp_printf("%s\r\n", s);
+      smtp_printf("%s\r\n", FALSE, s);
       if (c != OK)
        log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s",
          au->name, host_and_ident(FALSE), ss);
@@ -3951,7 +3976,7 @@ while (done <= 0)
 
     if (!check_helo(smtp_cmd_data))
       {
-      smtp_printf("501 Syntactically invalid %s argument(s)\r\n", hello);
+      smtp_printf("501 Syntactically invalid %s argument(s)\r\n", FALSE, hello);
 
       log_write(0, LOG_MAIN|LOG_REJECT, "rejected %s from %s: syntactically "
         "invalid argument(s): %s", hello, host_and_ident(FALSE),
@@ -4015,7 +4040,7 @@ while (done <= 0)
           {
           if (helo_required)
             {
-            smtp_printf("%d %s argument does not match calling host\r\n",
+            smtp_printf("%d %s argument does not match calling host\r\n", FALSE,
               tempfail? 451 : 550, hello);
             log_write(0, LOG_MAIN|LOG_REJECT, "%srejected \"%s %s\" from %s",
               tempfail? "temporarily " : "",
@@ -4285,7 +4310,7 @@ while (done <= 0)
     s[ptr] = 0;
 
 #ifdef SUPPORT_TLS
-    if (tls_in.active >= 0) (void)tls_write(TRUE, s, ptr); else
+    if (tls_in.active >= 0) (void)tls_write(TRUE, s, ptr, FALSE); else
 #endif
 
       {
@@ -4328,7 +4353,7 @@ while (done <= 0)
 
     if (helo_required && !helo_seen)
       {
-      smtp_printf("503 HELO or EHLO required\r\n");
+      smtp_printf("503 HELO or EHLO required\r\n", FALSE);
       log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL from %s: no "
         "HELO/EHLO given", host_and_ident(FALSE));
       break;
@@ -4354,7 +4379,7 @@ while (done <= 0)
     if (smtp_accept_max_per_connection > 0 &&
         smtp_mailcmd_count > smtp_accept_max_per_connection)
       {
-      smtp_printf("421 too many messages in this connection\r\n");
+      smtp_printf("421 too many messages in this connection\r\n", FALSE);
       log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL command %s: too many "
         "messages in one connection", host_and_ident(TRUE));
       break;
@@ -4619,7 +4644,7 @@ while (done <= 0)
 
     if (thismessage_size_limit > 0 && message_size > thismessage_size_limit)
       {
-      smtp_printf("552 Message size exceeds maximum permitted\r\n");
+      smtp_printf("552 Message size exceeds maximum permitted\r\n", FALSE);
       log_write(L_size_reject,
           LOG_MAIN|LOG_REJECT, "rejected MAIL FROM:<%s> %s: "
           "message too big: size%s=%d max=%d",
@@ -4644,7 +4669,7 @@ while (done <= 0)
          (smtp_check_spool_space && message_size >= 0)?
             message_size + 5000 : 0))
       {
-      smtp_printf("452 Space shortage, please try later\r\n");
+      smtp_printf("452 Space shortage, please try later\r\n", FALSE);
       sender_address = NULL;
       break;
       }
@@ -4666,7 +4691,7 @@ while (done <= 0)
         }
       else
         {
-        smtp_printf("501 %s: sender address must contain a domain\r\n",
+        smtp_printf("501 %s: sender address must contain a domain\r\n", FALSE,
           smtp_cmd_data);
         log_write(L_smtp_syntax_error,
           LOG_MAIN|LOG_REJECT,
@@ -4694,8 +4719,10 @@ while (done <= 0)
 
     if (rc == OK || rc == DISCARD)
       {
+      BOOL more = pipeline_response();
+
       if (!user_msg)
-        smtp_printf("%s%s%s", US"250 OK",
+        smtp_printf("%s%s%s", more, US"250 OK",
                   #ifndef DISABLE_PRDR
                     prdr_requested ? US", PRDR Requested" : US"",
                  #else
@@ -4741,7 +4768,7 @@ while (done <= 0)
       {
       if (pipelining_advertised && last_was_rej_mail)
         {
-        smtp_printf("503 sender not yet given\r\n");
+        smtp_printf("503 sender not yet given\r\n", FALSE);
         was_rej_mail = TRUE;
         }
       else
@@ -4889,7 +4916,7 @@ while (done <= 0)
       if (recipients_max_reject)
         {
         rcpt_fail_count++;
-        smtp_printf("552 too many recipients\r\n");
+        smtp_printf("552 too many recipients\r\n", FALSE);
         if (!toomany)
           log_write(0, LOG_MAIN|LOG_REJECT, "too many recipients: message "
             "rejected: sender=<%s> %s", sender_address, host_and_ident(TRUE));
@@ -4897,7 +4924,7 @@ while (done <= 0)
       else
         {
         rcpt_defer_count++;
-        smtp_printf("452 too many recipients\r\n");
+        smtp_printf("452 too many recipients\r\n", FALSE);
         if (!toomany)
           log_write(0, LOG_MAIN|LOG_REJECT, "too many recipients: excess "
             "temporarily rejected: sender=<%s> %s", sender_address,
@@ -4939,10 +4966,12 @@ while (done <= 0)
 
     if (rc == OK)
       {
+      BOOL more = pipeline_response();
+
       if (user_msg)
         smtp_user_msg(US"250", user_msg);
       else
-        smtp_printf("250 Accepted\r\n");
+        smtp_printf("250 Accepted\r\n", more);
       receive_add_recipient(recipient, -1);
 
       /* Set the dsn flags in the recipients_list */
@@ -4961,7 +4990,7 @@ while (done <= 0)
       if (user_msg)
         smtp_user_msg(US"250", user_msg);
       else
-        smtp_printf("250 Accepted\r\n");
+        smtp_printf("250 Accepted\r\n", FALSE);
       rcpt_fail_count++;
       discarded = TRUE;
       log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> RCPT %s: "
@@ -5054,7 +5083,7 @@ while (done <= 0)
         smtp_respond(code, 3, FALSE, rcpt_smtp_response);
         }
       if (pipelining_advertised && last_was_rcpt)
-        smtp_printf("503 Valid RCPT command must precede %s\r\n",
+        smtp_printf("503 Valid RCPT command must precede %s\r\n", FALSE,
          smtp_names[smtp_connection_had[smtp_ch_index-1]]);
       else
         done = synprot_error(L_smtp_protocol_error, 503, NULL,
@@ -5071,7 +5100,7 @@ while (done <= 0)
       {
       sender_address = NULL;  /* This will allow a new MAIL without RSET */
       sender_address_unrewritten = NULL;
-      smtp_printf("554 Too many recipients\r\n");
+      smtp_printf("554 Too many recipients\r\n", FALSE);
       break;
       }
 
@@ -5106,7 +5135,7 @@ while (done <= 0)
        smtp_user_msg(US"354", user_msg);
       else
        smtp_printf(
-         "354 Enter message, ending with \".\" on a line by itself\r\n");
+         "354 Enter message, ending with \".\" on a line by itself\r\n", FALSE);
       }
 
 #ifdef TCP_QUICKACK
@@ -5129,7 +5158,7 @@ while (done <= 0)
       if (!(address = parse_extract_address(smtp_cmd_data, &errmess,
             &start, &end, &recipient_domain, FALSE)))
        {
-       smtp_printf("501 %s\r\n", errmess);
+       smtp_printf("501 %s\r\n", FALSE, errmess);
        break;
        }
 
@@ -5168,7 +5197,7 @@ while (done <= 0)
            break;
          }
 
-       smtp_printf("%s\r\n", s);
+       smtp_printf("%s\r\n", FALSE, s);
        }
       break;
       }
@@ -5297,7 +5326,7 @@ while (done <= 0)
 
     if (rc == DEFER)
       {
-      smtp_printf("454 TLS currently unavailable\r\n");
+      smtp_printf("454 TLS currently unavailable\r\n", FALSE);
       break;
       }
 
@@ -5330,14 +5359,14 @@ while (done <= 0)
        if (user_msg)
          smtp_respond(US"221", 3, TRUE, user_msg);
        else
-         smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);
+         smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
        log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
          smtp_get_connection_info());
        done = 2;
        break;
 
       default:
-       smtp_printf("554 Security failure\r\n");
+       smtp_printf("554 Security failure\r\n", FALSE);
        break;
       }
     tls_close(TRUE, TRUE);
@@ -5365,7 +5394,7 @@ while (done <= 0)
 
     case NOOP_CMD:
     HAD(SCH_NOOP);
-    smtp_printf("250 OK\r\n");
+    smtp_printf("250 OK\r\n", FALSE);
     break;
 
 
@@ -5376,7 +5405,7 @@ while (done <= 0)
 
     case HELP_CMD:
     HAD(SCH_HELP);
-    smtp_printf("214-Commands supported:\r\n");
+    smtp_printf("214-Commands supported:\r\n", TRUE);
       {
       uschar buffer[256];
       buffer[0] = 0;
@@ -5391,7 +5420,7 @@ while (done <= 0)
       if (acl_smtp_etrn != NULL) Ustrcat(buffer, " ETRN");
       if (acl_smtp_expn != NULL) Ustrcat(buffer, " EXPN");
       if (acl_smtp_vrfy != NULL) Ustrcat(buffer, " VRFY");
-      smtp_printf("214%s\r\n", buffer);
+      smtp_printf("214%s\r\n", FALSE, buffer);
       }
     break;
 
@@ -5460,7 +5489,7 @@ while (done <= 0)
         {
         log_write(0, LOG_MAIN|LOG_PANIC, "failed to set up ETRN command: %s",
           error);
-        smtp_printf("458 Internal failure\r\n");
+        smtp_printf("458 Internal failure\r\n", FALSE);
         break;
         }
       }
@@ -5491,7 +5520,7 @@ while (done <= 0)
         debug_printf("ETRN command is: %s\n", etrn_command);
         debug_printf("ETRN command execution skipped\n");
         }
-      if (user_msg == NULL) smtp_printf("250 OK\r\n");
+      if (user_msg == NULL) smtp_printf("250 OK\r\n", FALSE);
         else smtp_user_msg(US"250", user_msg);
       break;
       }
@@ -5502,7 +5531,7 @@ while (done <= 0)
 
     if (smtp_etrn_serialize && !enq_start(etrn_serialize_key, 1))
       {
-      smtp_printf("458 Already processing %s\r\n", smtp_cmd_data);
+      smtp_printf("458 Already processing %s\r\n", FALSE, smtp_cmd_data);
       break;
       }
 
@@ -5565,12 +5594,12 @@ while (done <= 0)
       {
       log_write(0, LOG_MAIN|LOG_PANIC, "fork of process for ETRN failed: %s",
         strerror(errno));
-      smtp_printf("458 Unable to fork process\r\n");
+      smtp_printf("458 Unable to fork process\r\n", FALSE);
       if (smtp_etrn_serialize) enq_end(etrn_serialize_key);
       }
     else
       {
-      if (user_msg == NULL) smtp_printf("250 OK\r\n");
+      if (user_msg == NULL) smtp_printf("250 OK\r\n", FALSE);
         else smtp_user_msg(US"250", user_msg);
       }
 
@@ -5589,7 +5618,7 @@ while (done <= 0)
     case BADCHAR_CMD:
     done = synprot_error(L_smtp_syntax_error, 0, NULL,       /* Just logs */
       US"NULL character(s) present (shown as '?')");
-    smtp_printf("501 NULL characters are not allowed in SMTP commands\r\n");
+    smtp_printf("501 NULL characters are not allowed in SMTP commands\r\n", FALSE);
     break;
 
 
@@ -5626,7 +5655,7 @@ while (done <= 0)
 
 #ifdef SUPPORT_PROXY
     case PROXY_FAIL_IGNORE_CMD:
-    smtp_printf("503 Command refused, required Proxy negotiation failed\r\n");
+    smtp_printf("503 Command refused, required Proxy negotiation failed\r\n", FALSE);
     break;
 #endif
 
index 4328cb3..253d767 100644 (file)
@@ -323,7 +323,7 @@ pipelining.
 
 Argument:
   outblock   the SMTP output block
-  mode      more-expected, or plain
+  mode      further data expected, or plain
 
 Returns:     TRUE if OK, FALSE on error, with errno set
 */
@@ -333,18 +333,19 @@ flush_buffer(smtp_outblock * outblock, int mode)
 {
 int rc;
 int n = outblock->ptr - outblock->buffer;
+BOOL more = mode == SCMD_MORE;
 
 HDEBUG(D_transport|D_acl) debug_printf_indent("cmd buf flush %d bytes%s\n", n,
-  mode == SCMD_MORE ? " (more expected)" : "");
+  more ? " (more expected)" : "");
 
 #ifdef SUPPORT_TLS
 if (tls_out.active == outblock->sock)
-  rc = tls_write(FALSE, outblock->buffer, n);
+  rc = tls_write(FALSE, outblock->buffer, n, more);
 else
 #endif
   rc = send(outblock->sock, outblock->buffer, n,
 #ifdef MSG_MORE
-           mode == SCMD_MORE ? MSG_MORE : 0
+           more ? MSG_MORE : 0
 #else
            0
 #endif
index d623d8e..c68d11d 100644 (file)
@@ -60,6 +60,9 @@ require current GnuTLS, then we'll drop support for the ancient libraries).
 #if GNUTLS_VERSION_NUMBER >= 0x030014
 # define SUPPORT_SYSDEFAULT_CABUNDLE
 #endif
+#if GNUTLS_VERSION_NUMBER >= 0x030109
+# define SUPPORT_CORK
+#endif
 
 #ifndef DISABLE_OCSP
 # include <gnutls/ocsp.h>
@@ -1745,7 +1748,7 @@ exim_gnutls_state_st * state = NULL;
 if (tls_in.active >= 0)
   {
   tls_error(US"STARTTLS received after TLS started", "", NULL, errstr);
-  smtp_printf("554 Already in TLS\r\n");
+  smtp_printf("554 Already in TLS\r\n", FALSE);
   return FAIL;
   }
 
@@ -1806,7 +1809,7 @@ mode, the fflush() happens when smtp_getc() is called. */
 
 if (!state->tlsp->on_connect)
   {
-  smtp_printf("220 TLS go ahead\r\n");
+  smtp_printf("220 TLS go ahead\r\n", FALSE);
   fflush(smtp_out);
   }
 
@@ -2288,6 +2291,14 @@ if (n > 0)
 }
 
 
+BOOL
+tls_could_read(void)
+{
+return state_server.xfer_buffer_lwm < state_server.xfer_buffer_hwm
+ || gnutls_record_check_pending(state_server.session) > 0;
+}
+
+
 
 
 /*************************************************
@@ -2347,19 +2358,27 @@ Arguments:
   is_server channel specifier
   buff      buffer of data
   len       number of bytes
+  more     more data expected soon
 
 Returns:    the number of bytes after a successful write,
             -1 after a failed write
 */
 
 int
-tls_write(BOOL is_server, const uschar *buff, size_t len)
+tls_write(BOOL is_server, const uschar *buff, size_t len, BOOL more)
 {
 ssize_t outbytes;
 size_t left = len;
 exim_gnutls_state_st *state = is_server ? &state_server : &state_client;
+#ifdef SUPPORT_CORK
+static BOOL corked = FALSE;
+
+if (more && !corked) gnutls_record_cork(state->session);
+#endif
+
+DEBUG(D_tls) debug_printf("%s(%p, " SIZE_T_FMT "%s)\n", __FUNCTION__,
+  buff, left, more ? ", more" : "");
 
-DEBUG(D_tls) debug_printf("tls_do_write(%p, " SIZE_T_FMT ")\n", buff, left);
 while (left > 0)
   {
   DEBUG(D_tls) debug_printf("gnutls_record_send(SSL, %p, " SIZE_T_FMT ")\n",
@@ -2390,6 +2409,14 @@ if (len > INT_MAX)
   len = INT_MAX;
   }
 
+#ifdef SUPPORT_CORK
+if (more != corked)
+  {
+  if (!more) (void) gnutls_record_uncork(state->session, 0);
+  corked = more;
+  }
+#endif
+
 return (int) len;
 }
 
index f17d94b..7f41c10 100644 (file)
@@ -1875,7 +1875,7 @@ static uschar cipherbuf[256];
 if (tls_in.active >= 0)
   {
   tls_error(US"STARTTLS received after TLS started", NULL, US"", errstr);
-  smtp_printf("554 Already in TLS\r\n");
+  smtp_printf("554 Already in TLS\r\n", FALSE);
   return FAIL;
   }
 
@@ -1959,7 +1959,7 @@ mode, the fflush() happens when smtp_getc() is called. */
 SSL_set_session_id_context(server_ssl, sid_ctx, Ustrlen(sid_ctx));
 if (!tls_in.on_connect)
   {
-  smtp_printf("220 TLS go ahead\r\n");
+  smtp_printf("220 TLS go ahead\r\n", FALSE);
   fflush(smtp_out);
   }
 
@@ -2480,6 +2480,13 @@ if (n > 0)
 }
 
 
+BOOL
+tls_could_read(void)
+{
+/* XXX no actual inquiry into library; only our buffer */
+return ssl_xfer_buffer_lwm < ssl_xfer_buffer_hwm;
+}
+
 
 /*************************************************
 *          Read bytes from TLS channel           *
@@ -2533,6 +2540,7 @@ Arguments:
   is_server channel specifier
   buff      buffer of data
   len       number of bytes
+  more     further data expected soon
 
 Returns:    the number of bytes after a successful write,
             -1 after a failed write
@@ -2541,14 +2549,14 @@ Used by both server-side and client-side TLS.
 */
 
 int
-tls_write(BOOL is_server, const uschar *buff, size_t len)
+tls_write(BOOL is_server, const uschar *buff, size_t len, BOOL more)
 {
 int outbytes;
 int error;
 int left = len;
 SSL *ssl = is_server ? server_ssl : client_ssl;
 
-DEBUG(D_tls) debug_printf("tls_do_write(%p, %d)\n", buff, left);
+DEBUG(D_tls) debug_printf("%s(%p, %d)\n", __FUNCTION__, buff, left);
 while (left > 0)
   {
   DEBUG(D_tls) debug_printf("SSL_write(SSL, %p, %d)\n", buff, left);
index 5bcf8c1..04b67f9 100644 (file)
@@ -203,6 +203,7 @@ Arguments:
   tctx      transport context: file descriptor or string to write to
   block     block of bytes to write
   len       number of bytes to write
+  more     further data expected soon
 
 Returns:    TRUE on success, FALSE on failure (with errno preserved);
               transport_count is incremented by the number of bytes written
@@ -233,7 +234,7 @@ for (i = 0; i < 100; i++)
     {
     rc =
 #ifdef SUPPORT_TLS
-       (tls_out.active == fd) ? tls_write(FALSE, block, len) :
+       tls_out.active == fd ? tls_write(FALSE, block, len, more) :
 #endif
 #ifdef MSG_MORE
        more ? send(fd, block, len, MSG_MORE) :
@@ -250,7 +251,7 @@ for (i = 0; i < 100; i++)
 
     rc =
 #ifdef SUPPORT_TLS
-       (tls_out.active == fd) ? tls_write(FALSE, block, len) :
+       tls_out.active == fd ? tls_write(FALSE, block, len, more) :
 #endif
 #ifdef MSG_MORE
        more ? send(fd, block, len, MSG_MORE) :
index 8f1e0bf..454c0f7 100644 (file)
@@ -2618,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);
index 6a50af5..3a8f9b6 100644 (file)
@@ -1177,7 +1177,7 @@ if(cutthrough.fd < 0)
 
 if(
 #ifdef SUPPORT_TLS
-   (tls_out.active == cutthrough.fd) ? tls_write(FALSE, ctblock.buffer, n) :
+   tls_out.active == cutthrough.fd ? tls_write(FALSE, ctblock.buffer, n, FALSE) :
 #endif
    send(cutthrough.fd, ctblock.buffer, n, 0) > 0
   )
@@ -1517,7 +1517,7 @@ va_list ap;
 
 va_start(ap, format);
 if (smtp_out && (f == smtp_out))
-  smtp_vprintf(format, ap);
+  smtp_vprintf(format, FALSE, ap);
 else
   vfprintf(f, format, ap);
 va_end(ap);
index 7b58b73..a16b9a5 100644 (file)
@@ -16,6 +16,7 @@ queue_only
 queue_run_in_order
 
 tls_advertise_hosts = *
+tls_require_ciphers = NORMAL:!DHE-RSA:!DHE-DSS:!ECDHE-RSA:!ECDHE-ECDSA:!ECDHE-PSK
 
 # Set certificate only if server
 
index 278f034..91227e8 100644 (file)
@@ -58,13 +58,7 @@ EHLO rhu.barb
 ??? 250-PIPELINING
 ??? 250-CHUNKING
 ??? 250 HELP
-MAIL FROM:<someone@some.domain>
-RCPT TO:<CALLER@test.ex>
-BDAT 88 LAST
-To: Susan@random.com
-From: Sam@random.com
-Subject: This is a bodyless test message
-
+MAIL FROM:<someone@some.domain>\r\nRCPT TO:<CALLER@test.ex>\r\nBDAT 88 LAST\r\nTo: Susan@random.com\r\nFrom: Sam@random.com\r\nSubject: This is a bodyless test message\r\n
 ??? 250
 ??? 250
 ??? 250-
index 54095d4..e004e5e 100644 (file)
@@ -57,13 +57,7 @@ EHLO rhu.barb
 ??? 250-PIPELINING
 ??? 250-CHUNKING
 ??? 250 HELP
-MAIL FROM:<someone@some.domain>
-RCPT TO:<CALLER@test.ex>
-BDAT 88 LAST
-To: Susan@random.com
-From: Sam@random.com
-Subject: This is a bodyless test message
-
+MAIL FROM:<someone@some.domain>\r\nRCPT TO:<CALLER@test.ex>\r\nBDAT 88 LAST\r\nTo: Susan@random.com\r\nFrom: Sam@random.com\r\nSubject: This is a bodyless test message\r\n
 ??? 250
 ??? 250
 ??? 250-
index 7bf7031..f08abd1 100644 (file)
@@ -89,13 +89,7 @@ Succeeded in starting TLS
 <<< 250-CHUNKING
 ??? 250 HELP
 <<< 250 HELP
->>> MAIL FROM:<someone@some.domain>
->>> RCPT TO:<CALLER@test.ex>
->>> BDAT 88 LAST
->>> To: Susan@random.com
->>> From: Sam@random.com
->>> Subject: This is a bodyless test message
->>> 
+>>> MAIL FROM:<someone@some.domain>\r\nRCPT TO:<CALLER@test.ex>\r\nBDAT 88 LAST\r\nTo: Susan@random.com\r\nFrom: Sam@random.com\r\nSubject: This is a bodyless test message\r\n
 ??? 250
 <<< 250 OK
 ??? 250
index 9d386bd..e09556e 100644 (file)
@@ -91,13 +91,7 @@ Succeeded in starting TLS
 <<< 250-CHUNKING
 ??? 250 HELP
 <<< 250 HELP
->>> MAIL FROM:<someone@some.domain>
->>> RCPT TO:<CALLER@test.ex>
->>> BDAT 88 LAST
->>> To: Susan@random.com
->>> From: Sam@random.com
->>> Subject: This is a bodyless test message
->>> 
+>>> MAIL FROM:<someone@some.domain>\r\nRCPT TO:<CALLER@test.ex>\r\nBDAT 88 LAST\r\nTo: Susan@random.com\r\nFrom: Sam@random.com\r\nSubject: This is a bodyless test message\r\n
 ??? 250
 <<< 250 OK
 ??? 250