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