✓ProveIt
Examples / Report #3783438 Saved analysis

CVE-2026-11352: QUIC zero-length UDP datagrams busy-loop

curl · 2026-06-0514.3s analysis · $0.029 in model calls

What the report claims

curl's QUIC UDP receive helper ignores zero-length UDP datagrams before counting them against the per-call packet budget, causing a busy-loop that can keep curl running past its timeout.

Compare with curl's recorded outcome

Known vulnerability

A real vulnerability: CVE-2026-11352 QUIC zero-length UDP datagrams busy-loop

Shown for comparison after analysis. The pipeline did not see this label.

ProveIt's finding

Code supports the claim

The code supports the reported flaw. A maintainer should review its impact and decide what to do next.

Why this finding?
  • recvmsg_packets and recvmmsg_packets skip zero-length datagrams before incrementing pkts, defeating the per-call packet budget (lib/vquic/vquic.c lines 511-512, 596-597).
  • The fallback recvfrom_packets increments pkts before the zero-length check (line 661 vs 665), confirming the intended safe accounting.
  • vquic_recv_packets uses the vulnerable helpers on Linux (HAVE_SENDMMSG) and other platforms with HAVE_SENDMSG (lines 692, 694).
  • A remote QUIC peer can send unlimited zero-length datagrams after handshake completion, keeping curl busy in vquic_recv_packets without returning to the event loop where --max-time is enforced.
  • The project's own 'Busy-loops' policy states: 'If the problem bypasses the regular counter-measures and it causes a never-ending transfer, it might be a security problem.' This qualifies.
How this analysis ran 5 claims · 7 citations verified

Checked against curl master on 2026-06-05 (3d721a1d41a7). Dev evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.

1 · Grounding
grounded
no model · 16 references checked out, 0 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
5 claims · 6670 tokens · $0.0005
3 · Evidence
9 code snippets
real curl source at 3d721a1d41a7
4 · Judgment
Nemotron-3-Ultra-550b-a55b
21858 tokens · 12.1s · $0.0285
5 · Receipts
7 of 8 citations verified
every citation must point at code the judge was shown
Review a draft reply to the reporter

Edit and verify before sending.

Thank you for the detailed report and reproduction script. The code confirms that recvmsg_packets() and recvmmsg_packets() skip zero-length datagrams before incrementing the packet counter pkts (lib/vquic/vquic.c lines 511-512, 596-597), while the fallback recvfrom_packets() increments pkts first (line 661). This defeats the per-call max_pkts budget, allowing a remote QUIC peer to keep curl busy in vquic_recv_packets() indefinitely, bypassing --max-time. We will prepare a fix that moves the pkts increment before the zero-length check in both helpers.

Inspect all claims and source code 5 claims · 9 code excerpts

Report claims

C1flaw supported

recvmsg_packets() and recvmmsg_packets() skip zero-length datagrams without incrementing the packet counter, leading to a busy-loop when a peer sends many zero-length UDP datagrams.

Read the reasoning

Both helpers check for zero-length payloads before incrementing the packet counter pkts. The loop condition 'pkts < max_pkts' therefore makes no progress when only zero-length datagrams arrive.

C2location supported

The bug is in lib/vquic/vquic.c where vquic_recv_packets() uses these helpers to receive packets.

Read the reasoning

vquic_recv_packets dispatches to the two vulnerable helpers on Linux (HAVE_SENDMMSG) and other platforms with HAVE_SENDMSG.

C3trigger supported

A malicious HTTP/3/QUIC peer sends continuous zero-length UDP datagrams after completing the QUIC handshake.

Read the reasoning

The attack vector is plausible: a QUIC peer that completed the handshake can send zero-length UDP datagrams to the connected socket, which will be processed by vquic_recv_packets via the vulnerable helpers.

C4impact supported

curl remains busy in vquic_recv_packets() and does not respect --max-time or other timeout settings, causing denial of service.

Read the reasoning

Because pkts never increments on zero-length datagrams, the per-call budget max_pkts is never exhausted. The function does not return, so application-level timeouts (--max-time, CURLOPT_TIMEOUT) that are checked in the caller's event loop never get a chance to fire.

C5reproduction supported

The provided script reproduces the issue by running a real HTTP/3 server, completing the QUIC handshake, then sending zero-length datagrams until curl is killed after 30 seconds.

Read the reasoning

