From 60d10ce7e68a5f2cf771a5c079521c8e4f18d157 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Tue, 27 Sep 2016 23:23:52 +0100 Subject: [PATCH] Drain socket to get clean TCP FINs --- src/src/daemon.c | 8 +++ src/src/smtp_in.c | 98 ++++++++++++++++------------------- src/src/tls-gnu.c | 38 ++++++++++---- src/src/tls-openssl.c | 2 +- src/src/transports/smtp.c | 6 +++ test/log/2013 | 4 +- test/log/2027 | 2 +- test/mail/2013.userx | 18 ------- test/mail/2013.usery | 18 +++++++ test/scripts/2000-GnuTLS/2013 | 2 +- test/src/client.c | 2 + test/stderr/2013 | 4 +- test/stdout/0574 | 1 + test/stdout/2002 | 1 + test/stdout/2014 | 1 + 15 files changed, 117 insertions(+), 88 deletions(-) create mode 100644 test/mail/2013.usery diff --git a/src/src/daemon.c b/src/src/daemon.c index 64412c97d..a22ac8d68 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -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); } diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 3b631ea10..9484105d6 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -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; diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index a5a680fd2..383a00f4e 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -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"); diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 64dcab600..d9db7243f 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -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); } diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 21c57209c..ecdb8bf26 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -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 diff --git a/test/log/2013 b/test/log/2013 index d9ff4a344..21fad4866 100644 --- a/test/log/2013 +++ b/test/log/2013 @@ -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 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 R=server T=local_delivery +1999-03-02 09:44:33 10HmbA-0005vi-00 => usery 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 diff --git a/test/log/2027 b/test/log/2027 index 25155c96a..18b020a62 100644 --- a/test/log/2027 +++ b/test/log/2027 @@ -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 diff --git a/test/mail/2013.userx b/test/mail/2013.userx index def44beae..a0615146b 100644 --- a/test/mail/2013.userx +++ b/test/mail/2013.userx @@ -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 ) - 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 ) - id 10HmaY-0005vi-00 - for userx@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 -Message-Id: -From: CALLER_NAME -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 index 000000000..a93a63d14 --- /dev/null +++ b/test/mail/2013.usery @@ -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 ) + 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 ) + id 10HmaY-0005vi-00 + for usery@test.ex; Tue, 2 Mar 1999 09:44:33 +0000 +Message-Id: +From: CALLER_NAME +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/scripts/2000-GnuTLS/2013 b/test/scripts/2000-GnuTLS/2013 index f3d5719f0..24c2c58fc 100644 --- a/test/scripts/2000-GnuTLS/2013 +++ b/test/scripts/2000-GnuTLS/2013 @@ -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 diff --git a/test/src/client.c b/test/src/client.c index 2bd640205..fe646d64f 100644 --- a/test/src/client.c +++ b/test/src/client.c @@ -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); diff --git a/test/stderr/2013 b/test/stderr/2013 index 9ddfc2f66..be80161c2 100644 --- a/test/stderr/2013 +++ b/test/stderr/2013 @@ -59,7 +59,7 @@ cmd buf flush ddd bytes 250-PIPELINING 250 HELP SMTP>> MAIL FROM: SIZE=ssss - SMTP>> RCPT TO: + SMTP>> RCPT TO: 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 >>>>>>>>>>>>>>>> diff --git a/test/stdout/0574 b/test/stdout/0574 index aea0754b7..08aacb17c 100644 --- a/test/stdout/0574 +++ b/test/stdout/0574 @@ -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) diff --git a/test/stdout/2002 b/test/stdout/2002 index ec3c1f954..7fd17f029 100644 --- a/test/stdout/2002 +++ b/test/stdout/2002 @@ -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 diff --git a/test/stdout/2014 b/test/stdout/2014 index 56c959f20..c7aab62f1 100644 --- a/test/stdout/2014 +++ b/test/stdout/2014 @@ -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 -- 2.30.2