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