schannel: fix hang on unexpected server close

- Treat TLS connection close (either due to a close_notify from the
  server or just closed due to receiving 0) as pending data.

This is because in some cases schannel_recv knows the connection is
closed but has to return actual pending data so it can't return 0 or an
error to indicate no more data. In this case schannel_recv must be
called again, which only happens if readwrite_data sees that there is
still pending data.

Prior to this change if the total size of the body that libcurl expected
to receive from the server was unknown then it was possible under some
network conditions that libcurl would hang waiting to receive more data,
when in fact a close_notify alert indicating no more data would be sent
was already processed.

Fixes https://github.com/curl/curl/issues/12894
Closes https://github.com/curl/curl/pull/12910
This commit is contained in:
Jay Satiro 2024-02-09 03:41:30 -05:00
parent ed596eb409
commit 24d6c2889f

View File

@ -2133,7 +2133,6 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
infof(data, "schannel: server indicated shutdown in a prior call");
goto cleanup;
}
/* It's debatable what to return when !len. Regardless we can't return
immediately because there may be data to decrypt (in the case we want to
decrypt all encrypted cached data) so handle !len later in cleanup.
@ -2317,10 +2316,10 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
/* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not
returned so we have to work around that in cleanup. */
backend->recv_sspi_close_notify = true;
if(!backend->recv_connection_closed) {
if(!backend->recv_connection_closed)
backend->recv_connection_closed = true;
infof(data, "schannel: server closed the connection");
}
infof(data,
"schannel: server close notification received (close_notify)");
goto cleanup;
}
}
@ -2443,7 +2442,10 @@ static bool schannel_data_pending(struct Curl_cfilter *cf,
if(backend->ctxt) /* SSL/TLS is in use */
return (backend->decdata_offset > 0 ||
(backend->encdata_offset > 0 && !backend->encdata_is_incomplete));
(backend->encdata_offset > 0 && !backend->encdata_is_incomplete) ||
backend->recv_connection_closed ||
backend->recv_sspi_close_notify ||
backend->recv_unrecoverable_err);
else
return FALSE;
}