Return to ngtcp2.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / lib / vquic |
1.1 ! misho 1: /*************************************************************************** ! 2: * _ _ ____ _ ! 3: * Project ___| | | | _ \| | ! 4: * / __| | | | |_) | | ! 5: * | (__| |_| | _ <| |___ ! 6: * \___|\___/|_| \_\_____| ! 7: * ! 8: * Copyright (C) 1998 - 2020, 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.haxx.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: ***************************************************************************/ ! 22: ! 23: #include "curl_setup.h" ! 24: ! 25: #ifdef USE_NGTCP2 ! 26: #include <ngtcp2/ngtcp2.h> ! 27: #include <ngtcp2/ngtcp2_crypto.h> ! 28: #include <nghttp3/nghttp3.h> ! 29: #ifdef USE_OPENSSL ! 30: #include <openssl/err.h> ! 31: #endif ! 32: #include "urldata.h" ! 33: #include "sendf.h" ! 34: #include "strdup.h" ! 35: #include "rand.h" ! 36: #include "ngtcp2.h" ! 37: #include "multiif.h" ! 38: #include "strcase.h" ! 39: #include "connect.h" ! 40: #include "strerror.h" ! 41: ! 42: /* The last 3 #include files should be in this order */ ! 43: #include "curl_printf.h" ! 44: #include "curl_memory.h" ! 45: #include "memdebug.h" ! 46: ! 47: /* #define DEBUG_NGTCP2 */ ! 48: #ifdef CURLDEBUG ! 49: #define DEBUG_HTTP3 ! 50: #endif ! 51: #ifdef DEBUG_HTTP3 ! 52: #define H3BUGF(x) x ! 53: #else ! 54: #define H3BUGF(x) do { } while(0) ! 55: #endif ! 56: ! 57: /* ! 58: * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked. ! 59: * It is used as a circular buffer. Add new bytes at the end until it reaches ! 60: * the far end, then start over at index 0 again. ! 61: */ ! 62: ! 63: #define H3_SEND_SIZE (20*1024) ! 64: struct h3out { ! 65: uint8_t buf[H3_SEND_SIZE]; ! 66: size_t used; /* number of bytes used in the buffer */ ! 67: size_t windex; /* index in the buffer where to start writing the next ! 68: data block */ ! 69: }; ! 70: ! 71: #define QUIC_MAX_STREAMS (256*1024) ! 72: #define QUIC_MAX_DATA (1*1024*1024) ! 73: #define QUIC_IDLE_TIMEOUT 60000 /* milliseconds */ ! 74: ! 75: #ifdef USE_OPENSSL ! 76: #define QUIC_CIPHERS \ ! 77: "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ ! 78: "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" ! 79: #define QUIC_GROUPS "P-256:X25519:P-384:P-521" ! 80: #elif defined(USE_GNUTLS) ! 81: #define QUIC_PRIORITY \ ! 82: "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \ ! 83: "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \ ! 84: "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1" ! 85: #endif ! 86: ! 87: static CURLcode ng_process_ingress(struct connectdata *conn, ! 88: curl_socket_t sockfd, ! 89: struct quicsocket *qs); ! 90: static CURLcode ng_flush_egress(struct connectdata *conn, int sockfd, ! 91: struct quicsocket *qs); ! 92: static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, ! 93: size_t datalen, void *user_data, ! 94: void *stream_user_data); ! 95: ! 96: static ngtcp2_tstamp timestamp(void) ! 97: { ! 98: struct curltime ct = Curl_now(); ! 99: return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS; ! 100: } ! 101: ! 102: #ifdef DEBUG_NGTCP2 ! 103: static void quic_printf(void *user_data, const char *fmt, ...) ! 104: { ! 105: va_list ap; ! 106: (void)user_data; /* TODO, use this to do infof() instead long-term */ ! 107: va_start(ap, fmt); ! 108: vfprintf(stderr, fmt, ap); ! 109: va_end(ap); ! 110: fprintf(stderr, "\n"); ! 111: } ! 112: #endif ! 113: ! 114: #ifdef USE_OPENSSL ! 115: static ngtcp2_crypto_level ! 116: quic_from_ossl_level(OSSL_ENCRYPTION_LEVEL ossl_level) ! 117: { ! 118: switch(ossl_level) { ! 119: case ssl_encryption_initial: ! 120: return NGTCP2_CRYPTO_LEVEL_INITIAL; ! 121: case ssl_encryption_early_data: ! 122: return NGTCP2_CRYPTO_LEVEL_EARLY; ! 123: case ssl_encryption_handshake: ! 124: return NGTCP2_CRYPTO_LEVEL_HANDSHAKE; ! 125: case ssl_encryption_application: ! 126: return NGTCP2_CRYPTO_LEVEL_APP; ! 127: default: ! 128: assert(0); ! 129: } ! 130: } ! 131: #elif defined(USE_GNUTLS) ! 132: static ngtcp2_crypto_level ! 133: quic_from_gtls_level(gnutls_record_encryption_level_t gtls_level) ! 134: { ! 135: switch(gtls_level) { ! 136: case GNUTLS_ENCRYPTION_LEVEL_INITIAL: ! 137: return NGTCP2_CRYPTO_LEVEL_INITIAL; ! 138: case GNUTLS_ENCRYPTION_LEVEL_EARLY: ! 139: return NGTCP2_CRYPTO_LEVEL_EARLY; ! 140: case GNUTLS_ENCRYPTION_LEVEL_HANDSHAKE: ! 141: return NGTCP2_CRYPTO_LEVEL_HANDSHAKE; ! 142: case GNUTLS_ENCRYPTION_LEVEL_APPLICATION: ! 143: return NGTCP2_CRYPTO_LEVEL_APP; ! 144: default: ! 145: assert(0); ! 146: } ! 147: } ! 148: #endif ! 149: ! 150: static int setup_initial_crypto_context(struct quicsocket *qs) ! 151: { ! 152: const ngtcp2_cid *dcid = ngtcp2_conn_get_dcid(qs->qconn); ! 153: ! 154: if(ngtcp2_crypto_derive_and_install_initial_key( ! 155: qs->qconn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, ! 156: dcid) != 0) ! 157: return -1; ! 158: ! 159: return 0; ! 160: } ! 161: ! 162: static void quic_settings(ngtcp2_settings *s, ! 163: uint64_t stream_buffer_size) ! 164: { ! 165: ngtcp2_settings_default(s); ! 166: #ifdef DEBUG_NGTCP2 ! 167: s->log_printf = quic_printf; ! 168: #else ! 169: s->log_printf = NULL; ! 170: #endif ! 171: s->initial_ts = timestamp(); ! 172: s->transport_params.initial_max_stream_data_bidi_local = stream_buffer_size; ! 173: s->transport_params.initial_max_stream_data_bidi_remote = QUIC_MAX_STREAMS; ! 174: s->transport_params.initial_max_stream_data_uni = QUIC_MAX_STREAMS; ! 175: s->transport_params.initial_max_data = QUIC_MAX_DATA; ! 176: s->transport_params.initial_max_streams_bidi = 1; ! 177: s->transport_params.initial_max_streams_uni = 3; ! 178: s->transport_params.max_idle_timeout = QUIC_IDLE_TIMEOUT; ! 179: } ! 180: ! 181: static FILE *keylog_file; /* not thread-safe */ ! 182: #ifdef USE_OPENSSL ! 183: static void keylog_callback(const SSL *ssl, const char *line) ! 184: { ! 185: (void)ssl; ! 186: fputs(line, keylog_file); ! 187: fputc('\n', keylog_file); ! 188: fflush(keylog_file); ! 189: } ! 190: #elif defined(USE_GNUTLS) ! 191: static int keylog_callback(gnutls_session_t session, const char *label, ! 192: const gnutls_datum_t *secret) ! 193: { ! 194: gnutls_datum_t crandom; ! 195: gnutls_datum_t srandom; ! 196: gnutls_datum_t crandom_hex = { NULL, 0 }; ! 197: gnutls_datum_t secret_hex = { NULL, 0 }; ! 198: int rc = 0; ! 199: ! 200: gnutls_session_get_random(session, &crandom, &srandom); ! 201: if(crandom.size != 32) { ! 202: return -1; ! 203: } ! 204: ! 205: rc = gnutls_hex_encode2(&crandom, &crandom_hex); ! 206: if(rc < 0) { ! 207: fprintf(stderr, "gnutls_hex_encode2 failed: %s\n", ! 208: gnutls_strerror(rc)); ! 209: goto out; ! 210: } ! 211: ! 212: rc = gnutls_hex_encode2(secret, &secret_hex); ! 213: if(rc < 0) { ! 214: fprintf(stderr, "gnutls_hex_encode2 failed: %s\n", ! 215: gnutls_strerror(rc)); ! 216: goto out; ! 217: } ! 218: ! 219: fprintf(keylog_file, "%s %s %s\n", label, crandom_hex.data, secret_hex.data); ! 220: fflush(keylog_file); ! 221: ! 222: out: ! 223: gnutls_free(crandom_hex.data); ! 224: gnutls_free(secret_hex.data); ! 225: return rc; ! 226: } ! 227: #endif ! 228: ! 229: static int init_ngh3_conn(struct quicsocket *qs); ! 230: ! 231: static int write_client_handshake(struct quicsocket *qs, ! 232: ngtcp2_crypto_level level, ! 233: const uint8_t *data, size_t len) ! 234: { ! 235: struct quic_handshake *crypto_data; ! 236: int rv; ! 237: ! 238: crypto_data = &qs->crypto_data[level]; ! 239: if(crypto_data->buf == NULL) { ! 240: crypto_data->buf = malloc(4096); ! 241: if(!crypto_data->buf) ! 242: return 0; ! 243: crypto_data->alloclen = 4096; ! 244: } ! 245: ! 246: /* TODO Just pretend that handshake does not grow more than 4KiB for ! 247: now */ ! 248: assert(crypto_data->len + len <= crypto_data->alloclen); ! 249: ! 250: memcpy(&crypto_data->buf[crypto_data->len], data, len); ! 251: crypto_data->len += len; ! 252: ! 253: rv = ngtcp2_conn_submit_crypto_data( ! 254: qs->qconn, level, (uint8_t *)(&crypto_data->buf[crypto_data->len] - len), ! 255: len); ! 256: if(rv) { ! 257: H3BUGF(fprintf(stderr, "write_client_handshake failed\n")); ! 258: } ! 259: assert(0 == rv); ! 260: ! 261: return 1; ! 262: } ! 263: ! 264: #ifdef USE_OPENSSL ! 265: static int quic_set_encryption_secrets(SSL *ssl, ! 266: OSSL_ENCRYPTION_LEVEL ossl_level, ! 267: const uint8_t *rx_secret, ! 268: const uint8_t *tx_secret, ! 269: size_t secretlen) ! 270: { ! 271: struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl); ! 272: int level = quic_from_ossl_level(ossl_level); ! 273: ! 274: if(level != NGTCP2_CRYPTO_LEVEL_EARLY && ! 275: ngtcp2_crypto_derive_and_install_rx_key( ! 276: qs->qconn, ssl, NULL, NULL, NULL, level, rx_secret, secretlen) != 0) ! 277: return 0; ! 278: ! 279: if(ngtcp2_crypto_derive_and_install_tx_key( ! 280: qs->qconn, ssl, NULL, NULL, NULL, level, tx_secret, secretlen) != 0) ! 281: return 0; ! 282: ! 283: if(level == NGTCP2_CRYPTO_LEVEL_APP) { ! 284: if(init_ngh3_conn(qs) != CURLE_OK) ! 285: return 0; ! 286: } ! 287: ! 288: return 1; ! 289: } ! 290: ! 291: static int quic_add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level, ! 292: const uint8_t *data, size_t len) ! 293: { ! 294: struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl); ! 295: ngtcp2_crypto_level level = quic_from_ossl_level(ossl_level); ! 296: ! 297: return write_client_handshake(qs, level, data, len); ! 298: } ! 299: ! 300: static int quic_flush_flight(SSL *ssl) ! 301: { ! 302: (void)ssl; ! 303: return 1; ! 304: } ! 305: ! 306: static int quic_send_alert(SSL *ssl, enum ssl_encryption_level_t level, ! 307: uint8_t alert) ! 308: { ! 309: struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl); ! 310: (void)level; ! 311: ! 312: qs->tls_alert = alert; ! 313: return 1; ! 314: } ! 315: ! 316: static SSL_QUIC_METHOD quic_method = {quic_set_encryption_secrets, ! 317: quic_add_handshake_data, ! 318: quic_flush_flight, quic_send_alert}; ! 319: ! 320: static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data) ! 321: { ! 322: SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); ! 323: const char *keylog_filename; ! 324: ! 325: SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); ! 326: SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION); ! 327: ! 328: SSL_CTX_set_default_verify_paths(ssl_ctx); ! 329: ! 330: if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) { ! 331: char error_buffer[256]; ! 332: ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer)); ! 333: failf(data, "SSL_CTX_set_ciphersuites: %s", error_buffer); ! 334: return NULL; ! 335: } ! 336: ! 337: if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) { ! 338: failf(data, "SSL_CTX_set1_groups_list failed"); ! 339: return NULL; ! 340: } ! 341: ! 342: SSL_CTX_set_quic_method(ssl_ctx, &quic_method); ! 343: ! 344: keylog_filename = getenv("SSLKEYLOGFILE"); ! 345: if(keylog_filename) { ! 346: keylog_file = fopen(keylog_filename, "wb"); ! 347: if(keylog_file) { ! 348: SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); ! 349: } ! 350: } ! 351: ! 352: return ssl_ctx; ! 353: } ! 354: ! 355: /** SSL callbacks ***/ ! 356: ! 357: static int quic_init_ssl(struct quicsocket *qs) ! 358: { ! 359: const uint8_t *alpn = NULL; ! 360: size_t alpnlen = 0; ! 361: /* this will need some attention when HTTPS proxy over QUIC get fixed */ ! 362: const char * const hostname = qs->conn->host.name; ! 363: ! 364: if(qs->ssl) ! 365: SSL_free(qs->ssl); ! 366: ! 367: qs->ssl = SSL_new(qs->sslctx); ! 368: ! 369: SSL_set_app_data(qs->ssl, qs); ! 370: SSL_set_connect_state(qs->ssl); ! 371: ! 372: switch(qs->version) { ! 373: #ifdef NGTCP2_PROTO_VER ! 374: case NGTCP2_PROTO_VER: ! 375: alpn = (const uint8_t *)NGTCP2_ALPN_H3; ! 376: alpnlen = sizeof(NGTCP2_ALPN_H3) - 1; ! 377: break; ! 378: #endif ! 379: } ! 380: if(alpn) ! 381: SSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen); ! 382: ! 383: /* set SNI */ ! 384: SSL_set_tlsext_host_name(qs->ssl, hostname); ! 385: return 0; ! 386: } ! 387: #elif defined(USE_GNUTLS) ! 388: static int secret_func(gnutls_session_t ssl, ! 389: gnutls_record_encryption_level_t gtls_level, ! 390: const void *rx_secret, ! 391: const void *tx_secret, size_t secretlen) ! 392: { ! 393: struct quicsocket *qs = gnutls_session_get_ptr(ssl); ! 394: int level = quic_from_gtls_level(gtls_level); ! 395: ! 396: if(level != NGTCP2_CRYPTO_LEVEL_EARLY && ! 397: ngtcp2_crypto_derive_and_install_rx_key( ! 398: qs->qconn, ssl, NULL, NULL, NULL, level, rx_secret, secretlen) != 0) ! 399: return 0; ! 400: ! 401: if(ngtcp2_crypto_derive_and_install_tx_key( ! 402: qs->qconn, ssl, NULL, NULL, NULL, level, tx_secret, secretlen) != 0) ! 403: return 0; ! 404: ! 405: if(level == NGTCP2_CRYPTO_LEVEL_APP) { ! 406: if(init_ngh3_conn(qs) != CURLE_OK) ! 407: return -1; ! 408: } ! 409: ! 410: return 0; ! 411: } ! 412: ! 413: static int read_func(gnutls_session_t ssl, ! 414: gnutls_record_encryption_level_t gtls_level, ! 415: gnutls_handshake_description_t htype, const void *data, ! 416: size_t len) ! 417: { ! 418: struct quicsocket *qs = gnutls_session_get_ptr(ssl); ! 419: ngtcp2_crypto_level level = quic_from_gtls_level(gtls_level); ! 420: int rv; ! 421: ! 422: if(htype == GNUTLS_HANDSHAKE_CHANGE_CIPHER_SPEC) ! 423: return 0; ! 424: ! 425: rv = write_client_handshake(qs, level, data, len); ! 426: if(rv == 0) ! 427: return -1; ! 428: ! 429: return 0; ! 430: } ! 431: ! 432: static int alert_read_func(gnutls_session_t ssl, ! 433: gnutls_record_encryption_level_t gtls_level, ! 434: gnutls_alert_level_t alert_level, ! 435: gnutls_alert_description_t alert_desc) ! 436: { ! 437: struct quicsocket *qs = gnutls_session_get_ptr(ssl); ! 438: (void)gtls_level; ! 439: (void)alert_level; ! 440: ! 441: qs->tls_alert = alert_desc; ! 442: return 1; ! 443: } ! 444: ! 445: static int tp_recv_func(gnutls_session_t ssl, const uint8_t *data, ! 446: size_t data_size) ! 447: { ! 448: struct quicsocket *qs = gnutls_session_get_ptr(ssl); ! 449: ngtcp2_transport_params params; ! 450: ! 451: if(ngtcp2_decode_transport_params( ! 452: ¶ms, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, ! 453: data, data_size) != 0) ! 454: return -1; ! 455: ! 456: if(ngtcp2_conn_set_remote_transport_params(qs->qconn, ¶ms) != 0) ! 457: return -1; ! 458: ! 459: return 0; ! 460: } ! 461: ! 462: static int tp_send_func(gnutls_session_t ssl, gnutls_buffer_t extdata) ! 463: { ! 464: struct quicsocket *qs = gnutls_session_get_ptr(ssl); ! 465: uint8_t paramsbuf[64]; ! 466: ngtcp2_transport_params params; ! 467: ssize_t nwrite; ! 468: int rc; ! 469: ! 470: ngtcp2_conn_get_local_transport_params(qs->qconn, ¶ms); ! 471: nwrite = ngtcp2_encode_transport_params( ! 472: paramsbuf, sizeof(paramsbuf), NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, ! 473: ¶ms); ! 474: if(nwrite < 0) { ! 475: fprintf(stderr, "ngtcp2_encode_transport_params: %s\n", ! 476: ngtcp2_strerror((int)nwrite)); ! 477: return -1; ! 478: } ! 479: ! 480: rc = gnutls_buffer_append_data(extdata, paramsbuf, nwrite); ! 481: if(rc < 0) ! 482: return rc; ! 483: ! 484: return (int)nwrite; ! 485: } ! 486: ! 487: static int quic_init_ssl(struct quicsocket *qs) ! 488: { ! 489: gnutls_datum_t alpn = {NULL, 0}; ! 490: /* this will need some attention when HTTPS proxy over QUIC get fixed */ ! 491: const char * const hostname = qs->conn->host.name; ! 492: const char *keylog_filename; ! 493: int rc; ! 494: ! 495: if(qs->ssl) ! 496: gnutls_deinit(qs->ssl); ! 497: ! 498: gnutls_init(&qs->ssl, GNUTLS_CLIENT); ! 499: gnutls_session_set_ptr(qs->ssl, qs); ! 500: ! 501: rc = gnutls_priority_set_direct(qs->ssl, QUIC_PRIORITY, NULL); ! 502: if(rc < 0) { ! 503: fprintf(stderr, "gnutls_priority_set_direct failed: %s\n", ! 504: gnutls_strerror(rc)); ! 505: return 1; ! 506: } ! 507: ! 508: gnutls_handshake_set_secret_function(qs->ssl, secret_func); ! 509: gnutls_handshake_set_read_function(qs->ssl, read_func); ! 510: gnutls_alert_set_read_function(qs->ssl, alert_read_func); ! 511: ! 512: rc = gnutls_session_ext_register(qs->ssl, "QUIC Transport Parameters", ! 513: 0xffa5, GNUTLS_EXT_TLS, ! 514: tp_recv_func, tp_send_func, ! 515: NULL, NULL, NULL, ! 516: GNUTLS_EXT_FLAG_TLS | ! 517: GNUTLS_EXT_FLAG_CLIENT_HELLO | ! 518: GNUTLS_EXT_FLAG_EE); ! 519: if(rc < 0) { ! 520: fprintf(stderr, "gnutls_session_ext_register failed: %s\n", ! 521: gnutls_strerror(rc)); ! 522: return 1; ! 523: } ! 524: ! 525: keylog_filename = getenv("SSLKEYLOGFILE"); ! 526: if(keylog_filename) { ! 527: keylog_file = fopen(keylog_filename, "wb"); ! 528: if(keylog_file) { ! 529: gnutls_session_set_keylog_function(qs->ssl, keylog_callback); ! 530: } ! 531: } ! 532: ! 533: if(qs->cred) ! 534: gnutls_certificate_free_credentials(qs->cred); ! 535: ! 536: rc = gnutls_certificate_allocate_credentials(&qs->cred); ! 537: if(rc < 0) { ! 538: fprintf(stderr, "gnutls_certificate_allocate_credentials failed: %s\n", ! 539: gnutls_strerror(rc)); ! 540: return 1; ! 541: } ! 542: ! 543: rc = gnutls_certificate_set_x509_system_trust(qs->cred); ! 544: if(rc < 0) { ! 545: fprintf(stderr, "gnutls_certificate_set_x509_system_trust failed: %s\n", ! 546: gnutls_strerror(rc)); ! 547: return 1; ! 548: } ! 549: ! 550: rc = gnutls_credentials_set(qs->ssl, GNUTLS_CRD_CERTIFICATE, qs->cred); ! 551: if(rc < 0) { ! 552: fprintf(stderr, "gnutls_credentials_set failed: %s\n", ! 553: gnutls_strerror(rc)); ! 554: return 1; ! 555: } ! 556: ! 557: switch(qs->version) { ! 558: #ifdef NGTCP2_PROTO_VER ! 559: case NGTCP2_PROTO_VER: ! 560: /* strip the first byte from NGTCP2_ALPN_H3 */ ! 561: alpn.data = (unsigned char *)NGTCP2_ALPN_H3 + 1; ! 562: alpn.size = sizeof(NGTCP2_ALPN_H3) - 2; ! 563: break; ! 564: #endif ! 565: } ! 566: if(alpn.data) ! 567: gnutls_alpn_set_protocols(qs->ssl, &alpn, 1, 0); ! 568: ! 569: /* set SNI */ ! 570: gnutls_server_name_set(qs->ssl, GNUTLS_NAME_DNS, hostname, strlen(hostname)); ! 571: return 0; ! 572: } ! 573: #endif ! 574: ! 575: static int cb_initial(ngtcp2_conn *quic, void *user_data) ! 576: { ! 577: struct quicsocket *qs = (struct quicsocket *)user_data; ! 578: ! 579: if(ngtcp2_crypto_read_write_crypto_data( ! 580: quic, qs->ssl, NGTCP2_CRYPTO_LEVEL_INITIAL, NULL, 0) != 0) ! 581: return NGTCP2_ERR_CALLBACK_FAILURE; ! 582: ! 583: return 0; ! 584: } ! 585: ! 586: static int ! 587: cb_recv_crypto_data(ngtcp2_conn *tconn, ngtcp2_crypto_level crypto_level, ! 588: uint64_t offset, ! 589: const uint8_t *data, size_t datalen, ! 590: void *user_data) ! 591: { ! 592: struct quicsocket *qs = (struct quicsocket *)user_data; ! 593: (void)offset; ! 594: ! 595: if(ngtcp2_crypto_read_write_crypto_data(tconn, qs->ssl, crypto_level, data, ! 596: datalen) != 0) ! 597: return NGTCP2_ERR_CRYPTO; ! 598: ! 599: return 0; ! 600: } ! 601: ! 602: static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data) ! 603: { ! 604: struct quicsocket *qs = (struct quicsocket *)user_data; ! 605: (void)tconn; ! 606: infof(qs->conn->data, "QUIC handshake is completed\n"); ! 607: ! 608: return 0; ! 609: } ! 610: ! 611: static void extend_stream_window(ngtcp2_conn *tconn, ! 612: struct HTTP *stream) ! 613: { ! 614: size_t thismuch = stream->unacked_window; ! 615: ngtcp2_conn_extend_max_stream_offset(tconn, stream->stream3_id, thismuch); ! 616: ngtcp2_conn_extend_max_offset(tconn, thismuch); ! 617: stream->unacked_window = 0; ! 618: } ! 619: ! 620: ! 621: static int cb_recv_stream_data(ngtcp2_conn *tconn, int64_t stream_id, ! 622: int fin, uint64_t offset, ! 623: const uint8_t *buf, size_t buflen, ! 624: void *user_data, void *stream_user_data) ! 625: { ! 626: struct quicsocket *qs = (struct quicsocket *)user_data; ! 627: ssize_t nconsumed; ! 628: (void)offset; ! 629: (void)stream_user_data; ! 630: ! 631: nconsumed = ! 632: nghttp3_conn_read_stream(qs->h3conn, stream_id, buf, buflen, fin); ! 633: if(nconsumed < 0) { ! 634: failf(qs->conn->data, "nghttp3_conn_read_stream returned error: %s\n", ! 635: nghttp3_strerror((int)nconsumed)); ! 636: return NGTCP2_ERR_CALLBACK_FAILURE; ! 637: } ! 638: ! 639: /* number of bytes inside buflen which consists of framing overhead ! 640: * including QPACK HEADERS. In other words, it does not consume payload of ! 641: * DATA frame. */ ! 642: ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed); ! 643: ngtcp2_conn_extend_max_offset(tconn, nconsumed); ! 644: ! 645: return 0; ! 646: } ! 647: ! 648: static int ! 649: cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, ! 650: uint64_t offset, size_t datalen, void *user_data, ! 651: void *stream_user_data) ! 652: { ! 653: struct quicsocket *qs = (struct quicsocket *)user_data; ! 654: int rv; ! 655: (void)stream_id; ! 656: (void)tconn; ! 657: (void)offset; ! 658: (void)datalen; ! 659: (void)stream_user_data; ! 660: ! 661: rv = nghttp3_conn_add_ack_offset(qs->h3conn, stream_id, datalen); ! 662: if(rv != 0) { ! 663: failf(qs->conn->data, "nghttp3_conn_add_ack_offset returned error: %s\n", ! 664: nghttp3_strerror(rv)); ! 665: return NGTCP2_ERR_CALLBACK_FAILURE; ! 666: } ! 667: ! 668: return 0; ! 669: } ! 670: ! 671: static int cb_stream_close(ngtcp2_conn *tconn, int64_t stream_id, ! 672: uint64_t app_error_code, ! 673: void *user_data, void *stream_user_data) ! 674: { ! 675: struct quicsocket *qs = (struct quicsocket *)user_data; ! 676: int rv; ! 677: (void)tconn; ! 678: (void)stream_user_data; ! 679: /* stream is closed... */ ! 680: ! 681: rv = nghttp3_conn_close_stream(qs->h3conn, stream_id, ! 682: app_error_code); ! 683: if(rv != 0) { ! 684: failf(qs->conn->data, "nghttp3_conn_close_stream returned error: %s\n", ! 685: nghttp3_strerror(rv)); ! 686: return NGTCP2_ERR_CALLBACK_FAILURE; ! 687: } ! 688: ! 689: return 0; ! 690: } ! 691: ! 692: static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, ! 693: uint64_t final_size, uint64_t app_error_code, ! 694: void *user_data, void *stream_user_data) ! 695: { ! 696: struct quicsocket *qs = (struct quicsocket *)user_data; ! 697: int rv; ! 698: (void)tconn; ! 699: (void)final_size; ! 700: (void)app_error_code; ! 701: (void)stream_user_data; ! 702: ! 703: rv = nghttp3_conn_reset_stream(qs->h3conn, stream_id); ! 704: if(rv != 0) { ! 705: failf(qs->conn->data, "nghttp3_conn_reset_stream returned error: %s\n", ! 706: nghttp3_strerror(rv)); ! 707: return NGTCP2_ERR_CALLBACK_FAILURE; ! 708: } ! 709: ! 710: return 0; ! 711: } ! 712: ! 713: static int cb_recv_retry(ngtcp2_conn *tconn, const ngtcp2_pkt_hd *hd, ! 714: const ngtcp2_pkt_retry *retry, void *user_data) ! 715: { ! 716: /* Re-generate handshake secrets here because connection ID might change. */ ! 717: struct quicsocket *qs = (struct quicsocket *)user_data; ! 718: (void)tconn; ! 719: (void)hd; ! 720: (void)retry; ! 721: ! 722: setup_initial_crypto_context(qs); ! 723: ! 724: return 0; ! 725: } ! 726: ! 727: static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn, ! 728: uint64_t max_streams, ! 729: void *user_data) ! 730: { ! 731: (void)tconn; ! 732: (void)max_streams; ! 733: (void)user_data; ! 734: ! 735: return 0; ! 736: } ! 737: ! 738: static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, ! 739: uint64_t max_data, void *user_data, ! 740: void *stream_user_data) ! 741: { ! 742: struct quicsocket *qs = (struct quicsocket *)user_data; ! 743: int rv; ! 744: (void)tconn; ! 745: (void)max_data; ! 746: (void)stream_user_data; ! 747: ! 748: rv = nghttp3_conn_unblock_stream(qs->h3conn, stream_id); ! 749: if(rv != 0) { ! 750: failf(qs->conn->data, "nghttp3_conn_unblock_stream returned error: %s\n", ! 751: nghttp3_strerror(rv)); ! 752: return NGTCP2_ERR_CALLBACK_FAILURE; ! 753: } ! 754: ! 755: return 0; ! 756: } ! 757: ! 758: static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, ! 759: uint8_t *token, size_t cidlen, ! 760: void *user_data) ! 761: { ! 762: struct quicsocket *qs = (struct quicsocket *)user_data; ! 763: CURLcode result; ! 764: (void)tconn; ! 765: ! 766: result = Curl_rand(qs->conn->data, cid->data, cidlen); ! 767: if(result) ! 768: return NGTCP2_ERR_CALLBACK_FAILURE; ! 769: cid->datalen = cidlen; ! 770: ! 771: result = Curl_rand(qs->conn->data, token, NGTCP2_STATELESS_RESET_TOKENLEN); ! 772: if(result) ! 773: return NGTCP2_ERR_CALLBACK_FAILURE; ! 774: ! 775: return 0; ! 776: } ! 777: ! 778: static ngtcp2_conn_callbacks ng_callbacks = { ! 779: cb_initial, ! 780: NULL, /* recv_client_initial */ ! 781: cb_recv_crypto_data, ! 782: cb_handshake_completed, ! 783: NULL, /* recv_version_negotiation */ ! 784: ngtcp2_crypto_encrypt_cb, ! 785: ngtcp2_crypto_decrypt_cb, ! 786: ngtcp2_crypto_hp_mask_cb, ! 787: cb_recv_stream_data, ! 788: NULL, /* acked_crypto_offset */ ! 789: cb_acked_stream_data_offset, ! 790: NULL, /* stream_open */ ! 791: cb_stream_close, ! 792: NULL, /* recv_stateless_reset */ ! 793: cb_recv_retry, ! 794: cb_extend_max_local_streams_bidi, ! 795: NULL, /* extend_max_local_streams_uni */ ! 796: NULL, /* rand */ ! 797: cb_get_new_connection_id, ! 798: NULL, /* remove_connection_id */ ! 799: ngtcp2_crypto_update_key_cb, /* update_key */ ! 800: NULL, /* path_validation */ ! 801: NULL, /* select_preferred_addr */ ! 802: cb_stream_reset, ! 803: NULL, /* extend_max_remote_streams_bidi */ ! 804: NULL, /* extend_max_remote_streams_uni */ ! 805: cb_extend_max_stream_data, ! 806: NULL, /* dcid_status */ ! 807: NULL /* handshake_confirmed */ ! 808: }; ! 809: ! 810: /* ! 811: * Might be called twice for happy eyeballs. ! 812: */ ! 813: CURLcode Curl_quic_connect(struct connectdata *conn, ! 814: curl_socket_t sockfd, ! 815: int sockindex, ! 816: const struct sockaddr *addr, ! 817: socklen_t addrlen) ! 818: { ! 819: int rc; ! 820: int rv; ! 821: CURLcode result; ! 822: ngtcp2_path path; /* TODO: this must be initialized properly */ ! 823: struct Curl_easy *data = conn->data; ! 824: struct quicsocket *qs = &conn->hequic[sockindex]; ! 825: char ipbuf[40]; ! 826: long port; ! 827: #ifdef USE_OPENSSL ! 828: uint8_t paramsbuf[64]; ! 829: ngtcp2_transport_params params; ! 830: ssize_t nwrite; ! 831: #endif ! 832: ! 833: qs->conn = conn; ! 834: ! 835: /* extract the used address as a string */ ! 836: if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) { ! 837: char buffer[STRERROR_LEN]; ! 838: failf(data, "ssrem inet_ntop() failed with errno %d: %s", ! 839: SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer))); ! 840: return CURLE_BAD_FUNCTION_ARGUMENT; ! 841: } ! 842: ! 843: infof(data, "Connect socket %d over QUIC to %s:%ld\n", ! 844: sockfd, ipbuf, port); ! 845: ! 846: qs->version = NGTCP2_PROTO_VER; ! 847: #ifdef USE_OPENSSL ! 848: qs->sslctx = quic_ssl_ctx(data); ! 849: if(!qs->sslctx) ! 850: return CURLE_QUIC_CONNECT_ERROR; ! 851: #endif ! 852: ! 853: if(quic_init_ssl(qs)) ! 854: return CURLE_QUIC_CONNECT_ERROR; ! 855: ! 856: qs->dcid.datalen = NGTCP2_MAX_CIDLEN; ! 857: result = Curl_rand(data, qs->dcid.data, NGTCP2_MAX_CIDLEN); ! 858: if(result) ! 859: return result; ! 860: ! 861: qs->scid.datalen = NGTCP2_MAX_CIDLEN; ! 862: result = Curl_rand(data, qs->scid.data, NGTCP2_MAX_CIDLEN); ! 863: if(result) ! 864: return result; ! 865: ! 866: quic_settings(&qs->settings, data->set.buffer_size); ! 867: ! 868: qs->local_addrlen = sizeof(qs->local_addr); ! 869: rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr, ! 870: &qs->local_addrlen); ! 871: if(rv == -1) ! 872: return CURLE_QUIC_CONNECT_ERROR; ! 873: ! 874: ngtcp2_addr_init(&path.local, (uint8_t *)&qs->local_addr, qs->local_addrlen, ! 875: NULL); ! 876: ngtcp2_addr_init(&path.remote, (uint8_t*)addr, addrlen, NULL); ! 877: ! 878: #ifdef NGTCP2_PROTO_VER ! 879: #define QUICVER NGTCP2_PROTO_VER ! 880: #else ! 881: #error "unsupported ngtcp2 version" ! 882: #endif ! 883: rc = ngtcp2_conn_client_new(&qs->qconn, &qs->dcid, &qs->scid, &path, QUICVER, ! 884: &ng_callbacks, &qs->settings, NULL, qs); ! 885: if(rc) ! 886: return CURLE_QUIC_CONNECT_ERROR; ! 887: ! 888: #ifdef USE_OPENSSL ! 889: ngtcp2_conn_get_local_transport_params(qs->qconn, ¶ms); ! 890: nwrite = ngtcp2_encode_transport_params( ! 891: paramsbuf, sizeof(paramsbuf), NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, ! 892: ¶ms); ! 893: if(nwrite < 0) { ! 894: failf(data, "ngtcp2_encode_transport_params: %s\n", ! 895: ngtcp2_strerror((int)nwrite)); ! 896: return CURLE_QUIC_CONNECT_ERROR; ! 897: } ! 898: ! 899: if(!SSL_set_quic_transport_params(qs->ssl, paramsbuf, nwrite)) ! 900: return CURLE_QUIC_CONNECT_ERROR; ! 901: #endif ! 902: ! 903: rc = setup_initial_crypto_context(qs); ! 904: if(rc) ! 905: return CURLE_QUIC_CONNECT_ERROR; ! 906: ! 907: return CURLE_OK; ! 908: } ! 909: ! 910: /* ! 911: * Store ngtp2 version info in this buffer, Prefix with a space. Return total ! 912: * length written. ! 913: */ ! 914: int Curl_quic_ver(char *p, size_t len) ! 915: { ! 916: ngtcp2_info *ng2 = ngtcp2_version(0); ! 917: nghttp3_info *ht3 = nghttp3_version(0); ! 918: return msnprintf(p, len, "ngtcp2/%s nghttp3/%s", ! 919: ng2->version_str, ht3->version_str); ! 920: } ! 921: ! 922: static int ng_getsock(struct connectdata *conn, curl_socket_t *socks) ! 923: { ! 924: struct SingleRequest *k = &conn->data->req; ! 925: int bitmap = GETSOCK_BLANK; ! 926: ! 927: socks[0] = conn->sock[FIRSTSOCKET]; ! 928: ! 929: /* in a HTTP/2 connection we can basically always get a frame so we should ! 930: always be ready for one */ ! 931: bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); ! 932: ! 933: /* we're still uploading or the HTTP/2 layer wants to send data */ ! 934: if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) ! 935: bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); ! 936: ! 937: return bitmap; ! 938: } ! 939: ! 940: static int ng_perform_getsock(const struct connectdata *conn, ! 941: curl_socket_t *socks) ! 942: { ! 943: return ng_getsock((struct connectdata *)conn, socks); ! 944: } ! 945: ! 946: static CURLcode ng_disconnect(struct connectdata *conn, ! 947: bool dead_connection) ! 948: { ! 949: int i; ! 950: struct quicsocket *qs = &conn->hequic[0]; ! 951: (void)dead_connection; ! 952: if(qs->ssl) ! 953: #ifdef USE_OPENSSL ! 954: SSL_free(qs->ssl); ! 955: #elif defined(USE_GNUTLS) ! 956: gnutls_deinit(qs->ssl); ! 957: #endif ! 958: #ifdef USE_GNUTLS ! 959: if(qs->cred) ! 960: gnutls_certificate_free_credentials(qs->cred); ! 961: #endif ! 962: for(i = 0; i < 3; i++) ! 963: free(qs->crypto_data[i].buf); ! 964: nghttp3_conn_del(qs->h3conn); ! 965: ngtcp2_conn_del(qs->qconn); ! 966: #ifdef USE_OPENSSL ! 967: SSL_CTX_free(qs->sslctx); ! 968: #endif ! 969: return CURLE_OK; ! 970: } ! 971: ! 972: static unsigned int ng_conncheck(struct connectdata *conn, ! 973: unsigned int checks_to_perform) ! 974: { ! 975: (void)conn; ! 976: (void)checks_to_perform; ! 977: return CONNRESULT_NONE; ! 978: } ! 979: ! 980: static const struct Curl_handler Curl_handler_http3 = { ! 981: "HTTPS", /* scheme */ ! 982: ZERO_NULL, /* setup_connection */ ! 983: Curl_http, /* do_it */ ! 984: Curl_http_done, /* done */ ! 985: ZERO_NULL, /* do_more */ ! 986: ZERO_NULL, /* connect_it */ ! 987: ZERO_NULL, /* connecting */ ! 988: ZERO_NULL, /* doing */ ! 989: ng_getsock, /* proto_getsock */ ! 990: ng_getsock, /* doing_getsock */ ! 991: ZERO_NULL, /* domore_getsock */ ! 992: ng_perform_getsock, /* perform_getsock */ ! 993: ng_disconnect, /* disconnect */ ! 994: ZERO_NULL, /* readwrite */ ! 995: ng_conncheck, /* connection_check */ ! 996: PORT_HTTP, /* defport */ ! 997: CURLPROTO_HTTPS, /* protocol */ ! 998: PROTOPT_SSL | PROTOPT_STREAM /* flags */ ! 999: }; ! 1000: ! 1001: static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, ! 1002: uint64_t app_error_code, void *user_data, ! 1003: void *stream_user_data) ! 1004: { ! 1005: struct Curl_easy *data = stream_user_data; ! 1006: struct HTTP *stream = data->req.protop; ! 1007: (void)conn; ! 1008: (void)stream_id; ! 1009: (void)app_error_code; ! 1010: (void)user_data; ! 1011: H3BUGF(infof(data, "cb_h3_stream_close CALLED\n")); ! 1012: ! 1013: stream->closed = TRUE; ! 1014: Curl_expire(data, 0, EXPIRE_QUIC); ! 1015: /* make sure that ngh3_stream_recv is called again to complete the transfer ! 1016: even if there are no more packets to be received from the server. */ ! 1017: data->state.drain = 1; ! 1018: return 0; ! 1019: } ! 1020: ! 1021: /* Minimum size of the overflow buffer */ ! 1022: #define OVERFLOWSIZE 1024 ! 1023: ! 1024: /* ! 1025: * allocate_overflow() ensures that there is room for incoming data in the ! 1026: * overflow buffer, growing it to accommodate the new data if necessary. We ! 1027: * may need to use the overflow buffer because we can't precisely limit the ! 1028: * amount of HTTP/3 header data we receive using QUIC flow control mechanisms. ! 1029: */ ! 1030: static CURLcode allocate_overflow(struct Curl_easy *data, ! 1031: struct HTTP *stream, ! 1032: size_t length) ! 1033: { ! 1034: size_t maxleft; ! 1035: size_t newsize; ! 1036: /* length can be arbitrarily large, so take care not to overflow newsize */ ! 1037: maxleft = CURL_MAX_READ_SIZE - stream->overflow_buflen; ! 1038: if(length > maxleft) { ! 1039: /* The reason to have a max limit for this is to avoid the risk of a bad ! 1040: server feeding libcurl with a highly compressed list of headers that ! 1041: will cause our overflow buffer to grow too large */ ! 1042: failf(data, "Rejected %zu bytes of overflow data (max is %d)!", ! 1043: stream->overflow_buflen + length, CURL_MAX_READ_SIZE); ! 1044: return CURLE_OUT_OF_MEMORY; ! 1045: } ! 1046: newsize = stream->overflow_buflen + length; ! 1047: if(newsize > stream->overflow_bufsize) { ! 1048: /* We enlarge the overflow buffer as it is too small */ ! 1049: char *newbuff; ! 1050: newsize = CURLMAX(newsize * 3 / 2, stream->overflow_bufsize*2); ! 1051: newsize = CURLMIN(CURLMAX(OVERFLOWSIZE, newsize), CURL_MAX_READ_SIZE); ! 1052: newbuff = realloc(stream->overflow_buf, newsize); ! 1053: if(!newbuff) { ! 1054: failf(data, "Failed to alloc memory for overflow buffer!"); ! 1055: return CURLE_OUT_OF_MEMORY; ! 1056: } ! 1057: stream->overflow_buf = newbuff; ! 1058: stream->overflow_bufsize = newsize; ! 1059: infof(data, "Grew HTTP/3 overflow buffer to %zu bytes\n", newsize); ! 1060: } ! 1061: return CURLE_OK; ! 1062: } ! 1063: ! 1064: /* ! 1065: * write_data() copies data to the stream's receive buffer. If not enough ! 1066: * space is available in the receive buffer, it copies the rest to the ! 1067: * stream's overflow buffer. ! 1068: */ ! 1069: static CURLcode write_data(struct Curl_easy *data, ! 1070: struct HTTP *stream, ! 1071: const void *mem, size_t memlen) ! 1072: { ! 1073: CURLcode result = CURLE_OK; ! 1074: const char *buf = mem; ! 1075: size_t ncopy = memlen; ! 1076: /* copy as much as possible to the receive buffer */ ! 1077: if(stream->len) { ! 1078: size_t len = CURLMIN(ncopy, stream->len); ! 1079: #if 0 /* extra debugging of incoming h3 data */ ! 1080: fprintf(stderr, "!! Copies %zd bytes to %p (total %zd)\n", ! 1081: len, stream->mem, stream->memlen); ! 1082: #endif ! 1083: memcpy(stream->mem, buf, len); ! 1084: stream->len -= len; ! 1085: stream->memlen += len; ! 1086: stream->mem += len; ! 1087: buf += len; ! 1088: ncopy -= len; ! 1089: } ! 1090: /* copy the rest to the overflow buffer */ ! 1091: if(ncopy) { ! 1092: result = allocate_overflow(data, stream, ncopy); ! 1093: if(result) { ! 1094: return result; ! 1095: } ! 1096: #if 0 /* extra debugging of incoming h3 data */ ! 1097: fprintf(stderr, "!! Copies %zd overflow bytes to %p (total %zd)\n", ! 1098: ncopy, stream->overflow_buf, stream->overflow_buflen); ! 1099: #endif ! 1100: memcpy(stream->overflow_buf + stream->overflow_buflen, buf, ncopy); ! 1101: stream->overflow_buflen += ncopy; ! 1102: } ! 1103: #if 0 /* extra debugging of incoming h3 data */ ! 1104: { ! 1105: size_t i; ! 1106: for(i = 0; i < memlen; i++) { ! 1107: fprintf(stderr, "!! data[%d]: %02x '%c'\n", i, buf[i], buf[i]); ! 1108: } ! 1109: } ! 1110: #endif ! 1111: return result; ! 1112: } ! 1113: ! 1114: static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream_id, ! 1115: const uint8_t *buf, size_t buflen, ! 1116: void *user_data, void *stream_user_data) ! 1117: { ! 1118: struct Curl_easy *data = stream_user_data; ! 1119: struct HTTP *stream = data->req.protop; ! 1120: CURLcode result = CURLE_OK; ! 1121: (void)conn; ! 1122: ! 1123: result = write_data(data, stream, buf, buflen); ! 1124: if(result) { ! 1125: return -1; ! 1126: } ! 1127: stream->unacked_window += buflen; ! 1128: (void)stream_id; ! 1129: (void)user_data; ! 1130: return 0; ! 1131: } ! 1132: ! 1133: static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id, ! 1134: size_t consumed, void *user_data, ! 1135: void *stream_user_data) ! 1136: { ! 1137: struct quicsocket *qs = user_data; ! 1138: (void)conn; ! 1139: (void)stream_user_data; ! 1140: (void)stream_id; ! 1141: ! 1142: ngtcp2_conn_extend_max_stream_offset(qs->qconn, stream_id, consumed); ! 1143: ngtcp2_conn_extend_max_offset(qs->qconn, consumed); ! 1144: return 0; ! 1145: } ! 1146: ! 1147: /* Decode HTTP status code. Returns -1 if no valid status code was ! 1148: decoded. (duplicate from http2.c) */ ! 1149: static int decode_status_code(const uint8_t *value, size_t len) ! 1150: { ! 1151: int i; ! 1152: int res; ! 1153: ! 1154: if(len != 3) { ! 1155: return -1; ! 1156: } ! 1157: ! 1158: res = 0; ! 1159: ! 1160: for(i = 0; i < 3; ++i) { ! 1161: char c = value[i]; ! 1162: ! 1163: if(c < '0' || c > '9') { ! 1164: return -1; ! 1165: } ! 1166: ! 1167: res *= 10; ! 1168: res += c - '0'; ! 1169: } ! 1170: ! 1171: return res; ! 1172: } ! 1173: ! 1174: static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, ! 1175: void *user_data, void *stream_user_data) ! 1176: { ! 1177: struct Curl_easy *data = stream_user_data; ! 1178: struct HTTP *stream = data->req.protop; ! 1179: CURLcode result = CURLE_OK; ! 1180: (void)conn; ! 1181: (void)stream_id; ! 1182: (void)user_data; ! 1183: ! 1184: /* add a CRLF only if we've received some headers */ ! 1185: if(stream->firstheader) { ! 1186: result = write_data(data, stream, "\r\n", 2); ! 1187: if(result) { ! 1188: return -1; ! 1189: } ! 1190: } ! 1191: return 0; ! 1192: } ! 1193: ! 1194: static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, ! 1195: int32_t token, nghttp3_rcbuf *name, ! 1196: nghttp3_rcbuf *value, uint8_t flags, ! 1197: void *user_data, void *stream_user_data) ! 1198: { ! 1199: nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); ! 1200: nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); ! 1201: struct Curl_easy *data = stream_user_data; ! 1202: struct HTTP *stream = data->req.protop; ! 1203: CURLcode result = CURLE_OK; ! 1204: (void)conn; ! 1205: (void)stream_id; ! 1206: (void)token; ! 1207: (void)flags; ! 1208: (void)user_data; ! 1209: ! 1210: if(h3name.len == sizeof(":status") - 1 && ! 1211: !memcmp(":status", h3name.base, h3name.len)) { ! 1212: char line[14]; /* status line is always 13 characters long */ ! 1213: size_t ncopy; ! 1214: int status = decode_status_code(h3val.base, h3val.len); ! 1215: DEBUGASSERT(status != -1); ! 1216: ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n", status); ! 1217: result = write_data(data, stream, line, ncopy); ! 1218: if(result) { ! 1219: return -1; ! 1220: } ! 1221: } ! 1222: else { ! 1223: /* store as a HTTP1-style header */ ! 1224: result = write_data(data, stream, h3name.base, h3name.len); ! 1225: if(result) { ! 1226: return -1; ! 1227: } ! 1228: result = write_data(data, stream, ": ", 2); ! 1229: if(result) { ! 1230: return -1; ! 1231: } ! 1232: result = write_data(data, stream, h3val.base, h3val.len); ! 1233: if(result) { ! 1234: return -1; ! 1235: } ! 1236: result = write_data(data, stream, "\r\n", 2); ! 1237: if(result) { ! 1238: return -1; ! 1239: } ! 1240: } ! 1241: ! 1242: stream->firstheader = TRUE; ! 1243: return 0; ! 1244: } ! 1245: ! 1246: static int cb_h3_send_stop_sending(nghttp3_conn *conn, int64_t stream_id, ! 1247: uint64_t app_error_code, ! 1248: void *user_data, ! 1249: void *stream_user_data) ! 1250: { ! 1251: (void)conn; ! 1252: (void)stream_id; ! 1253: (void)app_error_code; ! 1254: (void)user_data; ! 1255: (void)stream_user_data; ! 1256: return 0; ! 1257: } ! 1258: ! 1259: static nghttp3_conn_callbacks ngh3_callbacks = { ! 1260: cb_h3_acked_stream_data, /* acked_stream_data */ ! 1261: cb_h3_stream_close, ! 1262: cb_h3_recv_data, ! 1263: cb_h3_deferred_consume, ! 1264: NULL, /* begin_headers */ ! 1265: cb_h3_recv_header, ! 1266: cb_h3_end_headers, ! 1267: NULL, /* begin_trailers */ ! 1268: cb_h3_recv_header, ! 1269: NULL, /* end_trailers */ ! 1270: NULL, /* http_begin_push_promise */ ! 1271: NULL, /* http_recv_push_promise */ ! 1272: NULL, /* http_end_push_promise */ ! 1273: NULL, /* http_cancel_push */ ! 1274: cb_h3_send_stop_sending, ! 1275: NULL, /* push_stream */ ! 1276: NULL, /* end_stream */ ! 1277: }; ! 1278: ! 1279: static int init_ngh3_conn(struct quicsocket *qs) ! 1280: { ! 1281: CURLcode result; ! 1282: int rc; ! 1283: int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; ! 1284: ! 1285: if(ngtcp2_conn_get_max_local_streams_uni(qs->qconn) < 3) { ! 1286: failf(qs->conn->data, "too few available QUIC streams"); ! 1287: return CURLE_QUIC_CONNECT_ERROR; ! 1288: } ! 1289: ! 1290: nghttp3_conn_settings_default(&qs->h3settings); ! 1291: ! 1292: rc = nghttp3_conn_client_new(&qs->h3conn, ! 1293: &ngh3_callbacks, ! 1294: &qs->h3settings, ! 1295: nghttp3_mem_default(), ! 1296: qs); ! 1297: if(rc) { ! 1298: result = CURLE_OUT_OF_MEMORY; ! 1299: goto fail; ! 1300: } ! 1301: ! 1302: rc = ngtcp2_conn_open_uni_stream(qs->qconn, &ctrl_stream_id, NULL); ! 1303: if(rc) { ! 1304: result = CURLE_QUIC_CONNECT_ERROR; ! 1305: goto fail; ! 1306: } ! 1307: ! 1308: rc = nghttp3_conn_bind_control_stream(qs->h3conn, ctrl_stream_id); ! 1309: if(rc) { ! 1310: result = CURLE_QUIC_CONNECT_ERROR; ! 1311: goto fail; ! 1312: } ! 1313: ! 1314: rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_enc_stream_id, NULL); ! 1315: if(rc) { ! 1316: result = CURLE_QUIC_CONNECT_ERROR; ! 1317: goto fail; ! 1318: } ! 1319: ! 1320: rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_dec_stream_id, NULL); ! 1321: if(rc) { ! 1322: result = CURLE_QUIC_CONNECT_ERROR; ! 1323: goto fail; ! 1324: } ! 1325: ! 1326: rc = nghttp3_conn_bind_qpack_streams(qs->h3conn, qpack_enc_stream_id, ! 1327: qpack_dec_stream_id); ! 1328: if(rc) { ! 1329: result = CURLE_QUIC_CONNECT_ERROR; ! 1330: goto fail; ! 1331: } ! 1332: ! 1333: return CURLE_OK; ! 1334: fail: ! 1335: ! 1336: return result; ! 1337: } ! 1338: ! 1339: static Curl_recv ngh3_stream_recv; ! 1340: static Curl_send ngh3_stream_send; ! 1341: ! 1342: static size_t drain_overflow_buffer(struct HTTP *stream) ! 1343: { ! 1344: size_t ncopy = CURLMIN(stream->overflow_buflen, stream->len); ! 1345: if(ncopy > 0) { ! 1346: memcpy(stream->mem, stream->overflow_buf, ncopy); ! 1347: stream->len -= ncopy; ! 1348: stream->mem += ncopy; ! 1349: stream->memlen += ncopy; ! 1350: stream->overflow_buflen -= ncopy; ! 1351: memmove(stream->overflow_buf, stream->overflow_buf + ncopy, ! 1352: stream->overflow_buflen); ! 1353: } ! 1354: return ncopy; ! 1355: } ! 1356: ! 1357: /* incoming data frames on the h3 stream */ ! 1358: static ssize_t ngh3_stream_recv(struct connectdata *conn, ! 1359: int sockindex, ! 1360: char *buf, ! 1361: size_t buffersize, ! 1362: CURLcode *curlcode) ! 1363: { ! 1364: curl_socket_t sockfd = conn->sock[sockindex]; ! 1365: struct HTTP *stream = conn->data->req.protop; ! 1366: struct quicsocket *qs = conn->quic; ! 1367: ! 1368: if(!stream->memlen) { ! 1369: /* remember where to store incoming data for this stream and how big the ! 1370: buffer is */ ! 1371: stream->mem = buf; ! 1372: stream->len = buffersize; ! 1373: } ! 1374: /* else, there's data in the buffer already */ ! 1375: ! 1376: /* if there's data in the overflow buffer from a previous call, copy as much ! 1377: as possible to the receive buffer before receiving more */ ! 1378: drain_overflow_buffer(stream); ! 1379: ! 1380: if(ng_process_ingress(conn, sockfd, qs)) { ! 1381: *curlcode = CURLE_RECV_ERROR; ! 1382: return -1; ! 1383: } ! 1384: if(ng_flush_egress(conn, sockfd, qs)) { ! 1385: *curlcode = CURLE_SEND_ERROR; ! 1386: return -1; ! 1387: } ! 1388: ! 1389: if(stream->memlen) { ! 1390: ssize_t memlen = stream->memlen; ! 1391: /* data arrived */ ! 1392: *curlcode = CURLE_OK; ! 1393: /* reset to allow more data to come */ ! 1394: stream->memlen = 0; ! 1395: stream->mem = buf; ! 1396: stream->len = buffersize; ! 1397: /* extend the stream window with the data we're consuming and send out ! 1398: any additional packets to tell the server that we can receive more */ ! 1399: extend_stream_window(qs->qconn, stream); ! 1400: if(ng_flush_egress(conn, sockfd, qs)) { ! 1401: *curlcode = CURLE_SEND_ERROR; ! 1402: return -1; ! 1403: } ! 1404: return memlen; ! 1405: } ! 1406: ! 1407: if(stream->closed) { ! 1408: *curlcode = CURLE_OK; ! 1409: return 0; ! 1410: } ! 1411: ! 1412: infof(conn->data, "ngh3_stream_recv returns 0 bytes and EAGAIN\n"); ! 1413: *curlcode = CURLE_AGAIN; ! 1414: return -1; ! 1415: } ! 1416: ! 1417: /* this amount of data has now been acked on this stream */ ! 1418: static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, ! 1419: size_t datalen, void *user_data, ! 1420: void *stream_user_data) ! 1421: { ! 1422: struct Curl_easy *data = stream_user_data; ! 1423: struct HTTP *stream = data->req.protop; ! 1424: (void)conn; ! 1425: (void)stream_id; ! 1426: (void)user_data; ! 1427: ! 1428: if(!data->set.postfields) { ! 1429: stream->h3out->used -= datalen; ! 1430: H3BUGF(infof(data, ! 1431: "cb_h3_acked_stream_data, %zd bytes, %zd left unacked\n", ! 1432: datalen, stream->h3out->used)); ! 1433: DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE); ! 1434: } ! 1435: return 0; ! 1436: } ! 1437: ! 1438: static ssize_t cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id, ! 1439: nghttp3_vec *vec, size_t veccnt, ! 1440: uint32_t *pflags, void *user_data, ! 1441: void *stream_user_data) ! 1442: { ! 1443: struct Curl_easy *data = stream_user_data; ! 1444: size_t nread; ! 1445: struct HTTP *stream = data->req.protop; ! 1446: (void)conn; ! 1447: (void)stream_id; ! 1448: (void)user_data; ! 1449: (void)veccnt; ! 1450: ! 1451: if(data->set.postfields) { ! 1452: vec[0].base = data->set.postfields; ! 1453: vec[0].len = data->state.infilesize; ! 1454: *pflags = NGHTTP3_DATA_FLAG_EOF; ! 1455: return 1; ! 1456: } ! 1457: ! 1458: nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used); ! 1459: if(nread > 0) { ! 1460: /* nghttp3 wants us to hold on to the data until it tells us it is okay to ! 1461: delete it. Append the data at the end of the h3out buffer. Since we can ! 1462: only return consecutive data, copy the amount that fits and the next ! 1463: part comes in next invoke. */ ! 1464: struct h3out *out = stream->h3out; ! 1465: if(nread + out->windex > H3_SEND_SIZE) ! 1466: nread = H3_SEND_SIZE - out->windex; ! 1467: ! 1468: memcpy(&out->buf[out->windex], stream->upload_mem, nread); ! 1469: out->windex += nread; ! 1470: out->used += nread; ! 1471: ! 1472: /* that's the chunk we return to nghttp3 */ ! 1473: vec[0].base = &out->buf[out->windex]; ! 1474: vec[0].len = nread; ! 1475: ! 1476: if(out->windex == H3_SEND_SIZE) ! 1477: out->windex = 0; /* wrap */ ! 1478: stream->upload_mem += nread; ! 1479: stream->upload_len -= nread; ! 1480: if(data->state.infilesize != -1) { ! 1481: stream->upload_left -= nread; ! 1482: if(!stream->upload_left) ! 1483: *pflags = NGHTTP3_DATA_FLAG_EOF; ! 1484: } ! 1485: H3BUGF(infof(data, "cb_h3_readfunction %zd bytes%s (at %zd unacked)\n", ! 1486: nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", ! 1487: out->used)); ! 1488: } ! 1489: if(stream->upload_done && !stream->upload_len && ! 1490: (stream->upload_left <= 0)) { ! 1491: H3BUGF(infof(data, "!!!!!!!!! cb_h3_readfunction sets EOF\n")); ! 1492: *pflags = NGHTTP3_DATA_FLAG_EOF; ! 1493: return 0; ! 1494: } ! 1495: else if(!nread) { ! 1496: return NGHTTP3_ERR_WOULDBLOCK; ! 1497: } ! 1498: return 1; ! 1499: } ! 1500: ! 1501: /* Index where :authority header field will appear in request header ! 1502: field list. */ ! 1503: #define AUTHORITY_DST_IDX 3 ! 1504: ! 1505: static CURLcode http_request(struct connectdata *conn, const void *mem, ! 1506: size_t len) ! 1507: { ! 1508: struct HTTP *stream = conn->data->req.protop; ! 1509: size_t nheader; ! 1510: size_t i; ! 1511: size_t authority_idx; ! 1512: char *hdbuf = (char *)mem; ! 1513: char *end, *line_end; ! 1514: struct quicsocket *qs = conn->quic; ! 1515: CURLcode result = CURLE_OK; ! 1516: struct Curl_easy *data = conn->data; ! 1517: nghttp3_nv *nva = NULL; ! 1518: int64_t stream3_id; ! 1519: int rc; ! 1520: struct h3out *h3out = NULL; ! 1521: ! 1522: rc = ngtcp2_conn_open_bidi_stream(qs->qconn, &stream3_id, NULL); ! 1523: if(rc) { ! 1524: failf(conn->data, "can get bidi streams"); ! 1525: result = CURLE_SEND_ERROR; ! 1526: goto fail; ! 1527: } ! 1528: ! 1529: stream->stream3_id = stream3_id; ! 1530: stream->h3req = TRUE; /* senf off! */ ! 1531: ! 1532: /* Calculate number of headers contained in [mem, mem + len). Assumes a ! 1533: correctly generated HTTP header field block. */ ! 1534: nheader = 0; ! 1535: for(i = 1; i < len; ++i) { ! 1536: if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') { ! 1537: ++nheader; ! 1538: ++i; ! 1539: } ! 1540: } ! 1541: if(nheader < 2) ! 1542: goto fail; ! 1543: ! 1544: /* We counted additional 2 \r\n in the first and last line. We need 3 ! 1545: new headers: :method, :path and :scheme. Therefore we need one ! 1546: more space. */ ! 1547: nheader += 1; ! 1548: nva = malloc(sizeof(nghttp3_nv) * nheader); ! 1549: if(!nva) { ! 1550: result = CURLE_OUT_OF_MEMORY; ! 1551: goto fail; ! 1552: } ! 1553: ! 1554: /* Extract :method, :path from request line ! 1555: We do line endings with CRLF so checking for CR is enough */ ! 1556: line_end = memchr(hdbuf, '\r', len); ! 1557: if(!line_end) { ! 1558: result = CURLE_BAD_FUNCTION_ARGUMENT; /* internal error */ ! 1559: goto fail; ! 1560: } ! 1561: ! 1562: /* Method does not contain spaces */ ! 1563: end = memchr(hdbuf, ' ', line_end - hdbuf); ! 1564: if(!end || end == hdbuf) ! 1565: goto fail; ! 1566: nva[0].name = (unsigned char *)":method"; ! 1567: nva[0].namelen = strlen((char *)nva[0].name); ! 1568: nva[0].value = (unsigned char *)hdbuf; ! 1569: nva[0].valuelen = (size_t)(end - hdbuf); ! 1570: nva[0].flags = NGHTTP3_NV_FLAG_NONE; ! 1571: ! 1572: hdbuf = end + 1; ! 1573: ! 1574: /* Path may contain spaces so scan backwards */ ! 1575: end = NULL; ! 1576: for(i = (size_t)(line_end - hdbuf); i; --i) { ! 1577: if(hdbuf[i - 1] == ' ') { ! 1578: end = &hdbuf[i - 1]; ! 1579: break; ! 1580: } ! 1581: } ! 1582: if(!end || end == hdbuf) ! 1583: goto fail; ! 1584: nva[1].name = (unsigned char *)":path"; ! 1585: nva[1].namelen = strlen((char *)nva[1].name); ! 1586: nva[1].value = (unsigned char *)hdbuf; ! 1587: nva[1].valuelen = (size_t)(end - hdbuf); ! 1588: nva[1].flags = NGHTTP3_NV_FLAG_NONE; ! 1589: ! 1590: nva[2].name = (unsigned char *)":scheme"; ! 1591: nva[2].namelen = strlen((char *)nva[2].name); ! 1592: if(conn->handler->flags & PROTOPT_SSL) ! 1593: nva[2].value = (unsigned char *)"https"; ! 1594: else ! 1595: nva[2].value = (unsigned char *)"http"; ! 1596: nva[2].valuelen = strlen((char *)nva[2].value); ! 1597: nva[2].flags = NGHTTP3_NV_FLAG_NONE; ! 1598: ! 1599: ! 1600: authority_idx = 0; ! 1601: i = 3; ! 1602: while(i < nheader) { ! 1603: size_t hlen; ! 1604: ! 1605: hdbuf = line_end + 2; ! 1606: ! 1607: /* check for next CR, but only within the piece of data left in the given ! 1608: buffer */ ! 1609: line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem)); ! 1610: if(!line_end || (line_end == hdbuf)) ! 1611: goto fail; ! 1612: ! 1613: /* header continuation lines are not supported */ ! 1614: if(*hdbuf == ' ' || *hdbuf == '\t') ! 1615: goto fail; ! 1616: ! 1617: for(end = hdbuf; end < line_end && *end != ':'; ++end) ! 1618: ; ! 1619: if(end == hdbuf || end == line_end) ! 1620: goto fail; ! 1621: hlen = end - hdbuf; ! 1622: ! 1623: if(hlen == 4 && strncasecompare("host", hdbuf, 4)) { ! 1624: authority_idx = i; ! 1625: nva[i].name = (unsigned char *)":authority"; ! 1626: nva[i].namelen = strlen((char *)nva[i].name); ! 1627: } ! 1628: else { ! 1629: nva[i].namelen = (size_t)(end - hdbuf); ! 1630: /* Lower case the header name for HTTP/3 */ ! 1631: Curl_strntolower((char *)hdbuf, hdbuf, nva[i].namelen); ! 1632: nva[i].name = (unsigned char *)hdbuf; ! 1633: } ! 1634: nva[i].flags = NGHTTP3_NV_FLAG_NONE; ! 1635: hdbuf = end + 1; ! 1636: while(*hdbuf == ' ' || *hdbuf == '\t') ! 1637: ++hdbuf; ! 1638: end = line_end; ! 1639: ! 1640: #if 0 /* This should probably go in more or less like this */ ! 1641: switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf, ! 1642: end - hdbuf)) { ! 1643: case HEADERINST_IGNORE: ! 1644: /* skip header fields prohibited by HTTP/2 specification. */ ! 1645: --nheader; ! 1646: continue; ! 1647: case HEADERINST_TE_TRAILERS: ! 1648: nva[i].value = (uint8_t*)"trailers"; ! 1649: nva[i].value_len = sizeof("trailers") - 1; ! 1650: break; ! 1651: default: ! 1652: nva[i].value = (unsigned char *)hdbuf; ! 1653: nva[i].value_len = (size_t)(end - hdbuf); ! 1654: } ! 1655: #endif ! 1656: nva[i].value = (unsigned char *)hdbuf; ! 1657: nva[i].valuelen = (size_t)(end - hdbuf); ! 1658: nva[i].flags = NGHTTP3_NV_FLAG_NONE; ! 1659: ! 1660: ++i; ! 1661: } ! 1662: ! 1663: /* :authority must come before non-pseudo header fields */ ! 1664: if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) { ! 1665: nghttp3_nv authority = nva[authority_idx]; ! 1666: for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) { ! 1667: nva[i] = nva[i - 1]; ! 1668: } ! 1669: nva[i] = authority; ! 1670: } ! 1671: ! 1672: /* Warn stream may be rejected if cumulative length of headers is too ! 1673: large. */ ! 1674: #define MAX_ACC 60000 /* <64KB to account for some overhead */ ! 1675: { ! 1676: size_t acc = 0; ! 1677: for(i = 0; i < nheader; ++i) ! 1678: acc += nva[i].namelen + nva[i].valuelen; ! 1679: ! 1680: if(acc > MAX_ACC) { ! 1681: infof(data, "http_request: Warning: The cumulative length of all " ! 1682: "headers exceeds %zu bytes and that could cause the " ! 1683: "stream to be rejected.\n", MAX_ACC); ! 1684: } ! 1685: } ! 1686: ! 1687: switch(data->set.httpreq) { ! 1688: case HTTPREQ_POST: ! 1689: case HTTPREQ_POST_FORM: ! 1690: case HTTPREQ_POST_MIME: ! 1691: case HTTPREQ_PUT: { ! 1692: nghttp3_data_reader data_reader; ! 1693: if(data->state.infilesize != -1) ! 1694: stream->upload_left = data->state.infilesize; ! 1695: else ! 1696: /* data sending without specifying the data amount up front */ ! 1697: stream->upload_left = -1; /* unknown, but not zero */ ! 1698: ! 1699: data_reader.read_data = cb_h3_readfunction; ! 1700: ! 1701: h3out = calloc(sizeof(struct h3out), 1); ! 1702: if(!h3out) { ! 1703: result = CURLE_OUT_OF_MEMORY; ! 1704: goto fail; ! 1705: } ! 1706: stream->h3out = h3out; ! 1707: ! 1708: rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id, ! 1709: nva, nheader, &data_reader, ! 1710: conn->data); ! 1711: if(rc) { ! 1712: result = CURLE_SEND_ERROR; ! 1713: goto fail; ! 1714: } ! 1715: break; ! 1716: } ! 1717: default: ! 1718: stream->upload_left = 0; /* nothing left to send */ ! 1719: rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id, ! 1720: nva, nheader, ! 1721: NULL, /* no body! */ ! 1722: conn->data); ! 1723: if(rc) { ! 1724: result = CURLE_SEND_ERROR; ! 1725: goto fail; ! 1726: } ! 1727: break; ! 1728: } ! 1729: ! 1730: Curl_safefree(nva); ! 1731: ! 1732: infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)\n", ! 1733: stream3_id, (void *)data); ! 1734: ! 1735: return CURLE_OK; ! 1736: ! 1737: fail: ! 1738: free(nva); ! 1739: return result; ! 1740: } ! 1741: static ssize_t ngh3_stream_send(struct connectdata *conn, ! 1742: int sockindex, ! 1743: const void *mem, ! 1744: size_t len, ! 1745: CURLcode *curlcode) ! 1746: { ! 1747: ssize_t sent; ! 1748: struct quicsocket *qs = conn->quic; ! 1749: curl_socket_t sockfd = conn->sock[sockindex]; ! 1750: struct HTTP *stream = conn->data->req.protop; ! 1751: ! 1752: if(!stream->h3req) { ! 1753: CURLcode result = http_request(conn, mem, len); ! 1754: if(result) { ! 1755: *curlcode = CURLE_SEND_ERROR; ! 1756: return -1; ! 1757: } ! 1758: sent = len; ! 1759: } ! 1760: else { ! 1761: H3BUGF(infof(conn->data, "ngh3_stream_send() wants to send %zd bytes\n", ! 1762: len)); ! 1763: if(!stream->upload_len) { ! 1764: stream->upload_mem = mem; ! 1765: stream->upload_len = len; ! 1766: (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id); ! 1767: sent = len; ! 1768: } ! 1769: else { ! 1770: *curlcode = CURLE_AGAIN; ! 1771: return -1; ! 1772: } ! 1773: } ! 1774: ! 1775: if(ng_flush_egress(conn, sockfd, qs)) { ! 1776: *curlcode = CURLE_SEND_ERROR; ! 1777: return -1; ! 1778: } ! 1779: ! 1780: *curlcode = CURLE_OK; ! 1781: return sent; ! 1782: } ! 1783: ! 1784: static void ng_has_connected(struct connectdata *conn, int tempindex) ! 1785: { ! 1786: conn->recv[FIRSTSOCKET] = ngh3_stream_recv; ! 1787: conn->send[FIRSTSOCKET] = ngh3_stream_send; ! 1788: conn->handler = &Curl_handler_http3; ! 1789: conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ ! 1790: conn->httpversion = 30; ! 1791: conn->bundle->multiuse = BUNDLE_MULTIPLEX; ! 1792: conn->quic = &conn->hequic[tempindex]; ! 1793: DEBUGF(infof(conn->data, "ngtcp2 established connection!\n")); ! 1794: } ! 1795: ! 1796: /* ! 1797: * There can be multiple connection attempts going on in parallel. ! 1798: */ ! 1799: CURLcode Curl_quic_is_connected(struct connectdata *conn, ! 1800: int sockindex, ! 1801: bool *done) ! 1802: { ! 1803: CURLcode result; ! 1804: struct quicsocket *qs = &conn->hequic[sockindex]; ! 1805: curl_socket_t sockfd = conn->tempsock[sockindex]; ! 1806: ! 1807: result = ng_process_ingress(conn, sockfd, qs); ! 1808: if(result) ! 1809: return result; ! 1810: ! 1811: result = ng_flush_egress(conn, sockfd, qs); ! 1812: if(result) ! 1813: return result; ! 1814: ! 1815: if(ngtcp2_conn_get_handshake_completed(qs->qconn)) { ! 1816: *done = TRUE; ! 1817: ng_has_connected(conn, sockindex); ! 1818: } ! 1819: ! 1820: return result; ! 1821: } ! 1822: ! 1823: static CURLcode ng_process_ingress(struct connectdata *conn, int sockfd, ! 1824: struct quicsocket *qs) ! 1825: { ! 1826: ssize_t recvd; ! 1827: int rv; ! 1828: uint8_t buf[65536]; ! 1829: size_t bufsize = sizeof(buf); ! 1830: struct sockaddr_storage remote_addr; ! 1831: socklen_t remote_addrlen; ! 1832: ngtcp2_path path; ! 1833: ngtcp2_tstamp ts = timestamp(); ! 1834: ! 1835: for(;;) { ! 1836: remote_addrlen = sizeof(remote_addr); ! 1837: while((recvd = recvfrom(sockfd, buf, bufsize, 0, ! 1838: (struct sockaddr *)&remote_addr, ! 1839: &remote_addrlen)) == -1 && ! 1840: SOCKERRNO == EINTR) ! 1841: ; ! 1842: if(recvd == -1) { ! 1843: if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) ! 1844: break; ! 1845: ! 1846: failf(conn->data, "ngtcp2: recvfrom() unexpectedly returned %d", recvd); ! 1847: return CURLE_RECV_ERROR; ! 1848: } ! 1849: ! 1850: ngtcp2_addr_init(&path.local, (uint8_t *)&qs->local_addr, ! 1851: qs->local_addrlen, NULL); ! 1852: ngtcp2_addr_init(&path.remote, (uint8_t *)&remote_addr, remote_addrlen, ! 1853: NULL); ! 1854: ! 1855: rv = ngtcp2_conn_read_pkt(qs->qconn, &path, buf, recvd, ts); ! 1856: if(rv != 0) { ! 1857: /* TODO Send CONNECTION_CLOSE if possible */ ! 1858: return CURLE_RECV_ERROR; ! 1859: } ! 1860: } ! 1861: ! 1862: return CURLE_OK; ! 1863: } ! 1864: ! 1865: static CURLcode ng_flush_egress(struct connectdata *conn, int sockfd, ! 1866: struct quicsocket *qs) ! 1867: { ! 1868: int rv; ! 1869: ssize_t sent; ! 1870: ssize_t outlen; ! 1871: uint8_t out[NGTCP2_MAX_PKTLEN_IPV4]; ! 1872: size_t pktlen; ! 1873: ngtcp2_path_storage ps; ! 1874: ngtcp2_tstamp ts = timestamp(); ! 1875: struct sockaddr_storage remote_addr; ! 1876: ngtcp2_tstamp expiry; ! 1877: ngtcp2_duration timeout; ! 1878: int64_t stream_id; ! 1879: ssize_t veccnt; ! 1880: int fin; ! 1881: nghttp3_vec vec[16]; ! 1882: ssize_t ndatalen; ! 1883: ! 1884: switch(qs->local_addr.ss_family) { ! 1885: case AF_INET: ! 1886: pktlen = NGTCP2_MAX_PKTLEN_IPV4; ! 1887: break; ! 1888: #ifdef ENABLE_IPV6 ! 1889: case AF_INET6: ! 1890: pktlen = NGTCP2_MAX_PKTLEN_IPV6; ! 1891: break; ! 1892: #endif ! 1893: default: ! 1894: assert(0); ! 1895: } ! 1896: ! 1897: rv = ngtcp2_conn_handle_expiry(qs->qconn, ts); ! 1898: if(rv != 0) { ! 1899: failf(conn->data, "ngtcp2_conn_handle_expiry returned error: %s\n", ! 1900: ngtcp2_strerror(rv)); ! 1901: return CURLE_SEND_ERROR; ! 1902: } ! 1903: ! 1904: ngtcp2_path_storage_zero(&ps); ! 1905: ! 1906: for(;;) { ! 1907: outlen = -1; ! 1908: if(qs->h3conn && ngtcp2_conn_get_max_data_left(qs->qconn)) { ! 1909: veccnt = nghttp3_conn_writev_stream(qs->h3conn, &stream_id, &fin, vec, ! 1910: sizeof(vec) / sizeof(vec[0])); ! 1911: if(veccnt < 0) { ! 1912: failf(conn->data, "nghttp3_conn_writev_stream returned error: %s\n", ! 1913: nghttp3_strerror((int)veccnt)); ! 1914: return CURLE_SEND_ERROR; ! 1915: } ! 1916: else if(veccnt > 0) { ! 1917: outlen = ! 1918: ngtcp2_conn_writev_stream(qs->qconn, &ps.path, ! 1919: out, pktlen, &ndatalen, ! 1920: NGTCP2_WRITE_STREAM_FLAG_MORE, ! 1921: stream_id, fin, ! 1922: (const ngtcp2_vec *)vec, veccnt, ts); ! 1923: if(outlen == 0) { ! 1924: break; ! 1925: } ! 1926: if(outlen < 0) { ! 1927: if(outlen == NGTCP2_ERR_STREAM_DATA_BLOCKED || ! 1928: outlen == NGTCP2_ERR_STREAM_SHUT_WR) { ! 1929: rv = nghttp3_conn_block_stream(qs->h3conn, stream_id); ! 1930: if(rv != 0) { ! 1931: failf(conn->data, ! 1932: "nghttp3_conn_block_stream returned error: %s\n", ! 1933: nghttp3_strerror(rv)); ! 1934: return CURLE_SEND_ERROR; ! 1935: } ! 1936: continue; ! 1937: } ! 1938: else if(outlen == NGTCP2_ERR_WRITE_STREAM_MORE) { ! 1939: assert(ndatalen > 0); ! 1940: rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ! 1941: ndatalen); ! 1942: if(rv != 0) { ! 1943: failf(conn->data, ! 1944: "nghttp3_conn_add_write_offset returned error: %s\n", ! 1945: nghttp3_strerror(rv)); ! 1946: return CURLE_SEND_ERROR; ! 1947: } ! 1948: continue; ! 1949: } ! 1950: else { ! 1951: failf(conn->data, "ngtcp2_conn_writev_stream returned error: %s\n", ! 1952: ngtcp2_strerror((int)outlen)); ! 1953: return CURLE_SEND_ERROR; ! 1954: } ! 1955: } ! 1956: else if(ndatalen >= 0) { ! 1957: rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen); ! 1958: if(rv != 0) { ! 1959: failf(conn->data, ! 1960: "nghttp3_conn_add_write_offset returned error: %s\n", ! 1961: nghttp3_strerror(rv)); ! 1962: return CURLE_SEND_ERROR; ! 1963: } ! 1964: } ! 1965: } ! 1966: } ! 1967: if(outlen < 0) { ! 1968: outlen = ngtcp2_conn_write_pkt(qs->qconn, &ps.path, out, pktlen, ts); ! 1969: if(outlen < 0) { ! 1970: failf(conn->data, "ngtcp2_conn_write_pkt returned error: %s\n", ! 1971: ngtcp2_strerror((int)outlen)); ! 1972: return CURLE_SEND_ERROR; ! 1973: } ! 1974: if(outlen == 0) ! 1975: break; ! 1976: } ! 1977: ! 1978: memcpy(&remote_addr, ps.path.remote.addr, ps.path.remote.addrlen); ! 1979: while((sent = send(sockfd, out, outlen, 0)) == -1 && ! 1980: SOCKERRNO == EINTR) ! 1981: ; ! 1982: ! 1983: if(sent == -1) { ! 1984: if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { ! 1985: /* TODO Cache packet */ ! 1986: break; ! 1987: } ! 1988: else { ! 1989: failf(conn->data, "send() returned %zd (errno %d)\n", sent, ! 1990: SOCKERRNO); ! 1991: return CURLE_SEND_ERROR; ! 1992: } ! 1993: } ! 1994: } ! 1995: ! 1996: expiry = ngtcp2_conn_get_expiry(qs->qconn); ! 1997: if(expiry != UINT64_MAX) { ! 1998: if(expiry <= ts) { ! 1999: timeout = NGTCP2_MILLISECONDS; ! 2000: } ! 2001: else { ! 2002: timeout = expiry - ts; ! 2003: } ! 2004: Curl_expire(conn->data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC); ! 2005: } ! 2006: ! 2007: return CURLE_OK; ! 2008: } ! 2009: ! 2010: /* ! 2011: * Called from transfer.c:done_sending when we stop HTTP/3 uploading. ! 2012: */ ! 2013: CURLcode Curl_quic_done_sending(struct connectdata *conn) ! 2014: { ! 2015: if(conn->handler == &Curl_handler_http3) { ! 2016: /* only for HTTP/3 transfers */ ! 2017: struct HTTP *stream = conn->data->req.protop; ! 2018: struct quicsocket *qs = conn->quic; ! 2019: stream->upload_done = TRUE; ! 2020: (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id); ! 2021: } ! 2022: ! 2023: return CURLE_OK; ! 2024: } ! 2025: ! 2026: /* ! 2027: * Called from http.c:Curl_http_done when a request completes. ! 2028: */ ! 2029: void Curl_quic_done(struct Curl_easy *data, bool premature) ! 2030: { ! 2031: (void)premature; ! 2032: if(data->conn->handler == &Curl_handler_http3) { ! 2033: /* only for HTTP/3 transfers */ ! 2034: struct HTTP *stream = data->req.protop; ! 2035: Curl_safefree(stream->overflow_buf); ! 2036: } ! 2037: } ! 2038: ! 2039: /* ! 2040: * Called from transfer.c:data_pending to know if we should keep looping ! 2041: * to receive more data from the connection. ! 2042: */ ! 2043: bool Curl_quic_data_pending(const struct Curl_easy *data) ! 2044: { ! 2045: /* We may have received more data than we're able to hold in the receive ! 2046: buffer and allocated an overflow buffer. Since it's possible that ! 2047: there's no more data coming on the socket, we need to keep reading ! 2048: until the overflow buffer is empty. */ ! 2049: const struct HTTP *stream = data->req.protop; ! 2050: return stream->overflow_buflen > 0; ! 2051: } ! 2052: ! 2053: #endif