diff options
author | Jeremy Harris <jgh146exb@wizmail.org> | 2016-09-27 23:23:52 +0100 |
---|---|---|
committer | Jeremy Harris <jgh146exb@wizmail.org> | 2016-09-28 00:13:52 +0100 |
commit | 60d10ce7e68a5f2cf771a5c079521c8e4f18d157 (patch) | |
tree | 79b04a74b51ebe063ed50f83d09ba739e1cc3add /src | |
parent | c0b9d3e87264ae274b37116103ecc9e1d1b0c647 (diff) |
Drain socket to get clean TCP FINs
Diffstat (limited to 'src')
-rw-r--r-- | src/src/daemon.c | 8 | ||||
-rw-r--r-- | src/src/smtp_in.c | 98 | ||||
-rw-r--r-- | src/src/tls-gnu.c | 38 | ||||
-rw-r--r-- | src/src/tls-openssl.c | 2 | ||||
-rw-r--r-- | src/src/transports/smtp.c | 6 |
5 files changed, 88 insertions, 64 deletions
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 |