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