• 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_log.h"
34 #include "cfilters.h"
35 #include "cf-socket.h"
36 #include "connect.h"
37 #include "progress.h"
38 #include "h2h3.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 DEBUG_CF 1
49 
50 #if DEBUG_CF && defined(DEBUGBUILD)
51 #define CF_DEBUGF(x) x
52 #else
53 #define CF_DEBUGF(x) do { } while(0)
54 #endif
55 
56 #define MSH3_REQ_INIT_BUF_LEN 16384
57 #define MSH3_REQ_MAX_BUF_LEN 0x100000
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 curltime connect_started;   /* time the current attempt started */
120   struct curltime handshake_at;      /* time connect handshake finished */
121   /* Flags written by msh3/msquic thread */
122   bool handshake_complete;
123   bool handshake_succeeded;
124   bool connected;
125   /* Flags written by curl thread */
126   BIT(verbose);
127   BIT(active);
128 };
129 
130 static const MSH3_CONNECTION_IF msh3_conn_if = {
131   msh3_conn_connected,
132   msh3_conn_shutdown_complete,
133   msh3_conn_new_request
134 };
135 
msh3_conn_connected(MSH3_CONNECTION * Connection,void * IfContext)136 static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection,
137                                           void *IfContext)
138 {
139   struct cf_msh3_ctx *ctx = IfContext;
140   (void)Connection;
141   if(ctx->verbose)
142     CF_DEBUGF(fprintf(stderr, "* [MSH3] evt: connected\n"));
143   ctx->handshake_succeeded = true;
144   ctx->connected = true;
145   ctx->handshake_complete = true;
146 }
147 
msh3_conn_shutdown_complete(MSH3_CONNECTION * Connection,void * IfContext)148 static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection,
149                                           void *IfContext)
150 {
151   struct cf_msh3_ctx *ctx = IfContext;
152   (void)Connection;
153   if(ctx->verbose)
154     CF_DEBUGF(fprintf(stderr, "* [MSH3] evt: shutdown complete\n"));
155   ctx->connected = false;
156   ctx->handshake_complete = true;
157 }
158 
msh3_conn_new_request(MSH3_CONNECTION * Connection,void * IfContext,MSH3_REQUEST * Request)159 static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection,
160                                           void *IfContext,
161                                           MSH3_REQUEST *Request)
162 {
163   (void)Connection;
164   (void)IfContext;
165   (void)Request;
166 }
167 
168 static const MSH3_REQUEST_IF msh3_request_if = {
169   msh3_header_received,
170   msh3_data_received,
171   msh3_complete,
172   msh3_shutdown_complete,
173   msh3_data_sent
174 };
175 
msh3_data_setup(struct Curl_cfilter * cf,struct Curl_easy * data)176 static CURLcode msh3_data_setup(struct Curl_cfilter *cf,
177                                 struct Curl_easy *data)
178 {
179   struct HTTP *stream = data->req.p.http;
180   (void)cf;
181 
182   DEBUGASSERT(stream);
183   if(!stream->recv_buf) {
184     DEBUGF(LOG_CF(data, cf, "req: setup"));
185     stream->recv_buf = malloc(MSH3_REQ_INIT_BUF_LEN);
186     if(!stream->recv_buf) {
187       return CURLE_OUT_OF_MEMORY;
188     }
189     stream->req = ZERO_NULL;
190     msh3_lock_initialize(&stream->recv_lock);
191     stream->recv_buf_alloc = MSH3_REQ_INIT_BUF_LEN;
192     stream->recv_buf_max = MSH3_REQ_MAX_BUF_LEN;
193     stream->recv_header_len = 0;
194     stream->recv_header_complete = false;
195     stream->recv_data_len = 0;
196     stream->recv_data_complete = false;
197     stream->recv_error = CURLE_OK;
198   }
199   return CURLE_OK;
200 }
201 
202 /* Requires stream->recv_lock to be held */
msh3request_ensure_room(struct HTTP * stream,size_t len)203 static bool msh3request_ensure_room(struct HTTP *stream, size_t len)
204 {
205   uint8_t *new_recv_buf;
206   const size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
207 
208   if(cur_recv_len + len > stream->recv_buf_alloc) {
209     size_t new_recv_buf_alloc_len = stream->recv_buf_alloc;
210     do {
211       new_recv_buf_alloc_len <<= 1; /* TODO - handle overflow */
212     } while(cur_recv_len + len > new_recv_buf_alloc_len);
213     CF_DEBUGF(fprintf(stderr, "* enlarging buffer to %zu\n",
214               new_recv_buf_alloc_len));
215     new_recv_buf = malloc(new_recv_buf_alloc_len);
216     if(!new_recv_buf) {
217       CF_DEBUGF(fprintf(stderr, "* FAILED: enlarging buffer to %zu\n",
218                 new_recv_buf_alloc_len));
219       return false;
220     }
221     if(cur_recv_len) {
222       memcpy(new_recv_buf, stream->recv_buf, cur_recv_len);
223     }
224     stream->recv_buf_alloc = new_recv_buf_alloc_len;
225     free(stream->recv_buf);
226     stream->recv_buf = new_recv_buf;
227   }
228   return true;
229 }
230 
msh3_header_received(MSH3_REQUEST * Request,void * IfContext,const MSH3_HEADER * Header)231 static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
232                                            void *IfContext,
233                                            const MSH3_HEADER *Header)
234 {
235   struct Curl_easy *data = IfContext;
236   struct HTTP *stream = data->req.p.http;
237   size_t total_len;
238   (void)Request;
239 
240   if(stream->recv_header_complete) {
241     CF_DEBUGF(fprintf(stderr, "* ignoring header after data\n"));
242     return;
243   }
244 
245   msh3_lock_acquire(&stream->recv_lock);
246 
247   if((Header->NameLength == 7) &&
248      !strncmp(H2H3_PSEUDO_STATUS, (char *)Header->Name, 7)) {
249     total_len = 10 + Header->ValueLength;
250     if(!msh3request_ensure_room(stream, total_len)) {
251       CF_DEBUGF(fprintf(stderr, "* ERROR: unable to buffer: %.*s\n",
252                 (int)Header->NameLength, Header->Name));
253       stream->recv_error = CURLE_OUT_OF_MEMORY;
254       goto release_lock;
255     }
256     msnprintf((char *)stream->recv_buf + stream->recv_header_len,
257               stream->recv_buf_alloc - stream->recv_header_len,
258               "HTTP/3 %.*s \r\n", (int)Header->ValueLength, Header->Value);
259   }
260   else {
261     total_len = 4 + Header->NameLength + Header->ValueLength;
262     if(!msh3request_ensure_room(stream, total_len)) {
263       CF_DEBUGF(fprintf(stderr, "* ERROR: unable to buffer: %.*s\n",
264                 (int)Header->NameLength, Header->Name));
265       stream->recv_error = CURLE_OUT_OF_MEMORY;
266       goto release_lock;
267     }
268     msnprintf((char *)stream->recv_buf + stream->recv_header_len,
269               stream->recv_buf_alloc - stream->recv_header_len,
270               "%.*s: %.*s\r\n",
271               (int)Header->NameLength, Header->Name,
272               (int)Header->ValueLength, Header->Value);
273   }
274 
275   stream->recv_header_len += total_len;
276   data->state.drain = 1;
277 
278 release_lock:
279   msh3_lock_release(&stream->recv_lock);
280 }
281 
msh3_data_received(MSH3_REQUEST * Request,void * IfContext,uint32_t * Length,const uint8_t * Data)282 static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
283                                          void *IfContext, uint32_t *Length,
284                                          const uint8_t *Data)
285 {
286   struct Curl_easy *data = IfContext;
287   struct HTTP *stream = data->req.p.http;
288   size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
289 
290   (void)Request;
291   if(data && data->set.verbose)
292     CF_DEBUGF(fprintf(stderr, "* [MSH3] req: evt: received %u. %zu buffered, "
293               "%zu allocated\n",
294               *Length, cur_recv_len, stream->recv_buf_alloc));
295   /* TODO - Update this code to limit data bufferring by `stream->recv_buf_max`
296      and return `false` when we reach that limit. Then, when curl drains some
297      of the buffer, making room, call MsH3RequestSetReceiveEnabled to enable
298      receive callbacks again. */
299   msh3_lock_acquire(&stream->recv_lock);
300 
301   if(!stream->recv_header_complete) {
302     if(data && data->set.verbose)
303       CF_DEBUGF(fprintf(stderr, "* [MSH3] req: Headers complete!\n"));
304     if(!msh3request_ensure_room(stream, 2)) {
305       stream->recv_error = CURLE_OUT_OF_MEMORY;
306       goto release_lock;
307     }
308     stream->recv_buf[stream->recv_header_len++] = '\r';
309     stream->recv_buf[stream->recv_header_len++] = '\n';
310     stream->recv_header_complete = true;
311     cur_recv_len += 2;
312   }
313   if(!msh3request_ensure_room(stream, *Length)) {
314     stream->recv_error = CURLE_OUT_OF_MEMORY;
315     goto release_lock;
316   }
317   memcpy(stream->recv_buf + cur_recv_len, Data, *Length);
318   stream->recv_data_len += (size_t)*Length;
319   data->state.drain = 1;
320 
321 release_lock:
322   msh3_lock_release(&stream->recv_lock);
323   return true;
324 }
325 
msh3_complete(MSH3_REQUEST * Request,void * IfContext,bool Aborted,uint64_t AbortError)326 static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
327                                     bool Aborted, uint64_t AbortError)
328 {
329   struct Curl_easy *data = IfContext;
330   struct HTTP *stream = data->req.p.http;
331 
332   (void)Request;
333   (void)AbortError;
334   if(data && data->set.verbose)
335     CF_DEBUGF(fprintf(stderr, "* [MSH3] req: evt: complete, aborted=%s\n",
336               Aborted ? "true" : "false"));
337   msh3_lock_acquire(&stream->recv_lock);
338   if(Aborted) {
339     stream->recv_error = CURLE_HTTP3; /* TODO - how do we pass AbortError? */
340   }
341   stream->recv_header_complete = true;
342   stream->recv_data_complete = true;
343   msh3_lock_release(&stream->recv_lock);
344 }
345 
msh3_shutdown_complete(MSH3_REQUEST * Request,void * IfContext)346 static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
347                                              void *IfContext)
348 {
349   struct Curl_easy *data = IfContext;
350   struct HTTP *stream = data->req.p.http;
351   (void)Request;
352   (void)stream;
353 }
354 
msh3_data_sent(MSH3_REQUEST * Request,void * IfContext,void * SendContext)355 static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
356                                      void *IfContext, void *SendContext)
357 {
358   struct Curl_easy *data = IfContext;
359   struct HTTP *stream = data->req.p.http;
360   (void)Request;
361   (void)stream;
362   (void)SendContext;
363 }
364 
cf_msh3_recv(struct Curl_cfilter * cf,struct Curl_easy * data,char * buf,size_t len,CURLcode * err)365 static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
366                             char *buf, size_t len, CURLcode *err)
367 {
368   struct HTTP *stream = data->req.p.http;
369   size_t outsize = 0;
370 
371   (void)cf;
372   DEBUGF(LOG_CF(data, cf, "req: recv with %zu byte buffer", len));
373 
374   if(stream->recv_error) {
375     failf(data, "request aborted");
376     data->state.drain = 0;
377     *err = stream->recv_error;
378     return -1;
379   }
380 
381   *err = CURLE_OK;
382   msh3_lock_acquire(&stream->recv_lock);
383 
384   if(stream->recv_header_len) {
385     outsize = len;
386     if(stream->recv_header_len < outsize) {
387       outsize = stream->recv_header_len;
388     }
389     memcpy(buf, stream->recv_buf, outsize);
390     if(outsize < stream->recv_header_len + stream->recv_data_len) {
391       memmove(stream->recv_buf, stream->recv_buf + outsize,
392               stream->recv_header_len + stream->recv_data_len - outsize);
393     }
394     stream->recv_header_len -= outsize;
395     DEBUGF(LOG_CF(data, cf, "req: returned %zu bytes of header", outsize));
396   }
397   else if(stream->recv_data_len) {
398     outsize = len;
399     if(stream->recv_data_len < outsize) {
400       outsize = stream->recv_data_len;
401     }
402     memcpy(buf, stream->recv_buf, outsize);
403     if(outsize < stream->recv_data_len) {
404       memmove(stream->recv_buf, stream->recv_buf + outsize,
405               stream->recv_data_len - outsize);
406     }
407     stream->recv_data_len -= outsize;
408     DEBUGF(LOG_CF(data, cf, "req: returned %zu bytes of data", outsize));
409     if(stream->recv_data_len == 0 && stream->recv_data_complete)
410       data->state.drain = 1;
411   }
412   else if(stream->recv_data_complete) {
413     DEBUGF(LOG_CF(data, cf, "req: receive complete"));
414     data->state.drain = 0;
415   }
416   else {
417     DEBUGF(LOG_CF(data, cf, "req: nothing here, call again"));
418     *err = CURLE_AGAIN;
419     outsize = -1;
420   }
421 
422   msh3_lock_release(&stream->recv_lock);
423 
424   return (ssize_t)outsize;
425 }
426 
cf_msh3_send(struct Curl_cfilter * cf,struct Curl_easy * data,const void * buf,size_t len,CURLcode * err)427 static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
428                             const void *buf, size_t len, CURLcode *err)
429 {
430   struct cf_msh3_ctx *ctx = cf->ctx;
431   struct HTTP *stream = data->req.p.http;
432   struct h2h3req *hreq;
433   size_t hdrlen = 0;
434   size_t sentlen = 0;
435 
436   /* Sizes must match for cast below to work" */
437   DEBUGASSERT(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo));
438   DEBUGF(LOG_CF(data, cf, "req: send %zu bytes", len));
439 
440   if(!stream->req) {
441     /* The first send on the request contains the headers and possibly some
442        data. Parse out the headers and create the request, then if there is
443        any data left over go ahead and send it too. */
444 
445     *err = msh3_data_setup(cf, data);
446     if(*err) {
447       failf(data, "could not setup data");
448       return -1;
449     }
450 
451     *err = Curl_pseudo_headers(data, buf, len, &hdrlen, &hreq);
452     if(*err) {
453       failf(data, "Curl_pseudo_headers failed");
454       return -1;
455     }
456 
457     DEBUGF(LOG_CF(data, cf, "req: send %zu headers", hreq->entries));
458     stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data,
459                                   (MSH3_HEADER*)hreq->header, hreq->entries,
460                                   hdrlen == len ? MSH3_REQUEST_FLAG_FIN :
461                                   MSH3_REQUEST_FLAG_NONE);
462     Curl_pseudo_free(hreq);
463     if(!stream->req) {
464       failf(data, "request open failed");
465       *err = CURLE_SEND_ERROR;
466       return -1;
467     }
468     *err = CURLE_OK;
469     return len;
470   }
471 
472   DEBUGF(LOG_CF(data, cf, "req: send %zd body bytes", len));
473   if(len > 0xFFFFFFFF) {
474     /* msh3 doesn't support size_t sends currently. */
475     *err = CURLE_SEND_ERROR;
476     return -1;
477   }
478 
479   /* TODO - Need an explicit signal to know when to FIN. */
480   if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN, buf, (uint32_t)len,
481                       stream)) {
482     *err = CURLE_SEND_ERROR;
483     return -1;
484   }
485 
486   /* TODO - msh3/msquic will hold onto this memory until the send complete
487      event. How do we make sure curl doesn't free it until then? */
488   sentlen += len;
489   *err = CURLE_OK;
490   return sentlen;
491 }
492 
cf_msh3_get_select_socks(struct Curl_cfilter * cf,struct Curl_easy * data,curl_socket_t * socks)493 static int cf_msh3_get_select_socks(struct Curl_cfilter *cf,
494                                     struct Curl_easy *data,
495                                     curl_socket_t *socks)
496 {
497   struct cf_msh3_ctx *ctx = cf->ctx;
498   struct HTTP *stream = data->req.p.http;
499   int bitmap = GETSOCK_BLANK;
500 
501   if(stream && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
502     socks[0] = ctx->sock[SP_LOCAL];
503 
504     if(stream->recv_error) {
505       bitmap |= GETSOCK_READSOCK(0);
506       data->state.drain = 1;
507     }
508     else if(stream->recv_header_len || stream->recv_data_len) {
509       bitmap |= GETSOCK_READSOCK(0);
510       data->state.drain = 1;
511     }
512   }
513   DEBUGF(LOG_CF(data, cf, "select_sock %u -> %d",
514                 (uint32_t)data->state.drain, bitmap));
515 
516   return bitmap;
517 }
518 
cf_msh3_data_pending(struct Curl_cfilter * cf,const struct Curl_easy * data)519 static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
520                                  const struct Curl_easy *data)
521 {
522   struct HTTP *stream = data->req.p.http;
523 
524   (void)cf;
525   DEBUGF(LOG_CF((struct Curl_easy *)data, cf, "data pending = %hhu",
526                 (bool)(stream->recv_header_len || stream->recv_data_len)));
527   return stream->recv_header_len || stream->recv_data_len;
528 }
529 
cf_msh3_active(struct Curl_cfilter * cf,struct Curl_easy * data)530 static void cf_msh3_active(struct Curl_cfilter *cf, struct Curl_easy *data)
531 {
532   struct cf_msh3_ctx *ctx = cf->ctx;
533 
534   /* use this socket from now on */
535   cf->conn->sock[cf->sockindex] = ctx->sock[SP_LOCAL];
536   /* the first socket info gets set at conn and data */
537   if(cf->sockindex == FIRSTSOCKET) {
538     cf->conn->remote_addr = &ctx->addr;
539   #ifdef ENABLE_IPV6
540     cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
541   #endif
542     Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
543   }
544   ctx->active = TRUE;
545 }
546 
cf_msh3_data_event(struct Curl_cfilter * cf,struct Curl_easy * data,int event,int arg1,void * arg2)547 static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf,
548                                    struct Curl_easy *data,
549                                    int event, int arg1, void *arg2)
550 {
551   struct HTTP *stream = data->req.p.http;
552   CURLcode result = CURLE_OK;
553 
554   (void)arg1;
555   (void)arg2;
556   switch(event) {
557   case CF_CTRL_DATA_SETUP:
558     result = msh3_data_setup(cf, data);
559     break;
560   case CF_CTRL_DATA_DONE:
561     DEBUGF(LOG_CF(data, cf, "req: done"));
562     if(stream) {
563       if(stream->recv_buf) {
564         Curl_safefree(stream->recv_buf);
565         msh3_lock_uninitialize(&stream->recv_lock);
566       }
567       if(stream->req) {
568         MsH3RequestClose(stream->req);
569         stream->req = ZERO_NULL;
570       }
571     }
572     break;
573   case CF_CTRL_DATA_DONE_SEND:
574     DEBUGF(LOG_CF(data, cf, "req: send done"));
575     stream->upload_done = TRUE;
576     break;
577   case CF_CTRL_CONN_INFO_UPDATE:
578     DEBUGF(LOG_CF(data, cf, "req: update info"));
579     cf_msh3_active(cf, data);
580     break;
581   default:
582     break;
583   }
584   return result;
585 }
586 
cf_connect_start(struct Curl_cfilter * cf,struct Curl_easy * data)587 static CURLcode cf_connect_start(struct Curl_cfilter *cf,
588                                  struct Curl_easy *data)
589 {
590   struct cf_msh3_ctx *ctx = cf->ctx;
591   bool verify = !!cf->conn->ssl_config.verifypeer;
592   MSH3_ADDR addr = {0};
593   memcpy(&addr, &ctx->addr.sa_addr, ctx->addr.addrlen);
594   MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port);
595   ctx->verbose = (data && data->set.verbose);
596 
597   if(verify && (cf->conn->ssl_config.CAfile || cf->conn->ssl_config.CApath)) {
598     /* TODO: need a way to provide trust anchors to MSH3 */
599 #ifdef DEBUGBUILD
600     /* we need this for our test cases to run */
601     DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, "
602                   "switching off verifypeer in DEBUG mode"));
603     verify = 0;
604 #else
605     DEBUGF(LOG_CF(data, cf, "non-standard CA not supported, "
606                   "attempting with built-in verification"));
607 #endif
608   }
609 
610   DEBUGF(LOG_CF(data, cf, "connecting to %s:%d (verify=%d)",
611                 cf->conn->host.name, (int)cf->conn->remote_port, verify));
612 
613   ctx->api = MsH3ApiOpen();
614   if(!ctx->api) {
615     failf(data, "can't create msh3 api");
616     return CURLE_FAILED_INIT;
617   }
618 
619   ctx->qconn = MsH3ConnectionOpen(ctx->api,
620                                   &msh3_conn_if,
621                                   ctx,
622                                   cf->conn->host.name,
623                                   &addr,
624                                   !verify);
625   if(!ctx->qconn) {
626     failf(data, "can't create msh3 connection");
627     if(ctx->api) {
628       MsH3ApiClose(ctx->api);
629       ctx->api = NULL;
630     }
631     return CURLE_FAILED_INIT;
632   }
633 
634   return CURLE_OK;
635 }
636 
cf_msh3_connect(struct Curl_cfilter * cf,struct Curl_easy * data,bool blocking,bool * done)637 static CURLcode cf_msh3_connect(struct Curl_cfilter *cf,
638                                 struct Curl_easy *data,
639                                 bool blocking, bool *done)
640 {
641   struct cf_msh3_ctx *ctx = cf->ctx;
642   CURLcode result = CURLE_OK;
643 
644   (void)blocking;
645   if(cf->connected) {
646     *done = TRUE;
647     return CURLE_OK;
648   }
649 
650   if(ctx->sock[SP_LOCAL] == CURL_SOCKET_BAD) {
651     if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0]) < 0) {
652       ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
653       ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
654       return CURLE_COULDNT_CONNECT;
655     }
656   }
657 
658   *done = FALSE;
659   if(!ctx->qconn) {
660     ctx->connect_started = Curl_now();
661     result = cf_connect_start(cf, data);
662     if(result)
663       goto out;
664   }
665 
666   if(ctx->handshake_complete) {
667     ctx->handshake_at = Curl_now();
668     if(ctx->handshake_succeeded) {
669       cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
670       cf->conn->httpversion = 30;
671       cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
672       cf->connected = TRUE;
673       cf->conn->alpn = CURL_HTTP_VERSION_3;
674       *done = TRUE;
675       connkeep(cf->conn, "HTTP/3 default");
676       Curl_pgrsTime(data, TIMER_APPCONNECT);
677     }
678     else {
679       failf(data, "failed to connect, handshake failed");
680       result = CURLE_COULDNT_CONNECT;
681     }
682   }
683 
684 out:
685   return result;
686 }
687 
cf_msh3_close(struct Curl_cfilter * cf,struct Curl_easy * data)688 static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data)
689 {
690   struct cf_msh3_ctx *ctx = cf->ctx;
691 
692   (void)data;
693   if(ctx) {
694     DEBUGF(LOG_CF(data, cf, "destroying"));
695     if(ctx->qconn)
696       MsH3ConnectionClose(ctx->qconn);
697     if(ctx->api)
698       MsH3ApiClose(ctx->api);
699 
700     if(ctx->active) {
701       /* We share our socket at cf->conn->sock[cf->sockindex] when active.
702        * If it is no longer there, someone has stolen (and hopefully
703        * closed it) and we just forget about it.
704        */
705       if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) {
706         DEBUGF(LOG_CF(data, cf, "cf_msh3_close(%d) active",
707                       (int)ctx->sock[SP_LOCAL]));
708         cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
709       }
710       else {
711         DEBUGF(LOG_CF(data, cf, "cf_socket_close(%d) no longer at "
712                       "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]));
713         ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
714       }
715       if(cf->sockindex == FIRSTSOCKET)
716         cf->conn->remote_addr = NULL;
717     }
718     if(ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
719       sclose(ctx->sock[SP_LOCAL]);
720     }
721     if(ctx->sock[SP_REMOTE] != CURL_SOCKET_BAD) {
722       sclose(ctx->sock[SP_REMOTE]);
723     }
724     memset(ctx, 0, sizeof(*ctx));
725     ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
726     ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
727   }
728 }
729 
cf_msh3_destroy(struct Curl_cfilter * cf,struct Curl_easy * data)730 static void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
731 {
732   cf_msh3_close(cf, data);
733   free(cf->ctx);
734   cf->ctx = NULL;
735 }
736 
cf_msh3_query(struct Curl_cfilter * cf,struct Curl_easy * data,int query,int * pres1,void * pres2)737 static CURLcode cf_msh3_query(struct Curl_cfilter *cf,
738                               struct Curl_easy *data,
739                               int query, int *pres1, void *pres2)
740 {
741   struct cf_msh3_ctx *ctx = cf->ctx;
742 
743   switch(query) {
744   case CF_QUERY_MAX_CONCURRENT: {
745     /* TODO: we do not have access to this so far, fake it */
746     (void)ctx;
747     *pres1 = 100;
748     return CURLE_OK;
749   }
750   case CF_QUERY_TIMER_CONNECT: {
751     struct curltime *when = pres2;
752     /* we do not know when the first byte arrived */
753     if(cf->connected)
754       *when = ctx->handshake_at;
755     return CURLE_OK;
756   }
757   case CF_QUERY_TIMER_APPCONNECT: {
758     struct curltime *when = pres2;
759     if(cf->connected)
760       *when = ctx->handshake_at;
761     return CURLE_OK;
762   }
763   default:
764     break;
765   }
766   return cf->next?
767     cf->next->cft->query(cf->next, data, query, pres1, pres2) :
768     CURLE_UNKNOWN_OPTION;
769 }
770 
cf_msh3_conn_is_alive(struct Curl_cfilter * cf,struct Curl_easy * data,bool * input_pending)771 static bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf,
772                                   struct Curl_easy *data,
773                                   bool *input_pending)
774 {
775   struct cf_msh3_ctx *ctx = cf->ctx;
776 
777   (void)data;
778   *input_pending = FALSE;
779   return ctx && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD && ctx->qconn &&
780          ctx->connected;
781 }
782 
783 struct Curl_cftype Curl_cft_http3 = {
784   "HTTP/3",
785   CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
786   0,
787   cf_msh3_destroy,
788   cf_msh3_connect,
789   cf_msh3_close,
790   Curl_cf_def_get_host,
791   cf_msh3_get_select_socks,
792   cf_msh3_data_pending,
793   cf_msh3_send,
794   cf_msh3_recv,
795   cf_msh3_data_event,
796   cf_msh3_conn_is_alive,
797   Curl_cf_def_conn_keep_alive,
798   cf_msh3_query,
799 };
800 
Curl_cf_msh3_create(struct Curl_cfilter ** pcf,struct Curl_easy * data,struct connectdata * conn,const struct Curl_addrinfo * ai)801 CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
802                              struct Curl_easy *data,
803                              struct connectdata *conn,
804                              const struct Curl_addrinfo *ai)
805 {
806   struct cf_msh3_ctx *ctx = NULL;
807   struct Curl_cfilter *cf = NULL;
808   CURLcode result;
809 
810   (void)data;
811   (void)conn;
812   (void)ai; /* TODO: msh3 resolves itself? */
813   ctx = calloc(sizeof(*ctx), 1);
814   if(!ctx) {
815     result = CURLE_OUT_OF_MEMORY;
816     goto out;
817   }
818   Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC);
819   ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
820   ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
821 
822   result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
823 
824 out:
825   *pcf = (!result)? cf : NULL;
826   if(result) {
827     Curl_safefree(cf);
828     Curl_safefree(ctx);
829   }
830 
831   return result;
832 }
833 
Curl_conn_is_msh3(const struct Curl_easy * data,const struct connectdata * conn,int sockindex)834 bool Curl_conn_is_msh3(const struct Curl_easy *data,
835                        const struct connectdata *conn,
836                        int sockindex)
837 {
838   struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
839 
840   (void)data;
841   for(; cf; cf = cf->next) {
842     if(cf->cft == &Curl_cft_http3)
843       return TRUE;
844     if(cf->cft->flags & CF_TYPE_IP_CONNECT)
845       return FALSE;
846   }
847   return FALSE;
848 }
849 
850 #endif /* USE_MSH3 */
851