diff options
Diffstat (limited to 'Utilities/cmcurl/lib/vquic/curl_osslq.c')
-rw-r--r-- | Utilities/cmcurl/lib/vquic/curl_osslq.c | 124 |
1 files changed, 91 insertions, 33 deletions
diff --git a/Utilities/cmcurl/lib/vquic/curl_osslq.c b/Utilities/cmcurl/lib/vquic/curl_osslq.c index c499a00..1d53e2c 100644 --- a/Utilities/cmcurl/lib/vquic/curl_osslq.c +++ b/Utilities/cmcurl/lib/vquic/curl_osslq.c @@ -67,7 +67,7 @@ * Chunk size is large enough to take a full DATA frame */ #define H3_STREAM_WINDOW_SIZE (128 * 1024) #define H3_STREAM_CHUNK_SIZE (16 * 1024) -/* The pool keeps spares around and half of a full stream windows +/* The pool keeps spares around and half of a full stream window * seems good. More does not seem to improve performance. * The benefit of the pool is that stream buffer to not keep * spares. So memory consumption goes down when streams run empty, @@ -100,7 +100,7 @@ typedef unsigned long sslerr_t; static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, struct Curl_easy *data); -static const char *SSL_ERROR_to_str(int err) +static const char *osslq_SSL_ERROR_to_str(int err) { switch(err) { case SSL_ERROR_NONE: @@ -139,7 +139,7 @@ static const char *SSL_ERROR_to_str(int err) } /* Return error string for last OpenSSL error */ -static char *ossl_strerror(unsigned long error, char *buf, size_t size) +static char *osslq_strerror(unsigned long error, char *buf, size_t size) { DEBUGASSERT(size); *buf = '\0'; @@ -381,8 +381,8 @@ static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3, } static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf, - struct Curl_easy *data, - int detail, CURLcode def_result) + struct Curl_easy *data, + int detail, CURLcode def_result) { struct cf_osslq_ctx *ctx = cf->ctx; CURLcode result = def_result; @@ -421,17 +421,17 @@ static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf, /* If client certificate is required, communicate the error to client */ result = CURLE_SSL_CLIENTCERT; - ossl_strerror(errdetail, ebuf, sizeof(ebuf)); + osslq_strerror(errdetail, ebuf, sizeof(ebuf)); } #endif else if((lib == ERR_LIB_SSL) && (reason == SSL_R_PROTOCOL_IS_SHUTDOWN)) { ctx->protocol_shutdown = TRUE; - err_descr = "QUIC connectin has been shut down"; + err_descr = "QUIC connection has been shut down"; result = def_result; } else { result = def_result; - ossl_strerror(errdetail, ebuf, sizeof(ebuf)); + osslq_strerror(errdetail, ebuf, sizeof(ebuf)); } /* detail is already set to the SSL error above */ @@ -443,16 +443,14 @@ static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf, if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) { char extramsg[80]=""; int sockerr = SOCKERRNO; - const char *r_ip = NULL; - int r_port = 0; + struct ip_quadruple ip; - Curl_cf_socket_peek(cf->next, data, NULL, NULL, - &r_ip, &r_port, NULL, NULL); + Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); if(sockerr && detail == SSL_ERROR_SYSCALL) Curl_strerror(sockerr, extramsg, sizeof(extramsg)); failf(data, "QUIC connect: %s in connection to %s:%d (%s)", - extramsg[0] ? extramsg : SSL_ERROR_to_str(detail), - ctx->peer.dispname, r_port, r_ip); + extramsg[0] ? extramsg : osslq_SSL_ERROR_to_str(detail), + ctx->peer.dispname, ip.remote_port, ip.remote_ip); } else { /* Could be a CERT problem */ @@ -976,7 +974,7 @@ static nghttp3_callbacks ngh3_callbacks = { }; static CURLcode cf_osslq_h3conn_init(struct cf_osslq_ctx *ctx, SSL *conn, - void *user_data) + void *user_data) { struct cf_osslq_h3conn *h3 = &ctx->h3; CURLcode result; @@ -1039,7 +1037,6 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf, CURLcode result; int rv; const struct Curl_sockaddr_ex *peer_addr = NULL; - int peer_port; BIO *bio = NULL; BIO_ADDR *baddr = NULL; @@ -1061,8 +1058,7 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf, goto out; result = CURLE_QUIC_CONNECT_ERROR; - Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, - &peer_addr, NULL, &peer_port, NULL, NULL); + Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &peer_addr, NULL); if(!peer_addr) goto out; @@ -1078,7 +1074,20 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf, goto out; } + /* Type conversions, see #12861: OpenSSL wants an `int`, but on 64-bit + * Win32 systems, Microsoft defines SOCKET as `unsigned long long`. + */ +#if defined(_WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H) + if(ctx->q.sockfd > INT_MAX) { + failf(data, "Windows socket identifier larger than MAX_INT, " + "unable to set in OpenSSL dgram API."); + result = CURLE_QUIC_CONNECT_ERROR; + goto out; + } + bio = BIO_new_dgram((int)ctx->q.sockfd, BIO_NOCLOSE); +#else bio = BIO_new_dgram(ctx->q.sockfd, BIO_NOCLOSE); +#endif if(!bio) { result = CURLE_OUT_OF_MEMORY; goto out; @@ -1095,6 +1104,16 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf, goto out; } +#ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT + /* Added in OpenSSL v3.3.x */ + if(!SSL_set_feature_request_uint(ctx->tls.ssl, SSL_VALUE_QUIC_IDLE_TIMEOUT, + CURL_QUIC_MAX_IDLE_MS)) { + CURL_TRC_CF(data, cf, "error setting idle timeout, "); + result = CURLE_FAILED_INIT; + goto out; + } +#endif + SSL_set_bio(ctx->tls.ssl, bio, bio); bio = NULL; SSL_set_connect_state(ctx->tls.ssl); @@ -1146,7 +1165,7 @@ static ssize_t h3_quic_recv(void *reader_ctx, SSL_get_stream_read_error_code(x->s->ssl, &app_error_code); CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> RESET, " "rv=%d, app_err=%" PRIu64, - x->s->id, rv, app_error_code); + x->s->id, rv, app_error_code); if(app_error_code != NGHTTP3_H3_NO_ERROR) { x->s->reset = TRUE; } @@ -1361,7 +1380,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf, size_t written; int eos, ok, rv; size_t total_len, acked_len = 0; - bool blocked = FALSE; + bool blocked = FALSE, eos_written = FALSE; n = nghttp3_conn_writev_stream(ctx->h3.conn, &stream_id, &eos, vec, ARRAYSIZE(vec)); @@ -1392,9 +1411,19 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf, for(i = 0; (i < n) && !blocked; ++i) { /* Without stream->s.ssl, we closed that already, so * pretend the write did succeed. */ +#ifdef SSL_WRITE_FLAG_CONCLUDE + /* Since OpenSSL v3.3.x, on last chunk set EOS if needed */ + uint64_t flags = (eos && ((i + 1) == n))? SSL_WRITE_FLAG_CONCLUDE : 0; + written = vec[i].len; + ok = !s->ssl || SSL_write_ex2(s->ssl, vec[i].base, vec[i].len, flags, + &written); + if(ok && flags & SSL_WRITE_FLAG_CONCLUDE) + eos_written = TRUE; +#else written = vec[i].len; ok = !s->ssl || SSL_write_ex(s->ssl, vec[i].base, vec[i].len, &written); +#endif if(ok) { /* As OpenSSL buffers the data, we count this as acknowledged * from nghttp3's point of view */ @@ -1409,7 +1438,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf, case SSL_ERROR_WANT_READ: /* QUIC blocked us from writing more */ CURL_TRC_CF(data, cf, "[%"PRId64"] send %zu bytes to QUIC blocked", - s->id, vec[i].len); + s->id, vec[i].len); written = 0; nghttp3_conn_block_stream(ctx->h3.conn, s->id); s->send_blocked = blocked = TRUE; @@ -1426,6 +1455,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf, if(acked_len > 0 || (eos && !s->send_blocked)) { /* Since QUIC buffers the data written internally, we can tell * nghttp3 that it can move forward on it */ + ctx->q.last_io = Curl_now(); rv = nghttp3_conn_add_write_offset(ctx->h3.conn, s->id, acked_len); if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) { failf(data, "nghttp3_conn_add_write_offset returned error: %s\n", @@ -1444,7 +1474,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf, "to QUIC, eos=%d", s->id, acked_len, total_len, eos); } - if(eos && !s->send_blocked) { + if(eos && !s->send_blocked && !eos_written) { /* wrote everything and H3 indicates end of stream */ CURL_TRC_CF(data, cf, "[%" PRId64 "] closing QUIC stream", s->id); SSL_stream_conclude(s->ssl, 0); @@ -1569,6 +1599,7 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf, if(err == 1) { /* connected */ ctx->handshake_at = now; + ctx->q.last_io = now; CURL_TRC_CF(data, cf, "handshake complete after %dms", (int)Curl_timediff(now, ctx->started_at)); result = cf_osslq_verify_peer(cf, data); @@ -1584,15 +1615,18 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf, int detail = SSL_get_error(ctx->tls.ssl, err); switch(detail) { case SSL_ERROR_WANT_READ: + ctx->q.last_io = now; CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV"); result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data); goto out; case SSL_ERROR_WANT_WRITE: + ctx->q.last_io = now; CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_SEND"); result = CURLE_OK; goto out; #ifdef SSL_ERROR_WANT_ASYNC case SSL_ERROR_WANT_ASYNC: + ctx->q.last_io = now; CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_ASYNC"); result = CURLE_OK; goto out; @@ -1619,13 +1653,11 @@ out: #ifndef CURL_DISABLE_VERBOSE_STRINGS if(result) { - const char *r_ip = NULL; - int r_port = 0; + struct ip_quadruple ip; - Curl_cf_socket_peek(cf->next, data, NULL, NULL, - &r_ip, &r_port, NULL, NULL); + Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip); infof(data, "QUIC connect to %s port %u failed: %s", - r_ip, r_port, curl_easy_strerror(result)); + ip.remote_ip, ip.remote_port, curl_easy_strerror(result)); } #endif if(!result) @@ -1885,7 +1917,7 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf, if(stream->reset) { failf(data, "HTTP/3 stream %" PRId64 " reset by server", stream->s.id); - *err = stream->resp_hds_complete? CURLE_PARTIAL_FILE : CURLE_HTTP3; + *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3; goto out; } else if(!stream->resp_hds_complete) { @@ -2055,7 +2087,24 @@ static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf, if(!ctx->tls.ssl) goto out; - /* TODO: how to check negotiated connection idle time? */ +#ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT + /* Added in OpenSSL v3.3.x */ + { + timediff_t idletime; + uint64_t idle_ms = ctx->max_idle_ms; + if(!SSL_get_value_uint(ctx->tls.ssl, SSL_VALUE_CLASS_FEATURE_NEGOTIATED, + SSL_VALUE_QUIC_IDLE_TIMEOUT, &idle_ms)) { + CURL_TRC_CF(data, cf, "error getting negotiated idle timeout, " + "assume connection is dead."); + goto out; + } + CURL_TRC_CF(data, cf, "negotiated idle timeout: %zums", (size_t)idle_ms); + idletime = Curl_timediff(Curl_now(), ctx->q.last_io); + if(idletime > 0 && (uint64_t)idletime > idle_ms) + goto out; + } + +#endif if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) goto out; @@ -2111,15 +2160,24 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf, int query, int *pres1, void *pres2) { struct cf_osslq_ctx *ctx = cf->ctx; - struct cf_call_data save; switch(query) { case CF_QUERY_MAX_CONCURRENT: { - /* TODO: how to get this? */ - CF_DATA_SAVE(save, cf, data); +#ifdef SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL + /* Added in OpenSSL v3.3.x */ + uint64_t v; + if(!SSL_get_value_uint(ctx->tls.ssl, SSL_VALUE_CLASS_GENERIC, + SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL, &v)) { + CURL_TRC_CF(data, cf, "error getting available local bidi streams"); + return CURLE_HTTP3; + } + /* we report avail + in_use */ + v += CONN_INUSE(cf->conn); + *pres1 = (v > INT_MAX)? INT_MAX : (int)v; +#else *pres1 = 100; +#endif CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1); - CF_DATA_RESTORE(cf, save); return CURLE_OK; } case CF_QUERY_CONNECT_REPLY_MS: |