if(CURLE_AGAIN == result)
BIO_set_retry_read(bio);
}
+ else if(nread == 0) {
+ connssl->peer_closed = TRUE;
+ }
/* Before returning server replies to the SSL instance, we need
* to have setup the x509 store or verification will fail. */
DEBUGASSERT(backend);
if(backend->handle) {
- if(cf->next && cf->next->connected) {
+ /* Send the TLS shutdown if we are still connected *and* if
+ * the peer did not already close the connection. */
+ if(cf->next && cf->next->connected && !connssl->peer_closed) {
char buf[1024];
int nread, err;
long sslerr;
/* Maybe the server has already sent a close notify alert.
Read it to avoid an RST on the TCP connection. */
- (void)SSL_read(backend->handle, buf, (int)sizeof(buf));
ERR_clear_error();
- if(SSL_shutdown(backend->handle) == 1) {
+ nread = SSL_read(backend->handle, buf, (int)sizeof(buf));
+ err = SSL_get_error(backend->handle, nread);
+ if(!nread && err == SSL_ERROR_ZERO_RETURN) {
+ CURLcode result;
+ ssize_t n;
+ size_t blen = sizeof(buf);
+ CURL_TRC_CF(data, cf, "peer has shutdown TLS");
+ /* SSL_read() will not longer touch the socket, let's receive
+ * directly from the next filter to see if the underlying
+ * connection has also been closed. */
+ n = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
+ if(!n) {
+ connssl->peer_closed = TRUE;
+ CURL_TRC_CF(data, cf, "peer closed connection");
+ }
+ }
+ ERR_clear_error();
+ if(connssl->peer_closed) {
+ /* As the peer closed, we do not expect it to read anything more we
+ * may send. It may be harmful, leading to TCP RST and delaying
+ * a lingering close. Just leave. */
+ CURL_TRC_CF(data, cf, "not from sending TLS shutdown on "
+ "connection closed by peer");
+ }
+ else if(SSL_shutdown(backend->handle) == 1) {
CURL_TRC_CF(data, cf, "SSL shutdown finished");
}
else {
read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
{
struct io_ctx *io_ctx = userdata;
+ struct ssl_connect_data *const connssl = io_ctx->cf->ctx;
CURLcode result;
int ret = 0;
ssize_t nread = Curl_conn_cf_recv(io_ctx->cf->next, io_ctx->data,
else
ret = EINVAL;
}
+ else if(nread == 0)
+ connssl->peer_closed = TRUE;
*out_n = (int)nread;
return ret;
}
DEBUGASSERT(backend);
- if(backend->conn) {
+ if(backend->conn && !connssl->peer_closed) {
rustls_connection_send_close_notify(backend->conn);
n = cr_send(cf, data, NULL, 0, &tmperr);
if(n < 0) {
wolfSSL_BIO_clear_retry_flags(bio);
if(nread < 0 && CURLE_AGAIN == result)
BIO_set_retry_read(bio);
+ else if(nread == 0)
+ connssl->peer_closed = TRUE;
return (int)nread;
}
/* Maybe the server has already sent a close notify alert.
Read it to avoid an RST on the TCP connection. */
(void)wolfSSL_read(backend->handle, buf, (int)sizeof(buf));
- (void)wolfSSL_shutdown(backend->handle);
+ if(!connssl->peer_closed)
+ (void)wolfSSL_shutdown(backend->handle);
wolfSSL_free(backend->handle);
backend->handle = NULL;
}