curl_msh3.c (30856B)
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 USE_MSH3 28 29 #include "../urldata.h" 30 #include "../hash.h" 31 #include "../uint-hash.h" 32 #include "../curlx/timeval.h" 33 #include "../multiif.h" 34 #include "../sendf.h" 35 #include "../curl_trc.h" 36 #include "../cfilters.h" 37 #include "../cf-socket.h" 38 #include "../connect.h" 39 #include "../progress.h" 40 #include "../http1.h" 41 #include "curl_msh3.h" 42 #include "../socketpair.h" 43 #include "../vtls/vtls.h" 44 #include "vquic.h" 45 #include "vquic_int.h" 46 47 /* The last 3 #include files should be in this order */ 48 #include "../curl_printf.h" 49 #include "../curl_memory.h" 50 #include "../memdebug.h" 51 52 #ifdef CURL_DISABLE_SOCKETPAIR 53 #error "MSH3 cannot be build with CURL_DISABLE_SOCKETPAIR set" 54 #endif 55 56 #define H3_STREAM_WINDOW_SIZE (128 * 1024) 57 #define H3_STREAM_CHUNK_SIZE (16 * 1024) 58 #define H3_STREAM_RECV_CHUNKS \ 59 (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE) 60 61 #ifdef _WIN32 62 #define msh3_lock CRITICAL_SECTION 63 #define msh3_lock_initialize(lock) InitializeCriticalSection(lock) 64 #define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock) 65 #define msh3_lock_acquire(lock) EnterCriticalSection(lock) 66 #define msh3_lock_release(lock) LeaveCriticalSection(lock) 67 #else /* !_WIN32 */ 68 #include <pthread.h> 69 #define msh3_lock pthread_mutex_t 70 #define msh3_lock_initialize(lock) do { \ 71 pthread_mutexattr_t attr; \ 72 pthread_mutexattr_init(&attr); \ 73 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \ 74 pthread_mutex_init(lock, &attr); \ 75 pthread_mutexattr_destroy(&attr); \ 76 } while(0) 77 #define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock) 78 #define msh3_lock_acquire(lock) pthread_mutex_lock(lock) 79 #define msh3_lock_release(lock) pthread_mutex_unlock(lock) 80 #endif /* _WIN32 */ 81 82 83 static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection, 84 void *IfContext); 85 static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection, 86 void *IfContext); 87 static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection, 88 void *IfContext, 89 MSH3_REQUEST *Request); 90 static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, 91 void *IfContext, 92 const MSH3_HEADER *Header); 93 static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, 94 void *IfContext, uint32_t *Length, 95 const uint8_t *Data); 96 static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, 97 bool Aborted, uint64_t AbortError); 98 static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request, 99 void *IfContext); 100 static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request, 101 void *IfContext, void *SendContext); 102 103 104 void Curl_msh3_ver(char *p, size_t len) 105 { 106 uint32_t v[4]; 107 MsH3Version(v); 108 (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]); 109 } 110 111 #define SP_LOCAL 0 112 #define SP_REMOTE 1 113 114 struct cf_msh3_ctx { 115 MSH3_API *api; 116 MSH3_CONNECTION *qconn; 117 struct Curl_sockaddr_ex addr; 118 curl_socket_t sock[2]; /* fake socket pair until we get support in msh3 */ 119 char l_ip[MAX_IPADR_LEN]; /* local IP as string */ 120 int l_port; /* local port number */ 121 struct cf_call_data call_data; 122 struct curltime connect_started; /* time the current attempt started */ 123 struct curltime handshake_at; /* time connect handshake finished */ 124 struct uint_hash streams; /* hash `data->mid` to `stream_ctx` */ 125 /* Flags written by msh3/msquic thread */ 126 BIT(handshake_complete); 127 BIT(handshake_succeeded); 128 BIT(connected); 129 BIT(initialized); 130 /* Flags written by curl thread */ 131 BIT(verbose); 132 BIT(active); 133 }; 134 135 static void h3_stream_hash_free(unsigned int id, void *stream); 136 137 static CURLcode cf_msh3_ctx_init(struct cf_msh3_ctx *ctx, 138 const struct Curl_addrinfo *ai) 139 { 140 CURLcode result; 141 142 DEBUGASSERT(!ctx->initialized); 143 Curl_uint_hash_init(&ctx->streams, 63, h3_stream_hash_free); 144 145 result = Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC); 146 if(result) 147 return result; 148 149 ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; 150 ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; 151 ctx->initialized = TRUE; 152 153 return result; 154 } 155 156 static void cf_msh3_ctx_free(struct cf_msh3_ctx *ctx) 157 { 158 if(ctx && ctx->initialized) { 159 Curl_uint_hash_destroy(&ctx->streams); 160 } 161 free(ctx); 162 } 163 164 static struct cf_msh3_ctx *h3_get_msh3_ctx(struct Curl_easy *data); 165 166 /* How to access `call_data` from a cf_msh3 filter */ 167 #undef CF_CTX_CALL_DATA 168 #define CF_CTX_CALL_DATA(cf) \ 169 ((struct cf_msh3_ctx *)(cf)->ctx)->call_data 170 171 /** 172 * All about the H3 internals of a stream 173 */ 174 struct h3_stream_ctx { 175 struct MSH3_REQUEST *req; 176 struct bufq recvbuf; /* h3 response */ 177 #ifdef _WIN32 178 CRITICAL_SECTION recv_lock; 179 #else /* !_WIN32 */ 180 pthread_mutex_t recv_lock; 181 #endif /* _WIN32 */ 182 uint64_t error3; /* HTTP/3 stream error code */ 183 int status_code; /* HTTP status code */ 184 CURLcode recv_error; 185 BIT(closed); 186 BIT(reset); 187 BIT(upload_done); 188 BIT(firstheader); /* FALSE until headers arrive */ 189 BIT(recv_header_complete); 190 }; 191 192 static void h3_stream_ctx_free(struct h3_stream_ctx *stream) 193 { 194 Curl_bufq_free(&stream->recvbuf); 195 free(stream); 196 } 197 198 static void h3_stream_hash_free(unsigned int id, void *stream) 199 { 200 (void)id; 201 DEBUGASSERT(stream); 202 h3_stream_ctx_free((struct h3_stream_ctx *)stream); 203 } 204 205 static CURLcode h3_data_setup(struct Curl_cfilter *cf, 206 struct Curl_easy *data) 207 { 208 struct cf_msh3_ctx *ctx = cf->ctx; 209 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); 210 211 if(stream) 212 return CURLE_OK; 213 214 stream = calloc(1, sizeof(*stream)); 215 if(!stream) 216 return CURLE_OUT_OF_MEMORY; 217 218 stream->req = ZERO_NULL; 219 msh3_lock_initialize(&stream->recv_lock); 220 Curl_bufq_init2(&stream->recvbuf, H3_STREAM_CHUNK_SIZE, 221 H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT); 222 CURL_TRC_CF(data, cf, "data setup"); 223 224 if(!Curl_uint_hash_set(&ctx->streams, data->mid, stream)) { 225 h3_stream_ctx_free(stream); 226 return CURLE_OUT_OF_MEMORY; 227 } 228 229 return CURLE_OK; 230 } 231 232 static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data) 233 { 234 struct cf_msh3_ctx *ctx = cf->ctx; 235 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); 236 237 (void)cf; 238 if(stream) { 239 CURL_TRC_CF(data, cf, "easy handle is done"); 240 Curl_uint_hash_remove(&ctx->streams, data->mid); 241 } 242 } 243 244 static void drain_stream_from_other_thread(struct Curl_easy *data, 245 struct h3_stream_ctx *stream) 246 { 247 (void)data; 248 (void)stream; 249 /* cannot expire from other thread. 250 here is the disconnect between msh3 and curl */ 251 } 252 253 static void h3_drain_stream(struct Curl_cfilter *cf, 254 struct Curl_easy *data) 255 { 256 (void)cf; 257 Curl_multi_mark_dirty(data); 258 } 259 260 static const MSH3_CONNECTION_IF msh3_conn_if = { 261 msh3_conn_connected, 262 msh3_conn_shutdown_complete, 263 msh3_conn_new_request 264 }; 265 266 static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection, 267 void *IfContext) 268 { 269 struct Curl_cfilter *cf = IfContext; 270 struct cf_msh3_ctx *ctx = cf->ctx; 271 struct Curl_easy *data = CF_DATA_CURRENT(cf); 272 (void)Connection; 273 274 CURL_TRC_CF(data, cf, "[MSH3] connected"); 275 ctx->handshake_succeeded = TRUE; 276 ctx->connected = TRUE; 277 ctx->handshake_complete = TRUE; 278 } 279 280 static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection, 281 void *IfContext) 282 { 283 struct Curl_cfilter *cf = IfContext; 284 struct cf_msh3_ctx *ctx = cf->ctx; 285 struct Curl_easy *data = CF_DATA_CURRENT(cf); 286 287 (void)Connection; 288 CURL_TRC_CF(data, cf, "[MSH3] shutdown complete"); 289 ctx->connected = FALSE; 290 ctx->handshake_complete = TRUE; 291 } 292 293 static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection, 294 void *IfContext, 295 MSH3_REQUEST *Request) 296 { 297 (void)Connection; 298 (void)IfContext; 299 (void)Request; 300 } 301 302 static const MSH3_REQUEST_IF msh3_request_if = { 303 msh3_header_received, 304 msh3_data_received, 305 msh3_complete, 306 msh3_shutdown_complete, 307 msh3_data_sent 308 }; 309 310 /* Decode HTTP status code. Returns -1 if no valid status code was 311 decoded. (duplicate from http2.c) */ 312 static int decode_status_code(const char *value, size_t len) 313 { 314 int i; 315 int res; 316 317 if(len != 3) { 318 return -1; 319 } 320 321 res = 0; 322 323 for(i = 0; i < 3; ++i) { 324 char c = value[i]; 325 326 if(c < '0' || c > '9') { 327 return -1; 328 } 329 330 res *= 10; 331 res += c - '0'; 332 } 333 334 return res; 335 } 336 337 /* 338 * write_resp_raw() copies response data in raw format to the `data`'s 339 * receive buffer. If not enough space is available, it appends to the 340 * `data`'s overflow buffer. 341 */ 342 static CURLcode write_resp_raw(struct Curl_easy *data, 343 const void *mem, size_t memlen) 344 { 345 struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); 346 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); 347 CURLcode result = CURLE_OK; 348 size_t nwritten; 349 350 if(!stream) 351 return CURLE_RECV_ERROR; 352 353 result = Curl_bufq_write(&stream->recvbuf, mem, memlen, &nwritten); 354 if(result) 355 return result; 356 357 if(nwritten < memlen) { 358 /* This MUST not happen. Our recbuf is dimensioned to hold the 359 * full max_stream_window and then some for this very reason. */ 360 DEBUGASSERT(0); 361 return CURLE_RECV_ERROR; 362 } 363 return result; 364 } 365 366 static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request, 367 void *userp, 368 const MSH3_HEADER *hd) 369 { 370 struct Curl_easy *data = userp; 371 struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); 372 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); 373 CURLcode result; 374 (void)Request; 375 376 DEBUGF(infof(data, "[MSH3] header received, stream=%d", !!stream)); 377 if(!stream || stream->recv_header_complete) { 378 return; 379 } 380 381 msh3_lock_acquire(&stream->recv_lock); 382 383 if((hd->NameLength == 7) && 384 !strncmp(HTTP_PSEUDO_STATUS, (const char *)hd->Name, 7)) { 385 char line[14]; /* status line is always 13 characters long */ 386 size_t ncopy; 387 388 DEBUGASSERT(!stream->firstheader); 389 stream->status_code = decode_status_code(hd->Value, hd->ValueLength); 390 DEBUGASSERT(stream->status_code != -1); 391 ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", 392 stream->status_code); 393 result = write_resp_raw(data, line, ncopy); 394 if(result) 395 stream->recv_error = result; 396 stream->firstheader = TRUE; 397 } 398 else { 399 /* store as an HTTP1-style header */ 400 DEBUGASSERT(stream->firstheader); 401 result = write_resp_raw(data, hd->Name, hd->NameLength); 402 if(!result) 403 result = write_resp_raw(data, ": ", 2); 404 if(!result) 405 result = write_resp_raw(data, hd->Value, hd->ValueLength); 406 if(!result) 407 result = write_resp_raw(data, "\r\n", 2); 408 if(result) { 409 stream->recv_error = result; 410 } 411 } 412 413 drain_stream_from_other_thread(data, stream); 414 msh3_lock_release(&stream->recv_lock); 415 } 416 417 static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request, 418 void *IfContext, uint32_t *buflen, 419 const uint8_t *buf) 420 { 421 struct Curl_easy *data = IfContext; 422 struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); 423 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); 424 CURLcode result; 425 bool rv = FALSE; 426 427 /* We would like to limit the amount of data we are buffer here. There seems 428 * to be no mechanism in msh3 to adjust flow control and it is undocumented 429 * what happens if we return FALSE here or less length (buflen is an inout 430 * parameter). 431 */ 432 (void)Request; 433 if(!stream) 434 return FALSE; 435 436 msh3_lock_acquire(&stream->recv_lock); 437 438 if(!stream->recv_header_complete) { 439 result = write_resp_raw(data, "\r\n", 2); 440 if(result) { 441 stream->recv_error = result; 442 goto out; 443 } 444 stream->recv_header_complete = TRUE; 445 } 446 447 result = write_resp_raw(data, buf, *buflen); 448 if(result) { 449 stream->recv_error = result; 450 } 451 rv = TRUE; 452 453 out: 454 msh3_lock_release(&stream->recv_lock); 455 return rv; 456 } 457 458 static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext, 459 bool aborted, uint64_t error) 460 { 461 struct Curl_easy *data = IfContext; 462 struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); 463 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); 464 465 (void)Request; 466 if(!stream) 467 return; 468 msh3_lock_acquire(&stream->recv_lock); 469 stream->closed = TRUE; 470 stream->recv_header_complete = TRUE; 471 if(error) 472 stream->error3 = error; 473 if(aborted) 474 stream->reset = TRUE; 475 msh3_lock_release(&stream->recv_lock); 476 } 477 478 static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request, 479 void *IfContext) 480 { 481 struct Curl_easy *data = IfContext; 482 struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); 483 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); 484 485 if(!stream) 486 return; 487 (void)Request; 488 (void)stream; 489 } 490 491 static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request, 492 void *IfContext, void *SendContext) 493 { 494 struct Curl_easy *data = IfContext; 495 struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data); 496 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); 497 if(!stream) 498 return; 499 (void)Request; 500 (void)stream; 501 (void)SendContext; 502 } 503 504 static CURLcode recv_closed_stream(struct Curl_cfilter *cf, 505 struct Curl_easy *data, 506 size_t *pnread) 507 { 508 struct cf_msh3_ctx *ctx = cf->ctx; 509 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); 510 511 (void)cf; 512 *pnread = 0; 513 if(!stream) 514 return CURLE_RECV_ERROR; 515 516 if(stream->reset) { 517 failf(data, "HTTP/3 stream reset by server"); 518 CURL_TRC_CF(data, cf, "cf_recv, was reset"); 519 return CURLE_PARTIAL_FILE; 520 } 521 else if(stream->error3) { 522 failf(data, "HTTP/3 stream was not closed cleanly: (error %zd)", 523 (ssize_t)stream->error3); 524 CURL_TRC_CF(data, cf, "cf_recv, closed uncleanly"); 525 return CURLE_HTTP3; 526 } 527 else { 528 CURL_TRC_CF(data, cf, "cf_recv, closed ok"); 529 } 530 return CURLE_OK; 531 } 532 533 static void set_quic_expire(struct Curl_cfilter *cf, struct Curl_easy *data) 534 { 535 struct cf_msh3_ctx *ctx = cf->ctx; 536 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); 537 538 /* we have no indication from msh3 when it would be a good time 539 * to juggle the connection again. So, we compromise by calling 540 * us again every some milliseconds. */ 541 (void)cf; 542 if(stream && stream->req && !stream->closed) { 543 Curl_expire(data, 10, EXPIRE_QUIC); 544 } 545 else { 546 Curl_expire(data, 50, EXPIRE_QUIC); 547 } 548 } 549 550 static CURLcode cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data, 551 char *buf, size_t len, size_t *pnread) 552 { 553 struct cf_msh3_ctx *ctx = cf->ctx; 554 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); 555 struct cf_call_data save; 556 CURLcode result = CURLE_OK; 557 558 *pnread = 0; 559 CURL_TRC_CF(data, cf, "cf_recv(len=%zu), stream=%d", len, !!stream); 560 if(!stream) 561 return CURLE_RECV_ERROR; 562 CF_DATA_SAVE(save, cf, data); 563 564 msh3_lock_acquire(&stream->recv_lock); 565 566 if(stream->recv_error) { 567 failf(data, "request aborted"); 568 result = stream->recv_error; 569 goto out; 570 } 571 572 if(!Curl_bufq_is_empty(&stream->recvbuf)) { 573 result = Curl_bufq_cread(&stream->recvbuf, buf, len, pnread); 574 CURL_TRC_CF(data, cf, "read recvbuf(len=%zu) -> %d, %zu", 575 len, result, *pnread); 576 if(result) 577 goto out; 578 if(stream->closed) 579 h3_drain_stream(cf, data); 580 } 581 else if(stream->closed) { 582 result = recv_closed_stream(cf, data, pnread); 583 goto out; 584 } 585 else { 586 CURL_TRC_CF(data, cf, "req: nothing here, call again"); 587 result = CURLE_AGAIN; 588 } 589 590 out: 591 msh3_lock_release(&stream->recv_lock); 592 set_quic_expire(cf, data); 593 CF_DATA_RESTORE(cf, save); 594 return result; 595 } 596 597 static CURLcode cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data, 598 const void *buf, size_t len, bool eos, 599 size_t *pnwritten) 600 { 601 struct cf_msh3_ctx *ctx = cf->ctx; 602 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); 603 struct h1_req_parser h1; 604 struct dynhds h2_headers; 605 MSH3_HEADER *nva = NULL; 606 size_t nheader, i; 607 ssize_t nwritten = -1; 608 struct cf_call_data save; 609 CURLcode result = CURLE_OK; 610 611 *pnwritten = 0; 612 CF_DATA_SAVE(save, cf, data); 613 614 Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN); 615 Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST); 616 617 /* Sizes must match for cast below to work" */ 618 DEBUGASSERT(stream); 619 CURL_TRC_CF(data, cf, "req: send %zu bytes", len); 620 621 if(!stream->req) { 622 /* The first send on the request contains the headers and possibly some 623 data. Parse out the headers and create the request, then if there is 624 any data left over go ahead and send it too. */ 625 nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, &result); 626 if(nwritten < 0) 627 goto out; 628 DEBUGASSERT(h1.done); 629 DEBUGASSERT(h1.req); 630 *pnwritten = (size_t)nwritten; 631 632 result = Curl_http_req_to_h2(&h2_headers, h1.req, data); 633 if(result) 634 goto out; 635 636 nheader = Curl_dynhds_count(&h2_headers); 637 nva = malloc(sizeof(MSH3_HEADER) * nheader); 638 if(!nva) { 639 result = CURLE_OUT_OF_MEMORY; 640 goto out; 641 } 642 643 for(i = 0; i < nheader; ++i) { 644 struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i); 645 nva[i].Name = e->name; 646 nva[i].NameLength = e->namelen; 647 nva[i].Value = e->value; 648 nva[i].ValueLength = e->valuelen; 649 } 650 651 CURL_TRC_CF(data, cf, "req: send %zu headers", nheader); 652 stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data, 653 nva, nheader, 654 eos ? MSH3_REQUEST_FLAG_FIN : 655 MSH3_REQUEST_FLAG_NONE); 656 if(!stream->req) { 657 failf(data, "request open failed"); 658 result = CURLE_SEND_ERROR; 659 } 660 result = CURLE_OK; 661 *pnwritten = len; 662 goto out; 663 } 664 else { 665 /* request is open */ 666 CURL_TRC_CF(data, cf, "req: send %zu body bytes", len); 667 if(len > 0xFFFFFFFF) { 668 len = 0xFFFFFFFF; 669 } 670 671 if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_NONE, buf, 672 (uint32_t)len, stream)) { 673 result = CURLE_SEND_ERROR; 674 goto out; 675 } 676 677 /* msh3/msquic will hold onto this memory until the send complete event. 678 How do we make sure curl does not free it until then? */ 679 result = CURLE_OK; 680 *pnwritten = len; 681 } 682 683 out: 684 set_quic_expire(cf, data); 685 free(nva); 686 Curl_h1_req_parse_free(&h1); 687 Curl_dynhds_free(&h2_headers); 688 CF_DATA_RESTORE(cf, save); 689 return result; 690 } 691 692 static void cf_msh3_adjust_pollset(struct Curl_cfilter *cf, 693 struct Curl_easy *data, 694 struct easy_pollset *ps) 695 { 696 struct cf_msh3_ctx *ctx = cf->ctx; 697 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); 698 struct cf_call_data save; 699 700 CF_DATA_SAVE(save, cf, data); 701 if(stream && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) { 702 if(stream->recv_error) { 703 Curl_pollset_add_in(data, ps, ctx->sock[SP_LOCAL]); 704 h3_drain_stream(cf, data); 705 } 706 else if(stream->req) { 707 Curl_pollset_add_out(data, ps, ctx->sock[SP_LOCAL]); 708 h3_drain_stream(cf, data); 709 } 710 } 711 } 712 713 static bool cf_msh3_data_pending(struct Curl_cfilter *cf, 714 const struct Curl_easy *data) 715 { 716 struct cf_msh3_ctx *ctx = cf->ctx; 717 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); 718 struct cf_call_data save; 719 bool pending = FALSE; 720 721 CF_DATA_SAVE(save, cf, data); 722 723 (void)cf; 724 if(stream && stream->req) { 725 msh3_lock_acquire(&stream->recv_lock); 726 CURL_TRC_CF((struct Curl_easy *)CURL_UNCONST(data), cf, 727 "data pending = %zu", 728 Curl_bufq_len(&stream->recvbuf)); 729 pending = !Curl_bufq_is_empty(&stream->recvbuf); 730 msh3_lock_release(&stream->recv_lock); 731 if(pending) 732 h3_drain_stream(cf, (struct Curl_easy *)CURL_UNCONST(data)); 733 } 734 735 CF_DATA_RESTORE(cf, save); 736 return pending; 737 } 738 739 static CURLcode h3_data_pause(struct Curl_cfilter *cf, 740 struct Curl_easy *data, 741 bool pause) 742 { 743 if(!pause) { 744 h3_drain_stream(cf, data); 745 Curl_expire(data, 0, EXPIRE_RUN_NOW); 746 } 747 return CURLE_OK; 748 } 749 750 static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf, 751 struct Curl_easy *data, 752 int event, int arg1, void *arg2) 753 { 754 struct cf_msh3_ctx *ctx = cf->ctx; 755 struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data); 756 struct cf_call_data save; 757 CURLcode result = CURLE_OK; 758 759 CF_DATA_SAVE(save, cf, data); 760 761 (void)arg1; 762 (void)arg2; 763 switch(event) { 764 case CF_CTRL_DATA_SETUP: 765 result = h3_data_setup(cf, data); 766 break; 767 case CF_CTRL_DATA_PAUSE: 768 result = h3_data_pause(cf, data, (arg1 != 0)); 769 break; 770 case CF_CTRL_DATA_DONE: 771 h3_data_done(cf, data); 772 break; 773 case CF_CTRL_DATA_DONE_SEND: 774 CURL_TRC_CF(data, cf, "req: send done"); 775 if(stream) { 776 stream->upload_done = TRUE; 777 if(stream->req) { 778 char buf[1]; 779 if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN, 780 buf, 0, data)) { 781 result = CURLE_SEND_ERROR; 782 } 783 } 784 } 785 break; 786 default: 787 break; 788 } 789 790 CF_DATA_RESTORE(cf, save); 791 return result; 792 } 793 794 static CURLcode cf_connect_start(struct Curl_cfilter *cf, 795 struct Curl_easy *data) 796 { 797 struct cf_msh3_ctx *ctx = cf->ctx; 798 struct ssl_primary_config *conn_config; 799 MSH3_ADDR addr = {0}; 800 CURLcode result; 801 bool verify; 802 803 DEBUGASSERT(ctx->initialized); 804 conn_config = Curl_ssl_cf_get_primary_config(cf); 805 if(!conn_config) 806 return CURLE_FAILED_INIT; 807 verify = !!conn_config->verifypeer; 808 809 memcpy(&addr, &ctx->addr.curl_sa_addr, ctx->addr.addrlen); 810 MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port); 811 812 if(verify && (conn_config->CAfile || conn_config->CApath)) { 813 /* Note there's currently no way to provide trust anchors to MSH3 and 814 that causes tests to fail. */ 815 CURL_TRC_CF(data, cf, "non-standard CA not supported, " 816 "attempting with built-in verification"); 817 } 818 819 CURL_TRC_CF(data, cf, "connecting to %s:%d (verify=%d)", 820 cf->conn->host.name, (int)cf->conn->remote_port, verify); 821 822 ctx->api = MsH3ApiOpen(); 823 if(!ctx->api) { 824 failf(data, "cannot create msh3 api"); 825 return CURLE_FAILED_INIT; 826 } 827 828 ctx->qconn = MsH3ConnectionOpen(ctx->api, 829 &msh3_conn_if, 830 cf, 831 cf->conn->host.name, 832 &addr, 833 !verify); 834 if(!ctx->qconn) { 835 failf(data, "cannot create msh3 connection"); 836 if(ctx->api) { 837 MsH3ApiClose(ctx->api); 838 ctx->api = NULL; 839 } 840 return CURLE_FAILED_INIT; 841 } 842 843 result = h3_data_setup(cf, data); 844 if(result) 845 return result; 846 847 return CURLE_OK; 848 } 849 850 static CURLcode cf_msh3_connect(struct Curl_cfilter *cf, 851 struct Curl_easy *data, 852 bool *done) 853 { 854 struct cf_msh3_ctx *ctx = cf->ctx; 855 struct cf_call_data save; 856 CURLcode result = CURLE_OK; 857 858 if(cf->connected) { 859 *done = TRUE; 860 return CURLE_OK; 861 } 862 863 CF_DATA_SAVE(save, cf, data); 864 865 if(ctx->sock[SP_LOCAL] == CURL_SOCKET_BAD) { 866 if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0], FALSE) < 0) { 867 ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; 868 ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; 869 return CURLE_COULDNT_CONNECT; 870 } 871 } 872 873 *done = FALSE; 874 if(!ctx->qconn) { 875 ctx->connect_started = curlx_now(); 876 result = cf_connect_start(cf, data); 877 if(result) 878 goto out; 879 } 880 881 if(ctx->handshake_complete) { 882 ctx->handshake_at = curlx_now(); 883 if(ctx->handshake_succeeded) { 884 CURL_TRC_CF(data, cf, "handshake succeeded"); 885 cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ 886 cf->connected = TRUE; 887 cf->conn->alpn = CURL_HTTP_VERSION_3; 888 *done = TRUE; 889 connkeep(cf->conn, "HTTP/3 default"); 890 Curl_pgrsTime(data, TIMER_APPCONNECT); 891 } 892 else { 893 failf(data, "failed to connect, handshake failed"); 894 result = CURLE_COULDNT_CONNECT; 895 } 896 } 897 898 out: 899 CF_DATA_RESTORE(cf, save); 900 return result; 901 } 902 903 static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data) 904 { 905 struct cf_msh3_ctx *ctx = cf->ctx; 906 struct cf_call_data save; 907 908 (void)data; 909 CF_DATA_SAVE(save, cf, data); 910 911 if(ctx) { 912 CURL_TRC_CF(data, cf, "destroying"); 913 if(ctx->qconn) { 914 MsH3ConnectionClose(ctx->qconn); 915 ctx->qconn = NULL; 916 } 917 if(ctx->api) { 918 MsH3ApiClose(ctx->api); 919 ctx->api = NULL; 920 } 921 922 if(ctx->active) { 923 /* We share our socket at cf->conn->sock[cf->sockindex] when active. 924 * If it is no longer there, someone has stolen (and hopefully 925 * closed it) and we just forget about it. 926 */ 927 ctx->active = FALSE; 928 if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) { 929 CURL_TRC_CF(data, cf, "cf_msh3_close(%d) active", 930 (int)ctx->sock[SP_LOCAL]); 931 cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD; 932 } 933 else { 934 CURL_TRC_CF(data, cf, "cf_socket_close(%d) no longer at " 935 "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]); 936 ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; 937 } 938 } 939 if(ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) { 940 sclose(ctx->sock[SP_LOCAL]); 941 } 942 if(ctx->sock[SP_REMOTE] != CURL_SOCKET_BAD) { 943 sclose(ctx->sock[SP_REMOTE]); 944 } 945 ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD; 946 ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD; 947 } 948 CF_DATA_RESTORE(cf, save); 949 } 950 951 static void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data) 952 { 953 struct cf_call_data save; 954 955 CF_DATA_SAVE(save, cf, data); 956 cf_msh3_close(cf, data); 957 if(cf->ctx) { 958 cf_msh3_ctx_free(cf->ctx); 959 cf->ctx = NULL; 960 } 961 /* no CF_DATA_RESTORE(cf, save); its gone */ 962 } 963 964 static CURLcode cf_msh3_query(struct Curl_cfilter *cf, 965 struct Curl_easy *data, 966 int query, int *pres1, void *pres2) 967 { 968 struct cf_msh3_ctx *ctx = cf->ctx; 969 970 switch(query) { 971 case CF_QUERY_MAX_CONCURRENT: { 972 /* We do not have access to this so far, fake it */ 973 (void)ctx; 974 *pres1 = 100; 975 return CURLE_OK; 976 } 977 case CF_QUERY_TIMER_CONNECT: { 978 struct curltime *when = pres2; 979 /* we do not know when the first byte arrived */ 980 if(cf->connected) 981 *when = ctx->handshake_at; 982 return CURLE_OK; 983 } 984 case CF_QUERY_TIMER_APPCONNECT: { 985 struct curltime *when = pres2; 986 if(cf->connected) 987 *when = ctx->handshake_at; 988 return CURLE_OK; 989 } 990 case CF_QUERY_HTTP_VERSION: 991 *pres1 = 30; 992 return CURLE_OK; 993 default: 994 break; 995 } 996 return cf->next ? 997 cf->next->cft->query(cf->next, data, query, pres1, pres2) : 998 CURLE_UNKNOWN_OPTION; 999 } 1000 1001 static bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf, 1002 struct Curl_easy *data, 1003 bool *input_pending) 1004 { 1005 struct cf_msh3_ctx *ctx = cf->ctx; 1006 1007 (void)data; 1008 *input_pending = FALSE; 1009 return ctx && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD && ctx->qconn && 1010 ctx->connected; 1011 } 1012 1013 struct Curl_cftype Curl_cft_http3 = { 1014 "HTTP/3", 1015 CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP, 1016 0, 1017 cf_msh3_destroy, 1018 cf_msh3_connect, 1019 cf_msh3_close, 1020 Curl_cf_def_shutdown, 1021 cf_msh3_adjust_pollset, 1022 cf_msh3_data_pending, 1023 cf_msh3_send, 1024 cf_msh3_recv, 1025 cf_msh3_data_event, 1026 cf_msh3_conn_is_alive, 1027 Curl_cf_def_conn_keep_alive, 1028 cf_msh3_query, 1029 }; 1030 1031 static struct cf_msh3_ctx *h3_get_msh3_ctx(struct Curl_easy *data) 1032 { 1033 if(data && data->conn) { 1034 struct Curl_cfilter *cf = data->conn->cfilter[FIRSTSOCKET]; 1035 while(cf) { 1036 if(cf->cft == &Curl_cft_http3) 1037 return cf->ctx; 1038 cf = cf->next; 1039 } 1040 } 1041 DEBUGF(infof(data, "no filter context found")); 1042 return NULL; 1043 } 1044 1045 CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf, 1046 struct Curl_easy *data, 1047 struct connectdata *conn, 1048 const struct Curl_addrinfo *ai) 1049 { 1050 struct cf_msh3_ctx *ctx = NULL; 1051 struct Curl_cfilter *cf = NULL; 1052 CURLcode result; 1053 1054 (void)data; 1055 (void)conn; 1056 (void)ai; /* msh3 resolves itself? */ 1057 ctx = calloc(1, sizeof(*ctx)); 1058 if(!ctx) { 1059 result = CURLE_OUT_OF_MEMORY; 1060 goto out; 1061 } 1062 1063 result = cf_msh3_ctx_init(ctx, ai); 1064 if(result) 1065 goto out; 1066 1067 result = Curl_cf_create(&cf, &Curl_cft_http3, ctx); 1068 1069 out: 1070 *pcf = (!result) ? cf : NULL; 1071 if(result) { 1072 Curl_safefree(cf); 1073 cf_msh3_ctx_free(ctx); 1074 } 1075 1076 return result; 1077 } 1078 1079 bool Curl_conn_is_msh3(const struct Curl_easy *data, 1080 const struct connectdata *conn, 1081 int sockindex) 1082 { 1083 struct Curl_cfilter *cf = conn ? conn->cfilter[sockindex] : NULL; 1084 1085 (void)data; 1086 for(; cf; cf = cf->next) { 1087 if(cf->cft == &Curl_cft_http3) 1088 return TRUE; 1089 if(cf->cft->flags & CF_TYPE_IP_CONNECT) 1090 return FALSE; 1091 } 1092 return FALSE; 1093 } 1094 1095 #endif /* USE_MSH3 */