From 04f8f447b2c172be805db37a076bb561aec42ca6 Mon Sep 17 00:00:00 2001 From: Dmitri Tikhonov Date: Tue, 13 Oct 2020 08:20:25 -0400 Subject: [PATCH] Release 2.23.0 - [FEATURE] IETF Client 0-RTT support. - [BUGFIX] Do not schedule MTU probe on first tick. - [BUGFIX] Parsing DATAGRAM frame. - [BUGFIX] If push promise fails, do not invoke hset destructor. - [BUGFIX] Client: When connections are IDed by port number, check DCID. Fixes issue #176. - Revert the 2.22.1 lsquic_is_valid_hs_packet change. All that was necessary is a change to the way we call it in lsquic_engine. No change to the function itself is required. --- CHANGELOG | 12 + bin/http_server.c | 10 +- bin/test_cert.c | 6 +- bin/test_common.c | 5 + docs/apiref.rst | 4 +- docs/conf.py | 4 +- docs/faq.rst | 2 +- include/lsquic.h | 7 +- src/liblsquic/lsquic_conn.h | 4 + src/liblsquic/lsquic_enc_sess.h | 14 +- src/liblsquic/lsquic_enc_sess_ietf.c | 553 +++++++++++++++++--------- src/liblsquic/lsquic_engine.c | 54 +++ src/liblsquic/lsquic_engine_public.h | 5 + src/liblsquic/lsquic_frame_writer.c | 4 + src/liblsquic/lsquic_full_conn.c | 13 +- src/liblsquic/lsquic_full_conn_ietf.c | 233 +++++++---- src/liblsquic/lsquic_hspack_valid.c | 6 +- src/liblsquic/lsquic_mini_conn_ietf.c | 11 + src/liblsquic/lsquic_parse_ietf_v1.c | 4 +- src/liblsquic/lsquic_send_ctl.c | 75 ++++ src/liblsquic/lsquic_send_ctl.h | 7 + src/liblsquic/lsquic_stream.c | 51 ++- src/liblsquic/lsquic_stream.h | 11 + src/liblsquic/lsquic_trans_params.h | 22 + 24 files changed, 808 insertions(+), 309 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index af021da..60f558a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,15 @@ +2020-10-13 + - 2.23.0 + - [FEATURE] IETF Client 0-RTT support. + - [BUGFIX] Do not schedule MTU probe on first tick. + - [BUGFIX] Parsing DATAGRAM frame. + - [BUGFIX] If push promise fails, do not invoke hset destructor. + - [BUGFIX] Client: When connections are IDed by port number, check DCID. + Fixes issue #176. + - Revert the 2.22.1 lsquic_is_valid_hs_packet change. All that was + necessary is a change to the way we call it in lsquic_engine. No + change to the function itself is required. + 2020-10-08 - 2.22.1 - [BUGFIX] Function that checks validity of handshake packets. diff --git a/bin/http_server.c b/bin/http_server.c index bc8a94c..76715ba 100644 --- a/bin/http_server.c +++ b/bin/http_server.c @@ -1506,8 +1506,14 @@ idle_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h) headers.count = sizeof(header_arr) / sizeof(header_arr[0]); req = new_req(GET, push_path->path, st_h->req->authority_str); if (req) - (void) lsquic_conn_push_stream(lsquic_stream_conn(stream), - req, stream, &headers); + { + if (0 != lsquic_conn_push_stream(lsquic_stream_conn(stream), + req, stream, &headers)) + { + LSQ_WARN("stream push failed"); + interop_server_hset_destroy(req); + } + } else LSQ_WARN("cannot allocate req for push"); free(push_path); diff --git a/bin/test_cert.c b/bin/test_cert.c index 8a05070..9c9f1d2 100644 --- a/bin/test_cert.c +++ b/bin/test_cert.c @@ -93,7 +93,11 @@ load_cert (struct lsquic_hash *certs, const char *optarg) SSL_CTX_set_max_proto_version(cert->ce_ssl_ctx, TLS1_3_VERSION); SSL_CTX_set_default_verify_paths(cert->ce_ssl_ctx); SSL_CTX_set_alpn_select_cb(cert->ce_ssl_ctx, select_alpn, NULL); - SSL_CTX_set_early_data_enabled(cert->ce_ssl_ctx, 1); /* XXX */ + { + const char *const s = getenv("LSQUIC_ENABLE_EARLY_DATA"); + if (!s || atoi(s)) + SSL_CTX_set_early_data_enabled(cert->ce_ssl_ctx, 1); /* XXX */ + } if (1 != SSL_CTX_use_certificate_chain_file(cert->ce_ssl_ctx, cert_file)) { LSQ_ERROR("SSL_CTX_use_certificate_chain_file failed: %s", cert_file); diff --git a/bin/test_common.c b/bin/test_common.c index 85ff348..f4cf146 100644 --- a/bin/test_common.c +++ b/bin/test_common.c @@ -1993,6 +1993,11 @@ set_engine_option (struct lsquic_engine_settings *settings, settings->es_qpack_dec_max_blocked = atoi(val); return 0; } + if (0 == strncmp(name, "init_max_streams_bidi", 21)) + { + settings->es_init_max_streams_bidi = atoi(val); + return 0; + } break; case 23: if (0 == strncmp(name, "max_udp_payload_size_rx", 23)) diff --git a/docs/apiref.rst b/docs/apiref.rst index 852d45e..220ca65 100644 --- a/docs/apiref.rst +++ b/docs/apiref.rst @@ -58,9 +58,9 @@ developed by the IETF. Both types are included in a single enum: IETF QUIC version ID 29 - .. member:: LSQVER_ID30; this version is deprecated. + .. member:: LSQVER_ID30 - IETF QUIC version ID 30 + IETF QUIC version ID 30; this version is deprecated. .. member:: LSQVER_ID31 diff --git a/docs/conf.py b/docs/conf.py index e32d696..d28c75c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,9 +24,9 @@ copyright = u'2020, LiteSpeed Technologies' author = u'LiteSpeed Technologies' # The short X.Y version -version = u'2.22' +version = u'2.23' # The full version, including alpha/beta/rc tags -release = u'2.22.1' +release = u'2.23.0' # -- General configuration --------------------------------------------------- diff --git a/docs/faq.rst b/docs/faq.rst index 2da3ffc..88a5d67 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -18,7 +18,7 @@ connections. To aid development, there is a :macro:`LSQUIC_FORCED_TCID0_VERSIONS` that specifies the list of versions with 0-sized connections. (If you, for -example, want to turn them.) +example, want to turn them off.) Once gQUIC becomes deprecated in the future, there will remain no technical reason why a single engine instance could not be used both for client and diff --git a/include/lsquic.h b/include/lsquic.h index 6742f49..287b44a 100644 --- a/include/lsquic.h +++ b/include/lsquic.h @@ -24,8 +24,8 @@ extern "C" { #endif #define LSQUIC_MAJOR_VERSION 2 -#define LSQUIC_MINOR_VERSION 22 -#define LSQUIC_PATCH_VERSION 1 +#define LSQUIC_MINOR_VERSION 23 +#define LSQUIC_PATCH_VERSION 0 /** * Engine flags: @@ -1923,8 +1923,7 @@ lsquic_get_h3_alpns (unsigned versions); * been established: it will return incorrect result. */ int -lsquic_is_valid_hs_packet (lsquic_engine_t *, const unsigned char *, - size_t bufsz, size_t packet_in_sz); +lsquic_is_valid_hs_packet (lsquic_engine_t *, const unsigned char *, size_t); /** * Parse cid from packet stored in `buf' and store it to `cid'. Returns 0 diff --git a/src/liblsquic/lsquic_conn.h b/src/liblsquic/lsquic_conn.h index 2120e81..c742bf5 100644 --- a/src/liblsquic/lsquic_conn.h +++ b/src/liblsquic/lsquic_conn.h @@ -282,6 +282,10 @@ struct conn_iface /* Optional method */ size_t (*ci_get_min_datagram_size) (struct lsquic_conn *); + + /* Optional method */ + void + (*ci_early_data_failed) (struct lsquic_conn *); }; #define LSCONN_CCE_BITS 3 diff --git a/src/liblsquic/lsquic_enc_sess.h b/src/liblsquic/lsquic_enc_sess.h index ce8fc33..08de564 100644 --- a/src/liblsquic/lsquic_enc_sess.h +++ b/src/liblsquic/lsquic_enc_sess.h @@ -15,6 +15,8 @@ struct ssl_stream_method_st; struct ssl_st; struct sockaddr; struct conn_cid_elem; +struct lsquic_engine_settings; +enum lsquic_version; #define DNONC_LENGTH 32 #define SRST_LENGTH 16 @@ -242,12 +244,6 @@ struct enc_session_funcs_gquic void (*esf_reset_cid) (enc_session_t *, const lsquic_cid_t *); }; -enum iquic_handshake_status { - IHS_WANT_READ, - IHS_WANT_WRITE, - IHS_STOP, -}; - struct crypto_stream_if { ssize_t (*csi_write) (void *stream, const void *buf, size_t len); @@ -371,4 +367,10 @@ lsquic_sess_resume_version (const unsigned char *, size_t); */ #define IQUIC_TAG_LEN 16 +/* Return number of bytes written to `buf' or -1 on error */ +int +lsquic_enc_sess_ietf_gen_quic_ctx ( + const struct lsquic_engine_settings *settings, + enum lsquic_version version, unsigned char *buf, size_t bufsz); + #endif diff --git a/src/liblsquic/lsquic_enc_sess_ietf.c b/src/liblsquic/lsquic_enc_sess_ietf.c index 5c8cb82..1e1acb1 100644 --- a/src/liblsquic/lsquic_enc_sess_ietf.c +++ b/src/liblsquic/lsquic_enc_sess_ietf.c @@ -107,6 +107,20 @@ static void no_sess_ticket (enum alarm_id alarm_id, void *ctx, lsquic_time_t expiry, lsquic_time_t now); +static int +iquic_new_session_cb (SSL *, SSL_SESSION *); + +static void +keylog_callback (const SSL *, const char *); + +static enum ssl_verify_result_t +verify_server_cert_callback (SSL *, uint8_t *out_alert); + +static void +maybe_setup_key_logging (struct enc_sess_iquic *); + +static void +iquic_esfi_destroy (enc_session_t *); #define SAMPLE_SZ 16 @@ -223,7 +237,7 @@ struct enc_sess_iquic lsquic_cid_t esi_iscid; /* Initial SCID */ unsigned esi_key_phase; enum { - ESI_INITIALIZED = 1 << 0, + ESI_UNUSED0 = 1 << 0, ESI_LOG_SECRETS = 1 << 1, ESI_HANDSHAKE_OK = 1 << 2, ESI_ODCID = 1 << 3, @@ -243,17 +257,15 @@ struct enc_sess_iquic ESI_MAX_PACKNO_INIT = 1 << 17, ESI_MAX_PACKNO_HSK = ESI_MAX_PACKNO_INIT << PNS_HSK, ESI_MAX_PACKNO_APP = ESI_MAX_PACKNO_INIT << PNS_APP, + ESI_HAVE_0RTT_TP = 1 << 20, } esi_flags; enum enc_level esi_last_w; unsigned esi_trasec_sz; - char *esi_hostname; void *esi_keylog_handle; #ifndef NDEBUG char *esi_sni_bypass; #endif const unsigned char *esi_alpn; - unsigned char *esi_sess_resume_buf; - size_t esi_sess_resume_sz; /* Need MD and AEAD for key rotation */ const EVP_MD *esi_md; const EVP_AEAD *esi_aead; @@ -279,6 +291,7 @@ struct enc_sess_iquic esi_hp_batch_packets[HP_BATCH_SIZE]; unsigned char esi_hp_batch_samples[HP_BATCH_SIZE][SAMPLE_SZ]; unsigned char esi_grease; + signed char esi_have_forw; }; @@ -696,24 +709,23 @@ gen_trans_params (struct enc_sess_iquic *enc_sess, unsigned char *buf, static SSL_SESSION * maybe_create_SSL_SESSION (struct enc_sess_iquic *enc_sess, - const SSL_CTX *ssl_ctx) + const SSL_CTX *ssl_ctx, const unsigned char *sess_resume, + size_t sess_resume_sz) { SSL_SESSION *ssl_session; lsquic_ver_tag_t ver_tag; enum lsquic_version quic_ver; uint32_t rtt_ver, ticket_sz, trapa_sz; const unsigned char *ticket_buf, *trapa_buf, *p; - const unsigned char *const end - = enc_sess->esi_sess_resume_buf + enc_sess->esi_sess_resume_sz; + const unsigned char *const end = sess_resume + sess_resume_sz; - if (enc_sess->esi_sess_resume_sz - < sizeof(ver_tag) + sizeof(rtt_ver) + sizeof(ticket_sz)) + if (sess_resume_sz < sizeof(ver_tag) + sizeof(rtt_ver) + sizeof(ticket_sz)) { LSQ_DEBUG("rtt buf too short"); return NULL; } - p = enc_sess->esi_sess_resume_buf; + p = sess_resume; memcpy(&ver_tag, p, sizeof(ver_tag)); p += sizeof(ver_tag); quic_ver = lsquic_tag2ver(ver_tag); @@ -760,8 +772,6 @@ maybe_create_SSL_SESSION (struct enc_sess_iquic *enc_sess, p += trapa_sz; assert(p == end); - (void) /* TODO */ trapa_buf; - ssl_session = SSL_SESSION_from_bytes(ticket_buf, ticket_sz, ssl_ctx); if (!ssl_session) { @@ -769,6 +779,22 @@ maybe_create_SSL_SESSION (struct enc_sess_iquic *enc_sess, return NULL; } + if (SSL_SESSION_early_data_capable(ssl_session)) + { + if (0 > (quic_ver == LSQVER_ID27 ? lsquic_tp_decode_27 + : lsquic_tp_decode)(trapa_buf, trapa_sz, 1, + &enc_sess->esi_peer_tp)) + { + SSL_SESSION_free(ssl_session); + LSQ_WARN("cannot parse stored transport parameters"); + return NULL; + } + LSQ_DEBUG("early data capable, will try 0-RTT"); + enc_sess->esi_flags |= ESI_HAVE_0RTT_TP; + } + else + LSQ_DEBUG("early data not capable -- not trying 0-RTT"); + LSQ_INFO("instantiated SSL_SESSION from serialized buffer"); return ssl_session; } @@ -795,6 +821,16 @@ iquic_esfi_create_client (const char *hostname, struct lsquic_alarmset *alset, unsigned max_streams_uni) { struct enc_sess_iquic *enc_sess; + SSL_CTX *ssl_ctx; + SSL_SESSION *ssl_session; + const struct alpn_map *am; + int transpa_len; + char errbuf[ERR_ERROR_STRING_BUF_LEN]; + unsigned char trans_params[0x80 +#if LSQUIC_TEST_QUANTUM_READINESS + + 4 + lsquic_tp_get_quantum_sz() +#endif + ]; fiu_return_on("enc_sess_ietf/create_client", NULL); @@ -802,18 +838,6 @@ iquic_esfi_create_client (const char *hostname, if (!enc_sess) return NULL; - if (hostname) - { - enc_sess->esi_hostname = strdup(hostname); - if (!enc_sess->esi_hostname) - { - free(enc_sess); - return NULL; - } - } - else - enc_sess->esi_hostname = NULL; - enc_sess->esi_enpub = enpub; enc_sess->esi_streams = crypto_streams; enc_sess->esi_cryst_if = cryst_if; @@ -844,35 +868,123 @@ iquic_esfi_create_client (const char *hostname, return NULL; } - /* Have to wait until the call to init_client() -- this is when the - * result of version negotiation is known. - */ + enc_sess->esi_max_streams_uni = max_streams_uni; + + if (enc_sess->esi_enpub->enp_alpn) + enc_sess->esi_alpn = enc_sess->esi_enpub->enp_alpn; + else if (enc_sess->esi_enpub->enp_flags & ENPUB_HTTP) + { + for (am = s_h3_alpns; am < s_h3_alpns + sizeof(s_h3_alpns) + / sizeof(s_h3_alpns[0]); ++am) + if (am->version == enc_sess->esi_ver_neg->vn_ver) + goto alpn_selected; + LSQ_ERROR("version %s has no matching ALPN", + lsquic_ver2str[enc_sess->esi_ver_neg->vn_ver]); + goto err; + alpn_selected: + enc_sess->esi_alpn = am->alpn; + } + + LSQ_DEBUG("Create new SSL_CTX"); + ssl_ctx = SSL_CTX_new(TLS_method()); + if (!ssl_ctx) + { + LSQ_ERROR("cannot create SSL context: %s", + ERR_error_string(ERR_get_error(), errbuf)); + goto err; + } + SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); + SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION); + SSL_CTX_set_default_verify_paths(ssl_ctx); + SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_CLIENT); + if (enc_sess->esi_enpub->enp_stream_if->on_sess_resume_info) + SSL_CTX_sess_set_new_cb(ssl_ctx, iquic_new_session_cb); + if (enc_sess->esi_enpub->enp_kli) + SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); + if (enc_sess->esi_enpub->enp_verify_cert + || LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT) + || LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_QLOG)) + SSL_CTX_set_custom_verify(ssl_ctx, SSL_VERIFY_PEER, + verify_server_cert_callback); + SSL_CTX_set_early_data_enabled(ssl_ctx, 1); + + enc_sess->esi_ssl = SSL_new(ssl_ctx); + if (!enc_sess->esi_ssl) + { + LSQ_ERROR("cannot create SSL object: %s", + ERR_error_string(ERR_get_error(), errbuf)); + goto err; + } + + transpa_len = gen_trans_params(enc_sess, trans_params, + sizeof(trans_params)); + if (transpa_len < 0) + { + goto err; + } + if (1 != SSL_set_quic_transport_params(enc_sess->esi_ssl, trans_params, + transpa_len)) + { + LSQ_ERROR("cannot set QUIC transport params: %s", + ERR_error_string(ERR_get_error(), errbuf)); + goto err; + } + + if (!(SSL_set_quic_method(enc_sess->esi_ssl, &cry_quic_method))) + { + LSQ_INFO("could not set stream method"); + goto err; + } + + maybe_setup_key_logging(enc_sess); + + if (enc_sess->esi_alpn && + 0 != SSL_set_alpn_protos(enc_sess->esi_ssl, enc_sess->esi_alpn, + enc_sess->esi_alpn[0] + 1)) + { + LSQ_ERROR("cannot set ALPN: %s", + ERR_error_string(ERR_get_error(), errbuf)); + goto err; + } + if (1 != SSL_set_tlsext_host_name(enc_sess->esi_ssl, hostname)) + { + LSQ_ERROR("cannot set hostname: %s", + ERR_error_string(ERR_get_error(), errbuf)); + goto err; + } + if (sess_resume && sess_resume_sz) { - enc_sess->esi_sess_resume_buf = malloc(sess_resume_sz); - if (enc_sess->esi_sess_resume_buf) + ssl_session = maybe_create_SSL_SESSION(enc_sess, ssl_ctx, + sess_resume, sess_resume_sz); + if (ssl_session) { - memcpy(enc_sess->esi_sess_resume_buf, sess_resume, sess_resume_sz); - enc_sess->esi_sess_resume_sz = sess_resume_sz; + (void) /* This only ever returns 1: */ + SSL_set_session(enc_sess->esi_ssl, ssl_session); + SSL_SESSION_free(ssl_session); + ssl_session = NULL; + enc_sess->esi_flags |= ESI_USE_SSL_TICKET; } - else - enc_sess->esi_sess_resume_sz = 0; - } - else - { - enc_sess->esi_sess_resume_buf = NULL; - enc_sess->esi_sess_resume_sz = 0; } + SSL_set_ex_data(enc_sess->esi_ssl, s_idx, enc_sess); + SSL_set_connect_state(enc_sess->esi_ssl); + if (enc_sess->esi_enpub->enp_stream_if->on_sess_resume_info) enc_sess->esi_flags |= ESI_WANT_TICKET; enc_sess->esi_alset = alset; lsquic_alarmset_init_alarm(enc_sess->esi_alset, AL_SESS_TICKET, no_sess_ticket, enc_sess); - enc_sess->esi_max_streams_uni = max_streams_uni; - + SSL_CTX_free(ssl_ctx); return enc_sess; + + err: + if (enc_sess) + iquic_esfi_destroy(enc_sess); + if (ssl_ctx) + SSL_CTX_free(ssl_ctx); + return NULL; } @@ -1242,6 +1354,7 @@ iquic_esfi_init_server (enc_session_t *enc_session_p) { struct enc_sess_iquic *const enc_sess = enc_session_p; const struct alpn_map *am; + unsigned quic_ctx_idx; int transpa_len; SSL_CTX *ssl_ctx = NULL; union { @@ -1287,9 +1400,10 @@ iquic_esfi_init_server (enc_session_t *enc_session_p) LSQ_INFO("could not set stream method"); return -1; } - /* TODO: set to transport parameter string instead of the constant string */ + quic_ctx_idx = enc_sess->esi_conn->cn_version == LSQVER_ID27 ? 0 : 1; if (!SSL_set_quic_early_data_context(enc_sess->esi_ssl, - (unsigned char *) "lsquic", 6)) + enc_sess->esi_enpub->enp_quic_ctx_buf[quic_ctx_idx], + enc_sess->esi_enpub->enp_quic_ctx_sz[quic_ctx_idx])) { LSQ_INFO("could not set early data context"); return -1; @@ -1317,7 +1431,6 @@ iquic_esfi_init_server (enc_session_t *enc_session_p) SSL_set_ex_data(enc_sess->esi_ssl, s_idx, enc_sess); SSL_set_accept_state(enc_sess->esi_ssl); LSQ_DEBUG("initialized server enc session"); - enc_sess->esi_flags |= ESI_INITIALIZED; return 0; } @@ -1415,129 +1528,6 @@ iquic_new_session_cb (SSL *ssl, SSL_SESSION *session) } -static int -init_client (struct enc_sess_iquic *const enc_sess) -{ - SSL_CTX *ssl_ctx; - SSL_SESSION *ssl_session; - const struct alpn_map *am; - int transpa_len; - char errbuf[ERR_ERROR_STRING_BUF_LEN]; -#define hexbuf errbuf /* This is a dual-purpose buffer */ - unsigned char trans_params[0x80 -#if LSQUIC_TEST_QUANTUM_READINESS - + 4 + lsquic_tp_get_quantum_sz() -#endif - ]; - - if (enc_sess->esi_enpub->enp_alpn) - enc_sess->esi_alpn = enc_sess->esi_enpub->enp_alpn; - else if (enc_sess->esi_enpub->enp_flags & ENPUB_HTTP) - { - for (am = s_h3_alpns; am < s_h3_alpns + sizeof(s_h3_alpns) - / sizeof(s_h3_alpns[0]); ++am) - if (am->version == enc_sess->esi_ver_neg->vn_ver) - goto ok; - LSQ_ERROR("version %s has no matching ALPN", - lsquic_ver2str[enc_sess->esi_ver_neg->vn_ver]); - return -1; - ok: enc_sess->esi_alpn = am->alpn; - } - - LSQ_DEBUG("Create new SSL_CTX"); - ssl_ctx = SSL_CTX_new(TLS_method()); - if (!ssl_ctx) - { - LSQ_ERROR("cannot create SSL context: %s", - ERR_error_string(ERR_get_error(), errbuf)); - goto err; - } - SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); - SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION); - SSL_CTX_set_default_verify_paths(ssl_ctx); - SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_CLIENT); - if (enc_sess->esi_enpub->enp_stream_if->on_sess_resume_info) - SSL_CTX_sess_set_new_cb(ssl_ctx, iquic_new_session_cb); - if (enc_sess->esi_enpub->enp_kli) - SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); - if (enc_sess->esi_enpub->enp_verify_cert - || LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT) - || LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_QLOG)) - SSL_CTX_set_custom_verify(ssl_ctx, SSL_VERIFY_PEER, - verify_server_cert_callback); - SSL_CTX_set_early_data_enabled(ssl_ctx, 1); - - transpa_len = gen_trans_params(enc_sess, trans_params, - sizeof(trans_params)); - if (transpa_len < 0) - { - goto err; - } - - enc_sess->esi_ssl = SSL_new(ssl_ctx); - if (!enc_sess->esi_ssl) - { - LSQ_ERROR("cannot create SSL object: %s", - ERR_error_string(ERR_get_error(), errbuf)); - goto err; - } - if (!(SSL_set_quic_method(enc_sess->esi_ssl, &cry_quic_method))) - { - LSQ_INFO("could not set stream method"); - goto err; - } - maybe_setup_key_logging(enc_sess); - if (1 != SSL_set_quic_transport_params(enc_sess->esi_ssl, trans_params, - transpa_len)) - { - LSQ_ERROR("cannot set QUIC transport params: %s", - ERR_error_string(ERR_get_error(), errbuf)); - goto err; - } - if (enc_sess->esi_alpn && - 0 != SSL_set_alpn_protos(enc_sess->esi_ssl, enc_sess->esi_alpn, - enc_sess->esi_alpn[0] + 1)) - { - LSQ_ERROR("cannot set ALPN: %s", - ERR_error_string(ERR_get_error(), errbuf)); - goto err; - } - if (1 != SSL_set_tlsext_host_name(enc_sess->esi_ssl, - enc_sess->esi_hostname)) - { - LSQ_ERROR("cannot set hostname: %s", - ERR_error_string(ERR_get_error(), errbuf)); - goto err; - } - free(enc_sess->esi_hostname); - enc_sess->esi_hostname = NULL; - if (enc_sess->esi_sess_resume_buf) - { - ssl_session = maybe_create_SSL_SESSION(enc_sess, ssl_ctx); - if (ssl_session) - { - if (SSL_set_session(enc_sess->esi_ssl, ssl_session)) - enc_sess->esi_flags |= ESI_USE_SSL_TICKET; - else - LSQ_WARN("cannot set session"); - } - } - - SSL_set_ex_data(enc_sess->esi_ssl, s_idx, enc_sess); - SSL_set_connect_state(enc_sess->esi_ssl); - SSL_CTX_free(ssl_ctx); - LSQ_DEBUG("initialized client enc session"); - enc_sess->esi_flags |= ESI_INITIALIZED; - return 0; - - err: - if (ssl_ctx) - SSL_CTX_free(ssl_ctx); - return -1; -#undef hexbuf -} - - struct crypto_params { const EVP_AEAD *aead; @@ -1607,14 +1597,72 @@ get_crypto_params (const struct enc_sess_iquic *enc_sess, } +/* [draft-ietf-quic-transport-31] Section 7.4.1: + " If 0-RTT data is accepted by the server, the server MUST NOT reduce + " any limits or alter any values that might be violated by the client + " with its 0-RTT data. In particular, a server that accepts 0-RTT data + " MUST NOT set values for the following parameters (Section 18.2) that + " are smaller than the remembered value of the parameters. + " + " * active_connection_id_limit + " + " * initial_max_data + " + " * initial_max_stream_data_bidi_local + " + " * initial_max_stream_data_bidi_remote + " + " * initial_max_stream_data_uni + " + " * initial_max_streams_bidi + " + " * initial_max_streams_uni + */ +#define REDUCTION_PROHIBITED_TPS (0 \ + | (1 << TPI_ACTIVE_CONNECTION_ID_LIMIT) \ + | (1 << TPI_INIT_MAX_DATA) \ + | (1 << TPI_INIT_MAX_STREAMS_UNI) \ + | (1 << TPI_INIT_MAX_STREAMS_BIDI) \ + | (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL) \ + | (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE) \ + | (1 << TPI_INIT_MAX_STREAM_DATA_UNI) \ +) + + +static int +check_server_tps_for_violations (const struct enc_sess_iquic *enc_sess, + const struct transport_params *params_0rtt, + const struct transport_params *new_params) +{ + enum transport_param_id tpi; + + for (tpi = 0; tpi <= MAX_NUMERIC_TPI; ++tpi) + if ((1 << tpi) & REDUCTION_PROHIBITED_TPS) + if (new_params->tp_numerics[tpi] > params_0rtt->tp_numerics[tpi]) + { + LSQ_INFO("server's new TP %s increased in value from %"PRIu64 + " to %"PRIu64, lsquic_tpi2str[tpi], + params_0rtt->tp_numerics[tpi], + new_params->tp_numerics[tpi]); + return -1; + } + + LSQ_DEBUG("server's new transport parameters do not violate save 0-RTT " + "parameters"); + return 0; +} + + static int get_peer_transport_params (struct enc_sess_iquic *enc_sess) { struct transport_params *const trans_params = &enc_sess->esi_peer_tp; + struct transport_params params_0rtt; const uint8_t *params_buf; size_t bufsz; char *params_str; const enum lsquic_version version = enc_sess->esi_conn->cn_version; + int have_0rtt_tp; SSL_get_peer_quic_transport_params(enc_sess->esi_ssl, ¶ms_buf, &bufsz); if (!params_buf) @@ -1623,6 +1671,13 @@ get_peer_transport_params (struct enc_sess_iquic *enc_sess) return -1; } + have_0rtt_tp = !!(enc_sess->esi_flags & ESI_HAVE_0RTT_TP); + if (have_0rtt_tp) + { + params_0rtt = enc_sess->esi_peer_tp; + enc_sess->esi_flags &= ~ESI_HAVE_0RTT_TP; + } + LSQ_DEBUG("have peer transport parameters (%zu bytes)", bufsz); if (0 > (version == LSQVER_ID27 ? lsquic_tp_decode_27 : lsquic_tp_decode)(params_buf, bufsz, @@ -1646,6 +1701,10 @@ get_peer_transport_params (struct enc_sess_iquic *enc_sess) return -1; } + if (have_0rtt_tp && 0 != check_server_tps_for_violations(enc_sess, + ¶ms_0rtt, trans_params)) + return -1; + const lsquic_cid_t *const cids[LAST_TPI + 1] = { [TP_CID_IDX(TPI_ORIGINAL_DEST_CID)] = enc_sess->esi_flags & ESI_ODCID ? &enc_sess->esi_odcid : NULL, [TP_CID_IDX(TPI_RETRY_SOURCE_CID)] = enc_sess->esi_flags & ESI_RSCID ? &enc_sess->esi_rscid : NULL, @@ -1771,6 +1830,14 @@ maybe_get_peer_transport_params (struct enc_sess_iquic *enc_sess) } +enum iquic_handshake_status { + IHS_WANT_READ, + IHS_WANT_WRITE, + IHS_WANT_RW, + IHS_STOP, +}; + + static enum iquic_handshake_status iquic_esfi_handshake (struct enc_sess_iquic *enc_sess) { @@ -1791,9 +1858,12 @@ iquic_esfi_handshake (struct enc_sess_iquic *enc_sess) LSQ_DEBUG("retry write"); return IHS_WANT_WRITE; case SSL_ERROR_EARLY_DATA_REJECTED: - LSQ_DEBUG("early data rejected"); - hsk_status = LSQ_HSK_RESUMED_FAIL; - goto err; + LSQ_DEBUG("early data rejected: reset"); + SSL_reset_early_data_reject(enc_sess->esi_ssl); + if (enc_sess->esi_conn->cn_if->ci_early_data_failed) + enc_sess->esi_conn->cn_if->ci_early_data_failed( + enc_sess->esi_conn); + return IHS_WANT_RW; /* fall through */ default: LSQ_DEBUG("handshake: %s", ERR_error_string(err, errbuf)); @@ -1866,7 +1936,9 @@ iquic_esfi_get_peer_transport_params (enc_session_t *enc_session_p) { struct enc_sess_iquic *const enc_sess = enc_session_p; - if (0 == maybe_get_peer_transport_params(enc_sess)) + if (enc_sess->esi_flags & ESI_HAVE_0RTT_TP) + return &enc_sess->esi_peer_tp; + else if (0 == maybe_get_peer_transport_params(enc_sess)) return &enc_sess->esi_peer_tp; else return NULL; @@ -1892,8 +1964,6 @@ iquic_esfi_destroy (enc_session_t *enc_session_p) free_handshake_keys(enc_sess); cleanup_hp(&enc_sess->esi_hp); - free(enc_sess->esi_sess_resume_buf); - free(enc_sess->esi_hostname); free(enc_sess); } @@ -1910,11 +1980,18 @@ static const enum enc_level hety2el[] = }; -static const enum enc_level pns2enc_level[] = +static const enum enc_level pns2enc_level[2][N_PNS] = { - [PNS_INIT] = ENC_LEV_CLEAR, - [PNS_HSK] = ENC_LEV_INIT, - [PNS_APP] = ENC_LEV_FORW, + [0] = { + [PNS_INIT] = ENC_LEV_CLEAR, + [PNS_HSK] = ENC_LEV_INIT, + [PNS_APP] = ENC_LEV_EARLY, + }, + [1] = { + [PNS_INIT] = ENC_LEV_CLEAR, + [PNS_HSK] = ENC_LEV_INIT, + [PNS_APP] = ENC_LEV_FORW, + }, }; @@ -1941,8 +2018,7 @@ iquic_esf_encrypt_packet (enc_session_t *enc_session_p, char errbuf[ERR_ERROR_STRING_BUF_LEN]; pns = lsquic_packet_out_pns(packet_out); - /* TODO Obviously, will need more logic for 0-RTT */ - enc_level = pns2enc_level[ pns ]; + enc_level = pns2enc_level[ enc_sess->esi_have_forw ][ pns ]; if (enc_level == ENC_LEV_FORW) { @@ -2458,7 +2534,7 @@ static int iquic_esf_sess_resume_enabled (enc_session_t *enc_session_p) { struct enc_sess_iquic *const enc_sess = enc_session_p; - return enc_sess->esi_sess_resume_buf != NULL; + return !!(enc_sess->esi_flags & ESI_USE_SSL_TICKET); } @@ -2817,6 +2893,9 @@ set_secret (SSL *ssl, enum ssl_encryption_level_t level, key_len, hexbuf)); } + if (rw && enc_level == ENC_LEV_FORW) + enc_sess->esi_have_forw = 1; + return 1; err: @@ -2955,25 +3034,11 @@ chsk_ietf_on_new_stream (void *stream_if_ctx, struct lsquic_stream *stream) enum enc_level enc_level; enc_level = enc_sess->esi_cryst_if->csi_enc_level(stream); - if (enc_level != ENC_LEV_CLEAR) - { - LSQ_DEBUG("skip initialization of stream at level %u", enc_level); - goto end; - } - - if ( - (enc_sess->esi_flags & ESI_SERVER) == 0 && - 0 != init_client(enc_sess)) - { - LSQ_WARN("enc session could not be initialized"); - return NULL; - } - - enc_sess->esi_cryst_if->csi_wantwrite(stream, 1); + if (enc_level == ENC_LEV_CLEAR) + enc_sess->esi_cryst_if->csi_wantwrite(stream, 1); LSQ_DEBUG("handshake stream created successfully"); - end: return stream_if_ctx; } @@ -3006,6 +3071,7 @@ chsk_ietf_on_close (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx) static const char *const ihs2str[] = { [IHS_WANT_READ] = "want read", [IHS_WANT_WRITE] = "want write", + [IHS_WANT_RW] = "want rw", [IHS_STOP] = "stop", }; @@ -3036,6 +3102,10 @@ iquic_esfi_shake_stream (enc_session_t *sess, enc_sess->esi_cryst_if->csi_wantwrite(stream, 1); enc_sess->esi_cryst_if->csi_wantread(stream, 0); break; + case IHS_WANT_RW: + enc_sess->esi_cryst_if->csi_wantwrite(stream, 1); + enc_sess->esi_cryst_if->csi_wantread(stream, 1); + break; default: assert(st == IHS_STOP); write = !lsquic_frab_list_empty(&enc_sess->esi_frals[enc_level]); @@ -3209,3 +3279,96 @@ const unsigned char *const lsquic_retry_nonce_buf[N_IETF_RETRY_VERSIONS] = /* [draft-ietf-quic-tls-29] Section 5.8 */ (unsigned char *) "\xe5\x49\x30\xf9\x7f\x21\x36\xf0\x53\x0a\x8c\x1c", }; + + +int +lsquic_enc_sess_ietf_gen_quic_ctx ( + const struct lsquic_engine_settings *settings, + enum lsquic_version version, unsigned char *buf, size_t bufsz) +{ + struct transport_params params; + int len; + + /* This code is pretty much copied from gen_trans_params(), with + * small (but important) exceptions. + */ + + memset(¶ms, 0, sizeof(params)); + params.tp_init_max_data = settings->es_init_max_data; + params.tp_init_max_stream_data_bidi_local + = settings->es_init_max_stream_data_bidi_local; + params.tp_init_max_stream_data_bidi_remote + = settings->es_init_max_stream_data_bidi_remote; + params.tp_init_max_stream_data_uni + = settings->es_init_max_stream_data_uni; + params.tp_init_max_streams_uni + = settings->es_init_max_streams_uni; + params.tp_init_max_streams_bidi + = settings->es_init_max_streams_bidi; + params.tp_ack_delay_exponent + = TP_DEF_ACK_DELAY_EXP; + params.tp_max_idle_timeout = settings->es_idle_timeout * 1000; + params.tp_max_ack_delay = TP_DEF_MAX_ACK_DELAY; + params.tp_active_connection_id_limit = MAX_IETF_CONN_DCIDS; + params.tp_set |= (1 << TPI_INIT_MAX_DATA) + | (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL) + | (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE) + | (1 << TPI_INIT_MAX_STREAM_DATA_UNI) + | (1 << TPI_INIT_MAX_STREAMS_UNI) + | (1 << TPI_INIT_MAX_STREAMS_BIDI) + | (1 << TPI_ACK_DELAY_EXPONENT) + | (1 << TPI_MAX_IDLE_TIMEOUT) + | (1 << TPI_MAX_ACK_DELAY) + | (1 << TPI_ACTIVE_CONNECTION_ID_LIMIT) + ; + if (settings->es_max_udp_payload_size_rx) + { + params.tp_max_udp_payload_size = settings->es_max_udp_payload_size_rx; + params.tp_set |= 1 << TPI_MAX_UDP_PAYLOAD_SIZE; + } + if (!settings->es_allow_migration) + params.tp_set |= 1 << TPI_DISABLE_ACTIVE_MIGRATION; + if (settings->es_ql_bits) + { + params.tp_loss_bits = settings->es_ql_bits - 1; + params.tp_set |= 1 << TPI_LOSS_BITS; + } + if (settings->es_delayed_acks) + { + params.tp_numerics[TPI_MIN_ACK_DELAY] = 10000; /* TODO: make into a constant? make configurable? */ + params.tp_set |= 1 << TPI_MIN_ACK_DELAY; + } + if (settings->es_timestamps) + { + params.tp_numerics[TPI_TIMESTAMPS] = TS_GENERATE_THEM; + params.tp_set |= 1 << TPI_TIMESTAMPS; + } + if (settings->es_datagrams) + { + if (params.tp_set & (1 << TPI_MAX_UDP_PAYLOAD_SIZE)) + params.tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE] + = params.tp_max_udp_payload_size; + else + params.tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE] + = TP_DEF_MAX_UDP_PAYLOAD_SIZE; + params.tp_set |= 1 << TPI_MAX_DATAGRAM_FRAME_SIZE; + } + + params.tp_set &= SERVER_0RTT_TPS; + + len = (version == LSQVER_ID27 ? lsquic_tp_encode_27 : lsquic_tp_encode)( + ¶ms, 1, buf, bufsz); + if (len >= 0) + { + char str[MAX_TP_STR_SZ]; + LSQ_LOG1(LSQ_LOG_DEBUG, "generated QUIC server context of %d bytes " + "for version %s", len, lsquic_ver2str[version]); + LSQ_LOG1(LSQ_LOG_DEBUG, "%s", ((version == LSQVER_ID27 + ? lsquic_tp_to_str_27 : lsquic_tp_to_str)(¶ms, str, + sizeof(str)), str)); + } + else + LSQ_LOG1(LSQ_LOG_WARN, "cannot generate QUIC server context: %d", + errno); + return len; +} diff --git a/src/liblsquic/lsquic_engine.c b/src/liblsquic/lsquic_engine.c index 72ce89f..f71acf1 100644 --- a/src/liblsquic/lsquic_engine.c +++ b/src/liblsquic/lsquic_engine.c @@ -631,6 +631,22 @@ lsquic_engine_new (unsigned flags, engine->pub.enp_tokgen = lsquic_tg_new(&engine->pub); if (!engine->pub.enp_tokgen) return NULL; + if (engine->flags & ENG_SERVER) + for (i = 0; i < sizeof(engine->pub.enp_quic_ctx_sz) + / sizeof(engine->pub.enp_quic_ctx_sz[0]); ++i) + { + int sz = lsquic_enc_sess_ietf_gen_quic_ctx( + &engine->pub.enp_settings, + i == 0 ? LSQVER_ID27 : LSQVER_ID28, + engine->pub.enp_quic_ctx_buf[i], + sizeof(engine->pub.enp_quic_ctx_buf)); + if (sz < 0) + { + free(engine); + return NULL; + } + engine->pub.enp_quic_ctx_sz[i] = (unsigned) sz; + } engine->pub.enp_crand = &engine->crand; if (engine->pub.enp_settings.es_noprogress_timeout) engine->pub.enp_noprog_timeout @@ -1106,6 +1122,34 @@ find_conn_by_addr (struct lsquic_hash *hash, const struct sockaddr *sa) } +/* When connections are identified by the local address, we need to drop + * packets that use DCIDs that do not correspond to any of SCIDs. This + * can happen when peer retires a SCID. This mimics the normal behavior, + * when connections are looked up in engine->conns_hash by ID: when there + * is no match, the packet is dropped. + */ +static int +dcid_checks_out (const struct lsquic_conn *conn, const lsquic_cid_t *dcid) +{ + const struct conn_cid_elem *cce; + + if (LSQUIC_CIDS_EQ(CN_SCID(conn), dcid)) + return 1; + + /* Slow check for those rare cases */ + for (cce = conn->cn_cces; cce < END_OF_CCES(conn); ++cce) + if ((conn->cn_cces_mask & (1 << (cce - conn->cn_cces))) + && !(cce->cce_flags & CCE_PORT) + && LSQUIC_CIDS_EQ(&cce->cce_cid, dcid)) + { + LSQ_DEBUG("connection checks out"); + return 1; + } + + return 0; +} + + static lsquic_conn_t * find_conn (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in, struct packin_parse_state *ppstate, const struct sockaddr *sa_local) @@ -1114,7 +1158,17 @@ find_conn (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in, lsquic_conn_t *conn; if (engine->flags & ENG_CONNS_BY_ADDR) + { el = find_conn_by_addr(engine->conns_hash, sa_local); + if ((packet_in->pi_flags & PI_CONN_ID) + && !dcid_checks_out(lsquic_hashelem_getdata(el), + &packet_in->pi_conn_id)) + { + LSQ_DEBUGC("DCID matches no SCID in connection %"CID_FMT": drop it", + CID_BITS(&packet_in->pi_conn_id)); + return NULL; + } + } else if (packet_in->pi_flags & PI_CONN_ID) el = lsquic_hash_find(engine->conns_hash, packet_in->pi_conn_id.idbuf, packet_in->pi_conn_id.len); diff --git a/src/liblsquic/lsquic_engine_public.h b/src/liblsquic/lsquic_engine_public.h index 56b40b3..1d199fd 100644 --- a/src/liblsquic/lsquic_engine_public.h +++ b/src/liblsquic/lsquic_engine_public.h @@ -78,6 +78,11 @@ struct lsquic_engine_public { struct lsquic_hash *enp_server_certs; /* gQUIC server configuration: */ struct lsquic_server_config *enp_server_config; + /* Serialized subset of server engine transport parameters that is used + * as SSL QUIC context. 0 is for version <= LSQVER_ID27, 1 is for others. + */ + unsigned char enp_quic_ctx_buf[2][200]; + unsigned enp_quic_ctx_sz[2]; }; /* Put connection onto the Tickable Queue if it is not already on it. If diff --git a/src/liblsquic/lsquic_frame_writer.c b/src/liblsquic/lsquic_frame_writer.c index 4698017..68ab0a7 100644 --- a/src/liblsquic/lsquic_frame_writer.c +++ b/src/liblsquic/lsquic_frame_writer.c @@ -30,6 +30,8 @@ #include "lsquic_frab_list.h" #include "lsquic_ev_log.h" +#include "fiu-local.h" + #define LSQUIC_LOGGER_MODULE LSQLM_FRAME_WRITER #define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(\ lsquic_stream_conn(fw->fw_stream)) @@ -497,6 +499,8 @@ lsquic_frame_writer_write_promise (struct lsquic_frame_writer *fw, unsigned char *buf; int s; + fiu_return_on("frame_writer/writer_promise", -1); + if (fw->fw_max_header_list_sz && 0 != check_headers_size(fw, headers)) return -1; diff --git a/src/liblsquic/lsquic_full_conn.c b/src/liblsquic/lsquic_full_conn.c index 826d42d..f5e2d29 100644 --- a/src/liblsquic/lsquic_full_conn.c +++ b/src/liblsquic/lsquic_full_conn.c @@ -4156,10 +4156,15 @@ full_conn_ci_push_stream (struct lsquic_conn *lconn, void *hset, if (0 != lsquic_headers_stream_push_promise(conn->fc_pub.u.gquic.hs, dep_stream->id, pushed_stream->id, headers)) { - /* Since the failure to write to HEADERS stream results in aborting - * the connection, we do not bother rolling back. - */ - LSQ_ERROR("could not send push promise"); + /* If forget we ever had the hset pointer: */ + lsquic_stream_drop_hset_ref(pushed_stream); + /* Now roll back stream creation and return stream ID: */ + if (pushed_stream->sm_hash_el.qhe_flags & QHE_HASHED) + lsquic_hash_erase(conn->fc_pub.all_streams, + &pushed_stream->sm_hash_el); + lsquic_stream_destroy(pushed_stream); + conn->fc_last_stream_id -= 2; + LSQ_INFO("could not send push promise"); return -1; } diff --git a/src/liblsquic/lsquic_full_conn_ietf.c b/src/liblsquic/lsquic_full_conn_ietf.c index 4c75f69..3b904ff 100644 --- a/src/liblsquic/lsquic_full_conn_ietf.c +++ b/src/liblsquic/lsquic_full_conn_ietf.c @@ -137,7 +137,7 @@ enum ifull_conn_flags IFC_IGNORE_HSK = 1 << 25, IFC_PROC_CRYPTO = 1 << 26, IFC_MIGRA = 1 << 27, - IFC_UNUSED28 = 1 << 28, /* Unused */ + IFC_HTTP_INITED = 1 << 28, /* HTTP initialized */ IFC_DELAYED_ACKS = 1 << 29, /* Delayed ACKs are enabled */ IFC_TIMESTAMPS = 1 << 30, /* Timestamps are enabled */ IFC_DATAGRAMS = 1u<< 31, /* Datagrams are enabled */ @@ -153,6 +153,7 @@ enum more_flags MF_CONN_CLOSE_PACK = 1 << 4, /* CONNECTION_CLOSE has been packetized */ MF_SEND_WRONG_COUNTS= 1 << 5, /* Send wrong ECN counts to peer */ MF_WANT_DATAGRAM_WRITE = 1 << 6, + MF_DOING_0RTT = 1 << 7, }; @@ -568,6 +569,12 @@ find_cce_by_cid (struct ietf_full_conn *, const lsquic_cid_t *); static void mtu_probe_too_large (struct ietf_full_conn *, const struct lsquic_packet_out *); +static int +apply_trans_params (struct ietf_full_conn *, const struct transport_params *); + +static int +init_http (struct ietf_full_conn *); + static unsigned highest_bit_set (unsigned sz) { @@ -1252,7 +1259,7 @@ ietf_full_conn_init (struct ietf_full_conn *conn, conn->ifc_peer_hq_settings.header_table_size = HQ_DF_QPACK_MAX_TABLE_CAPACITY; conn->ifc_peer_hq_settings.qpack_blocked_streams = HQ_DF_QPACK_BLOCKED_STREAMS; - conn->ifc_flags = flags | IFC_CREATED_OK | IFC_FIRST_TICK; + conn->ifc_flags = flags | IFC_FIRST_TICK; conn->ifc_max_ack_packno[PNS_INIT] = IQUIC_INVALID_PACKNO; conn->ifc_max_ack_packno[PNS_HSK] = IQUIC_INVALID_PACKNO; conn->ifc_max_ack_packno[PNS_APP] = IQUIC_INVALID_PACKNO; @@ -1282,6 +1289,7 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub, const unsigned char *sess_resume, size_t sess_resume_sz, const unsigned char *token, size_t token_sz) { + const struct transport_params *params; const struct enc_session_funcs_iquic *esfi; struct ietf_full_conn *conn; enum lsquic_version ver, sess_resume_version; @@ -1397,6 +1405,18 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub, conn->ifc_created = now; LSQ_DEBUG("logging using %s SCID", LSQUIC_LOG_CONN_ID == CN_SCID(&conn->ifc_conn) ? "client" : "server"); + if (sess_resume && (params + = conn->ifc_conn.cn_esf.i->esfi_get_peer_transport_params( + conn->ifc_conn.cn_enc_session), params != NULL)) + { + LSQ_DEBUG("initializing transport parameters for 0RTT"); + if (0 != apply_trans_params(conn, params)) + goto full_err; + if ((conn->ifc_flags & IFC_HTTP) && 0 != init_http(conn)) + goto full_err; + conn->ifc_mflags |= MF_DOING_0RTT; + } + conn->ifc_flags |= IFC_CREATED_OK; return &conn->ifc_conn; err4: @@ -1411,6 +1431,10 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub, free(conn); err0: return NULL; + + full_err: + ietf_full_conn_ci_destroy(&conn->ifc_conn); + return NULL; } @@ -1635,6 +1659,7 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub, LSQ_DEBUG("logging using %s SCID", LSQUIC_LOG_CONN_ID == CN_SCID(&conn->ifc_conn) ? "server" : "client"); + conn->ifc_flags |= IFC_CREATED_OK; return &conn->ifc_conn; err3: @@ -3317,35 +3342,13 @@ maybe_start_migration (struct ietf_full_conn *conn) static int -handshake_ok (struct lsquic_conn *lconn) +apply_trans_params (struct ietf_full_conn *conn, + const struct transport_params *params) { - struct ietf_full_conn *const conn = (struct ietf_full_conn *) lconn; struct lsquic_stream *stream; struct lsquic_hash_elem *el; - struct dcid_elem *dce; - const struct transport_params *params; enum stream_id_type sit; uint64_t limit; - char buf[MAX_TP_STR_SZ]; - - fiu_return_on("full_conn_ietf/handshake_ok", -1); - - /* Need to set this flag even we hit an error in the rest of this funciton. - * This is because this flag is used to calculate packet out header size - */ - lconn->cn_flags |= LSCONN_HANDSHAKE_DONE; - - params = lconn->cn_esf.i->esfi_get_peer_transport_params( - lconn->cn_enc_session); - if (!params) - { - ABORT_WARN("could not get transport parameters"); - return -1; - } - - LSQ_DEBUG("peer transport parameters: %s", - ((lconn->cn_version == LSQVER_ID27 ? lsquic_tp_to_str_27 - : lsquic_tp_to_str)(params, buf, sizeof(buf)), buf)); if ((params->tp_set & (1 << TPI_LOSS_BITS)) && conn->ifc_settings->es_ql_bits == 2) @@ -3487,6 +3490,104 @@ handshake_ok (struct lsquic_conn *lconn) CUR_NPATH(conn)->np_pack_size); } + if (params->tp_active_connection_id_limit > conn->ifc_conn.cn_n_cces) + conn->ifc_active_cids_limit = conn->ifc_conn.cn_n_cces; + else + conn->ifc_active_cids_limit = params->tp_active_connection_id_limit; + conn->ifc_first_active_cid_seqno = conn->ifc_scid_seqno; + + return 0; +} + + +static int +init_http (struct ietf_full_conn *conn) +{ + fiu_return_on("full_conn_ietf/init_http", -1); + lsquic_qeh_init(&conn->ifc_qeh, &conn->ifc_conn); + if (0 == avail_streams_count(conn, conn->ifc_flags & IFC_SERVER, + SD_UNI)) + { + ABORT_QUIETLY(1, HEC_GENERAL_PROTOCOL_ERROR, "cannot create " + "control stream due to peer-imposed limit"); + conn->ifc_error = CONN_ERR(1, HEC_GENERAL_PROTOCOL_ERROR); + return -1; + } + if (0 != create_ctl_stream_out(conn)) + { + ABORT_WARN("cannot create outgoing control stream"); + return -1; + } + if (0 != lsquic_hcso_write_settings(&conn->ifc_hcso, + &conn->ifc_enpub->enp_settings, conn->ifc_flags & IFC_SERVER)) + { + ABORT_WARN("cannot write SETTINGS"); + return -1; + } + if (!(conn->ifc_flags & IFC_SERVER) + && (conn->ifc_u.cli.ifcli_flags & IFCLI_PUSH_ENABLED) + && 0 != lsquic_hcso_write_max_push_id(&conn->ifc_hcso, + conn->ifc_u.cli.ifcli_max_push_id)) + { + ABORT_WARN("cannot write MAX_PUSH_ID"); + return -1; + } + if (0 != lsquic_qdh_init(&conn->ifc_qdh, &conn->ifc_conn, + conn->ifc_flags & IFC_SERVER, conn->ifc_enpub, + conn->ifc_settings->es_qpack_dec_max_size, + conn->ifc_settings->es_qpack_dec_max_blocked)) + { + ABORT_WARN("cannot initialize QPACK decoder"); + return -1; + } + if (avail_streams_count(conn, conn->ifc_flags & IFC_SERVER, SD_UNI) > 0) + { + if (0 != create_qdec_stream_out(conn)) + { + ABORT_WARN("cannot create outgoing QPACK decoder stream"); + return -1; + } + } + else + { + queue_streams_blocked_frame(conn, SD_UNI); + LSQ_DEBUG("cannot create outgoing QPACK decoder stream due to " + "unidir limits"); + } + conn->ifc_flags |= IFC_HTTP_INITED; + return 0; +} + + +static int +handshake_ok (struct lsquic_conn *lconn) +{ + struct ietf_full_conn *const conn = (struct ietf_full_conn *) lconn; + struct dcid_elem *dce; + const struct transport_params *params; + char buf[MAX_TP_STR_SZ]; + + fiu_return_on("full_conn_ietf/handshake_ok", -1); + + /* Need to set this flag even we hit an error in the rest of this funciton. + * This is because this flag is used to calculate packet out header size + */ + lconn->cn_flags |= LSCONN_HANDSHAKE_DONE; + + params = lconn->cn_esf.i->esfi_get_peer_transport_params( + lconn->cn_enc_session); + if (!params) + { + ABORT_WARN("could not get transport parameters"); + return -1; + } + + LSQ_DEBUG("peer transport parameters: %s", + ((lconn->cn_version == LSQVER_ID27 ? lsquic_tp_to_str_27 + : lsquic_tp_to_str)(params, buf, sizeof(buf)), buf)); + if (0 != apply_trans_params(conn, params)) + return -1; + dce = get_new_dce(conn); if (!dce) { @@ -3518,65 +3619,9 @@ handshake_ok (struct lsquic_conn *lconn) LSQ_INFO("applied peer transport parameters"); - if (conn->ifc_flags & IFC_HTTP) - { - lsquic_qeh_init(&conn->ifc_qeh, &conn->ifc_conn); - if (0 == avail_streams_count(conn, conn->ifc_flags & IFC_SERVER, - SD_UNI)) - { - ABORT_QUIETLY(1, HEC_GENERAL_PROTOCOL_ERROR, "cannot create " - "control stream due to peer-imposed limit"); - conn->ifc_error = CONN_ERR(1, HEC_GENERAL_PROTOCOL_ERROR); + if ((conn->ifc_flags & (IFC_HTTP|IFC_HTTP_INITED)) == IFC_HTTP) + if (0 != init_http(conn)) return -1; - } - if (0 != create_ctl_stream_out(conn)) - { - ABORT_WARN("cannot create outgoing control stream"); - return -1; - } - if (0 != lsquic_hcso_write_settings(&conn->ifc_hcso, - &conn->ifc_enpub->enp_settings, conn->ifc_flags & IFC_SERVER)) - { - ABORT_WARN("cannot write SETTINGS"); - return -1; - } - if (!(conn->ifc_flags & IFC_SERVER) - && (conn->ifc_u.cli.ifcli_flags & IFCLI_PUSH_ENABLED) - && 0 != lsquic_hcso_write_max_push_id(&conn->ifc_hcso, - conn->ifc_u.cli.ifcli_max_push_id)) - { - ABORT_WARN("cannot write MAX_PUSH_ID"); - return -1; - } - if (0 != lsquic_qdh_init(&conn->ifc_qdh, &conn->ifc_conn, - conn->ifc_flags & IFC_SERVER, conn->ifc_enpub, - conn->ifc_settings->es_qpack_dec_max_size, - conn->ifc_settings->es_qpack_dec_max_blocked)) - { - ABORT_WARN("cannot initialize QPACK decoder"); - return -1; - } - if (avail_streams_count(conn, conn->ifc_flags & IFC_SERVER, SD_UNI) > 0) - { - if (0 != create_qdec_stream_out(conn)) - { - ABORT_WARN("cannot create outgoing QPACK decoder stream"); - return -1; - } - } - else - { - queue_streams_blocked_frame(conn, SD_UNI); - LSQ_DEBUG("cannot create outgoing QPACK decoder stream due to " - "unidir limits"); - } - } - - if (params->tp_active_connection_id_limit > conn->ifc_conn.cn_n_cces) - conn->ifc_active_cids_limit = conn->ifc_conn.cn_n_cces; - else - conn->ifc_active_cids_limit = params->tp_active_connection_id_limit; - conn->ifc_first_active_cid_seqno = conn->ifc_scid_seqno; if (conn->ifc_settings->es_dplpmtud) conn->ifc_mflags |= MF_CHECK_MTU_PROBE; @@ -3585,6 +3630,9 @@ handshake_ok (struct lsquic_conn *lconn) conn->ifc_send_flags |= SF_SEND_NEW_CID; maybe_create_delayed_streams(conn); + if (!(conn->ifc_flags & IFC_SERVER)) + lsquic_send_ctl_0rtt_to_1rtt(&conn->ifc_send_ctl); + return 0; } @@ -3615,10 +3663,10 @@ ietf_full_conn_ci_hsk_done (struct lsquic_conn *lconn, } break; default: + case LSQ_HSK_RESUMED_FAIL: /* IETF crypto never returns this */ assert(0); /* fall-through */ case LSQ_HSK_FAIL: - case LSQ_HSK_RESUMED_FAIL: handshake_failed(lconn); break; } @@ -7406,6 +7454,7 @@ check_or_schedule_mtu_probe (struct ietf_full_conn *conn, lsquic_time_t now) if (!(conn->ifc_conn.cn_flags & LSCONN_HANDSHAKE_DONE) || (conn->ifc_flags & IFC_CLOSING) + || ~0ull == lsquic_senhist_largest(&conn->ifc_send_ctl.sc_senhist) || lsquic_senhist_largest(&conn->ifc_send_ctl.sc_senhist) < 30 || lsquic_send_ctl_in_recovery(&conn->ifc_send_ctl) || !lsquic_send_ctl_can_send_probe(&conn->ifc_send_ctl, @@ -7594,6 +7643,16 @@ ietf_full_conn_ci_retx_timeout (struct lsquic_conn *lconn) } +static void +ietf_full_conn_ci_early_data_failed (struct lsquic_conn *lconn) +{ + struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn; + + LSQ_DEBUG("early data failed"); + lsquic_send_ctl_stash_0rtt_packets(&conn->ifc_send_ctl); +} + + static size_t ietf_full_conn_ci_get_min_datagram_size (struct lsquic_conn *lconn) { @@ -7830,7 +7889,8 @@ ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now) conn->ifc_flags |= (s < 0) << IFC_BIT_ERROR; if (0 == s) process_crypto_stream_write_events(conn); - goto end_write; + if (!(conn->ifc_mflags & MF_DOING_0RTT)) + goto end_write; } maybe_conn_flush_special_streams(conn); @@ -8279,6 +8339,7 @@ ietf_full_conn_ci_count_garbage (struct lsquic_conn *lconn, size_t garbage_sz) .ci_destroy = ietf_full_conn_ci_destroy, \ .ci_drain_time = ietf_full_conn_ci_drain_time, \ .ci_drop_crypto_streams = ietf_full_conn_ci_drop_crypto_streams, \ + .ci_early_data_failed = ietf_full_conn_ci_early_data_failed, \ .ci_get_engine = ietf_full_conn_ci_get_engine, \ .ci_get_log_cid = ietf_full_conn_ci_get_log_cid, \ .ci_get_min_datagram_size= ietf_full_conn_ci_get_min_datagram_size, \ diff --git a/src/liblsquic/lsquic_hspack_valid.c b/src/liblsquic/lsquic_hspack_valid.c index 7398d2a..a59bfb7 100644 --- a/src/liblsquic/lsquic_hspack_valid.c +++ b/src/liblsquic/lsquic_hspack_valid.c @@ -88,7 +88,7 @@ is_valid_gquic_hs_packet (const unsigned char *buf, size_t bufsz, int lsquic_is_valid_hs_packet (struct lsquic_engine *engine, - const unsigned char *buf, size_t bufsz, size_t packet_in_sz) + const unsigned char *buf, size_t bufsz) { lsquic_ver_tag_t tag; int is_valid; @@ -104,7 +104,7 @@ lsquic_is_valid_hs_packet (struct lsquic_engine *engine, case 0x80|0x00|0x20|0x10|0x08: case 0x80|0x40|0x20|0x10|0x00: case 0x80|0x00|0x20|0x10|0x00: - is_valid = packet_in_sz >= IQUIC_MIN_INIT_PACKET_SZ + is_valid = bufsz >= IQUIC_MIN_INIT_PACKET_SZ && lsquic_is_valid_iquic_hs_packet(buf, bufsz, &tag); break; /* 1X00 XGGG: ID-22 long header */ @@ -122,7 +122,7 @@ lsquic_is_valid_hs_packet (struct lsquic_engine *engine, case 0x80|0x00|0x20|0x00|0x08: case 0x80|0x40|0x20|0x00|0x00: case 0x80|0x00|0x20|0x00|0x00: - is_valid = packet_in_sz >= IQUIC_MIN_INIT_PACKET_SZ + is_valid = bufsz >= IQUIC_MIN_INIT_PACKET_SZ && lsquic_is_valid_ietf_v1_or_Q046plus_hs_packet(buf, bufsz, &tag); break; /* 01XX XGGG: ID-22 short header */ diff --git a/src/liblsquic/lsquic_mini_conn_ietf.c b/src/liblsquic/lsquic_mini_conn_ietf.c index 1a1aa70..b52ba43 100644 --- a/src/liblsquic/lsquic_mini_conn_ietf.c +++ b/src/liblsquic/lsquic_mini_conn_ietf.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -560,6 +561,16 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub, TAILQ_INIT(&conn->imc_crypto_frames); if (odcid) imico_peer_addr_validated(conn, "odcid"); +#if LSQUIC_DEVEL + { + const char *const s = getenv("LSQUIC_LOSE_0RTT"); + if (s && atoi(s)) + { + LSQ_DEBUG("will lose 0-RTT packets (via env variable)"); + conn->imc_delayed_packets_count = UCHAR_MAX; + } + } +#endif LSQ_DEBUG("created mini connection object %p; max packet size=%hu", conn, conn->imc_path.np_pack_size); diff --git a/src/liblsquic/lsquic_parse_ietf_v1.c b/src/liblsquic/lsquic_parse_ietf_v1.c index ca4218d..bb7cf96 100644 --- a/src/liblsquic/lsquic_parse_ietf_v1.c +++ b/src/liblsquic/lsquic_parse_ietf_v1.c @@ -2163,11 +2163,11 @@ ietf_v1_parse_datagram_frame (const unsigned char *buf, size_t buf_len, if (buf[0] & 1) { s = vint_read(buf + 1, buf + buf_len, &len); - if (s > 0 && 1 + s + len >= buf_len) + if (s > 0 && 1 + s + len <= buf_len) { *data = buf + 1 + s; *data_len = len; - return 1 + buf_len + len; + return 1 + s + len; } else return -1; diff --git a/src/liblsquic/lsquic_send_ctl.c b/src/liblsquic/lsquic_send_ctl.c index 1b24b2d..19b637b 100644 --- a/src/liblsquic/lsquic_send_ctl.c +++ b/src/liblsquic/lsquic_send_ctl.c @@ -338,6 +338,7 @@ lsquic_send_ctl_init (lsquic_send_ctl_t *ctl, struct lsquic_alarmset *alset, TAILQ_INIT(&ctl->sc_unacked_packets[PNS_HSK]); TAILQ_INIT(&ctl->sc_unacked_packets[PNS_APP]); TAILQ_INIT(&ctl->sc_lost_packets); + TAILQ_INIT(&ctl->sc_0rtt_stash); ctl->sc_enpub = enpub; ctl->sc_alset = alset; ctl->sc_ver_neg = ver_neg; @@ -1463,6 +1464,11 @@ lsquic_send_ctl_cleanup (lsquic_send_ctl_t *ctl) packet_out->po_flags &= ~PO_LOST; send_ctl_destroy_packet(ctl, packet_out); } + while ((packet_out = TAILQ_FIRST(&ctl->sc_0rtt_stash))) + { + TAILQ_REMOVE(&ctl->sc_0rtt_stash, packet_out, po_next); + send_ctl_destroy_packet(ctl, packet_out); + } for (n = 0; n < sizeof(ctl->sc_buffered_packets) / sizeof(ctl->sc_buffered_packets[0]); ++n) { @@ -3795,3 +3801,72 @@ lsquic_send_ctl_rollback (struct lsquic_send_ctl *ctl, if (buffered && (lost_types & QUIC_FTBIT_ACK)) lconn->cn_if->ci_ack_rollback(lconn, &ctl_state->ack_state); } + + +/* Find 0-RTT packets and change them to 1-RTT packets */ +void +lsquic_send_ctl_0rtt_to_1rtt (struct lsquic_send_ctl *ctl) +{ + struct lsquic_packet_out *packet_out; + unsigned count; + struct lsquic_packets_tailq *const *q; + struct lsquic_packets_tailq *const queues[] = { + &ctl->sc_scheduled_packets, + &ctl->sc_unacked_packets[PNS_APP], + &ctl->sc_lost_packets, + &ctl->sc_buffered_packets[0].bpq_packets, + &ctl->sc_buffered_packets[1].bpq_packets, + }; + + assert(ctl->sc_flags & SC_IETF); + + while (packet_out = TAILQ_FIRST(&ctl->sc_0rtt_stash), packet_out != NULL) + { + TAILQ_REMOVE(&ctl->sc_0rtt_stash, packet_out, po_next); + TAILQ_INSERT_TAIL(&ctl->sc_lost_packets, packet_out, po_next); + packet_out->po_flags |= PO_LOST; + } + + count = 0; + for (q = queues; q < queues + sizeof(queues) / sizeof(queues[0]); ++q) + TAILQ_FOREACH(packet_out, *q, po_next) + if (packet_out->po_header_type == HETY_0RTT) + { + ++count; + packet_out->po_header_type = HETY_NOT_SET; + if (packet_out->po_flags & PO_ENCRYPTED) + send_ctl_return_enc_data(ctl, packet_out); + } + + LSQ_DEBUG("handshake ok: changed %u packet%.*s from 0-RTT to 1-RTT", + count, count != 1, "s"); +} + + +/* Remove 0-RTT packets from the unacked queue and wait to retransmit them + * after handshake succeeds. This is the most common case. There could + * (theoretically) be some corner cases where 0-RTT packets are in the + * scheduled queue, but we let those be lost naturally if that occurs. + */ +void +lsquic_send_ctl_stash_0rtt_packets (struct lsquic_send_ctl *ctl) +{ + struct lsquic_packet_out *packet_out, *next; + unsigned count, packet_sz; + + count = 0; + for (packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets[PNS_APP]); + packet_out; packet_out = next) + { + next = TAILQ_NEXT(packet_out, po_next); + if (packet_out->po_header_type == HETY_0RTT) + { + packet_sz = packet_out_sent_sz(packet_out); + send_ctl_unacked_remove(ctl, packet_out, packet_sz); + TAILQ_INSERT_TAIL(&ctl->sc_0rtt_stash, packet_out, po_next); + ++count; + } + } + + LSQ_DEBUG("stashed %u 0-RTT packet%.*s", count, count != 1, "s"); +} diff --git a/src/liblsquic/lsquic_send_ctl.h b/src/liblsquic/lsquic_send_ctl.h index 96c29f4..196c2aa 100644 --- a/src/liblsquic/lsquic_send_ctl.h +++ b/src/liblsquic/lsquic_send_ctl.h @@ -87,6 +87,7 @@ typedef struct lsquic_send_ctl { /* Second section: everything else. */ struct lsquic_packets_tailq sc_scheduled_packets, + sc_0rtt_stash, sc_lost_packets; struct buf_packet_q sc_buffered_packets[BPT_OTHER_PRIO + 1]; const struct ver_neg *sc_ver_neg; @@ -431,4 +432,10 @@ void lsquic_send_ctl_rollback (struct lsquic_send_ctl *, struct send_ctl_state *, const struct iovec *, size_t); +void +lsquic_send_ctl_0rtt_to_1rtt (struct lsquic_send_ctl *); + +void +lsquic_send_ctl_stash_0rtt_packets (struct lsquic_send_ctl *); + #endif diff --git a/src/liblsquic/lsquic_stream.c b/src/liblsquic/lsquic_stream.c index 52d628d..fb1ef14 100644 --- a/src/liblsquic/lsquic_stream.c +++ b/src/liblsquic/lsquic_stream.c @@ -373,6 +373,39 @@ stream_is_hsk (const struct lsquic_stream *stream) } +/* This function's only job is to change the allocated packet's header + * type to HETY_0RTT when stream frames are written before handshake + * is complete. + */ +static struct lsquic_packet_out * +stream_get_packet_for_stream_0rtt (struct lsquic_send_ctl *ctl, + unsigned need_at_least, const struct network_path *path, + const struct lsquic_stream *stream) +{ + struct lsquic_packet_out *packet_out; + + if (stream->conn_pub->lconn->cn_flags & LSCONN_HANDSHAKE_DONE) + { + LSQ_DEBUG("switch to regular \"get packet for stream\" function"); + /* Here we drop the "const" because this is a static function. + * Otherwise, we would not condone such sorcery. + */ + ((struct lsquic_stream *) stream)->sm_get_packet_for_stream + = lsquic_send_ctl_get_packet_for_stream; + return lsquic_send_ctl_get_packet_for_stream(ctl, need_at_least, + path, stream); + } + else + { + packet_out = lsquic_send_ctl_get_packet_for_stream(ctl, need_at_least, + path, stream); + if (packet_out) + packet_out->po_header_type = HETY_0RTT; + return packet_out; + } +} + + static struct lsquic_stream * stream_new_common (lsquic_stream_id_t id, struct lsquic_conn_public *conn_pub, const struct lsquic_stream_if *stream_if, void *stream_if_ctx, @@ -405,6 +438,7 @@ stream_new_common (lsquic_stream_id_t id, struct lsquic_conn_public *conn_pub, stream->sm_bflags |= ctor_flags & ((1 << N_SMBF_FLAGS) - 1); if (conn_pub->lconn->cn_flags & LSCONN_SERVER) stream->sm_bflags |= SMBF_SERVER; + stream->sm_get_packet_for_stream = lsquic_send_ctl_get_packet_for_stream; return stream; } @@ -479,6 +513,13 @@ lsquic_stream_new (lsquic_stream_id_t id, } } + if ((stream->sm_bflags & (SMBF_SERVER|SMBF_IETF)) == SMBF_IETF + && !(conn_pub->lconn->cn_flags & LSCONN_HANDSHAKE_DONE)) + { + LSQ_DEBUG("use wrapper \"get packet for stream\" function"); + stream->sm_get_packet_for_stream = stream_get_packet_for_stream_0rtt; + } + lsquic_sfcw_init(&stream->fc, initial_window, cfcw, conn_pub, id); stream->max_send_off = initial_send_off; LSQ_DEBUG("created stream"); @@ -584,6 +625,14 @@ drop_buffered_data (struct lsquic_stream *stream) } +void +lsquic_stream_drop_hset_ref (struct lsquic_stream *stream) +{ + if (stream->uh) + stream->uh->uh_hset = NULL; +} + + static void destroy_uh (struct lsquic_stream *stream) { @@ -3087,7 +3136,7 @@ stream_write_to_packet_std (struct frame_gen_ctx *fg_ctx, const size_t size) else need_at_least += size > 0; get_packet: - packet_out = lsquic_send_ctl_get_packet_for_stream(send_ctl, + packet_out = stream->sm_get_packet_for_stream(send_ctl, need_at_least, stream->conn_pub->path, stream); if (packet_out) { diff --git a/src/liblsquic/lsquic_stream.h b/src/liblsquic/lsquic_stream.h index 1c4313a..8c23453 100644 --- a/src/liblsquic/lsquic_stream.h +++ b/src/liblsquic/lsquic_stream.h @@ -17,6 +17,9 @@ struct data_frame; enum quic_frame_type; struct push_promise; union hblock_ctx; +struct lsquic_packet_out; +struct lsquic_send_ctl; +struct network_path; TAILQ_HEAD(lsquic_streams_tailq, lsquic_stream); @@ -311,6 +314,11 @@ struct lsquic_stream size_t (*sm_write_avail)(struct lsquic_stream *); int (*sm_readable)(struct lsquic_stream *); + struct lsquic_packet_out * (*sm_get_packet_for_stream)( + struct lsquic_send_ctl *, + unsigned, const struct network_path *, + const struct lsquic_stream *); + /* This element is optional */ const struct stream_filter_if *sm_sfi; @@ -622,4 +630,7 @@ void lsquic_stream_set_pwritev_params (unsigned iovecs, unsigned frames); #endif +void +lsquic_stream_drop_hset_ref (struct lsquic_stream *); + #endif diff --git a/src/liblsquic/lsquic_trans_params.h b/src/liblsquic/lsquic_trans_params.h index 66bddeb..456ff29 100644 --- a/src/liblsquic/lsquic_trans_params.h +++ b/src/liblsquic/lsquic_trans_params.h @@ -184,4 +184,26 @@ size_t lsquic_tp_get_quantum_sz (void); #endif +#define SERVER_0RTT_TPS (0 \ + /* [draft-ietf-quic-transport-31] Section 7.4.1: */ \ + | (1 << TPI_ACTIVE_CONNECTION_ID_LIMIT) \ + | (1 << TPI_INIT_MAX_DATA) \ + | (1 << TPI_INIT_MAX_STREAMS_UNI) \ + | (1 << TPI_INIT_MAX_STREAMS_BIDI) \ + | (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL) \ + | (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE) \ + | (1 << TPI_INIT_MAX_STREAM_DATA_UNI) \ + | (1 << TPI_MAX_IDLE_TIMEOUT) \ + | (1 << TPI_MAX_UDP_PAYLOAD_SIZE) \ + | (1 << TPI_DISABLE_ACTIVE_MIGRATION) \ + /* Not including TPI_LOSS_BITS, see */ \ + /* draft-ferrieuxhamchaoui-quic-lossbits-03, Section 5.1 */ \ + /* [draft-ietf-quic-datagram-01] Section 3: */ \ + | (1 << TPI_MAX_DATAGRAM_FRAME_SIZE) \ + /* [draft-iyengar-quic-delayed-ack-01] does not specfiy, store: */ \ + | (1 << TPI_MIN_ACK_DELAY) \ + /* [draft-huitema-quic-ts-03] does not specfiy, store: */ \ + | (1 << TPI_TIMESTAMPS) \ +) + #endif