Coverage Report

Created: 2026-07-30 07:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/vquic/vquic.c
Line
Count
Source
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
#include "curl_setup.h"
25
#include "urldata.h"
26
#include "vquic/vquic.h"
27
28
#include "curl_trc.h"
29
30
#if !defined(CURL_DISABLE_HTTP) && defined(USE_HTTP3)
31
32
#ifdef HAVE_NETINET_UDP_H
33
#include <netinet/udp.h>
34
#endif
35
36
#ifdef USE_NGHTTP3
37
#include <nghttp3/nghttp3.h>
38
#endif
39
40
#if defined(USE_APPLE_FAST_UDP) && defined(__APPLE__)
41
#include <sys/syscall.h>
42
#if defined(SYS_recvmsg_x) && defined(SYS_sendmsg_x)
43
#define HAVE_APPLE_MSG_X
44
#endif
45
#endif
46
47
#include "bufq.h"
48
#include "curlx/dynbuf.h"
49
#include "curlx/fopen.h"
50
#include "cfilters.h"
51
#include "cf-dns.h"
52
#include "vquic/cf-ngtcp2.h"
53
#include "vquic/cf-ngtcp2-cmn.h"
54
#include "vquic/cf-ngtcp2-proxy.h"
55
#include "vquic/cf-quiche.h"
56
#include "multiif.h"
57
#include "progress.h"
58
#include "rand.h"
59
#include "vquic/vquic_int.h"
60
#include "curlx/strerr.h"
61
#include "curlx/strparse.h"
62
63
64
#define NW_CHUNK_SIZE     (64 * 1024)
65
#define NW_SEND_CHUNKS    1
66
67
#ifdef HAVE_APPLE_MSG_X
68
struct msghdr_x {
69
  void *msg_name;           /* optional address */
70
  socklen_t msg_namelen;    /* size of address */
71
  struct iovec *msg_iov;    /* scatter/gather array */
72
  int msg_iovlen;           /* # elements in msg_iov */
73
  void *msg_control;        /* ancillary data, see below */
74
  socklen_t msg_controllen; /* ancillary data buffer len */
75
  int msg_flags;            /* flags on received message */
76
  size_t msg_datalen;       /* byte length of buffer in msg_iov */
77
};
78
#endif
79
80
#ifdef CURLVERBOSE
81
#ifdef HAVE_APPLE_MSG_X
82
#define VQUIC_SEND_METHOD   "sendmsg_x"
83
#elif defined(HAVE_SENDMSG)
84
#define VQUIC_SEND_METHOD   "sendmsg"
85
#else
86
#define VQUIC_SEND_METHOD   "send"
87
#endif
88
#endif
89
90
int Curl_vquic_init(void)
91
{
92
#if defined(USE_NGTCP2) && defined(OPENSSL_QUIC_API2)
93
  if(ngtcp2_crypto_ossl_init())
94
    return 0;
95
#endif
96
97
  return 1;
98
}
99
100
void Curl_vquic_cleanup(void)
101
{
102
#if defined(USE_NGTCP2) && defined(OPENSSL_QUIC_API2) && \
103
  (NGTCP2_VERSION_NUM >= 0x011800)
104
  ngtcp2_crypto_ossl_free();
105
#endif
106
}
107
108
void Curl_quic_ver(char *p, size_t len)
109
{
110
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
111
  Curl_ngtcp2_ver(p, len);
112
#elif defined(USE_QUICHE)
113
  Curl_quiche_ver(p, len);
114
#endif
115
}
116
117
CURLcode Curl_vquic_ctx_init(struct Curl_easy *data,
118
                             struct cf_quic_ctx *qctx)
119
{
120
  Curl_bufq_init2(&qctx->sendbuf, NW_CHUNK_SIZE, NW_SEND_CHUNKS,
121
                  BUFQ_OPT_SOFT_LIMIT);
122
#if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG)
123
  qctx->no_gso = FALSE;
124
#elif defined(HAVE_APPLE_MSG_X)
125
  qctx->no_gso = FALSE;
126
#else
127
  qctx->no_gso = TRUE;
128
#endif
129
#ifdef DEBUGBUILD
130
  {
131
    const char *p = getenv("CURL_DBG_QUIC_WBLOCK");
132
    if(p) {
133
      curl_off_t l;
134
      if(!curlx_str_number(&p, &l, 100))
135
        qctx->wblock_percent = (int)l;
136
    }
137
  }
138
#endif
139
  Curl_vquic_ctx_set_time(qctx, Curl_pgrs_now(data));
140
141
  return CURLE_OK;
142
}
143
144
void Curl_vquic_ctx_free(struct cf_quic_ctx *qctx)
145
{
146
  Curl_bufq_free(&qctx->sendbuf);
147
}
148
149
void Curl_vquic_ctx_set_time(struct cf_quic_ctx *qctx,
150
                             const struct curltime *pnow)
151
{
152
  qctx->last_op = *pnow;
153
}
154
155
void Curl_vquic_ctx_update_time(struct cf_quic_ctx *qctx,
156
                                const struct curltime *pnow)
157
{
158
  qctx->last_op = *pnow;
159
}
160
161
#ifdef HAVE_APPLE_MSG_X
162
static CURLcode do_sendmsg(struct Curl_cfilter *cf,
163
                           struct Curl_easy *data,
164
                           struct cf_quic_ctx *qctx,
165
                           const uint8_t *pkt, size_t pktlen, size_t gsolen,
166
                           size_t *psent)
