Coverage Report

Created: 2023-12-08 06:48

/src/curl/lib/vquic/vquic.c
Line
Count
Source (jump to first uncovered line)
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
25
/* WIP, experimental: use recvmmsg() on linux
26
 * we have no configure check, yet
27
 * and also it is only available for _GNU_SOURCE, which
28
 * we do not use otherwise.
29
#define HAVE_SENDMMSG
30
 */
31
#if defined(HAVE_SENDMMSG)
32
#define _GNU_SOURCE
33
#include <sys/socket.h>
34
#undef _GNU_SOURCE
35
#endif
36
37
#include "curl_setup.h"
38
39
#ifdef HAVE_FCNTL_H
40
#include <fcntl.h>
41
#endif
42
#include "urldata.h"
43
#include "bufq.h"
44
#include "dynbuf.h"
45
#include "cfilters.h"
46
#include "curl_trc.h"
47
#include "curl_msh3.h"
48
#include "curl_ngtcp2.h"
49
#include "curl_quiche.h"
50
#include "rand.h"
51
#include "vquic.h"
52
#include "vquic_int.h"
53
#include "strerror.h"
54
55
/* The last 3 #include files should be in this order */
56
#include "curl_printf.h"
57
#include "curl_memory.h"
58
#include "memdebug.h"
59
60
61
#ifdef ENABLE_QUIC
62
63
#ifdef O_BINARY
64
#define QLOGMODE O_WRONLY|O_CREAT|O_BINARY
65
#else
66
#define QLOGMODE O_WRONLY|O_CREAT
67
#endif
68
69
#define NW_CHUNK_SIZE     (64 * 1024)
70
#define NW_SEND_CHUNKS    2
71
72
73
void Curl_quic_ver(char *p, size_t len)
74
{
75
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
76
  Curl_ngtcp2_ver(p, len);
77
#elif defined(USE_QUICHE)
78
  Curl_quiche_ver(p, len);
79
#elif defined(USE_MSH3)
80
  Curl_msh3_ver(p, len);
81
#endif
82
}
83
84
CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx)
85
{
86
  Curl_bufq_init2(&qctx->sendbuf, NW_CHUNK_SIZE, NW_SEND_CHUNKS,
87
                  BUFQ_OPT_SOFT_LIMIT);
88
#if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG)
89
  qctx->no_gso = FALSE;
90
#else
91
  qctx->no_gso = TRUE;
92
#endif
93
#ifdef DEBUGBUILD
94
  {
95
    char *p = getenv("CURL_DBG_QUIC_WBLOCK");
96
    if(p) {
97
      long l = strtol(p, NULL, 10);
98
      if(l >= 0 && l <= 100)
99
        qctx->wblock_percent = (int)l;
100
    }
101
  }
102
#endif
103
  vquic_ctx_update_time(qctx);
104
105
  return CURLE_OK;
106
}
107
108
void vquic_ctx_free(struct cf_quic_ctx *qctx)
109
{
110
  Curl_bufq_free(&qctx->sendbuf);
111
}
112
113
void vquic_ctx_update_time(struct cf_quic_ctx *qctx)
114
{
115
  qctx->last_op = Curl_now();
116
}
117
118
static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
119
                                   struct Curl_easy *data,
120
                                   struct cf_quic_ctx *qctx,
121
                                   const uint8_t *pkt, size_t pktlen,
122
                                   size_t gsolen, size_t *psent);
123
124
static CURLcode do_sendmsg(struct Curl_cfilter *cf,
125
                           struct Curl_easy *data,
126
                           struct cf_quic_ctx *qctx,
127
                           const uint8_t *pkt, size_t pktlen, size_t gsolen,
128
                           size_t *psent)
