113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci#include "curl_setup.h"
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci#ifdef USE_MSH3
2813498266Sopenharmony_ci
2913498266Sopenharmony_ci#include "urldata.h"
3013498266Sopenharmony_ci#include "timeval.h"
3113498266Sopenharmony_ci#include "multiif.h"
3213498266Sopenharmony_ci#include "sendf.h"
3313498266Sopenharmony_ci#include "curl_trc.h"
3413498266Sopenharmony_ci#include "cfilters.h"
3513498266Sopenharmony_ci#include "cf-socket.h"
3613498266Sopenharmony_ci#include "connect.h"
3713498266Sopenharmony_ci#include "progress.h"
3813498266Sopenharmony_ci#include "http1.h"
3913498266Sopenharmony_ci#include "curl_msh3.h"
4013498266Sopenharmony_ci#include "socketpair.h"
4113498266Sopenharmony_ci#include "vtls/vtls.h"
4213498266Sopenharmony_ci#include "vquic/vquic.h"
4313498266Sopenharmony_ci
4413498266Sopenharmony_ci/* The last 3 #include files should be in this order */
4513498266Sopenharmony_ci#include "curl_printf.h"
4613498266Sopenharmony_ci#include "curl_memory.h"
4713498266Sopenharmony_ci#include "memdebug.h"
4813498266Sopenharmony_ci
4913498266Sopenharmony_ci#ifdef CURL_DISABLE_SOCKETPAIR
5013498266Sopenharmony_ci#error "MSH3 cannot be build with CURL_DISABLE_SOCKETPAIR set"
5113498266Sopenharmony_ci#endif
5213498266Sopenharmony_ci
5313498266Sopenharmony_ci#define H3_STREAM_WINDOW_SIZE (128 * 1024)
5413498266Sopenharmony_ci#define H3_STREAM_CHUNK_SIZE   (16 * 1024)
5513498266Sopenharmony_ci#define H3_STREAM_RECV_CHUNKS \
5613498266Sopenharmony_ci          (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
5713498266Sopenharmony_ci
5813498266Sopenharmony_ci#ifdef _WIN32
5913498266Sopenharmony_ci#define msh3_lock CRITICAL_SECTION
6013498266Sopenharmony_ci#define msh3_lock_initialize(lock) InitializeCriticalSection(lock)
6113498266Sopenharmony_ci#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock)
6213498266Sopenharmony_ci#define msh3_lock_acquire(lock) EnterCriticalSection(lock)
6313498266Sopenharmony_ci#define msh3_lock_release(lock) LeaveCriticalSection(lock)
6413498266Sopenharmony_ci#else /* !_WIN32 */
6513498266Sopenharmony_ci#include <pthread.h>
6613498266Sopenharmony_ci#define msh3_lock pthread_mutex_t
6713498266Sopenharmony_ci#define msh3_lock_initialize(lock) do { \
6813498266Sopenharmony_ci  pthread_mutexattr_t attr; \
6913498266Sopenharmony_ci  pthread_mutexattr_init(&attr); \
7013498266Sopenharmony_ci  pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
7113498266Sopenharmony_ci  pthread_mutex_init(lock, &attr); \
7213498266Sopenharmony_ci  pthread_mutexattr_destroy(&attr); \
7313498266Sopenharmony_ci}while(0)
7413498266Sopenharmony_ci#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock)
7513498266Sopenharmony_ci#define msh3_lock_acquire(lock) pthread_mutex_lock(lock)
7613498266Sopenharmony_ci#define msh3_lock_release(lock) pthread_mutex_unlock(lock)
7713498266Sopenharmony_ci#endif /* _WIN32 */
7813498266Sopenharmony_ci
7913498266Sopenharmony_ci
8013498266Sopenharmony_cistatic void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection,
8113498266Sopenharmony_ci                                          void *IfContext);
8213498266Sopenharmony_cistatic void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection,
8313498266Sopenharmony_ci                                          void *IfContext);
8413498266Sopenharmony_cistatic void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection,
8513498266Sopenharmony_ci                                          void *IfContext,
8613498266Sopenharmony_ci                                          MSH3_REQUEST *Request);
8713498266Sopenharmony_cistatic void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
8813498266Sopenharmony_ci                                           void *IfContext,
8913498266Sopenharmony_ci                                           const MSH3_HEADER *Header);
9013498266Sopenharmony_cistatic bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
9113498266Sopenharmony_ci                                        void *IfContext, uint32_t *Length,
9213498266Sopenharmony_ci                                        const uint8_t *Data);
9313498266Sopenharmony_cistatic void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
9413498266Sopenharmony_ci                                    bool Aborted, uint64_t AbortError);
9513498266Sopenharmony_cistatic void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
9613498266Sopenharmony_ci                                             void *IfContext);
9713498266Sopenharmony_cistatic void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
9813498266Sopenharmony_ci                                     void *IfContext, void *SendContext);
9913498266Sopenharmony_ci
10013498266Sopenharmony_ci
10113498266Sopenharmony_civoid Curl_msh3_ver(char *p, size_t len)
10213498266Sopenharmony_ci{
10313498266Sopenharmony_ci  uint32_t v[4];
10413498266Sopenharmony_ci  MsH3Version(v);
10513498266Sopenharmony_ci  (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]);
10613498266Sopenharmony_ci}
10713498266Sopenharmony_ci
10813498266Sopenharmony_ci#define SP_LOCAL   0
10913498266Sopenharmony_ci#define SP_REMOTE  1
11013498266Sopenharmony_ci
11113498266Sopenharmony_cistruct cf_msh3_ctx {
11213498266Sopenharmony_ci  MSH3_API *api;
11313498266Sopenharmony_ci  MSH3_CONNECTION *qconn;
11413498266Sopenharmony_ci  struct Curl_sockaddr_ex addr;
11513498266Sopenharmony_ci  curl_socket_t sock[2]; /* fake socket pair until we get support in msh3 */
11613498266Sopenharmony_ci  char l_ip[MAX_IPADR_LEN];          /* local IP as string */
11713498266Sopenharmony_ci  int l_port;                        /* local port number */
11813498266Sopenharmony_ci  struct cf_call_data call_data;
11913498266Sopenharmony_ci  struct curltime connect_started;   /* time the current attempt started */
12013498266Sopenharmony_ci  struct curltime handshake_at;      /* time connect handshake finished */
12113498266Sopenharmony_ci  /* Flags written by msh3/msquic thread */
12213498266Sopenharmony_ci  bool handshake_complete;
12313498266Sopenharmony_ci  bool handshake_succeeded;
12413498266Sopenharmony_ci  bool connected;
12513498266Sopenharmony_ci  /* Flags written by curl thread */
12613498266Sopenharmony_ci  BIT(verbose);
12713498266Sopenharmony_ci  BIT(active);
12813498266Sopenharmony_ci};
12913498266Sopenharmony_ci
13013498266Sopenharmony_ci/* How to access `call_data` from a cf_msh3 filter */
13113498266Sopenharmony_ci#undef CF_CTX_CALL_DATA
13213498266Sopenharmony_ci#define CF_CTX_CALL_DATA(cf)  \
13313498266Sopenharmony_ci  ((struct cf_msh3_ctx *)(cf)->ctx)->call_data
13413498266Sopenharmony_ci
13513498266Sopenharmony_ci/**
13613498266Sopenharmony_ci * All about the H3 internals of a stream
13713498266Sopenharmony_ci */
13813498266Sopenharmony_cistruct stream_ctx {
13913498266Sopenharmony_ci  struct MSH3_REQUEST *req;
14013498266Sopenharmony_ci  struct bufq recvbuf;   /* h3 response */
14113498266Sopenharmony_ci#ifdef _WIN32
14213498266Sopenharmony_ci  CRITICAL_SECTION recv_lock;
14313498266Sopenharmony_ci#else /* !_WIN32 */
14413498266Sopenharmony_ci  pthread_mutex_t recv_lock;
14513498266Sopenharmony_ci#endif /* _WIN32 */
14613498266Sopenharmony_ci  uint64_t error3; /* HTTP/3 stream error code */
14713498266Sopenharmony_ci  int status_code; /* HTTP status code */
14813498266Sopenharmony_ci  CURLcode recv_error;
14913498266Sopenharmony_ci  bool closed;
15013498266Sopenharmony_ci  bool reset;
15113498266Sopenharmony_ci  bool upload_done;
15213498266Sopenharmony_ci  bool firstheader;  /* FALSE until headers arrive */
15313498266Sopenharmony_ci  bool recv_header_complete;
15413498266Sopenharmony_ci};
15513498266Sopenharmony_ci
15613498266Sopenharmony_ci#define H3_STREAM_CTX(d)    ((struct stream_ctx *)(((d) && (d)->req.p.http)? \
15713498266Sopenharmony_ci                             ((struct HTTP *)(d)->req.p.http)->h3_ctx \
15813498266Sopenharmony_ci                               : NULL))
15913498266Sopenharmony_ci#define H3_STREAM_LCTX(d)   ((struct HTTP *)(d)->req.p.http)->h3_ctx
16013498266Sopenharmony_ci#define H3_STREAM_ID(d)     (H3_STREAM_CTX(d)? \
16113498266Sopenharmony_ci                             H3_STREAM_CTX(d)->id : -2)
16213498266Sopenharmony_ci
16313498266Sopenharmony_ci
16413498266Sopenharmony_cistatic CURLcode h3_data_setup(struct Curl_cfilter *cf,
16513498266Sopenharmony_ci                              struct Curl_easy *data)
16613498266Sopenharmony_ci{
16713498266Sopenharmony_ci  struct stream_ctx *stream = H3_STREAM_CTX(data);
16813498266Sopenharmony_ci
16913498266Sopenharmony_ci  if(stream)
17013498266Sopenharmony_ci    return CURLE_OK;
17113498266Sopenharmony_ci
17213498266Sopenharmony_ci  stream = calloc(1, sizeof(*stream));
17313498266Sopenharmony_ci  if(!stream)
17413498266Sopenharmony_ci    return CURLE_OUT_OF_MEMORY;
17513498266Sopenharmony_ci
17613498266Sopenharmony_ci  H3_STREAM_LCTX(data) = stream;
17713498266Sopenharmony_ci  stream->req = ZERO_NULL;
17813498266Sopenharmony_ci  msh3_lock_initialize(&stream->recv_lock);
17913498266Sopenharmony_ci  Curl_bufq_init2(&stream->recvbuf, H3_STREAM_CHUNK_SIZE,
18013498266Sopenharmony_ci                  H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
18113498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "data setup");
18213498266Sopenharmony_ci  return CURLE_OK;
18313498266Sopenharmony_ci}
18413498266Sopenharmony_ci
18513498266Sopenharmony_cistatic void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
18613498266Sopenharmony_ci{
18713498266Sopenharmony_ci  struct stream_ctx *stream = H3_STREAM_CTX(data);
18813498266Sopenharmony_ci
18913498266Sopenharmony_ci  (void)cf;
19013498266Sopenharmony_ci  if(stream) {
19113498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "easy handle is done");
19213498266Sopenharmony_ci    Curl_bufq_free(&stream->recvbuf);
19313498266Sopenharmony_ci    free(stream);
19413498266Sopenharmony_ci    H3_STREAM_LCTX(data) = NULL;
19513498266Sopenharmony_ci  }
19613498266Sopenharmony_ci}
19713498266Sopenharmony_ci
19813498266Sopenharmony_cistatic void drain_stream_from_other_thread(struct Curl_easy *data,
19913498266Sopenharmony_ci                                           struct stream_ctx *stream)
20013498266Sopenharmony_ci{
20113498266Sopenharmony_ci  unsigned char bits;
20213498266Sopenharmony_ci
20313498266Sopenharmony_ci  /* risky */
20413498266Sopenharmony_ci  bits = CURL_CSELECT_IN;
20513498266Sopenharmony_ci  if(stream && !stream->upload_done)
20613498266Sopenharmony_ci    bits |= CURL_CSELECT_OUT;
20713498266Sopenharmony_ci  if(data->state.select_bits != bits) {
20813498266Sopenharmony_ci    data->state.select_bits = bits;
20913498266Sopenharmony_ci    /* cannot expire from other thread */
21013498266Sopenharmony_ci  }
21113498266Sopenharmony_ci}
21213498266Sopenharmony_ci
21313498266Sopenharmony_cistatic void drain_stream(struct Curl_cfilter *cf,
21413498266Sopenharmony_ci                         struct Curl_easy *data)
21513498266Sopenharmony_ci{
21613498266Sopenharmony_ci  struct stream_ctx *stream = H3_STREAM_CTX(data);
21713498266Sopenharmony_ci  unsigned char bits;
21813498266Sopenharmony_ci
21913498266Sopenharmony_ci  (void)cf;
22013498266Sopenharmony_ci  bits = CURL_CSELECT_IN;
22113498266Sopenharmony_ci  if(stream && !stream->upload_done)
22213498266Sopenharmony_ci    bits |= CURL_CSELECT_OUT;
22313498266Sopenharmony_ci  if(data->state.select_bits != bits) {
22413498266Sopenharmony_ci    data->state.select_bits = bits;
22513498266Sopenharmony_ci    Curl_expire(data, 0, EXPIRE_RUN_NOW);
22613498266Sopenharmony_ci  }
22713498266Sopenharmony_ci}
22813498266Sopenharmony_ci
22913498266Sopenharmony_cistatic const MSH3_CONNECTION_IF msh3_conn_if = {
23013498266Sopenharmony_ci  msh3_conn_connected,
23113498266Sopenharmony_ci  msh3_conn_shutdown_complete,
23213498266Sopenharmony_ci  msh3_conn_new_request
23313498266Sopenharmony_ci};
23413498266Sopenharmony_ci
23513498266Sopenharmony_cistatic void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection,
23613498266Sopenharmony_ci                                          void *IfContext)
23713498266Sopenharmony_ci{
23813498266Sopenharmony_ci  struct Curl_cfilter *cf = IfContext;
23913498266Sopenharmony_ci  struct cf_msh3_ctx *ctx = cf->ctx;
24013498266Sopenharmony_ci  struct Curl_easy *data = CF_DATA_CURRENT(cf);
24113498266Sopenharmony_ci  (void)Connection;
24213498266Sopenharmony_ci
24313498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "[MSH3] connected");
24413498266Sopenharmony_ci  ctx->handshake_succeeded = true;
24513498266Sopenharmony_ci  ctx->connected = true;
24613498266Sopenharmony_ci  ctx->handshake_complete = true;
24713498266Sopenharmony_ci}
24813498266Sopenharmony_ci
24913498266Sopenharmony_cistatic void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection,
25013498266Sopenharmony_ci                                          void *IfContext)
25113498266Sopenharmony_ci{
25213498266Sopenharmony_ci  struct Curl_cfilter *cf = IfContext;
25313498266Sopenharmony_ci  struct cf_msh3_ctx *ctx = cf->ctx;
25413498266Sopenharmony_ci  struct Curl_easy *data = CF_DATA_CURRENT(cf);
25513498266Sopenharmony_ci
25613498266Sopenharmony_ci  (void)Connection;
25713498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "[MSH3] shutdown complete");
25813498266Sopenharmony_ci  ctx->connected = false;
25913498266Sopenharmony_ci  ctx->handshake_complete = true;
26013498266Sopenharmony_ci}
26113498266Sopenharmony_ci
26213498266Sopenharmony_cistatic void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection,
26313498266Sopenharmony_ci                                          void *IfContext,
26413498266Sopenharmony_ci                                          MSH3_REQUEST *Request)
26513498266Sopenharmony_ci{
26613498266Sopenharmony_ci  (void)Connection;
26713498266Sopenharmony_ci  (void)IfContext;
26813498266Sopenharmony_ci  (void)Request;
26913498266Sopenharmony_ci}
27013498266Sopenharmony_ci
27113498266Sopenharmony_cistatic const MSH3_REQUEST_IF msh3_request_if = {
27213498266Sopenharmony_ci  msh3_header_received,
27313498266Sopenharmony_ci  msh3_data_received,
27413498266Sopenharmony_ci  msh3_complete,
27513498266Sopenharmony_ci  msh3_shutdown_complete,
27613498266Sopenharmony_ci  msh3_data_sent
27713498266Sopenharmony_ci};
27813498266Sopenharmony_ci
27913498266Sopenharmony_ci/* Decode HTTP status code.  Returns -1 if no valid status code was
28013498266Sopenharmony_ci   decoded. (duplicate from http2.c) */
28113498266Sopenharmony_cistatic int decode_status_code(const char *value, size_t len)
28213498266Sopenharmony_ci{
28313498266Sopenharmony_ci  int i;
28413498266Sopenharmony_ci  int res;
28513498266Sopenharmony_ci
28613498266Sopenharmony_ci  if(len != 3) {
28713498266Sopenharmony_ci    return -1;
28813498266Sopenharmony_ci  }
28913498266Sopenharmony_ci
29013498266Sopenharmony_ci  res = 0;
29113498266Sopenharmony_ci
29213498266Sopenharmony_ci  for(i = 0; i < 3; ++i) {
29313498266Sopenharmony_ci    char c = value[i];
29413498266Sopenharmony_ci
29513498266Sopenharmony_ci    if(c < '0' || c > '9') {
29613498266Sopenharmony_ci      return -1;
29713498266Sopenharmony_ci    }
29813498266Sopenharmony_ci
29913498266Sopenharmony_ci    res *= 10;
30013498266Sopenharmony_ci    res += c - '0';
30113498266Sopenharmony_ci  }
30213498266Sopenharmony_ci
30313498266Sopenharmony_ci  return res;
30413498266Sopenharmony_ci}
30513498266Sopenharmony_ci
30613498266Sopenharmony_ci/*
30713498266Sopenharmony_ci * write_resp_raw() copies response data in raw format to the `data`'s
30813498266Sopenharmony_ci  * receive buffer. If not enough space is available, it appends to the
30913498266Sopenharmony_ci * `data`'s overflow buffer.
31013498266Sopenharmony_ci */
31113498266Sopenharmony_cistatic CURLcode write_resp_raw(struct Curl_easy *data,
31213498266Sopenharmony_ci                               const void *mem, size_t memlen)
31313498266Sopenharmony_ci{
31413498266Sopenharmony_ci  struct stream_ctx *stream = H3_STREAM_CTX(data);
31513498266Sopenharmony_ci  CURLcode result = CURLE_OK;
31613498266Sopenharmony_ci  ssize_t nwritten;
31713498266Sopenharmony_ci
31813498266Sopenharmony_ci  if(!stream)
31913498266Sopenharmony_ci    return CURLE_RECV_ERROR;
32013498266Sopenharmony_ci
32113498266Sopenharmony_ci  nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result);
32213498266Sopenharmony_ci  if(nwritten < 0) {
32313498266Sopenharmony_ci    return result;
32413498266Sopenharmony_ci  }
32513498266Sopenharmony_ci
32613498266Sopenharmony_ci  if((size_t)nwritten < memlen) {
32713498266Sopenharmony_ci    /* This MUST not happen. Our recbuf is dimensioned to hold the
32813498266Sopenharmony_ci     * full max_stream_window and then some for this very reason. */
32913498266Sopenharmony_ci    DEBUGASSERT(0);
33013498266Sopenharmony_ci    return CURLE_RECV_ERROR;
33113498266Sopenharmony_ci  }
33213498266Sopenharmony_ci  return result;
33313498266Sopenharmony_ci}
33413498266Sopenharmony_ci
33513498266Sopenharmony_cistatic void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
33613498266Sopenharmony_ci                                           void *userp,
33713498266Sopenharmony_ci                                           const MSH3_HEADER *hd)
33813498266Sopenharmony_ci{
33913498266Sopenharmony_ci  struct Curl_easy *data = userp;
34013498266Sopenharmony_ci  struct stream_ctx *stream = H3_STREAM_CTX(data);
34113498266Sopenharmony_ci  CURLcode result;
34213498266Sopenharmony_ci  (void)Request;
34313498266Sopenharmony_ci
34413498266Sopenharmony_ci  if(!stream || stream->recv_header_complete) {
34513498266Sopenharmony_ci    return;
34613498266Sopenharmony_ci  }
34713498266Sopenharmony_ci
34813498266Sopenharmony_ci  msh3_lock_acquire(&stream->recv_lock);
34913498266Sopenharmony_ci
35013498266Sopenharmony_ci  if((hd->NameLength == 7) &&
35113498266Sopenharmony_ci     !strncmp(HTTP_PSEUDO_STATUS, (char *)hd->Name, 7)) {
35213498266Sopenharmony_ci    char line[14]; /* status line is always 13 characters long */
35313498266Sopenharmony_ci    size_t ncopy;
35413498266Sopenharmony_ci
35513498266Sopenharmony_ci    DEBUGASSERT(!stream->firstheader);
35613498266Sopenharmony_ci    stream->status_code = decode_status_code(hd->Value, hd->ValueLength);
35713498266Sopenharmony_ci    DEBUGASSERT(stream->status_code != -1);
35813498266Sopenharmony_ci    ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
35913498266Sopenharmony_ci                      stream->status_code);
36013498266Sopenharmony_ci    result = write_resp_raw(data, line, ncopy);
36113498266Sopenharmony_ci    if(result)
36213498266Sopenharmony_ci      stream->recv_error = result;
36313498266Sopenharmony_ci    stream->firstheader = TRUE;
36413498266Sopenharmony_ci  }
36513498266Sopenharmony_ci  else {
36613498266Sopenharmony_ci    /* store as an HTTP1-style header */
36713498266Sopenharmony_ci    DEBUGASSERT(stream->firstheader);
36813498266Sopenharmony_ci    result = write_resp_raw(data, hd->Name, hd->NameLength);
36913498266Sopenharmony_ci    if(!result)
37013498266Sopenharmony_ci      result = write_resp_raw(data, ": ", 2);
37113498266Sopenharmony_ci    if(!result)
37213498266Sopenharmony_ci      result = write_resp_raw(data, hd->Value, hd->ValueLength);
37313498266Sopenharmony_ci    if(!result)
37413498266Sopenharmony_ci      result = write_resp_raw(data, "\r\n", 2);
37513498266Sopenharmony_ci    if(result) {
37613498266Sopenharmony_ci      stream->recv_error = result;
37713498266Sopenharmony_ci    }
37813498266Sopenharmony_ci  }
37913498266Sopenharmony_ci
38013498266Sopenharmony_ci  drain_stream_from_other_thread(data, stream);
38113498266Sopenharmony_ci  msh3_lock_release(&stream->recv_lock);
38213498266Sopenharmony_ci}
38313498266Sopenharmony_ci
38413498266Sopenharmony_cistatic bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
38513498266Sopenharmony_ci                                         void *IfContext, uint32_t *buflen,
38613498266Sopenharmony_ci                                         const uint8_t *buf)
38713498266Sopenharmony_ci{
38813498266Sopenharmony_ci  struct Curl_easy *data = IfContext;
38913498266Sopenharmony_ci  struct stream_ctx *stream = H3_STREAM_CTX(data);
39013498266Sopenharmony_ci  CURLcode result;
39113498266Sopenharmony_ci  bool rv = FALSE;
39213498266Sopenharmony_ci
39313498266Sopenharmony_ci  /* TODO: we would like to limit the amount of data we are buffer here.
39413498266Sopenharmony_ci   * There seems to be no mechanism in msh3 to adjust flow control and
39513498266Sopenharmony_ci   * it is undocumented what happens if we return FALSE here or less
39613498266Sopenharmony_ci   * length (buflen is an inout parameter).
39713498266Sopenharmony_ci   */
39813498266Sopenharmony_ci  (void)Request;
39913498266Sopenharmony_ci  if(!stream)
40013498266Sopenharmony_ci    return FALSE;
40113498266Sopenharmony_ci
40213498266Sopenharmony_ci  msh3_lock_acquire(&stream->recv_lock);
40313498266Sopenharmony_ci
40413498266Sopenharmony_ci  if(!stream->recv_header_complete) {
40513498266Sopenharmony_ci    result = write_resp_raw(data, "\r\n", 2);
40613498266Sopenharmony_ci    if(result) {
40713498266Sopenharmony_ci      stream->recv_error = result;
40813498266Sopenharmony_ci      goto out;
40913498266Sopenharmony_ci    }
41013498266Sopenharmony_ci    stream->recv_header_complete = true;
41113498266Sopenharmony_ci  }
41213498266Sopenharmony_ci
41313498266Sopenharmony_ci  result = write_resp_raw(data, buf, *buflen);
41413498266Sopenharmony_ci  if(result) {
41513498266Sopenharmony_ci    stream->recv_error = result;
41613498266Sopenharmony_ci  }
41713498266Sopenharmony_ci  rv = TRUE;
41813498266Sopenharmony_ci
41913498266Sopenharmony_ciout:
42013498266Sopenharmony_ci  msh3_lock_release(&stream->recv_lock);
42113498266Sopenharmony_ci  return rv;
42213498266Sopenharmony_ci}
42313498266Sopenharmony_ci
42413498266Sopenharmony_cistatic void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
42513498266Sopenharmony_ci                                    bool aborted, uint64_t error)
42613498266Sopenharmony_ci{
42713498266Sopenharmony_ci  struct Curl_easy *data = IfContext;
42813498266Sopenharmony_ci  struct stream_ctx *stream = H3_STREAM_CTX(data);
42913498266Sopenharmony_ci
43013498266Sopenharmony_ci  (void)Request;
43113498266Sopenharmony_ci  if(!stream)
43213498266Sopenharmony_ci    return;
43313498266Sopenharmony_ci  msh3_lock_acquire(&stream->recv_lock);
43413498266Sopenharmony_ci  stream->closed = TRUE;
43513498266Sopenharmony_ci  stream->recv_header_complete = true;
43613498266Sopenharmony_ci  if(error)
43713498266Sopenharmony_ci    stream->error3 = error;
43813498266Sopenharmony_ci  if(aborted)
43913498266Sopenharmony_ci    stream->reset = TRUE;
44013498266Sopenharmony_ci  msh3_lock_release(&stream->recv_lock);
44113498266Sopenharmony_ci}
44213498266Sopenharmony_ci
44313498266Sopenharmony_cistatic void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
44413498266Sopenharmony_ci                                             void *IfContext)
44513498266Sopenharmony_ci{
44613498266Sopenharmony_ci  struct Curl_easy *data = IfContext;
44713498266Sopenharmony_ci  struct stream_ctx *stream = H3_STREAM_CTX(data);
44813498266Sopenharmony_ci
44913498266Sopenharmony_ci  if(!stream)
45013498266Sopenharmony_ci    return;
45113498266Sopenharmony_ci  (void)Request;
45213498266Sopenharmony_ci  (void)stream;
45313498266Sopenharmony_ci}
45413498266Sopenharmony_ci
45513498266Sopenharmony_cistatic void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
45613498266Sopenharmony_ci                                     void *IfContext, void *SendContext)
45713498266Sopenharmony_ci{
45813498266Sopenharmony_ci  struct Curl_easy *data = IfContext;
45913498266Sopenharmony_ci  struct stream_ctx *stream = H3_STREAM_CTX(data);
46013498266Sopenharmony_ci  if(!stream)
46113498266Sopenharmony_ci    return;
46213498266Sopenharmony_ci  (void)Request;
46313498266Sopenharmony_ci  (void)stream;
46413498266Sopenharmony_ci  (void)SendContext;
46513498266Sopenharmony_ci}
46613498266Sopenharmony_ci
46713498266Sopenharmony_cistatic ssize_t recv_closed_stream(struct Curl_cfilter *cf,
46813498266Sopenharmony_ci                                  struct Curl_easy *data,
46913498266Sopenharmony_ci                                  CURLcode *err)
47013498266Sopenharmony_ci{
47113498266Sopenharmony_ci  struct stream_ctx *stream = H3_STREAM_CTX(data);
47213498266Sopenharmony_ci  ssize_t nread = -1;
47313498266Sopenharmony_ci
47413498266Sopenharmony_ci  if(!stream) {
47513498266Sopenharmony_ci    *err = CURLE_RECV_ERROR;
47613498266Sopenharmony_ci    return -1;
47713498266Sopenharmony_ci  }
47813498266Sopenharmony_ci  (void)cf;
47913498266Sopenharmony_ci  if(stream->reset) {
48013498266Sopenharmony_ci    failf(data, "HTTP/3 stream reset by server");
48113498266Sopenharmony_ci    *err = CURLE_PARTIAL_FILE;
48213498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "cf_recv, was reset -> %d", *err);
48313498266Sopenharmony_ci    goto out;
48413498266Sopenharmony_ci  }
48513498266Sopenharmony_ci  else if(stream->error3) {
48613498266Sopenharmony_ci    failf(data, "HTTP/3 stream was not closed cleanly: (error %zd)",
48713498266Sopenharmony_ci          (ssize_t)stream->error3);
48813498266Sopenharmony_ci    *err = CURLE_HTTP3;
48913498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "cf_recv, closed uncleanly -> %d", *err);
49013498266Sopenharmony_ci    goto out;
49113498266Sopenharmony_ci  }
49213498266Sopenharmony_ci  else {
49313498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "cf_recv, closed ok -> %d", *err);
49413498266Sopenharmony_ci  }
49513498266Sopenharmony_ci  *err = CURLE_OK;
49613498266Sopenharmony_ci  nread = 0;
49713498266Sopenharmony_ci
49813498266Sopenharmony_ciout:
49913498266Sopenharmony_ci  return nread;
50013498266Sopenharmony_ci}
50113498266Sopenharmony_ci
50213498266Sopenharmony_cistatic void set_quic_expire(struct Curl_cfilter *cf, struct Curl_easy *data)
50313498266Sopenharmony_ci{
50413498266Sopenharmony_ci  struct stream_ctx *stream = H3_STREAM_CTX(data);
50513498266Sopenharmony_ci
50613498266Sopenharmony_ci  /* we have no indication from msh3 when it would be a good time
50713498266Sopenharmony_ci   * to juggle the connection again. So, we compromise by calling
50813498266Sopenharmony_ci   * us again every some milliseconds. */
50913498266Sopenharmony_ci  (void)cf;
51013498266Sopenharmony_ci  if(stream && stream->req && !stream->closed) {
51113498266Sopenharmony_ci    Curl_expire(data, 10, EXPIRE_QUIC);
51213498266Sopenharmony_ci  }
51313498266Sopenharmony_ci  else {
51413498266Sopenharmony_ci    Curl_expire(data, 50, EXPIRE_QUIC);
51513498266Sopenharmony_ci  }
51613498266Sopenharmony_ci}
51713498266Sopenharmony_ci
51813498266Sopenharmony_cistatic ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
51913498266Sopenharmony_ci                            char *buf, size_t len, CURLcode *err)
52013498266Sopenharmony_ci{
52113498266Sopenharmony_ci  struct stream_ctx *stream = H3_STREAM_CTX(data);
52213498266Sopenharmony_ci  ssize_t nread = -1;
52313498266Sopenharmony_ci  struct cf_call_data save;
52413498266Sopenharmony_ci
52513498266Sopenharmony_ci  (void)cf;
52613498266Sopenharmony_ci  if(!stream) {
52713498266Sopenharmony_ci    *err = CURLE_RECV_ERROR;
52813498266Sopenharmony_ci    return -1;
52913498266Sopenharmony_ci  }
53013498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
53113498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "req: recv with %zu byte buffer", len);
53213498266Sopenharmony_ci
53313498266Sopenharmony_ci  msh3_lock_acquire(&stream->recv_lock);
53413498266Sopenharmony_ci
53513498266Sopenharmony_ci  if(stream->recv_error) {
53613498266Sopenharmony_ci    failf(data, "request aborted");
53713498266Sopenharmony_ci    *err = stream->recv_error;
53813498266Sopenharmony_ci    goto out;
53913498266Sopenharmony_ci  }
54013498266Sopenharmony_ci
54113498266Sopenharmony_ci  *err = CURLE_OK;
54213498266Sopenharmony_ci
54313498266Sopenharmony_ci  if(!Curl_bufq_is_empty(&stream->recvbuf)) {
54413498266Sopenharmony_ci    nread = Curl_bufq_read(&stream->recvbuf,
54513498266Sopenharmony_ci                           (unsigned char *)buf, len, err);
54613498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "read recvbuf(len=%zu) -> %zd, %d",
54713498266Sopenharmony_ci                len, nread, *err);
54813498266Sopenharmony_ci    if(nread < 0)
54913498266Sopenharmony_ci      goto out;
55013498266Sopenharmony_ci    if(stream->closed)
55113498266Sopenharmony_ci      drain_stream(cf, data);
55213498266Sopenharmony_ci  }
55313498266Sopenharmony_ci  else if(stream->closed) {
55413498266Sopenharmony_ci    nread = recv_closed_stream(cf, data, err);
55513498266Sopenharmony_ci    goto out;
55613498266Sopenharmony_ci  }
55713498266Sopenharmony_ci  else {
55813498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "req: nothing here, call again");
55913498266Sopenharmony_ci    *err = CURLE_AGAIN;
56013498266Sopenharmony_ci  }
56113498266Sopenharmony_ci
56213498266Sopenharmony_ciout:
56313498266Sopenharmony_ci  msh3_lock_release(&stream->recv_lock);
56413498266Sopenharmony_ci  set_quic_expire(cf, data);
56513498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
56613498266Sopenharmony_ci  return nread;
56713498266Sopenharmony_ci}
56813498266Sopenharmony_ci
56913498266Sopenharmony_cistatic ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
57013498266Sopenharmony_ci                            const void *buf, size_t len, CURLcode *err)
57113498266Sopenharmony_ci{
57213498266Sopenharmony_ci  struct cf_msh3_ctx *ctx = cf->ctx;
57313498266Sopenharmony_ci  struct stream_ctx *stream = H3_STREAM_CTX(data);
57413498266Sopenharmony_ci  struct h1_req_parser h1;
57513498266Sopenharmony_ci  struct dynhds h2_headers;
57613498266Sopenharmony_ci  MSH3_HEADER *nva = NULL;
57713498266Sopenharmony_ci  size_t nheader, i;
57813498266Sopenharmony_ci  ssize_t nwritten = -1;
57913498266Sopenharmony_ci  struct cf_call_data save;
58013498266Sopenharmony_ci  bool eos;
58113498266Sopenharmony_ci
58213498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
58313498266Sopenharmony_ci
58413498266Sopenharmony_ci  Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
58513498266Sopenharmony_ci  Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
58613498266Sopenharmony_ci
58713498266Sopenharmony_ci  /* Sizes must match for cast below to work" */
58813498266Sopenharmony_ci  DEBUGASSERT(stream);
58913498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "req: send %zu bytes", len);
59013498266Sopenharmony_ci
59113498266Sopenharmony_ci  if(!stream->req) {
59213498266Sopenharmony_ci    /* The first send on the request contains the headers and possibly some
59313498266Sopenharmony_ci       data. Parse out the headers and create the request, then if there is
59413498266Sopenharmony_ci       any data left over go ahead and send it too. */
59513498266Sopenharmony_ci    nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err);
59613498266Sopenharmony_ci    if(nwritten < 0)
59713498266Sopenharmony_ci      goto out;
59813498266Sopenharmony_ci    DEBUGASSERT(h1.done);
59913498266Sopenharmony_ci    DEBUGASSERT(h1.req);
60013498266Sopenharmony_ci
60113498266Sopenharmony_ci    *err = Curl_http_req_to_h2(&h2_headers, h1.req, data);
60213498266Sopenharmony_ci    if(*err) {
60313498266Sopenharmony_ci      nwritten = -1;
60413498266Sopenharmony_ci      goto out;
60513498266Sopenharmony_ci    }
60613498266Sopenharmony_ci
60713498266Sopenharmony_ci    nheader = Curl_dynhds_count(&h2_headers);
60813498266Sopenharmony_ci    nva = malloc(sizeof(MSH3_HEADER) * nheader);
60913498266Sopenharmony_ci    if(!nva) {
61013498266Sopenharmony_ci      *err = CURLE_OUT_OF_MEMORY;
61113498266Sopenharmony_ci      nwritten = -1;
61213498266Sopenharmony_ci      goto out;
61313498266Sopenharmony_ci    }
61413498266Sopenharmony_ci
61513498266Sopenharmony_ci    for(i = 0; i < nheader; ++i) {
61613498266Sopenharmony_ci      struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
61713498266Sopenharmony_ci      nva[i].Name = e->name;
61813498266Sopenharmony_ci      nva[i].NameLength = e->namelen;
61913498266Sopenharmony_ci      nva[i].Value = e->value;
62013498266Sopenharmony_ci      nva[i].ValueLength = e->valuelen;
62113498266Sopenharmony_ci    }
62213498266Sopenharmony_ci
62313498266Sopenharmony_ci    switch(data->state.httpreq) {
62413498266Sopenharmony_ci    case HTTPREQ_POST:
62513498266Sopenharmony_ci    case HTTPREQ_POST_FORM:
62613498266Sopenharmony_ci    case HTTPREQ_POST_MIME:
62713498266Sopenharmony_ci    case HTTPREQ_PUT:
62813498266Sopenharmony_ci      /* known request body size or -1 */
62913498266Sopenharmony_ci      eos = FALSE;
63013498266Sopenharmony_ci      break;
63113498266Sopenharmony_ci    default:
63213498266Sopenharmony_ci      /* there is not request body */
63313498266Sopenharmony_ci      eos = TRUE;
63413498266Sopenharmony_ci      stream->upload_done = TRUE;
63513498266Sopenharmony_ci      break;
63613498266Sopenharmony_ci    }
63713498266Sopenharmony_ci
63813498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "req: send %zu headers", nheader);
63913498266Sopenharmony_ci    stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data,
64013498266Sopenharmony_ci                                  nva, nheader,
64113498266Sopenharmony_ci                                  eos ? MSH3_REQUEST_FLAG_FIN :
64213498266Sopenharmony_ci                                  MSH3_REQUEST_FLAG_NONE);
64313498266Sopenharmony_ci    if(!stream->req) {
64413498266Sopenharmony_ci      failf(data, "request open failed");
64513498266Sopenharmony_ci      *err = CURLE_SEND_ERROR;
64613498266Sopenharmony_ci      goto out;
64713498266Sopenharmony_ci    }
64813498266Sopenharmony_ci    *err = CURLE_OK;
64913498266Sopenharmony_ci    nwritten = len;
65013498266Sopenharmony_ci    goto out;
65113498266Sopenharmony_ci  }
65213498266Sopenharmony_ci  else {
65313498266Sopenharmony_ci    /* request is open */
65413498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "req: send %zu body bytes", len);
65513498266Sopenharmony_ci    if(len > 0xFFFFFFFF) {
65613498266Sopenharmony_ci      len = 0xFFFFFFFF;
65713498266Sopenharmony_ci    }
65813498266Sopenharmony_ci
65913498266Sopenharmony_ci    if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_NONE, buf,
66013498266Sopenharmony_ci                        (uint32_t)len, stream)) {
66113498266Sopenharmony_ci      *err = CURLE_SEND_ERROR;
66213498266Sopenharmony_ci      goto out;
66313498266Sopenharmony_ci    }
66413498266Sopenharmony_ci
66513498266Sopenharmony_ci    /* TODO - msh3/msquic will hold onto this memory until the send complete
66613498266Sopenharmony_ci       event. How do we make sure curl doesn't free it until then? */
66713498266Sopenharmony_ci    *err = CURLE_OK;
66813498266Sopenharmony_ci    nwritten = len;
66913498266Sopenharmony_ci  }
67013498266Sopenharmony_ci
67113498266Sopenharmony_ciout:
67213498266Sopenharmony_ci  set_quic_expire(cf, data);
67313498266Sopenharmony_ci  free(nva);
67413498266Sopenharmony_ci  Curl_h1_req_parse_free(&h1);
67513498266Sopenharmony_ci  Curl_dynhds_free(&h2_headers);
67613498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
67713498266Sopenharmony_ci  return nwritten;
67813498266Sopenharmony_ci}
67913498266Sopenharmony_ci
68013498266Sopenharmony_cistatic void cf_msh3_adjust_pollset(struct Curl_cfilter *cf,
68113498266Sopenharmony_ci                                   struct Curl_easy *data,
68213498266Sopenharmony_ci                                   struct easy_pollset *ps)
68313498266Sopenharmony_ci{
68413498266Sopenharmony_ci  struct cf_msh3_ctx *ctx = cf->ctx;
68513498266Sopenharmony_ci  struct stream_ctx *stream = H3_STREAM_CTX(data);
68613498266Sopenharmony_ci  struct cf_call_data save;
68713498266Sopenharmony_ci
68813498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
68913498266Sopenharmony_ci  if(stream && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
69013498266Sopenharmony_ci    if(stream->recv_error) {
69113498266Sopenharmony_ci      Curl_pollset_add_in(data, ps, ctx->sock[SP_LOCAL]);
69213498266Sopenharmony_ci      drain_stream(cf, data);
69313498266Sopenharmony_ci    }
69413498266Sopenharmony_ci    else if(stream->req) {
69513498266Sopenharmony_ci      Curl_pollset_add_out(data, ps, ctx->sock[SP_LOCAL]);
69613498266Sopenharmony_ci      drain_stream(cf, data);
69713498266Sopenharmony_ci    }
69813498266Sopenharmony_ci  }
69913498266Sopenharmony_ci}
70013498266Sopenharmony_ci
70113498266Sopenharmony_cistatic bool cf_msh3_data_pending(struct Curl_cfilter *cf,
70213498266Sopenharmony_ci                                 const struct Curl_easy *data)
70313498266Sopenharmony_ci{
70413498266Sopenharmony_ci  struct stream_ctx *stream = H3_STREAM_CTX(data);
70513498266Sopenharmony_ci  struct cf_call_data save;
70613498266Sopenharmony_ci  bool pending = FALSE;
70713498266Sopenharmony_ci
70813498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
70913498266Sopenharmony_ci
71013498266Sopenharmony_ci  (void)cf;
71113498266Sopenharmony_ci  if(stream && stream->req) {
71213498266Sopenharmony_ci    msh3_lock_acquire(&stream->recv_lock);
71313498266Sopenharmony_ci    CURL_TRC_CF((struct Curl_easy *)data, cf, "data pending = %zu",
71413498266Sopenharmony_ci                Curl_bufq_len(&stream->recvbuf));
71513498266Sopenharmony_ci    pending = !Curl_bufq_is_empty(&stream->recvbuf);
71613498266Sopenharmony_ci    msh3_lock_release(&stream->recv_lock);
71713498266Sopenharmony_ci    if(pending)
71813498266Sopenharmony_ci      drain_stream(cf, (struct Curl_easy *)data);
71913498266Sopenharmony_ci  }
72013498266Sopenharmony_ci
72113498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
72213498266Sopenharmony_ci  return pending;
72313498266Sopenharmony_ci}
72413498266Sopenharmony_ci
72513498266Sopenharmony_cistatic void cf_msh3_active(struct Curl_cfilter *cf, struct Curl_easy *data)
72613498266Sopenharmony_ci{
72713498266Sopenharmony_ci  struct cf_msh3_ctx *ctx = cf->ctx;
72813498266Sopenharmony_ci
72913498266Sopenharmony_ci  /* use this socket from now on */
73013498266Sopenharmony_ci  cf->conn->sock[cf->sockindex] = ctx->sock[SP_LOCAL];
73113498266Sopenharmony_ci  /* the first socket info gets set at conn and data */
73213498266Sopenharmony_ci  if(cf->sockindex == FIRSTSOCKET) {
73313498266Sopenharmony_ci    cf->conn->remote_addr = &ctx->addr;
73413498266Sopenharmony_ci  #ifdef ENABLE_IPV6
73513498266Sopenharmony_ci    cf->conn->bits.ipv6 = (ctx->addr.family == AF_INET6)? TRUE : FALSE;
73613498266Sopenharmony_ci  #endif
73713498266Sopenharmony_ci    Curl_persistconninfo(data, cf->conn, ctx->l_ip, ctx->l_port);
73813498266Sopenharmony_ci  }
73913498266Sopenharmony_ci  ctx->active = TRUE;
74013498266Sopenharmony_ci}
74113498266Sopenharmony_ci
74213498266Sopenharmony_cistatic CURLcode h3_data_pause(struct Curl_cfilter *cf,
74313498266Sopenharmony_ci                              struct Curl_easy *data,
74413498266Sopenharmony_ci                              bool pause)
74513498266Sopenharmony_ci{
74613498266Sopenharmony_ci  if(!pause) {
74713498266Sopenharmony_ci    drain_stream(cf, data);
74813498266Sopenharmony_ci    Curl_expire(data, 0, EXPIRE_RUN_NOW);
74913498266Sopenharmony_ci  }
75013498266Sopenharmony_ci  return CURLE_OK;
75113498266Sopenharmony_ci}
75213498266Sopenharmony_ci
75313498266Sopenharmony_cistatic CURLcode cf_msh3_data_event(struct Curl_cfilter *cf,
75413498266Sopenharmony_ci                                   struct Curl_easy *data,
75513498266Sopenharmony_ci                                   int event, int arg1, void *arg2)
75613498266Sopenharmony_ci{
75713498266Sopenharmony_ci  struct stream_ctx *stream = H3_STREAM_CTX(data);
75813498266Sopenharmony_ci  struct cf_call_data save;
75913498266Sopenharmony_ci  CURLcode result = CURLE_OK;
76013498266Sopenharmony_ci
76113498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
76213498266Sopenharmony_ci
76313498266Sopenharmony_ci  (void)arg1;
76413498266Sopenharmony_ci  (void)arg2;
76513498266Sopenharmony_ci  switch(event) {
76613498266Sopenharmony_ci  case CF_CTRL_DATA_SETUP:
76713498266Sopenharmony_ci    result = h3_data_setup(cf, data);
76813498266Sopenharmony_ci    break;
76913498266Sopenharmony_ci  case CF_CTRL_DATA_PAUSE:
77013498266Sopenharmony_ci    result = h3_data_pause(cf, data, (arg1 != 0));
77113498266Sopenharmony_ci    break;
77213498266Sopenharmony_ci  case CF_CTRL_DATA_DONE:
77313498266Sopenharmony_ci    h3_data_done(cf, data);
77413498266Sopenharmony_ci    break;
77513498266Sopenharmony_ci  case CF_CTRL_DATA_DONE_SEND:
77613498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "req: send done");
77713498266Sopenharmony_ci    if(stream) {
77813498266Sopenharmony_ci      stream->upload_done = TRUE;
77913498266Sopenharmony_ci      if(stream->req) {
78013498266Sopenharmony_ci        char buf[1];
78113498266Sopenharmony_ci        if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN,
78213498266Sopenharmony_ci                            buf, 0, data)) {
78313498266Sopenharmony_ci          result = CURLE_SEND_ERROR;
78413498266Sopenharmony_ci        }
78513498266Sopenharmony_ci      }
78613498266Sopenharmony_ci    }
78713498266Sopenharmony_ci    break;
78813498266Sopenharmony_ci  case CF_CTRL_CONN_INFO_UPDATE:
78913498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "req: update info");
79013498266Sopenharmony_ci    cf_msh3_active(cf, data);
79113498266Sopenharmony_ci    break;
79213498266Sopenharmony_ci  default:
79313498266Sopenharmony_ci    break;
79413498266Sopenharmony_ci  }
79513498266Sopenharmony_ci
79613498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
79713498266Sopenharmony_ci  return result;
79813498266Sopenharmony_ci}
79913498266Sopenharmony_ci
80013498266Sopenharmony_cistatic CURLcode cf_connect_start(struct Curl_cfilter *cf,
80113498266Sopenharmony_ci                                 struct Curl_easy *data)
80213498266Sopenharmony_ci{
80313498266Sopenharmony_ci  struct cf_msh3_ctx *ctx = cf->ctx;
80413498266Sopenharmony_ci  struct ssl_primary_config *conn_config;
80513498266Sopenharmony_ci  MSH3_ADDR addr = {0};
80613498266Sopenharmony_ci  CURLcode result;
80713498266Sopenharmony_ci  bool verify;
80813498266Sopenharmony_ci
80913498266Sopenharmony_ci  conn_config = Curl_ssl_cf_get_primary_config(cf);
81013498266Sopenharmony_ci  if(!conn_config)
81113498266Sopenharmony_ci    return CURLE_FAILED_INIT;
81213498266Sopenharmony_ci  verify = !!conn_config->verifypeer;
81313498266Sopenharmony_ci
81413498266Sopenharmony_ci  memcpy(&addr, &ctx->addr.sa_addr, ctx->addr.addrlen);
81513498266Sopenharmony_ci  MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port);
81613498266Sopenharmony_ci
81713498266Sopenharmony_ci  if(verify && (conn_config->CAfile || conn_config->CApath)) {
81813498266Sopenharmony_ci    /* TODO: need a way to provide trust anchors to MSH3 */
81913498266Sopenharmony_ci#ifdef DEBUGBUILD
82013498266Sopenharmony_ci    /* we need this for our test cases to run */
82113498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "non-standard CA not supported, "
82213498266Sopenharmony_ci                "switching off verifypeer in DEBUG mode");
82313498266Sopenharmony_ci    verify = 0;
82413498266Sopenharmony_ci#else
82513498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "non-standard CA not supported, "
82613498266Sopenharmony_ci                "attempting with built-in verification");
82713498266Sopenharmony_ci#endif
82813498266Sopenharmony_ci  }
82913498266Sopenharmony_ci
83013498266Sopenharmony_ci  CURL_TRC_CF(data, cf, "connecting to %s:%d (verify=%d)",
83113498266Sopenharmony_ci              cf->conn->host.name, (int)cf->conn->remote_port, verify);
83213498266Sopenharmony_ci
83313498266Sopenharmony_ci  ctx->api = MsH3ApiOpen();
83413498266Sopenharmony_ci  if(!ctx->api) {
83513498266Sopenharmony_ci    failf(data, "can't create msh3 api");
83613498266Sopenharmony_ci    return CURLE_FAILED_INIT;
83713498266Sopenharmony_ci  }
83813498266Sopenharmony_ci
83913498266Sopenharmony_ci  ctx->qconn = MsH3ConnectionOpen(ctx->api,
84013498266Sopenharmony_ci                                  &msh3_conn_if,
84113498266Sopenharmony_ci                                  cf,
84213498266Sopenharmony_ci                                  cf->conn->host.name,
84313498266Sopenharmony_ci                                  &addr,
84413498266Sopenharmony_ci                                  !verify);
84513498266Sopenharmony_ci  if(!ctx->qconn) {
84613498266Sopenharmony_ci    failf(data, "can't create msh3 connection");
84713498266Sopenharmony_ci    if(ctx->api) {
84813498266Sopenharmony_ci      MsH3ApiClose(ctx->api);
84913498266Sopenharmony_ci      ctx->api = NULL;
85013498266Sopenharmony_ci    }
85113498266Sopenharmony_ci    return CURLE_FAILED_INIT;
85213498266Sopenharmony_ci  }
85313498266Sopenharmony_ci
85413498266Sopenharmony_ci  result = h3_data_setup(cf, data);
85513498266Sopenharmony_ci  if(result)
85613498266Sopenharmony_ci    return result;
85713498266Sopenharmony_ci
85813498266Sopenharmony_ci  return CURLE_OK;
85913498266Sopenharmony_ci}
86013498266Sopenharmony_ci
86113498266Sopenharmony_cistatic CURLcode cf_msh3_connect(struct Curl_cfilter *cf,
86213498266Sopenharmony_ci                                struct Curl_easy *data,
86313498266Sopenharmony_ci                                bool blocking, bool *done)
86413498266Sopenharmony_ci{
86513498266Sopenharmony_ci  struct cf_msh3_ctx *ctx = cf->ctx;
86613498266Sopenharmony_ci  struct cf_call_data save;
86713498266Sopenharmony_ci  CURLcode result = CURLE_OK;
86813498266Sopenharmony_ci
86913498266Sopenharmony_ci  (void)blocking;
87013498266Sopenharmony_ci  if(cf->connected) {
87113498266Sopenharmony_ci    *done = TRUE;
87213498266Sopenharmony_ci    return CURLE_OK;
87313498266Sopenharmony_ci  }
87413498266Sopenharmony_ci
87513498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
87613498266Sopenharmony_ci
87713498266Sopenharmony_ci  if(ctx->sock[SP_LOCAL] == CURL_SOCKET_BAD) {
87813498266Sopenharmony_ci    if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0]) < 0) {
87913498266Sopenharmony_ci      ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
88013498266Sopenharmony_ci      ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
88113498266Sopenharmony_ci      return CURLE_COULDNT_CONNECT;
88213498266Sopenharmony_ci    }
88313498266Sopenharmony_ci  }
88413498266Sopenharmony_ci
88513498266Sopenharmony_ci  *done = FALSE;
88613498266Sopenharmony_ci  if(!ctx->qconn) {
88713498266Sopenharmony_ci    ctx->connect_started = Curl_now();
88813498266Sopenharmony_ci    result = cf_connect_start(cf, data);
88913498266Sopenharmony_ci    if(result)
89013498266Sopenharmony_ci      goto out;
89113498266Sopenharmony_ci  }
89213498266Sopenharmony_ci
89313498266Sopenharmony_ci  if(ctx->handshake_complete) {
89413498266Sopenharmony_ci    ctx->handshake_at = Curl_now();
89513498266Sopenharmony_ci    if(ctx->handshake_succeeded) {
89613498266Sopenharmony_ci      CURL_TRC_CF(data, cf, "handshake succeeded");
89713498266Sopenharmony_ci      cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
89813498266Sopenharmony_ci      cf->conn->httpversion = 30;
89913498266Sopenharmony_ci      cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
90013498266Sopenharmony_ci      cf->connected = TRUE;
90113498266Sopenharmony_ci      cf->conn->alpn = CURL_HTTP_VERSION_3;
90213498266Sopenharmony_ci      *done = TRUE;
90313498266Sopenharmony_ci      connkeep(cf->conn, "HTTP/3 default");
90413498266Sopenharmony_ci      Curl_pgrsTime(data, TIMER_APPCONNECT);
90513498266Sopenharmony_ci    }
90613498266Sopenharmony_ci    else {
90713498266Sopenharmony_ci      failf(data, "failed to connect, handshake failed");
90813498266Sopenharmony_ci      result = CURLE_COULDNT_CONNECT;
90913498266Sopenharmony_ci    }
91013498266Sopenharmony_ci  }
91113498266Sopenharmony_ci
91213498266Sopenharmony_ciout:
91313498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
91413498266Sopenharmony_ci  return result;
91513498266Sopenharmony_ci}
91613498266Sopenharmony_ci
91713498266Sopenharmony_cistatic void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data)
91813498266Sopenharmony_ci{
91913498266Sopenharmony_ci  struct cf_msh3_ctx *ctx = cf->ctx;
92013498266Sopenharmony_ci  struct cf_call_data save;
92113498266Sopenharmony_ci
92213498266Sopenharmony_ci  (void)data;
92313498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
92413498266Sopenharmony_ci
92513498266Sopenharmony_ci  if(ctx) {
92613498266Sopenharmony_ci    CURL_TRC_CF(data, cf, "destroying");
92713498266Sopenharmony_ci    if(ctx->qconn) {
92813498266Sopenharmony_ci      MsH3ConnectionClose(ctx->qconn);
92913498266Sopenharmony_ci      ctx->qconn = NULL;
93013498266Sopenharmony_ci    }
93113498266Sopenharmony_ci    if(ctx->api) {
93213498266Sopenharmony_ci      MsH3ApiClose(ctx->api);
93313498266Sopenharmony_ci      ctx->api = NULL;
93413498266Sopenharmony_ci    }
93513498266Sopenharmony_ci
93613498266Sopenharmony_ci    if(ctx->active) {
93713498266Sopenharmony_ci      /* We share our socket at cf->conn->sock[cf->sockindex] when active.
93813498266Sopenharmony_ci       * If it is no longer there, someone has stolen (and hopefully
93913498266Sopenharmony_ci       * closed it) and we just forget about it.
94013498266Sopenharmony_ci       */
94113498266Sopenharmony_ci      ctx->active = FALSE;
94213498266Sopenharmony_ci      if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) {
94313498266Sopenharmony_ci        CURL_TRC_CF(data, cf, "cf_msh3_close(%d) active",
94413498266Sopenharmony_ci                    (int)ctx->sock[SP_LOCAL]);
94513498266Sopenharmony_ci        cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
94613498266Sopenharmony_ci      }
94713498266Sopenharmony_ci      else {
94813498266Sopenharmony_ci        CURL_TRC_CF(data, cf, "cf_socket_close(%d) no longer at "
94913498266Sopenharmony_ci                    "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]);
95013498266Sopenharmony_ci        ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
95113498266Sopenharmony_ci      }
95213498266Sopenharmony_ci      if(cf->sockindex == FIRSTSOCKET)
95313498266Sopenharmony_ci        cf->conn->remote_addr = NULL;
95413498266Sopenharmony_ci    }
95513498266Sopenharmony_ci    if(ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
95613498266Sopenharmony_ci      sclose(ctx->sock[SP_LOCAL]);
95713498266Sopenharmony_ci    }
95813498266Sopenharmony_ci    if(ctx->sock[SP_REMOTE] != CURL_SOCKET_BAD) {
95913498266Sopenharmony_ci      sclose(ctx->sock[SP_REMOTE]);
96013498266Sopenharmony_ci    }
96113498266Sopenharmony_ci    ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
96213498266Sopenharmony_ci    ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
96313498266Sopenharmony_ci  }
96413498266Sopenharmony_ci  CF_DATA_RESTORE(cf, save);
96513498266Sopenharmony_ci}
96613498266Sopenharmony_ci
96713498266Sopenharmony_cistatic void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
96813498266Sopenharmony_ci{
96913498266Sopenharmony_ci  struct cf_call_data save;
97013498266Sopenharmony_ci
97113498266Sopenharmony_ci  CF_DATA_SAVE(save, cf, data);
97213498266Sopenharmony_ci  cf_msh3_close(cf, data);
97313498266Sopenharmony_ci  free(cf->ctx);
97413498266Sopenharmony_ci  cf->ctx = NULL;
97513498266Sopenharmony_ci  /* no CF_DATA_RESTORE(cf, save); its gone */
97613498266Sopenharmony_ci
97713498266Sopenharmony_ci}
97813498266Sopenharmony_ci
97913498266Sopenharmony_cistatic CURLcode cf_msh3_query(struct Curl_cfilter *cf,
98013498266Sopenharmony_ci                              struct Curl_easy *data,
98113498266Sopenharmony_ci                              int query, int *pres1, void *pres2)
98213498266Sopenharmony_ci{
98313498266Sopenharmony_ci  struct cf_msh3_ctx *ctx = cf->ctx;
98413498266Sopenharmony_ci
98513498266Sopenharmony_ci  switch(query) {
98613498266Sopenharmony_ci  case CF_QUERY_MAX_CONCURRENT: {
98713498266Sopenharmony_ci    /* TODO: we do not have access to this so far, fake it */
98813498266Sopenharmony_ci    (void)ctx;
98913498266Sopenharmony_ci    *pres1 = 100;
99013498266Sopenharmony_ci    return CURLE_OK;
99113498266Sopenharmony_ci  }
99213498266Sopenharmony_ci  case CF_QUERY_TIMER_CONNECT: {
99313498266Sopenharmony_ci    struct curltime *when = pres2;
99413498266Sopenharmony_ci    /* we do not know when the first byte arrived */
99513498266Sopenharmony_ci    if(cf->connected)
99613498266Sopenharmony_ci      *when = ctx->handshake_at;
99713498266Sopenharmony_ci    return CURLE_OK;
99813498266Sopenharmony_ci  }
99913498266Sopenharmony_ci  case CF_QUERY_TIMER_APPCONNECT: {
100013498266Sopenharmony_ci    struct curltime *when = pres2;
100113498266Sopenharmony_ci    if(cf->connected)
100213498266Sopenharmony_ci      *when = ctx->handshake_at;
100313498266Sopenharmony_ci    return CURLE_OK;
100413498266Sopenharmony_ci  }
100513498266Sopenharmony_ci  default:
100613498266Sopenharmony_ci    break;
100713498266Sopenharmony_ci  }
100813498266Sopenharmony_ci  return cf->next?
100913498266Sopenharmony_ci    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
101013498266Sopenharmony_ci    CURLE_UNKNOWN_OPTION;
101113498266Sopenharmony_ci}
101213498266Sopenharmony_ci
101313498266Sopenharmony_cistatic bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf,
101413498266Sopenharmony_ci                                  struct Curl_easy *data,
101513498266Sopenharmony_ci                                  bool *input_pending)
101613498266Sopenharmony_ci{
101713498266Sopenharmony_ci  struct cf_msh3_ctx *ctx = cf->ctx;
101813498266Sopenharmony_ci
101913498266Sopenharmony_ci  (void)data;
102013498266Sopenharmony_ci  *input_pending = FALSE;
102113498266Sopenharmony_ci  return ctx && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD && ctx->qconn &&
102213498266Sopenharmony_ci         ctx->connected;
102313498266Sopenharmony_ci}
102413498266Sopenharmony_ci
102513498266Sopenharmony_cistruct Curl_cftype Curl_cft_http3 = {
102613498266Sopenharmony_ci  "HTTP/3",
102713498266Sopenharmony_ci  CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
102813498266Sopenharmony_ci  0,
102913498266Sopenharmony_ci  cf_msh3_destroy,
103013498266Sopenharmony_ci  cf_msh3_connect,
103113498266Sopenharmony_ci  cf_msh3_close,
103213498266Sopenharmony_ci  Curl_cf_def_get_host,
103313498266Sopenharmony_ci  cf_msh3_adjust_pollset,
103413498266Sopenharmony_ci  cf_msh3_data_pending,
103513498266Sopenharmony_ci  cf_msh3_send,
103613498266Sopenharmony_ci  cf_msh3_recv,
103713498266Sopenharmony_ci  cf_msh3_data_event,
103813498266Sopenharmony_ci  cf_msh3_conn_is_alive,
103913498266Sopenharmony_ci  Curl_cf_def_conn_keep_alive,
104013498266Sopenharmony_ci  cf_msh3_query,
104113498266Sopenharmony_ci};
104213498266Sopenharmony_ci
104313498266Sopenharmony_ciCURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
104413498266Sopenharmony_ci                             struct Curl_easy *data,
104513498266Sopenharmony_ci                             struct connectdata *conn,
104613498266Sopenharmony_ci                             const struct Curl_addrinfo *ai)
104713498266Sopenharmony_ci{
104813498266Sopenharmony_ci  struct cf_msh3_ctx *ctx = NULL;
104913498266Sopenharmony_ci  struct Curl_cfilter *cf = NULL;
105013498266Sopenharmony_ci  CURLcode result;
105113498266Sopenharmony_ci
105213498266Sopenharmony_ci  (void)data;
105313498266Sopenharmony_ci  (void)conn;
105413498266Sopenharmony_ci  (void)ai; /* TODO: msh3 resolves itself? */
105513498266Sopenharmony_ci  ctx = calloc(1, sizeof(*ctx));
105613498266Sopenharmony_ci  if(!ctx) {
105713498266Sopenharmony_ci    result = CURLE_OUT_OF_MEMORY;
105813498266Sopenharmony_ci    goto out;
105913498266Sopenharmony_ci  }
106013498266Sopenharmony_ci  Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC);
106113498266Sopenharmony_ci  ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
106213498266Sopenharmony_ci  ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
106313498266Sopenharmony_ci
106413498266Sopenharmony_ci  result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
106513498266Sopenharmony_ci
106613498266Sopenharmony_ciout:
106713498266Sopenharmony_ci  *pcf = (!result)? cf : NULL;
106813498266Sopenharmony_ci  if(result) {
106913498266Sopenharmony_ci    Curl_safefree(cf);
107013498266Sopenharmony_ci    Curl_safefree(ctx);
107113498266Sopenharmony_ci  }
107213498266Sopenharmony_ci
107313498266Sopenharmony_ci  return result;
107413498266Sopenharmony_ci}
107513498266Sopenharmony_ci
107613498266Sopenharmony_cibool Curl_conn_is_msh3(const struct Curl_easy *data,
107713498266Sopenharmony_ci                       const struct connectdata *conn,
107813498266Sopenharmony_ci                       int sockindex)
107913498266Sopenharmony_ci{
108013498266Sopenharmony_ci  struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
108113498266Sopenharmony_ci
108213498266Sopenharmony_ci  (void)data;
108313498266Sopenharmony_ci  for(; cf; cf = cf->next) {
108413498266Sopenharmony_ci    if(cf->cft == &Curl_cft_http3)
108513498266Sopenharmony_ci      return TRUE;
108613498266Sopenharmony_ci    if(cf->cft->flags & CF_TYPE_IP_CONNECT)
108713498266Sopenharmony_ci      return FALSE;
108813498266Sopenharmony_ci  }
108913498266Sopenharmony_ci  return FALSE;
109013498266Sopenharmony_ci}
109113498266Sopenharmony_ci
109213498266Sopenharmony_ci#endif /* USE_MSH3 */
1093