Drain socket to get clean TCP FINs
authorJeremy Harris <jgh146exb@wizmail.org>
Tue, 27 Sep 2016 22:23:52 +0000 (23:23 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Tue, 27 Sep 2016 23:13:52 +0000 (00:13 +0100)
15 files changed:
src/src/daemon.c
src/src/smtp_in.c
src/src/tls-gnu.c
src/src/tls-openssl.c
src/src/transports/smtp.c
test/log/2013
test/log/2027
test/mail/2013.userx
test/mail/2013.usery [new file with mode: 0644]
test/scripts/2000-GnuTLS/2013
test/src/client.c
test/stderr/2013
test/stdout/0574
test/stdout/2002
test/stdout/2014

index 64412c9..a22ac8d 100644 (file)
@@ -523,9 +523,17 @@ if (pid == 0)
       }
     else
       {
+      int i;
+      uschar * buf[128];
       mac_smtp_fflush();
+      /* drain socket, for clean TCP FINs */
+      for(i = 16; read(fileno(smtp_in), buf, sizeof(buf)) > 0 && i > 0; ) i--;
       search_tidyup();
       smtp_log_no_mail();                 /* Log no mail if configured */
+
+      /*XXX should we pause briefly, hoping that the client will be the
+      active TCP closer hence get the TCP_WAIT endpoint? */
+      DEBUG(D_receive) debug_printf("SMTP>>(close on process exit)\n");
       _exit((rc == 0)? EXIT_SUCCESS : EXIT_FAILURE);
       }
 
index 3b631ea..9484105 100644 (file)
@@ -325,6 +325,7 @@ smtp_getc(void)
 if (smtp_inptr >= smtp_inend)
   {
   int rc, save_errno;
+  if (!smtp_out) return EOF;
   fflush(smtp_out);
   if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
   rc = read(fileno(smtp_in), smtp_inbuffer, in_buffer_size);
@@ -1343,26 +1344,23 @@ if (smtp_in == NULL || smtp_batched_input) return;
 receive_swallow_smtp();
 smtp_printf("421 %s\r\n", message);
 
-for (;;)
+for (;;) switch(smtp_read_command(FALSE))
   {
-  switch(smtp_read_command(FALSE))
-    {
-    case EOF_CMD:
-    return;
+  case EOF_CMD:
+  return;
 
-    case QUIT_CMD:
-    smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);
-    mac_smtp_fflush();
-    return;
+  case QUIT_CMD:
+  smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);
+  mac_smtp_fflush();
+  return;
 
-    case RSET_CMD:
-    smtp_printf("250 Reset OK\r\n");
-    break;
+  case RSET_CMD:
+  smtp_printf("250 Reset OK\r\n");
+  break;
 
-    default:
-    smtp_printf("421 %s\r\n", message);
-    break;
-    }
+  default:
+  smtp_printf("421 %s\r\n", message);
+  break;
   }
 }
 
@@ -3403,7 +3401,7 @@ smtp_quit_handler(uschar ** user_msgp, uschar ** log_msgp)
 {
 HAD(SCH_QUIT);
 incomplete_transaction_log(US"QUIT");
-if (acl_smtp_quit != NULL)
+if (acl_smtp_quit)
   {
   int rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, user_msgp, log_msgp);
   if (rc == ERROR)
@@ -5026,45 +5024,39 @@ while (done <= 0)
     set, but we must still reject all incoming commands. */
 
     DEBUG(D_tls) debug_printf("TLS failed to start\n");
-    while (done <= 0)
+    while (done <= 0) switch(smtp_read_command(FALSE))
       {
-      switch(smtp_read_command(FALSE))
-        {
-        case EOF_CMD:
-        log_write(L_smtp_connection, LOG_MAIN, "%s closed by EOF",
-          smtp_get_connection_info());
-        smtp_notquit_exit(US"tls-failed", NULL, NULL);
-        done = 2;
-        break;
-
-        /* It is perhaps arguable as to which exit ACL should be called here,
-        but as it is probably a situation that almost never arises, it
-        probably doesn't matter. We choose to call the real QUIT ACL, which in
-        some sense is perhaps "right". */
+      case EOF_CMD:
+       log_write(L_smtp_connection, LOG_MAIN, "%s closed by EOF",
+         smtp_get_connection_info());
+       smtp_notquit_exit(US"tls-failed", NULL, NULL);
+       done = 2;
+       break;
 
-        case QUIT_CMD:
-        user_msg = NULL;
-        if (acl_smtp_quit != NULL)
-          {
-          rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, &user_msg,
-            &log_msg);
-          if (rc == ERROR)
-            log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s",
-              log_msg);
-          }
-        if (user_msg == NULL)
-          smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);
-        else
-          smtp_respond(US"221", 3, TRUE, user_msg);
-        log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
-          smtp_get_connection_info());
-        done = 2;
-        break;
+      /* It is perhaps arguable as to which exit ACL should be called here,
+      but as it is probably a situation that almost never arises, it
+      probably doesn't matter. We choose to call the real QUIT ACL, which in
+      some sense is perhaps "right". */
+
+      case QUIT_CMD:
+       user_msg = NULL;
+       if (  acl_smtp_quit
+          && ((rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, &user_msg,
+                             &log_msg)) == ERROR))
+           log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s",
+             log_msg);
+       if (user_msg)
+         smtp_respond(US"221", 3, TRUE, user_msg);
+       else
+         smtp_printf("221 %s closing connection\r\n", 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");
-        break;
-        }
+      default:
+       smtp_printf("554 Security failure\r\n");
+       break;
       }
     tls_close(TRUE, TRUE);
     break;