129
{
130
#ifdef HAVE_SENDMSG
131
  struct iovec msg_iov;
132
  struct msghdr msg = {0};
133
  ssize_t sent;
134
#if defined(__linux__) && defined(UDP_SEGMENT)
135
  uint8_t msg_ctrl[32];
136
  struct cmsghdr *cm;
137
#endif
138
139
  *psent = 0;
140
  msg_iov.iov_base = (uint8_t *)pkt;
141
  msg_iov.iov_len = pktlen;
142
  msg.msg_iov = &msg_iov;
143
  msg.msg_iovlen = 1;
144
145
#if defined(__linux__) && defined(UDP_SEGMENT)
146
  if(pktlen > gsolen) {
147
    /* Only set this, when we need it. macOS, for example,
148
     * does not seem to like a msg_control of length 0. */
149
    msg.msg_control = msg_ctrl;
150
    assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(uint16_t)));
151
    msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t));
152
    cm = CMSG_FIRSTHDR(&msg);
153
    cm->cmsg_level = SOL_UDP;
154
    cm->cmsg_type = UDP_SEGMENT;
155
    cm->cmsg_len = CMSG_LEN(sizeof(uint16_t));
156
    *(uint16_t *)(void *)CMSG_DATA(cm) = gsolen & 0xffff;
157
  }
158
#endif
159
160
161
  while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR)
162
    ;
163
164
  if(sent == -1) {
165
    switch(SOCKERRNO) {
166
    case EAGAIN:
167
#if EAGAIN != EWOULDBLOCK
168
    case EWOULDBLOCK:
169
#endif
170
      return CURLE_AGAIN;
171
    case EMSGSIZE:
172
      /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */
173
      break;
174
    case EIO:
175
      if(pktlen > gsolen) {
176
        /* GSO failure */
177
        failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent,
178
              SOCKERRNO);
179
        qctx->no_gso = TRUE;
180
        return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
181
      }
182
      /* FALLTHROUGH */
183
    default:
184
      failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO);
185
      return CURLE_SEND_ERROR;
186
    }
187
  }
188
  else {
189
    assert(pktlen == (size_t)sent);
190
  }
191
#else
192
  ssize_t sent;
193
  (void)gsolen;
194
195
  *psent = 0;
196
197
  while((sent = send(qctx->sockfd,
198
                     (const char *)pkt, (SEND_TYPE_ARG3)pktlen, 0)) == -1 &&
199
        SOCKERRNO == EINTR)
200
    ;
201
202
  if(sent == -1) {
203
    if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
204
      return CURLE_AGAIN;
205
    }
206
    else {
207
      failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO);
208
      if(SOCKERRNO != EMSGSIZE) {
209
        return CURLE_SEND_ERROR;
210
      }
211
      /* UDP datagram is too large; caused by PMTUD. Just let it be
212
         lost. */
213
    }
214
  }
215
#endif
216
  (void)cf;
217
  *psent = pktlen;
218
219
  return CURLE_OK;
220
}
221
222
static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
223
                                   struct Curl_easy *data,
224
                                   struct cf_quic_ctx *qctx,
225
                                   const uint8_t *pkt, size_t pktlen,
226
                                   size_t gsolen, size_t *psent)
227
{
228
  const uint8_t *p, *end = pkt + pktlen;
229
  size_t sent;
230
231
  *psent = 0;
232
233
  for(p = pkt; p < end; p += gsolen) {
234
    size_t len = CURLMIN(gsolen, (size_t)(end - p));
235
    CURLcode curlcode = do_sendmsg(cf, data, qctx, p, len, len, &sent);
236
    if(curlcode != CURLE_OK) {
237
      return curlcode;
238
    }
239
    *psent += sent;
240
  }
241
242
  return CURLE_OK;
243
}
244
245
static CURLcode vquic_send_packets(struct Curl_cfilter *cf,
246
                                   struct Curl_easy *data,
247
                                   struct cf_quic_ctx *qctx,
248
                                   const uint8_t *pkt, size_t pktlen,
249
                                   size_t gsolen, size_t *psent)
