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