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