167
{
168
#define MSG_X_SNUM  64
169
  struct iovec msg_iov[MSG_X_SNUM];
170
  struct msghdr_x mmsg[MSG_X_SNUM];
171
  char errstr[STRERROR_LEN];
172
  size_t n, i = 0, sent = 0;
173
  int rc;
174
  CURLcode result = CURLE_OK;
175
  VERBOSE(size_t calls = 0);
176
177
  *psent = 0;
178
  if(!pktlen)
179
    return CURLE_OK;
180
  if(!gsolen || (pktlen < gsolen))
181
    gsolen = pktlen;
182
  n = pktlen / gsolen;
183
  while(i < n) {
184
    size_t j, batch = CURLMIN(n - i, MSG_X_SNUM);
185
186
    for(j = 0; j < batch; ++j) {
187
      msg_iov[j].iov_base = CURL_UNCONST(&pkt[(i + j) * gsolen]);
188
      msg_iov[j].iov_len = gsolen;
189
      memset(&mmsg[j], 0, sizeof(mmsg[j]));
190
      mmsg[j].msg_iov = &msg_iov[j];
191
      mmsg[j].msg_iovlen = 1;
192
    }
193
194
#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
195
#pragma GCC diagnostic push
196
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
197
#endif
198
    while((rc = syscall(SYS_sendmsg_x, qctx->sockfd, &mmsg, batch, 0)) == -1 &&
199
          (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE))
200
      ;
201
#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
202
#pragma GCC diagnostic pop
203
#endif
204
205
    if(rc == -1) {
206
      if(SOCK_EAGAIN(SOCKERRNO)) {
207
        CURL_TRC_CF(data, cf, "egress, sendmsg_x -> EAGAIN");
208
        result = sent ? CURLE_OK : CURLE_AGAIN;
209
        goto out;
210
      }
211
      curlx_strerror(SOCKERRNO, errstr, sizeof(errstr));
212
      failf(data, "QUIC: sendmsg_x() unexpectedly returned %d (errno=%d; %s)",
213
            rc, SOCKERRNO, errstr);
214
      result = CURLE_RECV_ERROR;
215
      goto out;
216
    }
217
    VERBOSE(++calls);
218
    i += batch;
219
    sent += (batch * gsolen);
220
  }
221
222
out:
223
  *psent = sent;
224
  if(sent || result)
225
    CURL_TRC_CF(data, cf,
226
                "vquic_sendmsg_x(len=%zu, gso=%zu, packets=%zu, "
227
                "calls=%zu) -> %d",
228
                sent, gsolen, n, calls, (int)result);
229
  return result;
230
}
231
232
#else /* HAVE_APPLE_MSG_X */
233
234
static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
235
                                   struct Curl_easy *data,
236
                                   struct cf_quic_ctx *qctx,
237
                                   const uint8_t *pkt, size_t pktlen,
238
                                   size_t gsolen, size_t *psent);
239
240
static CURLcode do_sendmsg(struct Curl_cfilter *cf,
241
                           struct Curl_easy *data,
242
                           struct cf_quic_ctx *qctx,
243
                           const uint8_t *pkt, size_t pktlen, size_t gsolen,
244
                           size_t *psent)
245
{
246
  CURLcode result = CURLE_OK;
247
#ifdef HAVE_SENDMSG
248
  struct iovec msg_iov;
249
  struct msghdr msg = { 0 };
250
  ssize_t rv;
251
#if defined(__linux__) && defined(UDP_SEGMENT)
252
  uint8_t msg_ctrl[32];
253
  struct cmsghdr *cm;
254
#endif
255
256
  *psent = 0;
257
  msg_iov.iov_base = (uint8_t *)CURL_UNCONST(pkt);
258
  msg_iov.iov_len = pktlen;
259
  msg.msg_iov = &msg_iov;
260
  msg.msg_iovlen = 1;
261
262
#if defined(__linux__) && defined(UDP_SEGMENT)
263
  if(pktlen > gsolen) {
264
    /* Only set this, when we need it. macOS, for example,
265
     * does not seem to like a msg_control of length 0. */
266
    memset(msg_ctrl, 0, sizeof(msg_ctrl));
267
    msg.msg_control = msg_ctrl;
268
    assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(int)));
269
    msg.msg_controllen = CMSG_SPACE(sizeof(int));
270
    cm = CMSG_FIRSTHDR(&msg);
271
    cm->cmsg_level = SOL_UDP;
272
    cm->cmsg_type = UDP_SEGMENT;
273
    cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
274
    *(uint16_t *)(void *)CMSG_DATA(cm) = gsolen & 0xffff;
275
  }
276
#endif
277
278
  while((rv = sendmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == SOCKEINTR)
279
    ;
280
281
  if(!curlx_sztouz(rv, psent)) {
282
    int sockerr = SOCKERRNO;
283
    if(SOCK_EAGAIN(sockerr))
284
      return CURLE_AGAIN;
285
    switch(sockerr) {
286
    case SOCKEMSGSIZE:
287
      /* UDP datagram is too large; caused by PMTUD. Let it be lost. */
288
      *psent = pktlen;
289
      break;
290
    case EIO:
291
      if(pktlen > gsolen) {
292
        /* GSO failure */
293
        infof(data, "sendmsg() returned %zd (errno %d); disable GSO", rv,
294
              sockerr);
295
        qctx->no_gso = TRUE;
296
        return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
297
      }
298
      FALLTHROUGH();
299
    default:
300
      failf(data, "sendmsg() returned %zd (errno %d)", rv, sockerr);
301
      result = CURLE_SEND_ERROR;
302
      goto out;
303
    }
304
  }
305
  else if(pktlen != *psent) {
306
    failf(data, "sendmsg() sent only %zu/%zu bytes", *psent, pktlen);
307
    result = CURLE_SEND_ERROR;
308
    goto out;
309
  }
310
#else
311
  ssize_t rv;
312
  (void)gsolen;
313
314
  *psent = 0;
315
316
  while((rv = swrite(qctx->sockfd, pkt, pktlen)) == -1 &&
317
        SOCKERRNO == SOCKEINTR)
318
    ;
319
320
  if(!curlx_sztouz(rv, psent)) {
321
    if(SOCK_EAGAIN(SOCKERRNO)) {
322
      result = CURLE_AGAIN;
323
      goto out;
324
    }
325
    else {
326
      if(SOCKERRNO != SOCKEMSGSIZE) {
327
        failf(data, "send() returned %zd (errno %d)", rv, SOCKERRNO);
328
        result = CURLE_SEND_ERROR;
329
        goto out;
330
      }
331
      /* UDP datagram is too large; caused by PMTUD. Let it be lost. */
332
      *psent = pktlen;
333
    }
334
  }
335
#endif
336
  (void)cf;
337
338
out:
339
  CURL_TRC_CF(data, cf,
340
              "vquic_%s(len=%zu, gso=%zu, calls=1) -> %d, sent=%zu",
341
              VQUIC_SEND_METHOD, pktlen, gsolen, (int)result, *psent);
342
  return result;
343
}
344
345
#endif /* !HAVE_APPLE_MSG_X */
346
347
static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
348
                                   struct Curl_easy *data,