index a5a680f..383a00f 100644 (file)
@@ -1829,16 +1829,22 @@ alarm(0);
 
 if (rc != GNUTLS_E_SUCCESS)
   {
-  tls_error(US"gnutls_handshake",
-      sigalrm_seen ? "timed out" : gnutls_strerror(rc), NULL);
   /* It seems that, except in the case of a timeout, we have to close the
   connection right here; otherwise if the other end is running OpenSSL it hangs
   until the server times out. */
 
-  if (!sigalrm_seen)
+  if (sigalrm_seen)
+    tls_error(US"gnutls_handshake", "timed out", NULL);
+  else
     {
+    tls_error(US"gnutls_handshake", gnutls_strerror(rc), NULL);
+    gnutls_alert_send_appropriate(state->session, rc);
+    millisleep(500);
+    shutdown(fileno(smtp_out), SHUT_WR);
+    for (rc = 1024; fgetc(smtp_in) != EOF && rc > 0; ) rc--;   /* drain skt */
     (void)fclose(smtp_out);
     (void)fclose(smtp_in);
+    smtp_out = smtp_in = NULL;
     }
 
   return FAIL;
@@ -1863,8 +1869,7 @@ if (  state->verify_requirement != VERIFY_NONE
 
 /* Figure out peer DN, and if authenticated, etc. */
 
-rc = peer_status(state);
-if (rc != OK) return rc;
+if ((rc = peer_status(state)) != OK) return rc;
 
 /* Sets various Exim expansion variables; always safe within server */
 
@@ -2040,8 +2045,13 @@ do
 alarm(0);
 
 if (rc != GNUTLS_E_SUCCESS)
-  return tls_error(US"gnutls_handshake",
-      sigalrm_seen ? "timed out" : gnutls_strerror(rc), state->host);
+  if (sigalrm_seen)
+    {
+    gnutls_alert_send(state->session, GNUTLS_AL_FATAL, GNUTLS_A_USER_CANCELED);
+    return tls_error(US"gnutls_handshake", "timed out", state->host);
+    }
+  else
+    return tls_error(US"gnutls_handshake", gnutls_strerror(rc), state->host);
 
 DEBUG(D_tls) debug_printf("gnutls_handshake was successful\n");
 
@@ -2118,7 +2128,7 @@ if (!state->tlsp || state->tlsp->active < 0) return;  /* TLS was not active */
 
 if (shutdown)
   {
-  DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS\n");
+  DEBUG(D_tls) debug_printf("tls_close() from '%s': shutting down TLS\n");
   gnutls_bye(state->session, GNUTLS_SHUT_WR);
   }
 
@@ -2168,11 +2178,19 @@ if (state->xfer_buffer_lwm >= state->xfer_buffer_hwm)
     ssl_xfer_buffer_size);
   alarm(0);
 
-  /* A zero-byte return appears to mean that the TLS session has been
+  /* Timeouts do not get this far; see command_timeout_handler().
+     A zero-byte return appears to mean that the TLS session has been
      closed down, not that the socket itself has been closed down. Revert to
      non-TLS handling. */
 
-  if (inbytes == 0)
+  if (sigalrm_seen)
+    {
+    DEBUG(D_tls) debug_printf("Got tls read timeout\n");
+    state->xfer_error = 1;
+    return EOF;
+    }
+
+  else if (inbytes == 0)
     {
     DEBUG(D_tls) debug_printf("Got TLS_EOF\n");
 
index 64dcab6..d9db724 100644 (file)
@@ -2504,7 +2504,7 @@ if (*fdp < 0) return;  /* TLS was not active */
 
 if (shutdown)
   {
-  DEBUG(D_tls) debug_printf("tls_close(): shutting down SSL\n");
+  DEBUG(D_tls) debug_printf("tls_close() from '%s': shutting down SSL\n");
   SSL_shutdown(*sslp);
   }
 
index 21c5720..ecdb8bf 100644 (file)
@@ -3162,6 +3162,12 @@ 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("  SMTP(close)>>\n");
+if (lflags.send_quit)
+  {
+  shutdown(outblock.sock, SHUT_WR);
+  for (rc = 16; read(inblock.sock, inbuffer, sizeof(inbuffer)) > 0 && rc > 0;)
+    rc--;                              /* drain socket */
+  }
 (void)close(inblock.sock);
 
 #ifndef DISABLE_EVENT
index d9ff4a3..21fad48 100644 (file)
@@ -3,7 +3,7 @@
 1999-03-02 09:44:33 Start queue run: pid=pppp -qqf
 1999-03-02 09:44:33 10HmaX-0005vi-00 => userx@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=no DN="C=UK,O=The Exim Maintainers,OU=Test Suite,CN=Phil Pennock" C="250 OK id=10HmaZ-0005vi-00"
 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
-1999-03-02 09:44:33 10HmaY-0005vi-00 => userx@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1]* X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=no DN="C=UK,O=The Exim Maintainers,OU=Test Suite,CN=Phil Pennock" C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaY-0005vi-00 => usery@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1]* X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=no DN="C=UK,O=The Exim Maintainers,OU=Test Suite,CN=Phil Pennock" C="250 OK id=10HmbA-0005vi-00"
 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
 1999-03-02 09:44:33 End queue run: pid=pppp -qqf
 
