summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2021-01-22 19:58:54 +0000
committerJeremy Harris <jgh146exb@wizmail.org>2021-01-22 21:58:25 +0000
commit97cfe3942f67200f77f6ae9b302409075e4e5792 (patch)
tree6a7951f281ef186b63bffca3065a8bca81d6e9cd /src
parent81df60f6229e66dc8306e55ea2103e577782d984 (diff)
Fix getting non-TLS QUIT in FIN segment
Linux was behaving oddly with the TCP_CORK method, and using MSG_MORE is one fewer syscall.
Diffstat (limited to 'src')
-rw-r--r--src/src/tls-gnu.c2
-rw-r--r--src/src/tls-openssl.c2
-rw-r--r--src/src/transports/smtp.c40
3 files changed, 22 insertions, 22 deletions
diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
index b14bca483..46547d732 100644
--- a/src/src/tls-gnu.c
+++ b/src/src/tls-gnu.c
@@ -3513,6 +3513,8 @@ tls_support * tlsp = state->tlsp;
if (!tlsp || tlsp->active.sock < 0) return; /* TLS was not active */
+tls_write(ct_ctx, NULL, 0, FALSE); /* flush write buffer */
+
if (shutdown)
{
DEBUG(D_tls) debug_printf("tls_close(): shutting down TLS%s\n",
diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
index 0dfd8e01a..0d653a445 100644
--- a/src/src/tls-openssl.c
+++ b/src/src/tls-openssl.c
@@ -4147,6 +4147,8 @@ int *fdp = o_ctx ? &tls_out.active.sock : &tls_in.active.sock;
if (*fdp < 0) return; /* TLS was not active */
+tls_write(ct_ctx, NULL, 0, FALSE); /* flush write buffer */
+
if (shutdown)
{
int rc;
diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
index eb6b77416..0a6bfde18 100644
--- a/src/src/transports/smtp.c
+++ b/src/src/transports/smtp.c
@@ -4380,35 +4380,21 @@ propagate it from the initial
}
/* End off tidily with QUIT unless the connection has died or the socket has
-been passed to another process. There has been discussion on the net about what
-to do after sending QUIT. The wording of the RFC suggests that it is necessary
-to wait for a response, but on the other hand, there isn't anything one can do
-with an error response, other than log it. Exim used to do that. However,
-further discussion suggested that it is positively advantageous not to wait for
-the response, but to close the session immediately. This is supposed to move
-the TCP/IP TIME_WAIT state from the server to the client, thereby removing some
-load from the server. (Hosts that are both servers and clients may not see much
-difference, of course.) Further discussion indicated that this was safe to do
-on Unix systems which have decent implementations of TCP/IP that leave the
-connection around for a while (TIME_WAIT) after the application has gone away.
-This enables the response sent by the server to be properly ACKed rather than
-timed out, as can happen on broken TCP/IP implementations on other OS.
-
-This change is being made on 31-Jul-98. After over a year of trouble-free
-operation, the old commented-out code was removed on 17-Sep-99. */
+been passed to another process. */
SEND_QUIT:
if (sx->send_quit)
- {
-#ifdef EXIM_TCP_CORK
- (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, EXIM_TCP_CORK, US &on, sizeof(on));
-#endif
- (void)smtp_write_command(sx, SCMD_FLUSH, "QUIT\r\n");
- }
+ /* Use _MORE to get QUIT in FIN segment */
+ (void)smtp_write_command(sx, SCMD_MORE, "QUIT\r\n");
END_OFF:
#ifndef DISABLE_TLS
+# ifdef EXIM_TCP_CORK
+if (sx->cctx.tls_ctx) /* Use _CORK to get TLS Close Notify in FIN segment */
+ (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, EXIM_TCP_CORK, US &on, sizeof(on));
+# endif
+
tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
sx->cctx.tls_ctx = NULL;
#endif
@@ -4426,7 +4412,17 @@ case continue_more won't get set. */
HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n");
if (sx->send_quit)
{
+ /* This flushes data queued in the socket, being the QUIT and any TLS Close,
+ sending them along with the client FIN flag. Us (we hope) sending FIN first
+ means we (client) take the TIME_WAIT state, so the server (which likely has a
+ higher connection rate) does no have to. */
+
shutdown(sx->cctx.sock, SHUT_WR);
+
+ /* Wait for (we hope) ack of our QUIT, and a server FIN. Discard any data
+ received, then discard the socket. Any packet received after then, or receive
+ data still in the socket, will get a RST - hence the pause/drain. */
+
millisleep(20);
testharness_pause_ms(200);
if (fcntl(sx->cctx.sock, F_SETFL, O_NONBLOCK) == 0)