The reproduction script completes a real QUIC handshake, then floods zero-length datagrams via sendmmsg, and demonstrates curl --http3-only --max-time 2 running past 30 seconds.

script:0
Read the original report (excerpt)

Full report on HackerOne ↗

## Summary: curl's QUIC UDP receive helper ignores zero-length UDP datagrams before counting them against the per-call packet budget. On Linux, `recvmmsg_packets()` loops while `pkts < max_pkts`, but `if(!mmsg[i].msg_len) continue;` runs before `pkts` is incremented. The `recvmsg_packets()` backend has the same no-progress pattern for `if(!nread) continue;`. A malicious HTTP/3/QUIC peer can continuously send zero-length UDP datagrams to the connected QUIC socket and keep curl inside `vquic_recv_packets(..., max_pkts=1000, ...)`. In my end-to-end CLI PoC with a real HTTP/3 server that completed the QUIC handshake before switching to zero-length datagrams, `curl --http3-only --max-time 2` was still running after 30 seconds and had to be killed; the same setup also reproduced with `WAIT_SECS=60` locally. The fallback `recvfrom_packets()` already counts a received datagram before skipping zero-length payloads, which indicates the safer intended budget accounting. ## Affected version Validated on Linux x86_64 with an HTTP/3-enabled curl built from the signed `curl-8_20_0` release tag. In this repository snapshot the tag's `curlver.h` still reports `8.20.0-DEV`, but the tag object is `curl-8_20_0` with tag message `8.20.0`. `curl -V` from the reproduced build: ```text curl 8.20.0-DEV (Linux) libcurl/8.20.0-DEV OpenSSL/3.5.6 zlib/1.2.11 libpsl/0.21.0 ngtcp2/1.23.0 nghttp3/1.16.0 Release-Date: [unreleased] Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs ipns mqtt mqtts pop3 pop3s rtsp smtp smtps telnet tftp ws wss Features: alt-svc AsynchDNS HSTS HTTP3 HTTPS-proxy IPv6 Largefile libz PSL SSL threadsafe TLS-SRP UnixSockets ``` Source tags checked: - `curl-8_18_0`, `curl-8_19_0`, and `curl-8_20_0` contain the vulnerable zero-length skip before `pkts` is advanced in `recvmmsg_packets()` and `recvmsg_packets()`. - `curl-8_17_0` does not contain this same zero-length skip pattern. - Current master / `8.21.0-DEV` also contains the pattern. Relevant code in `lib/vquic/vquic.c`: ```c while(pkts < max_pkts) { ... if(!mmsg[i].msg_len) continue; ... pkts += (mmsg[i].msg_len + gso_size - 1) / gso_size; } for(pkts = 0; pkts < max_pkts;) { ... if(!nread) continue; ... pkts += (nread + gso_size - 1) / gso_size; } /* contrast: recvfrom_packets() */ ++pkts; if(!nread) continue; ``` ## Steps To Reproduce: Run the following s …

Source code

