From 94d4f060ff7856b28489911dc2be07060d23451e Mon Sep 17 00:00:00 2001
From: Přemysl Janouch <p.janouch@gmail.com>
Date: Mon, 23 Mar 2015 22:57:13 +0100
Subject: kike: be more careful when closing the connection

---
 kike.c | 26 ++++++++++++++++++++++++--
 1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/kike.c b/kike.c
index 258c088..e2aca2d 100644
--- a/kike.c
+++ b/kike.c
@@ -311,6 +311,7 @@ struct client
 	bool initialized;                   ///< Has any data been received yet?
 	bool registered;                    ///< The user has registered
 	bool closing_link;                  ///< Closing link
+	bool half_closed;                   ///< Closing link: conn. is half-closed
 
 	bool ssl_rx_want_tx;                ///< SSL_read() wants to write
 	bool ssl_tx_want_rx;                ///< SSL_write() wants to read
@@ -787,7 +788,9 @@ client_kill (struct client *c, const char *reason)
 	struct server_context *ctx = c->ctx;
 
 	if (c->ssl)
+		// Note that we might have already called this once, but that is fine
 		(void) SSL_shutdown (c->ssl);
+
 	xclose (c->socket_fd);
 
 	c->socket_event.closed = true;
@@ -2620,8 +2623,27 @@ on_client_ready (const struct pollfd *pfd, void *user_data)
 	client_update_poller (c, pfd);
 
 	// The purpose of the `closing_link' state is to transfer the `ERROR'
-	if (c->closing_link && !c->write_buffer.len)
-		client_kill (c, NULL);
+	if (c->closing_link && !c->half_closed && !c->write_buffer.len)
+	{
+		// To make sure the client has received our ERROR message, we must
+		// first half-close the connection, otherwise it could happen that they
+		// receive a RST from our TCP stack first when we receive further data
+
+		// We only send the "close notify" alert if libssl can write to the
+		// socket at this moment.  All the other data has been already written,
+		// though, and the client will receive a TCP half-close as usual, so
+		// it's not that important if the alert actually gets through.
+		if (c->ssl)
+			(void) SSL_shutdown (c->ssl);
+
+		// Either the shutdown succeeds, in which case we set a flag so that
+		// we don't retry this action and wait until we get an EOF, or it fails
+		// and we just kill the client straight away
+		if (!shutdown (c->socket_fd, SHUT_WR))
+			c->half_closed = true;
+		else
+			client_kill (c, NULL);
+	}
 }
 
 static void
-- 
cgit v1.2.3-70-g09d2