Coverage Report

Created: 2026-09-14 07:05

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