lib/vquic/vquic.clines 538–616 · definition of recvmsg_packets
538static CURLcode recvmsg_packets(struct Curl_cfilter *cf,
539 struct Curl_easy *data,
540 struct cf_quic_ctx *qctx,
541 size_t max_pkts,
542 vquic_recv_pkts_cb *recv_cb, void *userp)
543{
544 struct iovec msg_iov;
545 struct msghdr msg;
546 uint8_t buf[64 * 1024];
547 struct sockaddr_storage remote_addr;
548 size_t total_nread, pkts, calls;
549 ssize_t rc;
550 size_t nread;
551 char errstr[STRERROR_LEN];
552 CURLcode result = CURLE_OK;
553 uint8_t msg_ctrl[CMSG_SPACE(sizeof(int))];
554 size_t gso_size;
555
556 DEBUGASSERT(max_pkts > 0);
557 for(pkts = 0, total_nread = 0, calls = 0; pkts < max_pkts;) {
558 /* fully initialize this on each call to `recvmsg()`. There seem to
559 * operating systems out there that mess with `msg_iov.iov_len`. */
560 memset(&msg, 0, sizeof(msg));
561 msg_iov.iov_base = buf;
562 msg_iov.iov_len = (int)sizeof(buf);
563 msg.msg_iov = &msg_iov;
564 msg.msg_iovlen = 1;
565 msg.msg_control = msg_ctrl;
566 msg.msg_name = &remote_addr;
567 msg.msg_namelen = sizeof(remote_addr);
568 msg.msg_controllen = sizeof(msg_ctrl);
569
570 while((rc = recvmsg(qctx->sockfd, &msg, 0)) == -1 &&
571 (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE))
572 ;
573 if(!curlx_sztouz(rc, &nread)) {
574 if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
575 goto out;
576 }
577 if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
578 struct ip_quadruple ip;
579 if(!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 curlx_strerror(SOCKERRNO, errstr, sizeof(errstr));
586 failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d; %s)",
587 rc, SOCKERRNO, errstr);
588 result = CURLE_RECV_ERROR;
589 goto out;
590 }
591
592 total_nread += nread;
593 ++calls;
594
595 /* A 0-length UDP packet is no QUIC packet */
596 if(!nread)
C1: recvmsg_packets: 'if(!nread) continue;' at line 597 skips pkts increment at line 607
597 continue;
598
599 gso_size = vquic_msghdr_get_udp_gro(&msg);
600 if(gso_size == 0)
601 gso_size = nread;
602
603 result = recv_cb(buf, nread, gso_size,
604 msg.msg_name, msg.msg_namelen, 0, userp);
605 if(result)
606 goto out;
607 pkts += (nread + gso_size - 1) / gso_size;
608 }
609
610out:
611 if(total_nread || result)
612 CURL_TRC_CF(data, cf,
613 "vquic_recvmsg(len=%zu, packets=%zu, calls=%zu) -> %d",
614 total_nread, pkts, calls, result);
615 return result;
616}
lib/vquic/vquic.clines 434–535 · definition of recvmmsg_packets
434static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
435 struct Curl_easy *data,
436 struct cf_quic_ctx *qctx,
437 size_t max_pkts,
438 vquic_recv_pkts_cb *recv_cb, void *userp)
439{
440#if defined(__linux__) && defined(UDP_GRO)
441#define MMSG_NUM 16
442#define UDP_GRO_CNT_MAX 64
443#else
444#define MMSG_NUM 64
445#define UDP_GRO_CNT_MAX 1
446#endif
447#define MSG_BUF_SIZE (UDP_GRO_CNT_MAX * 1500)
448 struct iovec msg_iov[MMSG_NUM];
449 struct mmsghdr mmsg[MMSG_NUM];
450 uint8_t msg_ctrl[MMSG_NUM * CMSG_SPACE(sizeof(int))];
451 struct sockaddr_storage remote_addr[MMSG_NUM];
452 size_t total_nread = 0, pkts = 0;
453#ifdef CURLVERBOSE
454 size_t calls = 0;
455#endif
456 int mcount, i, n;
457 char errstr[STRERROR_LEN];
458 CURLcode result = CURLE_OK;
459 size_t gso_size;
460 char *sockbuf = NULL;
461 uint8_t (*bufs)[MSG_BUF_SIZE] = NULL;
462
463 DEBUGASSERT(max_pkts > 0);
464 result = Curl_multi_xfer_sockbuf_borrow(data, MMSG_NUM * MSG_BUF_SIZE,
465 &sockbuf);
466 if(result)
467 goto out;
468 bufs = (uint8_t (*)[MSG_BUF_SIZE])sockbuf;
469
470 total_nread = 0;
471 while(pkts < max_pkts) {
472 n = (int)CURLMIN(CURLMIN(MMSG_NUM, IOV_MAX), max_pkts);
473 memset(&mmsg, 0, sizeof(mmsg));
474 for(i = 0; i < n; ++i) {
475 msg_iov[i].iov_base = bufs[i];
476 msg_iov[i].iov_len = (int)sizeof(bufs[i]);
477 mmsg[i].msg_hdr.msg_iov = &msg_iov[i];
478 mmsg[i].msg_hdr.msg_iovlen = 1;
479 mmsg[i].msg_hdr.msg_name = &remote_addr[i];
480 mmsg[i].msg_hdr.msg_namelen = sizeof(remote_addr[i]);
481 mmsg[i].msg_hdr.msg_control = &msg_ctrl[i * CMSG_SPACE(sizeof(int))];
482 mmsg[i].msg_hdr.msg_controllen = CMSG_SPACE(sizeof(int));
483 }
484
485 while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 &&
486 (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE))
487 ;
488 if(mcount == -1) {
489 if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
490 CURL_TRC_CF(data, cf, "ingress, recvmmsg -> EAGAIN");
491 goto out;
492 }
493 if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
494 struct ip_quadruple ip;
495 if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip))
496 failf(data, "QUIC: connection to %s port %u refused",
497 ip.remote_ip, ip.remote_port);
498 result = CURLE_COULDNT_CONNECT;
499 goto out;
500 }
501 curlx_strerror(SOCKERRNO, errstr, sizeof(errstr));
502 failf(data, "QUIC: recvmmsg() unexpectedly returned %d (errno=%d; %s)",
503 mcount, SOCKERRNO, errstr);
504 result = CURLE_RECV_ERROR;
505 goto out;
506 }
507
508 VERBOSE(++calls);
509 for(i = 0; i < mcount; ++i) {
510 /* A zero-length UDP packet is no QUIC packet. Ignore. */
511 if(!mmsg[i].msg_len)
C1: recvmmsg_packets: 'if(!mmsg[i].msg_len) continue;' at line 512 skips pkts increment at line 524
512 continue;
513 total_nread += mmsg[i].msg_len;
514
515 gso_size = vquic_msghdr_get_udp_gro(&mmsg[i].msg_hdr);
516 if(gso_size == 0)
517 gso_size = mmsg[i].msg_len;
518
519 result = recv_cb(bufs[i], mmsg[i].msg_len, gso_size,
520 mmsg[i].msg_hdr.msg_name,
521 mmsg[i].msg_hdr.msg_namelen, 0, userp);
522 if(result)
523 goto out;
524 pkts += (mmsg[i].msg_len + gso_size - 1) / gso_size;
525 }
526 }
527
528out:
529 if(total_nread || result)
530 CURL_TRC_CF(data, cf,
531 "vquic_recvmmsg(len=%zu, packets=%zu, calls=%zu) -> %d",
532 total_nread, pkts, calls, result);
533 Curl_multi_xfer_sockbuf_release(data, sockbuf);
534 return result;
535}
lib/vquic/vquic.clines 684–706 · definition of vquic_recv_packets
684CURLcode vquic_recv_packets(struct Curl_cfilter *cf,
C3: vquic_recv_packets is the entry point for receiving QUIC packets from the peer
C4: vquic_recv_packets runs the busy loop; control does not return to the caller until the loop exits
685 struct Curl_easy *data,
686 struct cf_quic_ctx *qctx,
687 size_t max_pkts,
688 vquic_recv_pkts_cb *recv_cb, void *userp)
689{
690 CURLcode result;
691#ifdef HAVE_SENDMMSG
692 result = recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp);
C2: vquic_recv_packets calls recvmmsg_packets when HAVE_SENDMMSG is defined
693#elif defined(HAVE_SENDMSG)
694 result = recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp);
C2: vquic_recv_packets calls recvmsg_packets when HAVE_SENDMSG is defined
695#else
696 result = recvfrom_packets(cf, data, qctx, max_pkts, recv_cb, userp);
697#endif
698 if(!result) {
699 if(!qctx->got_first_byte) {
700 qctx->got_first_byte = TRUE;
701 qctx->first_byte_at = qctx->last_op;
702 }
703 qctx->last_io = qctx->last_op;
704 }
705 return result;
706}
lib/altsvc.clines 63–63 · definition of time
63#define time(x) altsvc_debugtime(x)
C4: time() macro shows timeout handling is at a higher level that only runs when control returns
lib/vquic/vquic.c@curl-8_20_0lines 483–560 · definition of recvmsg_packets as in release curl-8_20_0; differs from master; commits that changed it since: 4780e509aa tidy-up: prefer "initialize" with a 'z'
Show 78 lines (not cited by the judge)
483static CURLcode recvmsg_packets(struct Curl_cfilter *cf,
484 struct Curl_easy *data,
485 struct cf_quic_ctx *qctx,
486 size_t max_pkts,
487 vquic_recv_pkts_cb *recv_cb, void *userp)
488{
489 struct iovec msg_iov;
490 struct msghdr msg;
491 uint8_t buf[64 * 1024];
492 struct sockaddr_storage remote_addr;
493 size_t total_nread, pkts, calls;
494 ssize_t rc;
495 size_t nread;
496 char errstr[STRERROR_LEN];
497 CURLcode result = CURLE_OK;
498 uint8_t msg_ctrl[CMSG_SPACE(sizeof(int))];
499 size_t gso_size;
500
501 DEBUGASSERT(max_pkts > 0);
502 for(pkts = 0, total_nread = 0, calls = 0; pkts < max_pkts;) {
503 /* fully initialise this on each call to `recvmsg()`. There seem to
504 * operating systems out there that mess with `msg_iov.iov_len`. */
505 memset(&msg, 0, sizeof(msg));
506 msg_iov.iov_base = buf;
507 msg_iov.iov_len = (int)sizeof(buf);
508 msg.msg_iov = &msg_iov;
509 msg.msg_iovlen = 1;
510 msg.msg_control = msg_ctrl;
511 msg.msg_name = &remote_addr;
512 msg.msg_namelen = sizeof(remote_addr);
513 msg.msg_controllen = sizeof(msg_ctrl);
514
515 while((rc = recvmsg(qctx->sockfd, &msg, 0)) == -1 &&
516 (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE))
517 ;
518 if(!curlx_sztouz(rc, &nread)) {
519 if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
520 goto out;
521 }
522 if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
523 struct ip_quadruple ip;
524 if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip))
525 failf(data, "QUIC: connection to %s port %u refused",
526 ip.remote_ip, ip.remote_port);
527 result = CURLE_COULDNT_CONNECT;
528 goto out;
529 }
530 curlx_strerror(SOCKERRNO, errstr, sizeof(errstr));
531 failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d; %s)",
532 rc, SOCKERRNO, errstr);
533 result = CURLE_RECV_ERROR;
534 goto out;
535 }
536
537 total_nread += nread;
538 ++calls;
539
540 /* A 0-length UDP packet is no QUIC packet */
541 if(!nread)
542 continue;
543
544 gso_size = vquic_msghdr_get_udp_gro(&msg);
545 if(gso_size == 0)
546 gso_size = nread;
547
548 result = recv_cb(buf, nread, gso_size,
549 msg.msg_name, msg.msg_namelen, 0, userp);
550 if(result)
551 goto out;
552 pkts += (nread + gso_size - 1) / gso_size;
553 }
554
555out:
556 if(total_nread || result)
557 CURL_TRC_CF(data, cf, "vquic_recvmsg(len=%zu, packets=%zu, calls=%zu)"
558 " -> %d", total_nread, pkts, calls, result);
559 return result;
560}
lib/vquic/vquic.c@curl-8_20_0lines 380–480 · definition of recvmmsg_packets as in release curl-8_20_0; differs from master; commits that changed it since: 7e1001bcd6 tidy-up: miscellaneous
Show 101 lines (not cited by the judge)
380static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
381 struct Curl_easy *data,
382 struct cf_quic_ctx *qctx,
383 size_t max_pkts,
384 vquic_recv_pkts_cb *recv_cb, void *userp)
385{
386#if defined(__linux__) && defined(UDP_GRO)
387#define MMSG_NUM 16
388#define UDP_GRO_CNT_MAX 64
389#else
390#define MMSG_NUM 64
391#define UDP_GRO_CNT_MAX 1
392#endif
393#define MSG_BUF_SIZE (UDP_GRO_CNT_MAX * 1500)
394 struct iovec msg_iov[MMSG_NUM];
395 struct mmsghdr mmsg[MMSG_NUM];
396 uint8_t msg_ctrl[MMSG_NUM * CMSG_SPACE(sizeof(int))];
397 struct sockaddr_storage remote_addr[MMSG_NUM];
398 size_t total_nread = 0, pkts = 0;
399#ifdef CURLVERBOSE
400 size_t calls = 0;
401#endif
402 int mcount, i, n;
403 char errstr[STRERROR_LEN];
404 CURLcode result = CURLE_OK;
405 size_t gso_size;
406 char *sockbuf = NULL;
407 uint8_t (*bufs)[MSG_BUF_SIZE] = NULL;
408
409 DEBUGASSERT(max_pkts > 0);
410 result = Curl_multi_xfer_sockbuf_borrow(data, MMSG_NUM * MSG_BUF_SIZE,
411 &sockbuf);
412 if(result)
413 goto out;
414 bufs = (uint8_t (*)[MSG_BUF_SIZE])sockbuf;
415
416 total_nread = 0;
417 while(pkts < max_pkts) {
418 n = (int)CURLMIN(CURLMIN(MMSG_NUM, IOV_MAX), max_pkts);
419 memset(&mmsg, 0, sizeof(mmsg));
420 for(i = 0; i < n; ++i) {
421 msg_iov[i].iov_base = bufs[i];
422 msg_iov[i].iov_len = (int)sizeof(bufs[i]);
423 mmsg[i].msg_hdr.msg_iov = &msg_iov[i];
424 mmsg[i].msg_hdr.msg_iovlen = 1;
425 mmsg[i].msg_hdr.msg_name = &remote_addr[i];
426 mmsg[i].msg_hdr.msg_namelen = sizeof(remote_addr[i]);
427 mmsg[i].msg_hdr.msg_control = &msg_ctrl[i * CMSG_SPACE(sizeof(int))];
428 mmsg[i].msg_hdr.msg_controllen = CMSG_SPACE(sizeof(int));
429 }
430
431 while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 &&
432 (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE))
433 ;
434 if(mcount == -1) {
435 if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
436 CURL_TRC_CF(data, cf, "ingress, recvmmsg -> EAGAIN");
437 goto out;
438 }
439 if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
440 struct ip_quadruple ip;
441 if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip))
442 failf(data, "QUIC: connection to %s port %u refused",
443 ip.remote_ip, ip.remote_port);
444 result = CURLE_COULDNT_CONNECT;
445 goto out;
446 }
447 curlx_strerror(SOCKERRNO, errstr, sizeof(errstr));
448 failf(data, "QUIC: recvmmsg() unexpectedly returned %d (errno=%d; %s)",
449 mcount, SOCKERRNO, errstr);
450 result = CURLE_RECV_ERROR;
451 goto out;
452 }
453
454 VERBOSE(++calls);
455 for(i = 0; i < mcount; ++i) {
456 /* A zero-length UDP packet is no QUIC packet. Ignore. */
457 if(!mmsg[i].msg_len)
458 continue;
459 total_nread += mmsg[i].msg_len;
460
461 gso_size = vquic_msghdr_get_udp_gro(&mmsg[i].msg_hdr);
462 if(gso_size == 0)
463 gso_size = mmsg[i].msg_len;
464
465 result = recv_cb(bufs[i], mmsg[i].msg_len, gso_size,
466 mmsg[i].msg_hdr.msg_name,
467 mmsg[i].msg_hdr.msg_namelen, 0, userp);
468 if(result)
469 goto out;
470 pkts += (mmsg[i].msg_len + gso_size - 1) / gso_size;
471 }
472 }
473
474out:
475 if(total_nread || result)
476 CURL_TRC_CF(data, cf, "vquic_recvmmsg(len=%zu, packets=%zu, calls=%zu)"
477 " -> %d", total_nread, pkts, calls, result);
478 Curl_multi_xfer_sockbuf_release(data, sockbuf);
479 return result;
480}
lib/vquic/vquic.clines 619–681 · definition of recvfrom_packets
Show 63 lines (not cited by the judge)
619static CURLcode recvfrom_packets(struct Curl_cfilter *cf,
620 struct Curl_easy *data,
621 struct cf_quic_ctx *qctx,
622 size_t max_pkts,
623 vquic_recv_pkts_cb *recv_cb, void *userp)
624{
625 uint8_t buf[64 * 1024];
626 int bufsize = (int)sizeof(buf);
627 struct sockaddr_storage remote_addr;
628 socklen_t remote_addrlen = sizeof(remote_addr);
629 size_t total_nread, pkts, calls = 0, nread;
630 ssize_t rv;
631 char errstr[STRERROR_LEN];
632 CURLcode result = CURLE_OK;
633
634 DEBUGASSERT(max_pkts > 0);
635 for(pkts = 0, total_nread = 0; pkts < max_pkts;) {
636 while((rv = recvfrom(qctx->sockfd, (char *)buf, bufsize, 0,
637 (struct sockaddr *)&remote_addr,
638 &remote_addrlen)) == -1 &&
639 (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE))
640 ;
641 if(!curlx_sztouz(rv, &nread)) {
642 if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
643 CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN");
644 goto out;
645 }
646 if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
647 struct ip_quadruple ip;
648 if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip))
649 failf(data, "QUIC: connection to %s port %u refused",
650 ip.remote_ip, ip.remote_port);
651 result = CURLE_COULDNT_CONNECT;
652 goto out;
653 }
654 curlx_strerror(SOCKERRNO, errstr, sizeof(errstr));
655 failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d; %s)",
656 rv, SOCKERRNO, errstr);
657 result = CURLE_RECV_ERROR;
658 goto out;
659 }
660
661 ++pkts;
662 ++calls;
663
664 /* A 0-length UDP packet is no QUIC packet */
665 if(!nread)
666 continue;
667
668 total_nread += nread;
669 result = recv_cb(buf, nread, nread, &remote_addr, remote_addrlen,
670 0, userp);
671 if(result)
672 goto out;
673 }
674
675out:
676 if(total_nread || result)
677 CURL_TRC_CF(data, cf,
678 "vquic_recvfrom(len=%zu, packets=%zu, calls=%zu) -> %d",
679 total_nread, pkts, calls, result);
680 return result;
681}
lib/vquic/vquic.c@curl-8_20_0lines 563–624 · definition of recvfrom_packets as in release curl-8_20_0; differs from master
Show 62 lines (not cited by the judge)
563static CURLcode recvfrom_packets(struct Curl_cfilter *cf,
564 struct Curl_easy *data,
565 struct cf_quic_ctx *qctx,
566 size_t max_pkts,
567 vquic_recv_pkts_cb *recv_cb, void *userp)
568{
569 uint8_t buf[64 * 1024];
570 int bufsize = (int)sizeof(buf);
571 struct sockaddr_storage remote_addr;
572 socklen_t remote_addrlen = sizeof(remote_addr);
573 size_t total_nread, pkts, calls = 0, nread;
574 ssize_t rv;
575 char errstr[STRERROR_LEN];
576 CURLcode result = CURLE_OK;
577
578 DEBUGASSERT(max_pkts > 0);
579 for(pkts = 0, total_nread = 0; pkts < max_pkts;) {
580 while((rv = recvfrom(qctx->sockfd, (char *)buf, bufsize, 0,
581 (struct sockaddr *)&remote_addr,
582 &remote_addrlen)) == -1 &&
583 (SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE))
584 ;
585 if(!curlx_sztouz(rv, &nread)) {
586 if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
587 CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN");
588 goto out;
589 }
590 if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
591 struct ip_quadruple ip;
592 if(!Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip))
593 failf(data, "QUIC: connection to %s port %u refused",
594 ip.remote_ip, ip.remote_port);
595 result = CURLE_COULDNT_CONNECT;
596 goto out;
597 }
598 curlx_strerror(SOCKERRNO, errstr, sizeof(errstr));
599 failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d; %s)",
600 rv, SOCKERRNO, errstr);
601 result = CURLE_RECV_ERROR;
602 goto out;
603 }
604
605 ++pkts;
606 ++calls;
607
608 /* A 0-length UDP packet is no QUIC packet */
609 if(!nread)
610 continue;
611
612 total_nread += nread;
613 result = recv_cb(buf, nread, nread, &remote_addr, remote_addrlen,
614 0, userp);
615 if(result)
616 goto out;
617 }
618
619out:
620 if(total_nread || result)
621 CURL_TRC_CF(data, cf, "vquic_recvfrom(len=%zu, packets=%zu, calls=%zu)"
622 " -> %d", total_nread, pkts, calls, result);
623 return result;
624}
lib/optiontable.pllines 84–125 · definition of while
Show 42 lines (not cited by the judge)
84while(<CURL_H>) {
85 my $l = $_;
86 if($fl) {
87 # continued deprecation
88 if($l =~ /(.*)\),/) {
89 $fl .= $1;
90
91 # the end
92 my @p=split(/, */, $fl);
93 add($p[0], $p[1], $p[2]);
94 undef $fl;
95 }
96 else {
97 # another line to append
98 chomp $l;
99 $fl .= $l;
100 }
101 }
102
103 if(/^ *CURLOPTDEPRECATED\((.*)/) {
104 $fl = $1;
105 chomp $fl;
106 }
107
108 if(/^ *CURLOPT\(([^,]*), ([^,]*), (\d+)\)/) {
109 my($opt, $type, $num)=($1,$2,$3);
110 add($opt, $type, $num);
111 }
112
113 # alias for an older option
114 # old = new
115 if(/^#define (CURLOPT_[^ ]*) *(CURLOPT_\S*)/) {
116 my ($o, $n)=($1, $2);
117 # skip obsolete ones
118 if(($n !~ /OBSOLETE/) && ($o !~ /OBSOLETE/)) {
119 $o =~ s/^CURLOPT_//;
120 $n =~ s/^CURLOPT_//;
121 $alias{$o} = $n;
122 push @names, $o;
123 }
124 }
125}