349
                                   struct cf_quic_ctx *qctx,
350
                                   const uint8_t *pkt, size_t pktlen,
351
                                   size_t gsolen, size_t *psent)
352
{
353
  const uint8_t *p, *end = pkt + pktlen;
354
  size_t sent, len;
355
  CURLcode result = CURLE_OK;
356
  VERBOSE(size_t calls = 0);
357
358
  *psent = 0;
359
360
  for(p = pkt; p < end; p += gsolen) {
361
    len = CURLMIN(gsolen, (size_t)(end - p));
362
    result = do_sendmsg(cf, data, qctx, p, len, len, &sent);
363
    if(result)
364
      goto out;
365
    *psent += sent;
366
    VERBOSE(++calls);
367
  }
368
out:
369
  CURL_TRC_CF(data, cf,
370
              "vquic_%s(len=%zu, gso=%zu, calls=%zu) -> %d, sent=%zu",
371
              VQUIC_SEND_METHOD, pktlen, gsolen, calls, (int)result, *psent);
372
  return result;
373
}
374
375
/* Split QUIC payload by datagram (gso) boundaries when sending over a
376
 * non-UDP lower filter (for example CONNECT-UDP proxy tunnel). */
377
static CURLcode send_packet_no_gso_cf(struct Curl_cfilter *cf,
378
                                      struct Curl_easy *data,
379
                                      const uint8_t *pkt, size_t pktlen,
380
                                      size_t gsolen, size_t *psent)
381
{
382
  const uint8_t *p, *end = pkt + pktlen;
383
  size_t sent, len;
384
  CURLcode result = CURLE_OK;
385
  VERBOSE(size_t calls = 0);
386
387
  *psent = 0;
388
389
  /* Send one datagram-sized chunk per call into the lower filter. */
390
  for(p = pkt; p < end; p += len) {
391
    len = CURLMIN(gsolen, (size_t)(end - p));
392
    result = Curl_conn_cf_send(cf->next, data, p, len, FALSE, &sent);
393
    /* Report forward progress even if we return CURLE_AGAIN later. */
394
    VERBOSE(++calls);
395
    /* Preserve lower-filter errors (including CURLE_AGAIN). */
396
    if(result)
397
      goto out;
398
399
    if(sent != len) {
400
      /* We can only send the complete datagram, not parts. */
401
      result = CURLE_SEND_ERROR;
402
      goto out;
403
    }
404
    *psent += sent;
405
  }
406
407
out:
408
  CURL_TRC_CF(data, cf,
409
              "vquic_cf_send(len=%zu, gso=%zu, calls=%zu) -> %d, sent=%zu",
410
              pktlen, gsolen, calls, (int)result, *psent);
411
  return result;
412
}
413
414
static CURLcode vquic_send_packets(struct Curl_cfilter *cf,
415
                                   struct Curl_easy *data,
416
                                   struct cf_quic_ctx *qctx,
417
                                   const uint8_t *pkt, size_t pktlen,
418
                                   size_t gsolen, size_t *psent)
419
{
420
  CURLcode result;
421
#ifdef DEBUGBUILD
422
  /* simulate network blocking/partial writes */
423
  if(qctx->wblock_percent > 0) {
424
    unsigned char c;
425
    *psent = 0;
426
    Curl_rand(data, &c, 1);
427
    if(c >= ((100 - qctx->wblock_percent) * 256 / 100)) {
428
      CURL_TRC_CF(data, cf, "vquic_flush() simulate EWOULDBLOCK");
429
      return CURLE_AGAIN;
430
    }
431
  }
432
#endif
433
  if(qctx->no_gso && pktlen > gsolen) {
434
    result = send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
435
  }
436
  else {
437
    result = do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent);
438
  }
439
  if(!result)
440
    qctx->last_io = qctx->last_op;
441
  return result;
442
}
443
444
CURLcode Curl_vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data,
445
                          struct cf_quic_ctx *qctx)
446
{
447
  const unsigned char *buf;
448
  size_t blen, sent;
449
  CURLcode result;
450
  size_t gsolen;
451
452
  while(Curl_bufq_peek(&qctx->sendbuf, &buf, &blen)) {
453
    gsolen = qctx->gsolen;
454
    if(qctx->split_len) {
455
      gsolen = qctx->split_gsolen;
456
      if(blen > qctx->split_len)
457
        blen = qctx->split_len;
458
    }
459
460
    if(qctx->sockfd != CURL_SOCKET_BAD) {
461
      /* Direct UDP socket (via happy eyeballs) */
462
      result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent);
463
    }
464
    else {
465
      /* Tunneled QUIC (CONNECT-UDP through proxy) */
466
      if(gsolen && (blen > gsolen)) {
467
        /* Send one datagram at a time to preserve packet boundaries. */
468
        result = send_packet_no_gso_cf(cf, data, buf, blen, gsolen, &sent);
469
      }
470
      else {
471
        /* No GSO aggregate to split, regular lower-filter send is enough. */
472
        result = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &sent);
473
      }
474
    }
475
476
    if(result) {
477
      if(result == CURLE_AGAIN) {
478
        Curl_bufq_skip(&qctx->sendbuf, sent);
479
        if(qctx->split_len)
480
          qctx->split_len -= sent;
481
      }
482
      return result;
483
    }
484
    Curl_bufq_skip(&qctx->sendbuf, sent);
485
    if(qctx->split_len)
486
      qctx->split_len -= sent;
487
  }
488
  return CURLE_OK;
489
}
490
491
CURLcode Curl_vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data,
492
                         struct cf_quic_ctx *qctx, size_t gsolen)
493
{
494
  qctx->gsolen = gsolen;
495
  return Curl_vquic_flush(cf, data, qctx);
496
}
497
498
CURLcode Curl_vquic_send_tail_split(struct Curl_cfilter *cf,
499
                                    struct Curl_easy *data,
500
                                    struct cf_quic_ctx *qctx, size_t gsolen,
501
                                    size_t tail_len, size_t tail_gsolen)
502
{
503
  DEBUGASSERT(Curl_bufq_len(&qctx->sendbuf) > tail_len);
504
  qctx->split_len = Curl_bufq_len(&qctx->sendbuf) - tail_len;
505
  qctx->split_gsolen = gsolen;
506
  qctx->gsolen = tail_gsolen;
507
  CURL_TRC_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]",
