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 #ifdef USE_MSH3
28
29 #include "urldata.h"
30 #include "timeval.h"
31 #include "multiif.h"
32 #include "sendf.h"
33 #include "curl_trc.h"
34 #include "cfilters.h"
35 #include "cf-socket.h"
36 #include "connect.h"
37 #include "progress.h"
38 #include "http1.h"
39 #include "curl_msh3.h"
40 #include "socketpair.h"
41 #include "vtls/vtls.h"
42 #include "vquic/vquic.h"
43
44 /* The last 3 #include files should be in this order */
45 #include "curl_printf.h"
46 #include "curl_memory.h"
47 #include "memdebug.h"
48
49 #ifdef CURL_DISABLE_SOCKETPAIR
50 #error "MSH3 cannot be build with CURL_DISABLE_SOCKETPAIR set"
51 #endif
52
53 #define H3_STREAM_WINDOW_SIZE (128 * 1024)
54 #define H3_STREAM_CHUNK_SIZE (16 * 1024)
55 #define H3_STREAM_RECV_CHUNKS \
56 (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
57
58 #ifdef _WIN32
59 #define msh3_lock CRITICAL_SECTION
60 #define msh3_lock_initialize(lock) InitializeCriticalSection(lock)
61 #define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock)
62 #define msh3_lock_acquire(lock) EnterCriticalSection(lock)
63 #define msh3_lock_release(lock) LeaveCriticalSection(lock)
64 #else /* !_WIN32 */
65 #include <pthread.h>
66 #define msh3_lock pthread_mutex_t
67 #define msh3_lock_initialize(lock) do { \
68 pthread_mutexattr_t attr; \
69 pthread_mutexattr_init(&attr); \
70 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
71 pthread_mutex_init(lock, &attr); \
72 pthread_mutexattr_destroy(&attr); \
73 }while(0)
74 #define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock)
75 #define msh3_lock_acquire(lock) pthread_mutex_lock(lock)
76 #define msh3_lock_release(lock) pthread_mutex_unlock(lock)
77 #endif /* _WIN32 */
78
79
80 static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection,
81 void *IfContext);
82 static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection,
83 void *IfContext);
84 static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection,
85 void *IfContext,
86 MSH3_REQUEST *Request);
87 static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
88 void *IfContext,
89 const MSH3_HEADER *Header);
90 static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
91 void *IfContext, uint32_t *Length,
92 const uint8_t *Data);
93 static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
94 bool Aborted, uint64_t AbortError);
95 static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
96 void *IfContext);
97 static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
98 void *IfContext, void *SendContext);
99
100
Curl_msh3_ver(char *p, size_t len)101 void Curl_msh3_ver(char *p, size_t len)
102 {
103 uint32_t v[4];
104 MsH3Version(v);
105 (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]);
106 }
107
108 #define SP_LOCAL 0
109 #define SP_REMOTE 1
110
111 struct cf_msh3_ctx {
112 MSH3_API *api;
113 MSH3_CONNECTION *qconn;
114 struct Curl_sockaddr_ex addr;
115 curl_socket_t sock[2]; /* fake socket pair until we get support in msh3 */
116 char l_ip[MAX_IPADR_LEN]; /* local IP as string */
117 int l_port; /* local port number */
118 struct cf_call_data call_data;
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
130 /* How to access `call_data` from a cf_msh3 filter */
131 #undef CF_CTX_CALL_DATA
132 #define CF_CTX_CALL_DATA(cf) \
133 ((struct cf_msh3_ctx *)(cf)->ctx)->call_data
134
135 /**
136 * All about the H3 internals of a stream
137 */
138 struct stream_ctx {
139 struct MSH3_REQUEST *req;
140 struct bufq recvbuf; /* h3 response */
141 #ifdef _WIN32
142 CRITICAL_SECTION recv_lock;
143 #else /* !_WIN32 */
144 pthread_mutex_t recv_lock;
145 #endif /* _WIN32 */
146 uint64_t error3; /* HTTP/3 stream error code */
147 int status_code; /* HTTP status code */
148 CURLcode recv_error;
149 bool closed;
150 bool reset;
151 bool upload_done;
152 bool firstheader; /* FALSE until headers arrive */
153 bool recv_header_complete;
154 };
155
156 #define H3_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \
157 ((struct HTTP *)(d)->req.p.http)->h3_ctx \
158 : NULL))
159 #define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx
160 #define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \
161 H3_STREAM_CTX(d)->id : -2)
162
163
h3_data_setup(struct Curl_cfilter *cf, struct Curl_easy *data)164 static CURLcode h3_data_setup(struct Curl_cfilter *cf,
165 struct Curl_easy *data)
166 {
167 struct stream_ctx *stream = H3_STREAM_CTX(data);
168
169 if(stream)
170 return CURLE_OK;
171
172 stream = calloc(1, sizeof(*stream));
173 if(!stream)
174 return CURLE_OUT_OF_MEMORY;
175
176 H3_STREAM_LCTX(data) = stream;
177 stream->req = ZERO_NULL;
178 msh3_lock_initialize(&stream->recv_lock);
179 Curl_bufq_init2(&stream->recvbuf, H3_STREAM_CHUNK_SIZE,
180 H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
181 CURL_TRC_CF(data, cf, "data setup");
182 return CURLE_OK;
183 }
184
h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)185 static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
186 {
187 struct stream_ctx *stream = H3_STREAM_CTX(data);
188
189 (void)cf;
190 if(stream) {
191 CURL_TRC_CF(data, cf, "easy handle is done");
192 Curl_bufq_free(&stream->recvbuf);
193 free(stream);
194 H3_STREAM_LCTX(data) = NULL;
195 }
196 }
197
drain_stream_from_other_thread(struct Curl_easy *data, struct stream_ctx *stream)198 static void drain_stream_from_other_thread(struct Curl_easy *data,
199 struct stream_ctx *stream)
200 {
201 unsigned char bits;
202
203 /* risky */
204 bits = CURL_CSELECT_IN;
205 if(stream && !stream->upload_done)
206 bits |= CURL_CSELECT_OUT;
207 if(data->state.select_bits != bits) {
208 data->state.select_bits = bits;
209 /* cannot expire from other thread */
210 }
211 }
212
drain_stream(struct Curl_cfilter *cf, struct Curl_easy *data)213 static void drain_stream(struct Curl_cfilter *cf,
214 struct Curl_easy *data)
215 {
216 struct stream_ctx *stream = H3_STREAM_CTX(data);
217 unsigned char bits;
218
219 (void)cf;
220 bits = CURL_CSELECT_IN;
221 if(stream && !stream->upload_done)
222 bits |= CURL_CSELECT_OUT;
223 if(data->state.select_bits != bits) {
224 data->state.select_bits = bits;
225 Curl_expire(data, 0, EXPIRE_RUN_NOW);
226 }
227 }
228
229 static const MSH3_CONNECTION_IF msh3_conn_if = {
230 msh3_conn_connected,
231 msh3_conn_shutdown_complete,
232 msh3_conn_new_request
233 };
234
msh3_conn_connected(MSH3_CONNECTION *Connection, void *IfContext)235 static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection,
236 void *IfContext)
237 {
238 struct Curl_cfilter *cf = IfContext;
239 struct cf_msh3_ctx *ctx = cf->ctx;
240 struct Curl_easy *data = CF_DATA_CURRENT(cf);
241 (void)Connection;
242
243 CURL_TRC_CF(data, cf, "[MSH3] connected");
244 ctx->handshake_succeeded = true;
245 ctx->connected = true;
246 ctx->handshake_complete = true;
247 }
248
msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection, void *IfContext)249 static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection,
250 void *IfContext)
251 {
252 struct Curl_cfilter *cf = IfContext;
253 struct cf_msh3_ctx *ctx = cf->ctx;
254 struct Curl_easy *data = CF_DATA_CURRENT(cf);
255
256 (void)Connection;
257 CURL_TRC_CF(data, cf, "[MSH3] shutdown complete");
258 ctx->connected = false;
259 ctx->handshake_complete = true;
260 }
261
msh3_conn_new_request(MSH3_CONNECTION *Connection, void *IfContext, MSH3_REQUEST *Request)262 static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection,
263 void *IfContext,
264 MSH3_REQUEST *Request)
265 {
266 (void)Connection;
267 (void)IfContext;
268 (void)Request;
269 }
270
271 static const MSH3_REQUEST_IF msh3_request_if = {
272 msh3_header_received,
273 msh3_data_received,
274 msh3_complete,
275 msh3_shutdown_complete,
276 msh3_data_sent
277 };
278
279 /* Decode HTTP status code. Returns -1 if no valid status code was
280 decoded. (duplicate from http2.c) */
decode_status_code(const char *value, size_t len)281 static int decode_status_code(const char *value, size_t len)
282 {
283 int i;
284 int res;
285
286 if(len != 3) {
287 return -1;
288 }
289
290 res = 0;
291
292 for(i = 0; i < 3; ++i) {
293 char c = value[i];
294
295 if(c < '0' || c > '9') {
296 return -1;
297 }
298
299 res *= 10;
300 res += c - '0';
301 }
302
303 return res;
304 }
305
306 /*
307 * write_resp_raw() copies response data in raw format to the `data`'s
308 * receive buffer. If not enough space is available, it appends to the
309 * `data`'s overflow buffer.
310 */
write_resp_raw(struct Curl_easy *data, const void *mem, size_t memlen)311 static CURLcode write_resp_raw(struct Curl_easy *data,
312 const void *mem, size_t memlen)
313 {
314 struct stream_ctx *stream = H3_STREAM_CTX(data);
315 CURLcode result = CURLE_OK;
316 ssize_t nwritten;
317
318 if(!stream)
319 return CURLE_RECV_ERROR;
320
321 nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result);
322 if(nwritten < 0) {
323 return result;
324 }
325
326 if((size_t)nwritten < memlen) {
327 /* This MUST not happen. Our recbuf is dimensioned to hold the
328 * full max_stream_window and then some for this very reason. */
329 DEBUGASSERT(0);
330 return CURLE_RECV_ERROR;
331 }
332 return result;
333 }
334
msh3_header_received(MSH3_REQUEST *Request, void *userp, const MSH3_HEADER *hd)335 static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
336 void *userp,
337 const MSH3_HEADER *hd)
338 {
339 struct Curl_easy *data = userp;
340 struct stream_ctx *stream = H3_STREAM_CTX(data);
341 CURLcode result;
342 (void)Request;
343
344 if(!stream || stream->recv_header_complete) {
345 return;
346 }
347
348 msh3_lock_acquire(&stream->recv_lock);
349
350 if((hd->NameLength == 7) &&
351 !strncmp(HTTP_PSEUDO_STATUS, (char *)hd->Name, 7)) {
352 char line[14]; /* status line is always 13 characters long */
353 size_t ncopy;
354
355 DEBUGASSERT(!stream->firstheader);
356 stream->status_code = decode_status_code(hd->Value, hd->ValueLength);
357 DEBUGASSERT(stream->status_code != -1);
358 ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
359 stream->status_code);
360 result = write_resp_raw(data, line, ncopy);
361 if(result)
362 stream->recv_error = result;
363 stream->firstheader = TRUE;
364 }
365 else {
366 /* store as an HTTP1-style header */
367 DEBUGASSERT(stream->firstheader);
368 result = write_resp_raw(data, hd->Name, hd->NameLength);
369 if(!result)
370 result = write_resp_raw(data, ": ", 2);
371 if(!result)
372 result = write_resp_raw(data, hd->Value, hd->ValueLength);
373 if(!result)
374 result = write_resp_raw(data, "\r\n", 2);
375 if(result) {
376 stream->recv_error = result;
377 }
378 }
379
380 drain_stream_from_other_thread(data, stream);
381 msh3_lock_release(&stream->recv_lock);
382 }
383
msh3_data_received(MSH3_REQUEST *Request, void *IfContext, uint32_t *buflen, const uint8_t *buf)384 static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
385 void *IfContext, uint32_t *buflen,
386 const uint8_t *buf)
387 {
388 struct Curl_easy *data = IfContext;
389 struct stream_ctx *stream = H3_STREAM_CTX(data);
390 CURLcode result;
391 bool rv = FALSE;
392
393 /* TODO: we would like to limit the amount of data we are buffer here.
394 * There seems to be no mechanism in msh3 to adjust flow control and
395 * it is undocumented what happens if we return FALSE here or less
396 * length (buflen is an inout parameter).
397 */
398 (void)Request;
399 if(!stream)
400 return FALSE;
401
402 msh3_lock_acquire(&stream->recv_lock);
403
404 if(!stream->recv_header_complete) {
405 result = write_resp_raw(data, "\r\n", 2);
406 if(result) {
407 stream->recv_error = result;
408 goto out;
409 }
410 stream->recv_header_complete = true;
411 }
412
413 result = write_resp_raw(data, buf, *buflen);
414 if(result) {
415 stream->recv_error = result;
416 }
417 rv = TRUE;
418
419 out:
420 msh3_lock_release(&stream->recv_lock);
421 return rv;
422 }
423
msh3_complete(MSH3_REQUEST *Request, void *IfContext, bool aborted, uint64_t error)424 static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
425 bool aborted, uint64_t error)
426 {
427 struct Curl_easy *data = IfContext;
428 struct stream_ctx *stream = H3_STREAM_CTX(data);
429
430 (void)Request;
431 if(!stream)
432 return;
433 msh3_lock_acquire(&stream->recv_lock);
434 stream->closed = TRUE;
435 stream->recv_header_complete = true;
436 if(error)
437 stream->error3 = error;
438 if(aborted)
439 stream->reset = TRUE;
440 msh3_lock_release(&stream->recv_lock);
441 }
442
msh3_shutdown_complete(MSH3_REQUEST *Request, void *IfContext)443 static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
444 void *IfContext)
445 {
446 struct Curl_easy *data = IfContext;
447 struct stream_ctx *stream = H3_STREAM_CTX(data);
448
449 if(!stream)
450 return;
451 (void)Request;
452 (void)stream;
453 }
454
msh3_data_sent(MSH3_REQUEST *Request, void *IfContext, void *SendContext)455 static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
456 void *IfContext, void *SendContext)
457 {
458 struct Curl_easy *data = IfContext;
459 struct stream_ctx *stream = H3_STREAM_CTX(data);
460 if(!stream)
461 return;
462 (void)Request;
463 (void)stream;
464 (void)SendContext;
465 }
466
recv_closed_stream(struct Curl_cfilter *cf, struct Curl_easy *data, CURLcode *err)467 static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
468 struct Curl_easy *data,
469 CURLcode *err)
470 {
471 struct stream_ctx *stream = H3_STREAM_CTX(data);
472 ssize_t nread = -1;
473
474 if(!stream) {
475 *err = CURLE_RECV_ERROR;
476 return -1;
477 }
478 (void)cf;
479 if(stream->reset) {
480 failf(data, "HTTP/3 stream reset by server");
481 *err = CURLE_PARTIAL_FILE;
482 CURL_TRC_CF(data, cf, "cf_recv, was reset -> %d", *err);
483 goto out;
484 }
485 else if(stream->error3) {
486 failf(data, "HTTP/3 stream was not closed cleanly: (error %zd)",
487 (ssize_t)stream->error3);
488 *err = CURLE_HTTP3;
489 CURL_TRC_CF(data, cf, "cf_recv, closed uncleanly -> %d", *err);
490 goto out;
491 }
492 else {
493 CURL_TRC_CF(data, cf, "cf_recv, closed ok -> %d", *err);
494 }
495 *err = CURLE_OK;
496 nread = 0;
497
498 out:
499 return nread;
500 }
501
set_quic_expire(struct Curl_cfilter *cf, struct Curl_easy *data)502 static void set_quic_expire(struct Curl_cfilter *cf, struct Curl_easy *data)
503 {
504 struct stream_ctx *stream = H3_STREAM_CTX(data);
505
506 /* we have no indication from msh3 when it would be a good time
507 * to juggle the connection again. So, we compromise by calling
508 * us again every some milliseconds. */
509 (void)cf;
510 if(stream && stream->req && !stream->closed) {
511 Curl_expire(data, 10, EXPIRE_QUIC);
512 }
513 else {
514 Curl_expire(data, 50, EXPIRE_QUIC);
515 }
516 }
517
cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data, char *buf, size_t len, CURLcode *err)518 static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
519 char *buf, size_t len, CURLcode *err)
520 {
521 struct stream_ctx *stream = H3_STREAM_CTX(data);
522 ssize_t nread = -1;
523 struct cf_call_data save;
524
525 (void)cf;
526 if(!stream) {
527 *err = CURLE_RECV_ERROR;
528 return -1;
529 }
530 CF_DATA_SAVE(save, cf, data);
531 CURL_TRC_CF(data, cf, "req: recv with %zu byte buffer", len);
532
533 msh3_lock_acquire(&stream->recv_lock);
534
535 if(stream->recv_error) {
536 failf(data, "request aborted");
537 *err = stream->recv_error;
538 goto out;
539 }
540
541 *err = CURLE_OK;
542
543 if(!Curl_bufq_is_empty(&stream->recvbuf)) {
544 nread = Curl_bufq_read(&stream->recvbuf,
545 (unsigned char *)buf, len, err);
546 CURL_TRC_CF(data, cf, "read recvbuf(len=%zu) -> %zd, %d",
547 len, nread, *err);
548 if(nread < 0)
549 goto out;
550 if(stream->closed)
551 drain_stream(cf, data);
552 }
553 else if(stream->closed) {
554 nread = recv_closed_stream(cf, data, err);
555 goto out;
556 }
557 else {
558 CURL_TRC_CF(data, cf, "req: nothing here, call again");
559 *err = CURLE_AGAIN;
560 }
561
562 out:
563 msh3_lock_release(&stream->recv_lock);
564 set_quic_expire(cf, data);
565 CF_DATA_RESTORE(cf, save);
566 return nread;
567 }
568
cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, const void *buf, size_t len, CURLcode *err)569 static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
570 const void *buf, size_t len, CURLcode *err)
571 {
572 struct cf_msh3_ctx *ctx = cf->ctx;
573 struct stream_ctx *stream = H3_STREAM_CTX(data);
574 struct h1_req_parser h1;
575 struct dynhds h2_headers;
576 MSH3_HEADER *nva = NULL;
577 size_t nheader, i;
578 ssize_t nwritten = -1;
579 struct cf_call_data save;
580 bool eos;
581
582 CF_DATA_SAVE(save, cf, data);
583
584 Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
585 Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
586
587 /* Sizes must match for cast below to work" */
588 DEBUGASSERT(stream);
589 CURL_TRC_CF(data, cf, "req: send %zu bytes", len);
590
591 if(!stream->req) {
592 /* The first send on the request contains the headers and possibly some
593 data. Parse out the headers and create the request, then if there is
594 any data left over go ahead and send it too. */
595 nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err);
596 if(nwritten < 0)
597 goto out;
598 DEBUGASSERT(h1.done);
599 DEBUGASSERT(h1.req);
600
601 *err = Curl_http_req_to_h2(&h2_headers, h1.req, data);
602 if(*err) {
603 nwritten = -1;
604 goto out;
605 }
606
607 nheader = Curl_dynhds_count(&h2_headers);
608 nva = malloc(sizeof(MSH3_HEADER) * nheader);
609 if(!nva) {
610 *err = CURLE_OUT_OF_MEMORY;
611 nwritten = -1;
612 goto out;
613 }
614
615 for(i = 0; i < nheader; ++i) {
616 struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
617 nva[i].Name = e->name;
618 nva[i].NameLength = e->namelen;
619 nva[i].Value = e->value;
620 nva[i].ValueLength = e->valuelen;
621 }
622
623 switch(data->state.httpreq) {
624 case HTTPREQ_POST:
625 case HTTPREQ_POST_FORM:
626 case HTTPREQ_POST_MIME:
627 case HTTPREQ_PUT:
628 /* known request body size or -1 */
629 eos = FALSE;
630 break;
631 default:
632 /* there is not request body */
633 eos = TRUE;
634 stream->upload_done = TRUE;
635 break;
636 }
637
638 CURL_TRC_CF(data, cf, "req: send %zu headers", nheader);
639 stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data,
640 nva, nheader,
641 eos ? MSH3_REQUEST_FLAG_FIN :
642 MSH3_REQUEST_FLAG_NONE);
643 if(!stream->req) {
644 failf(data, "request open failed");
645 *err = CURLE_SEND_ERROR;
646 goto out;
647 }
648 *err = CURLE_OK;
649 nwritten = len;
650 goto out;
651 }
652 else {
653 /* request is open */
654 CURL_TRC_CF(data, cf, "req: send %zu body bytes", len);
655 if(len > 0xFFFFFFFF) {
656 len = 0xFFFFFFFF;
657 }
658
659 if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_NONE, buf,
660 (uint32_t)len, stream)) {
661 *err = CURLE_SEND_ERROR;
662 goto out;
663 }
664
665 /* TODO - msh3/msquic will hold onto this memory until the send complete
666 event. How do we make sure curl doesn't free it until then? */
667 *err = CURLE_OK;
668 nwritten = len;
669 }
670
671 out:
672 set_quic_expire(cf, data);
673 free(nva);
674 Curl_h1_req_parse_free(&h1);
675 Curl_dynhds_free(&h2_headers);
676 CF_DATA_RESTORE(cf, save);
677 return nwritten;
678 }
679
cf_msh3_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data, struct easy_pollset *ps)680 static void cf_msh3_adjust_pollset(struct Curl_cfilter *cf,
681 struct Curl_easy *data,
682 struct easy_pollset *ps)
683 {
684 struct cf_msh3_ctx *ctx = cf->ctx;
685 struct stream_ctx *stream = H3_STREAM_CTX(data);
686 struct cf_call_data save;
687
688 CF_DATA_SAVE(save, cf, data);
689 if(stream && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
690 if(stream->recv_error) {
691 Curl_pollset_add_in(data, ps, ctx->sock[SP_LOCAL]);
692 drain_stream(cf, data);
693 }
694 else if(stream->req) {
695 Curl_pollset_add_out(data, ps, ctx->sock[SP_LOCAL]);
696 drain_stream(cf, data);
697 }
698 }
699 }
700
cf_msh3_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data)701 static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
702 const struct Curl_easy *data)
703 {
704 struct stream_ctx *stream = H3_STREAM_CTX(data);
705 struct cf_call_data save;
706 bool pending = FALSE;
707
708 CF_DATA_SAVE(save, cf, data);
709
710 (void)cf;
711 if(stream && stream->req) {
712 msh3_lock_acquire(&stream->recv_lock);
713 CURL_TRC_CF((struct Curl_easy *)data, cf, "data pending = %zu",
714 Curl_bufq_len(&stream->recvbuf));
715 pending = !Curl_bufq_is_empty(&stream->recvbuf);
716 msh3_lock_release(&stream->recv_lock);
717 if(pending)
718 drain_stream(cf, (struct Curl_easy *)data);
719 }
720
721 CF_DATA_RESTORE(cf, save);
722 return pending;
723 }
724
cf_msh3_active(struct Curl_cfilter *cf, struct Curl_easy *data)725 static void cf_msh3_active(struct Curl_cfilter *cf, struct Curl_easy *data)
726 {
727 struct cf_msh3_ctx *ctx = cf->ctx;
728
729 /* use this socket from now on */
730 cf->conn->sock[cf->sockindex] = ctx->sock[SP_LOCAL];
731 /* the first socket info gets set at conn and data */
732 if(cf->sockindex == FIRSTSOCKET) {
733 cf->conn->remote_addr = &ctx->addr;
734 #ifdef ENABLE_IPV6
735 cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
736 #endif
737 Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
738 }
739 ctx->active = TRUE;
740 }
741
h3_data_pause(struct Curl_cfilter *cf, struct Curl_easy *data, bool pause)742 static CURLcode h3_data_pause(struct Curl_cfilter *cf,
743 struct Curl_easy *data,
744 bool pause)
745 {
746 if(!pause) {
747 drain_stream(cf, data);
748 Curl_expire(data, 0, EXPIRE_RUN_NOW);
749 }
750 return CURLE_OK;
751 }
752
cf_msh3_data_event(struct Curl_cfilter *cf, struct Curl_easy *data, int event, int arg1, void *arg2)753 static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf,
754 struct Curl_easy *data,
755 int event, int arg1, void *arg2)
756 {
757 struct stream_ctx *stream = H3_STREAM_CTX(data);
758 struct cf_call_data save;
759 CURLcode result = CURLE_OK;
760
761 CF_DATA_SAVE(save, cf, data);
762
763 (void)arg1;
764 (void)arg2;
765 switch(event) {
766 case CF_CTRL_DATA_SETUP:
767 result = h3_data_setup(cf, data);
768 break;
769 case CF_CTRL_DATA_PAUSE:
770 result = h3_data_pause(cf, data, (arg1 != 0));
771 break;
772 case CF_CTRL_DATA_DONE:
773 h3_data_done(cf, data);
774 break;
775 case CF_CTRL_DATA_DONE_SEND:
776 CURL_TRC_CF(data, cf, "req: send done");
777 if(stream) {
778 stream->upload_done = TRUE;
779 if(stream->req) {
780 char buf[1];
781 if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN,
782 buf, 0, data)) {
783 result = CURLE_SEND_ERROR;
784 }
785 }
786 }
787 break;
788 case CF_CTRL_CONN_INFO_UPDATE:
789 CURL_TRC_CF(data, cf, "req: update info");
790 cf_msh3_active(cf, data);
791 break;
792 default:
793 break;
794 }
795
796 CF_DATA_RESTORE(cf, save);
797 return result;
798 }
799
cf_connect_start(struct Curl_cfilter *cf, struct Curl_easy *data)800 static CURLcode cf_connect_start(struct Curl_cfilter *cf,
801 struct Curl_easy *data)
802 {
803 struct cf_msh3_ctx *ctx = cf->ctx;
804 struct ssl_primary_config *conn_config;
805 MSH3_ADDR addr = {0};
806 CURLcode result;
807 bool verify;
808
809 conn_config = Curl_ssl_cf_get_primary_config(cf);
810 if(!conn_config)
811 return CURLE_FAILED_INIT;
812 verify = !!conn_config->verifypeer;
813
814 memcpy(&addr, &ctx->addr.sa_addr, ctx->addr.addrlen);
815 MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port);
816
817 if(verify && (conn_config->CAfile || conn_config->CApath)) {
818 /* TODO: need a way to provide trust anchors to MSH3 */
819 #ifdef DEBUGBUILD
820 /* we need this for our test cases to run */
821 CURL_TRC_CF(data, cf, "non-standard CA not supported, "
822 "switching off verifypeer in DEBUG mode");
823 verify = 0;
824 #else
825 CURL_TRC_CF(data, cf, "non-standard CA not supported, "
826 "attempting with built-in verification");
827 #endif
828 }
829
830 CURL_TRC_CF(data, cf, "connecting to %s:%d (verify=%d)",
831 cf->conn->host.name, (int)cf->conn->remote_port, verify);
832
833 ctx->api = MsH3ApiOpen();
834 if(!ctx->api) {
835 failf(data, "can't create msh3 api");
836 return CURLE_FAILED_INIT;
837 }
838
839 ctx->qconn = MsH3ConnectionOpen(ctx->api,
840 &msh3_conn_if,
841 cf,
842 cf->conn->host.name,
843 &addr,
844 !verify);
845 if(!ctx->qconn) {
846 failf(data, "can't create msh3 connection");
847 if(ctx->api) {
848 MsH3ApiClose(ctx->api);
849 ctx->api = NULL;
850 }
851 return CURLE_FAILED_INIT;
852 }
853
854 result = h3_data_setup(cf, data);
855 if(result)
856 return result;
857
858 return CURLE_OK;
859 }
860
cf_msh3_connect(struct Curl_cfilter *cf, struct Curl_easy *data, bool blocking, bool *done)861 static CURLcode cf_msh3_connect(struct Curl_cfilter *cf,
862 struct Curl_easy *data,
863 bool blocking, bool *done)
864 {
865 struct cf_msh3_ctx *ctx = cf->ctx;
866 struct cf_call_data save;
867 CURLcode result = CURLE_OK;
868
869 (void)blocking;
870 if(cf->connected) {
871 *done = TRUE;
872 return CURLE_OK;
873 }
874
875 CF_DATA_SAVE(save, cf, data);
876
877 if(ctx->sock[SP_LOCAL] == CURL_SOCKET_BAD) {
878 if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0]) < 0) {
879 ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
880 ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
881 return CURLE_COULDNT_CONNECT;
882 }
883 }
884
885 *done = FALSE;
886 if(!ctx->qconn) {
887 ctx->connect_started = Curl_now();
888 result = cf_connect_start(cf, data);
889 if(result)
890 goto out;
891 }
892
893 if(ctx->handshake_complete) {
894 ctx->handshake_at = Curl_now();
895 if(ctx->handshake_succeeded) {
896 CURL_TRC_CF(data, cf, "handshake succeeded");
897 cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
898 cf->conn->httpversion = 30;
899 cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
900 cf->connected = TRUE;
901 cf->conn->alpn = CURL_HTTP_VERSION_3;
902 *done = TRUE;
903 connkeep(cf->conn, "HTTP/3 default");
904 Curl_pgrsTime(data, TIMER_APPCONNECT);
905 }
906 else {
907 failf(data, "failed to connect, handshake failed");
908 result = CURLE_COULDNT_CONNECT;
909 }
910 }
911
912 out:
913 CF_DATA_RESTORE(cf, save);
914 return result;
915 }
916
cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data)917 static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data)
918 {
919 struct cf_msh3_ctx *ctx = cf->ctx;
920 struct cf_call_data save;
921
922 (void)data;
923 CF_DATA_SAVE(save, cf, data);
924
925 if(ctx) {
926 CURL_TRC_CF(data, cf, "destroying");
927 if(ctx->qconn) {
928 MsH3ConnectionClose(ctx->qconn);
929 ctx->qconn = NULL;
930 }
931 if(ctx->api) {
932 MsH3ApiClose(ctx->api);
933 ctx->api = NULL;
934 }
935
936 if(ctx->active) {
937 /* We share our socket at cf->conn->sock[cf->sockindex] when active.
938 * If it is no longer there, someone has stolen (and hopefully
939 * closed it) and we just forget about it.
940 */
941 ctx->active = FALSE;
942 if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) {
943 CURL_TRC_CF(data, cf, "cf_msh3_close(%d) active",
944 (int)ctx->sock[SP_LOCAL]);
945 cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
946 }
947 else {
948 CURL_TRC_CF(data, cf, "cf_socket_close(%d) no longer at "
949 "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]);
950 ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
951 }
952 if(cf->sockindex == FIRSTSOCKET)
953 cf->conn->remote_addr = NULL;
954 }
955 if(ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
956 sclose(ctx->sock[SP_LOCAL]);
957 }
958 if(ctx->sock[SP_REMOTE] != CURL_SOCKET_BAD) {
959 sclose(ctx->sock[SP_REMOTE]);
960 }
961 ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
962 ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
963 }
964 CF_DATA_RESTORE(cf, save);
965 }
966
cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)967 static void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
968 {
969 struct cf_call_data save;
970
971 CF_DATA_SAVE(save, cf, data);
972 cf_msh3_close(cf, data);
973 free(cf->ctx);
974 cf->ctx = NULL;
975 /* no CF_DATA_RESTORE(cf, save); its gone */
976
977 }
978
cf_msh3_query(struct Curl_cfilter *cf, struct Curl_easy *data, int query, int *pres1, void *pres2)979 static CURLcode cf_msh3_query(struct Curl_cfilter *cf,
980 struct Curl_easy *data,
981 int query, int *pres1, void *pres2)
982 {
983 struct cf_msh3_ctx *ctx = cf->ctx;
984
985 switch(query) {
986 case CF_QUERY_MAX_CONCURRENT: {
987 /* TODO: we do not have access to this so far, fake it */
988 (void)ctx;
989 *pres1 = 100;
990 return CURLE_OK;
991 }
992 case CF_QUERY_TIMER_CONNECT: {
993 struct curltime *when = pres2;
994 /* we do not know when the first byte arrived */
995 if(cf->connected)
996 *when = ctx->handshake_at;
997 return CURLE_OK;
998 }
999 case CF_QUERY_TIMER_APPCONNECT: {
1000 struct curltime *when = pres2;
1001 if(cf->connected)
1002 *when = ctx->handshake_at;
1003 return CURLE_OK;
1004 }
1005 default:
1006 break;
1007 }
1008 return cf->next?
1009 cf->next->cft->query(cf->next, data, query, pres1, pres2) :
1010 CURLE_UNKNOWN_OPTION;
1011 }
1012
cf_msh3_conn_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data, bool *input_pending)1013 static bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf,
1014 struct Curl_easy *data,
1015 bool *input_pending)
1016 {
1017 struct cf_msh3_ctx *ctx = cf->ctx;
1018
1019 (void)data;
1020 *input_pending = FALSE;
1021 return ctx && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD && ctx->qconn &&
1022 ctx->connected;
1023 }
1024
1025 struct Curl_cftype Curl_cft_http3 = {
1026 "HTTP/3",
1027 CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
1028 0,
1029 cf_msh3_destroy,
1030 cf_msh3_connect,
1031 cf_msh3_close,
1032 Curl_cf_def_get_host,
1033 cf_msh3_adjust_pollset,
1034 cf_msh3_data_pending,
1035 cf_msh3_send,
1036 cf_msh3_recv,
1037 cf_msh3_data_event,
1038 cf_msh3_conn_is_alive,
1039 Curl_cf_def_conn_keep_alive,
1040 cf_msh3_query,
1041 };
1042
Curl_cf_msh3_create(struct Curl_cfilter **pcf, struct Curl_easy *data, struct connectdata *conn, const struct Curl_addrinfo *ai)1043 CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
1044 struct Curl_easy *data,
1045 struct connectdata *conn,
1046 const struct Curl_addrinfo *ai)
1047 {
1048 struct cf_msh3_ctx *ctx = NULL;
1049 struct Curl_cfilter *cf = NULL;
1050 CURLcode result;
1051
1052 (void)data;
1053 (void)conn;
1054 (void)ai; /* TODO: msh3 resolves itself? */
1055 ctx = calloc(1, sizeof(*ctx));
1056 if(!ctx) {
1057 result = CURLE_OUT_OF_MEMORY;
1058 goto out;
1059 }
1060 Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC);
1061 ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
1062 ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
1063
1064 result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
1065
1066 out:
1067 *pcf = (!result)? cf : NULL;
1068 if(result) {
1069 Curl_safefree(cf);
1070 Curl_safefree(ctx);
1071 }
1072
1073 return result;
1074 }
1075
Curl_conn_is_msh3(const struct Curl_easy *data, const struct connectdata *conn, int sockindex)1076 bool Curl_conn_is_msh3(const struct Curl_easy *data,
1077 const struct connectdata *conn,
1078 int sockindex)
1079 {
1080 struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
1081
1082 (void)data;
1083 for(; cf; cf = cf->next) {
1084 if(cf->cft == &Curl_cft_http3)
1085 return TRUE;
1086 if(cf->cft->flags & CF_TYPE_IP_CONNECT)
1087 return FALSE;
1088 }
1089 return FALSE;
1090 }
1091
1092 #endif /* USE_MSH3 */
1093