250
{
251
  CURLcode result;
252
#ifdef DEBUGBUILD
253
  /* simulate network blocking/partial writes */
254
  if(qctx->wblock_percent > 0) {
255
    unsigned char c;
256
    Curl_rand(data, &c, 1);
257
    if(c >= ((100-qctx->wblock_percent)*256/100)) {
258
      CURL_TRC_CF(data, cf, "vquic_flush() simulate EWOULDBLOCK");
259
      return CURLE_AGAIN;
260
    }
261
  }
262
#endif
263
  if(qctx->no_gso && pktlen > gsolen) {
264
    result = send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
265
  }
266
  else {
267
    result = do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent);
268
  }
269
  if(!result)
270
    qctx->last_io = qctx->last_op;
271
  return result;
272
}
273
274
CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data,
275
                     struct cf_quic_ctx *qctx)
276
{
277
  const unsigned char *buf;
278
  size_t blen, sent;
279
  CURLcode result;
280
  size_t gsolen;
281
282
  while(Curl_bufq_peek(&qctx->sendbuf, &buf, &blen)) {
283
    gsolen = qctx->gsolen;
284
    if(qctx->split_len) {
285
      gsolen = qctx->split_gsolen;
286
      if(blen > qctx->split_len)
287
        blen = qctx->split_len;
288
    }
289
290
    result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent);
291
    CURL_TRC_CF(data, cf, "vquic_send(len=%zu, gso=%zu) -> %d, sent=%zu",
292
                blen, gsolen, result, sent);
293
    if(result) {
294
      if(result == CURLE_AGAIN) {
295
        Curl_bufq_skip(&qctx->sendbuf, sent);
296
        if(qctx->split_len)
297
          qctx->split_len -= sent;
298
      }
299
      return result;
300
    }
301
    Curl_bufq_skip(&qctx->sendbuf, sent);
302
    if(qctx->split_len)
303
      qctx->split_len -= sent;
304
  }
305
  return CURLE_OK;
306
}
307
308
CURLcode vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data,
309
                        struct cf_quic_ctx *qctx, size_t gsolen)
310
{
311
  qctx->gsolen = gsolen;
312
  return vquic_flush(cf, data, qctx);
313
}
314
315
CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data,
316
                               struct cf_quic_ctx *qctx, size_t gsolen,
317
                               size_t tail_len, size_t tail_gsolen)
318
{
319
  DEBUGASSERT(Curl_bufq_len(&qctx->sendbuf) > tail_len);
320
  qctx->split_len = Curl_bufq_len(&qctx->sendbuf) - tail_len;
321
  qctx->split_gsolen = gsolen;
322
  qctx->gsolen = tail_gsolen;
323
  CURL_TRC_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]",
324
              qctx->split_len, qctx->split_gsolen,
325
              tail_len, qctx->gsolen);
326
  return vquic_flush(cf, data, qctx);
327
}
328
329
#ifdef HAVE_SENDMMSG
330
static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
331
                                 struct Curl_easy *data,
332
                                 struct cf_quic_ctx *qctx,
333
                                 size_t max_pkts,
334
                                 vquic_recv_pkt_cb *recv_cb, void *userp)
335
{
336
#define MMSG_NUM  64
337
  struct iovec msg_iov[MMSG_NUM];
338
  struct mmsghdr mmsg[MMSG_NUM];
339
  uint8_t bufs[MMSG_NUM][2*1024];
340
  struct sockaddr_storage remote_addr[MMSG_NUM];
341
  size_t total_nread, pkts;
342
  int mcount, i, n;
343
  char errstr[STRERROR_LEN];
344
  CURLcode result = CURLE_OK;
345
346
  DEBUGASSERT(max_pkts > 0);
347
  pkts = 0;
348
  total_nread = 0;
349
  while(pkts < max_pkts) {
350
    n = (int)CURLMIN(MMSG_NUM, max_pkts);
351
    memset(&mmsg, 0, sizeof(mmsg));
352
    for(i = 0; i < n; ++i) {
353
      msg_iov[i].iov_base = bufs[i];
354
      msg_iov[i].iov_len = (int)sizeof(bufs[i]);
355
      mmsg[i].msg_hdr.msg_iov = &msg_iov[i];
356
      mmsg[i].msg_hdr.msg_iovlen = 1;
357
      mmsg[i].msg_hdr.msg_name = &remote_addr[i];
358
      mmsg[i].msg_hdr.msg_namelen = sizeof(remote_addr[i]);
359
    }
360
361
    while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 &&
362
          SOCKERRNO == EINTR)
363
      ;
364
    if(mcount == -1) {
365
      if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
366
        CURL_TRC_CF(data, cf, "ingress, recvmmsg -> EAGAIN");
367
        goto out;
368
      }
369
      if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
370
        const char *r_ip = NULL;
371
        int r_port = 0;
372
        Curl_cf_socket_peek(cf->next, data, NULL, NULL,
373
                            &r_ip, &r_port, NULL, NULL);
374
        failf(data, "QUIC: connection to %s port %u refused",
375
              r_ip, r_port);
376
        result = CURLE_COULDNT_CONNECT;
377
        goto out;
378
      }
