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