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