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 4bc5235ecdbfe4ac1f215192bab4fc6dc4988b08..12c1ff35b3cccc03077e9a630d10529822045cff 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 11bc581b9b75038cfe6dfe46df7bd42b55e97a6f..3857e07389d3563fb3f4a5b04131ba9120474d2d 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 71e71394ceedb4e05f8f214bcc23ee179e742984..1bb611c1c9d095c525b931e42f44bd6c44a71997 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 af501545439536b8fa2f9a3ee2bb7e7e7bfbcaca..06c2b258c11c693e6b71055266788702536312f2 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 95e750e5edd630edbb6292a9df444f6feebc648d..4d26f9291dba78cc3a75fa93e4eb8610257a5cd8 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 963f48ed41fb1f1adba7cefe38b34f4d486e4f61..4be375d7792c71480a8da3f994ef67584c2f2c6d 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 bc4fc8e25841f9ee2e87c8f116d467e90e89f5ad..c03d8703251fc25a2e89d3d29ac6d223409b4014 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 6316ff9613ae87689e2fa20f6f6cc61a5d19b0aa..9561a4baf79bcdc1f577be207ab5ee73d8ebd14f 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 e5885e477165be47c177ad884670cc551d11e19d..5ff1b7f32d7e9cf50e3fad89431e90d69534ef7f 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 4328cb34278fb8f39f8974c53508bbee25760fb8..253d7670dcf47394ca065eed23f63d4e7a2569d1 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 d623d8e4aae0caf046104d577a0c5c2ce50d2c3a..c68d11d72a9c0ca2c163d75686a858c3a6110b74 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 f17d94b5f85c6a8f2c3c154035ef5103f648e235..7f41c106eecef2e5ab49666017c0d650a5c63813 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 5bcf8c1ae7f5357b6f6885d8e330d224acff16d6..04b67f9e94c5114e7d8e22e7cdeb48ce900d2929 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 8f1e0bff8ac7592fe4a303b93c9eb68b8732cca5..454c0f74dbe15b8a9f8b7ee78de05420897e794b 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 6a50af5069abbf6cbc584189904f685577a7b81f..3a8f9b6bc97af3de252cf5fe334668f91eb00f42 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 7b58b73ee5e7285541b04d52a3b3d6d9afb09c35..a16b9a57ef5faf79b4a03e2fb9d934083809eb40 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 278f0342903d8b2d1f02f4793ae5b941d320cfe4..91227e8caa0a60cf708da40770dbe156cff3d019 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 54095d49a9edbb01bc01885ab278f3921525d371..e004e5e020139f1c72d6d218bebac1c9051bbe40 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 7bf70310a62bf837c451834fafd855dcf03941bc..f08abd10b1b6db2ae664056f176f22cc7037cd34 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 9d386bdf7caf2d99ece5007c1c75708fb601b297..e09556e696e734448f5f90741232adceb9d52046 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