508
              qctx->split_len, qctx->split_gsolen, tail_len, qctx->gsolen);
509
  return Curl_vquic_flush(cf, data, qctx);
510
}
511
512
#if (defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)) && \
513
  !defined(HAVE_APPLE_MSG_X)
514
static size_t vquic_msghdr_get_udp_gro(struct msghdr *msg)
515
{
516
  int gso_size = 0;
517
#if defined(__linux__) && defined(UDP_GRO)
518
  struct cmsghdr *cmsg;
519
520
  /* Workaround musl CMSG_NXTHDR issue */
521
#if defined(__clang__) && !defined(__GLIBC__)
522
#pragma clang diagnostic push
523
#pragma clang diagnostic ignored "-Wsign-compare"
524
#pragma clang diagnostic ignored "-Wcast-align"
525
#endif
526
  for(cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
527
#if defined(__clang__) && !defined(__GLIBC__)
528
#pragma clang diagnostic pop
529
#endif
530
    if(cmsg->cmsg_level == SOL_UDP && cmsg->cmsg_type == UDP_GRO) {
531
      memcpy(&gso_size, CMSG_DATA(cmsg), sizeof(gso_size));
532
533
      break;
534
    }
535
  }
536
#endif
537
  (void)msg;
538
539
  return (size_t)gso_size;
540
}
541
#endif
542
543
#ifdef HAVE_SENDMMSG
544
static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
545
                                 struct Curl_easy *data,
546
                                 struct cf_quic_ctx *qctx,
547
                                 size_t max_pkts,
548
                                 Curl_vquic_recv_pkts_cb *recv_cb, void *userp)
549
{
550
#if defined(__linux__) && defined(UDP_GRO)
551
#define MMSG_NUM  16
552
#define UDP_GRO_CNT_MAX  64
553
#else
554
#define MMSG_NUM  64
555
#define UDP_GRO_CNT_MAX  1
556
#endif
557
#define MSG_BUF_SIZE  (UDP_GRO_CNT_MAX * 1500)
558
  struct iovec msg_iov[MMSG_NUM];
559
  struct mmsghdr mmsg[MMSG_NUM];
560
  uint8_t msg_ctrl[MMSG_NUM * CMSG_SPACE(sizeof(int))];
561
  struct sockaddr_storage remote_addr[MMSG_NUM];
562
  size_t total_nread = 0, pkts = 0;
563
#ifdef CURLVERBOSE
564
  size_t calls = 0;
565
#endif
566
  int mcount, i, n;
567
  char errstr[STRERROR_LEN];
568
  CURLcode result = CURLE_OK;
569
  size_t gso_size;
570
  char *sockbuf = NULL;
571
  uint8_t (*bufs)[MSG_BUF_SIZE] = NULL;
572
573
  DEBUGASSERT(max_pkts > 0);
574
  result = Curl_multi_xfer_sockbuf_borrow(data, MMSG_NUM * MSG_BUF_SIZE,
575
                                          &sockbuf);
576
  if(result)
577
    goto out;
578
  bufs = (uint8_t (*)[MSG_BUF_SIZE])sockbuf;
579
580
  total_nread = 0;
581
  while(pkts < max_pkts) {
582
    n = (int)CURLMIN(CURLMIN(MMSG_NUM, IOV_MAX), max_pkts);
583
    memset(&mmsg, 0, sizeof(mmsg));
584
    for(i = 0; i < n; ++i) {
585
      msg_iov[i].iov_base = bufs[i];
586
      msg_iov[i].iov_len = sizeof(bufs[i]);
587
      mmsg[i].msg_hdr.msg_iov = &msg_iov[i];
588
      mmsg[i].msg_hdr.msg_iovlen = 1;
589
      mmsg[i].msg_hdr.msg_name = &remote_addr[i];
590
      mmsg[i].msg_hdr.msg_namelen = sizeof(remote_addr[i]);
591
      mmsg[i].msg_hdr.msg_control = &msg_ctrl[i * CMSG_SPACE(sizeof(int))];
592
      mmsg[i].msg_hdr.msg_controllen = CMSG_SPACE(sizeof(int));
593
    }
594
595
    while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 &&
596
          (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE))
597
      ;
598
    if(mcount == -1) {
599
      if(SOCK_EAGAIN(SOCKERRNO)) {
600
        CURL_TRC_CF(data, cf, "ingress, recvmmsg -> EAGAIN");
601
        goto out;
602
      }
603
      if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
604
        struct ip_quadruple ip;
605
        if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip))
606
          failf(data, "QUIC: connection to %s port %u refused",
607
                ip.remote_ip, ip.remote_port);
608
        result = CURLE_COULDNT_CONNECT;
609
        goto out;
610
      }
611
      curlx_strerror(SOCKERRNO, errstr, sizeof(errstr));
612
      failf(data, "QUIC: recvmmsg() unexpectedly returned %d (errno=%d; %s)",
613
            mcount, SOCKERRNO, errstr);
614
      result = CURLE_RECV_ERROR;
615
      goto out;
616
    }
617
618
    VERBOSE(++calls);
619
    for(i = 0; i < mcount; ++i) {
620
      /* A zero-length UDP packet is no QUIC packet. Ignore. */
621
      if(!mmsg[i].msg_len) {
622
        ++pkts;
623
        continue;
624
      }
625
      total_nread += mmsg[i].msg_len;
626
627
      gso_size = vquic_msghdr_get_udp_gro(&mmsg[i].msg_hdr);
628
      if(gso_size == 0)
629
        gso_size = mmsg[i].msg_len;
630
631
      result = recv_cb(bufs[i], mmsg[i].msg_len, gso_size,
632
                       mmsg[i].msg_hdr.msg_name,
633
                       mmsg[i].msg_hdr.msg_namelen, 0, userp);
634
      if(result)
635
        goto out;
636
      pkts += (mmsg[i].msg_len + gso_size - 1) / gso_size;
637
    }
638
  }
639
640
out:
641
  if(total_nread || result)
642
    CURL_TRC_CF(data, cf,
643
                "vquic_recvmmsg(len=%zu, packets=%zu, calls=%zu) -> %d",
644
                total_nread, pkts, calls, (int)result);