@@ -16,6 +16,6 @@
 1999-03-02 09:44:33 Start queue run: pid=pppp -qf
 1999-03-02 09:44:33 10HmaZ-0005vi-00 => userx <userx@test.ex> R=server T=local_delivery
 1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
-1999-03-02 09:44:33 10HmbA-0005vi-00 => userx <userx@test.ex> R=server T=local_delivery
+1999-03-02 09:44:33 10HmbA-0005vi-00 => usery <usery@test.ex> R=server T=local_delivery
 1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
 1999-03-02 09:44:33 End queue run: pid=pppp -qf
index 25155c9..18b020a 100644 (file)
@@ -3,7 +3,7 @@
 1999-03-02 09:44:33 Start queue run: pid=pppp -qf
 1999-03-02 09:44:33 10HmaX-0005vi-00 => userx@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1] X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=no C="250 OK id=10HmaZ-0005vi-00"
 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
-1999-03-02 09:44:33 10HmaY-0005vi-00 H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] a TLS session is required but an attempt to start TLS failed
+1999-03-02 09:44:33 10HmaY-0005vi-00 H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] TLS error on connection (gnutls_handshake): A TLS fatal alert has been received.
 1999-03-02 09:44:33 10HmaY-0005vi-00 TLS session failure: delivering unencrypted to ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] (not in hosts_require_tls)
 1999-03-02 09:44:33 10HmaY-0005vi-00 => usery@test.ex R=client T=send_to_server H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] C="250 OK id=10HmbA-0005vi-00"
 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
index def44be..a061514 100644 (file)
@@ -16,21 +16,3 @@ TLS: cipher=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 peerdn=
 
 Test message 1
 
