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