645
  Curl_multi_xfer_sockbuf_release(data, sockbuf);
646
  return result;
647
}
648
649
#elif defined(HAVE_APPLE_MSG_X)
650
651
static CURLcode recvmsg_x_packets(struct Curl_cfilter *cf,
652
                                  struct Curl_easy *data,
653
                                  struct cf_quic_ctx *qctx,
654
                                  size_t max_pkts,
655
                                  Curl_vquic_recv_pkts_cb *recv_cb,
656
                                  void *userp)
657
{
658
#define MSG_X_NUM  64
659
#define MSG_BUF_SIZE  (2048)
660
  struct iovec msg_iov[MSG_X_NUM];
661
  struct msghdr_x mmsg[MSG_X_NUM];
662
  uint8_t msg_ctrl[MSG_X_NUM * CMSG_SPACE(sizeof(int))];
663
  struct sockaddr_storage remote_addr[MSG_X_NUM];
664
  size_t total_nread = 0, pkts = 0;
665
#ifdef CURLVERBOSE
666
  size_t calls = 0;
667
#endif
668
  int mcount, i;
669
  char errstr[STRERROR_LEN];
670
  CURLcode result = CURLE_OK;
671
  size_t gso_size;
672
  char *sockbuf = NULL;
673
  uint8_t (*bufs)[MSG_BUF_SIZE] = NULL;
674
675
  DEBUGASSERT(max_pkts > 0);
676
  result = Curl_multi_xfer_sockbuf_borrow(data, MSG_X_NUM * MSG_BUF_SIZE,
677
                                          &sockbuf);
678
  if(result)
679
    goto out;
680
  bufs = (uint8_t (*)[MSG_BUF_SIZE])sockbuf;
681
682
  total_nread = 0;
683
  while(pkts < max_pkts) {
684
    int n = (int)CURLMIN(CURLMIN(MSG_X_NUM, IOV_MAX), max_pkts);
685
    memset(&mmsg, 0, sizeof(mmsg));
686
    for(i = 0; i < n; ++i) {
687
      msg_iov[i].iov_base = bufs[i];
688
      msg_iov[i].iov_len = sizeof(bufs[i]);
689
      mmsg[i].msg_iov = &msg_iov[i];
690
      mmsg[i].msg_iovlen = 1;
691
      mmsg[i].msg_name = &remote_addr[i];
692
      mmsg[i].msg_namelen = sizeof(remote_addr[i]);
693
      mmsg[i].msg_control = &msg_ctrl[i * CMSG_SPACE(sizeof(int))];
694
      mmsg[i].msg_controllen = CMSG_SPACE(sizeof(int));
695
    }
696
697
#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
698
#pragma GCC diagnostic push
699
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
700
#endif
701
    while((mcount = syscall(SYS_recvmsg_x, qctx->sockfd, mmsg, n, 0)) == -1 &&
702
          (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE))
703
      ;
704
#if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
705
#pragma GCC diagnostic pop
706
#endif
707
    if(mcount == -1) {
708
      if(SOCK_EAGAIN(SOCKERRNO)) {
709
        CURL_TRC_CF(data, cf, "ingress, recvmsg_x -> EAGAIN");
710
        goto out;
711
      }
712
      if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
713
        struct ip_quadruple ip;
714
        if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip))
715
          failf(data, "QUIC: connection to %s port %u refused",
716
                ip.remote_ip, ip.remote_port);
717
        result = CURLE_COULDNT_CONNECT;
718
        goto out;
719
      }
720
      curlx_strerror(SOCKERRNO, errstr, sizeof(errstr));
721
      failf(data, "QUIC: recvmsg_x() unexpectedly returned %d (errno=%d; %s)",
722
            mcount, SOCKERRNO, errstr);
723
      result = CURLE_RECV_ERROR;
724
      goto out;
725
    }
726
727
    VERBOSE(++calls);
728
    for(i = 0; i < mcount; ++i) {
729
      /* A zero-length UDP packet is no QUIC packet. Ignore. */
730
      if(!mmsg[i].msg_datalen) {
731
        ++pkts;
732
        continue;
733
      }
734
      total_nread += mmsg[i].msg_datalen;
735
      gso_size = mmsg[i].msg_datalen;
736
737
      result = recv_cb(bufs[i], mmsg[i].msg_datalen, gso_size,
738
                       mmsg[i].msg_name,
739
                       mmsg[i].msg_namelen, 0, userp);
740
      if(result)
741
        goto out;
742
      pkts += (mmsg[i].msg_datalen + gso_size - 1) / gso_size;
743
    }
744
  }
745
746
out:
747
  if(total_nread || result)
748
    CURL_TRC_CF(data, cf,
749
                "vquic_recvmsg_x(len=%zu, packets=%zu, calls=%zu) -> %d",
750
                total_nread, pkts, calls, (int)result);
751
  Curl_multi_xfer_sockbuf_release(data, sockbuf);
752
  return result;
753
}
754
755
#elif defined(HAVE_SENDMSG)
756
static CURLcode recvmsg_packets(struct Curl_cfilter *cf,
757
                                struct Curl_easy *data,
758
                                struct cf_quic_ctx *qctx,
759
                                size_t max_pkts,
760
                                Curl_vquic_recv_pkts_cb *recv_cb, void *userp)
761
{
762
  struct iovec msg_iov;
763
  struct msghdr msg;
764
  uint8_t buf[64 * 1024];
765
  struct sockaddr_storage remote_addr;
766
  size_t total_nread, pkts, calls;
767
  ssize_t rc;
768
  size_t nread;
769
  char errstr[STRERROR_LEN];
770
  CURLcode result = CURLE_OK;
771
  uint8_t msg_ctrl[CMSG_SPACE(sizeof(int))];
772
  size_t gso_size;
773
774
  DEBUGASSERT(max_pkts > 0);
775
  for(pkts = 0, total_nread = 0, calls = 0; pkts < max_pkts;) {
776
    /* fully initialize this on each call to `recvmsg()`. There seem to
777
     * operating systems out there that mess with `msg_iov.iov_len`. */
778
    memset(&msg, 0, sizeof(msg));
779
    msg_iov.iov_base = buf;
780
    msg_iov.iov_len = sizeof(buf);
781
    msg.msg_iov = &msg_iov;
782
    msg.msg_iovlen = 1;
783
    msg.msg_control = msg_ctrl;
784
    msg.msg_name = &remote_addr;
785
    msg.msg_namelen = sizeof(remote_addr);
786
    msg.msg_controllen = sizeof(msg_ctrl);
787
788
    while((rc = recvmsg(qctx->sockfd, &msg, 0)) == -1 &&
789
          (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE))
790
      ;
791
    if(!curlx_sztouz(rc, &nread)) {
792
      if(SOCK_EAGAIN(SOCKERRNO)) {
793
        goto out;
794
      }
795
      if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
796
        struct ip_quadruple ip;
797
        if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip))
798
          failf(data, "QUIC: connection to %s port %u refused",
799
                ip.remote_ip, ip.remote_port);
800
        result = CURLE_COULDNT_CONNECT;
801
        goto out;
802
      }
