Coverage Report

Created: 2025-07-11 06:33

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