VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/vquic/curl_osslq.c@ 106542

最後變更 在這個檔案從106542是 104204,由 vboxsync 提交於 11 月 前

fixing export flags in libs

檔案大小: 67.1 KB
 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25#include "curl_setup.h"
26
27#if defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
28
29#include <openssl/ssl.h>
30#include <openssl/bio.h>
31#include <openssl/err.h>
32#include <nghttp3/nghttp3.h>
33
34#include "urldata.h"
35#include "sendf.h"
36#include "strdup.h"
37#include "rand.h"
38#include "multiif.h"
39#include "strcase.h"
40#include "cfilters.h"
41#include "cf-socket.h"
42#include "connect.h"
43#include "progress.h"
44#include "strerror.h"
45#include "dynbuf.h"
46#include "http1.h"
47#include "select.h"
48#include "inet_pton.h"
49#include "vquic.h"
50#include "vquic_int.h"
51#include "vquic-tls.h"
52#include "vtls/keylog.h"
53#include "vtls/vtls.h"
54#include "vtls/openssl.h"
55#include "curl_osslq.h"
56
57#include "warnless.h"
58
59/* The last 3 #include files should be in this order */
60#include "curl_printf.h"
61#include "curl_memory.h"
62#include "memdebug.h"
63
64/* A stream window is the maximum amount we need to buffer for
65 * each active transfer. We use HTTP/3 flow control and only ACK
66 * when we take things out of the buffer.
67 * Chunk size is large enough to take a full DATA frame */
68#define H3_STREAM_WINDOW_SIZE (128 * 1024)
69#define H3_STREAM_CHUNK_SIZE (16 * 1024)
70/* The pool keeps spares around and half of a full stream window
71 * seems good. More does not seem to improve performance.
72 * The benefit of the pool is that stream buffer to not keep
73 * spares. So memory consumption goes down when streams run empty,
74 * have a large upload done, etc. */
75#define H3_STREAM_POOL_SPARES \
76 (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2
77/* Receive and Send max number of chunks just follows from the
78 * chunk size and window size */
79#define H3_STREAM_RECV_CHUNKS \
80 (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
81#define H3_STREAM_SEND_CHUNKS \
82 (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
83
84#ifndef ARRAYSIZE
85#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
86#endif
87
88#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
89typedef uint32_t sslerr_t;
90#else
91typedef unsigned long sslerr_t;
92#endif
93
94
95/* How to access `call_data` from a cf_osslq filter */
96#undef CF_CTX_CALL_DATA
97#define CF_CTX_CALL_DATA(cf) \
98 ((struct cf_osslq_ctx *)(cf)->ctx)->call_data
99
100static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
101 struct Curl_easy *data);
102
103static const char *osslq_SSL_ERROR_to_str(int err)
104{
105 switch(err) {
106 case SSL_ERROR_NONE:
107 return "SSL_ERROR_NONE";
108 case SSL_ERROR_SSL:
109 return "SSL_ERROR_SSL";
110 case SSL_ERROR_WANT_READ:
111 return "SSL_ERROR_WANT_READ";
112 case SSL_ERROR_WANT_WRITE:
113 return "SSL_ERROR_WANT_WRITE";
114 case SSL_ERROR_WANT_X509_LOOKUP:
115 return "SSL_ERROR_WANT_X509_LOOKUP";
116 case SSL_ERROR_SYSCALL:
117 return "SSL_ERROR_SYSCALL";
118 case SSL_ERROR_ZERO_RETURN:
119 return "SSL_ERROR_ZERO_RETURN";
120 case SSL_ERROR_WANT_CONNECT:
121 return "SSL_ERROR_WANT_CONNECT";
122 case SSL_ERROR_WANT_ACCEPT:
123 return "SSL_ERROR_WANT_ACCEPT";
124#if defined(SSL_ERROR_WANT_ASYNC)
125 case SSL_ERROR_WANT_ASYNC:
126 return "SSL_ERROR_WANT_ASYNC";
127#endif
128#if defined(SSL_ERROR_WANT_ASYNC_JOB)
129 case SSL_ERROR_WANT_ASYNC_JOB:
130 return "SSL_ERROR_WANT_ASYNC_JOB";
131#endif
132#if defined(SSL_ERROR_WANT_EARLY)
133 case SSL_ERROR_WANT_EARLY:
134 return "SSL_ERROR_WANT_EARLY";
135#endif
136 default:
137 return "SSL_ERROR unknown";
138 }
139}
140
141/* Return error string for last OpenSSL error */
142static char *osslq_strerror(unsigned long error, char *buf, size_t size)
143{
144 DEBUGASSERT(size);
145 *buf = '\0';
146
147#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
148 ERR_error_string_n((uint32_t)error, buf, size);
149#else
150 ERR_error_string_n(error, buf, size);
151#endif
152
153 if(!*buf) {
154 const char *msg = error ? "Unknown error" : "No error";
155 if(strlen(msg) < size)
156 strcpy(buf, msg);
157 }
158
159 return buf;
160}
161
162static CURLcode make_bio_addr(BIO_ADDR **pbio_addr,
163 const struct Curl_sockaddr_ex *addr)
164{
165 BIO_ADDR *ba;
166 CURLcode result = CURLE_FAILED_INIT;
167
168 ba = BIO_ADDR_new();
169 if(!ba) {
170 result = CURLE_OUT_OF_MEMORY;
171 goto out;
172 }
173
174 switch(addr->family) {
175 case AF_INET: {
176 struct sockaddr_in * const sin =
177 (struct sockaddr_in * const)(void *)&addr->sa_addr;
178 if(!BIO_ADDR_rawmake(ba, AF_INET, &sin->sin_addr,
179 sizeof(sin->sin_addr), sin->sin_port)) {
180 goto out;
181 }
182 result = CURLE_OK;
183 break;
184 }
185#ifdef ENABLE_IPV6
186 case AF_INET6: {
187 struct sockaddr_in6 * const sin =
188 (struct sockaddr_in6 * const)(void *)&addr->sa_addr;
189 if(!BIO_ADDR_rawmake(ba, AF_INET6, &sin->sin6_addr,
190 sizeof(sin->sin6_addr), sin->sin6_port)) {
191 }
192 result = CURLE_OK;
193 break;
194 }
195#endif /* ENABLE_IPV6 */
196 default:
197 /* sunsupported */
198 DEBUGASSERT(0);
199 break;
200 }
201
202out:
203 if(result && ba) {
204 BIO_ADDR_free(ba);
205 ba = NULL;
206 }
207 *pbio_addr = ba;
208 return result;
209}
210
211/* QUIC stream (not necessarily H3) */
212struct cf_osslq_stream {
213 int64_t id;
214 SSL *ssl;
215 struct bufq recvbuf; /* QUIC war data recv buffer */
216 BIT(recvd_eos);
217 BIT(closed);
218 BIT(reset);
219 BIT(send_blocked);
220};
221
222static CURLcode cf_osslq_stream_open(struct cf_osslq_stream *s,
223 SSL *conn,
224 uint64_t flags,
225 struct bufc_pool *bufcp,
226 void *user_data)
227{
228 DEBUGASSERT(!s->ssl);
229 Curl_bufq_initp(&s->recvbuf, bufcp, 1, BUFQ_OPT_NONE);
230 s->ssl = SSL_new_stream(conn, flags);
231 if(!s->ssl) {
232 return CURLE_FAILED_INIT;
233 }
234 s->id = SSL_get_stream_id(s->ssl);
235 SSL_set_app_data(s->ssl, user_data);
236 return CURLE_OK;
237}
238
239static void cf_osslq_stream_cleanup(struct cf_osslq_stream *s)
240{
241 if(s->ssl) {
242 SSL_set_app_data(s->ssl, NULL);
243 SSL_free(s->ssl);
244 }
245 Curl_bufq_free(&s->recvbuf);
246 memset(s, 0, sizeof(*s));
247}
248
249static void cf_osslq_stream_close(struct cf_osslq_stream *s)
250{
251 if(s->ssl) {
252 SSL_free(s->ssl);
253 s->ssl = NULL;
254 }
255}
256
257struct cf_osslq_h3conn {
258 nghttp3_conn *conn;
259 nghttp3_settings settings;
260 struct cf_osslq_stream s_ctrl;
261 struct cf_osslq_stream s_qpack_enc;
262 struct cf_osslq_stream s_qpack_dec;
263 struct cf_osslq_stream remote_ctrl[3]; /* uni streams opened by the peer */
264 size_t remote_ctrl_n; /* number of peer streams opened */
265};
266
267static void cf_osslq_h3conn_cleanup(struct cf_osslq_h3conn *h3)
268{
269 size_t i;
270
271 if(h3->conn)
272 nghttp3_conn_del(h3->conn);
273 cf_osslq_stream_cleanup(&h3->s_ctrl);
274 cf_osslq_stream_cleanup(&h3->s_qpack_enc);
275 cf_osslq_stream_cleanup(&h3->s_qpack_dec);
276 for(i = 0; i < h3->remote_ctrl_n; ++i) {
277 cf_osslq_stream_cleanup(&h3->remote_ctrl[i]);
278 }
279}
280
281struct cf_osslq_ctx {
282 struct cf_quic_ctx q;
283 struct ssl_peer peer;
284 struct quic_tls_ctx tls;
285 struct cf_call_data call_data;
286 struct cf_osslq_h3conn h3;
287 struct curltime started_at; /* time the current attempt started */
288 struct curltime handshake_at; /* time connect handshake finished */
289 struct curltime first_byte_at; /* when first byte was recvd */
290 struct curltime reconnect_at; /* time the next attempt should start */
291 struct bufc_pool stream_bufcp; /* chunk pool for streams */
292 size_t max_stream_window; /* max flow window for one stream */
293 uint64_t max_idle_ms; /* max idle time for QUIC connection */
294 BIT(got_first_byte); /* if first byte was received */
295#ifdef USE_OPENSSL
296 BIT(x509_store_setup); /* if x509 store has been set up */
297 BIT(protocol_shutdown); /* QUIC connection is shut down */
298#endif
299};
300
301static void cf_osslq_ctx_clear(struct cf_osslq_ctx *ctx)
302{
303 struct cf_call_data save = ctx->call_data;
304
305 cf_osslq_h3conn_cleanup(&ctx->h3);
306 Curl_vquic_tls_cleanup(&ctx->tls);
307 vquic_ctx_free(&ctx->q);
308 Curl_bufcp_free(&ctx->stream_bufcp);
309 Curl_ssl_peer_cleanup(&ctx->peer);
310
311 memset(ctx, 0, sizeof(*ctx));
312 ctx->call_data = save;
313}
314
315static void cf_osslq_close(struct Curl_cfilter *cf, struct Curl_easy *data)
316{
317 struct cf_osslq_ctx *ctx = cf->ctx;
318 struct cf_call_data save;
319
320 CF_DATA_SAVE(save, cf, data);
321 if(ctx && ctx->tls.ssl) {
322 /* TODO: send connection close */
323 CURL_TRC_CF(data, cf, "cf_osslq_close()");
324 cf_osslq_ctx_clear(ctx);
325 }
326
327 cf->connected = FALSE;
328 CF_DATA_RESTORE(cf, save);
329}
330
331static void cf_osslq_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
332{
333 struct cf_osslq_ctx *ctx = cf->ctx;
334 struct cf_call_data save;
335
336 CF_DATA_SAVE(save, cf, data);
337 CURL_TRC_CF(data, cf, "destroy");
338 if(ctx) {
339 CURL_TRC_CF(data, cf, "cf_osslq_destroy()");
340 cf_osslq_ctx_clear(ctx);
341 free(ctx);
342 }
343 cf->ctx = NULL;
344 /* No CF_DATA_RESTORE(cf, save) possible */
345 (void)save;
346}
347
348static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3,
349 SSL *stream_ssl,
350 struct Curl_cfilter *cf,
351 struct Curl_easy *data)
352{
353 struct cf_osslq_ctx *ctx = cf->ctx;
354 int64_t stream_id = SSL_get_stream_id(stream_ssl);
355
356 if(h3->remote_ctrl_n >= ARRAYSIZE(h3->remote_ctrl)) {
357 /* rejected, we are full */
358 CURL_TRC_CF(data, cf, "[%" PRId64 "] rejecting additional remote stream",
359 stream_id);
360 SSL_free(stream_ssl);
361 return CURLE_FAILED_INIT;
362 }
363 switch(SSL_get_stream_type(stream_ssl)) {
364 case SSL_STREAM_TYPE_READ: {
365 struct cf_osslq_stream *nstream = &h3->remote_ctrl[h3->remote_ctrl_n++];
366 nstream->id = stream_id;
367 nstream->ssl = stream_ssl;
368 Curl_bufq_initp(&nstream->recvbuf, &ctx->stream_bufcp, 1, BUFQ_OPT_NONE);
369 CURL_TRC_CF(data, cf, "[%" PRId64 "] accepted new remote uni stream",
370 stream_id);
371 break;
372 }
373 default:
374 CURL_TRC_CF(data, cf, "[%" PRId64 "] rejecting remote non-uni-read"
375 " stream", stream_id);
376 SSL_free(stream_ssl);
377 return CURLE_FAILED_INIT;
378 }
379 return CURLE_OK;
380
381}
382
383static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf,
384 struct Curl_easy *data,
385 int detail, CURLcode def_result)
386{
387 struct cf_osslq_ctx *ctx = cf->ctx;
388 CURLcode result = def_result;
389 sslerr_t errdetail;
390 char ebuf[256] = "unknown";
391 const char *err_descr = ebuf;
392 long lerr;
393 int lib;
394 int reason;
395 struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
396
397 errdetail = ERR_get_error();
398 lib = ERR_GET_LIB(errdetail);
399 reason = ERR_GET_REASON(errdetail);
400
401 if((lib == ERR_LIB_SSL) &&
402 ((reason == SSL_R_CERTIFICATE_VERIFY_FAILED) ||
403 (reason == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED))) {
404 result = CURLE_PEER_FAILED_VERIFICATION;
405
406 lerr = SSL_get_verify_result(ctx->tls.ssl);
407 if(lerr != X509_V_OK) {
408 ssl_config->certverifyresult = lerr;
409 msnprintf(ebuf, sizeof(ebuf),
410 "SSL certificate problem: %s",
411 X509_verify_cert_error_string(lerr));
412 }
413 else
414 err_descr = "SSL certificate verification failed";
415 }
416#if defined(SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)
417 /* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on
418 OpenSSL version above v1.1.1, not LibreSSL, BoringSSL, or AWS-LC */
419 else if((lib == ERR_LIB_SSL) &&
420 (reason == SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)) {
421 /* If client certificate is required, communicate the
422 error to client */
423 result = CURLE_SSL_CLIENTCERT;
424 osslq_strerror(errdetail, ebuf, sizeof(ebuf));
425 }
426#endif
427 else if((lib == ERR_LIB_SSL) && (reason == SSL_R_PROTOCOL_IS_SHUTDOWN)) {
428 ctx->protocol_shutdown = TRUE;
429 err_descr = "QUIC connection has been shut down";
430 result = def_result;
431 }
432 else {
433 result = def_result;
434 osslq_strerror(errdetail, ebuf, sizeof(ebuf));
435 }
436
437 /* detail is already set to the SSL error above */
438
439 /* If we e.g. use SSLv2 request-method and the server doesn't like us
440 * (RST connection, etc.), OpenSSL gives no explanation whatsoever and
441 * the SO_ERROR is also lost.
442 */
443 if(CURLE_SSL_CONNECT_ERROR == result && errdetail == 0) {
444 char extramsg[80]="";
445 int sockerr = SOCKERRNO;
446 struct ip_quadruple ip;
447
448 Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
449 if(sockerr && detail == SSL_ERROR_SYSCALL)
450 Curl_strerror(sockerr, extramsg, sizeof(extramsg));
451 failf(data, "QUIC connect: %s in connection to %s:%d (%s)",
452 extramsg[0] ? extramsg : osslq_SSL_ERROR_to_str(detail),
453 ctx->peer.dispname, ip.remote_port, ip.remote_ip);
454 }
455 else {
456 /* Could be a CERT problem */
457 failf(data, "%s", err_descr);
458 }
459 return result;
460}
461
462static CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf,
463 struct Curl_easy *data)
464{
465 struct cf_osslq_ctx *ctx = cf->ctx;
466
467 cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
468 cf->conn->httpversion = 30;
469 cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
470
471 return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
472}
473
474/**
475 * All about the H3 internals of a stream
476 */
477struct h3_stream_ctx {
478 struct cf_osslq_stream s;
479 struct bufq sendbuf; /* h3 request body */
480 struct bufq recvbuf; /* h3 response body */
481 struct h1_req_parser h1; /* h1 request parsing */
482 size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */
483 size_t upload_blocked_len; /* the amount written last and EGAINed */
484 size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */
485 uint64_t error3; /* HTTP/3 stream error code */
486 curl_off_t upload_left; /* number of request bytes left to upload */
487 curl_off_t download_recvd; /* number of response DATA bytes received */
488 int status_code; /* HTTP status code */
489 bool resp_hds_complete; /* we have a complete, final response */
490 bool closed; /* TRUE on stream close */
491 bool reset; /* TRUE on stream reset */
492 bool send_closed; /* stream is local closed */
493 BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */
494};
495
496#define H3_STREAM_CTX(d) ((struct h3_stream_ctx *)(((d) && (d)->req.p.http)? \
497 ((struct HTTP *)(d)->req.p.http)->h3_ctx \
498 : NULL))
499#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx
500#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \
501 H3_STREAM_CTX(d)->s.id : -2)
502
503static CURLcode h3_data_setup(struct Curl_cfilter *cf,
504 struct Curl_easy *data)
505{
506 struct cf_osslq_ctx *ctx = cf->ctx;
507 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
508
509 if(!data || !data->req.p.http) {
510 failf(data, "initialization failure, transfer not http initialized");
511 return CURLE_FAILED_INIT;
512 }
513
514 if(stream)
515 return CURLE_OK;
516
517 stream = calloc(1, sizeof(*stream));
518 if(!stream)
519 return CURLE_OUT_OF_MEMORY;
520
521 stream->s.id = -1;
522 /* on send, we control how much we put into the buffer */
523 Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp,
524 H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
525 stream->sendbuf_len_in_flight = 0;
526 /* on recv, we need a flexible buffer limit since we also write
527 * headers to it that are not counted against the nghttp3 flow limits. */
528 Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp,
529 H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
530 stream->recv_buf_nonflow = 0;
531 Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
532
533 H3_STREAM_LCTX(data) = stream;
534 return CURLE_OK;
535}
536
537static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
538{
539 struct cf_osslq_ctx *ctx = cf->ctx;
540 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
541
542 (void)cf;
543 if(stream) {
544 CURL_TRC_CF(data, cf, "[%"PRId64"] easy handle is done", stream->s.id);
545 if(ctx->h3.conn && !stream->closed) {
546 nghttp3_conn_shutdown_stream_read(ctx->h3.conn, stream->s.id);
547 nghttp3_conn_close_stream(ctx->h3.conn, stream->s.id,
548 NGHTTP3_H3_REQUEST_CANCELLED);
549 nghttp3_conn_set_stream_user_data(ctx->h3.conn, stream->s.id, NULL);
550 stream->closed = TRUE;
551 }
552
553 cf_osslq_stream_cleanup(&stream->s);
554 Curl_bufq_free(&stream->sendbuf);
555 Curl_bufq_free(&stream->recvbuf);
556 Curl_h1_req_parse_free(&stream->h1);
557 free(stream);
558 H3_STREAM_LCTX(data) = NULL;
559 }
560}
561
562static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf,
563 struct Curl_easy *data,
564 int64_t stream_id)
565{
566 struct cf_osslq_ctx *ctx = cf->ctx;
567 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
568 struct Curl_easy *sdata;
569
570 if(stream && stream->s.id == stream_id) {
571 return &stream->s;
572 }
573 else if(ctx->h3.s_ctrl.id == stream_id) {
574 return &ctx->h3.s_ctrl;
575 }
576 else if(ctx->h3.s_qpack_enc.id == stream_id) {
577 return &ctx->h3.s_qpack_enc;
578 }
579 else if(ctx->h3.s_qpack_dec.id == stream_id) {
580 return &ctx->h3.s_qpack_dec;
581 }
582 else {
583 DEBUGASSERT(data->multi);
584 for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
585 if((sdata->conn == data->conn) && H3_STREAM_ID(sdata) == stream_id) {
586 stream = H3_STREAM_CTX(sdata);
587 return stream? &stream->s : NULL;
588 }
589 }
590 }
591 return NULL;
592}
593
594static void h3_drain_stream(struct Curl_cfilter *cf,
595 struct Curl_easy *data)
596{
597 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
598 unsigned char bits;
599
600 (void)cf;
601 bits = CURL_CSELECT_IN;
602 if(stream && stream->upload_left && !stream->send_closed)
603 bits |= CURL_CSELECT_OUT;
604 if(data->state.select_bits != bits) {
605 data->state.select_bits = bits;
606 Curl_expire(data, 0, EXPIRE_RUN_NOW);
607 }
608}
609
610static CURLcode h3_data_pause(struct Curl_cfilter *cf,
611 struct Curl_easy *data,
612 bool pause)
613{
614 if(!pause) {
615 /* unpaused. make it run again right away */
616 h3_drain_stream(cf, data);
617 Curl_expire(data, 0, EXPIRE_RUN_NOW);
618 }
619 return CURLE_OK;
620}
621
622static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
623 uint64_t app_error_code, void *user_data,
624 void *stream_user_data)
625{
626 struct Curl_cfilter *cf = user_data;
627 struct Curl_easy *data = stream_user_data;
628 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
629 (void)conn;
630 (void)stream_id;
631
632 /* we might be called by nghttp3 after we already cleaned up */
633 if(!stream)
634 return 0;
635
636 stream->closed = TRUE;
637 stream->error3 = app_error_code;
638 if(stream->error3 != NGHTTP3_H3_NO_ERROR) {
639 stream->reset = TRUE;
640 stream->send_closed = TRUE;
641 CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRId64,
642 stream->s.id, stream->error3);
643 }
644 else {
645 CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED", stream->s.id);
646 }
647 h3_drain_stream(cf, data);
648 return 0;
649}
650
651/*
652 * write_resp_raw() copies response data in raw format to the `data`'s
653 * receive buffer. If not enough space is available, it appends to the
654 * `data`'s overflow buffer.
655 */
656static CURLcode write_resp_raw(struct Curl_cfilter *cf,
657 struct Curl_easy *data,
658 const void *mem, size_t memlen,
659 bool flow)
660{
661 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
662 CURLcode result = CURLE_OK;
663 ssize_t nwritten;
664
665 (void)cf;
666 if(!stream) {
667 return CURLE_RECV_ERROR;
668 }
669 nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result);
670 if(nwritten < 0) {
671 return result;
672 }
673
674 if(!flow)
675 stream->recv_buf_nonflow += (size_t)nwritten;
676
677 if((size_t)nwritten < memlen) {
678 /* This MUST not happen. Our recbuf is dimensioned to hold the
679 * full max_stream_window and then some for this very reason. */
680 DEBUGASSERT(0);
681 return CURLE_RECV_ERROR;
682 }
683 return result;
684}
685
686static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
687 const uint8_t *buf, size_t buflen,
688 void *user_data, void *stream_user_data)
689{
690 struct Curl_cfilter *cf = user_data;
691 struct Curl_easy *data = stream_user_data;
692 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
693 CURLcode result;
694
695 (void)conn;
696 (void)stream3_id;
697
698 if(!stream)
699 return NGHTTP3_ERR_CALLBACK_FAILURE;
700
701 result = write_resp_raw(cf, data, buf, buflen, TRUE);
702 if(result) {
703 CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, ERROR receiving %d",
704 stream->s.id, buflen, result);
705 return NGHTTP3_ERR_CALLBACK_FAILURE;
706 }
707 stream->download_recvd += (curl_off_t)buflen;
708 CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, total=%zd",
709 stream->s.id, buflen, stream->download_recvd);
710 h3_drain_stream(cf, data);
711 return 0;
712}
713
714static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id,
715 size_t consumed, void *user_data,
716 void *stream_user_data)
717{
718 struct Curl_cfilter *cf = user_data;
719 struct Curl_easy *data = stream_user_data;
720 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
721
722 (void)conn;
723 (void)stream_id;
724 if(stream)
725 CURL_TRC_CF(data, cf, "[%" PRId64 "] deferred consume %zu bytes",
726 stream->s.id, consumed);
727 return 0;
728}
729
730static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
731 int32_t token, nghttp3_rcbuf *name,
732 nghttp3_rcbuf *value, uint8_t flags,
733 void *user_data, void *stream_user_data)
734{
735 struct Curl_cfilter *cf = user_data;
736 nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
737 nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
738 struct Curl_easy *data = stream_user_data;
739 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
740 CURLcode result = CURLE_OK;
741 (void)conn;
742 (void)stream_id;
743 (void)token;
744 (void)flags;
745 (void)cf;
746
747 /* we might have cleaned up this transfer already */
748 if(!stream)
749 return 0;
750
751 if(token == NGHTTP3_QPACK_TOKEN__STATUS) {
752 char line[14]; /* status line is always 13 characters long */
753 size_t ncopy;
754
755 result = Curl_http_decode_status(&stream->status_code,
756 (const char *)h3val.base, h3val.len);
757 if(result)
758 return -1;
759 ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
760 stream->status_code);
761 CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s", stream_id, line);
762 result = write_resp_raw(cf, data, line, ncopy, FALSE);
763 if(result) {
764 return -1;
765 }
766 }
767 else {
768 /* store as an HTTP1-style header */
769 CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s",
770 stream_id, (int)h3name.len, h3name.base,
771 (int)h3val.len, h3val.base);
772 result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE);
773 if(result) {
774 return -1;
775 }
776 result = write_resp_raw(cf, data, ": ", 2, FALSE);
777 if(result) {
778 return -1;
779 }
780 result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE);
781 if(result) {
782 return -1;
783 }
784 result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
785 if(result) {
786 return -1;
787 }
788 }
789 return 0;
790}
791
792static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
793 int fin, void *user_data, void *stream_user_data)
794{
795 struct Curl_cfilter *cf = user_data;
796 struct Curl_easy *data = stream_user_data;
797 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
798 CURLcode result = CURLE_OK;
799 (void)conn;
800 (void)stream_id;
801 (void)fin;
802 (void)cf;
803
804 if(!stream)
805 return 0;
806 /* add a CRLF only if we've received some headers */
807 result = write_resp_raw(cf, data, "\r\n", 2, FALSE);
808 if(result) {
809 return -1;
810 }
811
812 CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d",
813 stream_id, stream->status_code);
814 if(stream->status_code / 100 != 1) {
815 stream->resp_hds_complete = TRUE;
816 }
817 h3_drain_stream(cf, data);
818 return 0;
819}
820
821static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id,
822 uint64_t app_error_code, void *user_data,
823 void *stream_user_data)
824{
825 struct Curl_cfilter *cf = user_data;
826 struct Curl_easy *data = stream_user_data;
827 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
828 (void)conn;
829 (void)app_error_code;
830
831 if(!stream || !stream->s.ssl)
832 return 0;
833
834 CURL_TRC_CF(data, cf, "[%" PRId64 "] stop_sending", stream_id);
835 cf_osslq_stream_close(&stream->s);
836 return 0;
837}
838
839static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id,
840 uint64_t app_error_code, void *user_data,
841 void *stream_user_data) {
842 struct Curl_cfilter *cf = user_data;
843 struct Curl_easy *data = stream_user_data;
844 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
845 int rv;
846 (void)conn;
847
848 if(stream && stream->s.ssl) {
849 SSL_STREAM_RESET_ARGS args = {0};
850 args.quic_error_code = app_error_code;
851 rv = !SSL_stream_reset(stream->s.ssl, &args, sizeof(args));
852 CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d", stream_id, rv);
853 if(!rv) {
854 return NGHTTP3_ERR_CALLBACK_FAILURE;
855 }
856 }
857 return 0;
858}
859
860static nghttp3_ssize
861cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
862 nghttp3_vec *vec, size_t veccnt,
863 uint32_t *pflags, void *user_data,
864 void *stream_user_data)
865{
866 struct Curl_cfilter *cf = user_data;
867 struct Curl_easy *data = stream_user_data;
868 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
869 ssize_t nwritten = 0;
870 size_t nvecs = 0;
871 (void)cf;
872 (void)conn;
873 (void)stream_id;
874 (void)user_data;
875 (void)veccnt;
876
877 if(!stream)
878 return NGHTTP3_ERR_CALLBACK_FAILURE;
879 /* nghttp3 keeps references to the sendbuf data until it is ACKed
880 * by the server (see `cb_h3_acked_req_body()` for updates).
881 * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf`
882 * that we have already passed to nghttp3, but which have not been
883 * ACKed yet.
884 * Any amount beyond `sendbuf_len_in_flight` we need still to pass
885 * to nghttp3. Do that now, if we can. */
886 if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) {
887 nvecs = 0;
888 while(nvecs < veccnt &&
889 Curl_bufq_peek_at(&stream->sendbuf,
890 stream->sendbuf_len_in_flight,
891 (const unsigned char **)&vec[nvecs].base,
892 &vec[nvecs].len)) {
893 stream->sendbuf_len_in_flight += vec[nvecs].len;
894 nwritten += vec[nvecs].len;
895 ++nvecs;
896 }
897 DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */
898 }
899
900 if(nwritten > 0 && stream->upload_left != -1)
901 stream->upload_left -= nwritten;
902
903 /* When we stopped sending and everything in `sendbuf` is "in flight",
904 * we are at the end of the request body. */
905 if(stream->upload_left == 0) {
906 *pflags = NGHTTP3_DATA_FLAG_EOF;
907 stream->send_closed = TRUE;
908 }
909 else if(!nwritten) {
910 /* Not EOF, and nothing to give, we signal WOULDBLOCK. */
911 CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN",
912 stream->s.id);
913 return NGHTTP3_ERR_WOULDBLOCK;
914 }
915
916 CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> "
917 "%d vecs%s with %zu (buffered=%zu, left=%"
918 CURL_FORMAT_CURL_OFF_T ")",
919 stream->s.id, (int)nvecs,
920 *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
921 nwritten, Curl_bufq_len(&stream->sendbuf),
922 stream->upload_left);
923 return (nghttp3_ssize)nvecs;
924}
925
926static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
927 uint64_t datalen, void *user_data,
928 void *stream_user_data)
929{
930 struct Curl_cfilter *cf = user_data;
931 struct Curl_easy *data = stream_user_data;
932 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
933 size_t skiplen;
934
935 (void)cf;
936 if(!stream)
937 return 0;
938 /* The server acknowledged `datalen` of bytes from our request body.
939 * This is a delta. We have kept this data in `sendbuf` for
940 * re-transmissions and can free it now. */
941 if(datalen >= (uint64_t)stream->sendbuf_len_in_flight)
942 skiplen = stream->sendbuf_len_in_flight;
943 else
944 skiplen = (size_t)datalen;
945 Curl_bufq_skip(&stream->sendbuf, skiplen);
946 stream->sendbuf_len_in_flight -= skiplen;
947
948 /* Everything ACKed, we resume upload processing */
949 if(!stream->sendbuf_len_in_flight) {
950 int rv = nghttp3_conn_resume_stream(conn, stream_id);
951 if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
952 return NGHTTP3_ERR_CALLBACK_FAILURE;
953 }
954 }
955 return 0;
956}
957
958static nghttp3_callbacks ngh3_callbacks = {
959 cb_h3_acked_stream_data,
960 cb_h3_stream_close,
961 cb_h3_recv_data,
962 cb_h3_deferred_consume,
963 NULL, /* begin_headers */
964 cb_h3_recv_header,
965 cb_h3_end_headers,
966 NULL, /* begin_trailers */
967 cb_h3_recv_header,
968 NULL, /* end_trailers */
969 cb_h3_stop_sending,
970 NULL, /* end_stream */
971 cb_h3_reset_stream,
972 NULL, /* shutdown */
973 NULL /* recv_settings */
974};
975
976static CURLcode cf_osslq_h3conn_init(struct cf_osslq_ctx *ctx, SSL *conn,
977 void *user_data)
978{
979 struct cf_osslq_h3conn *h3 = &ctx->h3;
980 CURLcode result;
981 int rc;
982
983 nghttp3_settings_default(&h3->settings);
984 rc = nghttp3_conn_client_new(&h3->conn,
985 &ngh3_callbacks,
986 &h3->settings,
987 nghttp3_mem_default(),
988 user_data);
989 if(rc) {
990 result = CURLE_OUT_OF_MEMORY;
991 goto out;
992 }
993
994 result = cf_osslq_stream_open(&h3->s_ctrl, conn,
995 SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI,
996 &ctx->stream_bufcp, NULL);
997 if(result) {
998 result = CURLE_QUIC_CONNECT_ERROR;
999 goto out;
1000 }
1001 result = cf_osslq_stream_open(&h3->s_qpack_enc, conn,
1002 SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI,
1003 &ctx->stream_bufcp, NULL);
1004 if(result) {
1005 result = CURLE_QUIC_CONNECT_ERROR;
1006 goto out;
1007 }
1008 result = cf_osslq_stream_open(&h3->s_qpack_dec, conn,
1009 SSL_STREAM_FLAG_ADVANCE|SSL_STREAM_FLAG_UNI,
1010 &ctx->stream_bufcp, NULL);
1011 if(result) {
1012 result = CURLE_QUIC_CONNECT_ERROR;
1013 goto out;
1014 }
1015
1016 rc = nghttp3_conn_bind_control_stream(h3->conn, h3->s_ctrl.id);
1017 if(rc) {
1018 result = CURLE_QUIC_CONNECT_ERROR;
1019 goto out;
1020 }
1021 rc = nghttp3_conn_bind_qpack_streams(h3->conn, h3->s_qpack_enc.id,
1022 h3->s_qpack_dec.id);
1023 if(rc) {
1024 result = CURLE_QUIC_CONNECT_ERROR;
1025 goto out;
1026 }
1027
1028 result = CURLE_OK;
1029out:
1030 return result;
1031}
1032
1033static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
1034 struct Curl_easy *data)
1035{
1036 struct cf_osslq_ctx *ctx = cf->ctx;
1037 CURLcode result;
1038 int rv;
1039 const struct Curl_sockaddr_ex *peer_addr = NULL;
1040 BIO *bio = NULL;
1041 BIO_ADDR *baddr = NULL;
1042
1043 Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
1044 H3_STREAM_POOL_SPARES);
1045 result = Curl_ssl_peer_init(&ctx->peer, cf);
1046 if(result)
1047 goto out;
1048
1049#define H3_ALPN "\x2h3"
1050 result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
1051 H3_ALPN, sizeof(H3_ALPN) - 1,
1052 NULL, NULL);
1053 if(result)
1054 goto out;
1055
1056 result = vquic_ctx_init(&ctx->q);
1057 if(result)
1058 goto out;
1059
1060 result = CURLE_QUIC_CONNECT_ERROR;
1061 Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, &peer_addr, NULL);
1062 if(!peer_addr)
1063 goto out;
1064
1065 ctx->q.local_addrlen = sizeof(ctx->q.local_addr);
1066 rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr,
1067 &ctx->q.local_addrlen);
1068 if(rv == -1)
1069 goto out;
1070
1071 result = make_bio_addr(&baddr, peer_addr);
1072 if(result) {
1073 failf(data, "error creating BIO_ADDR from sockaddr");
1074 goto out;
1075 }
1076
1077 /* Type conversions, see #12861: OpenSSL wants an `int`, but on 64-bit
1078 * Win32 systems, Microsoft defines SOCKET as `unsigned long long`.
1079 */
1080#if defined(_WIN32) && !defined(__LWIP_OPT_H__) && !defined(LWIP_HDR_OPT_H)
1081 if(ctx->q.sockfd > INT_MAX) {
1082 failf(data, "Windows socket identifier larger than MAX_INT, "
1083 "unable to set in OpenSSL dgram API.");
1084 result = CURLE_QUIC_CONNECT_ERROR;
1085 goto out;
1086 }
1087 bio = BIO_new_dgram((int)ctx->q.sockfd, BIO_NOCLOSE);
1088#else
1089 bio = BIO_new_dgram(ctx->q.sockfd, BIO_NOCLOSE);
1090#endif
1091 if(!bio) {
1092 result = CURLE_OUT_OF_MEMORY;
1093 goto out;
1094 }
1095
1096 if(!SSL_set1_initial_peer_addr(ctx->tls.ssl, baddr)) {
1097 failf(data, "failed to set the initial peer address");
1098 result = CURLE_FAILED_INIT;
1099 goto out;
1100 }
1101 if(!SSL_set_blocking_mode(ctx->tls.ssl, 0)) {
1102 failf(data, "failed to turn off blocking mode");
1103 result = CURLE_FAILED_INIT;
1104 goto out;
1105 }
1106
1107#ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT
1108 /* Added in OpenSSL v3.3.x */
1109 if(!SSL_set_feature_request_uint(ctx->tls.ssl, SSL_VALUE_QUIC_IDLE_TIMEOUT,
1110 CURL_QUIC_MAX_IDLE_MS)) {
1111 CURL_TRC_CF(data, cf, "error setting idle timeout, ");
1112 result = CURLE_FAILED_INIT;
1113 goto out;
1114 }
1115#endif
1116
1117 SSL_set_bio(ctx->tls.ssl, bio, bio);
1118 bio = NULL;
1119 SSL_set_connect_state(ctx->tls.ssl);
1120 SSL_set_incoming_stream_policy(ctx->tls.ssl,
1121 SSL_INCOMING_STREAM_POLICY_ACCEPT, 0);
1122 /* setup the H3 things on top of the QUIC connection */
1123 result = cf_osslq_h3conn_init(ctx, ctx->tls.ssl, cf);
1124
1125out:
1126 if(bio)
1127 BIO_free(bio);
1128 if(baddr)
1129 BIO_ADDR_free(baddr);
1130 CURL_TRC_CF(data, cf, "QUIC tls init -> %d", result);
1131 return result;
1132}
1133
1134struct h3_quic_recv_ctx {
1135 struct Curl_cfilter *cf;
1136 struct Curl_easy *data;
1137 struct cf_osslq_stream *s;
1138};
1139
1140static ssize_t h3_quic_recv(void *reader_ctx,
1141 unsigned char *buf, size_t len,
1142 CURLcode *err)
1143{
1144 struct h3_quic_recv_ctx *x = reader_ctx;
1145 size_t nread;
1146 int rv;
1147
1148 *err = CURLE_OK;
1149 rv = SSL_read_ex(x->s->ssl, buf, len, &nread);
1150 if(rv <= 0) {
1151 int detail = SSL_get_error(x->s->ssl, rv);
1152 if(detail == SSL_ERROR_WANT_READ || detail == SSL_ERROR_WANT_WRITE) {
1153 *err = CURLE_AGAIN;
1154 return -1;
1155 }
1156 else if(detail == SSL_ERROR_ZERO_RETURN) {
1157 CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> EOS",
1158 x->s->id);
1159 x->s->recvd_eos = TRUE;
1160 return 0;
1161 }
1162 else if(SSL_get_stream_read_state(x->s->ssl) ==
1163 SSL_STREAM_STATE_RESET_REMOTE) {
1164 uint64_t app_error_code = NGHTTP3_H3_NO_ERROR;
1165 SSL_get_stream_read_error_code(x->s->ssl, &app_error_code);
1166 CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> RESET, "
1167 "rv=%d, app_err=%" PRIu64,
1168 x->s->id, rv, app_error_code);
1169 if(app_error_code != NGHTTP3_H3_NO_ERROR) {
1170 x->s->reset = TRUE;
1171 }
1172 x->s->recvd_eos = TRUE;
1173 return 0;
1174 }
1175 else {
1176 *err = cf_osslq_ssl_err(x->cf, x->data, detail, CURLE_RECV_ERROR);
1177 return -1;
1178 }
1179 }
1180 else {
1181 /* CURL_TRC_CF(x->data, x->cf, "[%" PRId64 "] h3_quic_recv -> %zu bytes",
1182 x->s->id, nread); */
1183 }
1184 return (ssize_t)nread;
1185}
1186
1187static CURLcode cf_osslq_stream_recv(struct cf_osslq_stream *s,
1188 struct Curl_cfilter *cf,
1189 struct Curl_easy *data)
1190{
1191 struct cf_osslq_ctx *ctx = cf->ctx;
1192 CURLcode result = CURLE_OK;
1193 ssize_t nread;
1194 struct h3_quic_recv_ctx x;
1195 int rv, eagain = FALSE;
1196 size_t total_recv_len = 0;
1197
1198 DEBUGASSERT(s);
1199 if(s->closed)
1200 return CURLE_OK;
1201
1202 x.cf = cf;
1203 x.data = data;
1204 x.s = s;
1205 while(s->ssl && !s->closed && !eagain &&
1206 (total_recv_len < H3_STREAM_CHUNK_SIZE)) {
1207 if(Curl_bufq_is_empty(&s->recvbuf) && !s->recvd_eos) {
1208 while(!eagain && !s->recvd_eos && !Curl_bufq_is_full(&s->recvbuf)) {
1209 nread = Curl_bufq_sipn(&s->recvbuf, 0, h3_quic_recv, &x, &result);
1210 if(nread < 0) {
1211 if(result != CURLE_AGAIN)
1212 goto out;
1213 result = CURLE_OK;
1214 eagain = TRUE;
1215 }
1216 }
1217 }
1218
1219 /* Forward what we have to nghttp3 */
1220 if(!Curl_bufq_is_empty(&s->recvbuf)) {
1221 const unsigned char *buf;
1222 size_t blen;
1223
1224 while(Curl_bufq_peek(&s->recvbuf, &buf, &blen)) {
1225 nread = nghttp3_conn_read_stream(ctx->h3.conn, s->id,
1226 buf, blen, 0);
1227 CURL_TRC_CF(data, cf, "[%" PRId64 "] forward %zu bytes "
1228 "to nghttp3 -> %zd", s->id, blen, nread);
1229 if(nread < 0) {
1230 failf(data, "nghttp3_conn_read_stream(len=%zu) error: %s",
1231 blen, nghttp3_strerror((int)nread));
1232 result = CURLE_RECV_ERROR;
1233 goto out;
1234 }
1235 /* success, `nread` is the flow for QUIC to count as "consumed",
1236 * not sure how that will work with OpenSSL. Anyways, without error,
1237 * all data that we passed is not owned by nghttp3. */
1238 Curl_bufq_skip(&s->recvbuf, blen);
1239 total_recv_len += blen;
1240 }
1241 }
1242
1243 /* When we forwarded everything, handle RESET/EOS */
1244 if(Curl_bufq_is_empty(&s->recvbuf) && !s->closed) {
1245 result = CURLE_OK;
1246 if(s->reset) {
1247 uint64_t app_error;
1248 if(!SSL_get_stream_read_error_code(s->ssl, &app_error)) {
1249 failf(data, "SSL_get_stream_read_error_code returned error");
1250 result = CURLE_RECV_ERROR;
1251 goto out;
1252 }
1253 rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id, app_error);
1254 s->closed = TRUE;
1255 if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
1256 failf(data, "nghttp3_conn_close_stream returned error: %s",
1257 nghttp3_strerror(rv));
1258 result = CURLE_RECV_ERROR;
1259 goto out;
1260 }
1261 }
1262 else if(s->recvd_eos) {
1263 rv = nghttp3_conn_close_stream(ctx->h3.conn, s->id,
1264 NGHTTP3_H3_NO_ERROR);
1265 s->closed = TRUE;
1266 CURL_TRC_CF(data, cf, "[%" PRId64 "] close nghttp3 stream -> %d",
1267 s->id, rv);
1268 if(rv < 0 && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
1269 failf(data, "nghttp3_conn_close_stream returned error: %s",
1270 nghttp3_strerror(rv));
1271 result = CURLE_RECV_ERROR;
1272 goto out;
1273 }
1274 }
1275 }
1276 }
1277out:
1278 if(result)
1279 CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_osslq_stream_recv -> %d",
1280 s->id, result);
1281 return result;
1282}
1283
1284static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
1285 struct Curl_easy *data)
1286{
1287 struct cf_osslq_ctx *ctx = cf->ctx;
1288 CURLcode result = CURLE_OK;
1289
1290 if(!ctx->tls.ssl)
1291 goto out;
1292
1293 ERR_clear_error();
1294
1295 /* 1. Check for new incoming streams */
1296 while(1) {
1297 SSL *snew = SSL_accept_stream(ctx->tls.ssl, SSL_ACCEPT_STREAM_NO_BLOCK);
1298 if(!snew)
1299 break;
1300
1301 (void)cf_osslq_h3conn_add_stream(&ctx->h3, snew, cf, data);
1302 }
1303
1304 if(!SSL_handle_events(ctx->tls.ssl)) {
1305 int detail = SSL_get_error(ctx->tls.ssl, 0);
1306 result = cf_osslq_ssl_err(cf, data, detail, CURLE_RECV_ERROR);
1307 }
1308
1309 if(ctx->h3.conn) {
1310 size_t i;
1311 for(i = 0; i < ctx->h3.remote_ctrl_n; ++i) {
1312 result = cf_osslq_stream_recv(&ctx->h3.remote_ctrl[i], cf, data);
1313 if(result)
1314 goto out;
1315 }
1316 }
1317
1318 if(ctx->h3.conn) {
1319 struct Curl_easy *sdata;
1320 struct h3_stream_ctx *stream;
1321 /* PULL all open streams */
1322 DEBUGASSERT(data->multi);
1323 for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
1324 if(sdata->conn == data->conn && CURL_WANT_RECV(sdata)) {
1325 stream = H3_STREAM_CTX(sdata);
1326 if(stream && !stream->closed &&
1327 !Curl_bufq_is_full(&stream->recvbuf)) {
1328 result = cf_osslq_stream_recv(&stream->s, cf, sdata);
1329 if(result)
1330 goto out;
1331 }
1332 }
1333 }
1334 }
1335
1336out:
1337 CURL_TRC_CF(data, cf, "progress_ingress -> %d", result);
1338 return result;
1339}
1340
1341/* Iterate over all streams and check if blocked can be unblocked */
1342static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf,
1343 struct Curl_easy *data)
1344{
1345 struct cf_osslq_ctx *ctx = cf->ctx;
1346 struct Curl_easy *sdata;
1347 struct h3_stream_ctx *stream;
1348
1349 if(ctx->h3.conn) {
1350 for(sdata = data->multi->easyp; sdata; sdata = sdata->next) {
1351 if(sdata->conn == data->conn) {
1352 stream = H3_STREAM_CTX(sdata);
1353 if(stream && stream->s.ssl && stream->s.send_blocked &&
1354 !SSL_want_write(stream->s.ssl)) {
1355 nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id);
1356 stream->s.send_blocked = FALSE;
1357 h3_drain_stream(cf, sdata);
1358 CURL_TRC_CF(sdata, cf, "unblocked");
1359 }
1360 }
1361 }
1362 }
1363 return CURLE_OK;
1364}
1365
1366static CURLcode h3_send_streams(struct Curl_cfilter *cf,
1367 struct Curl_easy *data)
1368{
1369 struct cf_osslq_ctx *ctx = cf->ctx;
1370 CURLcode result = CURLE_OK;
1371
1372 if(!ctx->tls.ssl || !ctx->h3.conn)
1373 goto out;
1374
1375 for(;;) {
1376 struct cf_osslq_stream *s = NULL;
1377 nghttp3_vec vec[16];
1378 nghttp3_ssize n, i;
1379 int64_t stream_id;
1380 size_t written;
1381 int eos, ok, rv;
1382 size_t total_len, acked_len = 0;
1383 bool blocked = FALSE, eos_written = FALSE;
1384
1385 n = nghttp3_conn_writev_stream(ctx->h3.conn, &stream_id, &eos,
1386 vec, ARRAYSIZE(vec));
1387 if(n < 0) {
1388 failf(data, "nghttp3_conn_writev_stream returned error: %s",
1389 nghttp3_strerror((int)n));
1390 result = CURLE_SEND_ERROR;
1391 goto out;
1392 }
1393 if(stream_id < 0) {
1394 result = CURLE_OK;
1395 goto out;
1396 }
1397
1398 /* Get the stream for this data */
1399 s = cf_osslq_get_qstream(cf, data, stream_id);
1400 if(!s) {
1401 failf(data, "nghttp3_conn_writev_stream gave unknown stream %" PRId64,
1402 stream_id);
1403 result = CURLE_SEND_ERROR;
1404 goto out;
1405 }
1406 /* Now write the data to the stream's SSL*, it may not all fit! */
1407 DEBUGASSERT(s->id == stream_id);
1408 for(i = 0, total_len = 0; i < n; ++i) {
1409 total_len += vec[i].len;
1410 }
1411 for(i = 0; (i < n) && !blocked; ++i) {
1412 /* Without stream->s.ssl, we closed that already, so
1413 * pretend the write did succeed. */
1414#ifdef SSL_WRITE_FLAG_CONCLUDE
1415 /* Since OpenSSL v3.3.x, on last chunk set EOS if needed */
1416 uint64_t flags = (eos && ((i + 1) == n))? SSL_WRITE_FLAG_CONCLUDE : 0;
1417 written = vec[i].len;
1418 ok = !s->ssl || SSL_write_ex2(s->ssl, vec[i].base, vec[i].len, flags,
1419 &written);
1420 if(ok && flags & SSL_WRITE_FLAG_CONCLUDE)
1421 eos_written = TRUE;
1422#else
1423 written = vec[i].len;
1424 ok = !s->ssl || SSL_write_ex(s->ssl, vec[i].base, vec[i].len,
1425 &written);
1426#endif
1427 if(ok) {
1428 /* As OpenSSL buffers the data, we count this as acknowledged
1429 * from nghttp3's point of view */
1430 CURL_TRC_CF(data, cf, "[%"PRId64"] send %zu bytes to QUIC ok",
1431 s->id, vec[i].len);
1432 acked_len += vec[i].len;
1433 }
1434 else {
1435 int detail = SSL_get_error(s->ssl, 0);
1436 switch(detail) {
1437 case SSL_ERROR_WANT_WRITE:
1438 case SSL_ERROR_WANT_READ:
1439 /* QUIC blocked us from writing more */
1440 CURL_TRC_CF(data, cf, "[%"PRId64"] send %zu bytes to QUIC blocked",
1441 s->id, vec[i].len);
1442 written = 0;
1443 nghttp3_conn_block_stream(ctx->h3.conn, s->id);
1444 s->send_blocked = blocked = TRUE;
1445 break;
1446 default:
1447 failf(data, "[%"PRId64"] send %zu bytes to QUIC, SSL error %d",
1448 s->id, vec[i].len, detail);
1449 result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR);
1450 goto out;
1451 }
1452 }
1453 }
1454
1455 if(acked_len > 0 || (eos && !s->send_blocked)) {
1456 /* Since QUIC buffers the data written internally, we can tell
1457 * nghttp3 that it can move forward on it */
1458 ctx->q.last_io = Curl_now();
1459 rv = nghttp3_conn_add_write_offset(ctx->h3.conn, s->id, acked_len);
1460 if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
1461 failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
1462 nghttp3_strerror(rv));
1463 result = CURLE_SEND_ERROR;
1464 goto out;
1465 }
1466 rv = nghttp3_conn_add_ack_offset(ctx->h3.conn, s->id, acked_len);
1467 if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
1468 failf(data, "nghttp3_conn_add_ack_offset returned error: %s\n",
1469 nghttp3_strerror(rv));
1470 result = CURLE_SEND_ERROR;
1471 goto out;
1472 }
1473 CURL_TRC_CF(data, cf, "[%" PRId64 "] forwarded %zu/%zu h3 bytes "
1474 "to QUIC, eos=%d", s->id, acked_len, total_len, eos);
1475 }
1476
1477 if(eos && !s->send_blocked && !eos_written) {
1478 /* wrote everything and H3 indicates end of stream */
1479 CURL_TRC_CF(data, cf, "[%" PRId64 "] closing QUIC stream", s->id);
1480 SSL_stream_conclude(s->ssl, 0);
1481 }
1482 }
1483
1484out:
1485 CURL_TRC_CF(data, cf, "h3_send_streams -> %d", result);
1486 return result;
1487}
1488
1489static CURLcode cf_progress_egress(struct Curl_cfilter *cf,
1490 struct Curl_easy *data)
1491{
1492 struct cf_osslq_ctx *ctx = cf->ctx;
1493 CURLcode result = CURLE_OK;
1494
1495 if(!ctx->tls.ssl)
1496 goto out;
1497
1498 ERR_clear_error();
1499 result = h3_send_streams(cf, data);
1500 if(result)
1501 goto out;
1502
1503 if(!SSL_handle_events(ctx->tls.ssl)) {
1504 int detail = SSL_get_error(ctx->tls.ssl, 0);
1505 result = cf_osslq_ssl_err(cf, data, detail, CURLE_SEND_ERROR);
1506 }
1507
1508 result = cf_osslq_check_and_unblock(cf, data);
1509
1510out:
1511 CURL_TRC_CF(data, cf, "progress_egress -> %d", result);
1512 return result;
1513}
1514
1515static CURLcode check_and_set_expiry(struct Curl_cfilter *cf,
1516 struct Curl_easy *data)
1517{
1518 struct cf_osslq_ctx *ctx = cf->ctx;
1519 CURLcode result = CURLE_OK;
1520 struct timeval tv;
1521 timediff_t timeoutms;
1522 int is_infinite = TRUE;
1523
1524 if(ctx->tls.ssl &&
1525 SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite) &&
1526 !is_infinite) {
1527 timeoutms = curlx_tvtoms(&tv);
1528 /* QUIC want to be called again latest at the returned timeout */
1529 if(timeoutms <= 0) {
1530 result = cf_progress_ingress(cf, data);
1531 if(result)
1532 goto out;
1533 result = cf_progress_egress(cf, data);
1534 if(result)
1535 goto out;
1536 if(SSL_get_event_timeout(ctx->tls.ssl, &tv, &is_infinite)) {
1537 timeoutms = curlx_tvtoms(&tv);
1538 }
1539 }
1540 if(!is_infinite) {
1541 Curl_expire(data, timeoutms, EXPIRE_QUIC);
1542 CURL_TRC_CF(data, cf, "QUIC expiry in %ldms", (long)timeoutms);
1543 }
1544 }
1545out:
1546 return result;
1547}
1548
1549static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
1550 struct Curl_easy *data,
1551 bool blocking, bool *done)
1552{
1553 struct cf_osslq_ctx *ctx = cf->ctx;
1554 CURLcode result = CURLE_OK;
1555 struct cf_call_data save;
1556 struct curltime now;
1557 int err;
1558
1559 if(cf->connected) {
1560 *done = TRUE;
1561 return CURLE_OK;
1562 }
1563
1564 /* Connect the UDP filter first */
1565 if(!cf->next->connected) {
1566 result = Curl_conn_cf_connect(cf->next, data, blocking, done);
1567 if(result || !*done)
1568 return result;
1569 }
1570
1571 *done = FALSE;
1572 now = Curl_now();
1573 CF_DATA_SAVE(save, cf, data);
1574
1575 if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) {
1576 /* Not time yet to attempt the next connect */
1577 CURL_TRC_CF(data, cf, "waiting for reconnect time");
1578 goto out;
1579 }
1580
1581 if(!ctx->tls.ssl) {
1582 ctx->started_at = now;
1583 result = cf_osslq_ctx_start(cf, data);
1584 if(result)
1585 goto out;
1586 }
1587
1588 if(!ctx->got_first_byte) {
1589 int readable = SOCKET_READABLE(ctx->q.sockfd, 0);
1590 if(readable > 0 && (readable & CURL_CSELECT_IN)) {
1591 ctx->got_first_byte = TRUE;
1592 ctx->first_byte_at = Curl_now();
1593 }
1594 }
1595
1596 ERR_clear_error();
1597 err = SSL_do_handshake(ctx->tls.ssl);
1598
1599 if(err == 1) {
1600 /* connected */
1601 ctx->handshake_at = now;
1602 ctx->q.last_io = now;
1603 CURL_TRC_CF(data, cf, "handshake complete after %dms",
1604 (int)Curl_timediff(now, ctx->started_at));
1605 result = cf_osslq_verify_peer(cf, data);
1606 if(!result) {
1607 CURL_TRC_CF(data, cf, "peer verified");
1608 cf->connected = TRUE;
1609 cf->conn->alpn = CURL_HTTP_VERSION_3;
1610 *done = TRUE;
1611 connkeep(cf->conn, "HTTP/3 default");
1612 }
1613 }
1614 else {
1615 int detail = SSL_get_error(ctx->tls.ssl, err);
1616 switch(detail) {
1617 case SSL_ERROR_WANT_READ:
1618 ctx->q.last_io = now;
1619 CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_RECV");
1620 result = Curl_vquic_tls_before_recv(&ctx->tls, cf, data);
1621 goto out;
1622 case SSL_ERROR_WANT_WRITE:
1623 ctx->q.last_io = now;
1624 CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_SEND");
1625 result = CURLE_OK;
1626 goto out;
1627#ifdef SSL_ERROR_WANT_ASYNC
1628 case SSL_ERROR_WANT_ASYNC:
1629 ctx->q.last_io = now;
1630 CURL_TRC_CF(data, cf, "QUIC SSL_connect() -> WANT_ASYNC");
1631 result = CURLE_OK;
1632 goto out;
1633#endif
1634#ifdef SSL_ERROR_WANT_RETRY_VERIFY
1635 case SSL_ERROR_WANT_RETRY_VERIFY:
1636 result = CURLE_OK;
1637 goto out;
1638#endif
1639 default:
1640 result = cf_osslq_ssl_err(cf, data, detail, CURLE_COULDNT_CONNECT);
1641 goto out;
1642 }
1643 }
1644
1645out:
1646 if(result == CURLE_RECV_ERROR && ctx->tls.ssl && ctx->protocol_shutdown) {
1647 /* When a QUIC server instance is shutting down, it may send us a
1648 * CONNECTION_CLOSE right away. Our connection then enters the DRAINING
1649 * state. The CONNECT may work in the near future again. Indicate
1650 * that as a "weird" reply. */
1651 result = CURLE_WEIRD_SERVER_REPLY;
1652 }
1653
1654#ifndef CURL_DISABLE_VERBOSE_STRINGS
1655 if(result) {
1656 struct ip_quadruple ip;
1657
1658 Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
1659 infof(data, "QUIC connect to %s port %u failed: %s",
1660 ip.remote_ip, ip.remote_port, curl_easy_strerror(result));
1661 }
1662#endif
1663 if(!result)
1664 result = check_and_set_expiry(cf, data);
1665 if(result || *done)
1666 CURL_TRC_CF(data, cf, "connect -> %d, done=%d", result, *done);
1667 CF_DATA_RESTORE(cf, save);
1668 return result;
1669}
1670
1671static ssize_t h3_stream_open(struct Curl_cfilter *cf,
1672 struct Curl_easy *data,
1673 const void *buf, size_t len,
1674 CURLcode *err)
1675{
1676 struct cf_osslq_ctx *ctx = cf->ctx;
1677 struct h3_stream_ctx *stream = NULL;
1678 struct dynhds h2_headers;
1679 size_t nheader;
1680 nghttp3_nv *nva = NULL;
1681 int rc = 0;
1682 unsigned int i;
1683 ssize_t nwritten = -1;
1684 nghttp3_data_reader reader;
1685 nghttp3_data_reader *preader = NULL;
1686
1687 Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
1688
1689 *err = h3_data_setup(cf, data);
1690 if(*err)
1691 goto out;
1692 stream = H3_STREAM_CTX(data);
1693 DEBUGASSERT(stream);
1694 if(!stream) {
1695 *err = CURLE_FAILED_INIT;
1696 goto out;
1697 }
1698
1699 nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err);
1700 if(nwritten < 0)
1701 goto out;
1702 if(!stream->h1.done) {
1703 /* need more data */
1704 goto out;
1705 }
1706 DEBUGASSERT(stream->h1.req);
1707
1708 *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data);
1709 if(*err) {
1710 nwritten = -1;
1711 goto out;
1712 }
1713 /* no longer needed */
1714 Curl_h1_req_parse_free(&stream->h1);
1715
1716 nheader = Curl_dynhds_count(&h2_headers);
1717 nva = malloc(sizeof(nghttp3_nv) * nheader);
1718 if(!nva) {
1719 *err = CURLE_OUT_OF_MEMORY;
1720 nwritten = -1;
1721 goto out;
1722 }
1723
1724 for(i = 0; i < nheader; ++i) {
1725 struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
1726 nva[i].name = (unsigned char *)e->name;
1727 nva[i].namelen = e->namelen;
1728 nva[i].value = (unsigned char *)e->value;
1729 nva[i].valuelen = e->valuelen;
1730 nva[i].flags = NGHTTP3_NV_FLAG_NONE;
1731 }
1732
1733 DEBUGASSERT(stream->s.id == -1);
1734 *err = cf_osslq_stream_open(&stream->s, ctx->tls.ssl, 0,
1735 &ctx->stream_bufcp, data);
1736 if(*err) {
1737 failf(data, "can't get bidi streams");
1738 *err = CURLE_SEND_ERROR;
1739 goto out;
1740 }
1741
1742 switch(data->state.httpreq) {
1743 case HTTPREQ_POST:
1744 case HTTPREQ_POST_FORM:
1745 case HTTPREQ_POST_MIME:
1746 case HTTPREQ_PUT:
1747 /* known request body size or -1 */
1748 if(data->state.infilesize != -1)
1749 stream->upload_left = data->state.infilesize;
1750 else
1751 /* data sending without specifying the data amount up front */
1752 stream->upload_left = -1; /* unknown */
1753 break;
1754 default:
1755 /* there is not request body */
1756 stream->upload_left = 0; /* no request body */
1757 break;
1758 }
1759
1760 stream->send_closed = (stream->upload_left == 0);
1761 if(!stream->send_closed) {
1762 reader.read_data = cb_h3_read_req_body;
1763 preader = &reader;
1764 }
1765
1766 rc = nghttp3_conn_submit_request(ctx->h3.conn, stream->s.id,
1767 nva, nheader, preader, data);
1768 if(rc) {
1769 switch(rc) {
1770 case NGHTTP3_ERR_CONN_CLOSING:
1771 CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send, "
1772 "connection is closing", stream->s.id);
1773 break;
1774 default:
1775 CURL_TRC_CF(data, cf, "h3sid[%"PRId64"] failed to send -> %d (%s)",
1776 stream->s.id, rc, nghttp3_strerror(rc));
1777 break;
1778 }
1779 *err = CURLE_SEND_ERROR;
1780 nwritten = -1;
1781 goto out;
1782 }
1783
1784 if(Curl_trc_is_verbose(data)) {
1785 infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s",
1786 stream->s.id, data->state.url);
1787 for(i = 0; i < nheader; ++i) {
1788 infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]", stream->s.id,
1789 (int)nva[i].namelen, nva[i].name,
1790 (int)nva[i].valuelen, nva[i].value);
1791 }
1792 }
1793
1794out:
1795 free(nva);
1796 Curl_dynhds_free(&h2_headers);
1797 return nwritten;
1798}
1799
1800static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data,
1801 const void *buf, size_t len, CURLcode *err)
1802{
1803 struct cf_osslq_ctx *ctx = cf->ctx;
1804 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
1805 struct cf_call_data save;
1806 ssize_t nwritten;
1807 CURLcode result;
1808
1809 CF_DATA_SAVE(save, cf, data);
1810 DEBUGASSERT(cf->connected);
1811 DEBUGASSERT(ctx->tls.ssl);
1812 DEBUGASSERT(ctx->h3.conn);
1813 *err = CURLE_OK;
1814
1815 result = cf_progress_ingress(cf, data);
1816 if(result) {
1817 *err = result;
1818 nwritten = -1;
1819 goto out;
1820 }
1821
1822 result = cf_progress_egress(cf, data);
1823 if(result) {
1824 *err = result;
1825 nwritten = -1;
1826 goto out;
1827 }
1828
1829 if(!stream || stream->s.id < 0) {
1830 nwritten = h3_stream_open(cf, data, buf, len, err);
1831 if(nwritten < 0) {
1832 CURL_TRC_CF(data, cf, "failed to open stream -> %d", *err);
1833 goto out;
1834 }
1835 stream = H3_STREAM_CTX(data);
1836 }
1837 else if(stream->upload_blocked_len) {
1838 /* the data in `buf` has already been submitted or added to the
1839 * buffers, but have been EAGAINed on the last invocation. */
1840 DEBUGASSERT(len >= stream->upload_blocked_len);
1841 if(len < stream->upload_blocked_len) {
1842 /* Did we get called again with a smaller `len`? This should not
1843 * happen. We are not prepared to handle that. */
1844 failf(data, "HTTP/3 send again with decreased length");
1845 *err = CURLE_HTTP3;
1846 nwritten = -1;
1847 goto out;
1848 }
1849 nwritten = (ssize_t)stream->upload_blocked_len;
1850 stream->upload_blocked_len = 0;
1851 }
1852 else if(stream->closed) {
1853 if(stream->resp_hds_complete) {
1854 /* Server decided to close the stream after having sent us a final
1855 * response. This is valid if it is not interested in the request
1856 * body. This happens on 30x or 40x responses.
1857 * We silently discard the data sent, since this is not a transport
1858 * error situation. */
1859 CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data"
1860 "on closed stream with response", stream->s.id);
1861 *err = CURLE_OK;
1862 nwritten = (ssize_t)len;
1863 goto out;
1864 }
1865 CURL_TRC_CF(data, cf, "[%" PRId64 "] send_body(len=%zu) "
1866 "-> stream closed", stream->s.id, len);
1867 *err = CURLE_HTTP3;
1868 nwritten = -1;
1869 goto out;
1870 }
1871 else {
1872 nwritten = Curl_bufq_write(&stream->sendbuf, buf, len, err);
1873 CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to "
1874 "sendbuf(len=%zu) -> %zd, %d",
1875 stream->s.id, len, nwritten, *err);
1876 if(nwritten < 0) {
1877 goto out;
1878 }
1879
1880 (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id);
1881 }
1882
1883 result = cf_progress_egress(cf, data);
1884 if(result) {
1885 *err = result;
1886 nwritten = -1;
1887 }
1888
1889 if(stream && nwritten > 0 && stream->sendbuf_len_in_flight) {
1890 /* We have unacknowledged DATA and cannot report success to our
1891 * caller. Instead we EAGAIN and remember how much we have already
1892 * "written" into our various internal connection buffers. */
1893 stream->upload_blocked_len = nwritten;
1894 CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu), "
1895 "%zu bytes in flight -> EGAIN", stream->s.id, len,
1896 stream->sendbuf_len_in_flight);
1897 *err = CURLE_AGAIN;
1898 nwritten = -1;
1899 }
1900
1901out:
1902 result = check_and_set_expiry(cf, data);
1903 CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d",
1904 stream? stream->s.id : -1, len, nwritten, *err);
1905 CF_DATA_RESTORE(cf, save);
1906 return nwritten;
1907}
1908
1909static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
1910 struct Curl_easy *data,
1911 struct h3_stream_ctx *stream,
1912 CURLcode *err)
1913{
1914 ssize_t nread = -1;
1915
1916 (void)cf;
1917 if(stream->reset) {
1918 failf(data,
1919 "HTTP/3 stream %" PRId64 " reset by server", stream->s.id);
1920 *err = data->req.bytecount? CURLE_PARTIAL_FILE : CURLE_HTTP3;
1921 goto out;
1922 }
1923 else if(!stream->resp_hds_complete) {
1924 failf(data,
1925 "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting"
1926 " all response header fields, treated as error",
1927 stream->s.id);
1928 *err = CURLE_HTTP3;
1929 goto out;
1930 }
1931 *err = CURLE_OK;
1932 nread = 0;
1933
1934out:
1935 return nread;
1936}
1937
1938static ssize_t cf_osslq_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
1939 char *buf, size_t len, CURLcode *err)
1940{
1941 struct cf_osslq_ctx *ctx = cf->ctx;
1942 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
1943 ssize_t nread = -1;
1944 struct cf_call_data save;
1945 CURLcode result;
1946
1947 (void)ctx;
1948 CF_DATA_SAVE(save, cf, data);
1949 DEBUGASSERT(cf->connected);
1950 DEBUGASSERT(ctx);
1951 DEBUGASSERT(ctx->tls.ssl);
1952 DEBUGASSERT(ctx->h3.conn);
1953 *err = CURLE_OK;
1954
1955 if(!stream) {
1956 *err = CURLE_RECV_ERROR;
1957 goto out;
1958 }
1959
1960 if(!Curl_bufq_is_empty(&stream->recvbuf)) {
1961 nread = Curl_bufq_read(&stream->recvbuf,
1962 (unsigned char *)buf, len, err);
1963 if(nread < 0) {
1964 CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
1965 "-> %zd, %d", stream->s.id, len, nread, *err);
1966 goto out;
1967 }
1968 }
1969
1970 result = cf_progress_ingress(cf, data);
1971 if(result) {
1972 *err = result;
1973 nread = -1;
1974 goto out;
1975 }
1976
1977 /* recvbuf had nothing before, maybe after progressing ingress? */
1978 if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) {
1979 nread = Curl_bufq_read(&stream->recvbuf,
1980 (unsigned char *)buf, len, err);
1981 if(nread < 0) {
1982 CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) "
1983 "-> %zd, %d", stream->s.id, len, nread, *err);
1984 goto out;
1985 }
1986 }
1987
1988 if(nread > 0) {
1989 h3_drain_stream(cf, data);
1990 }
1991 else {
1992 if(stream->closed) {
1993 nread = recv_closed_stream(cf, data, stream, err);
1994 goto out;
1995 }
1996 *err = CURLE_AGAIN;
1997 nread = -1;
1998 }
1999
2000out:
2001 if(cf_progress_egress(cf, data)) {
2002 *err = CURLE_SEND_ERROR;
2003 nread = -1;
2004 }
2005 else {
2006 CURLcode result2 = check_and_set_expiry(cf, data);
2007 if(result2) {
2008 *err = result2;
2009 nread = -1;
2010 }
2011 }
2012 CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %zd, %d",
2013 stream? stream->s.id : -1, len, nread, *err);
2014 CF_DATA_RESTORE(cf, save);
2015 return nread;
2016}
2017
2018/*
2019 * Called from transfer.c:data_pending to know if we should keep looping
2020 * to receive more data from the connection.
2021 */
2022static bool cf_osslq_data_pending(struct Curl_cfilter *cf,
2023 const struct Curl_easy *data)
2024{
2025 const struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
2026 (void)cf;
2027 return stream && !Curl_bufq_is_empty(&stream->recvbuf);
2028}
2029
2030static CURLcode cf_osslq_data_event(struct Curl_cfilter *cf,
2031 struct Curl_easy *data,
2032 int event, int arg1, void *arg2)
2033{
2034 struct cf_osslq_ctx *ctx = cf->ctx;
2035 CURLcode result = CURLE_OK;
2036 struct cf_call_data save;
2037
2038 CF_DATA_SAVE(save, cf, data);
2039 (void)arg1;
2040 (void)arg2;
2041 switch(event) {
2042 case CF_CTRL_DATA_SETUP:
2043 break;
2044 case CF_CTRL_DATA_PAUSE:
2045 result = h3_data_pause(cf, data, (arg1 != 0));
2046 break;
2047 case CF_CTRL_DATA_DETACH:
2048 h3_data_done(cf, data);
2049 break;
2050 case CF_CTRL_DATA_DONE:
2051 h3_data_done(cf, data);
2052 break;
2053 case CF_CTRL_DATA_DONE_SEND: {
2054 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
2055 if(stream && !stream->send_closed) {
2056 stream->send_closed = TRUE;
2057 stream->upload_left = Curl_bufq_len(&stream->sendbuf);
2058 (void)nghttp3_conn_resume_stream(ctx->h3.conn, stream->s.id);
2059 }
2060 break;
2061 }
2062 case CF_CTRL_DATA_IDLE: {
2063 struct h3_stream_ctx *stream = H3_STREAM_CTX(data);
2064 CURL_TRC_CF(data, cf, "data idle");
2065 if(stream && !stream->closed) {
2066 result = check_and_set_expiry(cf, data);
2067 }
2068 break;
2069 }
2070 default:
2071 break;
2072 }
2073 CF_DATA_RESTORE(cf, save);
2074 return result;
2075}
2076
2077static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf,
2078 struct Curl_easy *data,
2079 bool *input_pending)
2080{
2081 struct cf_osslq_ctx *ctx = cf->ctx;
2082 bool alive = FALSE;
2083 struct cf_call_data save;
2084
2085 CF_DATA_SAVE(save, cf, data);
2086 *input_pending = FALSE;
2087 if(!ctx->tls.ssl)
2088 goto out;
2089
2090#ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT
2091 /* Added in OpenSSL v3.3.x */
2092 {
2093 timediff_t idletime;
2094 uint64_t idle_ms = ctx->max_idle_ms;
2095 if(!SSL_get_value_uint(ctx->tls.ssl, SSL_VALUE_CLASS_FEATURE_NEGOTIATED,
2096 SSL_VALUE_QUIC_IDLE_TIMEOUT, &idle_ms)) {
2097 CURL_TRC_CF(data, cf, "error getting negotiated idle timeout, "
2098 "assume connection is dead.");
2099 goto out;
2100 }
2101 CURL_TRC_CF(data, cf, "negotiated idle timeout: %zums", (size_t)idle_ms);
2102 idletime = Curl_timediff(Curl_now(), ctx->q.last_io);
2103 if(idletime > 0 && (uint64_t)idletime > idle_ms)
2104 goto out;
2105 }
2106
2107#endif
2108
2109 if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending))
2110 goto out;
2111
2112 alive = TRUE;
2113 if(*input_pending) {
2114 CURLcode result;
2115 /* This happens before we've sent off a request and the connection is
2116 not in use by any other transfer, there shouldn't be any data here,
2117 only "protocol frames" */
2118 *input_pending = FALSE;
2119 result = cf_progress_ingress(cf, data);
2120 CURL_TRC_CF(data, cf, "is_alive, progress ingress -> %d", result);
2121 alive = result? FALSE : TRUE;
2122 }
2123
2124out:
2125 CF_DATA_RESTORE(cf, save);
2126 return alive;
2127}
2128
2129static void cf_osslq_adjust_pollset(struct Curl_cfilter *cf,
2130 struct Curl_easy *data,
2131 struct easy_pollset *ps)
2132{
2133 struct cf_osslq_ctx *ctx = cf->ctx;
2134
2135 if(!ctx->tls.ssl) {
2136 /* NOP */
2137 }
2138 else if(!cf->connected) {
2139 /* during handshake, transfer has not started yet. we always
2140 * add our socket for polling if SSL wants to send/recv */
2141 Curl_pollset_set(data, ps, ctx->q.sockfd,
2142 SSL_net_read_desired(ctx->tls.ssl),
2143 SSL_net_write_desired(ctx->tls.ssl));
2144 }
2145 else {
2146 /* once connected, we only modify the socket if it is present.
2147 * this avoids adding it for paused transfers. */
2148 bool want_recv, want_send;
2149 Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send);
2150 if(want_recv || want_send) {
2151 Curl_pollset_set(data, ps, ctx->q.sockfd,
2152 SSL_net_read_desired(ctx->tls.ssl),
2153 SSL_net_write_desired(ctx->tls.ssl));
2154 }
2155 }
2156}
2157
2158static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
2159 struct Curl_easy *data,
2160 int query, int *pres1, void *pres2)
2161{
2162 struct cf_osslq_ctx *ctx = cf->ctx;
2163
2164 switch(query) {
2165 case CF_QUERY_MAX_CONCURRENT: {
2166#ifdef SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL
2167 /* Added in OpenSSL v3.3.x */
2168 uint64_t v;
2169 if(!SSL_get_value_uint(ctx->tls.ssl, SSL_VALUE_CLASS_GENERIC,
2170 SSL_VALUE_QUIC_STREAM_BIDI_LOCAL_AVAIL, &v)) {
2171 CURL_TRC_CF(data, cf, "error getting available local bidi streams");
2172 return CURLE_HTTP3;
2173 }
2174 /* we report avail + in_use */
2175 v += CONN_INUSE(cf->conn);
2176 *pres1 = (v > INT_MAX)? INT_MAX : (int)v;
2177#else
2178 *pres1 = 100;
2179#endif
2180 CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1);
2181 return CURLE_OK;
2182 }
2183 case CF_QUERY_CONNECT_REPLY_MS:
2184 if(ctx->got_first_byte) {
2185 timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
2186 *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX;
2187 }
2188 else
2189 *pres1 = -1;
2190 return CURLE_OK;
2191 case CF_QUERY_TIMER_CONNECT: {
2192 struct curltime *when = pres2;
2193 if(ctx->got_first_byte)
2194 *when = ctx->first_byte_at;
2195 return CURLE_OK;
2196 }
2197 case CF_QUERY_TIMER_APPCONNECT: {
2198 struct curltime *when = pres2;
2199 if(cf->connected)
2200 *when = ctx->handshake_at;
2201 return CURLE_OK;
2202 }
2203 default:
2204 break;
2205 }
2206 return cf->next?
2207 cf->next->cft->query(cf->next, data, query, pres1, pres2) :
2208 CURLE_UNKNOWN_OPTION;
2209}
2210
2211struct Curl_cftype Curl_cft_http3 = {
2212 "HTTP/3",
2213 CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
2214 0,
2215 cf_osslq_destroy,
2216 cf_osslq_connect,
2217 cf_osslq_close,
2218 Curl_cf_def_get_host,
2219 cf_osslq_adjust_pollset,
2220 cf_osslq_data_pending,
2221 cf_osslq_send,
2222 cf_osslq_recv,
2223 cf_osslq_data_event,
2224 cf_osslq_conn_is_alive,
2225 Curl_cf_def_conn_keep_alive,
2226 cf_osslq_query,
2227};
2228
2229CURLcode Curl_cf_osslq_create(struct Curl_cfilter **pcf,
2230 struct Curl_easy *data,
2231 struct connectdata *conn,
2232 const struct Curl_addrinfo *ai)
2233{
2234 struct cf_osslq_ctx *ctx = NULL;
2235 struct Curl_cfilter *cf = NULL, *udp_cf = NULL;
2236 CURLcode result;
2237
2238 (void)data;
2239 ctx = calloc(1, sizeof(*ctx));
2240 if(!ctx) {
2241 result = CURLE_OUT_OF_MEMORY;
2242 goto out;
2243 }
2244 cf_osslq_ctx_clear(ctx);
2245
2246 result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
2247 if(result)
2248 goto out;
2249
2250 result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC);
2251 if(result)
2252 goto out;
2253
2254 cf->conn = conn;
2255 udp_cf->conn = cf->conn;
2256 udp_cf->sockindex = cf->sockindex;
2257 cf->next = udp_cf;
2258
2259out:
2260 *pcf = (!result)? cf : NULL;
2261 if(result) {
2262 if(udp_cf)
2263 Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE);
2264 Curl_safefree(cf);
2265 Curl_safefree(ctx);
2266 }
2267 return result;
2268}
2269
2270bool Curl_conn_is_osslq(const struct Curl_easy *data,
2271 const struct connectdata *conn,
2272 int sockindex)
2273{
2274 struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
2275
2276 (void)data;
2277 for(; cf; cf = cf->next) {
2278 if(cf->cft == &Curl_cft_http3)
2279 return TRUE;
2280 if(cf->cft->flags & CF_TYPE_IP_CONNECT)
2281 return FALSE;
2282 }
2283 return FALSE;
2284}
2285
2286/*
2287 * Store ngtcp2 version info in this buffer.
2288 */
2289void Curl_osslq_ver(char *p, size_t len)
2290{
2291 const nghttp3_info *ht3 = nghttp3_version(0);
2292 (void)msnprintf(p, len, "nghttp3/%s", ht3->version_str);
2293}
2294
2295#endif /* USE_OPENSSL_QUIC && USE_NGHTTP3 */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette