diff --git a/CHANGELOG b/CHANGELOG index f2bd7c0..18eaa93 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,10 @@ +2021-04-16 + - 2.30.1 + - Fixed an unintialized data member for handling pushed headers. + - Added a new status code for version negotiation failure. + - Fixed a few compliance issues found by h3spec. + - Addressed high CPU usage when transport cannot send pending packets. + 2021-04-12 - 2.30.0 - Added support for sending/receiving multiple headers to address the diff --git a/docs/conf.py b/docs/conf.py index d831549..be6b281 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -26,7 +26,7 @@ author = u'LiteSpeed Technologies' # The short X.Y version version = u'2.30' # The full version, including alpha/beta/rc tags -release = u'2.30.0' +release = u'2.30.1' # -- General configuration --------------------------------------------------- diff --git a/include/lsquic.h b/include/lsquic.h index 344303c..8f90315 100644 --- a/include/lsquic.h +++ b/include/lsquic.h @@ -25,7 +25,7 @@ extern "C" { #define LSQUIC_MAJOR_VERSION 2 #define LSQUIC_MINOR_VERSION 30 -#define LSQUIC_PATCH_VERSION 0 +#define LSQUIC_PATCH_VERSION 1 /** * Engine flags: @@ -2088,6 +2088,7 @@ enum LSQUIC_CONN_STATUS LSCONN_ST_ERROR, LSCONN_ST_CLOSED, LSCONN_ST_PEER_GOING_AWAY, + LSCONN_ST_VERNEG_FAILURE, }; enum LSQUIC_CONN_STATUS diff --git a/src/liblsquic/lsquic_engine.c b/src/liblsquic/lsquic_engine.c index 0a11837..be6341d 100644 --- a/src/liblsquic/lsquic_engine.c +++ b/src/liblsquic/lsquic_engine.c @@ -3188,7 +3188,8 @@ lsquic_engine_earliest_adv_tick (lsquic_engine_t *engine, int *diff) return 1; } - if (engine->pr_queue && lsquic_prq_have_pending(engine->pr_queue)) + if ((engine->pub.enp_flags & ENPUB_CAN_SEND) + && engine->pr_queue && lsquic_prq_have_pending(engine->pr_queue)) { #if LSQUIC_DEBUG_NEXT_ADV_TICK engine->last_logged_conn = 0; diff --git a/src/liblsquic/lsquic_full_conn.c b/src/liblsquic/lsquic_full_conn.c index 95572bf..b561865 100644 --- a/src/liblsquic/lsquic_full_conn.c +++ b/src/liblsquic/lsquic_full_conn.c @@ -2283,6 +2283,7 @@ process_ver_neg_packet (struct full_conn *conn, lsquic_packet_in_t *packet_in) versions &= conn->fc_ver_neg.vn_supp; if (0 == versions) { + conn->fc_flags |= FC_HSK_FAILED; ABORT_ERROR("client does not support any of the server-specified " "versions"); return; @@ -4087,6 +4088,7 @@ synthesize_push_request (struct full_conn *conn, void *hset, if (lsquic_http1x_if == conn->fc_enpub->enp_hsi_if) uh->uh_flags |= UH_H1H; uh->uh_hset = hset; + uh->uh_next = NULL; return uh; } @@ -4230,7 +4232,12 @@ full_conn_ci_status (struct lsquic_conn *lconn, char *errbuf, size_t bufsz) } if (conn->fc_flags & FC_ERROR) - return LSCONN_ST_ERROR; + { + if (conn->fc_flags & FC_HSK_FAILED) + return LSCONN_ST_VERNEG_FAILURE; + else + return LSCONN_ST_ERROR; + } if (conn->fc_flags & FC_TIMED_OUT) return LSCONN_ST_TIMED_OUT; if (conn->fc_flags & FC_ABORTED) diff --git a/src/liblsquic/lsquic_full_conn_ietf.c b/src/liblsquic/lsquic_full_conn_ietf.c index 4ce0570..b1f9979 100644 --- a/src/liblsquic/lsquic_full_conn_ietf.c +++ b/src/liblsquic/lsquic_full_conn_ietf.c @@ -4018,6 +4018,7 @@ ietf_full_conn_ci_push_stream (struct lsquic_conn *lconn, void *hset, uh->uh_exclusive = 0; uh->uh_flags = UH_FIN; uh->uh_hset = hset; + uh->uh_next = NULL; memset(promise, 0, sizeof(*promise)); promise->pp_refcnt = 1; /* This function itself keeps a reference */ @@ -7587,7 +7588,7 @@ process_incoming_packet_verneg (struct ietf_full_conn *conn, */ if (!verneg_ok(conn)) { - ABORT_WITH_FLAG(conn, LSQ_LOG_NOTICE, IFC_ERROR, + ABORT_WITH_FLAG(conn, LSQ_LOG_NOTICE, IFC_ERROR|IFC_HSK_FAILED, "version negotiation not permitted in this version of QUIC"); return -1; } @@ -7595,7 +7596,7 @@ process_incoming_packet_verneg (struct ietf_full_conn *conn, versions &= conn->ifc_u.cli.ifcli_ver_neg.vn_supp; if (0 == versions) { - ABORT_WITH_FLAG(conn, LSQ_LOG_NOTICE, IFC_ERROR, + ABORT_WITH_FLAG(conn, LSQ_LOG_NOTICE, IFC_ERROR|IFC_HSK_FAILED, "client does not support any of the server-specified versions"); return -1; } @@ -8461,7 +8462,12 @@ ietf_full_conn_ci_status (struct lsquic_conn *lconn, char *errbuf, size_t bufsz) } if (conn->ifc_flags & IFC_ERROR) - return LSCONN_ST_ERROR; + { + if (conn->ifc_flags & IFC_HSK_FAILED) + return LSCONN_ST_VERNEG_FAILURE; + else + return LSCONN_ST_ERROR; + } if (conn->ifc_flags & IFC_TIMED_OUT) return LSCONN_ST_TIMED_OUT; if (conn->ifc_flags & IFC_ABORTED) @@ -8564,6 +8570,8 @@ ietf_full_conn_ci_abort_error (struct lsquic_conn *lconn, int is_app, const char *err_str, *percent; char err_buf[0x100]; + if (conn->ifc_error.u.err != 0) + return; percent = strchr(fmt, '%'); if (percent) { @@ -9344,11 +9352,15 @@ on_priority_update_server (void *ctx, enum hq_frame_type frame_type, static void -on_unexpected_frame (void *ctx, uint64_t frame_type) +on_frame_error (void *ctx, unsigned code, uint64_t frame_type) { struct ietf_full_conn *const conn = ctx; - ABORT_QUIETLY(1, HEC_FRAME_UNEXPECTED, "Frame type %"PRIu64" is not " - "allowed on the control stream", frame_type); + if (code == HEC_MISSING_SETTINGS) + ABORT_QUIETLY(1, code, "The first control frame is not SETTINGS, " + "got frame type %"PRIu64, frame_type); + else + ABORT_QUIETLY(1, HEC_FRAME_UNEXPECTED, "Frame type %"PRIu64" is not " + "allowed on the control stream", frame_type); } @@ -9359,7 +9371,7 @@ static const struct hcsi_callbacks hcsi_callbacks_server_27 = .on_settings_frame = on_settings_frame, .on_setting = on_setting, .on_goaway = on_goaway_server_27, - .on_unexpected_frame = on_unexpected_frame, + .on_frame_error = on_frame_error, .on_priority_update = on_priority_update_server, }; @@ -9370,7 +9382,7 @@ static const struct hcsi_callbacks hcsi_callbacks_client_27 = .on_settings_frame = on_settings_frame, .on_setting = on_setting, .on_goaway = on_goaway_client_27, - .on_unexpected_frame = on_unexpected_frame, + .on_frame_error = on_frame_error, .on_priority_update = on_priority_update_client, }; @@ -9382,7 +9394,7 @@ static const struct hcsi_callbacks hcsi_callbacks_server_29 = .on_settings_frame = on_settings_frame, .on_setting = on_setting, .on_goaway = on_goaway_server, - .on_unexpected_frame = on_unexpected_frame, + .on_frame_error = on_frame_error, .on_priority_update = on_priority_update_server, }; @@ -9393,7 +9405,7 @@ static const struct hcsi_callbacks hcsi_callbacks_client_29 = .on_settings_frame = on_settings_frame, .on_setting = on_setting, .on_goaway = on_goaway_client, - .on_unexpected_frame = on_unexpected_frame, + .on_frame_error = on_frame_error, .on_priority_update = on_priority_update_client, }; diff --git a/src/liblsquic/lsquic_hcsi_reader.c b/src/liblsquic/lsquic_hcsi_reader.c index 671ef2c..8b365b6 100644 --- a/src/liblsquic/lsquic_hcsi_reader.c +++ b/src/liblsquic/lsquic_hcsi_reader.c @@ -61,9 +61,19 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf, break; reader->hr_frame_type = reader->hr_u.vint2_state.vr2s_one; reader->hr_frame_length = reader->hr_u.vint2_state.vr2s_two; + + if (!(reader->hr_flag & HR_FLAG_RCVD_SETTING) + && reader->hr_frame_type != HQFT_SETTINGS) + { + reader->hr_cb->on_frame_error(reader->hr_ctx, + HEC_MISSING_SETTINGS, reader->hr_frame_type); + return -1; + } + switch (reader->hr_frame_type) { case HQFT_SETTINGS: + reader->hr_flag |= HR_FLAG_RCVD_SETTING; if (reader->hr_frame_length) { reader->hr_state = HR_READ_SETTING_BEGIN; @@ -91,8 +101,8 @@ lsquic_hcsi_reader_feed (struct hcsi_reader *reader, const void *buf, case HQFT_DATA: case HQFT_HEADERS: case HQFT_PUSH_PROMISE: - reader->hr_cb->on_unexpected_frame(reader->hr_ctx, - reader->hr_frame_type); + reader->hr_cb->on_frame_error(reader->hr_ctx, + HEC_FRAME_UNEXPECTED, reader->hr_frame_type); return -1; default: { diff --git a/src/liblsquic/lsquic_hcsi_reader.h b/src/liblsquic/lsquic_hcsi_reader.h index 44360eb..75a91ea 100644 --- a/src/liblsquic/lsquic_hcsi_reader.h +++ b/src/liblsquic/lsquic_hcsi_reader.h @@ -17,7 +17,7 @@ struct hcsi_callbacks void (*on_settings_frame)(void *ctx); void (*on_setting)(void *ctx, uint64_t setting_id, uint64_t value); void (*on_goaway)(void *ctx, uint64_t stream_id); - void (*on_unexpected_frame)(void *ctx, uint64_t frame_type); + void (*on_frame_error)(void *ctx, unsigned code, uint64_t frame_type); void (*on_priority_update)(void *ctx, enum hq_frame_type, uint64_t id, const char *, size_t); }; @@ -35,7 +35,11 @@ struct hcsi_reader HR_READ_VARINT, HR_READ_VARINT_CONTINUE, HR_ERROR, - } hr_state; + } hr_state:8; + enum { + HR_FLAG_RCVD_SETTING = 1, + } hr_flag:8; + unsigned hr_nread; /* Used for PRIORITY_UPDATE and SETTINGS frames */ struct lsquic_conn *hr_conn; uint64_t hr_frame_type; uint64_t hr_frame_length; @@ -55,7 +59,6 @@ struct hcsi_reader } hr_u; const struct hcsi_callbacks *hr_cb; void *hr_ctx; - unsigned hr_nread; /* Used for PRIORITY_UPDATE and SETTINGS frames */ }; diff --git a/src/liblsquic/lsquic_qdec_hdl.c b/src/liblsquic/lsquic_qdec_hdl.c index 062a9cf..e8c50de 100644 --- a/src/liblsquic/lsquic_qdec_hdl.c +++ b/src/liblsquic/lsquic_qdec_hdl.c @@ -565,7 +565,7 @@ qdh_hsi_process_wrapper (struct qpack_dec_hdl *qdh, void *hset, retval = qdh->qdh_enpub->enp_hsi_if->hsi_process_header(hset, xhdr); if (0 != retval) qdh->qdh_conn->cn_if->ci_abort_error(qdh->qdh_conn, 1, - 1 == retval ? HEC_MESSAGE_ERROR : HEC_INTERNAL_ERROR, + HEC_MESSAGE_ERROR, "error processing headers"); return retval; diff --git a/tests/test_hcsi_reader.c b/tests/test_hcsi_reader.c index 0e802ea..74432c8 100644 --- a/tests/test_hcsi_reader.c +++ b/tests/test_hcsi_reader.c @@ -189,7 +189,7 @@ on_goaway (void *ctx, uint64_t stream_id) } static void -on_unexpected_frame (void *ctx, uint64_t frame_type) +on_frame_error (void *ctx, unsigned code, uint64_t frame_type) { fprintf(ctx, "%s: %"PRIu64"\n", __func__, frame_type); } @@ -219,7 +219,7 @@ static const struct hcsi_callbacks callbacks = .on_settings_frame = on_settings_frame, .on_setting = on_setting, .on_goaway = on_goaway, - .on_unexpected_frame = on_unexpected_frame, + .on_frame_error = on_frame_error, .on_priority_update = on_priority_update, }; @@ -252,6 +252,7 @@ run_test (const struct test *test) { out_f = open_memstream(&output, &out_sz); lsquic_hcsi_reader_init(&reader, &lconn, &callbacks, out_f); + reader.hr_flag |= HR_FLAG_RCVD_SETTING; p = test->input; do