-From CALLER@myhost.test.ex Tue Mar 02 09:44:33 1999
-Received: from localhost ([127.0.0.1]:1111 helo=myhost.test.ex)
-       by myhost.test.ex with esmtps (TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256)
-       (Exim x.yz)
-       (envelope-from <CALLER@myhost.test.ex>)
-       id 10HmbA-0005vi-00
-       for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
-Received: from CALLER by myhost.test.ex with local (Exim x.yz)
-       (envelope-from <CALLER@myhost.test.ex>)
-       id 10HmaY-0005vi-00
-       for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
-Message-Id: <E10HmaY-0005vi-00@myhost.test.ex>
-From: CALLER_NAME <CALLER@myhost.test.ex>
-Date: Tue, 2 Mar 1999 09:44:33 +0000
-TLS: cipher=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 peerdn=
-
-Test message 2
-
diff --git a/test/mail/2013.usery b/test/mail/2013.usery
new file mode 100644 (file)
index 0000000..a93a63d
--- /dev/null
@@ -0,0 +1,18 @@
+From CALLER@myhost.test.ex Tue Mar 02 09:44:33 1999
+Received: from localhost ([127.0.0.1]:1111 helo=myhost.test.ex)
+       by myhost.test.ex with esmtps (TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256)
+       (Exim x.yz)
+       (envelope-from <CALLER@myhost.test.ex>)
+       id 10HmbA-0005vi-00
+       for usery@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+Received: from CALLER by myhost.test.ex with local (Exim x.yz)
+       (envelope-from <CALLER@myhost.test.ex>)
+       id 10HmaY-0005vi-00
+       for usery@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaY-0005vi-00@myhost.test.ex>
+From: CALLER_NAME <CALLER@myhost.test.ex>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+TLS: cipher=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 peerdn=
+
+Test message 2
+
index f3d5719..24c2c58 100644 (file)
@@ -5,7 +5,7 @@ exim -DSERVER=server -bd -oX PORT_D
 exim userx@test.ex
 Test message 1
 ****
-exim userx@test.ex
+exim usery@test.ex
 Test message 2
 ****
 exim -qqf -d-all+acl
index 2bd6402..fe646d6 100644 (file)
@@ -1117,6 +1117,8 @@ int rc;
   }
 
 printf("End of script\n");
+shutdown(sock, SHUT_WR);
+while ((rc = read(sock, inbuffer, sizeof(inbuffer))) > 0) ;
 close(sock);
 
 exit(0);
index 9ddfc2f..be80161 100644 (file)
@@ -59,7 +59,7 @@ cmd buf flush ddd bytes
          250-PIPELINING
          250 HELP
   SMTP>> MAIL FROM:<CALLER@myhost.test.ex> SIZE=ssss
-  SMTP>> RCPT TO:<userx@test.ex>
+  SMTP>> RCPT TO:<usery@test.ex>
   SMTP>> DATA
 cmd buf flush ddd bytes
   SMTP<< 250 OK
@@ -70,7 +70,7 @@ cmd buf flush ddd bytes
 cmd buf flush ddd bytes
   SMTP(close)>>
 LOG: MAIN
-  => userx@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1]* X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=no DN="C=UK,O=The Exim Maintainers,OU=Test Suite,CN=Phil Pennock" C="250 OK id=10HmbA-0005vi-00"
+  => usery@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1]* X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=no DN="C=UK,O=The Exim Maintainers,OU=Test Suite,CN=Phil Pennock" C="250 OK id=10HmbA-0005vi-00"
 LOG: MAIN
   Completed
 >>>>>>>>>>>>>>>> Exim pid=pppp terminating with rc=0 >>>>>>>>>>>>>>>>
index aea0754..08aacb1 100644 (file)
@@ -59,3 +59,4 @@ SMTP>> 221 myhost.test.ex closing connection
 LOG: smtp_connection MAIN
   SMTP connection from (test.ex) [127.0.0.1] closed by QUIT
 search_tidyup called
+SMTP>>(close on process exit)
index ec3c1f9..7fd17f0 100644 (file)
@@ -94,6 +94,7 @@ Connecting to ip4.ip4.ip4.ip4 port 1225 ... connected
 ??? 220
 <<< 220 TLS go ahead
 Attempting to start TLS
+A TLS fatal alert has been received.
 Failed to start TLS
 End of script
 Connecting to ip4.ip4.ip4.ip4 port 1225 ... connected
index 56c959f..c7aab62 100644 (file)
@@ -18,6 +18,7 @@ Connecting to ip4.ip4.ip4.ip4 port 1225 ... connected
 ??? 220
 <<< 220 TLS go ahead
 Attempting to start TLS
+A TLS fatal alert has been received.
 Failed to start TLS
 End of script
 Connecting to 127.0.0.1 port 1225 ... connected