summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2016-09-27 23:23:52 +0100
committerJeremy Harris <jgh146exb@wizmail.org>2016-09-28 00:13:52 +0100
commit60d10ce7e68a5f2cf771a5c079521c8e4f18d157 (patch)
tree79b04a74b51ebe063ed50f83d09ba739e1cc3add /src
parentc0b9d3e87264ae274b37116103ecc9e1d1b0c647 (diff)
Drain socket to get clean TCP FINs
Diffstat (limited to 'src')
-rw-r--r--src/src/daemon.c8
-rw-r--r--src/src/smtp_in.c98
-rw-r--r--src/src/tls-gnu.c38
-rw-r--r--src/src/tls-openssl.c2
-rw-r--r--src/src/transports/smtp.c6
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