apple-fast-udp: fix sendmsg_x partial results

When sending with sendmsg_x(), fix handling of last gso chunk being
smaller. Handle partial results correctly. Ignore SOCKEMSGSIZE by
reporting success which drops PMTUD probes into the void.

Closes #22429
This commit is contained in:
Stefan Eissing 2026-07-29 13:11:20 +02:00 committed by Daniel Stenberg
parent 5eb2f0757e
commit 6e130eb484
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2

View file

@ -179,16 +179,18 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf,
return CURLE_OK; return CURLE_OK;
if(!gsolen || (pktlen < gsolen)) if(!gsolen || (pktlen < gsolen))
gsolen = pktlen; gsolen = pktlen;
n = pktlen / gsolen; n = (pktlen + gsolen - 1) / gsolen;
while(i < n) { while(i < n) {
size_t j, batch = CURLMIN(n - i, MSG_X_SNUM); size_t j, batch = CURLMIN(n - i, MSG_X_SNUM), pkts_sent = 0;
for(j = 0; j < batch; ++j) { for(j = 0; j < batch; ++j) {
msg_iov[j].iov_base = CURL_UNCONST(&pkt[(i + j) * gsolen]); const size_t offset = (i + j) * gsolen;
msg_iov[j].iov_len = gsolen; msg_iov[j].iov_base = CURL_UNCONST(pkt + offset);
msg_iov[j].iov_len = CURLMIN(gsolen, pktlen - offset);
memset(&mmsg[j], 0, sizeof(mmsg[j])); memset(&mmsg[j], 0, sizeof(mmsg[j]));
mmsg[j].msg_iov = &msg_iov[j]; mmsg[j].msg_iov = &msg_iov[j];
mmsg[j].msg_iovlen = 1; mmsg[j].msg_iovlen = 1;
mmsg[j].msg_datalen = msg_iov[j].iov_len;
} }
#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
@ -196,27 +198,43 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf,
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#endif #endif
while((rc = syscall(SYS_sendmsg_x, qctx->sockfd, &mmsg, batch, 0)) == -1 && while((rc = syscall(SYS_sendmsg_x, qctx->sockfd, &mmsg, batch, 0)) == -1 &&
(SOCKERRNO == SOCKEINTR || SOCKERRNO == SOCKEMSGSIZE)) (SOCKERRNO == SOCKEINTR))
; ;
#if defined(CURL_HAVE_DIAG) && defined(__APPLE__) #if defined(CURL_HAVE_DIAG) && defined(__APPLE__)
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
if(rc == -1) { if(rc < 0) {
if(SOCK_EAGAIN(SOCKERRNO)) { if(SOCK_EAGAIN(SOCKERRNO)) {
CURL_TRC_CF(data, cf, "egress, sendmsg_x -> EAGAIN"); CURL_TRC_CF(data, cf, "egress, sendmsg_x -> EAGAIN");
result = sent ? CURLE_OK : CURLE_AGAIN; result = sent ? CURLE_OK : CURLE_AGAIN;
goto out; goto out;
} }
curlx_strerror(SOCKERRNO, errstr, sizeof(errstr)); if(SOCKERRNO != SOCKEMSGSIZE) {
failf(data, "QUIC: sendmsg_x() unexpectedly returned %d (errno=%d; %s)", curlx_strerror(SOCKERRNO, errstr, sizeof(errstr));
rc, SOCKERRNO, errstr); failf(data, "QUIC: sendmsg_x() returned %d (errno=%d; %s)",
result = CURLE_RECV_ERROR; rc, SOCKERRNO, errstr);
result = CURLE_SEND_ERROR;
goto out;
}
/* Error was SOCKEMSGSIZE. Network stack does not accept the packet
* length(s). This might be a PMTUD. Just drop it into the void
* and fall through to the success handling. */
pkts_sent = batch;
}
else
pkts_sent = CURLMIN((size_t)rc, batch);
VERBOSE(++calls);
if(!pkts_sent) { /* no packets of the current batch were sent */
result = sent ? CURLE_OK : CURLE_AGAIN;
goto out; goto out;
} }
VERBOSE(++calls); i += pkts_sent; /* Some have been sent */
i += batch; for(j = 0; j < pkts_sent; ++j)
sent += (batch * gsolen); sent += msg_iov[j].iov_len;
if(pkts_sent < batch)
goto out; /* but not all of them */
} }
out: out: