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