mirror of
https://github.com/curl/curl.git
synced 2026-07-09 21:57:17 +03:00
openssl: check SSL_write() length on retries
When an SSL_write() blocks we need to retry it with the same length as before or stupid OpenSSL freaks out. Remember it, limit any longer sends and fail shorter ones. Fixes #18121 Reported-by: adamse on github Closes #18132
This commit is contained in:
parent
8df0a5ba32
commit
ec4c559104
4 changed files with 33 additions and 6 deletions
|
|
@ -380,8 +380,14 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req,
|
|||
data->req.httpversion_sent = httpversion;
|
||||
buf = curlx_dyn_ptr(req);
|
||||
blen = curlx_dyn_len(req);
|
||||
if(!Curl_creader_total_length(data)) {
|
||||
/* Request without body. Try to send directly from the buf given. */
|
||||
/* if the sendbuf is empty and the request without body and
|
||||
* the length to send fits info a sendbuf chunk, we send it directly.
|
||||
* If `blen` is larger then `chunk_size`, we can not. Because we
|
||||
* might have to retry a blocked send later from sendbuf and that
|
||||
* would result in retry sends with a shrunken length. That is trouble. */
|
||||
if(Curl_bufq_is_empty(&data->req.sendbuf) &&
|
||||
!Curl_creader_total_length(data) &&
|
||||
(blen <= data->req.sendbuf.chunk_size)) {
|
||||
data->req.eos_read = TRUE;
|
||||
result = xfer_send(data, buf, blen, blen, &nwritten);
|
||||
if(result)
|
||||
|
|
|
|||
|
|
@ -5316,6 +5316,17 @@ static CURLcode ossl_send(struct Curl_cfilter *cf,
|
|||
|
||||
connssl->io_need = CURL_SSL_IO_NEED_NONE;
|
||||
memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
|
||||
if(octx->blocked_ssl_write_len && (octx->blocked_ssl_write_len != memlen)) {
|
||||
/* The previous SSL_write() call was blocked, using that length.
|
||||
* We need to use that again or OpenSSL will freak out. A shorter
|
||||
* length should not happen and is a bug in libcurl. */
|
||||
if(octx->blocked_ssl_write_len > memlen) {
|
||||
DEBUGASSERT(0);
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
}
|
||||
memlen = octx->blocked_ssl_write_len;
|
||||
}
|
||||
octx->blocked_ssl_write_len = 0;
|
||||
nwritten = SSL_write(octx->ssl, mem, memlen);
|
||||
|
||||
if(nwritten > 0)
|
||||
|
|
@ -5326,16 +5337,19 @@ static CURLcode ossl_send(struct Curl_cfilter *cf,
|
|||
switch(err) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
connssl->io_need = CURL_SSL_IO_NEED_RECV;
|
||||
octx->blocked_ssl_write_len = memlen;
|
||||
result = CURLE_AGAIN;
|
||||
goto out;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
result = CURLE_AGAIN;
|
||||
octx->blocked_ssl_write_len = memlen;
|
||||
goto out;
|
||||
case SSL_ERROR_SYSCALL:
|
||||
{
|
||||
int sockerr = SOCKERRNO;
|
||||
|
||||
if(octx->io_result == CURLE_AGAIN) {
|
||||
octx->blocked_ssl_write_len = memlen;
|
||||
result = CURLE_AGAIN;
|
||||
goto out;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ struct ossl_ctx {
|
|||
X509* server_cert;
|
||||
BIO_METHOD *bio_method;
|
||||
CURLcode io_result; /* result of last BIO cfilter operation */
|
||||
/* blocked writes need to retry with same length, remember it */
|
||||
int blocked_ssl_write_len;
|
||||
#ifndef HAVE_KEYLOG_CALLBACK
|
||||
/* Set to true once a valid keylog entry has been created to avoid dupes.
|
||||
This is a bool and not a bitfield because it is passed by address. */
|
||||
|
|
|
|||
|
|
@ -754,7 +754,7 @@ class TestDownload:
|
|||
|
||||
# download with looong urls
|
||||
@pytest.mark.parametrize("proto", ['http/1.1', 'h2', 'h3'])
|
||||
@pytest.mark.parametrize("url_junk", [1024, 16*1024, 32*1024, 64*1024])
|
||||
@pytest.mark.parametrize("url_junk", [1024, 16*1024, 32*1024, 64*1024, 80*1024, 96*1024])
|
||||
def test_02_36_looong_urls(self, env: Env, httpd, nghttpx, proto, url_junk):
|
||||
if proto == 'h3' and not env.have_h3():
|
||||
pytest.skip("h3 not supported")
|
||||
|
|
@ -784,6 +784,11 @@ class TestDownload:
|
|||
# h2 is unable to send such large headers (frame limits)
|
||||
r.check_exit_code(55)
|
||||
elif proto == 'h3':
|
||||
r.check_exit_code(0)
|
||||
# nghttpx reports 431 Request Header Field too Large
|
||||
r.check_response(http_status=431)
|
||||
if url_junk <= 64*1024:
|
||||
r.check_exit_code(0)
|
||||
# nghttpx reports 431 Request Header Field too Large
|
||||
r.check_response(http_status=431)
|
||||
else:
|
||||
# nghttpx destroys the connection with internal error
|
||||
# ERR_QPACK_HEADER_TOO_LARGE
|
||||
r.check_exit_code(56)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue