VirtualBox

source: vbox/trunk/src/libs/curl-8.0.1/lib/vquic/curl_msh3.c@ 99797

Last change on this file since 99797 was 99459, checked in by vboxsync, 2 years ago

setting missed svn:sync-process property on curl-8.0.1 files

File size: 25.7 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <[email protected]>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25#include "curl_setup.h"
26
27#ifdef USE_MSH3
28
29#include "urldata.h"
30#include "timeval.h"
31#include "multiif.h"
32#include "sendf.h"
33#include "curl_log.h"
34#include "cfilters.h"
35#include "cf-socket.h"
36#include "connect.h"
37#include "progress.h"
38#include "h2h3.h"
39#include "curl_msh3.h"
40#include "socketpair.h"
41#include "vquic/vquic.h"
42
43/* The last 3 #include files should be in this order */
44#include "curl_printf.h"
45#include "curl_memory.h"
46#include "memdebug.h"
47
48#define DEBUG_CF 1
49
50#if DEBUG_CF && defined(DEBUGBUILD)
51#define CF_DEBUGF(x) x
52#else
53#define CF_DEBUGF(x) do { } while(0)
54#endif
55
56#define MSH3_REQ_INIT_BUF_LEN 16384
57#define MSH3_REQ_MAX_BUF_LEN 0x100000
58
59#ifdef _WIN32
60#define msh3_lock CRITICAL_SECTION
61#define msh3_lock_initialize(lock) InitializeCriticalSection(lock)
62#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock)
63#define msh3_lock_acquire(lock) EnterCriticalSection(lock)
64#define msh3_lock_release(lock) LeaveCriticalSection(lock)
65#else /* !_WIN32 */
66#include <pthread.h>
67#define msh3_lock pthread_mutex_t
68#define msh3_lock_initialize(lock) do { \
69 pthread_mutexattr_t attr; \
70 pthread_mutexattr_init(&attr); \
71 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
72 pthread_mutex_init(lock, &attr); \
73 pthread_mutexattr_destroy(&attr); \
74}while(0)
75#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock)
76#define msh3_lock_acquire(lock) pthread_mutex_lock(lock)
77#define msh3_lock_release(lock) pthread_mutex_unlock(lock)
78#endif /* _WIN32 */
79
80
81static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection,
82 void *IfContext);
83static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection,
84 void *IfContext);
85static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection,
86 void *IfContext,
87 MSH3_REQUEST *Request);
88static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
89 void *IfContext,
90 const MSH3_HEADER *Header);
91static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
92 void *IfContext, uint32_t *Length,
93 const uint8_t *Data);
94static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
95 bool Aborted, uint64_t AbortError);
96static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
97 void *IfContext);
98static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
99 void *IfContext, void *SendContext);
100
101
102void Curl_msh3_ver(char *p, size_t len)
103{
104 uint32_t v[4];
105 MsH3Version(v);
106 (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]);
107}
108
109#define SP_LOCAL 0
110#define SP_REMOTE 1
111
112struct cf_msh3_ctx {
113 MSH3_API *api;
114 MSH3_CONNECTION *qconn;
115 struct Curl_sockaddr_ex addr;
116 curl_socket_t sock[2]; /* fake socket pair until we get support in msh3 */
117 char l_ip[MAX_IPADR_LEN]; /* local IP as string */
118 int l_port; /* local port number */
119 struct curltime connect_started; /* time the current attempt started */
120 struct curltime handshake_at; /* time connect handshake finished */
121 /* Flags written by msh3/msquic thread */
122 bool handshake_complete;
123 bool handshake_succeeded;
124 bool connected;
125 /* Flags written by curl thread */
126 BIT(verbose);
127 BIT(active);
128};
129
130static const MSH3_CONNECTION_IF msh3_conn_if = {
131 msh3_conn_connected,
132 msh3_conn_shutdown_complete,
133 msh3_conn_new_request
134};
135
136static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection,
137 void *IfContext)
138{
139 struct cf_msh3_ctx *ctx = IfContext;
140 (void)Connection;
141 if(ctx->verbose)
142 CF_DEBUGF(fprintf(stderr, "* [MSH3] evt: connected\n"));
143 ctx->handshake_succeeded = true;
144 ctx->connected = true;
145 ctx->handshake_complete = true;
146}
147
148static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection,
149 void *IfContext)
150{
151 struct cf_msh3_ctx *ctx = IfContext;
152 (void)Connection;
153 if(ctx->verbose)
154 CF_DEBUGF(fprintf(stderr, "* [MSH3] evt: shutdown complete\n"));
155 ctx->connected = false;
156 ctx->handshake_complete = true;
157}
158
159static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection,
160 void *IfContext,
161 MSH3_REQUEST *Request)
162{
163 (void)Connection;
164 (void)IfContext;
165 (void)Request;
166}
167
168static const MSH3_REQUEST_IF msh3_request_if = {
169 msh3_header_received,
170 msh3_data_received,
171 msh3_complete,
172 msh3_shutdown_complete,
173 msh3_data_sent
174};
175
176static CURLcode msh3_data_setup(struct Curl_cfilter *cf,
177 struct Curl_easy *data)
178{
179 struct HTTP *stream = data->req.p.http;
180 (void)cf;
181
182 DEBUGASSERT(stream);
183 if(!stream->recv_buf) {
184 DEBUGF(LOG_CF(data, cf, "req: setup"));
185 stream->recv_buf = malloc(MSH3_REQ_INIT_BUF_LEN);
186 if(!stream->recv_buf) {
187 return CURLE_OUT_OF_MEMORY;
188 }
189 stream->req = ZERO_NULL;
190 msh3_lock_initialize(&stream->recv_lock);
191 stream->recv_buf_alloc = MSH3_REQ_INIT_BUF_LEN;
192 stream->recv_buf_max = MSH3_REQ_MAX_BUF_LEN;
193 stream->recv_header_len = 0;
194 stream->recv_header_complete = false;
195 stream->recv_data_len = 0;
196 stream->recv_data_complete = false;
197 stream->recv_error = CURLE_OK;
198 }
199 return CURLE_OK;
200}
201
202/* Requires stream->recv_lock to be held */
203static bool msh3request_ensure_room(struct HTTP *stream, size_t len)
204{
205 uint8_t *new_recv_buf;
206 const size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
207
208 if(cur_recv_len + len > stream->recv_buf_alloc) {
209 size_t new_recv_buf_alloc_len = stream->recv_buf_alloc;
210 do {
211 new_recv_buf_alloc_len <<= 1; /* TODO - handle overflow */
212 } while(cur_recv_len + len > new_recv_buf_alloc_len);
213 CF_DEBUGF(fprintf(stderr, "* enlarging buffer to %zu\n",
214 new_recv_buf_alloc_len));
215 new_recv_buf = malloc(new_recv_buf_alloc_len);
216 if(!new_recv_buf) {
217 CF_DEBUGF(fprintf(stderr, "* FAILED: enlarging buffer to %zu\n",
218 new_recv_buf_alloc_len));
219 return false;
220 }
221 if(cur_recv_len) {
222 memcpy(new_recv_buf, stream->recv_buf, cur_recv_len);
223 }
224 stream->recv_buf_alloc = new_recv_buf_alloc_len;
225 free(stream->recv_buf);
226 stream->recv_buf = new_recv_buf;
227 }
228 return true;
229}
230
231static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
232 void *IfContext,
233 const MSH3_HEADER *Header)
234{
235 struct Curl_easy *data = IfContext;
236 struct HTTP *stream = data->req.p.http;
237 size_t total_len;
238 (void)Request;
239
240 if(stream->recv_header_complete) {
241 CF_DEBUGF(fprintf(stderr, "* ignoring header after data\n"));
242 return;
243 }
244
245 msh3_lock_acquire(&stream->recv_lock);
246
247 if((Header->NameLength == 7) &&
248 !strncmp(H2H3_PSEUDO_STATUS, (char *)Header->Name, 7)) {
249 total_len = 10 + Header->ValueLength;
250 if(!msh3request_ensure_room(stream, total_len)) {
251 CF_DEBUGF(fprintf(stderr, "* ERROR: unable to buffer: %.*s\n",
252 (int)Header->NameLength, Header->Name));
253 stream->recv_error = CURLE_OUT_OF_MEMORY;
254 goto release_lock;
255 }
256 msnprintf((char *)stream->recv_buf + stream->recv_header_len,
257 stream->recv_buf_alloc - stream->recv_header_len,
258 "HTTP/3 %.*s \r\n", (int)Header->ValueLength, Header->Value);
259 }
260 else {
261 total_len = 4 + Header->NameLength + Header->ValueLength;
262 if(!msh3request_ensure_room(stream, total_len)) {
263 CF_DEBUGF(fprintf(stderr, "* ERROR: unable to buffer: %.*s\n",
264 (int)Header->NameLength, Header->Name));
265 stream->recv_error = CURLE_OUT_OF_MEMORY;
266 goto release_lock;
267 }
268 msnprintf((char *)stream->recv_buf + stream->recv_header_len,
269 stream->recv_buf_alloc - stream->recv_header_len,
270 "%.*s: %.*s\r\n",
271 (int)Header->NameLength, Header->Name,
272 (int)Header->ValueLength, Header->Value);
273 }
274
275 stream->recv_header_len += total_len;
276 data->state.drain = 1;
277
278release_lock:
279 msh3_lock_release(&stream->recv_lock);
280}
281
282static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
283 void *IfContext, uint32_t *Length,
284 const uint8_t *Data)
285{
286 struct Curl_easy *data = IfContext;
287 struct HTTP *stream = data->req.p.http;
288 size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
289
290 (void)Request;
291 if(data && data->set.verbose)
292 CF_DEBUGF(fprintf(stderr, "* [MSH3] req: evt: received %u. %zu buffered, "
293 "%zu allocated\n",
294 *Length, cur_recv_len, stream->recv_buf_alloc));
295 /* TODO - Update this code to limit data bufferring by `stream->recv_buf_max`
296 and return `false` when we reach that limit. Then, when curl drains some
297 of the buffer, making room, call MsH3RequestSetReceiveEnabled to enable
298 receive callbacks again. */
299 msh3_lock_acquire(&stream->recv_lock);
300
301 if(!stream->recv_header_complete) {
302 if(data && data->set.verbose)
303 CF_DEBUGF(fprintf(stderr, "* [MSH3] req: Headers complete!\n"));
304 if(!msh3request_ensure_room(stream, 2)) {
305 stream->recv_error = CURLE_OUT_OF_MEMORY;
306 goto release_lock;
307 }
308 stream->recv_buf[stream->recv_header_len++] = '\r';
309 stream->recv_buf[stream->recv_header_len++] = '\n';
310 stream->recv_header_complete = true;
311 cur_recv_len += 2;
312 }
313 if(!msh3request_ensure_room(stream, *Length)) {
314 stream->recv_error = CURLE_OUT_OF_MEMORY;
315 goto release_lock;
316 }
317 memcpy(stream->recv_buf + cur_recv_len, Data, *Length);
318 stream->recv_data_len += (size_t)*Length;
319 data->state.drain = 1;
320
321release_lock:
322 msh3_lock_release(&stream->recv_lock);
323 return true;
324}
325
326static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
327 bool Aborted, uint64_t AbortError)
328{
329 struct Curl_easy *data = IfContext;
330 struct HTTP *stream = data->req.p.http;
331
332 (void)Request;
333 (void)AbortError;
334 if(data && data->set.verbose)
335 CF_DEBUGF(fprintf(stderr, "* [MSH3] req: evt: complete, aborted=%s\n",
336 Aborted ? "true" : "false"));
337 msh3_lock_acquire(&stream->recv_lock);
338 if(Aborted) {
339 stream->recv_error = CURLE_HTTP3; /* TODO - how do we pass AbortError? */
340 }
341 stream->recv_header_complete = true;
342 stream->recv_data_complete = true;
343 msh3_lock_release(&stream->recv_lock);
344}
345
346static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
347 void *IfContext)
348{
349 struct Curl_easy *data = IfContext;
350 struct HTTP *stream = data->req.p.http;
351 (void)Request;
352 (void)stream;
353}
354
355static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
356 void *IfContext, void *SendContext)
357{
358 struct Curl_easy *data = IfContext;
359 struct HTTP *stream = data->req.p.http;
360 (void)Request;
361 (void)stream;
362 (void)SendContext;
363}
364
365static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
366 char *buf, size_t len, CURLcode *err)
367{
368 struct HTTP *stream = data->req.p.http;
369 size_t outsize = 0;
370
371 (void)cf;
372 DEBUGF(LOG_CF(data, cf, "req: recv with %zu byte buffer", len));
373
374 if(stream->recv_error) {
375 failf(data, "request aborted");
376 data->state.drain = 0;
377 *err = stream->recv_error;
378 return -1;
379 }
380
381 *err = CURLE_OK;
382 msh3_lock_acquire(&stream->recv_lock);
383
384 if(stream->recv_header_len) {
385 outsize = len;
386 if(stream->recv_header_len < outsize) {
387 outsize = stream->recv_header_len;
388 }
389 memcpy(buf, stream->recv_buf, outsize);
390 if(outsize < stream->recv_header_len + stream->recv_data_len) {
391 memmove(stream->recv_buf, stream->recv_buf + outsize,
392 stream->recv_header_len + stream->recv_data_len - outsize);
393 }
394 stream->recv_header_len -= outsize;
395 DEBUGF(LOG_CF(data, cf, "req: returned %zu bytes of header", outsize));
396 }
397 else if(stream->recv_data_len) {
398 outsize = len;
399 if(stream->recv_data_len < outsize) {
400 outsize = stream->recv_data_len;
401 }
402 memcpy(buf, stream->recv_buf, outsize);
403 if(outsize < stream->recv_data_len) {
404 memmove(stream->recv_buf, stream->recv_buf + outsize,
405 stream->recv_data_len - outsize);
406 }
407 stream->recv_data_len -= outsize;
408 DEBUGF(LOG_CF(data, cf, "req: returned %zu bytes of data", outsize));
409 if(stream->recv_data_len == 0 && stream->recv_data_complete)
410 data->state.drain = 1;
411 }
412 else if(stream->recv_data_complete) {
413 DEBUGF(LOG_CF(data, cf, "req: receive complete"));
414 data->state.drain = 0;
415 }
416 else {
417 DEBUGF(LOG_CF(data, cf, "req: nothing here, call again"));
418 *err = CURLE_AGAIN;
419 outsize = -1;
420 }
421
422 msh3_lock_release(&stream->recv_lock);
423
424 return (ssize_t)outsize;
425}
426
427static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
428 const void *buf, size_t len, CURLcode *err)
429{
430 struct cf_msh3_ctx *ctx = cf->ctx;
431 struct HTTP *stream = data->req.p.http;
432 struct h2h3req *hreq;
433 size_t hdrlen = 0;
434 size_t sentlen = 0;
435
436 /* Sizes must match for cast below to work" */
437 DEBUGASSERT(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo));
438 DEBUGF(LOG_CF(data, cf, "req: send %zu bytes", len));
439
440 if(!stream->req) {
441 /* The first send on the request contains the headers and possibly some
442 data. Parse out the headers and create the request, then if there is
443 any data left over go ahead and send it too. */
444
445 *err = msh3_data_setup(cf, data);
446 if(*err) {
447 failf(data, "could not setup data");
448 return -1;
449 }
450
451 *err = Curl_pseudo_headers(data, buf, len, &hdrlen, &hreq);
452 if(*err) {
453 failf(data, "Curl_pseudo_headers failed");
454 return -1;
455 }
456
457 DEBUGF(LOG_CF(data, cf, "req: send %zu headers", hreq->entries));
458 stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data,
459 (MSH3_HEADER*)hreq->header, hreq->entries,
460 hdrlen == len ? MSH3_REQUEST_FLAG_FIN :
461 MSH3_REQUEST_FLAG_NONE);
462 Curl_pseudo_free(hreq);
463 if(!stream->req) {
464 failf(data, "request open failed");
465 *err = CURLE_SEND_ERROR;
466 return -1;
467 }
468 *err = CURLE_OK;
469 return len;
470 }
471
472 DEBUGF(LOG_CF(data, cf, "req: send %zd body bytes", len));
473 if(len > 0xFFFFFFFF) {
474 /* msh3 doesn't support size_t sends currently. */
475 *err = CURLE_SEND_ERROR;
476 return -1;
477 }
478
479 /* TODO - Need an explicit signal to know when to FIN. */
480 if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN, buf, (uint32_t)len,
481 stream)) {
482 *err = CURLE_SEND_ERROR;
483 return -1;
484 }
485
486 /* TODO - msh3/msquic will hold onto this memory until the send complete
487 event. How do we make sure curl doesn't free it until then? */
488 sentlen += len;
489 *err = CURLE_OK;
490 return sentlen;
491}
492
493static int cf_msh3_get_select_socks(struct Curl_cfilter *cf,
494 struct Curl_easy *data,
495 curl_socket_t *socks)
496{
497 struct cf_msh3_ctx *ctx = cf->ctx;
498 struct HTTP *stream = data->req.p.http;
499 int bitmap = GETSOCK_BLANK;
500
501 if(stream && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
502 socks[0] = ctx->sock[SP_LOCAL];
503
504 if(stream->recv_error) {
505 bitmap |= GETSOCK_READSOCK(0);
506 data->state.drain = 1;
507 }
508 else if(stream->recv_header_len || stream->recv_data_len) {
509 bitmap |= GETSOCK_READSOCK(0);
510 data->state.drain = 1;
511 }
512 }
513 DEBUGF(LOG_CF(data, cf, "select_sock %u -> %d",
514 (uint32_t)data->state.drain, bitmap));
515
516 return bitmap;
517}
518
519static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
520 const struct Curl_easy *data)
521{
522 struct HTTP *stream = data->req.p.http;
523
524 (void)cf;
525 DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data pending = %hhu",
526 (bool)(stream->recv_header_len || stream->recv_data_len)));
527 return stream->recv_header_len || stream->recv_data_len;
528}
529
530static void cf_msh3_active(struct Curl_cfilter *cf, struct Curl_easy *data)
531{
532 struct cf_msh3_ctx *ctx = cf->ctx;
533
534 /* use this socket from now on */
535 cf->conn->sock[cf->sockindex] = ctx->sock[SP_LOCAL];
536 /* the first socket info gets set at conn and data */
537 if(cf->sockindex == FIRSTSOCKET) {
538 cf->conn->remote_addr = &ctx->addr;
539 #ifdef ENABLE_IPV6
540 cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
541 #endif
542 Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
543 }
544 ctx->active = TRUE;
545}
546
547static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf,
548 struct Curl_easy *data,
549 int event, int arg1, void *arg2)
550{
551 struct HTTP *stream = data->req.p.http;
552 CURLcode result = CURLE_OK;
553
554 (void)arg1;
555 (void)arg2;
556 switch(event) {
557 case CF_CTRL_DATA_SETUP:
558 result = msh3_data_setup(cf, data);
559 break;
560 case CF_CTRL_DATA_DONE:
561 DEBUGF(LOG_CF(data, cf, "req: done"));
562 if(stream) {
563 if(stream->recv_buf) {
564 Curl_safefree(stream->recv_buf);
565 msh3_lock_uninitialize(&stream->recv_lock);
566 }
567 if(stream->req) {
568 MsH3RequestClose(stream->req);
569 stream->req = ZERO_NULL;
570 }
571 }
572 break;
573 case CF_CTRL_DATA_DONE_SEND:
574 DEBUGF(LOG_CF(data, cf, "req: send done"));
575 stream->upload_done = TRUE;
576 break;
577 case CF_CTRL_CONN_INFO_UPDATE:
578 DEBUGF(LOG_CF(data, cf, "req: update info"));
579 cf_msh3_active(cf, data);
580 break;
581 default:
582 break;
583 }
584 return result;
585}
586
587static CURLcode cf_connect_start(struct Curl_cfilter *cf,
588 struct Curl_easy *data)
589{
590 struct cf_msh3_ctx *ctx = cf->ctx;
591 bool verify = !!cf->conn->ssl_config.verifypeer;
592 MSH3_ADDR addr = {0};
593 memcpy(&addr, &ctx->addr.sa_addr, ctx->addr.addrlen);
594 MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port);
595 ctx->verbose = (data && data->set.verbose);
596
597 if(verify && (cf->conn->ssl_config.CAfile || cf->conn->ssl_config.CApath)) {
598 /* TODO: need a way to provide trust anchors to MSH3 */
599#ifdef DEBUGBUILD
600 /* we need this for our test cases to run */
601 DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, "
602 "switching off verifypeer in DEBUG mode"));
603 verify = 0;
604#else
605 DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, "
606 "attempting with built-in verification"));
607#endif
608 }
609
610 DEBUGF(LOG_CF(data, cf, "connecting to %s:%d (verify=%d)",
611 cf->conn->host.name, (int)cf->conn->remote_port, verify));
612
613 ctx->api = MsH3ApiOpen();
614 if(!ctx->api) {
615 failf(data, "can't create msh3 api");
616 return CURLE_FAILED_INIT;
617 }
618
619 ctx->qconn = MsH3ConnectionOpen(ctx->api,
620 &msh3_conn_if,
621 ctx,
622 cf->conn->host.name,
623 &addr,
624 !verify);
625 if(!ctx->qconn) {
626 failf(data, "can't create msh3 connection");
627 if(ctx->api) {
628 MsH3ApiClose(ctx->api);
629 ctx->api = NULL;
630 }
631 return CURLE_FAILED_INIT;
632 }
633
634 return CURLE_OK;
635}
636
637static CURLcode cf_msh3_connect(struct Curl_cfilter *cf,
638 struct Curl_easy *data,
639 bool blocking, bool *done)
640{
641 struct cf_msh3_ctx *ctx = cf->ctx;
642 CURLcode result = CURLE_OK;
643
644 (void)blocking;
645 if(cf->connected) {
646 *done = TRUE;
647 return CURLE_OK;
648 }
649
650 if(ctx->sock[SP_LOCAL] == CURL_SOCKET_BAD) {
651 if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0]) < 0) {
652 ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
653 ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
654 return CURLE_COULDNT_CONNECT;
655 }
656 }
657
658 *done = FALSE;
659 if(!ctx->qconn) {
660 ctx->connect_started = Curl_now();
661 result = cf_connect_start(cf, data);
662 if(result)
663 goto out;
664 }
665
666 if(ctx->handshake_complete) {
667 ctx->handshake_at = Curl_now();
668 if(ctx->handshake_succeeded) {
669 cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
670 cf->conn->httpversion = 30;
671 cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
672 cf->connected = TRUE;
673 cf->conn->alpn = CURL_HTTP_VERSION_3;
674 *done = TRUE;
675 connkeep(cf->conn, "HTTP/3 default");
676 Curl_pgrsTime(data, TIMER_APPCONNECT);
677 }
678 else {
679 failf(data, "failed to connect, handshake failed");
680 result = CURLE_COULDNT_CONNECT;
681 }
682 }
683
684out:
685 return result;
686}
687
688static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data)
689{
690 struct cf_msh3_ctx *ctx = cf->ctx;
691
692 (void)data;
693 if(ctx) {
694 DEBUGF(LOG_CF(data, cf, "destroying"));
695 if(ctx->qconn)
696 MsH3ConnectionClose(ctx->qconn);
697 if(ctx->api)
698 MsH3ApiClose(ctx->api);
699
700 if(ctx->active) {
701 /* We share our socket at cf->conn->sock[cf->sockindex] when active.
702 * If it is no longer there, someone has stolen (and hopefully
703 * closed it) and we just forget about it.
704 */
705 if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) {
706 DEBUGF(LOG_CF(data, cf, "cf_msh3_close(%d) active",
707 (int)ctx->sock[SP_LOCAL]));
708 cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
709 }
710 else {
711 DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d) no longer at "
712 "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]));
713 ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
714 }
715 if(cf->sockindex == FIRSTSOCKET)
716 cf->conn->remote_addr = NULL;
717 }
718 if(ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
719 sclose(ctx->sock[SP_LOCAL]);
720 }
721 if(ctx->sock[SP_REMOTE] != CURL_SOCKET_BAD) {
722 sclose(ctx->sock[SP_REMOTE]);
723 }
724 memset(ctx, 0, sizeof(*ctx));
725 ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
726 ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
727 }
728}
729
730static void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
731{
732 cf_msh3_close(cf, data);
733 free(cf->ctx);
734 cf->ctx = NULL;
735}
736
737static CURLcode cf_msh3_query(struct Curl_cfilter *cf,
738 struct Curl_easy *data,
739 int query, int *pres1, void *pres2)
740{
741 struct cf_msh3_ctx *ctx = cf->ctx;
742
743 switch(query) {
744 case CF_QUERY_MAX_CONCURRENT: {
745 /* TODO: we do not have access to this so far, fake it */
746 (void)ctx;
747 *pres1 = 100;
748 return CURLE_OK;
749 }
750 case CF_QUERY_TIMER_CONNECT: {
751 struct curltime *when = pres2;
752 /* we do not know when the first byte arrived */
753 if(cf->connected)
754 *when = ctx->handshake_at;
755 return CURLE_OK;
756 }
757 case CF_QUERY_TIMER_APPCONNECT: {
758 struct curltime *when = pres2;
759 if(cf->connected)
760 *when = ctx->handshake_at;
761 return CURLE_OK;
762 }
763 default:
764 break;
765 }
766 return cf->next?
767 cf->next->cft->query(cf->next, data, query, pres1, pres2) :
768 CURLE_UNKNOWN_OPTION;
769}
770
771static bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf,
772 struct Curl_easy *data,
773 bool *input_pending)
774{
775 struct cf_msh3_ctx *ctx = cf->ctx;
776
777 (void)data;
778 *input_pending = FALSE;
779 return ctx && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD && ctx->qconn &&
780 ctx->connected;
781}
782
783struct Curl_cftype Curl_cft_http3 = {
784 "HTTP/3",
785 CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
786 0,
787 cf_msh3_destroy,
788 cf_msh3_connect,
789 cf_msh3_close,
790 Curl_cf_def_get_host,
791 cf_msh3_get_select_socks,
792 cf_msh3_data_pending,
793 cf_msh3_send,
794 cf_msh3_recv,
795 cf_msh3_data_event,
796 cf_msh3_conn_is_alive,
797 Curl_cf_def_conn_keep_alive,
798 cf_msh3_query,
799};
800
801CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
802 struct Curl_easy *data,
803 struct connectdata *conn,
804 const struct Curl_addrinfo *ai)
805{
806 struct cf_msh3_ctx *ctx = NULL;
807 struct Curl_cfilter *cf = NULL;
808 CURLcode result;
809
810 (void)data;
811 (void)conn;
812 (void)ai; /* TODO: msh3 resolves itself? */
813 ctx = calloc(sizeof(*ctx), 1);
814 if(!ctx) {
815 result = CURLE_OUT_OF_MEMORY;
816 goto out;
817 }
818 Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC);
819 ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
820 ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
821
822 result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
823
824out:
825 *pcf = (!result)? cf : NULL;
826 if(result) {
827 Curl_safefree(cf);
828 Curl_safefree(ctx);
829 }
830
831 return result;
832}
833
834bool Curl_conn_is_msh3(const struct Curl_easy *data,
835 const struct connectdata *conn,
836 int sockindex)
837{
838 struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
839
840 (void)data;
841 for(; cf; cf = cf->next) {
842 if(cf->cft == &Curl_cft_http3)
843 return TRUE;
844 if(cf->cft->flags & CF_TYPE_IP_CONNECT)
845 return FALSE;
846 }
847 return FALSE;
848}
849
850#endif /* USE_MSH3 */
Note: See TracBrowser for help on using the repository browser.

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