803
      curlx_strerror(SOCKERRNO, errstr, sizeof(errstr));
804
      failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d; %s)",
805
            rc, SOCKERRNO, errstr);
806
      result = CURLE_RECV_ERROR;
807
      goto out;
808
    }
809
810
    total_nread += nread;
811
    ++calls;
812
813
    /* A 0-length UDP packet is no QUIC packet */
814
    if(!nread) {
815
      ++pkts;
816
      continue;
817
    }
818
819
    gso_size = vquic_msghdr_get_udp_gro(&msg);
820
    if(gso_size == 0)
821
      gso_size = nread;
822
823
    result = recv_cb(buf, nread, gso_size,
824
                     msg.msg_name, msg.msg_namelen, 0, userp);
825
    if(result)
826
      goto out;
827
    pkts += (nread + gso_size - 1) / gso_size;
828
  }
829
830
out:
831
  if(total_nread || result)
832
    CURL_TRC_CF(data, cf,
833
                "vquic_recvmsg(len=%zu, packets=%zu, calls=%zu) -> %d",
834
                total_nread, pkts, calls, (int)result);
835
  return result;
836
}
837
838
#else /* HAVE_SENDMMSG || HAVE_SENDMSG */
839
static CURLcode recvfrom_packets(struct Curl_cfilter *cf,
840
                                 struct Curl_easy *data,
841
                                 struct cf_quic_ctx *qctx,
842
                                 size_t max_pkts,
843
                                 Curl_vquic_recv_pkts_cb *recv_cb, void *userp)
844
{
845
  uint8_t buf[64 * 1024];
846
  int bufsize = (int)sizeof(buf);
847
  struct sockaddr_storage remote_addr;
848
  socklen_t remote_addrlen = sizeof(remote_addr);
849
  size_t total_nread, pkts, calls = 0, nread;
850
  ssize_t rv;
851
  char errstr[STRERROR_LEN];
852
  CURLcode result = CURLE_OK;
853
854
  DEBUGASSERT(max_pkts > 0);
855
  for(pkts = 0, total_nread = 0; pkts < max_pkts;) {
856
    while((rv = recvfrom(qctx->sockfd, (char *)buf, bufsize, 0,
857
                         (struct sockaddr *)&remote_addr,
858
                         &remote_addrlen)) == -1 &&
859
          (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE))
860
      ;
861
    if(!curlx_sztouz(rv, &nread)) {
862
      if(SOCK_EAGAIN(SOCKERRNO)) {
863
        CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN");
864
        goto out;
865
      }
866
      if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
867
        struct ip_quadruple ip;
868
        if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip))
869
          failf(data, "QUIC: connection to %s port %u refused",
870
                ip.remote_ip, ip.remote_port);
871
        result = CURLE_COULDNT_CONNECT;
872
        goto out;
873
      }
874
      curlx_strerror(SOCKERRNO, errstr, sizeof(errstr));
875
      failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d; %s)",
876
            rv, SOCKERRNO, errstr);
877
      result = CURLE_RECV_ERROR;
878
      goto out;
879
    }
880
881
    ++pkts;
882
    ++calls;
883
884
    /* A 0-length UDP packet is no QUIC packet */
885
    if(!nread)
886
      continue;
887
888
    total_nread += nread;
889
    result = recv_cb(buf, nread, nread, &remote_addr, remote_addrlen,
890
                     0, userp);
891
    if(result)
892
      goto out;
893
  }
894
895
out:
896
  if(total_nread || result)
897
    CURL_TRC_CF(data, cf,
898
                "vquic_recvfrom(len=%zu, packets=%zu, calls=%zu) -> %d",
899
                total_nread, pkts, calls, (int)result);
900
  return result;
901
}
902
#endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */
903
904
CURLcode Curl_vquic_recv_packets(struct Curl_cfilter *cf,
905
                                 struct Curl_easy *data,
906
                                 struct cf_quic_ctx *qctx,
907
                                 size_t max_pkts,
908
                                 Curl_vquic_recv_pkts_cb *recv_cb, void *userp)
909
{
910
  CURLcode result;
911
#ifdef HAVE_SENDMMSG
912
  result = recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp);
913
#elif defined(HAVE_APPLE_MSG_X)
914
  result = recvmsg_x_packets(cf, data, qctx, max_pkts, recv_cb, userp);
915
#elif defined(HAVE_SENDMSG)
916
  result = recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp);
917
#else
918
  result = recvfrom_packets(cf, data, qctx, max_pkts, recv_cb, userp);
919
#endif
920
  if(!result) {
921
    if(!qctx->got_first_byte) {
922
      qctx->got_first_byte = TRUE;
923
      qctx->first_byte_at = qctx->last_op;
924
    }
925
    qctx->last_io = qctx->last_op;
926
  }
927
  return result;
928
}
929
930
/*
931
 * If the QLOGDIR environment variable is set, open and return a file
932
 * descriptor to write the log to.
933
 *
934
 * This function returns error if something failed outside of failing to
935
 * create the file. Open file success is deemed by seeing if the returned fd
936
 * is != -1.
937
 */
938
CURLcode Curl_qlogdir(struct Curl_easy *data,
939
                      unsigned char *scid,
940
                      size_t scidlen,
941
                      int *qlogfdp)
