113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci 2513498266Sopenharmony_ci/* WIP, experimental: use recvmmsg() on linux 2613498266Sopenharmony_ci * we have no configure check, yet 2713498266Sopenharmony_ci * and also it is only available for _GNU_SOURCE, which 2813498266Sopenharmony_ci * we do not use otherwise. 2913498266Sopenharmony_ci#define HAVE_SENDMMSG 3013498266Sopenharmony_ci */ 3113498266Sopenharmony_ci#if defined(HAVE_SENDMMSG) 3213498266Sopenharmony_ci#define _GNU_SOURCE 3313498266Sopenharmony_ci#include <sys/socket.h> 3413498266Sopenharmony_ci#undef _GNU_SOURCE 3513498266Sopenharmony_ci#endif 3613498266Sopenharmony_ci 3713498266Sopenharmony_ci#include "curl_setup.h" 3813498266Sopenharmony_ci 3913498266Sopenharmony_ci#ifdef HAVE_FCNTL_H 4013498266Sopenharmony_ci#include <fcntl.h> 4113498266Sopenharmony_ci#endif 4213498266Sopenharmony_ci#include "urldata.h" 4313498266Sopenharmony_ci#include "bufq.h" 4413498266Sopenharmony_ci#include "dynbuf.h" 4513498266Sopenharmony_ci#include "cfilters.h" 4613498266Sopenharmony_ci#include "curl_trc.h" 4713498266Sopenharmony_ci#include "curl_msh3.h" 4813498266Sopenharmony_ci#include "curl_ngtcp2.h" 4913498266Sopenharmony_ci#include "curl_osslq.h" 5013498266Sopenharmony_ci#include "curl_quiche.h" 5113498266Sopenharmony_ci#include "rand.h" 5213498266Sopenharmony_ci#include "vquic.h" 5313498266Sopenharmony_ci#include "vquic_int.h" 5413498266Sopenharmony_ci#include "strerror.h" 5513498266Sopenharmony_ci 5613498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 5713498266Sopenharmony_ci#include "curl_printf.h" 5813498266Sopenharmony_ci#include "curl_memory.h" 5913498266Sopenharmony_ci#include "memdebug.h" 6013498266Sopenharmony_ci 6113498266Sopenharmony_ci 6213498266Sopenharmony_ci#ifdef ENABLE_QUIC 6313498266Sopenharmony_ci 6413498266Sopenharmony_ci#ifdef O_BINARY 6513498266Sopenharmony_ci#define QLOGMODE O_WRONLY|O_CREAT|O_BINARY 6613498266Sopenharmony_ci#else 6713498266Sopenharmony_ci#define QLOGMODE O_WRONLY|O_CREAT 6813498266Sopenharmony_ci#endif 6913498266Sopenharmony_ci 7013498266Sopenharmony_ci#define NW_CHUNK_SIZE (64 * 1024) 7113498266Sopenharmony_ci#define NW_SEND_CHUNKS 2 7213498266Sopenharmony_ci 7313498266Sopenharmony_ci 7413498266Sopenharmony_civoid Curl_quic_ver(char *p, size_t len) 7513498266Sopenharmony_ci{ 7613498266Sopenharmony_ci#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) 7713498266Sopenharmony_ci Curl_ngtcp2_ver(p, len); 7813498266Sopenharmony_ci#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3) 7913498266Sopenharmony_ci Curl_osslq_ver(p, len); 8013498266Sopenharmony_ci#elif defined(USE_QUICHE) 8113498266Sopenharmony_ci Curl_quiche_ver(p, len); 8213498266Sopenharmony_ci#elif defined(USE_MSH3) 8313498266Sopenharmony_ci Curl_msh3_ver(p, len); 8413498266Sopenharmony_ci#endif 8513498266Sopenharmony_ci} 8613498266Sopenharmony_ci 8713498266Sopenharmony_ciCURLcode vquic_ctx_init(struct cf_quic_ctx *qctx) 8813498266Sopenharmony_ci{ 8913498266Sopenharmony_ci Curl_bufq_init2(&qctx->sendbuf, NW_CHUNK_SIZE, NW_SEND_CHUNKS, 9013498266Sopenharmony_ci BUFQ_OPT_SOFT_LIMIT); 9113498266Sopenharmony_ci#if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG) 9213498266Sopenharmony_ci qctx->no_gso = FALSE; 9313498266Sopenharmony_ci#else 9413498266Sopenharmony_ci qctx->no_gso = TRUE; 9513498266Sopenharmony_ci#endif 9613498266Sopenharmony_ci#ifdef DEBUGBUILD 9713498266Sopenharmony_ci { 9813498266Sopenharmony_ci char *p = getenv("CURL_DBG_QUIC_WBLOCK"); 9913498266Sopenharmony_ci if(p) { 10013498266Sopenharmony_ci long l = strtol(p, NULL, 10); 10113498266Sopenharmony_ci if(l >= 0 && l <= 100) 10213498266Sopenharmony_ci qctx->wblock_percent = (int)l; 10313498266Sopenharmony_ci } 10413498266Sopenharmony_ci } 10513498266Sopenharmony_ci#endif 10613498266Sopenharmony_ci vquic_ctx_update_time(qctx); 10713498266Sopenharmony_ci 10813498266Sopenharmony_ci return CURLE_OK; 10913498266Sopenharmony_ci} 11013498266Sopenharmony_ci 11113498266Sopenharmony_civoid vquic_ctx_free(struct cf_quic_ctx *qctx) 11213498266Sopenharmony_ci{ 11313498266Sopenharmony_ci Curl_bufq_free(&qctx->sendbuf); 11413498266Sopenharmony_ci} 11513498266Sopenharmony_ci 11613498266Sopenharmony_civoid vquic_ctx_update_time(struct cf_quic_ctx *qctx) 11713498266Sopenharmony_ci{ 11813498266Sopenharmony_ci qctx->last_op = Curl_now(); 11913498266Sopenharmony_ci} 12013498266Sopenharmony_ci 12113498266Sopenharmony_cistatic CURLcode send_packet_no_gso(struct Curl_cfilter *cf, 12213498266Sopenharmony_ci struct Curl_easy *data, 12313498266Sopenharmony_ci struct cf_quic_ctx *qctx, 12413498266Sopenharmony_ci const uint8_t *pkt, size_t pktlen, 12513498266Sopenharmony_ci size_t gsolen, size_t *psent); 12613498266Sopenharmony_ci 12713498266Sopenharmony_cistatic CURLcode do_sendmsg(struct Curl_cfilter *cf, 12813498266Sopenharmony_ci struct Curl_easy *data, 12913498266Sopenharmony_ci struct cf_quic_ctx *qctx, 13013498266Sopenharmony_ci const uint8_t *pkt, size_t pktlen, size_t gsolen, 13113498266Sopenharmony_ci size_t *psent) 13213498266Sopenharmony_ci{ 13313498266Sopenharmony_ci#ifdef HAVE_SENDMSG 13413498266Sopenharmony_ci struct iovec msg_iov; 13513498266Sopenharmony_ci struct msghdr msg = {0}; 13613498266Sopenharmony_ci ssize_t sent; 13713498266Sopenharmony_ci#if defined(__linux__) && defined(UDP_SEGMENT) 13813498266Sopenharmony_ci uint8_t msg_ctrl[32]; 13913498266Sopenharmony_ci struct cmsghdr *cm; 14013498266Sopenharmony_ci#endif 14113498266Sopenharmony_ci 14213498266Sopenharmony_ci *psent = 0; 14313498266Sopenharmony_ci msg_iov.iov_base = (uint8_t *)pkt; 14413498266Sopenharmony_ci msg_iov.iov_len = pktlen; 14513498266Sopenharmony_ci msg.msg_iov = &msg_iov; 14613498266Sopenharmony_ci msg.msg_iovlen = 1; 14713498266Sopenharmony_ci 14813498266Sopenharmony_ci#if defined(__linux__) && defined(UDP_SEGMENT) 14913498266Sopenharmony_ci if(pktlen > gsolen) { 15013498266Sopenharmony_ci /* Only set this, when we need it. macOS, for example, 15113498266Sopenharmony_ci * does not seem to like a msg_control of length 0. */ 15213498266Sopenharmony_ci msg.msg_control = msg_ctrl; 15313498266Sopenharmony_ci assert(sizeof(msg_ctrl) >= CMSG_SPACE(sizeof(uint16_t))); 15413498266Sopenharmony_ci msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t)); 15513498266Sopenharmony_ci cm = CMSG_FIRSTHDR(&msg); 15613498266Sopenharmony_ci cm->cmsg_level = SOL_UDP; 15713498266Sopenharmony_ci cm->cmsg_type = UDP_SEGMENT; 15813498266Sopenharmony_ci cm->cmsg_len = CMSG_LEN(sizeof(uint16_t)); 15913498266Sopenharmony_ci *(uint16_t *)(void *)CMSG_DATA(cm) = gsolen & 0xffff; 16013498266Sopenharmony_ci } 16113498266Sopenharmony_ci#endif 16213498266Sopenharmony_ci 16313498266Sopenharmony_ci 16413498266Sopenharmony_ci while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR) 16513498266Sopenharmony_ci ; 16613498266Sopenharmony_ci 16713498266Sopenharmony_ci if(sent == -1) { 16813498266Sopenharmony_ci switch(SOCKERRNO) { 16913498266Sopenharmony_ci case EAGAIN: 17013498266Sopenharmony_ci#if EAGAIN != EWOULDBLOCK 17113498266Sopenharmony_ci case EWOULDBLOCK: 17213498266Sopenharmony_ci#endif 17313498266Sopenharmony_ci return CURLE_AGAIN; 17413498266Sopenharmony_ci case EMSGSIZE: 17513498266Sopenharmony_ci /* UDP datagram is too large; caused by PMTUD. Just let it be lost. */ 17613498266Sopenharmony_ci break; 17713498266Sopenharmony_ci case EIO: 17813498266Sopenharmony_ci if(pktlen > gsolen) { 17913498266Sopenharmony_ci /* GSO failure */ 18013498266Sopenharmony_ci failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent, 18113498266Sopenharmony_ci SOCKERRNO); 18213498266Sopenharmony_ci qctx->no_gso = TRUE; 18313498266Sopenharmony_ci return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent); 18413498266Sopenharmony_ci } 18513498266Sopenharmony_ci FALLTHROUGH(); 18613498266Sopenharmony_ci default: 18713498266Sopenharmony_ci failf(data, "sendmsg() returned %zd (errno %d)", sent, SOCKERRNO); 18813498266Sopenharmony_ci return CURLE_SEND_ERROR; 18913498266Sopenharmony_ci } 19013498266Sopenharmony_ci } 19113498266Sopenharmony_ci else { 19213498266Sopenharmony_ci assert(pktlen == (size_t)sent); 19313498266Sopenharmony_ci } 19413498266Sopenharmony_ci#else 19513498266Sopenharmony_ci ssize_t sent; 19613498266Sopenharmony_ci (void)gsolen; 19713498266Sopenharmony_ci 19813498266Sopenharmony_ci *psent = 0; 19913498266Sopenharmony_ci 20013498266Sopenharmony_ci while((sent = send(qctx->sockfd, 20113498266Sopenharmony_ci (const char *)pkt, (SEND_TYPE_ARG3)pktlen, 0)) == -1 && 20213498266Sopenharmony_ci SOCKERRNO == EINTR) 20313498266Sopenharmony_ci ; 20413498266Sopenharmony_ci 20513498266Sopenharmony_ci if(sent == -1) { 20613498266Sopenharmony_ci if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { 20713498266Sopenharmony_ci return CURLE_AGAIN; 20813498266Sopenharmony_ci } 20913498266Sopenharmony_ci else { 21013498266Sopenharmony_ci failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO); 21113498266Sopenharmony_ci if(SOCKERRNO != EMSGSIZE) { 21213498266Sopenharmony_ci return CURLE_SEND_ERROR; 21313498266Sopenharmony_ci } 21413498266Sopenharmony_ci /* UDP datagram is too large; caused by PMTUD. Just let it be 21513498266Sopenharmony_ci lost. */ 21613498266Sopenharmony_ci } 21713498266Sopenharmony_ci } 21813498266Sopenharmony_ci#endif 21913498266Sopenharmony_ci (void)cf; 22013498266Sopenharmony_ci *psent = pktlen; 22113498266Sopenharmony_ci 22213498266Sopenharmony_ci return CURLE_OK; 22313498266Sopenharmony_ci} 22413498266Sopenharmony_ci 22513498266Sopenharmony_cistatic CURLcode send_packet_no_gso(struct Curl_cfilter *cf, 22613498266Sopenharmony_ci struct Curl_easy *data, 22713498266Sopenharmony_ci struct cf_quic_ctx *qctx, 22813498266Sopenharmony_ci const uint8_t *pkt, size_t pktlen, 22913498266Sopenharmony_ci size_t gsolen, size_t *psent) 23013498266Sopenharmony_ci{ 23113498266Sopenharmony_ci const uint8_t *p, *end = pkt + pktlen; 23213498266Sopenharmony_ci size_t sent; 23313498266Sopenharmony_ci 23413498266Sopenharmony_ci *psent = 0; 23513498266Sopenharmony_ci 23613498266Sopenharmony_ci for(p = pkt; p < end; p += gsolen) { 23713498266Sopenharmony_ci size_t len = CURLMIN(gsolen, (size_t)(end - p)); 23813498266Sopenharmony_ci CURLcode curlcode = do_sendmsg(cf, data, qctx, p, len, len, &sent); 23913498266Sopenharmony_ci if(curlcode != CURLE_OK) { 24013498266Sopenharmony_ci return curlcode; 24113498266Sopenharmony_ci } 24213498266Sopenharmony_ci *psent += sent; 24313498266Sopenharmony_ci } 24413498266Sopenharmony_ci 24513498266Sopenharmony_ci return CURLE_OK; 24613498266Sopenharmony_ci} 24713498266Sopenharmony_ci 24813498266Sopenharmony_cistatic CURLcode vquic_send_packets(struct Curl_cfilter *cf, 24913498266Sopenharmony_ci struct Curl_easy *data, 25013498266Sopenharmony_ci struct cf_quic_ctx *qctx, 25113498266Sopenharmony_ci const uint8_t *pkt, size_t pktlen, 25213498266Sopenharmony_ci size_t gsolen, size_t *psent) 25313498266Sopenharmony_ci{ 25413498266Sopenharmony_ci CURLcode result; 25513498266Sopenharmony_ci#ifdef DEBUGBUILD 25613498266Sopenharmony_ci /* simulate network blocking/partial writes */ 25713498266Sopenharmony_ci if(qctx->wblock_percent > 0) { 25813498266Sopenharmony_ci unsigned char c; 25913498266Sopenharmony_ci Curl_rand(data, &c, 1); 26013498266Sopenharmony_ci if(c >= ((100-qctx->wblock_percent)*256/100)) { 26113498266Sopenharmony_ci CURL_TRC_CF(data, cf, "vquic_flush() simulate EWOULDBLOCK"); 26213498266Sopenharmony_ci return CURLE_AGAIN; 26313498266Sopenharmony_ci } 26413498266Sopenharmony_ci } 26513498266Sopenharmony_ci#endif 26613498266Sopenharmony_ci if(qctx->no_gso && pktlen > gsolen) { 26713498266Sopenharmony_ci result = send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent); 26813498266Sopenharmony_ci } 26913498266Sopenharmony_ci else { 27013498266Sopenharmony_ci result = do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent); 27113498266Sopenharmony_ci } 27213498266Sopenharmony_ci if(!result) 27313498266Sopenharmony_ci qctx->last_io = qctx->last_op; 27413498266Sopenharmony_ci return result; 27513498266Sopenharmony_ci} 27613498266Sopenharmony_ci 27713498266Sopenharmony_ciCURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, 27813498266Sopenharmony_ci struct cf_quic_ctx *qctx) 27913498266Sopenharmony_ci{ 28013498266Sopenharmony_ci const unsigned char *buf; 28113498266Sopenharmony_ci size_t blen, sent; 28213498266Sopenharmony_ci CURLcode result; 28313498266Sopenharmony_ci size_t gsolen; 28413498266Sopenharmony_ci 28513498266Sopenharmony_ci while(Curl_bufq_peek(&qctx->sendbuf, &buf, &blen)) { 28613498266Sopenharmony_ci gsolen = qctx->gsolen; 28713498266Sopenharmony_ci if(qctx->split_len) { 28813498266Sopenharmony_ci gsolen = qctx->split_gsolen; 28913498266Sopenharmony_ci if(blen > qctx->split_len) 29013498266Sopenharmony_ci blen = qctx->split_len; 29113498266Sopenharmony_ci } 29213498266Sopenharmony_ci 29313498266Sopenharmony_ci result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent); 29413498266Sopenharmony_ci CURL_TRC_CF(data, cf, "vquic_send(len=%zu, gso=%zu) -> %d, sent=%zu", 29513498266Sopenharmony_ci blen, gsolen, result, sent); 29613498266Sopenharmony_ci if(result) { 29713498266Sopenharmony_ci if(result == CURLE_AGAIN) { 29813498266Sopenharmony_ci Curl_bufq_skip(&qctx->sendbuf, sent); 29913498266Sopenharmony_ci if(qctx->split_len) 30013498266Sopenharmony_ci qctx->split_len -= sent; 30113498266Sopenharmony_ci } 30213498266Sopenharmony_ci return result; 30313498266Sopenharmony_ci } 30413498266Sopenharmony_ci Curl_bufq_skip(&qctx->sendbuf, sent); 30513498266Sopenharmony_ci if(qctx->split_len) 30613498266Sopenharmony_ci qctx->split_len -= sent; 30713498266Sopenharmony_ci } 30813498266Sopenharmony_ci return CURLE_OK; 30913498266Sopenharmony_ci} 31013498266Sopenharmony_ci 31113498266Sopenharmony_ciCURLcode vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data, 31213498266Sopenharmony_ci struct cf_quic_ctx *qctx, size_t gsolen) 31313498266Sopenharmony_ci{ 31413498266Sopenharmony_ci qctx->gsolen = gsolen; 31513498266Sopenharmony_ci return vquic_flush(cf, data, qctx); 31613498266Sopenharmony_ci} 31713498266Sopenharmony_ci 31813498266Sopenharmony_ciCURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, 31913498266Sopenharmony_ci struct cf_quic_ctx *qctx, size_t gsolen, 32013498266Sopenharmony_ci size_t tail_len, size_t tail_gsolen) 32113498266Sopenharmony_ci{ 32213498266Sopenharmony_ci DEBUGASSERT(Curl_bufq_len(&qctx->sendbuf) > tail_len); 32313498266Sopenharmony_ci qctx->split_len = Curl_bufq_len(&qctx->sendbuf) - tail_len; 32413498266Sopenharmony_ci qctx->split_gsolen = gsolen; 32513498266Sopenharmony_ci qctx->gsolen = tail_gsolen; 32613498266Sopenharmony_ci CURL_TRC_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]", 32713498266Sopenharmony_ci qctx->split_len, qctx->split_gsolen, 32813498266Sopenharmony_ci tail_len, qctx->gsolen); 32913498266Sopenharmony_ci return vquic_flush(cf, data, qctx); 33013498266Sopenharmony_ci} 33113498266Sopenharmony_ci 33213498266Sopenharmony_ci#ifdef HAVE_SENDMMSG 33313498266Sopenharmony_cistatic CURLcode recvmmsg_packets(struct Curl_cfilter *cf, 33413498266Sopenharmony_ci struct Curl_easy *data, 33513498266Sopenharmony_ci struct cf_quic_ctx *qctx, 33613498266Sopenharmony_ci size_t max_pkts, 33713498266Sopenharmony_ci vquic_recv_pkt_cb *recv_cb, void *userp) 33813498266Sopenharmony_ci{ 33913498266Sopenharmony_ci#define MMSG_NUM 64 34013498266Sopenharmony_ci struct iovec msg_iov[MMSG_NUM]; 34113498266Sopenharmony_ci struct mmsghdr mmsg[MMSG_NUM]; 34213498266Sopenharmony_ci uint8_t bufs[MMSG_NUM][2*1024]; 34313498266Sopenharmony_ci struct sockaddr_storage remote_addr[MMSG_NUM]; 34413498266Sopenharmony_ci size_t total_nread, pkts; 34513498266Sopenharmony_ci int mcount, i, n; 34613498266Sopenharmony_ci char errstr[STRERROR_LEN]; 34713498266Sopenharmony_ci CURLcode result = CURLE_OK; 34813498266Sopenharmony_ci 34913498266Sopenharmony_ci DEBUGASSERT(max_pkts > 0); 35013498266Sopenharmony_ci pkts = 0; 35113498266Sopenharmony_ci total_nread = 0; 35213498266Sopenharmony_ci while(pkts < max_pkts) { 35313498266Sopenharmony_ci n = (int)CURLMIN(MMSG_NUM, max_pkts); 35413498266Sopenharmony_ci memset(&mmsg, 0, sizeof(mmsg)); 35513498266Sopenharmony_ci for(i = 0; i < n; ++i) { 35613498266Sopenharmony_ci msg_iov[i].iov_base = bufs[i]; 35713498266Sopenharmony_ci msg_iov[i].iov_len = (int)sizeof(bufs[i]); 35813498266Sopenharmony_ci mmsg[i].msg_hdr.msg_iov = &msg_iov[i]; 35913498266Sopenharmony_ci mmsg[i].msg_hdr.msg_iovlen = 1; 36013498266Sopenharmony_ci mmsg[i].msg_hdr.msg_name = &remote_addr[i]; 36113498266Sopenharmony_ci mmsg[i].msg_hdr.msg_namelen = sizeof(remote_addr[i]); 36213498266Sopenharmony_ci } 36313498266Sopenharmony_ci 36413498266Sopenharmony_ci while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 && 36513498266Sopenharmony_ci SOCKERRNO == EINTR) 36613498266Sopenharmony_ci ; 36713498266Sopenharmony_ci if(mcount == -1) { 36813498266Sopenharmony_ci if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { 36913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "ingress, recvmmsg -> EAGAIN"); 37013498266Sopenharmony_ci goto out; 37113498266Sopenharmony_ci } 37213498266Sopenharmony_ci if(!cf->connected && SOCKERRNO == ECONNREFUSED) { 37313498266Sopenharmony_ci const char *r_ip = NULL; 37413498266Sopenharmony_ci int r_port = 0; 37513498266Sopenharmony_ci Curl_cf_socket_peek(cf->next, data, NULL, NULL, 37613498266Sopenharmony_ci &r_ip, &r_port, NULL, NULL); 37713498266Sopenharmony_ci failf(data, "QUIC: connection to %s port %u refused", 37813498266Sopenharmony_ci r_ip, r_port); 37913498266Sopenharmony_ci result = CURLE_COULDNT_CONNECT; 38013498266Sopenharmony_ci goto out; 38113498266Sopenharmony_ci } 38213498266Sopenharmony_ci Curl_strerror(SOCKERRNO, errstr, sizeof(errstr)); 38313498266Sopenharmony_ci failf(data, "QUIC: recvmsg() unexpectedly returned %d (errno=%d; %s)", 38413498266Sopenharmony_ci mcount, SOCKERRNO, errstr); 38513498266Sopenharmony_ci result = CURLE_RECV_ERROR; 38613498266Sopenharmony_ci goto out; 38713498266Sopenharmony_ci } 38813498266Sopenharmony_ci 38913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "recvmmsg() -> %d packets", mcount); 39013498266Sopenharmony_ci pkts += mcount; 39113498266Sopenharmony_ci for(i = 0; i < mcount; ++i) { 39213498266Sopenharmony_ci total_nread += mmsg[i].msg_len; 39313498266Sopenharmony_ci result = recv_cb(bufs[i], mmsg[i].msg_len, 39413498266Sopenharmony_ci mmsg[i].msg_hdr.msg_name, mmsg[i].msg_hdr.msg_namelen, 39513498266Sopenharmony_ci 0, userp); 39613498266Sopenharmony_ci if(result) 39713498266Sopenharmony_ci goto out; 39813498266Sopenharmony_ci } 39913498266Sopenharmony_ci } 40013498266Sopenharmony_ci 40113498266Sopenharmony_ciout: 40213498266Sopenharmony_ci if(total_nread || result) 40313498266Sopenharmony_ci CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", 40413498266Sopenharmony_ci pkts, total_nread, result); 40513498266Sopenharmony_ci return result; 40613498266Sopenharmony_ci} 40713498266Sopenharmony_ci 40813498266Sopenharmony_ci#elif defined(HAVE_SENDMSG) 40913498266Sopenharmony_cistatic CURLcode recvmsg_packets(struct Curl_cfilter *cf, 41013498266Sopenharmony_ci struct Curl_easy *data, 41113498266Sopenharmony_ci struct cf_quic_ctx *qctx, 41213498266Sopenharmony_ci size_t max_pkts, 41313498266Sopenharmony_ci vquic_recv_pkt_cb *recv_cb, void *userp) 41413498266Sopenharmony_ci{ 41513498266Sopenharmony_ci struct iovec msg_iov; 41613498266Sopenharmony_ci struct msghdr msg; 41713498266Sopenharmony_ci uint8_t buf[64*1024]; 41813498266Sopenharmony_ci struct sockaddr_storage remote_addr; 41913498266Sopenharmony_ci size_t total_nread, pkts; 42013498266Sopenharmony_ci ssize_t nread; 42113498266Sopenharmony_ci char errstr[STRERROR_LEN]; 42213498266Sopenharmony_ci CURLcode result = CURLE_OK; 42313498266Sopenharmony_ci 42413498266Sopenharmony_ci msg_iov.iov_base = buf; 42513498266Sopenharmony_ci msg_iov.iov_len = (int)sizeof(buf); 42613498266Sopenharmony_ci 42713498266Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 42813498266Sopenharmony_ci msg.msg_iov = &msg_iov; 42913498266Sopenharmony_ci msg.msg_iovlen = 1; 43013498266Sopenharmony_ci 43113498266Sopenharmony_ci DEBUGASSERT(max_pkts > 0); 43213498266Sopenharmony_ci for(pkts = 0, total_nread = 0; pkts < max_pkts;) { 43313498266Sopenharmony_ci msg.msg_name = &remote_addr; 43413498266Sopenharmony_ci msg.msg_namelen = sizeof(remote_addr); 43513498266Sopenharmony_ci while((nread = recvmsg(qctx->sockfd, &msg, 0)) == -1 && 43613498266Sopenharmony_ci SOCKERRNO == EINTR) 43713498266Sopenharmony_ci ; 43813498266Sopenharmony_ci if(nread == -1) { 43913498266Sopenharmony_ci if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { 44013498266Sopenharmony_ci goto out; 44113498266Sopenharmony_ci } 44213498266Sopenharmony_ci if(!cf->connected && SOCKERRNO == ECONNREFUSED) { 44313498266Sopenharmony_ci const char *r_ip = NULL; 44413498266Sopenharmony_ci int r_port = 0; 44513498266Sopenharmony_ci Curl_cf_socket_peek(cf->next, data, NULL, NULL, 44613498266Sopenharmony_ci &r_ip, &r_port, NULL, NULL); 44713498266Sopenharmony_ci failf(data, "QUIC: connection to %s port %u refused", 44813498266Sopenharmony_ci r_ip, r_port); 44913498266Sopenharmony_ci result = CURLE_COULDNT_CONNECT; 45013498266Sopenharmony_ci goto out; 45113498266Sopenharmony_ci } 45213498266Sopenharmony_ci Curl_strerror(SOCKERRNO, errstr, sizeof(errstr)); 45313498266Sopenharmony_ci failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d; %s)", 45413498266Sopenharmony_ci nread, SOCKERRNO, errstr); 45513498266Sopenharmony_ci result = CURLE_RECV_ERROR; 45613498266Sopenharmony_ci goto out; 45713498266Sopenharmony_ci } 45813498266Sopenharmony_ci 45913498266Sopenharmony_ci ++pkts; 46013498266Sopenharmony_ci total_nread += (size_t)nread; 46113498266Sopenharmony_ci result = recv_cb(buf, (size_t)nread, msg.msg_name, msg.msg_namelen, 46213498266Sopenharmony_ci 0, userp); 46313498266Sopenharmony_ci if(result) 46413498266Sopenharmony_ci goto out; 46513498266Sopenharmony_ci } 46613498266Sopenharmony_ci 46713498266Sopenharmony_ciout: 46813498266Sopenharmony_ci if(total_nread || result) 46913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", 47013498266Sopenharmony_ci pkts, total_nread, result); 47113498266Sopenharmony_ci return result; 47213498266Sopenharmony_ci} 47313498266Sopenharmony_ci 47413498266Sopenharmony_ci#else /* HAVE_SENDMMSG || HAVE_SENDMSG */ 47513498266Sopenharmony_cistatic CURLcode recvfrom_packets(struct Curl_cfilter *cf, 47613498266Sopenharmony_ci struct Curl_easy *data, 47713498266Sopenharmony_ci struct cf_quic_ctx *qctx, 47813498266Sopenharmony_ci size_t max_pkts, 47913498266Sopenharmony_ci vquic_recv_pkt_cb *recv_cb, void *userp) 48013498266Sopenharmony_ci{ 48113498266Sopenharmony_ci uint8_t buf[64*1024]; 48213498266Sopenharmony_ci int bufsize = (int)sizeof(buf); 48313498266Sopenharmony_ci struct sockaddr_storage remote_addr; 48413498266Sopenharmony_ci socklen_t remote_addrlen = sizeof(remote_addr); 48513498266Sopenharmony_ci size_t total_nread, pkts; 48613498266Sopenharmony_ci ssize_t nread; 48713498266Sopenharmony_ci char errstr[STRERROR_LEN]; 48813498266Sopenharmony_ci CURLcode result = CURLE_OK; 48913498266Sopenharmony_ci 49013498266Sopenharmony_ci DEBUGASSERT(max_pkts > 0); 49113498266Sopenharmony_ci for(pkts = 0, total_nread = 0; pkts < max_pkts;) { 49213498266Sopenharmony_ci while((nread = recvfrom(qctx->sockfd, (char *)buf, bufsize, 0, 49313498266Sopenharmony_ci (struct sockaddr *)&remote_addr, 49413498266Sopenharmony_ci &remote_addrlen)) == -1 && 49513498266Sopenharmony_ci SOCKERRNO == EINTR) 49613498266Sopenharmony_ci ; 49713498266Sopenharmony_ci if(nread == -1) { 49813498266Sopenharmony_ci if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { 49913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN"); 50013498266Sopenharmony_ci goto out; 50113498266Sopenharmony_ci } 50213498266Sopenharmony_ci if(!cf->connected && SOCKERRNO == ECONNREFUSED) { 50313498266Sopenharmony_ci const char *r_ip = NULL; 50413498266Sopenharmony_ci int r_port = 0; 50513498266Sopenharmony_ci Curl_cf_socket_peek(cf->next, data, NULL, NULL, 50613498266Sopenharmony_ci &r_ip, &r_port, NULL, NULL); 50713498266Sopenharmony_ci failf(data, "QUIC: connection to %s port %u refused", 50813498266Sopenharmony_ci r_ip, r_port); 50913498266Sopenharmony_ci result = CURLE_COULDNT_CONNECT; 51013498266Sopenharmony_ci goto out; 51113498266Sopenharmony_ci } 51213498266Sopenharmony_ci Curl_strerror(SOCKERRNO, errstr, sizeof(errstr)); 51313498266Sopenharmony_ci failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d; %s)", 51413498266Sopenharmony_ci nread, SOCKERRNO, errstr); 51513498266Sopenharmony_ci result = CURLE_RECV_ERROR; 51613498266Sopenharmony_ci goto out; 51713498266Sopenharmony_ci } 51813498266Sopenharmony_ci 51913498266Sopenharmony_ci ++pkts; 52013498266Sopenharmony_ci total_nread += (size_t)nread; 52113498266Sopenharmony_ci result = recv_cb(buf, (size_t)nread, &remote_addr, remote_addrlen, 52213498266Sopenharmony_ci 0, userp); 52313498266Sopenharmony_ci if(result) 52413498266Sopenharmony_ci goto out; 52513498266Sopenharmony_ci } 52613498266Sopenharmony_ci 52713498266Sopenharmony_ciout: 52813498266Sopenharmony_ci if(total_nread || result) 52913498266Sopenharmony_ci CURL_TRC_CF(data, cf, "recvd %zu packets with %zu bytes -> %d", 53013498266Sopenharmony_ci pkts, total_nread, result); 53113498266Sopenharmony_ci return result; 53213498266Sopenharmony_ci} 53313498266Sopenharmony_ci#endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */ 53413498266Sopenharmony_ci 53513498266Sopenharmony_ciCURLcode vquic_recv_packets(struct Curl_cfilter *cf, 53613498266Sopenharmony_ci struct Curl_easy *data, 53713498266Sopenharmony_ci struct cf_quic_ctx *qctx, 53813498266Sopenharmony_ci size_t max_pkts, 53913498266Sopenharmony_ci vquic_recv_pkt_cb *recv_cb, void *userp) 54013498266Sopenharmony_ci{ 54113498266Sopenharmony_ci CURLcode result; 54213498266Sopenharmony_ci#if defined(HAVE_SENDMMSG) 54313498266Sopenharmony_ci result = recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); 54413498266Sopenharmony_ci#elif defined(HAVE_SENDMSG) 54513498266Sopenharmony_ci result = recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); 54613498266Sopenharmony_ci#else 54713498266Sopenharmony_ci result = recvfrom_packets(cf, data, qctx, max_pkts, recv_cb, userp); 54813498266Sopenharmony_ci#endif 54913498266Sopenharmony_ci if(!result) { 55013498266Sopenharmony_ci if(!qctx->got_first_byte) { 55113498266Sopenharmony_ci qctx->got_first_byte = TRUE; 55213498266Sopenharmony_ci qctx->first_byte_at = qctx->last_op; 55313498266Sopenharmony_ci } 55413498266Sopenharmony_ci qctx->last_io = qctx->last_op; 55513498266Sopenharmony_ci } 55613498266Sopenharmony_ci return result; 55713498266Sopenharmony_ci} 55813498266Sopenharmony_ci 55913498266Sopenharmony_ci/* 56013498266Sopenharmony_ci * If the QLOGDIR environment variable is set, open and return a file 56113498266Sopenharmony_ci * descriptor to write the log to. 56213498266Sopenharmony_ci * 56313498266Sopenharmony_ci * This function returns error if something failed outside of failing to 56413498266Sopenharmony_ci * create the file. Open file success is deemed by seeing if the returned fd 56513498266Sopenharmony_ci * is != -1. 56613498266Sopenharmony_ci */ 56713498266Sopenharmony_ciCURLcode Curl_qlogdir(struct Curl_easy *data, 56813498266Sopenharmony_ci unsigned char *scid, 56913498266Sopenharmony_ci size_t scidlen, 57013498266Sopenharmony_ci int *qlogfdp) 57113498266Sopenharmony_ci{ 57213498266Sopenharmony_ci const char *qlog_dir = getenv("QLOGDIR"); 57313498266Sopenharmony_ci *qlogfdp = -1; 57413498266Sopenharmony_ci if(qlog_dir) { 57513498266Sopenharmony_ci struct dynbuf fname; 57613498266Sopenharmony_ci CURLcode result; 57713498266Sopenharmony_ci unsigned int i; 57813498266Sopenharmony_ci Curl_dyn_init(&fname, DYN_QLOG_NAME); 57913498266Sopenharmony_ci result = Curl_dyn_add(&fname, qlog_dir); 58013498266Sopenharmony_ci if(!result) 58113498266Sopenharmony_ci result = Curl_dyn_add(&fname, "/"); 58213498266Sopenharmony_ci for(i = 0; (i < scidlen) && !result; i++) { 58313498266Sopenharmony_ci char hex[3]; 58413498266Sopenharmony_ci msnprintf(hex, 3, "%02x", scid[i]); 58513498266Sopenharmony_ci result = Curl_dyn_add(&fname, hex); 58613498266Sopenharmony_ci } 58713498266Sopenharmony_ci if(!result) 58813498266Sopenharmony_ci result = Curl_dyn_add(&fname, ".sqlog"); 58913498266Sopenharmony_ci 59013498266Sopenharmony_ci if(!result) { 59113498266Sopenharmony_ci int qlogfd = open(Curl_dyn_ptr(&fname), QLOGMODE, 59213498266Sopenharmony_ci data->set.new_file_perms); 59313498266Sopenharmony_ci if(qlogfd != -1) 59413498266Sopenharmony_ci *qlogfdp = qlogfd; 59513498266Sopenharmony_ci } 59613498266Sopenharmony_ci Curl_dyn_free(&fname); 59713498266Sopenharmony_ci if(result) 59813498266Sopenharmony_ci return result; 59913498266Sopenharmony_ci } 60013498266Sopenharmony_ci 60113498266Sopenharmony_ci return CURLE_OK; 60213498266Sopenharmony_ci} 60313498266Sopenharmony_ci 60413498266Sopenharmony_ciCURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, 60513498266Sopenharmony_ci struct Curl_easy *data, 60613498266Sopenharmony_ci struct connectdata *conn, 60713498266Sopenharmony_ci const struct Curl_addrinfo *ai, 60813498266Sopenharmony_ci int transport) 60913498266Sopenharmony_ci{ 61013498266Sopenharmony_ci (void)transport; 61113498266Sopenharmony_ci DEBUGASSERT(transport == TRNSPRT_QUIC); 61213498266Sopenharmony_ci#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) 61313498266Sopenharmony_ci return Curl_cf_ngtcp2_create(pcf, data, conn, ai); 61413498266Sopenharmony_ci#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3) 61513498266Sopenharmony_ci return Curl_cf_osslq_create(pcf, data, conn, ai); 61613498266Sopenharmony_ci#elif defined(USE_QUICHE) 61713498266Sopenharmony_ci return Curl_cf_quiche_create(pcf, data, conn, ai); 61813498266Sopenharmony_ci#elif defined(USE_MSH3) 61913498266Sopenharmony_ci return Curl_cf_msh3_create(pcf, data, conn, ai); 62013498266Sopenharmony_ci#else 62113498266Sopenharmony_ci *pcf = NULL; 62213498266Sopenharmony_ci (void)data; 62313498266Sopenharmony_ci (void)conn; 62413498266Sopenharmony_ci (void)ai; 62513498266Sopenharmony_ci return CURLE_NOT_BUILT_IN; 62613498266Sopenharmony_ci#endif 62713498266Sopenharmony_ci} 62813498266Sopenharmony_ci 62913498266Sopenharmony_cibool Curl_conn_is_http3(const struct Curl_easy *data, 63013498266Sopenharmony_ci const struct connectdata *conn, 63113498266Sopenharmony_ci int sockindex) 63213498266Sopenharmony_ci{ 63313498266Sopenharmony_ci#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) 63413498266Sopenharmony_ci return Curl_conn_is_ngtcp2(data, conn, sockindex); 63513498266Sopenharmony_ci#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3) 63613498266Sopenharmony_ci return Curl_conn_is_osslq(data, conn, sockindex); 63713498266Sopenharmony_ci#elif defined(USE_QUICHE) 63813498266Sopenharmony_ci return Curl_conn_is_quiche(data, conn, sockindex); 63913498266Sopenharmony_ci#elif defined(USE_MSH3) 64013498266Sopenharmony_ci return Curl_conn_is_msh3(data, conn, sockindex); 64113498266Sopenharmony_ci#else 64213498266Sopenharmony_ci return ((conn->handler->protocol & PROTO_FAMILY_HTTP) && 64313498266Sopenharmony_ci (conn->httpversion == 30)); 64413498266Sopenharmony_ci#endif 64513498266Sopenharmony_ci} 64613498266Sopenharmony_ci 64713498266Sopenharmony_ciCURLcode Curl_conn_may_http3(struct Curl_easy *data, 64813498266Sopenharmony_ci const struct connectdata *conn) 64913498266Sopenharmony_ci{ 65013498266Sopenharmony_ci if(conn->transport == TRNSPRT_UNIX) { 65113498266Sopenharmony_ci /* cannot do QUIC over a unix domain socket */ 65213498266Sopenharmony_ci return CURLE_QUIC_CONNECT_ERROR; 65313498266Sopenharmony_ci } 65413498266Sopenharmony_ci if(!(conn->handler->flags & PROTOPT_SSL)) { 65513498266Sopenharmony_ci failf(data, "HTTP/3 requested for non-HTTPS URL"); 65613498266Sopenharmony_ci return CURLE_URL_MALFORMAT; 65713498266Sopenharmony_ci } 65813498266Sopenharmony_ci#ifndef CURL_DISABLE_PROXY 65913498266Sopenharmony_ci if(conn->bits.socksproxy) { 66013498266Sopenharmony_ci failf(data, "HTTP/3 is not supported over a SOCKS proxy"); 66113498266Sopenharmony_ci return CURLE_URL_MALFORMAT; 66213498266Sopenharmony_ci } 66313498266Sopenharmony_ci if(conn->bits.httpproxy && conn->bits.tunnel_proxy) { 66413498266Sopenharmony_ci failf(data, "HTTP/3 is not supported over a HTTP proxy"); 66513498266Sopenharmony_ci return CURLE_URL_MALFORMAT; 66613498266Sopenharmony_ci } 66713498266Sopenharmony_ci#endif 66813498266Sopenharmony_ci 66913498266Sopenharmony_ci return CURLE_OK; 67013498266Sopenharmony_ci} 67113498266Sopenharmony_ci 67213498266Sopenharmony_ci#else /* ENABLE_QUIC */ 67313498266Sopenharmony_ci 67413498266Sopenharmony_ciCURLcode Curl_conn_may_http3(struct Curl_easy *data, 67513498266Sopenharmony_ci const struct connectdata *conn) 67613498266Sopenharmony_ci{ 67713498266Sopenharmony_ci (void)conn; 67813498266Sopenharmony_ci (void)data; 67913498266Sopenharmony_ci DEBUGF(infof(data, "QUIC is not supported in this build")); 68013498266Sopenharmony_ci return CURLE_NOT_BUILT_IN; 68113498266Sopenharmony_ci} 68213498266Sopenharmony_ci 68313498266Sopenharmony_ci#endif /* !ENABLE_QUIC */ 684