379
      Curl_strerror(SOCKERRNO, errstr, sizeof(errstr));
380
      failf(data, "QUIC: recvmsg() unexpectedly returned %d (errno=%d; %s)",
381
                  mcount, SOCKERRNO, errstr);
382
      result = CURLE_RECV_ERROR;
383
      goto out;
384
    }
385
386
    CURL_TRC_CF(data, cf, "recvmmsg() -> %d packets", mcount);
387
    pkts += mcount;
388
    for(i = 0; i < mcount; ++i) {
389
      total_nread += mmsg[i].msg_len;
390
      result = recv_cb(bufs[i], mmsg[i].msg_len,
391
                       mmsg[i].msg_hdr.msg_name, mmsg[i].msg_hdr.msg_namelen,
392
                       0, userp);
393
      if(result)
394
        goto out;
395
    }
396
  }
397
398
out:
399
  if(total_nread || result)
400
    CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
401
                pkts, total_nread, result);
402
  return result;
403
}
404
405
#elif defined(HAVE_SENDMSG)
406
static CURLcode recvmsg_packets(struct Curl_cfilter *cf,
407
                                struct Curl_easy *data,
408
                                struct cf_quic_ctx *qctx,
409
                                size_t max_pkts,
410
                                vquic_recv_pkt_cb *recv_cb, void *userp)
411
{
412
  struct iovec msg_iov;
413
  struct msghdr msg;
414
  uint8_t buf[64*1024];
415
  struct sockaddr_storage remote_addr;
416
  size_t total_nread, pkts;
417
  ssize_t nread;
418
  char errstr[STRERROR_LEN];
419
  CURLcode result = CURLE_OK;
420
421
  msg_iov.iov_base = buf;
422
  msg_iov.iov_len = (int)sizeof(buf);
423
424
  memset(&msg, 0, sizeof(msg));
425
  msg.msg_iov = &msg_iov;
426
  msg.msg_iovlen = 1;
427
428
  DEBUGASSERT(max_pkts > 0);
429
  for(pkts = 0, total_nread = 0; pkts < max_pkts;) {
430
    msg.msg_name = &remote_addr;
431
    msg.msg_namelen = sizeof(remote_addr);
432
    while((nread = recvmsg(qctx->sockfd, &msg, 0)) == -1 &&
433
          SOCKERRNO == EINTR)
434
      ;
435
    if(nread == -1) {
436
      if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
437
        goto out;
438
      }
439
      if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
440
        const char *r_ip = NULL;
441
        int r_port = 0;
442
        Curl_cf_socket_peek(cf->next, data, NULL, NULL,
443
                            &r_ip, &r_port, NULL, NULL);
444
        failf(data, "QUIC: connection to %s port %u refused",
445
              r_ip, r_port);
446
        result = CURLE_COULDNT_CONNECT;
447
        goto out;
448
      }
449
      Curl_strerror(SOCKERRNO, errstr, sizeof(errstr));
450
      failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d; %s)",
451
                  nread, SOCKERRNO, errstr);
452
      result = CURLE_RECV_ERROR;
453
      goto out;
454
    }
