Coverage Report

Created: 2026-08-14 09:29

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