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