455
456
    ++pkts;
457
    total_nread += (size_t)nread;
458
    result = recv_cb(buf, (size_t)nread, msg.msg_name, msg.msg_namelen,
459
                     0, userp);
460
    if(result)
461
      goto out;
462
  }
463
464
out:
465
  if(total_nread || result)
466
    CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
467
                pkts, total_nread, result);
468
  return result;
469
}
470
471
#else /* HAVE_SENDMMSG || HAVE_SENDMSG */
472
static CURLcode recvfrom_packets(struct Curl_cfilter *cf,
473
                                 struct Curl_easy *data,
474
                                 struct cf_quic_ctx *qctx,
475
                                 size_t max_pkts,
476
                                 vquic_recv_pkt_cb *recv_cb, void *userp)
477
{
478
  uint8_t buf[64*1024];
479
  int bufsize = (int)sizeof(buf);
480
  struct sockaddr_storage remote_addr;
481
  socklen_t remote_addrlen = sizeof(remote_addr);
482
  size_t total_nread, pkts;
483
  ssize_t nread;
484
  char errstr[STRERROR_LEN];
485
  CURLcode result = CURLE_OK;
486
487
  DEBUGASSERT(max_pkts > 0);
488
  for(pkts = 0, total_nread = 0; pkts < max_pkts;) {
489
    while((nread = recvfrom(qctx->sockfd, (char *)buf, bufsize, 0,
490
                            (struct sockaddr *)&remote_addr,
491
                            &remote_addrlen)) == -1 &&
492
          SOCKERRNO == EINTR)
493
      ;
494
    if(nread == -1) {
495
      if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
496
        CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN");
497
        goto out;
498
      }
499
      if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
500
        const char *r_ip = NULL;
501
        int r_port = 0;
502
        Curl_cf_socket_peek(cf->next, data, NULL, NULL,
503
                            &r_ip, &r_port, NULL, NULL);
504
        failf(data, "QUIC: connection to %s port %u refused",
505
              r_ip, r_port);
506
        result = CURLE_COULDNT_CONNECT;
507
        goto out;
508
      }
509
      Curl_strerror(SOCKERRNO, errstr, sizeof(errstr));
510
      failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d; %s)",
511
                  nread, SOCKERRNO, errstr);
512
      result = CURLE_RECV_ERROR;
513
      goto out;
514
    }
515
516
    ++pkts;
517
    total_nread += (size_t)nread;
518
    result = recv_cb(buf, (size_t)nread, &remote_addr, remote_addrlen,
519
                     0, userp);
520
    if(result)
521
      goto out;
522
  }
523
524
out:
525
  if(total_nread || result)
526
    CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d",
527
                pkts, total_nread, result);
528
  return result;
529
}
530
#endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */
531
532
CURLcode vquic_recv_packets(struct Curl_cfilter *cf,
533
                            struct Curl_easy *data,
534
                            struct cf_quic_ctx *qctx,
535
                            size_t max_pkts,
536
                            vquic_recv_pkt_cb *recv_cb, void *userp)
537
{
538
  CURLcode result;
539
#if defined(HAVE_SENDMMSG)
540
  result = recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp);
541
#elif defined(HAVE_SENDMSG)
542
  result = recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp);
543
#else
544
  result = recvfrom_packets(cf, data, qctx, max_pkts, recv_cb, userp);
545
#endif
546
  if(!result)
547
    qctx->last_io = qctx->last_op;
548
  return result;
549
}
550
551
/*
552
 * If the QLOGDIR environment variable is set, open and return a file
553
 * descriptor to write the log to.
554
 *
555
 * This function returns error if something failed outside of failing to
556
 * create the file. Open file success is deemed by seeing if the returned fd
557
 * is != -1.
558
 */
559
CURLcode Curl_qlogdir(struct Curl_easy *data,
560
                      unsigned char *scid,
561
                      size_t scidlen,
562
                      int *qlogfdp)
