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