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_NGTCP2) && defined(USE_NGHTTP3) |
28 | #include <ngtcp2/ngtcp2.h> |
29 | #include <nghttp3/nghttp3.h> |
30 | |
31 | #ifdef USE_OPENSSL |
32 | #include <openssl/err.h> |
33 | #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) |
34 | #include <ngtcp2/ngtcp2_crypto_boringssl.h> |
35 | #else |
36 | #include <ngtcp2/ngtcp2_crypto_quictls.h> |
37 | #endif |
38 | #include "vtls/openssl.h" |
39 | #elif defined(USE_GNUTLS) |
40 | #include <ngtcp2/ngtcp2_crypto_gnutls.h> |
41 | #include "vtls/gtls.h" |
42 | #elif defined(USE_WOLFSSL) |
43 | #include <ngtcp2/ngtcp2_crypto_wolfssl.h> |
44 | #include "vtls/wolfssl.h" |
45 | #endif |
46 | |
47 | #include "urldata.h" |
48 | #include "sendf.h" |
49 | #include "strdup.h" |
50 | #include "rand.h" |
51 | #include "multiif.h" |
52 | #include "strcase.h" |
53 | #include "cfilters.h" |
54 | #include "cf-socket.h" |
55 | #include "connect.h" |
56 | #include "progress.h" |
57 | #include "strerror.h" |
58 | #include "dynbuf.h" |
59 | #include "http1.h" |
60 | #include "select.h" |
61 | #include "inet_pton.h" |
62 | #include "vquic.h" |
63 | #include "vquic_int.h" |
64 | #include "vtls/keylog.h" |
65 | #include "vtls/vtls.h" |
66 | #include "curl_ngtcp2.h" |
67 | |
68 | #include "warnless.h" |
69 | |
70 | /* The last 3 #include files should be in this order */ |
71 | #include "curl_printf.h" |
72 | #include "curl_memory.h" |
73 | #include "memdebug.h" |
74 | |
75 | |
76 | #define H3_ALPN_H3_29 "\x5h3-29" |
77 | #define H3_ALPN_H3 "\x2h3" |
78 | |
79 | #define QUIC_MAX_STREAMS (256*1024) |
80 | #define QUIC_MAX_DATA (1*1024*1024) |
81 | #define QUIC_IDLE_TIMEOUT (60*NGTCP2_SECONDS) |
82 | #define QUIC_HANDSHAKE_TIMEOUT (10*NGTCP2_SECONDS) |
83 | |
84 | /* A stream window is the maximum amount we need to buffer for |
85 | * each active transfer. We use HTTP/3 flow control and only ACK |
86 | * when we take things out of the buffer. |
87 | * Chunk size is large enough to take a full DATA frame */ |
88 | #define H3_STREAM_WINDOW_SIZE (128 * 1024) |
89 | #define H3_STREAM_CHUNK_SIZE (16 * 1024) |
90 | /* The pool keeps spares around and half of a full stream windows |
91 | * seems good. More does not seem to improve performance. |
92 | * The benefit of the pool is that stream buffer to not keep |
93 | * spares. So memory consumption goes down when streams run empty, |
94 | * have a large upload done, etc. */ |
95 | #define H3_STREAM_POOL_SPARES \ |
96 | (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE ) / 2 |
97 | /* Receive and Send max number of chunks just follows from the |
98 | * chunk size and window size */ |
99 | #define H3_STREAM_RECV_CHUNKS \ |
100 | (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) |
101 | #define H3_STREAM_SEND_CHUNKS \ |
102 | (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) |
103 | |
104 | |
105 | #ifdef USE_OPENSSL |
106 | #define QUIC_CIPHERS \ |
107 | "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ |
108 | "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" |
109 | #define QUIC_GROUPS "P-256:X25519:P-384:P-521" |
110 | #elif defined(USE_GNUTLS) |
111 | #define QUIC_PRIORITY \ |
112 | "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \ |
113 | "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \ |
114 | "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \ |
115 | "%DISABLE_TLS13_COMPAT_MODE" |
116 | #elif defined(USE_WOLFSSL) |
117 | #define QUIC_CIPHERS \ |
118 | "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ |
119 | "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" |
120 | #define QUIC_GROUPS "P-256:P-384:P-521" |
121 | #endif |
122 | |
123 | |
124 | /* |
125 | * Store ngtcp2 version info in this buffer. |
126 | */ |
127 | void Curl_ngtcp2_ver(char *p, size_t len) |
128 | { |
129 | const ngtcp2_info *ng2 = ngtcp2_version(0); |
130 | const nghttp3_info *ht3 = nghttp3_version(0); |
131 | (void)msnprintf(p, len, "ngtcp2/%s nghttp3/%s" , |
132 | ng2->version_str, ht3->version_str); |
133 | } |
134 | |
135 | struct cf_ngtcp2_ctx { |
136 | struct cf_quic_ctx q; |
137 | ngtcp2_path connected_path; |
138 | ngtcp2_conn *qconn; |
139 | ngtcp2_cid dcid; |
140 | ngtcp2_cid scid; |
141 | uint32_t version; |
142 | ngtcp2_settings settings; |
143 | ngtcp2_transport_params transport_params; |
144 | ngtcp2_ccerr last_error; |
145 | ngtcp2_crypto_conn_ref conn_ref; |
146 | #ifdef USE_OPENSSL |
147 | SSL_CTX *sslctx; |
148 | SSL *ssl; |
149 | #elif defined(USE_GNUTLS) |
150 | struct gtls_instance *gtls; |
151 | #elif defined(USE_WOLFSSL) |
152 | WOLFSSL_CTX *sslctx; |
153 | WOLFSSL *ssl; |
154 | #endif |
155 | struct cf_call_data call_data; |
156 | nghttp3_conn *h3conn; |
157 | nghttp3_settings h3settings; |
158 | struct curltime started_at; /* time the current attempt started */ |
159 | struct curltime handshake_at; /* time connect handshake finished */ |
160 | struct curltime first_byte_at; /* when first byte was recvd */ |
161 | struct curltime reconnect_at; /* time the next attempt should start */ |
162 | struct bufc_pool stream_bufcp; /* chunk pool for streams */ |
163 | size_t max_stream_window; /* max flow window for one stream */ |
164 | int qlogfd; |
165 | BIT(got_first_byte); /* if first byte was received */ |
166 | #ifdef USE_OPENSSL |
167 | BIT(x509_store_setup); /* if x509 store has been set up */ |
168 | #endif |
169 | }; |
170 | |
171 | /* How to access `call_data` from a cf_ngtcp2 filter */ |
172 | #undef CF_CTX_CALL_DATA |
173 | #define CF_CTX_CALL_DATA(cf) \ |
174 | ((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data |
175 | |
176 | /** |
177 | * All about the H3 internals of a stream |
178 | */ |
179 | struct h3_stream_ctx { |
180 | int64_t id; /* HTTP/3 protocol identifier */ |
181 | struct bufq sendbuf; /* h3 request body */ |
182 | struct bufq recvbuf; /* h3 response body */ |
183 | struct h1_req_parser h1; /* h1 request parsing */ |
184 | size_t sendbuf_len_in_flight; /* sendbuf amount "in flight" */ |
185 | size_t upload_blocked_len; /* the amount written last and EGAINed */ |
186 | size_t recv_buf_nonflow; /* buffered bytes, not counting for flow control */ |
187 | uint64_t error3; /* HTTP/3 stream error code */ |
188 | curl_off_t upload_left; /* number of request bytes left to upload */ |
189 | int status_code; /* HTTP status code */ |
190 | bool resp_hds_complete; /* we have a complete, final response */ |
191 | bool closed; /* TRUE on stream close */ |
192 | bool reset; /* TRUE on stream reset */ |
193 | bool send_closed; /* stream is local closed */ |
194 | }; |
195 | |
196 | #define H3_STREAM_CTX(d) ((struct h3_stream_ctx *)(((d) && (d)->req.p.http)? \ |
197 | ((struct HTTP *)(d)->req.p.http)->h3_ctx \ |
198 | : NULL)) |
199 | #define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx |
200 | #define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \ |
201 | H3_STREAM_CTX(d)->id : -2) |
202 | |
203 | static CURLcode h3_data_setup(struct Curl_cfilter *cf, |
204 | struct Curl_easy *data) |
205 | { |
206 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
207 | struct h3_stream_ctx *stream = H3_STREAM_CTX(data); |
208 | |
209 | if(!data || !data->req.p.http) { |
210 | failf(data, "initialization failure, transfer not http initialized" ); |
211 | return CURLE_FAILED_INIT; |
212 | } |
213 | |
214 | if(stream) |
215 | return CURLE_OK; |
216 | |
217 | stream = calloc(1, sizeof(*stream)); |
218 | if(!stream) |
219 | return CURLE_OUT_OF_MEMORY; |
220 | |
221 | stream->id = -1; |
222 | /* on send, we control how much we put into the buffer */ |
223 | Curl_bufq_initp(&stream->sendbuf, &ctx->stream_bufcp, |
224 | H3_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE); |
225 | stream->sendbuf_len_in_flight = 0; |
226 | /* on recv, we need a flexible buffer limit since we also write |
227 | * headers to it that are not counted against the nghttp3 flow limits. */ |
228 | Curl_bufq_initp(&stream->recvbuf, &ctx->stream_bufcp, |
229 | H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); |
230 | stream->recv_buf_nonflow = 0; |
231 | Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); |
232 | |
233 | H3_STREAM_LCTX(data) = stream; |
234 | return CURLE_OK; |
235 | } |
236 | |
237 | static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) |
238 | { |
239 | struct h3_stream_ctx *stream = H3_STREAM_CTX(data); |
240 | |
241 | (void)cf; |
242 | if(stream) { |
243 | CURL_TRC_CF(data, cf, "[%" PRId64"] easy handle is done" , stream->id); |
244 | Curl_bufq_free(&stream->sendbuf); |
245 | Curl_bufq_free(&stream->recvbuf); |
246 | Curl_h1_req_parse_free(&stream->h1); |
247 | free(stream); |
248 | H3_STREAM_LCTX(data) = NULL; |
249 | } |
250 | } |
251 | |
252 | /* ngtcp2 default congestion controller does not perform pacing. Limit |
253 | the maximum packet burst to MAX_PKT_BURST packets. */ |
254 | #define MAX_PKT_BURST 10 |
255 | |
256 | struct pkt_io_ctx { |
257 | struct Curl_cfilter *cf; |
258 | struct Curl_easy *data; |
259 | ngtcp2_tstamp ts; |
260 | size_t pkt_count; |
261 | ngtcp2_path_storage ps; |
262 | }; |
263 | |
264 | static ngtcp2_tstamp timestamp(void) |
265 | { |
266 | struct curltime ct = Curl_now(); |
267 | return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS; |
268 | } |
269 | |
270 | static void pktx_init(struct pkt_io_ctx *pktx, |
271 | struct Curl_cfilter *cf, |
272 | struct Curl_easy *data) |
273 | { |
274 | pktx->cf = cf; |
275 | pktx->data = data; |
276 | pktx->ts = timestamp(); |
277 | pktx->pkt_count = 0; |
278 | ngtcp2_path_storage_zero(&pktx->ps); |
279 | } |
280 | |
281 | static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, |
282 | struct Curl_easy *data, |
283 | struct pkt_io_ctx *pktx); |
284 | static CURLcode cf_progress_egress(struct Curl_cfilter *cf, |
285 | struct Curl_easy *data, |
286 | struct pkt_io_ctx *pktx); |
287 | static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, |
288 | uint64_t datalen, void *user_data, |
289 | void *stream_user_data); |
290 | |
291 | static ngtcp2_conn *get_conn(ngtcp2_crypto_conn_ref *conn_ref) |
292 | { |
293 | struct Curl_cfilter *cf = conn_ref->user_data; |
294 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
295 | return ctx->qconn; |
296 | } |
297 | |
298 | #ifdef DEBUG_NGTCP2 |
299 | static void quic_printf(void *user_data, const char *fmt, ...) |
300 | { |
301 | struct Curl_cfilter *cf = user_data; |
302 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
303 | |
304 | (void)ctx; /* TODO: need an easy handle to infof() message */ |
305 | va_list ap; |
306 | va_start(ap, fmt); |
307 | vfprintf(stderr, fmt, ap); |
308 | va_end(ap); |
309 | fprintf(stderr, "\n" ); |
310 | } |
311 | #endif |
312 | |
313 | static void qlog_callback(void *user_data, uint32_t flags, |
314 | const void *data, size_t datalen) |
315 | { |
316 | struct Curl_cfilter *cf = user_data; |
317 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
318 | (void)flags; |
319 | if(ctx->qlogfd != -1) { |
320 | ssize_t rc = write(ctx->qlogfd, data, datalen); |
321 | if(rc == -1) { |
322 | /* on write error, stop further write attempts */ |
323 | close(ctx->qlogfd); |
324 | ctx->qlogfd = -1; |
325 | } |
326 | } |
327 | |
328 | } |
329 | |
330 | static void quic_settings(struct cf_ngtcp2_ctx *ctx, |
331 | struct Curl_easy *data, |
332 | struct pkt_io_ctx *pktx) |
333 | { |
334 | ngtcp2_settings *s = &ctx->settings; |
335 | ngtcp2_transport_params *t = &ctx->transport_params; |
336 | |
337 | ngtcp2_settings_default(s); |
338 | ngtcp2_transport_params_default(t); |
339 | #ifdef DEBUG_NGTCP2 |
340 | s->log_printf = quic_printf; |
341 | #else |
342 | s->log_printf = NULL; |
343 | #endif |
344 | |
345 | (void)data; |
346 | s->initial_ts = pktx->ts; |
347 | s->handshake_timeout = QUIC_HANDSHAKE_TIMEOUT; |
348 | s->max_window = 100 * ctx->max_stream_window; |
349 | s->max_stream_window = ctx->max_stream_window; |
350 | |
351 | t->initial_max_data = 10 * ctx->max_stream_window; |
352 | t->initial_max_stream_data_bidi_local = ctx->max_stream_window; |
353 | t->initial_max_stream_data_bidi_remote = ctx->max_stream_window; |
354 | t->initial_max_stream_data_uni = ctx->max_stream_window; |
355 | t->initial_max_streams_bidi = QUIC_MAX_STREAMS; |
356 | t->initial_max_streams_uni = QUIC_MAX_STREAMS; |
357 | t->max_idle_timeout = QUIC_IDLE_TIMEOUT; |
358 | if(ctx->qlogfd != -1) { |
359 | s->qlog_write = qlog_callback; |
360 | } |
361 | } |
362 | |
363 | #ifdef USE_OPENSSL |
364 | static void keylog_callback(const SSL *ssl, const char *line) |
365 | { |
366 | (void)ssl; |
367 | Curl_tls_keylog_write_line(line); |
368 | } |
369 | #elif defined(USE_GNUTLS) |
370 | static int keylog_callback(gnutls_session_t session, const char *label, |
371 | const gnutls_datum_t *secret) |
372 | { |
373 | gnutls_datum_t crandom; |
374 | gnutls_datum_t srandom; |
375 | |
376 | gnutls_session_get_random(session, &crandom, &srandom); |
377 | if(crandom.size != 32) { |
378 | return -1; |
379 | } |
380 | |
381 | Curl_tls_keylog_write(label, crandom.data, secret->data, secret->size); |
382 | return 0; |
383 | } |
384 | #elif defined(USE_WOLFSSL) |
385 | #if defined(HAVE_SECRET_CALLBACK) |
386 | static void keylog_callback(const WOLFSSL *ssl, const char *line) |
387 | { |
388 | (void)ssl; |
389 | Curl_tls_keylog_write_line(line); |
390 | } |
391 | #endif |
392 | #endif |
393 | |
394 | static int init_ngh3_conn(struct Curl_cfilter *cf); |
395 | |
396 | #ifdef USE_OPENSSL |
397 | static CURLcode quic_ssl_ctx(SSL_CTX **pssl_ctx, |
398 | struct Curl_cfilter *cf, struct Curl_easy *data) |
399 | { |
400 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
401 | struct connectdata *conn = cf->conn; |
402 | CURLcode result = CURLE_FAILED_INIT; |
403 | SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); |
404 | |
405 | if(!ssl_ctx) { |
406 | result = CURLE_OUT_OF_MEMORY; |
407 | goto out; |
408 | } |
409 | |
410 | #if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC) |
411 | if(ngtcp2_crypto_boringssl_configure_client_context(ssl_ctx) != 0) { |
412 | failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed" ); |
413 | goto out; |
414 | } |
415 | #else |
416 | if(ngtcp2_crypto_quictls_configure_client_context(ssl_ctx) != 0) { |
417 | failf(data, "ngtcp2_crypto_quictls_configure_client_context failed" ); |
418 | goto out; |
419 | } |
420 | #endif |
421 | |
422 | SSL_CTX_set_default_verify_paths(ssl_ctx); |
423 | |
424 | { |
425 | const char *curves = conn->ssl_config.curves ? |
426 | conn->ssl_config.curves : QUIC_GROUPS; |
427 | if(!SSL_CTX_set1_curves_list(ssl_ctx, curves)) { |
428 | failf(data, "failed setting curves list for QUIC: '%s'" , curves); |
429 | return CURLE_SSL_CIPHER; |
430 | } |
431 | } |
432 | |
433 | #ifndef OPENSSL_IS_BORINGSSL |
434 | { |
435 | const char *ciphers13 = conn->ssl_config.cipher_list13 ? |
436 | conn->ssl_config.cipher_list13 : QUIC_CIPHERS; |
437 | if(SSL_CTX_set_ciphersuites(ssl_ctx, ciphers13) != 1) { |
438 | failf(data, "failed setting QUIC cipher suite: %s" , ciphers13); |
439 | return CURLE_SSL_CIPHER; |
440 | } |
441 | infof(data, "QUIC cipher selection: %s" , ciphers13); |
442 | } |
443 | #endif |
444 | |
445 | /* Open the file if a TLS or QUIC backend has not done this before. */ |
446 | Curl_tls_keylog_open(); |
447 | if(Curl_tls_keylog_enabled()) { |
448 | SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); |
449 | } |
450 | |
451 | /* OpenSSL always tries to verify the peer, this only says whether it should |
452 | * fail to connect if the verification fails, or if it should continue |
453 | * anyway. In the latter case the result of the verification is checked with |
454 | * SSL_get_verify_result() below. */ |
455 | SSL_CTX_set_verify(ssl_ctx, conn->ssl_config.verifypeer ? |
456 | SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL); |
457 | |
458 | /* give application a chance to interfere with SSL set up. */ |
459 | if(data->set.ssl.fsslctx) { |
460 | /* When a user callback is installed to modify the SSL_CTX, |
461 | * we need to do the full initialization before calling it. |
462 | * See: #11800 */ |
463 | if(!ctx->x509_store_setup) { |
464 | result = Curl_ssl_setup_x509_store(cf, data, ssl_ctx); |
465 | if(result) |
466 | goto out; |
467 | ctx->x509_store_setup = TRUE; |
468 | } |
469 | Curl_set_in_callback(data, true); |
470 | result = (*data->set.ssl.fsslctx)(data, ssl_ctx, |
471 | data->set.ssl.fsslctxp); |
472 | Curl_set_in_callback(data, false); |
473 | if(result) { |
474 | failf(data, "error signaled by ssl ctx callback" ); |
475 | goto out; |
476 | } |
477 | } |
478 | result = CURLE_OK; |
479 | |
480 | out: |
481 | *pssl_ctx = result? NULL : ssl_ctx; |
482 | if(result && ssl_ctx) |
483 | SSL_CTX_free(ssl_ctx); |
484 | return result; |
485 | } |
486 | |
487 | static CURLcode quic_set_client_cert(struct Curl_cfilter *cf, |
488 | struct Curl_easy *data) |
489 | { |
490 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
491 | SSL_CTX *ssl_ctx = ctx->sslctx; |
492 | const struct ssl_config_data *ssl_config; |
493 | |
494 | ssl_config = Curl_ssl_get_config(data, FIRSTSOCKET); |
495 | DEBUGASSERT(ssl_config); |
496 | |
497 | if(ssl_config->primary.clientcert || ssl_config->primary.cert_blob |
498 | || ssl_config->cert_type) { |
499 | return Curl_ossl_set_client_cert( |
500 | data, ssl_ctx, ssl_config->primary.clientcert, |
501 | ssl_config->primary.cert_blob, ssl_config->cert_type, |
502 | ssl_config->key, ssl_config->key_blob, |
503 | ssl_config->key_type, ssl_config->key_passwd); |
504 | } |
505 | |
506 | return CURLE_OK; |
507 | } |
508 | |
509 | /** SSL callbacks ***/ |
510 | |
511 | static CURLcode quic_init_ssl(struct Curl_cfilter *cf, |
512 | struct Curl_easy *data) |
513 | { |
514 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
515 | const uint8_t *alpn = NULL; |
516 | size_t alpnlen = 0; |
517 | unsigned char checkip[16]; |
518 | |
519 | DEBUGASSERT(!ctx->ssl); |
520 | ctx->ssl = SSL_new(ctx->sslctx); |
521 | |
522 | SSL_set_app_data(ctx->ssl, &ctx->conn_ref); |
523 | SSL_set_connect_state(ctx->ssl); |
524 | SSL_set_quic_use_legacy_codepoint(ctx->ssl, 0); |
525 | |
526 | alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3; |
527 | alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1; |
528 | if(alpn) |
529 | SSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen); |
530 | |
531 | /* set SNI */ |
532 | if((0 == Curl_inet_pton(AF_INET, cf->conn->host.name, checkip)) |
533 | #ifdef ENABLE_IPV6 |
534 | && (0 == Curl_inet_pton(AF_INET6, cf->conn->host.name, checkip)) |
535 | #endif |
536 | ) { |
537 | char *snihost = Curl_ssl_snihost(data, cf->conn->host.name, NULL); |
538 | if(!snihost || !SSL_set_tlsext_host_name(ctx->ssl, snihost)) { |
539 | failf(data, "Failed set SNI" ); |
540 | SSL_free(ctx->ssl); |
541 | ctx->ssl = NULL; |
542 | return CURLE_QUIC_CONNECT_ERROR; |
543 | } |
544 | } |
545 | return CURLE_OK; |
546 | } |
547 | #elif defined(USE_GNUTLS) |
548 | static CURLcode quic_init_ssl(struct Curl_cfilter *cf, |
549 | struct Curl_easy *data) |
550 | { |
551 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
552 | CURLcode result; |
553 | gnutls_datum_t alpn[2]; |
554 | /* this will need some attention when HTTPS proxy over QUIC get fixed */ |
555 | const char * const hostname = cf->conn->host.name; |
556 | long * const pverifyresult = &data->set.ssl.certverifyresult; |
557 | int rc; |
558 | |
559 | DEBUGASSERT(ctx->gtls == NULL); |
560 | ctx->gtls = calloc(1, sizeof(*(ctx->gtls))); |
561 | if(!ctx->gtls) |
562 | return CURLE_OUT_OF_MEMORY; |
563 | |
564 | result = gtls_client_init(data, &cf->conn->ssl_config, &data->set.ssl, |
565 | hostname, ctx->gtls, pverifyresult); |
566 | if(result) |
567 | return result; |
568 | |
569 | gnutls_session_set_ptr(ctx->gtls->session, &ctx->conn_ref); |
570 | |
571 | if(ngtcp2_crypto_gnutls_configure_client_session(ctx->gtls->session) != 0) { |
572 | CURL_TRC_CF(data, cf, |
573 | "ngtcp2_crypto_gnutls_configure_client_session failed\n" ); |
574 | return CURLE_QUIC_CONNECT_ERROR; |
575 | } |
576 | |
577 | rc = gnutls_priority_set_direct(ctx->gtls->session, QUIC_PRIORITY, NULL); |
578 | if(rc < 0) { |
579 | CURL_TRC_CF(data, cf, "gnutls_priority_set_direct failed: %s\n" , |
580 | gnutls_strerror(rc)); |
581 | return CURLE_QUIC_CONNECT_ERROR; |
582 | } |
583 | |
584 | /* Open the file if a TLS or QUIC backend has not done this before. */ |
585 | Curl_tls_keylog_open(); |
586 | if(Curl_tls_keylog_enabled()) { |
587 | gnutls_session_set_keylog_function(ctx->gtls->session, keylog_callback); |
588 | } |
589 | |
590 | /* strip the first byte (the length) from NGHTTP3_ALPN_H3 */ |
591 | alpn[0].data = (unsigned char *)H3_ALPN_H3_29 + 1; |
592 | alpn[0].size = sizeof(H3_ALPN_H3_29) - 2; |
593 | alpn[1].data = (unsigned char *)H3_ALPN_H3 + 1; |
594 | alpn[1].size = sizeof(H3_ALPN_H3) - 2; |
595 | |
596 | gnutls_alpn_set_protocols(ctx->gtls->session, |
597 | alpn, 2, GNUTLS_ALPN_MANDATORY); |
598 | return CURLE_OK; |
599 | } |
600 | #elif defined(USE_WOLFSSL) |
601 | |
602 | static CURLcode quic_ssl_ctx(WOLFSSL_CTX **pssl_ctx, |
603 | struct Curl_cfilter *cf, struct Curl_easy *data) |
604 | { |
605 | struct connectdata *conn = cf->conn; |
606 | CURLcode result = CURLE_FAILED_INIT; |
607 | WOLFSSL_CTX *ssl_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method()); |
608 | |
609 | if(!ssl_ctx) { |
610 | result = CURLE_OUT_OF_MEMORY; |
611 | goto out; |
612 | } |
613 | |
614 | if(ngtcp2_crypto_wolfssl_configure_client_context(ssl_ctx) != 0) { |
615 | failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed" ); |
616 | goto out; |
617 | } |
618 | |
619 | wolfSSL_CTX_set_default_verify_paths(ssl_ctx); |
620 | |
621 | if(wolfSSL_CTX_set_cipher_list(ssl_ctx, conn->ssl_config.cipher_list13 ? |
622 | conn->ssl_config.cipher_list13 : |
623 | QUIC_CIPHERS) != 1) { |
624 | char error_buffer[256]; |
625 | ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); |
626 | failf(data, "wolfSSL failed to set ciphers: %s" , error_buffer); |
627 | goto out; |
628 | } |
629 | |
630 | if(wolfSSL_CTX_set1_groups_list(ssl_ctx, conn->ssl_config.curves ? |
631 | conn->ssl_config.curves : |
632 | (char *)QUIC_GROUPS) != 1) { |
633 | failf(data, "wolfSSL failed to set curves" ); |
634 | goto out; |
635 | } |
636 | |
637 | /* Open the file if a TLS or QUIC backend has not done this before. */ |
638 | Curl_tls_keylog_open(); |
639 | if(Curl_tls_keylog_enabled()) { |
640 | #if defined(HAVE_SECRET_CALLBACK) |
641 | wolfSSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); |
642 | #else |
643 | failf(data, "wolfSSL was built without keylog callback" ); |
644 | goto out; |
645 | #endif |
646 | } |
647 | |
648 | if(conn->ssl_config.verifypeer) { |
649 | const char * const ssl_cafile = conn->ssl_config.CAfile; |
650 | const char * const ssl_capath = conn->ssl_config.CApath; |
651 | |
652 | wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); |
653 | if(ssl_cafile || ssl_capath) { |
654 | /* tell wolfSSL where to find CA certificates that are used to verify |
655 | the server's certificate. */ |
656 | int rc = |
657 | wolfSSL_CTX_load_verify_locations_ex(ssl_ctx, ssl_cafile, ssl_capath, |
658 | WOLFSSL_LOAD_FLAG_IGNORE_ERR); |
659 | if(SSL_SUCCESS != rc) { |
660 | /* Fail if we insist on successfully verifying the server. */ |
661 | failf(data, "error setting certificate verify locations:" |
662 | " CAfile: %s CApath: %s" , |
663 | ssl_cafile ? ssl_cafile : "none" , |
664 | ssl_capath ? ssl_capath : "none" ); |
665 | goto out; |
666 | } |
667 | infof(data, " CAfile: %s" , ssl_cafile ? ssl_cafile : "none" ); |
668 | infof(data, " CApath: %s" , ssl_capath ? ssl_capath : "none" ); |
669 | } |
670 | #ifdef CURL_CA_FALLBACK |
671 | else { |
672 | /* verifying the peer without any CA certificates won't work so |
673 | use wolfssl's built-in default as fallback */ |
674 | wolfSSL_CTX_set_default_verify_paths(ssl_ctx); |
675 | } |
676 | #endif |
677 | } |
678 | else { |
679 | wolfSSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL); |
680 | } |
681 | |
682 | /* give application a chance to interfere with SSL set up. */ |
683 | if(data->set.ssl.fsslctx) { |
684 | Curl_set_in_callback(data, true); |
685 | result = (*data->set.ssl.fsslctx)(data, ssl_ctx, |
686 | data->set.ssl.fsslctxp); |
687 | Curl_set_in_callback(data, false); |
688 | if(result) { |
689 | failf(data, "error signaled by ssl ctx callback" ); |
690 | goto out; |
691 | } |
692 | } |
693 | result = CURLE_OK; |
694 | |
695 | out: |
696 | *pssl_ctx = result? NULL : ssl_ctx; |
697 | if(result && ssl_ctx) |
698 | SSL_CTX_free(ssl_ctx); |
699 | return result; |
700 | } |
701 | |
702 | /** SSL callbacks ***/ |
703 | |
704 | static CURLcode quic_init_ssl(struct Curl_cfilter *cf, |
705 | struct Curl_easy *data) |
706 | { |
707 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
708 | const uint8_t *alpn = NULL; |
709 | size_t alpnlen = 0; |
710 | /* this will need some attention when HTTPS proxy over QUIC get fixed */ |
711 | const char * const hostname = cf->conn->host.name; |
712 | |
713 | (void)data; |
714 | DEBUGASSERT(!ctx->ssl); |
715 | ctx->ssl = wolfSSL_new(ctx->sslctx); |
716 | |
717 | wolfSSL_set_app_data(ctx->ssl, &ctx->conn_ref); |
718 | wolfSSL_set_connect_state(ctx->ssl); |
719 | wolfSSL_set_quic_use_legacy_codepoint(ctx->ssl, 0); |
720 | |
721 | alpn = (const uint8_t *)H3_ALPN_H3_29 H3_ALPN_H3; |
722 | alpnlen = sizeof(H3_ALPN_H3_29) - 1 + sizeof(H3_ALPN_H3) - 1; |
723 | if(alpn) |
724 | wolfSSL_set_alpn_protos(ctx->ssl, alpn, (int)alpnlen); |
725 | |
726 | /* set SNI */ |
727 | wolfSSL_UseSNI(ctx->ssl, WOLFSSL_SNI_HOST_NAME, |
728 | hostname, (unsigned short)strlen(hostname)); |
729 | |
730 | return CURLE_OK; |
731 | } |
732 | #endif /* defined(USE_WOLFSSL) */ |
733 | |
734 | static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data) |
735 | { |
736 | (void)user_data; |
737 | (void)tconn; |
738 | return 0; |
739 | } |
740 | |
741 | static void report_consumed_data(struct Curl_cfilter *cf, |
742 | struct Curl_easy *data, |
743 | size_t consumed) |
744 | { |
745 | struct h3_stream_ctx *stream = H3_STREAM_CTX(data); |
746 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
747 | |
748 | if(!stream) |
749 | return; |
750 | /* the HTTP/1.1 response headers are written to the buffer, but |
751 | * consuming those does not count against flow control. */ |
752 | if(stream->recv_buf_nonflow) { |
753 | if(consumed >= stream->recv_buf_nonflow) { |
754 | consumed -= stream->recv_buf_nonflow; |
755 | stream->recv_buf_nonflow = 0; |
756 | } |
757 | else { |
758 | stream->recv_buf_nonflow -= consumed; |
759 | consumed = 0; |
760 | } |
761 | } |
762 | if(consumed > 0) { |
763 | CURL_TRC_CF(data, cf, "[%" PRId64 "] ACK %zu bytes of DATA" , |
764 | stream->id, consumed); |
765 | ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream->id, |
766 | consumed); |
767 | ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); |
768 | } |
769 | } |
770 | |
771 | static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags, |
772 | int64_t stream_id, uint64_t offset, |
773 | const uint8_t *buf, size_t buflen, |
774 | void *user_data, void *stream_user_data) |
775 | { |
776 | struct Curl_cfilter *cf = user_data; |
777 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
778 | nghttp3_ssize nconsumed; |
779 | int fin = (flags & NGTCP2_STREAM_DATA_FLAG_FIN) ? 1 : 0; |
780 | struct Curl_easy *data = stream_user_data; |
781 | (void)offset; |
782 | (void)data; |
783 | |
784 | nconsumed = |
785 | nghttp3_conn_read_stream(ctx->h3conn, stream_id, buf, buflen, fin); |
786 | CURL_TRC_CF(data, cf, "[%" PRId64 "] read_stream(len=%zu) -> %zd" , |
787 | stream_id, buflen, nconsumed); |
788 | if(nconsumed < 0) { |
789 | ngtcp2_ccerr_set_application_error( |
790 | &ctx->last_error, |
791 | nghttp3_err_infer_quic_app_error_code((int)nconsumed), NULL, 0); |
792 | return NGTCP2_ERR_CALLBACK_FAILURE; |
793 | } |
794 | |
795 | /* number of bytes inside buflen which consists of framing overhead |
796 | * including QPACK HEADERS. In other words, it does not consume payload of |
797 | * DATA frame. */ |
798 | ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed); |
799 | ngtcp2_conn_extend_max_offset(tconn, nconsumed); |
800 | |
801 | return 0; |
802 | } |
803 | |
804 | static int |
805 | cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, |
806 | uint64_t offset, uint64_t datalen, void *user_data, |
807 | void *stream_user_data) |
808 | { |
809 | struct Curl_cfilter *cf = user_data; |
810 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
811 | int rv; |
812 | (void)stream_id; |
813 | (void)tconn; |
814 | (void)offset; |
815 | (void)datalen; |
816 | (void)stream_user_data; |
817 | |
818 | rv = nghttp3_conn_add_ack_offset(ctx->h3conn, stream_id, datalen); |
819 | if(rv) { |
820 | return NGTCP2_ERR_CALLBACK_FAILURE; |
821 | } |
822 | |
823 | return 0; |
824 | } |
825 | |
826 | static int cb_stream_close(ngtcp2_conn *tconn, uint32_t flags, |
827 | int64_t stream3_id, uint64_t app_error_code, |
828 | void *user_data, void *stream_user_data) |
829 | { |
830 | struct Curl_cfilter *cf = user_data; |
831 | struct Curl_easy *data = stream_user_data; |
832 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
833 | int rv; |
834 | |
835 | (void)tconn; |
836 | (void)data; |
837 | /* stream is closed... */ |
838 | |
839 | if(!(flags & NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET)) { |
840 | app_error_code = NGHTTP3_H3_NO_ERROR; |
841 | } |
842 | |
843 | rv = nghttp3_conn_close_stream(ctx->h3conn, stream3_id, |
844 | app_error_code); |
845 | CURL_TRC_CF(data, cf, "[%" PRId64 "] quic close(err=%" |
846 | PRIu64 ") -> %d" , stream3_id, app_error_code, rv); |
847 | if(rv) { |
848 | ngtcp2_ccerr_set_application_error( |
849 | &ctx->last_error, nghttp3_err_infer_quic_app_error_code(rv), NULL, 0); |
850 | return NGTCP2_ERR_CALLBACK_FAILURE; |
851 | } |
852 | |
853 | return 0; |
854 | } |
855 | |
856 | static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, |
857 | uint64_t final_size, uint64_t app_error_code, |
858 | void *user_data, void *stream_user_data) |
859 | { |
860 | struct Curl_cfilter *cf = user_data; |
861 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
862 | struct Curl_easy *data = stream_user_data; |
863 | int rv; |
864 | (void)tconn; |
865 | (void)final_size; |
866 | (void)app_error_code; |
867 | (void)data; |
868 | |
869 | rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); |
870 | CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d" , stream_id, rv); |
871 | if(rv) { |
872 | return NGTCP2_ERR_CALLBACK_FAILURE; |
873 | } |
874 | |
875 | return 0; |
876 | } |
877 | |
878 | static int cb_stream_stop_sending(ngtcp2_conn *tconn, int64_t stream_id, |
879 | uint64_t app_error_code, void *user_data, |
880 | void *stream_user_data) |
881 | { |
882 | struct Curl_cfilter *cf = user_data; |
883 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
884 | int rv; |
885 | (void)tconn; |
886 | (void)app_error_code; |
887 | (void)stream_user_data; |
888 | |
889 | rv = nghttp3_conn_shutdown_stream_read(ctx->h3conn, stream_id); |
890 | if(rv) { |
891 | return NGTCP2_ERR_CALLBACK_FAILURE; |
892 | } |
893 | |
894 | return 0; |
895 | } |
896 | |
897 | static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn, |
898 | uint64_t max_streams, |
899 | void *user_data) |
900 | { |
901 | (void)tconn; |
902 | (void)max_streams; |
903 | (void)user_data; |
904 | |
905 | return 0; |
906 | } |
907 | |
908 | static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, |
909 | uint64_t max_data, void *user_data, |
910 | void *stream_user_data) |
911 | { |
912 | struct Curl_cfilter *cf = user_data; |
913 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
914 | int rv; |
915 | (void)tconn; |
916 | (void)max_data; |
917 | (void)stream_user_data; |
918 | |
919 | rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id); |
920 | if(rv) { |
921 | return NGTCP2_ERR_CALLBACK_FAILURE; |
922 | } |
923 | |
924 | return 0; |
925 | } |
926 | |
927 | static void cb_rand(uint8_t *dest, size_t destlen, |
928 | const ngtcp2_rand_ctx *rand_ctx) |
929 | { |
930 | CURLcode result; |
931 | (void)rand_ctx; |
932 | |
933 | result = Curl_rand(NULL, dest, destlen); |
934 | if(result) { |
935 | /* cb_rand is only used for non-cryptographic context. If Curl_rand |
936 | failed, just fill 0 and call it *random*. */ |
937 | memset(dest, 0, destlen); |
938 | } |
939 | } |
940 | |
941 | static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, |
942 | uint8_t *token, size_t cidlen, |
943 | void *user_data) |
944 | { |
945 | CURLcode result; |
946 | (void)tconn; |
947 | (void)user_data; |
948 | |
949 | result = Curl_rand(NULL, cid->data, cidlen); |
950 | if(result) |
951 | return NGTCP2_ERR_CALLBACK_FAILURE; |
952 | cid->datalen = cidlen; |
953 | |
954 | result = Curl_rand(NULL, token, NGTCP2_STATELESS_RESET_TOKENLEN); |
955 | if(result) |
956 | return NGTCP2_ERR_CALLBACK_FAILURE; |
957 | |
958 | return 0; |
959 | } |
960 | |
961 | static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level, |
962 | void *user_data) |
963 | { |
964 | struct Curl_cfilter *cf = user_data; |
965 | (void)tconn; |
966 | |
967 | if(level != NGTCP2_ENCRYPTION_LEVEL_1RTT) { |
968 | return 0; |
969 | } |
970 | |
971 | if(init_ngh3_conn(cf) != CURLE_OK) { |
972 | return NGTCP2_ERR_CALLBACK_FAILURE; |
973 | } |
974 | |
975 | return 0; |
976 | } |
977 | |
978 | static ngtcp2_callbacks ng_callbacks = { |
979 | ngtcp2_crypto_client_initial_cb, |
980 | NULL, /* recv_client_initial */ |
981 | ngtcp2_crypto_recv_crypto_data_cb, |
982 | cb_handshake_completed, |
983 | NULL, /* recv_version_negotiation */ |
984 | ngtcp2_crypto_encrypt_cb, |
985 | ngtcp2_crypto_decrypt_cb, |
986 | ngtcp2_crypto_hp_mask_cb, |
987 | cb_recv_stream_data, |
988 | cb_acked_stream_data_offset, |
989 | NULL, /* stream_open */ |
990 | cb_stream_close, |
991 | NULL, /* recv_stateless_reset */ |
992 | ngtcp2_crypto_recv_retry_cb, |
993 | cb_extend_max_local_streams_bidi, |
994 | NULL, /* extend_max_local_streams_uni */ |
995 | cb_rand, |
996 | cb_get_new_connection_id, |
997 | NULL, /* remove_connection_id */ |
998 | ngtcp2_crypto_update_key_cb, /* update_key */ |
999 | NULL, /* path_validation */ |
1000 | NULL, /* select_preferred_addr */ |
1001 | cb_stream_reset, |
1002 | NULL, /* extend_max_remote_streams_bidi */ |
1003 | NULL, /* extend_max_remote_streams_uni */ |
1004 | cb_extend_max_stream_data, |
1005 | NULL, /* dcid_status */ |
1006 | NULL, /* handshake_confirmed */ |
1007 | NULL, /* recv_new_token */ |
1008 | ngtcp2_crypto_delete_crypto_aead_ctx_cb, |
1009 | ngtcp2_crypto_delete_crypto_cipher_ctx_cb, |
1010 | NULL, /* recv_datagram */ |
1011 | NULL, /* ack_datagram */ |
1012 | NULL, /* lost_datagram */ |
1013 | ngtcp2_crypto_get_path_challenge_data_cb, |
1014 | cb_stream_stop_sending, |
1015 | NULL, /* version_negotiation */ |
1016 | cb_recv_rx_key, |
1017 | NULL, /* recv_tx_key */ |
1018 | NULL, /* early_data_rejected */ |
1019 | }; |
1020 | |
1021 | /** |
1022 | * Connection maintenance like timeouts on packet ACKs etc. are done by us, not |
1023 | * the OS like for TCP. POLL events on the socket therefore are not |
1024 | * sufficient. |
1025 | * ngtcp2 tells us when it wants to be invoked again. We handle that via |
1026 | * the `Curl_expire()` mechanisms. |
1027 | */ |
1028 | static CURLcode check_and_set_expiry(struct Curl_cfilter *cf, |
1029 | struct Curl_easy *data, |
1030 | struct pkt_io_ctx *pktx) |
1031 | { |
1032 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
1033 | struct pkt_io_ctx local_pktx; |
1034 | ngtcp2_tstamp expiry; |
1035 | |
1036 | if(!pktx) { |
1037 | pktx_init(&local_pktx, cf, data); |
1038 | pktx = &local_pktx; |
1039 | } |
1040 | else { |
1041 | pktx->ts = timestamp(); |
1042 | } |
1043 | |
1044 | expiry = ngtcp2_conn_get_expiry(ctx->qconn); |
1045 | if(expiry != UINT64_MAX) { |
1046 | if(expiry <= pktx->ts) { |
1047 | CURLcode result; |
1048 | int rv = ngtcp2_conn_handle_expiry(ctx->qconn, pktx->ts); |
1049 | if(rv) { |
1050 | failf(data, "ngtcp2_conn_handle_expiry returned error: %s" , |
1051 | ngtcp2_strerror(rv)); |
1052 | ngtcp2_ccerr_set_liberr(&ctx->last_error, rv, NULL, 0); |
1053 | return CURLE_SEND_ERROR; |
1054 | } |
1055 | result = cf_progress_ingress(cf, data, pktx); |
1056 | if(result) |
1057 | return result; |
1058 | result = cf_progress_egress(cf, data, pktx); |
1059 | if(result) |
1060 | return result; |
1061 | /* ask again, things might have changed */ |
1062 | expiry = ngtcp2_conn_get_expiry(ctx->qconn); |
1063 | } |
1064 | |
1065 | if(expiry > pktx->ts) { |
1066 | ngtcp2_duration timeout = expiry - pktx->ts; |
1067 | if(timeout % NGTCP2_MILLISECONDS) { |
1068 | timeout += NGTCP2_MILLISECONDS; |
1069 | } |
1070 | Curl_expire(data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC); |
1071 | } |
1072 | } |
1073 | return CURLE_OK; |
1074 | } |
1075 | |
1076 | static int cf_ngtcp2_get_select_socks(struct Curl_cfilter *cf, |
1077 | struct Curl_easy *data, |
1078 | curl_socket_t *socks) |
1079 | { |
1080 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
1081 | struct SingleRequest *k = &data->req; |
1082 | int rv = GETSOCK_BLANK; |
1083 | struct h3_stream_ctx *stream = H3_STREAM_CTX(data); |
1084 | struct cf_call_data save; |
1085 | |
1086 | CF_DATA_SAVE(save, cf, data); |
1087 | socks[0] = ctx->q.sockfd; |
1088 | |
1089 | /* in HTTP/3 we can always get a frame, so check read */ |
1090 | rv |= GETSOCK_READSOCK(0); |
1091 | |
1092 | /* we're still uploading or the HTTP/2 layer wants to send data */ |
1093 | if((k->keepon & KEEP_SENDBITS) == KEEP_SEND && |
1094 | ngtcp2_conn_get_cwnd_left(ctx->qconn) && |
1095 | ngtcp2_conn_get_max_data_left(ctx->qconn) && |
1096 | stream && nghttp3_conn_is_stream_writable(ctx->h3conn, stream->id)) |
1097 | rv |= GETSOCK_WRITESOCK(0); |
1098 | |
1099 | CF_DATA_RESTORE(cf, save); |
1100 | return rv; |
1101 | } |
1102 | |
1103 | static void h3_drain_stream(struct Curl_cfilter *cf, |
1104 | struct Curl_easy *data) |
1105 | { |
1106 | struct h3_stream_ctx *stream = H3_STREAM_CTX(data); |
1107 | unsigned char bits; |
1108 | |
1109 | (void)cf; |
1110 | bits = CURL_CSELECT_IN; |
1111 | if(stream && stream->upload_left && !stream->send_closed) |
1112 | bits |= CURL_CSELECT_OUT; |
1113 | if(data->state.dselect_bits != bits) { |
1114 | data->state.dselect_bits = bits; |
1115 | Curl_expire(data, 0, EXPIRE_RUN_NOW); |
1116 | } |
1117 | } |
1118 | |
1119 | static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, |
1120 | uint64_t app_error_code, void *user_data, |
1121 | void *stream_user_data) |
1122 | { |
1123 | struct Curl_cfilter *cf = user_data; |
1124 | struct Curl_easy *data = stream_user_data; |
1125 | struct h3_stream_ctx *stream = H3_STREAM_CTX(data); |
1126 | (void)conn; |
1127 | (void)stream_id; |
1128 | |
1129 | /* we might be called by nghttp3 after we already cleaned up */ |
1130 | if(!stream) |
1131 | return 0; |
1132 | |
1133 | stream->closed = TRUE; |
1134 | stream->error3 = app_error_code; |
1135 | if(stream->error3 != NGHTTP3_H3_NO_ERROR) { |
1136 | stream->reset = TRUE; |
1137 | stream->send_closed = TRUE; |
1138 | CURL_TRC_CF(data, cf, "[%" PRId64 "] RESET: error %" PRId64, |
1139 | stream->id, stream->error3); |
1140 | } |
1141 | else { |
1142 | CURL_TRC_CF(data, cf, "[%" PRId64 "] CLOSED" , stream->id); |
1143 | } |
1144 | data->req.keepon &= ~KEEP_SEND_HOLD; |
1145 | h3_drain_stream(cf, data); |
1146 | return 0; |
1147 | } |
1148 | |
1149 | /* |
1150 | * write_resp_raw() copies response data in raw format to the `data`'s |
1151 | * receive buffer. If not enough space is available, it appends to the |
1152 | * `data`'s overflow buffer. |
1153 | */ |
1154 | static CURLcode write_resp_raw(struct Curl_cfilter *cf, |
1155 | struct Curl_easy *data, |
1156 | const void *mem, size_t memlen, |
1157 | bool flow) |
1158 | { |
1159 | struct h3_stream_ctx *stream = H3_STREAM_CTX(data); |
1160 | CURLcode result = CURLE_OK; |
1161 | ssize_t nwritten; |
1162 | |
1163 | (void)cf; |
1164 | if(!stream) { |
1165 | return CURLE_RECV_ERROR; |
1166 | } |
1167 | nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result); |
1168 | if(nwritten < 0) { |
1169 | return result; |
1170 | } |
1171 | |
1172 | if(!flow) |
1173 | stream->recv_buf_nonflow += (size_t)nwritten; |
1174 | |
1175 | if((size_t)nwritten < memlen) { |
1176 | /* This MUST not happen. Our recbuf is dimensioned to hold the |
1177 | * full max_stream_window and then some for this very reason. */ |
1178 | DEBUGASSERT(0); |
1179 | return CURLE_RECV_ERROR; |
1180 | } |
1181 | return result; |
1182 | } |
1183 | |
1184 | static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id, |
1185 | const uint8_t *buf, size_t buflen, |
1186 | void *user_data, void *stream_user_data) |
1187 | { |
1188 | struct Curl_cfilter *cf = user_data; |
1189 | struct Curl_easy *data = stream_user_data; |
1190 | struct h3_stream_ctx *stream = H3_STREAM_CTX(data); |
1191 | CURLcode result; |
1192 | |
1193 | (void)conn; |
1194 | (void)stream3_id; |
1195 | |
1196 | if(!stream) |
1197 | return NGHTTP3_ERR_CALLBACK_FAILURE; |
1198 | |
1199 | result = write_resp_raw(cf, data, buf, buflen, TRUE); |
1200 | if(result) { |
1201 | CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu, ERROR receiving %d" , |
1202 | stream->id, buflen, result); |
1203 | return NGHTTP3_ERR_CALLBACK_FAILURE; |
1204 | } |
1205 | CURL_TRC_CF(data, cf, "[%" PRId64 "] DATA len=%zu" , stream->id, buflen); |
1206 | h3_drain_stream(cf, data); |
1207 | return 0; |
1208 | } |
1209 | |
1210 | static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream3_id, |
1211 | size_t consumed, void *user_data, |
1212 | void *stream_user_data) |
1213 | { |
1214 | struct Curl_cfilter *cf = user_data; |
1215 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
1216 | (void)conn; |
1217 | (void)stream_user_data; |
1218 | |
1219 | /* nghttp3 has consumed bytes on the QUIC stream and we need to |
1220 | * tell the QUIC connection to increase its flow control */ |
1221 | ngtcp2_conn_extend_max_stream_offset(ctx->qconn, stream3_id, consumed); |
1222 | ngtcp2_conn_extend_max_offset(ctx->qconn, consumed); |
1223 | return 0; |
1224 | } |
1225 | |
1226 | static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, |
1227 | int fin, void *user_data, void *stream_user_data) |
1228 | { |
1229 | struct Curl_cfilter *cf = user_data; |
1230 | struct Curl_easy *data = stream_user_data; |
1231 | struct h3_stream_ctx *stream = H3_STREAM_CTX(data); |
1232 | CURLcode result = CURLE_OK; |
1233 | (void)conn; |
1234 | (void)stream_id; |
1235 | (void)fin; |
1236 | (void)cf; |
1237 | |
1238 | if(!stream) |
1239 | return 0; |
1240 | /* add a CRLF only if we've received some headers */ |
1241 | result = write_resp_raw(cf, data, "\r\n" , 2, FALSE); |
1242 | if(result) { |
1243 | return -1; |
1244 | } |
1245 | |
1246 | CURL_TRC_CF(data, cf, "[%" PRId64 "] end_headers, status=%d" , |
1247 | stream_id, stream->status_code); |
1248 | if(stream->status_code / 100 != 1) { |
1249 | stream->resp_hds_complete = TRUE; |
1250 | } |
1251 | h3_drain_stream(cf, data); |
1252 | return 0; |
1253 | } |
1254 | |
1255 | static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, |
1256 | int32_t token, nghttp3_rcbuf *name, |
1257 | nghttp3_rcbuf *value, uint8_t flags, |
1258 | void *user_data, void *stream_user_data) |
1259 | { |
1260 | struct Curl_cfilter *cf = user_data; |
1261 | nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); |
1262 | nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); |
1263 | struct Curl_easy *data = stream_user_data; |
1264 | struct h3_stream_ctx *stream = H3_STREAM_CTX(data); |
1265 | CURLcode result = CURLE_OK; |
1266 | (void)conn; |
1267 | (void)stream_id; |
1268 | (void)token; |
1269 | (void)flags; |
1270 | (void)cf; |
1271 | |
1272 | /* we might have cleaned up this transfer already */ |
1273 | if(!stream) |
1274 | return 0; |
1275 | |
1276 | if(token == NGHTTP3_QPACK_TOKEN__STATUS) { |
1277 | char line[14]; /* status line is always 13 characters long */ |
1278 | size_t ncopy; |
1279 | |
1280 | result = Curl_http_decode_status(&stream->status_code, |
1281 | (const char *)h3val.base, h3val.len); |
1282 | if(result) |
1283 | return -1; |
1284 | ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n" , |
1285 | stream->status_code); |
1286 | CURL_TRC_CF(data, cf, "[%" PRId64 "] status: %s" , stream_id, line); |
1287 | result = write_resp_raw(cf, data, line, ncopy, FALSE); |
1288 | if(result) { |
1289 | return -1; |
1290 | } |
1291 | } |
1292 | else { |
1293 | /* store as an HTTP1-style header */ |
1294 | CURL_TRC_CF(data, cf, "[%" PRId64 "] header: %.*s: %.*s" , |
1295 | stream_id, (int)h3name.len, h3name.base, |
1296 | (int)h3val.len, h3val.base); |
1297 | result = write_resp_raw(cf, data, h3name.base, h3name.len, FALSE); |
1298 | if(result) { |
1299 | return -1; |
1300 | } |
1301 | result = write_resp_raw(cf, data, ": " , 2, FALSE); |
1302 | if(result) { |
1303 | return -1; |
1304 | } |
1305 | result = write_resp_raw(cf, data, h3val.base, h3val.len, FALSE); |
1306 | if(result) { |
1307 | return -1; |
1308 | } |
1309 | result = write_resp_raw(cf, data, "\r\n" , 2, FALSE); |
1310 | if(result) { |
1311 | return -1; |
1312 | } |
1313 | } |
1314 | return 0; |
1315 | } |
1316 | |
1317 | static int cb_h3_stop_sending(nghttp3_conn *conn, int64_t stream_id, |
1318 | uint64_t app_error_code, void *user_data, |
1319 | void *stream_user_data) |
1320 | { |
1321 | struct Curl_cfilter *cf = user_data; |
1322 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
1323 | int rv; |
1324 | (void)conn; |
1325 | (void)stream_user_data; |
1326 | |
1327 | rv = ngtcp2_conn_shutdown_stream_read(ctx->qconn, 0, stream_id, |
1328 | app_error_code); |
1329 | if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { |
1330 | return NGTCP2_ERR_CALLBACK_FAILURE; |
1331 | } |
1332 | |
1333 | return 0; |
1334 | } |
1335 | |
1336 | static int cb_h3_reset_stream(nghttp3_conn *conn, int64_t stream_id, |
1337 | uint64_t app_error_code, void *user_data, |
1338 | void *stream_user_data) { |
1339 | struct Curl_cfilter *cf = user_data; |
1340 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
1341 | struct Curl_easy *data = stream_user_data; |
1342 | int rv; |
1343 | (void)conn; |
1344 | (void)data; |
1345 | |
1346 | rv = ngtcp2_conn_shutdown_stream_write(ctx->qconn, 0, stream_id, |
1347 | app_error_code); |
1348 | CURL_TRC_CF(data, cf, "[%" PRId64 "] reset -> %d" , stream_id, rv); |
1349 | if(rv && rv != NGTCP2_ERR_STREAM_NOT_FOUND) { |
1350 | return NGTCP2_ERR_CALLBACK_FAILURE; |
1351 | } |
1352 | |
1353 | return 0; |
1354 | } |
1355 | |
1356 | static nghttp3_callbacks ngh3_callbacks = { |
1357 | cb_h3_acked_req_body, /* acked_stream_data */ |
1358 | cb_h3_stream_close, |
1359 | cb_h3_recv_data, |
1360 | cb_h3_deferred_consume, |
1361 | NULL, /* begin_headers */ |
1362 | cb_h3_recv_header, |
1363 | cb_h3_end_headers, |
1364 | NULL, /* begin_trailers */ |
1365 | cb_h3_recv_header, |
1366 | NULL, /* end_trailers */ |
1367 | cb_h3_stop_sending, |
1368 | NULL, /* end_stream */ |
1369 | cb_h3_reset_stream, |
1370 | NULL, /* shutdown */ |
1371 | NULL /* recv_settings */ |
1372 | }; |
1373 | |
1374 | static int init_ngh3_conn(struct Curl_cfilter *cf) |
1375 | { |
1376 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
1377 | CURLcode result; |
1378 | int rc; |
1379 | int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; |
1380 | |
1381 | if(ngtcp2_conn_get_streams_uni_left(ctx->qconn) < 3) { |
1382 | return CURLE_QUIC_CONNECT_ERROR; |
1383 | } |
1384 | |
1385 | nghttp3_settings_default(&ctx->h3settings); |
1386 | |
1387 | rc = nghttp3_conn_client_new(&ctx->h3conn, |
1388 | &ngh3_callbacks, |
1389 | &ctx->h3settings, |
1390 | nghttp3_mem_default(), |
1391 | cf); |
1392 | if(rc) { |
1393 | result = CURLE_OUT_OF_MEMORY; |
1394 | goto fail; |
1395 | } |
1396 | |
1397 | rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL); |
1398 | if(rc) { |
1399 | result = CURLE_QUIC_CONNECT_ERROR; |
1400 | goto fail; |
1401 | } |
1402 | |
1403 | rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id); |
1404 | if(rc) { |
1405 | result = CURLE_QUIC_CONNECT_ERROR; |
1406 | goto fail; |
1407 | } |
1408 | |
1409 | rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL); |
1410 | if(rc) { |
1411 | result = CURLE_QUIC_CONNECT_ERROR; |
1412 | goto fail; |
1413 | } |
1414 | |
1415 | rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL); |
1416 | if(rc) { |
1417 | result = CURLE_QUIC_CONNECT_ERROR; |
1418 | goto fail; |
1419 | } |
1420 | |
1421 | rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id, |
1422 | qpack_dec_stream_id); |
1423 | if(rc) { |
1424 | result = CURLE_QUIC_CONNECT_ERROR; |
1425 | goto fail; |
1426 | } |
1427 | |
1428 | return CURLE_OK; |
1429 | fail: |
1430 | |
1431 | return result; |
1432 | } |
1433 | |
1434 | static ssize_t recv_closed_stream(struct Curl_cfilter *cf, |
1435 | struct Curl_easy *data, |
1436 | struct h3_stream_ctx *stream, |
1437 | CURLcode *err) |
1438 | { |
1439 | ssize_t nread = -1; |
1440 | |
1441 | (void)cf; |
1442 | if(stream->reset) { |
1443 | failf(data, |
1444 | "HTTP/3 stream %" PRId64 " reset by server" , stream->id); |
1445 | *err = stream->resp_hds_complete? CURLE_PARTIAL_FILE : CURLE_HTTP3; |
1446 | goto out; |
1447 | } |
1448 | else if(!stream->resp_hds_complete) { |
1449 | failf(data, |
1450 | "HTTP/3 stream %" PRId64 " was closed cleanly, but before getting" |
1451 | " all response header fields, treated as error" , |
1452 | stream->id); |
1453 | *err = CURLE_HTTP3; |
1454 | goto out; |
1455 | } |
1456 | *err = CURLE_OK; |
1457 | nread = 0; |
1458 | |
1459 | out: |
1460 | return nread; |
1461 | } |
1462 | |
1463 | /* incoming data frames on the h3 stream */ |
1464 | static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data, |
1465 | char *buf, size_t len, CURLcode *err) |
1466 | { |
1467 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
1468 | struct h3_stream_ctx *stream = H3_STREAM_CTX(data); |
1469 | ssize_t nread = -1; |
1470 | struct cf_call_data save; |
1471 | struct pkt_io_ctx pktx; |
1472 | |
1473 | (void)ctx; |
1474 | |
1475 | CF_DATA_SAVE(save, cf, data); |
1476 | DEBUGASSERT(cf->connected); |
1477 | DEBUGASSERT(ctx); |
1478 | DEBUGASSERT(ctx->qconn); |
1479 | DEBUGASSERT(ctx->h3conn); |
1480 | *err = CURLE_OK; |
1481 | |
1482 | pktx_init(&pktx, cf, data); |
1483 | |
1484 | if(!stream) { |
1485 | *err = CURLE_RECV_ERROR; |
1486 | goto out; |
1487 | } |
1488 | |
1489 | if(!Curl_bufq_is_empty(&stream->recvbuf)) { |
1490 | nread = Curl_bufq_read(&stream->recvbuf, |
1491 | (unsigned char *)buf, len, err); |
1492 | if(nread < 0) { |
1493 | CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " |
1494 | "-> %zd, %d" , stream->id, len, nread, *err); |
1495 | goto out; |
1496 | } |
1497 | report_consumed_data(cf, data, nread); |
1498 | } |
1499 | |
1500 | if(cf_progress_ingress(cf, data, &pktx)) { |
1501 | *err = CURLE_RECV_ERROR; |
1502 | nread = -1; |
1503 | goto out; |
1504 | } |
1505 | |
1506 | /* recvbuf had nothing before, maybe after progressing ingress? */ |
1507 | if(nread < 0 && !Curl_bufq_is_empty(&stream->recvbuf)) { |
1508 | nread = Curl_bufq_read(&stream->recvbuf, |
1509 | (unsigned char *)buf, len, err); |
1510 | if(nread < 0) { |
1511 | CURL_TRC_CF(data, cf, "[%" PRId64 "] read recvbuf(len=%zu) " |
1512 | "-> %zd, %d" , stream->id, len, nread, *err); |
1513 | goto out; |
1514 | } |
1515 | report_consumed_data(cf, data, nread); |
1516 | } |
1517 | |
1518 | if(nread > 0) { |
1519 | h3_drain_stream(cf, data); |
1520 | } |
1521 | else { |
1522 | if(stream->closed) { |
1523 | nread = recv_closed_stream(cf, data, stream, err); |
1524 | goto out; |
1525 | } |
1526 | *err = CURLE_AGAIN; |
1527 | nread = -1; |
1528 | } |
1529 | |
1530 | out: |
1531 | if(cf_progress_egress(cf, data, &pktx)) { |
1532 | *err = CURLE_SEND_ERROR; |
1533 | nread = -1; |
1534 | } |
1535 | else { |
1536 | CURLcode result2 = check_and_set_expiry(cf, data, &pktx); |
1537 | if(result2) { |
1538 | *err = result2; |
1539 | nread = -1; |
1540 | } |
1541 | } |
1542 | CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_recv(len=%zu) -> %zd, %d" , |
1543 | stream? stream->id : -1, len, nread, *err); |
1544 | CF_DATA_RESTORE(cf, save); |
1545 | return nread; |
1546 | } |
1547 | |
1548 | static int cb_h3_acked_req_body(nghttp3_conn *conn, int64_t stream_id, |
1549 | uint64_t datalen, void *user_data, |
1550 | void *stream_user_data) |
1551 | { |
1552 | struct Curl_cfilter *cf = user_data; |
1553 | struct Curl_easy *data = stream_user_data; |
1554 | struct h3_stream_ctx *stream = H3_STREAM_CTX(data); |
1555 | size_t skiplen; |
1556 | |
1557 | (void)cf; |
1558 | if(!stream) |
1559 | return 0; |
1560 | /* The server acknowledged `datalen` of bytes from our request body. |
1561 | * This is a delta. We have kept this data in `sendbuf` for |
1562 | * re-transmissions and can free it now. */ |
1563 | if(datalen >= (uint64_t)stream->sendbuf_len_in_flight) |
1564 | skiplen = stream->sendbuf_len_in_flight; |
1565 | else |
1566 | skiplen = (size_t)datalen; |
1567 | Curl_bufq_skip(&stream->sendbuf, skiplen); |
1568 | stream->sendbuf_len_in_flight -= skiplen; |
1569 | |
1570 | /* Everything ACKed, we resume upload processing */ |
1571 | if(!stream->sendbuf_len_in_flight) { |
1572 | int rv = nghttp3_conn_resume_stream(conn, stream_id); |
1573 | if(rv) { |
1574 | return NGTCP2_ERR_CALLBACK_FAILURE; |
1575 | } |
1576 | if((data->req.keepon & KEEP_SEND_HOLD) && |
1577 | (data->req.keepon & KEEP_SEND)) { |
1578 | data->req.keepon &= ~KEEP_SEND_HOLD; |
1579 | h3_drain_stream(cf, data); |
1580 | CURL_TRC_CF(data, cf, "[%" PRId64 "] unpausing acks" , stream_id); |
1581 | } |
1582 | } |
1583 | return 0; |
1584 | } |
1585 | |
1586 | static nghttp3_ssize |
1587 | cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id, |
1588 | nghttp3_vec *vec, size_t veccnt, |
1589 | uint32_t *pflags, void *user_data, |
1590 | void *stream_user_data) |
1591 | { |
1592 | struct Curl_cfilter *cf = user_data; |
1593 | struct Curl_easy *data = stream_user_data; |
1594 | struct h3_stream_ctx *stream = H3_STREAM_CTX(data); |
1595 | ssize_t nwritten = 0; |
1596 | size_t nvecs = 0; |
1597 | (void)cf; |
1598 | (void)conn; |
1599 | (void)stream_id; |
1600 | (void)user_data; |
1601 | (void)veccnt; |
1602 | |
1603 | if(!stream) |
1604 | return NGHTTP3_ERR_CALLBACK_FAILURE; |
1605 | /* nghttp3 keeps references to the sendbuf data until it is ACKed |
1606 | * by the server (see `cb_h3_acked_req_body()` for updates). |
1607 | * `sendbuf_len_in_flight` is the amount of bytes in `sendbuf` |
1608 | * that we have already passed to nghttp3, but which have not been |
1609 | * ACKed yet. |
1610 | * Any amount beyond `sendbuf_len_in_flight` we need still to pass |
1611 | * to nghttp3. Do that now, if we can. */ |
1612 | if(stream->sendbuf_len_in_flight < Curl_bufq_len(&stream->sendbuf)) { |
1613 | nvecs = 0; |
1614 | while(nvecs < veccnt && |
1615 | Curl_bufq_peek_at(&stream->sendbuf, |
1616 | stream->sendbuf_len_in_flight, |
1617 | (const unsigned char **)&vec[nvecs].base, |
1618 | &vec[nvecs].len)) { |
1619 | stream->sendbuf_len_in_flight += vec[nvecs].len; |
1620 | nwritten += vec[nvecs].len; |
1621 | ++nvecs; |
1622 | } |
1623 | DEBUGASSERT(nvecs > 0); /* we SHOULD have been be able to peek */ |
1624 | } |
1625 | |
1626 | if(nwritten > 0 && stream->upload_left != -1) |
1627 | stream->upload_left -= nwritten; |
1628 | |
1629 | /* When we stopped sending and everything in `sendbuf` is "in flight", |
1630 | * we are at the end of the request body. */ |
1631 | if(stream->upload_left == 0) { |
1632 | *pflags = NGHTTP3_DATA_FLAG_EOF; |
1633 | stream->send_closed = TRUE; |
1634 | } |
1635 | else if(!nwritten) { |
1636 | /* Not EOF, and nothing to give, we signal WOULDBLOCK. */ |
1637 | CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> AGAIN" , |
1638 | stream->id); |
1639 | return NGHTTP3_ERR_WOULDBLOCK; |
1640 | } |
1641 | |
1642 | CURL_TRC_CF(data, cf, "[%" PRId64 "] read req body -> " |
1643 | "%d vecs%s with %zu (buffered=%zu, left=%" |
1644 | CURL_FORMAT_CURL_OFF_T ")" , |
1645 | stream->id, (int)nvecs, |
1646 | *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF" :"" , |
1647 | nwritten, Curl_bufq_len(&stream->sendbuf), |
1648 | stream->upload_left); |
1649 | return (nghttp3_ssize)nvecs; |
1650 | } |
1651 | |
1652 | /* Index where :authority header field will appear in request header |
1653 | field list. */ |
1654 | #define AUTHORITY_DST_IDX 3 |
1655 | |
1656 | static ssize_t h3_stream_open(struct Curl_cfilter *cf, |
1657 | struct Curl_easy *data, |
1658 | const void *buf, size_t len, |
1659 | CURLcode *err) |
1660 | { |
1661 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
1662 | struct h3_stream_ctx *stream = NULL; |
1663 | struct dynhds h2_headers; |
1664 | size_t nheader; |
1665 | nghttp3_nv *nva = NULL; |
1666 | int rc = 0; |
1667 | unsigned int i; |
1668 | ssize_t nwritten = -1; |
1669 | nghttp3_data_reader reader; |
1670 | nghttp3_data_reader *preader = NULL; |
1671 | |
1672 | Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); |
1673 | |
1674 | *err = h3_data_setup(cf, data); |
1675 | if(*err) |
1676 | goto out; |
1677 | stream = H3_STREAM_CTX(data); |
1678 | DEBUGASSERT(stream); |
1679 | |
1680 | nwritten = Curl_h1_req_parse_read(&stream->h1, buf, len, NULL, 0, err); |
1681 | if(nwritten < 0) |
1682 | goto out; |
1683 | if(!stream->h1.done) { |
1684 | /* need more data */ |
1685 | goto out; |
1686 | } |
1687 | DEBUGASSERT(stream->h1.req); |
1688 | |
1689 | *err = Curl_http_req_to_h2(&h2_headers, stream->h1.req, data); |
1690 | if(*err) { |
1691 | nwritten = -1; |
1692 | goto out; |
1693 | } |
1694 | /* no longer needed */ |
1695 | Curl_h1_req_parse_free(&stream->h1); |
1696 | |
1697 | nheader = Curl_dynhds_count(&h2_headers); |
1698 | nva = malloc(sizeof(nghttp3_nv) * nheader); |
1699 | if(!nva) { |
1700 | *err = CURLE_OUT_OF_MEMORY; |
1701 | nwritten = -1; |
1702 | goto out; |
1703 | } |
1704 | |
1705 | for(i = 0; i < nheader; ++i) { |
1706 | struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); |
1707 | nva[i].name = (unsigned char *)e->name; |
1708 | nva[i].namelen = e->namelen; |
1709 | nva[i].value = (unsigned char *)e->value; |
1710 | nva[i].valuelen = e->valuelen; |
1711 | nva[i].flags = NGHTTP3_NV_FLAG_NONE; |
1712 | } |
1713 | |
1714 | rc = ngtcp2_conn_open_bidi_stream(ctx->qconn, &stream->id, NULL); |
1715 | if(rc) { |
1716 | failf(data, "can get bidi streams" ); |
1717 | *err = CURLE_SEND_ERROR; |
1718 | goto out; |
1719 | } |
1720 | |
1721 | switch(data->state.httpreq) { |
1722 | case HTTPREQ_POST: |
1723 | case HTTPREQ_POST_FORM: |
1724 | case HTTPREQ_POST_MIME: |
1725 | case HTTPREQ_PUT: |
1726 | /* known request body size or -1 */ |
1727 | if(data->state.infilesize != -1) |
1728 | stream->upload_left = data->state.infilesize; |
1729 | else |
1730 | /* data sending without specifying the data amount up front */ |
1731 | stream->upload_left = -1; /* unknown */ |
1732 | break; |
1733 | default: |
1734 | /* there is not request body */ |
1735 | stream->upload_left = 0; /* no request body */ |
1736 | break; |
1737 | } |
1738 | |
1739 | stream->send_closed = (stream->upload_left == 0); |
1740 | if(!stream->send_closed) { |
1741 | reader.read_data = cb_h3_read_req_body; |
1742 | preader = &reader; |
1743 | } |
1744 | |
1745 | rc = nghttp3_conn_submit_request(ctx->h3conn, stream->id, |
1746 | nva, nheader, preader, data); |
1747 | if(rc) { |
1748 | switch(rc) { |
1749 | case NGHTTP3_ERR_CONN_CLOSING: |
1750 | CURL_TRC_CF(data, cf, "h3sid[%" PRId64"] failed to send, " |
1751 | "connection is closing" , stream->id); |
1752 | break; |
1753 | default: |
1754 | CURL_TRC_CF(data, cf, "h3sid[%" PRId64"] failed to send -> %d (%s)" , |
1755 | stream->id, rc, ngtcp2_strerror(rc)); |
1756 | break; |
1757 | } |
1758 | *err = CURLE_SEND_ERROR; |
1759 | nwritten = -1; |
1760 | goto out; |
1761 | } |
1762 | |
1763 | if(Curl_trc_is_verbose(data)) { |
1764 | infof(data, "[HTTP/3] [%" PRId64 "] OPENED stream for %s" , |
1765 | stream->id, data->state.url); |
1766 | for(i = 0; i < nheader; ++i) { |
1767 | infof(data, "[HTTP/3] [%" PRId64 "] [%.*s: %.*s]" , stream->id, |
1768 | (int)nva[i].namelen, nva[i].name, |
1769 | (int)nva[i].valuelen, nva[i].value); |
1770 | } |
1771 | } |
1772 | |
1773 | out: |
1774 | free(nva); |
1775 | Curl_dynhds_free(&h2_headers); |
1776 | return nwritten; |
1777 | } |
1778 | |
1779 | static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data, |
1780 | const void *buf, size_t len, CURLcode *err) |
1781 | { |
1782 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
1783 | struct h3_stream_ctx *stream = H3_STREAM_CTX(data); |
1784 | ssize_t sent = 0; |
1785 | struct cf_call_data save; |
1786 | struct pkt_io_ctx pktx; |
1787 | CURLcode result; |
1788 | |
1789 | CF_DATA_SAVE(save, cf, data); |
1790 | DEBUGASSERT(cf->connected); |
1791 | DEBUGASSERT(ctx->qconn); |
1792 | DEBUGASSERT(ctx->h3conn); |
1793 | pktx_init(&pktx, cf, data); |
1794 | *err = CURLE_OK; |
1795 | |
1796 | result = cf_progress_ingress(cf, data, &pktx); |
1797 | if(result) { |
1798 | *err = result; |
1799 | sent = -1; |
1800 | } |
1801 | |
1802 | if(!stream || stream->id < 0) { |
1803 | sent = h3_stream_open(cf, data, buf, len, err); |
1804 | if(sent < 0) { |
1805 | CURL_TRC_CF(data, cf, "failed to open stream -> %d" , *err); |
1806 | goto out; |
1807 | } |
1808 | stream = H3_STREAM_CTX(data); |
1809 | } |
1810 | else if(stream->upload_blocked_len) { |
1811 | /* the data in `buf` has already been submitted or added to the |
1812 | * buffers, but have been EAGAINed on the last invocation. */ |
1813 | DEBUGASSERT(len >= stream->upload_blocked_len); |
1814 | if(len < stream->upload_blocked_len) { |
1815 | /* Did we get called again with a smaller `len`? This should not |
1816 | * happen. We are not prepared to handle that. */ |
1817 | failf(data, "HTTP/3 send again with decreased length" ); |
1818 | *err = CURLE_HTTP3; |
1819 | sent = -1; |
1820 | goto out; |
1821 | } |
1822 | sent = (ssize_t)stream->upload_blocked_len; |
1823 | stream->upload_blocked_len = 0; |
1824 | } |
1825 | else if(stream->closed) { |
1826 | if(stream->resp_hds_complete) { |
1827 | /* Server decided to close the stream after having sent us a final |
1828 | * response. This is valid if it is not interested in the request |
1829 | * body. This happens on 30x or 40x responses. |
1830 | * We silently discard the data sent, since this is not a transport |
1831 | * error situation. */ |
1832 | CURL_TRC_CF(data, cf, "[%" PRId64 "] discarding data" |
1833 | "on closed stream with response" , stream->id); |
1834 | *err = CURLE_OK; |
1835 | sent = (ssize_t)len; |
1836 | goto out; |
1837 | } |
1838 | *err = CURLE_HTTP3; |
1839 | sent = -1; |
1840 | goto out; |
1841 | } |
1842 | else { |
1843 | sent = Curl_bufq_write(&stream->sendbuf, buf, len, err); |
1844 | CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send, add to " |
1845 | "sendbuf(len=%zu) -> %zd, %d" , |
1846 | stream->id, len, sent, *err); |
1847 | if(sent < 0) { |
1848 | goto out; |
1849 | } |
1850 | |
1851 | (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); |
1852 | } |
1853 | |
1854 | result = cf_progress_egress(cf, data, &pktx); |
1855 | if(result) { |
1856 | *err = result; |
1857 | sent = -1; |
1858 | } |
1859 | |
1860 | if(stream && sent > 0 && stream->sendbuf_len_in_flight) { |
1861 | /* We have unacknowledged DATA and cannot report success to our |
1862 | * caller. Instead we EAGAIN and remember how much we have already |
1863 | * "written" into our various internal connection buffers. |
1864 | * We put the stream upload on HOLD, until this gets ACKed. */ |
1865 | stream->upload_blocked_len = sent; |
1866 | CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu), " |
1867 | "%zu bytes in flight -> EGAIN" , stream->id, len, |
1868 | stream->sendbuf_len_in_flight); |
1869 | *err = CURLE_AGAIN; |
1870 | sent = -1; |
1871 | data->req.keepon |= KEEP_SEND_HOLD; |
1872 | } |
1873 | |
1874 | out: |
1875 | result = check_and_set_expiry(cf, data, &pktx); |
1876 | if(result) { |
1877 | *err = result; |
1878 | sent = -1; |
1879 | } |
1880 | CURL_TRC_CF(data, cf, "[%" PRId64 "] cf_send(len=%zu) -> %zd, %d" , |
1881 | stream? stream->id : -1, len, sent, *err); |
1882 | CF_DATA_RESTORE(cf, save); |
1883 | return sent; |
1884 | } |
1885 | |
1886 | static CURLcode qng_verify_peer(struct Curl_cfilter *cf, |
1887 | struct Curl_easy *data) |
1888 | { |
1889 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
1890 | CURLcode result = CURLE_OK; |
1891 | const char *hostname, *disp_hostname; |
1892 | int port; |
1893 | char *snihost; |
1894 | |
1895 | Curl_conn_get_host(data, cf->sockindex, &hostname, &disp_hostname, &port); |
1896 | snihost = Curl_ssl_snihost(data, hostname, NULL); |
1897 | if(!snihost) |
1898 | return CURLE_PEER_FAILED_VERIFICATION; |
1899 | |
1900 | cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ |
1901 | cf->conn->httpversion = 30; |
1902 | cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX; |
1903 | |
1904 | if(cf->conn->ssl_config.verifyhost) { |
1905 | #ifdef USE_OPENSSL |
1906 | X509 *server_cert; |
1907 | server_cert = SSL_get_peer_certificate(ctx->ssl); |
1908 | if(!server_cert) { |
1909 | return CURLE_PEER_FAILED_VERIFICATION; |
1910 | } |
1911 | result = Curl_ossl_verifyhost(data, cf->conn, server_cert); |
1912 | X509_free(server_cert); |
1913 | if(result) |
1914 | return result; |
1915 | #elif defined(USE_GNUTLS) |
1916 | result = Curl_gtls_verifyserver(data, ctx->gtls->session, |
1917 | &cf->conn->ssl_config, &data->set.ssl, |
1918 | hostname, disp_hostname, |
1919 | data->set.str[STRING_SSL_PINNEDPUBLICKEY]); |
1920 | if(result) |
1921 | return result; |
1922 | #elif defined(USE_WOLFSSL) |
1923 | if(wolfSSL_check_domain_name(ctx->ssl, snihost) == SSL_FAILURE) |
1924 | return CURLE_PEER_FAILED_VERIFICATION; |
1925 | #endif |
1926 | infof(data, "Verified certificate just fine" ); |
1927 | } |
1928 | else |
1929 | infof(data, "Skipped certificate verification" ); |
1930 | #ifdef USE_OPENSSL |
1931 | if(data->set.ssl.certinfo) |
1932 | /* asked to gather certificate info */ |
1933 | (void)Curl_ossl_certchain(data, ctx->ssl); |
1934 | #endif |
1935 | return result; |
1936 | } |
1937 | |
1938 | static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen, |
1939 | struct sockaddr_storage *remote_addr, |
1940 | socklen_t remote_addrlen, int ecn, |
1941 | void *userp) |
1942 | { |
1943 | struct pkt_io_ctx *pktx = userp; |
1944 | struct cf_ngtcp2_ctx *ctx = pktx->cf->ctx; |
1945 | ngtcp2_pkt_info pi; |
1946 | ngtcp2_path path; |
1947 | int rv; |
1948 | |
1949 | ++pktx->pkt_count; |
1950 | ngtcp2_addr_init(&path.local, (struct sockaddr *)&ctx->q.local_addr, |
1951 | ctx->q.local_addrlen); |
1952 | ngtcp2_addr_init(&path.remote, (struct sockaddr *)remote_addr, |
1953 | remote_addrlen); |
1954 | pi.ecn = (uint8_t)ecn; |
1955 | |
1956 | rv = ngtcp2_conn_read_pkt(ctx->qconn, &path, &pi, pkt, pktlen, pktx->ts); |
1957 | if(rv) { |
1958 | CURL_TRC_CF(pktx->data, pktx->cf, "ingress, read_pkt -> %s" , |
1959 | ngtcp2_strerror(rv)); |
1960 | if(!ctx->last_error.error_code) { |
1961 | if(rv == NGTCP2_ERR_CRYPTO) { |
1962 | ngtcp2_ccerr_set_tls_alert(&ctx->last_error, |
1963 | ngtcp2_conn_get_tls_alert(ctx->qconn), |
1964 | NULL, 0); |
1965 | } |
1966 | else { |
1967 | ngtcp2_ccerr_set_liberr(&ctx->last_error, rv, NULL, 0); |
1968 | } |
1969 | } |
1970 | |
1971 | if(rv == NGTCP2_ERR_CRYPTO) |
1972 | /* this is a "TLS problem", but a failed certificate verification |
1973 | is a common reason for this */ |
1974 | return CURLE_PEER_FAILED_VERIFICATION; |
1975 | return CURLE_RECV_ERROR; |
1976 | } |
1977 | |
1978 | return CURLE_OK; |
1979 | } |
1980 | |
1981 | static CURLcode cf_progress_ingress(struct Curl_cfilter *cf, |
1982 | struct Curl_easy *data, |
1983 | struct pkt_io_ctx *pktx) |
1984 | { |
1985 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
1986 | struct pkt_io_ctx local_pktx; |
1987 | size_t pkts_chunk = 128, i; |
1988 | size_t pkts_max = 10 * pkts_chunk; |
1989 | CURLcode result = CURLE_OK; |
1990 | |
1991 | if(!pktx) { |
1992 | pktx_init(&local_pktx, cf, data); |
1993 | pktx = &local_pktx; |
1994 | } |
1995 | else { |
1996 | pktx->ts = timestamp(); |
1997 | } |
1998 | |
1999 | #ifdef USE_OPENSSL |
2000 | if(!ctx->x509_store_setup) { |
2001 | result = Curl_ssl_setup_x509_store(cf, data, ctx->sslctx); |
2002 | if(result) |
2003 | return result; |
2004 | ctx->x509_store_setup = TRUE; |
2005 | } |
2006 | #endif |
2007 | |
2008 | for(i = 0; i < pkts_max; i += pkts_chunk) { |
2009 | pktx->pkt_count = 0; |
2010 | result = vquic_recv_packets(cf, data, &ctx->q, pkts_chunk, |
2011 | recv_pkt, pktx); |
2012 | if(result) /* error */ |
2013 | break; |
2014 | if(pktx->pkt_count < pkts_chunk) /* got less than we could */ |
2015 | break; |
2016 | /* give egress a chance before we receive more */ |
2017 | result = cf_progress_egress(cf, data, pktx); |
2018 | if(result) /* error */ |
2019 | break; |
2020 | } |
2021 | return result; |
2022 | } |
2023 | |
2024 | /** |
2025 | * Read a network packet to send from ngtcp2 into `buf`. |
2026 | * Return number of bytes written or -1 with *err set. |
2027 | */ |
2028 | static ssize_t read_pkt_to_send(void *userp, |
2029 | unsigned char *buf, size_t buflen, |
2030 | CURLcode *err) |
2031 | { |
2032 | struct pkt_io_ctx *x = userp; |
2033 | struct cf_ngtcp2_ctx *ctx = x->cf->ctx; |
2034 | nghttp3_vec vec[16]; |
2035 | nghttp3_ssize veccnt; |
2036 | ngtcp2_ssize ndatalen; |
2037 | uint32_t flags; |
2038 | int64_t stream_id; |
2039 | int fin; |
2040 | ssize_t nwritten, n; |
2041 | veccnt = 0; |
2042 | stream_id = -1; |
2043 | fin = 0; |
2044 | |
2045 | /* ngtcp2 may want to put several frames from different streams into |
2046 | * this packet. `NGTCP2_WRITE_STREAM_FLAG_MORE` tells it to do so. |
2047 | * When `NGTCP2_ERR_WRITE_MORE` is returned, we *need* to make |
2048 | * another iteration. |
2049 | * When ngtcp2 is happy (because it has no other frame that would fit |
2050 | * or it has nothing more to send), it returns the total length |
2051 | * of the assembled packet. This may be 0 if there was nothing to send. */ |
2052 | nwritten = 0; |
2053 | *err = CURLE_OK; |
2054 | for(;;) { |
2055 | |
2056 | if(ctx->h3conn && ngtcp2_conn_get_max_data_left(ctx->qconn)) { |
2057 | veccnt = nghttp3_conn_writev_stream(ctx->h3conn, &stream_id, &fin, vec, |
2058 | sizeof(vec) / sizeof(vec[0])); |
2059 | if(veccnt < 0) { |
2060 | failf(x->data, "nghttp3_conn_writev_stream returned error: %s" , |
2061 | nghttp3_strerror((int)veccnt)); |
2062 | ngtcp2_ccerr_set_application_error( |
2063 | &ctx->last_error, |
2064 | nghttp3_err_infer_quic_app_error_code((int)veccnt), NULL, 0); |
2065 | *err = CURLE_SEND_ERROR; |
2066 | return -1; |
2067 | } |
2068 | } |
2069 | |
2070 | flags = NGTCP2_WRITE_STREAM_FLAG_MORE | |
2071 | (fin ? NGTCP2_WRITE_STREAM_FLAG_FIN : 0); |
2072 | n = ngtcp2_conn_writev_stream(ctx->qconn, &x->ps.path, |
2073 | NULL, buf, buflen, |
2074 | &ndatalen, flags, stream_id, |
2075 | (const ngtcp2_vec *)vec, veccnt, x->ts); |
2076 | if(n == 0) { |
2077 | /* nothing to send */ |
2078 | *err = CURLE_AGAIN; |
2079 | nwritten = -1; |
2080 | goto out; |
2081 | } |
2082 | else if(n < 0) { |
2083 | switch(n) { |
2084 | case NGTCP2_ERR_STREAM_DATA_BLOCKED: |
2085 | DEBUGASSERT(ndatalen == -1); |
2086 | nghttp3_conn_block_stream(ctx->h3conn, stream_id); |
2087 | n = 0; |
2088 | break; |
2089 | case NGTCP2_ERR_STREAM_SHUT_WR: |
2090 | DEBUGASSERT(ndatalen == -1); |
2091 | nghttp3_conn_shutdown_stream_write(ctx->h3conn, stream_id); |
2092 | n = 0; |
2093 | break; |
2094 | case NGTCP2_ERR_WRITE_MORE: |
2095 | /* ngtcp2 wants to send more. update the flow of the stream whose data |
2096 | * is in the buffer and continue */ |
2097 | DEBUGASSERT(ndatalen >= 0); |
2098 | n = 0; |
2099 | break; |
2100 | default: |
2101 | DEBUGASSERT(ndatalen == -1); |
2102 | failf(x->data, "ngtcp2_conn_writev_stream returned error: %s" , |
2103 | ngtcp2_strerror((int)n)); |
2104 | ngtcp2_ccerr_set_liberr(&ctx->last_error, (int)n, NULL, 0); |
2105 | *err = CURLE_SEND_ERROR; |
2106 | nwritten = -1; |
2107 | goto out; |
2108 | } |
2109 | } |
2110 | |
2111 | if(ndatalen >= 0) { |
2112 | /* we add the amount of data bytes to the flow windows */ |
2113 | int rv = nghttp3_conn_add_write_offset(ctx->h3conn, stream_id, ndatalen); |
2114 | if(rv) { |
2115 | failf(x->data, "nghttp3_conn_add_write_offset returned error: %s\n" , |
2116 | nghttp3_strerror(rv)); |
2117 | return CURLE_SEND_ERROR; |
2118 | } |
2119 | } |
2120 | |
2121 | if(n > 0) { |
2122 | /* packet assembled, leave */ |
2123 | nwritten = n; |
2124 | goto out; |
2125 | } |
2126 | } |
2127 | out: |
2128 | return nwritten; |
2129 | } |
2130 | |
2131 | static CURLcode cf_progress_egress(struct Curl_cfilter *cf, |
2132 | struct Curl_easy *data, |
2133 | struct pkt_io_ctx *pktx) |
2134 | { |
2135 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
2136 | ssize_t nread; |
2137 | size_t max_payload_size, path_max_payload_size, max_pktcnt; |
2138 | size_t pktcnt = 0; |
2139 | size_t gsolen = 0; /* this disables gso until we have a clue */ |
2140 | CURLcode curlcode; |
2141 | struct pkt_io_ctx local_pktx; |
2142 | |
2143 | if(!pktx) { |
2144 | pktx_init(&local_pktx, cf, data); |
2145 | pktx = &local_pktx; |
2146 | } |
2147 | else { |
2148 | pktx->ts = timestamp(); |
2149 | ngtcp2_path_storage_zero(&pktx->ps); |
2150 | } |
2151 | |
2152 | curlcode = vquic_flush(cf, data, &ctx->q); |
2153 | if(curlcode) { |
2154 | if(curlcode == CURLE_AGAIN) { |
2155 | Curl_expire(data, 1, EXPIRE_QUIC); |
2156 | return CURLE_OK; |
2157 | } |
2158 | return curlcode; |
2159 | } |
2160 | |
2161 | /* In UDP, there is a maximum theoretical packet paload length and |
2162 | * a minimum payload length that is "guarantueed" to work. |
2163 | * To detect if this minimum payload can be increased, ngtcp2 sends |
2164 | * now and then a packet payload larger than the minimum. It that |
2165 | * is ACKed by the peer, both parties know that it works and |
2166 | * the subsequent packets can use a larger one. |
2167 | * This is called PMTUD (Path Maximum Transmission Unit Discovery). |
2168 | * Since a PMTUD might be rejected right on send, we do not want it |
2169 | * be followed by other packets of lesser size. Because those would |
2170 | * also fail then. So, if we detect a PMTUD while buffering, we flush. |
2171 | */ |
2172 | max_payload_size = ngtcp2_conn_get_max_tx_udp_payload_size(ctx->qconn); |
2173 | path_max_payload_size = |
2174 | ngtcp2_conn_get_path_max_tx_udp_payload_size(ctx->qconn); |
2175 | /* maximum number of packets buffered before we flush to the socket */ |
2176 | max_pktcnt = CURLMIN(MAX_PKT_BURST, |
2177 | ctx->q.sendbuf.chunk_size / max_payload_size); |
2178 | |
2179 | for(;;) { |
2180 | /* add the next packet to send, if any, to our buffer */ |
2181 | nread = Curl_bufq_sipn(&ctx->q.sendbuf, max_payload_size, |
2182 | read_pkt_to_send, pktx, &curlcode); |
2183 | if(nread < 0) { |
2184 | if(curlcode != CURLE_AGAIN) |
2185 | return curlcode; |
2186 | /* Nothing more to add, flush and leave */ |
2187 | curlcode = vquic_send(cf, data, &ctx->q, gsolen); |
2188 | if(curlcode) { |
2189 | if(curlcode == CURLE_AGAIN) { |
2190 | Curl_expire(data, 1, EXPIRE_QUIC); |
2191 | return CURLE_OK; |
2192 | } |
2193 | return curlcode; |
2194 | } |
2195 | goto out; |
2196 | } |
2197 | |
2198 | DEBUGASSERT(nread > 0); |
2199 | if(pktcnt == 0) { |
2200 | /* first packet in buffer. This is either of a known, "good" |
2201 | * payload size or it is a PMTUD. We'll see. */ |
2202 | gsolen = (size_t)nread; |
2203 | } |
2204 | else if((size_t)nread > gsolen || |
2205 | (gsolen > path_max_payload_size && (size_t)nread != gsolen)) { |
2206 | /* The just added packet is a PMTUD *or* the one(s) before the |
2207 | * just added were PMTUD and the last one is smaller. |
2208 | * Flush the buffer before the last add. */ |
2209 | curlcode = vquic_send_tail_split(cf, data, &ctx->q, |
2210 | gsolen, nread, nread); |
2211 | if(curlcode) { |
2212 | if(curlcode == CURLE_AGAIN) { |
2213 | Curl_expire(data, 1, EXPIRE_QUIC); |
2214 | return CURLE_OK; |
2215 | } |
2216 | return curlcode; |
2217 | } |
2218 | pktcnt = 0; |
2219 | continue; |
2220 | } |
2221 | |
2222 | if(++pktcnt >= max_pktcnt || (size_t)nread < gsolen) { |
2223 | /* Reached MAX_PKT_BURST *or* |
2224 | * the capacity of our buffer *or* |
2225 | * last add was shorter than the previous ones, flush */ |
2226 | curlcode = vquic_send(cf, data, &ctx->q, gsolen); |
2227 | if(curlcode) { |
2228 | if(curlcode == CURLE_AGAIN) { |
2229 | Curl_expire(data, 1, EXPIRE_QUIC); |
2230 | return CURLE_OK; |
2231 | } |
2232 | return curlcode; |
2233 | } |
2234 | /* pktbuf has been completely sent */ |
2235 | pktcnt = 0; |
2236 | } |
2237 | } |
2238 | |
2239 | out: |
2240 | return CURLE_OK; |
2241 | } |
2242 | |
2243 | /* |
2244 | * Called from transfer.c:data_pending to know if we should keep looping |
2245 | * to receive more data from the connection. |
2246 | */ |
2247 | static bool cf_ngtcp2_data_pending(struct Curl_cfilter *cf, |
2248 | const struct Curl_easy *data) |
2249 | { |
2250 | const struct h3_stream_ctx *stream = H3_STREAM_CTX(data); |
2251 | (void)cf; |
2252 | return stream && !Curl_bufq_is_empty(&stream->recvbuf); |
2253 | } |
2254 | |
2255 | static CURLcode h3_data_pause(struct Curl_cfilter *cf, |
2256 | struct Curl_easy *data, |
2257 | bool pause) |
2258 | { |
2259 | /* TODO: there seems right now no API in ngtcp2 to shrink/enlarge |
2260 | * the streams windows. As we do in HTTP/2. */ |
2261 | if(!pause) { |
2262 | h3_drain_stream(cf, data); |
2263 | Curl_expire(data, 0, EXPIRE_RUN_NOW); |
2264 | } |
2265 | return CURLE_OK; |
2266 | } |
2267 | |
2268 | static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf, |
2269 | struct Curl_easy *data, |
2270 | int event, int arg1, void *arg2) |
2271 | { |
2272 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
2273 | CURLcode result = CURLE_OK; |
2274 | struct cf_call_data save; |
2275 | |
2276 | CF_DATA_SAVE(save, cf, data); |
2277 | (void)arg1; |
2278 | (void)arg2; |
2279 | switch(event) { |
2280 | case CF_CTRL_DATA_SETUP: |
2281 | break; |
2282 | case CF_CTRL_DATA_PAUSE: |
2283 | result = h3_data_pause(cf, data, (arg1 != 0)); |
2284 | break; |
2285 | case CF_CTRL_DATA_DONE: { |
2286 | h3_data_done(cf, data); |
2287 | break; |
2288 | } |
2289 | case CF_CTRL_DATA_DONE_SEND: { |
2290 | struct h3_stream_ctx *stream = H3_STREAM_CTX(data); |
2291 | if(stream && !stream->send_closed) { |
2292 | stream->send_closed = TRUE; |
2293 | stream->upload_left = Curl_bufq_len(&stream->sendbuf); |
2294 | (void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id); |
2295 | } |
2296 | break; |
2297 | } |
2298 | case CF_CTRL_DATA_IDLE: { |
2299 | struct h3_stream_ctx *stream = H3_STREAM_CTX(data); |
2300 | CURL_TRC_CF(data, cf, "data idle" ); |
2301 | if(stream && !stream->closed) { |
2302 | result = check_and_set_expiry(cf, data, NULL); |
2303 | if(result) |
2304 | CURL_TRC_CF(data, cf, "data idle, check_and_set_expiry -> %d" , result); |
2305 | } |
2306 | break; |
2307 | } |
2308 | default: |
2309 | break; |
2310 | } |
2311 | CF_DATA_RESTORE(cf, save); |
2312 | return result; |
2313 | } |
2314 | |
2315 | static void cf_ngtcp2_ctx_clear(struct cf_ngtcp2_ctx *ctx) |
2316 | { |
2317 | struct cf_call_data save = ctx->call_data; |
2318 | |
2319 | if(ctx->qlogfd != -1) { |
2320 | close(ctx->qlogfd); |
2321 | } |
2322 | #ifdef USE_OPENSSL |
2323 | if(ctx->ssl) |
2324 | SSL_free(ctx->ssl); |
2325 | if(ctx->sslctx) |
2326 | SSL_CTX_free(ctx->sslctx); |
2327 | #elif defined(USE_GNUTLS) |
2328 | if(ctx->gtls) { |
2329 | if(ctx->gtls->cred) |
2330 | gnutls_certificate_free_credentials(ctx->gtls->cred); |
2331 | if(ctx->gtls->session) |
2332 | gnutls_deinit(ctx->gtls->session); |
2333 | free(ctx->gtls); |
2334 | } |
2335 | #elif defined(USE_WOLFSSL) |
2336 | if(ctx->ssl) |
2337 | wolfSSL_free(ctx->ssl); |
2338 | if(ctx->sslctx) |
2339 | wolfSSL_CTX_free(ctx->sslctx); |
2340 | #endif |
2341 | vquic_ctx_free(&ctx->q); |
2342 | if(ctx->h3conn) |
2343 | nghttp3_conn_del(ctx->h3conn); |
2344 | if(ctx->qconn) |
2345 | ngtcp2_conn_del(ctx->qconn); |
2346 | Curl_bufcp_free(&ctx->stream_bufcp); |
2347 | |
2348 | memset(ctx, 0, sizeof(*ctx)); |
2349 | ctx->qlogfd = -1; |
2350 | ctx->call_data = save; |
2351 | } |
2352 | |
2353 | static void cf_ngtcp2_close(struct Curl_cfilter *cf, struct Curl_easy *data) |
2354 | { |
2355 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
2356 | struct cf_call_data save; |
2357 | |
2358 | CF_DATA_SAVE(save, cf, data); |
2359 | if(ctx && ctx->qconn) { |
2360 | char buffer[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; |
2361 | ngtcp2_tstamp ts; |
2362 | ngtcp2_ssize rc; |
2363 | |
2364 | CURL_TRC_CF(data, cf, "close" ); |
2365 | ts = timestamp(); |
2366 | rc = ngtcp2_conn_write_connection_close(ctx->qconn, NULL, /* path */ |
2367 | NULL, /* pkt_info */ |
2368 | (uint8_t *)buffer, sizeof(buffer), |
2369 | &ctx->last_error, ts); |
2370 | if(rc > 0) { |
2371 | while((send(ctx->q.sockfd, buffer, (SEND_TYPE_ARG3)rc, 0) == -1) && |
2372 | SOCKERRNO == EINTR); |
2373 | } |
2374 | |
2375 | cf_ngtcp2_ctx_clear(ctx); |
2376 | } |
2377 | |
2378 | cf->connected = FALSE; |
2379 | CF_DATA_RESTORE(cf, save); |
2380 | } |
2381 | |
2382 | static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) |
2383 | { |
2384 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
2385 | struct cf_call_data save; |
2386 | |
2387 | CF_DATA_SAVE(save, cf, data); |
2388 | CURL_TRC_CF(data, cf, "destroy" ); |
2389 | if(ctx) { |
2390 | cf_ngtcp2_ctx_clear(ctx); |
2391 | free(ctx); |
2392 | } |
2393 | cf->ctx = NULL; |
2394 | /* No CF_DATA_RESTORE(cf, save) possible */ |
2395 | (void)save; |
2396 | } |
2397 | |
2398 | /* |
2399 | * Might be called twice for happy eyeballs. |
2400 | */ |
2401 | static CURLcode cf_connect_start(struct Curl_cfilter *cf, |
2402 | struct Curl_easy *data, |
2403 | struct pkt_io_ctx *pktx) |
2404 | { |
2405 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
2406 | int rc; |
2407 | int rv; |
2408 | CURLcode result; |
2409 | const struct Curl_sockaddr_ex *sockaddr = NULL; |
2410 | int qfd; |
2411 | |
2412 | ctx->version = NGTCP2_PROTO_VER_MAX; |
2413 | ctx->max_stream_window = H3_STREAM_WINDOW_SIZE; |
2414 | Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE, |
2415 | H3_STREAM_POOL_SPARES); |
2416 | |
2417 | #ifdef USE_OPENSSL |
2418 | result = quic_ssl_ctx(&ctx->sslctx, cf, data); |
2419 | if(result) |
2420 | return result; |
2421 | |
2422 | result = quic_set_client_cert(cf, data); |
2423 | if(result) |
2424 | return result; |
2425 | #elif defined(USE_WOLFSSL) |
2426 | result = quic_ssl_ctx(&ctx->sslctx, cf, data); |
2427 | if(result) |
2428 | return result; |
2429 | #endif |
2430 | |
2431 | result = quic_init_ssl(cf, data); |
2432 | if(result) |
2433 | return result; |
2434 | |
2435 | ctx->dcid.datalen = NGTCP2_MAX_CIDLEN; |
2436 | result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN); |
2437 | if(result) |
2438 | return result; |
2439 | |
2440 | ctx->scid.datalen = NGTCP2_MAX_CIDLEN; |
2441 | result = Curl_rand(data, ctx->scid.data, NGTCP2_MAX_CIDLEN); |
2442 | if(result) |
2443 | return result; |
2444 | |
2445 | (void)Curl_qlogdir(data, ctx->scid.data, NGTCP2_MAX_CIDLEN, &qfd); |
2446 | ctx->qlogfd = qfd; /* -1 if failure above */ |
2447 | quic_settings(ctx, data, pktx); |
2448 | |
2449 | result = vquic_ctx_init(&ctx->q); |
2450 | if(result) |
2451 | return result; |
2452 | |
2453 | Curl_cf_socket_peek(cf->next, data, &ctx->q.sockfd, |
2454 | &sockaddr, NULL, NULL, NULL, NULL); |
2455 | if(!sockaddr) |
2456 | return CURLE_QUIC_CONNECT_ERROR; |
2457 | ctx->q.local_addrlen = sizeof(ctx->q.local_addr); |
2458 | rv = getsockname(ctx->q.sockfd, (struct sockaddr *)&ctx->q.local_addr, |
2459 | &ctx->q.local_addrlen); |
2460 | if(rv == -1) |
2461 | return CURLE_QUIC_CONNECT_ERROR; |
2462 | |
2463 | ngtcp2_addr_init(&ctx->connected_path.local, |
2464 | (struct sockaddr *)&ctx->q.local_addr, |
2465 | ctx->q.local_addrlen); |
2466 | ngtcp2_addr_init(&ctx->connected_path.remote, |
2467 | &sockaddr->sa_addr, sockaddr->addrlen); |
2468 | |
2469 | rc = ngtcp2_conn_client_new(&ctx->qconn, &ctx->dcid, &ctx->scid, |
2470 | &ctx->connected_path, |
2471 | NGTCP2_PROTO_VER_V1, &ng_callbacks, |
2472 | &ctx->settings, &ctx->transport_params, |
2473 | NULL, cf); |
2474 | if(rc) |
2475 | return CURLE_QUIC_CONNECT_ERROR; |
2476 | |
2477 | #ifdef USE_GNUTLS |
2478 | ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->gtls->session); |
2479 | #else |
2480 | ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ssl); |
2481 | #endif |
2482 | |
2483 | ngtcp2_ccerr_default(&ctx->last_error); |
2484 | |
2485 | ctx->conn_ref.get_conn = get_conn; |
2486 | ctx->conn_ref.user_data = cf; |
2487 | |
2488 | return CURLE_OK; |
2489 | } |
2490 | |
2491 | static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf, |
2492 | struct Curl_easy *data, |
2493 | bool blocking, bool *done) |
2494 | { |
2495 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
2496 | CURLcode result = CURLE_OK; |
2497 | struct cf_call_data save; |
2498 | struct curltime now; |
2499 | struct pkt_io_ctx pktx; |
2500 | |
2501 | if(cf->connected) { |
2502 | *done = TRUE; |
2503 | return CURLE_OK; |
2504 | } |
2505 | |
2506 | /* Connect the UDP filter first */ |
2507 | if(!cf->next->connected) { |
2508 | result = Curl_conn_cf_connect(cf->next, data, blocking, done); |
2509 | if(result || !*done) |
2510 | return result; |
2511 | } |
2512 | |
2513 | *done = FALSE; |
2514 | now = Curl_now(); |
2515 | pktx_init(&pktx, cf, data); |
2516 | |
2517 | CF_DATA_SAVE(save, cf, data); |
2518 | |
2519 | if(ctx->reconnect_at.tv_sec && Curl_timediff(now, ctx->reconnect_at) < 0) { |
2520 | /* Not time yet to attempt the next connect */ |
2521 | CURL_TRC_CF(data, cf, "waiting for reconnect time" ); |
2522 | goto out; |
2523 | } |
2524 | |
2525 | if(!ctx->qconn) { |
2526 | ctx->started_at = now; |
2527 | result = cf_connect_start(cf, data, &pktx); |
2528 | if(result) |
2529 | goto out; |
2530 | result = cf_progress_egress(cf, data, &pktx); |
2531 | /* we do not expect to be able to recv anything yet */ |
2532 | goto out; |
2533 | } |
2534 | |
2535 | result = cf_progress_ingress(cf, data, &pktx); |
2536 | if(result) |
2537 | goto out; |
2538 | |
2539 | result = cf_progress_egress(cf, data, &pktx); |
2540 | if(result) |
2541 | goto out; |
2542 | |
2543 | if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) { |
2544 | ctx->handshake_at = now; |
2545 | CURL_TRC_CF(data, cf, "handshake complete after %dms" , |
2546 | (int)Curl_timediff(now, ctx->started_at)); |
2547 | result = qng_verify_peer(cf, data); |
2548 | if(!result) { |
2549 | CURL_TRC_CF(data, cf, "peer verified" ); |
2550 | cf->connected = TRUE; |
2551 | cf->conn->alpn = CURL_HTTP_VERSION_3; |
2552 | *done = TRUE; |
2553 | connkeep(cf->conn, "HTTP/3 default" ); |
2554 | } |
2555 | } |
2556 | |
2557 | out: |
2558 | if(result == CURLE_RECV_ERROR && ctx->qconn && |
2559 | ngtcp2_conn_in_draining_period(ctx->qconn)) { |
2560 | /* When a QUIC server instance is shutting down, it may send us a |
2561 | * CONNECTION_CLOSE right away. Our connection then enters the DRAINING |
2562 | * state. |
2563 | * This may be a stopping of the service or it may be that the server |
2564 | * is reloading and a new instance will start serving soon. |
2565 | * In any case, we tear down our socket and start over with a new one. |
2566 | * We re-open the underlying UDP cf right now, but do not start |
2567 | * connecting until called again. |
2568 | */ |
2569 | int reconn_delay_ms = 200; |
2570 | |
2571 | CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms" , |
2572 | reconn_delay_ms); |
2573 | Curl_conn_cf_close(cf->next, data); |
2574 | cf_ngtcp2_ctx_clear(ctx); |
2575 | result = Curl_conn_cf_connect(cf->next, data, FALSE, done); |
2576 | if(!result && *done) { |
2577 | *done = FALSE; |
2578 | ctx->reconnect_at = now; |
2579 | ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000; |
2580 | Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC); |
2581 | result = CURLE_OK; |
2582 | } |
2583 | } |
2584 | |
2585 | #ifndef CURL_DISABLE_VERBOSE_STRINGS |
2586 | if(result) { |
2587 | const char *r_ip = NULL; |
2588 | int r_port = 0; |
2589 | |
2590 | Curl_cf_socket_peek(cf->next, data, NULL, NULL, |
2591 | &r_ip, &r_port, NULL, NULL); |
2592 | infof(data, "QUIC connect to %s port %u failed: %s" , |
2593 | r_ip, r_port, curl_easy_strerror(result)); |
2594 | } |
2595 | #endif |
2596 | if(!result && ctx->qconn) { |
2597 | result = check_and_set_expiry(cf, data, &pktx); |
2598 | } |
2599 | if(result || *done) |
2600 | CURL_TRC_CF(data, cf, "connect -> %d, done=%d" , result, *done); |
2601 | CF_DATA_RESTORE(cf, save); |
2602 | return result; |
2603 | } |
2604 | |
2605 | static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf, |
2606 | struct Curl_easy *data, |
2607 | int query, int *pres1, void *pres2) |
2608 | { |
2609 | struct cf_ngtcp2_ctx *ctx = cf->ctx; |
2610 | struct cf_call_data save; |
2611 | |
2612 | switch(query) { |
2613 | case CF_QUERY_MAX_CONCURRENT: { |
2614 | const ngtcp2_transport_params *rp; |
2615 | DEBUGASSERT(pres1); |
2616 | |
2617 | CF_DATA_SAVE(save, cf, data); |
2618 | rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn); |
2619 | if(rp) |
2620 | *pres1 = (rp->initial_max_streams_bidi > INT_MAX)? |
2621 | INT_MAX : (int)rp->initial_max_streams_bidi; |
2622 | else /* not arrived yet? */ |
2623 | *pres1 = Curl_multi_max_concurrent_streams(data->multi); |
2624 | CURL_TRC_CF(data, cf, "query max_conncurrent -> %d" , *pres1); |
2625 | CF_DATA_RESTORE(cf, save); |
2626 | return CURLE_OK; |
2627 | } |
2628 | case CF_QUERY_CONNECT_REPLY_MS: |
2629 | if(ctx->got_first_byte) { |
2630 | timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at); |
2631 | *pres1 = (ms < INT_MAX)? (int)ms : INT_MAX; |
2632 | } |
2633 | else |
2634 | *pres1 = -1; |
2635 | return CURLE_OK; |
2636 | case CF_QUERY_TIMER_CONNECT: { |
2637 | struct curltime *when = pres2; |
2638 | if(ctx->got_first_byte) |
2639 | *when = ctx->first_byte_at; |
2640 | return CURLE_OK; |
2641 | } |
2642 | case CF_QUERY_TIMER_APPCONNECT: { |
2643 | struct curltime *when = pres2; |
2644 | if(cf->connected) |
2645 | *when = ctx->handshake_at; |
2646 | return CURLE_OK; |
2647 | } |
2648 | default: |
2649 | break; |
2650 | } |
2651 | return cf->next? |
2652 | cf->next->cft->query(cf->next, data, query, pres1, pres2) : |
2653 | CURLE_UNKNOWN_OPTION; |
2654 | } |
2655 | |
2656 | static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf, |
2657 | struct Curl_easy *data, |
2658 | bool *input_pending) |
2659 | { |
2660 | bool alive = TRUE; |
2661 | |
2662 | *input_pending = FALSE; |
2663 | if(!cf->next || !cf->next->cft->is_alive(cf->next, data, input_pending)) |
2664 | return FALSE; |
2665 | |
2666 | if(*input_pending) { |
2667 | /* This happens before we've sent off a request and the connection is |
2668 | not in use by any other transfer, there shouldn't be any data here, |
2669 | only "protocol frames" */ |
2670 | *input_pending = FALSE; |
2671 | if(cf_progress_ingress(cf, data, NULL)) |
2672 | alive = FALSE; |
2673 | else { |
2674 | alive = TRUE; |
2675 | } |
2676 | } |
2677 | |
2678 | return alive; |
2679 | } |
2680 | |
2681 | struct Curl_cftype Curl_cft_http3 = { |
2682 | "HTTP/3" , |
2683 | CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX, |
2684 | 0, |
2685 | cf_ngtcp2_destroy, |
2686 | cf_ngtcp2_connect, |
2687 | cf_ngtcp2_close, |
2688 | Curl_cf_def_get_host, |
2689 | cf_ngtcp2_get_select_socks, |
2690 | cf_ngtcp2_data_pending, |
2691 | cf_ngtcp2_send, |
2692 | cf_ngtcp2_recv, |
2693 | cf_ngtcp2_data_event, |
2694 | cf_ngtcp2_conn_is_alive, |
2695 | Curl_cf_def_conn_keep_alive, |
2696 | cf_ngtcp2_query, |
2697 | }; |
2698 | |
2699 | CURLcode Curl_cf_ngtcp2_create(struct Curl_cfilter **pcf, |
2700 | struct Curl_easy *data, |
2701 | struct connectdata *conn, |
2702 | const struct Curl_addrinfo *ai) |
2703 | { |
2704 | struct cf_ngtcp2_ctx *ctx = NULL; |
2705 | struct Curl_cfilter *cf = NULL, *udp_cf = NULL; |
2706 | CURLcode result; |
2707 | |
2708 | (void)data; |
2709 | ctx = calloc(sizeof(*ctx), 1); |
2710 | if(!ctx) { |
2711 | result = CURLE_OUT_OF_MEMORY; |
2712 | goto out; |
2713 | } |
2714 | ctx->qlogfd = -1; |
2715 | cf_ngtcp2_ctx_clear(ctx); |
2716 | |
2717 | result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); |
2718 | if(result) |
2719 | goto out; |
2720 | |
2721 | result = Curl_cf_udp_create(&udp_cf, data, conn, ai, TRNSPRT_QUIC); |
2722 | if(result) |
2723 | goto out; |
2724 | |
2725 | cf->conn = conn; |
2726 | udp_cf->conn = cf->conn; |
2727 | udp_cf->sockindex = cf->sockindex; |
2728 | cf->next = udp_cf; |
2729 | |
2730 | out: |
2731 | *pcf = (!result)? cf : NULL; |
2732 | if(result) { |
2733 | if(udp_cf) |
2734 | Curl_conn_cf_discard_sub(cf, udp_cf, data, TRUE); |
2735 | Curl_safefree(cf); |
2736 | Curl_safefree(ctx); |
2737 | } |
2738 | return result; |
2739 | } |
2740 | |
2741 | bool Curl_conn_is_ngtcp2(const struct Curl_easy *data, |
2742 | const struct connectdata *conn, |
2743 | int sockindex) |
2744 | { |
2745 | struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL; |
2746 | |
2747 | (void)data; |
2748 | for(; cf; cf = cf->next) { |
2749 | if(cf->cft == &Curl_cft_http3) |
2750 | return TRUE; |
2751 | if(cf->cft->flags & CF_TYPE_IP_CONNECT) |
2752 | return FALSE; |
2753 | } |
2754 | return FALSE; |
2755 | } |
2756 | |
2757 | #endif |
2758 | |