Coverage Report

Created: 2025-08-03 06:36

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