xref: /curl/lib/vquic/curl_msh3.c (revision 1064dfa8)
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