563
{
564
  const char *qlog_dir = getenv("QLOGDIR");
565
  *qlogfdp = -1;
566
  if(qlog_dir) {
567
    struct dynbuf fname;
568
    CURLcode result;
569
    unsigned int i;
570
    Curl_dyn_init(&fname, DYN_QLOG_NAME);
571
    result = Curl_dyn_add(&fname, qlog_dir);
572
    if(!result)
573
      result = Curl_dyn_add(&fname, "/");
574
    for(i = 0; (i < scidlen) && !result; i++) {
575
      char hex[3];
576
      msnprintf(hex, 3, "%02x", scid[i]);
577
      result = Curl_dyn_add(&fname, hex);
578
    }
579
    if(!result)
580
      result = Curl_dyn_add(&fname, ".sqlog");
581
582
    if(!result) {
583
      int qlogfd = open(Curl_dyn_ptr(&fname), QLOGMODE,
584
                        data->set.new_file_perms);
585
      if(qlogfd != -1)
586
        *qlogfdp = qlogfd;
587
    }
588
    Curl_dyn_free(&fname);
589
    if(result)
590
      return result;
591
  }
592
593
  return CURLE_OK;
594
}
595
596
CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
597
                             struct Curl_easy *data,
598
                             struct connectdata *conn,
599
                             const struct Curl_addrinfo *ai,
600
                             int transport)
601
{
602
  (void)transport;
603
  DEBUGASSERT(transport == TRNSPRT_QUIC);
604
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
605
  return Curl_cf_ngtcp2_create(pcf, data, conn, ai);
606
#elif defined(USE_QUICHE)
607
  return Curl_cf_quiche_create(pcf, data, conn, ai);
608
#elif defined(USE_MSH3)
609
  return Curl_cf_msh3_create(pcf, data, conn, ai);
610
#else
611
  *pcf = NULL;
612
  (void)data;
613
  (void)conn;
614
  (void)ai;
615
  return CURLE_NOT_BUILT_IN;
616
#endif
617
}
618
619
bool Curl_conn_is_http3(const struct Curl_easy *data,
620
                        const struct connectdata *conn,
621
                        int sockindex)
622
{
623
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
624
  return Curl_conn_is_ngtcp2(data, conn, sockindex);
625
#elif defined(USE_QUICHE)
626
  return Curl_conn_is_quiche(data, conn, sockindex);
627
#elif defined(USE_MSH3)
628
  return Curl_conn_is_msh3(data, conn, sockindex);
629
#else
630
  return ((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
631
          (conn->httpversion == 30));
632
#endif
633
}
634
635
CURLcode Curl_conn_may_http3(struct Curl_easy *data,
636
                             const struct connectdata *conn)
637
{
638
  if(conn->transport == TRNSPRT_UNIX) {
639
    /* cannot do QUIC over a unix domain socket */
640
    return CURLE_QUIC_CONNECT_ERROR;
641
  }
642
  if(!(conn->handler->flags & PROTOPT_SSL)) {
643
    failf(data, "HTTP/3 requested for non-HTTPS URL");
644
    return CURLE_URL_MALFORMAT;
645
  }
646
#ifndef CURL_DISABLE_PROXY
647
  if(conn->bits.socksproxy) {
648
    failf(data, "HTTP/3 is not supported over a SOCKS proxy");
649
    return CURLE_URL_MALFORMAT;
650
  }
651
  if(conn->bits.httpproxy && conn->bits.tunnel_proxy) {
652
    failf(data, "HTTP/3 is not supported over a HTTP proxy");
653
    return CURLE_URL_MALFORMAT;
654
  }
655
#endif
656
657
  return CURLE_OK;
658
}
659
660
#else /* ENABLE_QUIC */
661
662
CURLcode Curl_conn_may_http3(struct Curl_easy *data,
663
                             const struct connectdata *conn)
664
0
{
665
0
  (void)conn;
666
0
  (void)data;
667
0
  DEBUGF(infof(data, "QUIC is not supported in this build"));
668
0
  return CURLE_NOT_BUILT_IN;
669
0
}
670
671
#endif /* !ENABLE_QUIC */