Coverage Report

Created: 2026-10-15 06:19

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