942
{
943
  char *qlog_dir = curl_getenv("QLOGDIR");
944
  *qlogfdp = -1;
945
  if(qlog_dir) {
946
    struct dynbuf fname;
947
    CURLcode result;
948
    unsigned int i;
949
    curlx_dyn_init(&fname, DYN_QLOG_NAME);
950
    result = curlx_dyn_add(&fname, qlog_dir);
951
    if(!result)
952
      result = curlx_dyn_add(&fname, "/");
953
    for(i = 0; (i < scidlen) && !result; i++) {
954
      char hex[3];
955
      curl_msnprintf(hex, 3, "%02x", scid[i]);
956
      result = curlx_dyn_add(&fname, hex);
957
    }
958
    if(!result)
959
      result = curlx_dyn_add(&fname, ".sqlog");
960
961
    if(!result) {
962
      int qlogfd = curlx_open(curlx_dyn_ptr(&fname),
963
                              O_WRONLY | O_CREAT | CURL_O_BINARY,
964
                              data->set.new_file_perms
965
#ifdef _WIN32
966
                              & (_S_IREAD | _S_IWRITE)
967
#endif
968
                              );
969
      if(qlogfd != -1)
970
        *qlogfdp = qlogfd;
971
    }
972
    curlx_dyn_free(&fname);
973
    curlx_free(qlog_dir);
974
    if(result)
975
      return result;
976
  }
977
978
  return CURLE_OK;
979
}
980
981
CURLcode Curl_cf_quic_insert_after(struct Curl_cfilter *cf_at,
982
                                   struct Curl_easy *data,
983
                                   struct Curl_peer *origin,
984
                                   struct Curl_peer *peer)
985
{
986
  CURLcode result;
987
988
  (void)data; /* not used in all cases and compilers are stupid */
989
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
990
  result = Curl_cf_ngtcp2_insert_after(cf_at, origin, peer);
991
#elif defined(USE_QUICHE)
992
  result = Curl_cf_quiche_insert_after(cf_at, origin, peer);
993
#else
994
  (void)cf_at;
995
  (void)origin;
996
  (void)peer;
997
  result = CURLE_NOT_BUILT_IN;
998
#endif
999
1000
#if defined(USE_HTTPSRR) && defined(USE_ECH)
1001
  /* When using ECH, kick off the HTTPS-RR resolve */
1002
  if(!result && (origin->scheme->family == CURLPROTO_HTTP) &&
1003
     CURLECH_ENABLED(data) &&
1004
     Curl_ssl_supports(data, SSLSUPP_ECH) &&
1005
     (data->set.tls_ech != CURLECH_GREASE) &&
1006
     !data->set.str[STRING_ECH_CONFIG]) {
1007
    result = Curl_conn_dns_add_https_resolve(data, cf_at->conn,
1008
                                             cf_at->sockindex, origin);
1009
  }
1010
#endif /* USE_HTTPSRR && USE_ECH */
1011
  return result;
1012
}
1013
1014
CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
1015
                             struct Curl_easy *data,
1016
                             struct Curl_peer *origin,
1017
                             struct Curl_peer *peer,
1018
                             uint8_t transport_peer,
1019
                             struct connectdata *conn,
1020
                             struct Curl_sockaddr_ex *addr,
1021
                             struct Curl_peer *tunnel_peer,
1022
                             uint8_t tunnel_transport)
1023
{
1024
  (void)transport_peer;
1025
  (void)tunnel_transport;
1026
  (void)tunnel_peer;
1027
  DEBUGASSERT(transport_peer == TRNSPRT_QUIC);
1028
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
1029
  return Curl_cf_ngtcp2_create(pcf, data, origin, peer, conn, addr);
1030
#elif defined(USE_QUICHE)
1031
  return Curl_cf_quiche_create(pcf, data, origin, peer, conn, addr);
1032
#else
1033
  *pcf = NULL;
1034
  (void)data;
1035
  (void)origin;
1036
  (void)peer;
1037
  (void)conn;
1038
  (void)addr;
1039
  (void)tunnel_peer;
1040
  (void)tunnel_transport;
1041
  return CURLE_NOT_BUILT_IN;
1042
#endif
1043
}
1044
1045
#if !defined(CURL_DISABLE_PROXY) && defined(USE_PROXY_HTTP3)
1046
1047
CURLcode Curl_cf_h3_proxy_insert_after(struct Curl_cfilter *cf_at,
1048
                                       struct Curl_easy *data,
1049
                                       struct Curl_peer *origin,
1050
                                       struct Curl_peer *peer,
1051
                                       struct Curl_peer *tunnel_peer,
1052
                                       uint8_t tunnel_transport)
1053
{
1054
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
1055
  return Curl_cf_ngtcp2_proxy_insert_after(cf_at, data, origin, peer,
1056
                                           tunnel_peer, tunnel_transport);
1057
#else
1058
  (void)cf_at;
1059
  (void)data;
1060
  (void)origin;
1061
  (void)peer;
1062
  (void)tunnel_peer;
1063
  (void)tunnel_transport;
1064
  return CURLE_NOT_BUILT_IN;
1065
#endif
1066
}
1067
1068
CURLcode Curl_cf_h3_proxy_create(struct Curl_cfilter **pcf,
1069
                                 struct Curl_easy *data,
1070
                                 struct Curl_peer *origin,
1071
                                 struct Curl_peer *peer,
1072
                                 uint8_t transport_peer,
1073
                                 struct connectdata *conn,
1074
                                 struct Curl_sockaddr_ex *addr,
1075
                                 struct Curl_peer *tunnel_peer,
1076
                                 uint8_t tunnel_transport)
1077
{
1078
  DEBUGASSERT(transport_peer == TRNSPRT_QUIC);
1079
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
1080
  return Curl_cf_ngtcp2_proxy_create(pcf, data, origin, peer, transport_peer,
1081
                                     conn, addr,
1082
                                     tunnel_peer, tunnel_transport);
1083
#else
1084
  *pcf = NULL;
1085
  (void)data;
1086
  (void)conn;
1087
  (void)addr;
1088
  (void)peer;
1089
  (void)transport_peer;
1090
  (void)tunnel_peer;
1091
  (void)tunnel_transport;
1092
  return CURLE_NOT_BUILT_IN;
1093
#endif
1094
}
1095
1096
#endif /* !CURL_DISABLE_PROXY && USE_PROXY_HTTP3 */
1097
1098
CURLcode Curl_conn_may_http3(struct Curl_easy *data,
1099
                             const struct connectdata *conn,
1100
                             unsigned char transport)
