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