Coverage Report

Created: 2024-02-25 06:14

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