1101
{
1102
  if(transport == TRNSPRT_UNIX) {
1103
    failf(data, "HTTP/3 cannot be used over UNIX domain sockets");
1104
    return CURLE_QUIC_CONNECT_ERROR;
1105
  }
1106
  if(!(data->state.origin->scheme->flags & PROTOPT_SSL)) {
1107
    failf(data, "HTTP/3 requested for non-HTTPS URL");
1108
    return CURLE_URL_MALFORMAT;
1109
  }
1110
#ifndef CURL_DISABLE_PROXY
1111
  if(conn->socks_proxy.peer) {
1112
    failf(data, "HTTP/3 is not supported over a SOCKS proxy");
1113
    return CURLE_URL_MALFORMAT;
1114
  }
1115
#else
1116
  (void)conn;
1117
#endif
1118
1119
  return CURLE_OK;
1120
}
1121
1122
#ifdef CURLVERBOSE
1123
const char *Curl_vquic_h3_err_str(uint64_t error_code)
1124
{
1125
  if(error_code <= UINT_MAX) {
1126
    switch((unsigned int)error_code) {
1127
    case CURL_H3_ERR_NO_ERROR:
1128
      return "NO_ERROR";
1129
    case CURL_H3_ERR_GENERAL_PROTOCOL_ERROR:
1130
      return "GENERAL_PROTOCOL_ERROR";
1131
    case CURL_H3_ERR_INTERNAL_ERROR:
1132
      return "INTERNAL_ERROR";
1133
    case CURL_H3_ERR_STREAM_CREATION_ERROR:
1134
      return "STREAM_CREATION_ERROR";
1135
    case CURL_H3_ERR_CLOSED_CRITICAL_STREAM:
1136
      return "CLOSED_CRITICAL_STREAM";
1137
    case CURL_H3_ERR_FRAME_UNEXPECTED:
1138
      return "FRAME_UNEXPECTED";
1139
    case CURL_H3_ERR_FRAME_ERROR:
1140
      return "FRAME_ERROR";
1141
    case CURL_H3_ERR_EXCESSIVE_LOAD:
1142
      return "EXCESSIVE_LOAD";
1143
    case CURL_H3_ERR_ID_ERROR:
1144
      return "ID_ERROR";
1145
    case CURL_H3_ERR_SETTINGS_ERROR:
1146
      return "SETTINGS_ERROR";
1147
    case CURL_H3_ERR_MISSING_SETTINGS:
1148
      return "MISSING_SETTINGS";
1149
    case CURL_H3_ERR_REQUEST_REJECTED:
1150
      return "REQUEST_REJECTED";
1151
    case CURL_H3_ERR_REQUEST_CANCELLED:
1152
      return "REQUEST_CANCELLED";
1153
    case CURL_H3_ERR_REQUEST_INCOMPLETE:
1154
      return "REQUEST_INCOMPLETE";
1155
    case CURL_H3_ERR_MESSAGE_ERROR:
1156
      return "MESSAGE_ERROR";
1157
    case CURL_H3_ERR_CONNECT_ERROR:
1158
      return "CONNECT_ERROR";
1159
    case CURL_H3_ERR_VERSION_FALLBACK:
1160
      return "VERSION_FALLBACK";
1161
    default:
1162
      break;
1163
    }
1164
  }
1165
  /* RFC 9114 ch. 8.1 + 9, reserved future error codes that are NO_ERROR */
1166
  if((error_code >= 0x21) && !((error_code - 0x21) % 0x1f))
1167
    return "NO_ERROR";
1168
  return "unknown";
1169
}
1170
#endif /* CURLVERBOSE */
1171
1172
#if defined(USE_NGTCP2) || defined(USE_NGHTTP3)
1173
1174
static void *vquic_ngtcp2_malloc(size_t size, void *user_data)
1175
{
1176
  (void)user_data;
1177
  return Curl_cmalloc(size);
1178
}
1179
1180
static void vquic_ngtcp2_free(void *ptr, void *user_data)
1181
{
1182
  (void)user_data;
1183
  Curl_cfree(ptr);
1184
}
1185
1186
static void *vquic_ngtcp2_calloc(size_t nmemb, size_t size, void *user_data)
1187
{
1188
  (void)user_data;
1189
  return Curl_ccalloc(nmemb, size);
1190
}
1191
1192
static void *vquic_ngtcp2_realloc(void *ptr, size_t size, void *user_data)
1193
{
1194
  (void)user_data;
1195
  return Curl_crealloc(ptr, size);
1196
}
1197
1198
#ifdef USE_NGTCP2
1199
static struct ngtcp2_mem vquic_ngtcp2_mem = {
1200
  NULL,
1201
  vquic_ngtcp2_malloc,
1202
  vquic_ngtcp2_free,
1203
  vquic_ngtcp2_calloc,
1204
  vquic_ngtcp2_realloc
1205
};
1206
struct ngtcp2_mem *Curl_ngtcp2_mem(void)
1207
{
1208
  return &vquic_ngtcp2_mem;
1209
}
1210
#endif
1211
1212
#ifdef USE_NGHTTP3
1213
static struct nghttp3_mem vquic_nghttp3_mem = {
1214
  NULL,
1215
  vquic_ngtcp2_malloc,
1216
  vquic_ngtcp2_free,
1217
  vquic_ngtcp2_calloc,
1218
  vquic_ngtcp2_realloc
1219
};
1220
struct nghttp3_mem *Curl_nghttp3_mem(void)
1221
{
1222
  return &vquic_nghttp3_mem;
1223
}
1224
#endif
1225
1226
#endif /* USE_NGTCP2 || USE_NGHTTP3 */
1227
1228
#else /* CURL_DISABLE_HTTP || !USE_HTTP3 */
1229
1230
CURLcode Curl_conn_may_http3(struct Curl_easy *data,
1231
                             const struct connectdata *conn,
1232
                             unsigned char transport)
1233
0
{
1234
0
  (void)data;
1235
0
  (void)conn;
1236
0
  (void)transport;
1237
0
  DEBUGF(infof(data, "QUIC is not supported in this build"));
1238
0
  return CURLE_NOT_BUILT_IN;
1239
0
}
1240
1241
#endif /* !CURL_DISABLE_HTTP && USE_HTTP3 */