Annotation of embedaddon/curl/lib/vquic/ngtcp2.c, revision 1.1
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
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>