diff --git a/CHANGELOG b/CHANGELOG index 1d9a7bb..4c9f097 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,15 @@ +2019-02-04 + - 1.19.0 + - [FEATURE, API Change] 0-RTT support. Add function to export 0-RTT + information; it can be supplied to a subsequent connect() call. + - [FEATURE] Add -0 flag to http_client to exercise 0-RTT support. + - [BUGFIX] Resuscitate the Windows build. + - [BUGFIX] Send HTTP settings (max header list size) if necessary. + - [BUGFIX] Buffered packets can contain ACK frames. + - [BUGFIX] Make packet writeable once all STREAM frames are elided. + - [BUGFIX] Fix potential null dereference when realloc fails. + - cmake: simplify build configuration. + 2019-01-28 - 1.18.0 - [API Change] Can specify clock granularity in engine settings. diff --git a/CMakeLists.txt b/CMakeLists.txt index e512f6e..c07a7ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,42 +81,74 @@ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MY_CMAKE_FLAGS} $ENV{EXTRA_CFLAGS}") MESSAGE(STATUS "Compiler flags: ${CMAKE_C_FLAGS}") +IF (NOT DEFINED BORINGSSL_INCLUDE AND DEFINED BORINGSSL_DIR) + FIND_PATH(BORINGSSL_INCLUDE NAMES openssl/ssl.h + PATHS ${BORINGSSL_DIR}/include + NO_DEFAULT_PATH) +ENDIF() # This must be done before adding other include directories to take # precedence over header files from other SSL installs. -FIND_PATH(BORINGSSL_INCLUDE_DIR NAMES openssl/ssl.h - PATHS ${BORINGSSL_DIR}/include - NO_DEFAULT_PATH) -IF (BORINGSSL_INCLUDE_DIR) - MESSAGE(STATUS "BoringSSL include directory ${BORINGSSL_INCLUDE_DIR}") - INCLUDE_DIRECTORIES(${BORINGSSL_INCLUDE_DIR}) + +IF (BORINGSSL_INCLUDE) + MESSAGE(STATUS "BoringSSL include directory ${BORINGSSL_INCLUDE}") + INCLUDE_DIRECTORIES(${BORINGSSL_INCLUDE}) ELSE() MESSAGE(FATAL_ERROR "BoringSSL headers not found") ENDIF() -FOREACH(BORINGSSL_LIB ssl crypto decrepit) - IF (CMAKE_SYSTEM_NAME STREQUAL Windows) - FIND_LIBRARY(BORINGSSL_LIB_${BORINGSSL_LIB} - NAMES ${BORINGSSL_LIB} - PATHS ${BORINGSSL_DIR}/${BORINGSSL_LIB} - PATH_SUFFIXES Debug Release MinSizeRel RelWithDebInfo - NO_DEFAULT_PATH) - ELSE() - FIND_LIBRARY(BORINGSSL_LIB_${BORINGSSL_LIB} - NAMES lib${BORINGSSL_LIB}.a - PATHS ${BORINGSSL_DIR}/${BORINGSSL_LIB} - NO_DEFAULT_PATH) - ENDIF() - IF(BORINGSSL_LIB_${BORINGSSL_LIB}) - MESSAGE(STATUS "Found ${BORINGSSL_LIB} library: ${BORINGSSL_LIB_${BORINGSSL_LIB}}") - ELSE() - MESSAGE(STATUS "${BORINGSSL_LIB} library not found") - ENDIF() -ENDFOREACH() +IF (NOT DEFINED BORINGSSL_LIB AND DEFINED BORINGSSL_DIR) + FOREACH(LIB_NAME ssl crypto decrepit) + IF (CMAKE_SYSTEM_NAME STREQUAL Windows) + FIND_LIBRARY(BORINGSSL_LIB_${LIB_NAME} + NAMES ${LIB_NAME} + PATHS ${BORINGSSL_DIR}/${LIB_NAME} + PATH_SUFFIXES Debug Release MinSizeRel RelWithDebInfo + NO_DEFAULT_PATH) + ELSE() + FIND_LIBRARY(BORINGSSL_LIB_${LIB_NAME} + NAMES lib${LIB_NAME}.a + PATHS ${BORINGSSL_DIR}/${LIB_NAME} + NO_DEFAULT_PATH) + ENDIF() + IF(BORINGSSL_LIB_${LIB_NAME}) + MESSAGE(STATUS "Found ${LIB_NAME} library: ${BORINGSSL_LIB_${LIB_NAME}}") + ELSE() + MESSAGE(STATUS "${LIB_NAME} library not found") + ENDIF() + ENDFOREACH() +ELSE() + + + FOREACH(LIB_NAME ssl crypto decrepit) + IF (CMAKE_SYSTEM_NAME STREQUAL Windows) + FIND_LIBRARY(BORINGSSL_LIB_${LIB_NAME} + NAMES ${LIB_NAME} + PATHS ${BORINGSSL_LIB} + PATH_SUFFIXES Debug Release MinSizeRel RelWithDebInfo + NO_DEFAULT_PATH) + ELSE() + FIND_LIBRARY(BORINGSSL_LIB_${LIB_NAME} + NAMES lib${LIB_NAME}.a + PATHS ${BORINGSSL_LIB} + NO_DEFAULT_PATH) + ENDIF() + IF(BORINGSSL_LIB_${LIB_NAME}) + MESSAGE(STATUS "Found ${BORINGSSL_LIB} library: ${BORINGSSL_LIB_${LIB_NAME}}") + ELSE() + MESSAGE(STATUS "${BORINGSSL_LIB} library not found") + ENDIF() + ENDFOREACH() +#endif + +ENDIF() + +SET(CMAKE_INCLUDE_CURRENT_DIR ON) +INCLUDE_DIRECTORIES(include src/lshpack) IF(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "Darwin") # Find libevent on FreeBSD: - INCLUDE_DIRECTORIES( /usr/local/include ) - LINK_DIRECTORIES( /usr/local/lib ) + include_directories( /usr/local/include ) + link_directories( /usr/local/lib ) ENDIF() # Find zlib and libevent header files and library files @@ -157,8 +189,8 @@ ELSE() MESSAGE(STATUS "libevent not found") ENDIF() -SET(CMAKE_INCLUDE_CURRENT_DIR ON) -include_directories( include src/lshpack) + +SET(LIBS lsquic ${EVENT_LIB} ${BORINGSSL_LIB_ssl} ${BORINGSSL_LIB_crypto} ${ZLIB_LIB} ${LIBS}) IF (NOT MSVC) add_executable(http_client @@ -166,8 +198,7 @@ add_executable(http_client test/prog.c test/test_common.c ) -TARGET_LINK_LIBRARIES(http_client lsquic ${EVENT_LIB} pthread - ${BORINGSSL_LIB_ssl} ${BORINGSSL_LIB_crypto} ${LIBS} ${ZLIB_LIB} m) +LIST(APPEND LIBS pthread m) #MSVC ELSE() @@ -178,19 +209,12 @@ add_executable(http_client wincompat/getopt.c wincompat/getopt1.c ) - -target_link_libraries(http_client - lsquic - ${EVENT_LIB} - ${ZLIB_LIB} - ${BORINGSSL_LIB_ssl} - ${BORINGSSL_LIB_crypto} - ws2_32 - ${LIBS} ) +LIST(APPEND LIBS ws2_32) ENDIF() -#target_link_libraries(http_client lsquic event pthread libssl.a libcrypto.a ${LIBS} z m) +TARGET_LINK_LIBRARIES(http_client ${LIBS}) + add_subdirectory(src) diff --git a/include/lsquic.h b/include/lsquic.h index 3740d53..bc04f19 100644 --- a/include/lsquic.h +++ b/include/lsquic.h @@ -24,7 +24,7 @@ extern "C" { #endif #define LSQUIC_MAJOR_VERSION 1 -#define LSQUIC_MINOR_VERSION 18 +#define LSQUIC_MINOR_VERSION 19 #define LSQUIC_PATCH_VERSION 0 /** @@ -137,6 +137,22 @@ enum lsquic_version */ #define LSQUIC_FORCED_TCID0_VERSIONS (1 << LSQVER_044) +enum lsquic_hsk_status +{ + /** + * The handshake failed. + */ + LSQ_HSK_FAIL, + /** + * The handshake succeeded without 0-RTT. + */ + LSQ_HSK_OK, + /** + * The handshake succeeded with 0-RTT. + */ + LSQ_HSK_0RTT_OK, +}; + /** * @struct lsquic_stream_if * @brief The definition of callback functions call by lsquic_stream to @@ -178,7 +194,7 @@ struct lsquic_stream_if { * * This callback is optional. */ - void (*on_hsk_done)(lsquic_conn_t *c, int ok); + void (*on_hsk_done)(lsquic_conn_t *c, enum lsquic_hsk_status s); }; /** @@ -650,7 +666,8 @@ lsquic_conn_t * lsquic_engine_connect (lsquic_engine_t *, const struct sockaddr *local_sa, const struct sockaddr *peer_sa, void *peer_ctx, lsquic_conn_ctx_t *conn_ctx, - const char *hostname, unsigned short max_packet_size); + const char *hostname, unsigned short max_packet_size, + const unsigned char *zero_rtt, size_t zero_rtt_len); /** * Pass incoming packet to the QUIC engine. This function can be called @@ -835,6 +852,14 @@ int lsquic_stream_close(lsquic_stream_t *s); struct stack_st_X509 * lsquic_conn_get_server_cert_chain (lsquic_conn_t *); +/** + * Get server config zero_rtt from the encryption session. + * Returns the number of bytes written to the zero_rtt. + */ +ssize_t +lsquic_conn_get_zero_rtt(const lsquic_conn_t *, + unsigned char *zero_rtt, size_t zero_rtt_len); + /** Returns ID of the stream */ uint32_t lsquic_stream_id (const lsquic_stream_t *s); diff --git a/src/liblsquic/lsquic_chsk_stream.c b/src/liblsquic/lsquic_chsk_stream.c index 777d4a4..9013e92 100644 --- a/src/liblsquic/lsquic_chsk_stream.c +++ b/src/liblsquic/lsquic_chsk_stream.c @@ -47,6 +47,7 @@ hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh) struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh; ssize_t nread; int s; + enum lsquic_hsk_status status; if (!c_hsk->buf_in) { @@ -95,7 +96,7 @@ hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh) lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in); c_hsk->buf_in = NULL; lsquic_stream_wantread(stream, 0); - c_hsk->lconn->cn_if->ci_handshake_failed(c_hsk->lconn); + c_hsk->lconn->cn_if->ci_hsk_done(c_hsk->lconn, LSQ_HSK_FAIL); lsquic_conn_close(c_hsk->lconn); } break; @@ -106,7 +107,9 @@ hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh) if (c_hsk->lconn->cn_esf->esf_is_hsk_done(c_hsk->lconn->cn_enc_session)) { LSQ_DEBUG("handshake is successful, inform connection"); - c_hsk->lconn->cn_if->ci_handshake_ok(c_hsk->lconn); + status = (c_hsk->lconn->cn_esf->esf_did_zero_rtt_succeed( + c_hsk->lconn->cn_enc_session)) ? LSQ_HSK_0RTT_OK : LSQ_HSK_OK; + c_hsk->lconn->cn_if->ci_hsk_done(c_hsk->lconn, status); } else { @@ -122,7 +125,7 @@ hsk_client_on_read (lsquic_stream_t *stream, struct lsquic_stream_ctx *sh) LSQ_INFO("lsquic_enc_session_handle_chlo_reply returned an error"); c_hsk->buf_in = NULL; lsquic_stream_wantread(stream, 0); - c_hsk->lconn->cn_if->ci_handshake_failed(c_hsk->lconn); + c_hsk->lconn->cn_if->ci_hsk_done(c_hsk->lconn, LSQ_HSK_FAIL); lsquic_conn_close(c_hsk->lconn); break; } diff --git a/src/liblsquic/lsquic_conn.c b/src/liblsquic/lsquic_conn.c index 494f9b9..6a2e91d 100644 --- a/src/liblsquic/lsquic_conn.c +++ b/src/liblsquic/lsquic_conn.c @@ -150,3 +150,16 @@ lsquic_conn_get_server_cert_chain (struct lsquic_conn *lconn) else return NULL; } + + +ssize_t +lsquic_conn_get_zero_rtt(const lsquic_conn_t *lconn, + unsigned char *zero_rtt, size_t zero_rtt_len) +{ + ssize_t ret = -1; + if (lconn->cn_enc_session && (lconn->cn_flags & LSCONN_VER_SET)) + ret = lconn->cn_esf->esf_get_zero_rtt(lconn->cn_enc_session, + lconn->cn_version, + zero_rtt, zero_rtt_len); + return ret; +} diff --git a/src/liblsquic/lsquic_conn.h b/src/liblsquic/lsquic_conn.h index 091ef70..ea04dc7 100644 --- a/src/liblsquic/lsquic_conn.h +++ b/src/liblsquic/lsquic_conn.h @@ -73,10 +73,7 @@ struct conn_iface (*ci_packet_not_sent) (struct lsquic_conn *, struct lsquic_packet_out *); void - (*ci_handshake_ok) (struct lsquic_conn *); - - void - (*ci_handshake_failed) (struct lsquic_conn *); + (*ci_hsk_done) (struct lsquic_conn *, enum lsquic_hsk_status); void (*ci_destroy) (struct lsquic_conn *); diff --git a/src/liblsquic/lsquic_engine.c b/src/liblsquic/lsquic_engine.c index 9446dbc..07060ec 100644 --- a/src/liblsquic/lsquic_engine.c +++ b/src/liblsquic/lsquic_engine.c @@ -453,7 +453,8 @@ maybe_grow_conn_heaps (struct lsquic_engine *engine) static lsquic_conn_t * new_full_conn_client (lsquic_engine_t *engine, const char *hostname, - unsigned short max_packet_size) + unsigned short max_packet_size, const unsigned char *zero_rtt, + size_t zero_rtt_len) { lsquic_conn_t *conn; unsigned flags; @@ -461,7 +462,8 @@ new_full_conn_client (lsquic_engine_t *engine, const char *hostname, return NULL; flags = engine->flags & (ENG_SERVER|ENG_HTTP); conn = full_conn_client_new(&engine->pub, engine->stream_if, - engine->stream_if_ctx, flags, hostname, max_packet_size); + engine->stream_if_ctx, flags, hostname, + max_packet_size, zero_rtt, zero_rtt_len); if (!conn) return NULL; ++engine->n_conns; @@ -662,7 +664,8 @@ lsquic_conn_t * lsquic_engine_connect (lsquic_engine_t *engine, const struct sockaddr *local_sa, const struct sockaddr *peer_sa, void *peer_ctx, lsquic_conn_ctx_t *conn_ctx, - const char *hostname, unsigned short max_packet_size) + const char *hostname, unsigned short max_packet_size, + const unsigned char *zero_rtt, size_t zero_rtt_len) { lsquic_conn_t *conn; ENGINE_IN(engine); @@ -693,7 +696,8 @@ lsquic_engine_connect (lsquic_engine_t *engine, const struct sockaddr *local_sa, } } - conn = new_full_conn_client(engine, hostname, max_packet_size); + conn = new_full_conn_client(engine, hostname, max_packet_size, + zero_rtt, zero_rtt_len); if (!conn) goto err; lsquic_conn_record_sockaddr(conn, local_sa, peer_sa); diff --git a/src/liblsquic/lsquic_frame_writer.c b/src/liblsquic/lsquic_frame_writer.c index 4444124..4377442 100644 --- a/src/liblsquic/lsquic_frame_writer.c +++ b/src/liblsquic/lsquic_frame_writer.c @@ -418,14 +418,16 @@ check_headers_size (const struct lsquic_frame_writer *fw, headers_sz = calc_headers_size(headers); if (extra_headers) headers_sz += calc_headers_size(extra_headers); - if (headers_sz > fw->fw_max_header_list_sz) + + if (headers_sz <= fw->fw_max_header_list_sz) + return 0; + else { LSQ_INFO("Headers size %u is larger than max allowed (%u)", headers_sz, fw->fw_max_header_list_sz); errno = EMSGSIZE; return -1; } - return 0; } diff --git a/src/liblsquic/lsquic_full_conn.c b/src/liblsquic/lsquic_full_conn.c index aca9467..39a7389 100644 --- a/src/liblsquic/lsquic_full_conn.c +++ b/src/liblsquic/lsquic_full_conn.c @@ -435,7 +435,6 @@ send_smhl (const struct full_conn *conn) { uint32_t smhl; return conn->fc_conn.cn_enc_session - && (conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE) && 0 == conn->fc_conn.cn_esf->esf_get_peer_setting( conn->fc_conn.cn_enc_session, QTAG_SMHL, &smhl) && 1 == smhl; @@ -509,8 +508,6 @@ apply_peer_settings (struct full_conn *conn) LSQ_DEBUG("peer settings: CFCW: %u; SFCW: %u; MIDS: %u", cfcw, sfcw, mids); conn_on_peer_config(conn, cfcw, sfcw, mids); - if (conn->fc_flags & FC_HTTP) - maybe_send_settings(conn); return 0; } @@ -640,7 +637,8 @@ struct lsquic_conn * full_conn_client_new (struct lsquic_engine_public *enpub, const struct lsquic_stream_if *stream_if, void *stream_if_ctx, unsigned flags, - const char *hostname, unsigned short max_packet_size) + const char *hostname, unsigned short max_packet_size, + const unsigned char *zero_rtt, size_t zero_rtt_len) { struct full_conn *conn; enum lsquic_version version; @@ -656,7 +654,8 @@ full_conn_client_new (struct lsquic_engine_public *enpub, return NULL; conn->fc_conn.cn_esf = esf; conn->fc_conn.cn_enc_session = - conn->fc_conn.cn_esf->esf_create_client(hostname, cid, conn->fc_enpub); + conn->fc_conn.cn_esf->esf_create_client(hostname, cid, conn->fc_enpub, + zero_rtt, zero_rtt_len); if (!conn->fc_conn.cn_enc_session) { LSQ_WARN("could not create enc session: %s", strerror(errno)); @@ -2985,7 +2984,9 @@ full_conn_ci_tick (lsquic_conn_t *lconn, lsquic_time_t now) } lsquic_send_ctl_set_buffer_stream_packets(&conn->fc_send_ctl, 0); - if (!(conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)) + if (!(conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE) && + !conn->fc_conn.cn_esf->esf_is_zero_rtt_enabled( + conn->fc_conn.cn_enc_session)) { process_hsk_stream_write_events(conn); goto end_write; @@ -3145,29 +3146,30 @@ full_conn_ci_packet_not_sent (lsquic_conn_t *lconn, lsquic_packet_out_t *packet_ static void -full_conn_ci_handshake_ok (lsquic_conn_t *lconn) +full_conn_ci_hsk_done (lsquic_conn_t *lconn, enum lsquic_hsk_status status) { struct full_conn *conn = (struct full_conn *) lconn; - LSQ_DEBUG("handshake reportedly done"); lsquic_alarmset_unset(&conn->fc_alset, AL_HANDSHAKE); - if (0 == apply_peer_settings(conn)) - lconn->cn_flags |= LSCONN_HANDSHAKE_DONE; - else - conn->fc_flags |= FC_ERROR; + switch (status) + { + case LSQ_HSK_FAIL: + conn->fc_flags |= FC_HSK_FAILED; + break; + case LSQ_HSK_OK: + case LSQ_HSK_0RTT_OK: + if (0 == apply_peer_settings(conn)) + { + if (conn->fc_flags & FC_HTTP) + maybe_send_settings(conn); + lconn->cn_flags |= LSCONN_HANDSHAKE_DONE; + } + else + conn->fc_flags |= FC_ERROR; + break; + } if (conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_hsk_done) - conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_hsk_done(lconn, 1); -} - - -static void -full_conn_ci_handshake_failed (lsquic_conn_t *lconn) -{ - struct full_conn *conn = (struct full_conn *) lconn; - LSQ_DEBUG("handshake failed"); - lsquic_alarmset_unset(&conn->fc_alset, AL_HANDSHAKE); - conn->fc_flags |= FC_HSK_FAILED; - if (conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_hsk_done) - conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_hsk_done(lconn, 0); + conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_hsk_done(lconn, + status); } @@ -3539,7 +3541,9 @@ full_conn_ci_is_tickable (lsquic_conn_t *lconn) return 1; if (!TAILQ_EMPTY(&conn->fc_pub.sending_streams)) return 1; - if (conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE) + if ((conn->fc_conn.cn_flags & LSCONN_HANDSHAKE_DONE) || + conn->fc_conn.cn_esf->esf_is_zero_rtt_enabled( + conn->fc_conn.cn_enc_session)) { TAILQ_FOREACH(stream, &conn->fc_pub.write_streams, next_write_stream) @@ -3617,8 +3621,7 @@ static const struct conn_iface full_conn_iface = { #if LSQUIC_CONN_STATS .ci_get_stats = full_conn_ci_get_stats, #endif - .ci_handshake_failed = full_conn_ci_handshake_failed, - .ci_handshake_ok = full_conn_ci_handshake_ok, + .ci_hsk_done = full_conn_ci_hsk_done, .ci_is_tickable = full_conn_ci_is_tickable, .ci_next_packet_to_send = full_conn_ci_next_packet_to_send, .ci_next_tick_time = full_conn_ci_next_tick_time, diff --git a/src/liblsquic/lsquic_full_conn.h b/src/liblsquic/lsquic_full_conn.h index c8b2aa0..c50781d 100644 --- a/src/liblsquic/lsquic_full_conn.h +++ b/src/liblsquic/lsquic_full_conn.h @@ -11,7 +11,8 @@ full_conn_client_new (struct lsquic_engine_public *, const struct lsquic_stream_if *, void *stream_if_ctx, unsigned flags /* Only FC_SERVER and FC_HTTP */, - const char *hostname, unsigned short max_packet_size); + const char *hostname, unsigned short max_packet_size, + const unsigned char *zero_rtt, size_t zero_rtt_len); void full_conn_client_call_on_new (struct lsquic_conn *); diff --git a/src/liblsquic/lsquic_handshake.c b/src/liblsquic/lsquic_handshake.c index e79ab2e..89c16ba 100644 --- a/src/liblsquic/lsquic_handshake.c +++ b/src/liblsquic/lsquic_handshake.c @@ -115,6 +115,9 @@ typedef struct hs_ctx_st struct lsquic_enc_session { enum handshake_state hsk_state; + enum { + ES_RECV_REJ = 1 << 2, + } es_flags; uint8_t have_key; /* 0, no 1, I, 2, D, 3, F */ uint8_t peer_have_final_key; @@ -138,6 +141,7 @@ struct lsquic_enc_session hs_ctx_t hs_ctx; lsquic_session_cache_info_t *info; + c_cert_item_t *cert_item; SSL_CTX * ssl_ctx; const struct lsquic_engine_public *enpub; struct lsquic_str * cert_ptr; /* pointer to the leaf cert of the server, not real copy */ @@ -152,29 +156,10 @@ struct lsquic_enc_session }; -/*** - * client side, it will store the domain/certs as cache cert - */ -static struct lsquic_hash *s_cached_client_certs; - -/** - * client side will save the session_info for next time 0rtt - */ -static struct lsquic_hash *s_cached_client_session_infos; - -/* save to hash table */ -static lsquic_session_cache_info_t *retrieve_session_info_entry(const char *key); -static void remove_expire_session_info_entry(); -static void remove_session_info_entry(struct lsquic_str *key); - -static void free_info (lsquic_session_cache_info_t *); - /* client */ -static cert_hash_item_t *make_cert_hash_item(struct lsquic_str *domain, struct lsquic_str **certs, int count); -static int c_insert_certs(cert_hash_item_t *item); -static void c_erase_certs(struct lsquic_hash_elem *el); -static void c_free_cert_hash_item (cert_hash_item_t *item); +static c_cert_item_t *make_c_cert_item(struct lsquic_str **certs, int count); +static void free_c_cert_item(c_cert_item_t *item); static int get_tag_val_u32 (unsigned char *v, int len, uint32_t *val); static int init_hs_hash_tables(int flags); @@ -207,42 +192,9 @@ lsquic_handshake_init(int flags) } -static void -cleanup_hs_hash_tables (void) -{ - struct lsquic_hash_elem *el; - - if (s_cached_client_session_infos) - { - for (el = lsquic_hash_first(s_cached_client_session_infos); el; - el = lsquic_hash_next(s_cached_client_session_infos)) - { - lsquic_session_cache_info_t *entry = lsquic_hashelem_getdata(el); - free_info(entry); - } - lsquic_hash_destroy(s_cached_client_session_infos); - s_cached_client_session_infos = NULL; - } - - if (s_cached_client_certs) - { - for (el = lsquic_hash_first(s_cached_client_certs); el; - el = lsquic_hash_next(s_cached_client_certs)) - { - cert_hash_item_t *item = lsquic_hashelem_getdata(el); - c_free_cert_hash_item(item); - } - lsquic_hash_destroy(s_cached_client_certs); - s_cached_client_certs = NULL; - } - -} - - static void lsquic_handshake_cleanup (void) { - cleanup_hs_hash_tables(); lsquic_crt_cleanup(); } @@ -250,63 +202,26 @@ lsquic_handshake_cleanup (void) /* return -1 for fail, 0 OK*/ static int init_hs_hash_tables(int flags) { - if (flags & LSQUIC_GLOBAL_CLIENT) - { - s_cached_client_session_infos = lsquic_hash_create(); - if (!s_cached_client_session_infos) - return -1; - - s_cached_client_certs = lsquic_hash_create(); - if (!s_cached_client_certs) - return -1; - } return 0; } /* client */ -struct lsquic_hash_elem * -c_get_certs_elem (const lsquic_str_t *domain) -{ - if (!s_cached_client_certs) - return NULL; - - return lsquic_hash_find(s_cached_client_certs, lsquic_str_cstr(domain), - lsquic_str_len(domain)); -} - - -/* client */ -cert_hash_item_t * -c_find_certs (const lsquic_str_t *domain) -{ - struct lsquic_hash_elem *el = c_get_certs_elem(domain); - - if (el == NULL) - return NULL; - - return lsquic_hashelem_getdata(el); -} - - -/* client */ -/* certs is an array of lsquic_str_t * */ -static cert_hash_item_t * -make_cert_hash_item (lsquic_str_t *domain, lsquic_str_t **certs, int count) +static c_cert_item_t * +make_c_cert_item (lsquic_str_t **certs, int count) { int i; uint64_t hash; - cert_hash_item_t *item = (cert_hash_item_t *)malloc(sizeof(cert_hash_item_t)); + c_cert_item_t *item = (c_cert_item_t *)malloc(sizeof(c_cert_item_t)); item->crts = (lsquic_str_t *)malloc(count * sizeof(lsquic_str_t)); - item->domain = lsquic_str_new(NULL, 0); item->hashs = lsquic_str_new(NULL, 0); - lsquic_str_copy(item->domain, domain); item->count = count; - for(i=0; icrts[i], certs[i]); - hash = fnv1a_64((const uint8_t *)lsquic_str_cstr(certs[i]), lsquic_str_len(certs[i])); + hash = fnv1a_64((const uint8_t *)lsquic_str_cstr(certs[i]), + lsquic_str_len(certs[i])); lsquic_str_append(item->hashs, (char *)&hash, 8); } return item; @@ -315,13 +230,12 @@ make_cert_hash_item (lsquic_str_t *domain, lsquic_str_t **certs, int count) /* client */ static void -c_free_cert_hash_item (cert_hash_item_t *item) +free_c_cert_item (c_cert_item_t *item) { int i; if (item) { lsquic_str_delete(item->hashs); - lsquic_str_delete(item->domain); for(i=0; icount; ++i) lsquic_str_d(&item->crts[i]); free(item->crts); @@ -330,112 +244,134 @@ c_free_cert_hash_item (cert_hash_item_t *item) } -/* client */ -static int -c_insert_certs (cert_hash_item_t *item) +enum rtt_deserialize_return_type { - if (lsquic_hash_insert(s_cached_client_certs, - lsquic_str_cstr(item->domain), - lsquic_str_len(item->domain), item) == NULL) - return -1; - else - return 0; -} + RTT_DESERIALIZE_OK = 0, + RTT_DESERIALIZE_BAD_QUIC_VER = 1, + RTT_DESERIALIZE_BAD_SERIAL_VER = 2, + RTT_DESERIALIZE_BAD_CERT_SIZE = 3, +}; +#define RTT_SERIALIZER_VERSION (1 << 0) -/* client */ static void -c_erase_certs (struct lsquic_hash_elem *el) +lsquic_enc_session_serialize_zero_rtt(struct lsquic_zero_rtt_storage *storage, + enum lsquic_version version, + const lsquic_session_cache_info_t *info, + const c_cert_item_t *cert_item) { - if (s_cached_client_certs && el) - lsquic_hash_erase(s_cached_client_certs, el); -} - - -static int save_session_info_entry(lsquic_str_t *key, lsquic_session_cache_info_t *entry) -{ - lsquic_str_setto(&entry->sni_key, lsquic_str_cstr(key), lsquic_str_len(key)); - if (lsquic_hash_insert(s_cached_client_session_infos, - lsquic_str_cstr(&entry->sni_key), - lsquic_str_len(&entry->sni_key), entry) == NULL) + uint32_t i; + uint8_t *next_cert; + struct lsquic_cert_storage *cert_storage; + /* + * assign versions + */ + storage->quic_version_tag = lsquic_ver2tag(version); + storage->serializer_version = RTT_SERIALIZER_VERSION; + /* + * server config + */ + storage->ver = info->ver; + storage->aead = info->aead; + storage->kexs = info->kexs; + storage->pdmd = info->pdmd; + storage->orbt = info->orbt; + storage->expy = info->expy; + storage->sstk_len = lsquic_str_len(&info->sstk); + storage->scfg_len = lsquic_str_len(&info->scfg); + storage->scfg_flag = info->scfg_flag; + memcpy(storage->sstk, lsquic_str_buf(&info->sstk), storage->sstk_len); + memcpy(storage->scfg, lsquic_str_buf(&info->scfg), storage->scfg_len); + memcpy(storage->sscid, &info->sscid, SCID_LENGTH); + memcpy(storage->spubs, &info->spubs, MAX_SPUBS_LENGTH); + /* + * certificate chain + */ + storage->cert_count = (uint32_t)cert_item->count; + next_cert = (uint8_t *)&storage->cert_storage; + for (i = 0; i < storage->cert_count; i++) { - lsquic_str_d(&entry->sni_key); - return -1; - } - else - return 0; -} - - -/* If entry updated and need to remove cached entry */ -static void -remove_session_info_entry (lsquic_str_t *key) -{ - lsquic_session_cache_info_t *entry; - struct lsquic_hash_elem *el; - el = lsquic_hash_find(s_cached_client_session_infos, - lsquic_str_cstr(key), lsquic_str_len(key)); - if (el) - { - entry = lsquic_hashelem_getdata(el); - lsquic_str_d(&entry->sni_key); - lsquic_hash_erase(s_cached_client_session_infos, el); + cert_storage = (struct lsquic_cert_storage *)next_cert; + cert_storage->len = lsquic_str_len(&cert_item->crts[i]); + memcpy(cert_storage->data, lsquic_str_buf(&cert_item->crts[i]), + cert_storage->len); + next_cert += sizeof(struct lsquic_cert_storage) + cert_storage->len; } } -/* client */ -static lsquic_session_cache_info_t * -retrieve_session_info_entry (const char *key) +#define CHECK_SPACE(need, start, end) \ + do { if ((intptr_t) (need) > ( (intptr_t) (end) - (intptr_t) (start))) \ + { return RTT_DESERIALIZE_BAD_CERT_SIZE; } \ + } while (0) \ + +static enum rtt_deserialize_return_type +lsquic_enc_session_deserialize_zero_rtt( + const struct lsquic_zero_rtt_storage *storage, + size_t storage_size, + const struct lsquic_engine_settings *settings, + lsquic_session_cache_info_t *info, + c_cert_item_t *cert_item) { - lsquic_session_cache_info_t *entry; - struct lsquic_hash_elem *el; - - if (!s_cached_client_session_infos) - return NULL; - - if (!key) - return NULL; - - el = lsquic_hash_find(s_cached_client_session_infos, key, strlen(key)); - if (el == NULL) - return NULL; - - entry = lsquic_hashelem_getdata(el); - LSQ_DEBUG("[QUIC]retrieve_session_info_entry find cached session info %p.\n", entry); - return entry; -} - - -/* call it in timer() */ -#if __GNUC__ -__attribute__((unused)) -#endif -static void -remove_expire_session_info_entry (void) -{ - time_t tm = time(NULL); - struct lsquic_hash_elem *el; - - for (el = lsquic_hash_first(s_cached_client_session_infos); el; - el = lsquic_hash_next(s_cached_client_session_infos)) + uint32_t i, len; + uint64_t hash; + uint8_t *next_cert; + struct lsquic_cert_storage *cert_storage; + void *storage_end = (uint8_t *)storage + storage_size; + /* + * check versions + */ + if (lsquic_tag2ver(storage->quic_version_tag) & ~settings->es_versions) + return RTT_DESERIALIZE_BAD_QUIC_VER; + if (storage->serializer_version != RTT_SERIALIZER_VERSION) + return RTT_DESERIALIZE_BAD_SERIAL_VER; + /* + * server config + */ + info->ver = storage->ver; + info->aead = storage->aead; + info->kexs = storage->kexs; + info->pdmd = storage->pdmd; + info->orbt = storage->orbt; + info->expy = storage->expy; + info->scfg_flag = storage->scfg_flag; + lsquic_str_setto(&info->sstk, storage->sstk, storage->sstk_len); + lsquic_str_setto(&info->scfg, storage->scfg, storage->scfg_len); + memcpy(&info->sscid, storage->sscid, SCID_LENGTH); + memcpy(&info->spubs, storage->spubs, MAX_SPUBS_LENGTH); + /* + * certificate chain + */ + cert_item->count = storage->cert_count; + cert_item->crts = malloc(cert_item->count * sizeof(lsquic_str_t)); + cert_item->hashs = lsquic_str_new(NULL, 0); + next_cert = (uint8_t *)storage->cert_storage; + for (i = 0; i < storage->cert_count; i++) { - lsquic_session_cache_info_t *entry = lsquic_hashelem_getdata(el); - if ((uint64_t)tm > entry->expy) - { - free_info(entry); - lsquic_hash_erase(s_cached_client_session_infos, el); - } + CHECK_SPACE(sizeof(struct lsquic_cert_storage), next_cert, storage_end); + cert_storage = (struct lsquic_cert_storage *)next_cert; + memcpy(&len, cert_storage, sizeof(len)); + CHECK_SPACE(len, cert_storage->data, storage_end); + lsquic_str_prealloc(&cert_item->crts[i], len); + lsquic_str_setlen(&cert_item->crts[i], len); + memcpy(lsquic_str_buf(&cert_item->crts[i]), cert_storage->data, len); + hash = fnv1a_64((const uint8_t *)cert_storage->data, len); + lsquic_str_append(cert_item->hashs, (char *)&hash, 8); + next_cert += sizeof(struct lsquic_cert_storage) + len; } + return RTT_DESERIALIZE_OK; } static lsquic_enc_session_t * lsquic_enc_session_create_client (const char *domain, lsquic_cid_t cid, - const struct lsquic_engine_public *enpub) + const struct lsquic_engine_public *enpub, + const unsigned char *zero_rtt, size_t zero_rtt_len) { lsquic_session_cache_info_t *info; lsquic_enc_session_t *enc_session; + c_cert_item_t *item; + const struct lsquic_zero_rtt_storage *zero_rtt_storage; if (!domain) { @@ -447,19 +383,47 @@ lsquic_enc_session_create_client (const char *domain, lsquic_cid_t cid, if (!enc_session) return NULL; - info = retrieve_session_info_entry(domain); - if (info) - memcpy(enc_session->hs_ctx.pubs, info->spubs, 32); - else + /* have to allocate every time */ + info = calloc(1, sizeof(*info)); + if (!info) { - info = calloc(1, sizeof(*info)); - if (!info) - { - free(enc_session); - return NULL; - } + free(enc_session); + return NULL; } + if (zero_rtt && zero_rtt_len > sizeof(struct lsquic_zero_rtt_storage)) + { + item = calloc(1, sizeof(*item)); + if (!item) + { + free(enc_session); + free(info); + return NULL; + } + zero_rtt_storage = (const struct lsquic_zero_rtt_storage *)zero_rtt; + switch (lsquic_enc_session_deserialize_zero_rtt(zero_rtt_storage, + zero_rtt_len, + &enpub->enp_settings, + info, item)) + { + case RTT_DESERIALIZE_BAD_QUIC_VER: + LSQ_ERROR("provided zero_rtt has unsupported QUIC version"); + free(item); + break; + case RTT_DESERIALIZE_BAD_SERIAL_VER: + LSQ_ERROR("provided zero_rtt has bad serializer version"); + free(item); + break; + case RTT_DESERIALIZE_BAD_CERT_SIZE: + LSQ_ERROR("provided zero_rtt has bad cert size"); + free(item); + break; + case RTT_DESERIALIZE_OK: + memcpy(enc_session->hs_ctx.pubs, info->spubs, 32); + enc_session->cert_item = item; + break; + } + } enc_session->enpub = enpub; enc_session->cid = cid; enc_session->info = info; @@ -507,21 +471,23 @@ lsquic_enc_session_destroy (lsquic_enc_session_t *enc_session) EVP_AEAD_CTX_cleanup(enc_session->enc_ctx_f); free(enc_session->enc_ctx_f); } + if (enc_session->info) + { + lsquic_str_d(&enc_session->info->sstk); + lsquic_str_d(&enc_session->info->scfg); + lsquic_str_d(&enc_session->info->sni_key); + free(enc_session->info); + } + if (enc_session->cert_item) + { + free_c_cert_item(enc_session->cert_item); + enc_session->cert_item = NULL; + } free(enc_session); } -static void -free_info (lsquic_session_cache_info_t *info) -{ - lsquic_str_d(&info->sstk); - lsquic_str_d(&info->scfg); - lsquic_str_d(&info->sni_key); - free(info); -} - - static int get_hs_state(lsquic_enc_session_t *enc_session) { return enc_session->hsk_state; @@ -663,11 +629,9 @@ static int parse_hs_data (lsquic_enc_session_t *enc_session, uint32_t tag, break; case QTAG_STK: - if (lsquic_str_len(&enc_session->info->sstk) > 0) - remove_session_info_entry(&enc_session->info->sstk); lsquic_str_setto(&enc_session->info->sstk, val, len); - ESHIST_APPEND(enc_session, ESHE_SET_STK); - break; + ESHIST_APPEND(enc_session, ESHE_SET_STK); + break; case QTAG_SCID: if (len != SCID_LENGTH) @@ -952,8 +916,7 @@ lsquic_enc_session_gen_chlo (lsquic_enc_session_t *enc_session, const lsquic_str_t *const ccs = get_common_certs_hash(); const struct lsquic_engine_settings *const settings = &enc_session->enpub->enp_settings; - cert_hash_item_t *const cached_certs_item = - c_find_certs(&enc_session->hs_ctx.sni); + c_cert_item_t *const cert_item = enc_session->cert_item; unsigned char pub_key[32]; size_t ua_len; uint32_t opts[1]; /* Only NSTP is supported for now */ @@ -1006,10 +969,10 @@ lsquic_enc_session_gen_chlo (lsquic_enc_session_t *enc_session, MSG_LEN_ADD(msg_len, lsquic_str_len(&enc_session->hs_ctx.sni)); ++n_tags; /* SNI */ MSG_LEN_ADD(msg_len, lsquic_str_len(ccs)); ++n_tags; /* CCS */ - if (cached_certs_item) + if (cert_item) { - enc_session->cert_ptr = &cached_certs_item->crts[0]; - MSG_LEN_ADD(msg_len, lsquic_str_len(cached_certs_item->hashs)); + enc_session->cert_ptr = &cert_item->crts[0]; + MSG_LEN_ADD(msg_len, lsquic_str_len(cert_item->hashs)); ++n_tags; /* CCRT */ MSG_LEN_ADD(msg_len, 8); ++n_tags; /* XLCT */ } @@ -1083,14 +1046,14 @@ lsquic_enc_session_gen_chlo (lsquic_enc_session_t *enc_session, MW_WRITE_UINT32(&mw, QTAG_MIDS, settings->es_max_streams_in); MW_WRITE_UINT32(&mw, QTAG_SCLS, settings->es_silent_close); MW_WRITE_UINT32(&mw, QTAG_KEXS, settings->es_kexs); - if (cached_certs_item) - MW_WRITE_BUFFER(&mw, QTAG_XLCT, lsquic_str_buf(cached_certs_item->hashs), 8); + if (cert_item) + MW_WRITE_BUFFER(&mw, QTAG_XLCT, lsquic_str_buf(cert_item->hashs), 8); /* CSCT is empty on purpose (retained from original code) */ MW_WRITE_TABLE_ENTRY(&mw, QTAG_CSCT, 0); if (n_opts > 0) MW_WRITE_BUFFER(&mw, QTAG_COPT, opts, n_opts * sizeof(opts[0])); - if (cached_certs_item) - MW_WRITE_LS_STR(&mw, QTAG_CCRT, cached_certs_item->hashs); + if (cert_item) + MW_WRITE_LS_STR(&mw, QTAG_CCRT, cert_item->hashs); MW_WRITE_UINT32(&mw, QTAG_CFCW, settings->es_cfcw); MW_WRITE_UINT32(&mw, QTAG_SFCW, settings->es_sfcw); MW_END(&mw); @@ -1337,16 +1300,16 @@ static int determine_keys(lsquic_enc_session_t *enc_session) /* 0 Match */ -static int cached_certs_match(cert_hash_item_t *cached_certs_item, lsquic_str_t **certs, - int certs_count) +static int cached_certs_match(c_cert_item_t *item, + lsquic_str_t **certs, int count) { int i; - if (!cached_certs_item || cached_certs_item->count != certs_count) + if (!item || item->count != count) return -1; - for (i=0; icrts[i]) != 0) + if (lsquic_str_bcmp(certs[i], &item->crts[i]) != 0) return -1; } @@ -1383,12 +1346,7 @@ lsquic_enc_session_handle_chlo_reply (lsquic_enc_session_t *enc_session, uint32_t head_tag; int ret; lsquic_session_cache_info_t *info = enc_session->info; - hs_ctx_t * hs_ctx = &enc_session->hs_ctx; - cert_hash_item_t *cached_certs_item = NULL; - struct lsquic_hash_elem *el = c_get_certs_elem(&hs_ctx->sni); - - if (el) - cached_certs_item = lsquic_hashelem_getdata(el); + c_cert_item_t *cert_item = enc_session->cert_item; /* FIXME get the number first */ lsquic_str_t **out_certs = NULL; @@ -1407,7 +1365,10 @@ lsquic_enc_session_handle_chlo_reply (lsquic_enc_session_t *enc_session, } if (head_tag == QTAG_SREJ || head_tag == QTAG_REJ) + { enc_session->hsk_state = HSK_CHLO_REJ; + enc_session->es_flags |= ES_RECV_REJ; + } else if(head_tag == QTAG_SHLO) { enc_session->hsk_state = HSK_COMPLETED; @@ -1428,7 +1389,7 @@ lsquic_enc_session_handle_chlo_reply (lsquic_enc_session_t *enc_session, out_certs_count = get_certs_count(&enc_session->hs_ctx.crt); if (out_certs_count > 0) { - out_certs = (lsquic_str_t **)malloc(out_certs_count * sizeof(lsquic_str_t *)); + out_certs = malloc(out_certs_count * sizeof(lsquic_str_t *)); if (!out_certs) { ret = -1; @@ -1440,30 +1401,22 @@ lsquic_enc_session_handle_chlo_reply (lsquic_enc_session_t *enc_session, ret = handle_chlo_reply_verify_prof(enc_session, out_certs, &out_certs_count, - (cached_certs_item ? cached_certs_item->crts : NULL), - (cached_certs_item ? cached_certs_item->count : 0)); + (cert_item ? cert_item->crts : NULL), + (cert_item ? cert_item->count : 0)); if (ret == 0) { if (out_certs_count > 0) { - if (cached_certs_item && - cached_certs_match(cached_certs_item, out_certs, out_certs_count) == 0) - ; - else + if (cached_certs_match(cert_item, out_certs, + out_certs_count) != 0) { - if (el) - c_erase_certs(el); - if (cached_certs_item) - c_free_cert_hash_item(cached_certs_item); - - cached_certs_item = make_cert_hash_item(&hs_ctx->sni, - out_certs, out_certs_count); - c_insert_certs(cached_certs_item); + cert_item = make_c_cert_item(out_certs, + out_certs_count); + enc_session->cert_item = cert_item; + enc_session->cert_ptr = &cert_item->crts[0]; } - enc_session->cert_ptr = &cached_certs_item->crts[0]; } } - for (i=0; ihsk_state == HSK_COMPLETED) { - if (!lsquic_str_buf(&info->sni_key)) - save_session_info_entry(&enc_session->hs_ctx.sni, info); ret = determine_keys(enc_session ); /* FIXME: check ret */ enc_session->have_key = 3; @@ -1894,15 +1845,36 @@ lsquic_enc_session_verify_reset_token (lsquic_enc_session_t *enc_session, } +static int +lsquic_enc_session_did_zero_rtt_succeed (const lsquic_enc_session_t *enc_session) +{ + return !(enc_session->es_flags & ES_RECV_REJ); +} + + +static int +lsquic_enc_session_is_zero_rtt_enabled (const lsquic_enc_session_t *enc_session) +{ + return enc_session->info && enc_session->cert_item; +} + + +static c_cert_item_t * +lsquic_enc_session_get_cert_item (const lsquic_enc_session_t *enc_session) +{ + return enc_session->cert_item; +} + + static STACK_OF(X509) * lsquic_enc_session_get_server_cert_chain (lsquic_enc_session_t *enc_session) { - const struct cert_hash_item_st *item; + const struct c_cert_item_st *item; STACK_OF(X509) *chain; X509 *cert; int i; - item = c_find_certs(&enc_session->hs_ctx.sni); + item = enc_session->cert_item; if (!item) { LSQ_WARN("could not find certificates for `%.*s'", @@ -1929,6 +1901,37 @@ lsquic_enc_session_get_server_cert_chain (lsquic_enc_session_t *enc_session) } +ssize_t +lsquic_enc_session_get_zero_rtt (lsquic_enc_session_t *enc_session, + enum lsquic_version version, + void *buf, size_t len) +{ + int i; + size_t sz = 0; + if (!enc_session->info || !enc_session->cert_item) + { + LSQ_DEBUG("client asked for rtt_into but it is not available"); + return 0; + } + for (i = 0; i < enc_session->cert_item->count; ++i) + { + sz += sizeof(struct lsquic_cert_storage); + sz += lsquic_str_len(&enc_session->cert_item->crts[i]); + } + sz += sizeof(struct lsquic_zero_rtt_storage); + if (len < sz) + { + LSQ_DEBUG("client provided buf is too small %lu < %lu", len, sz); + errno = ENOBUFS; + return -1; + } + lsquic_enc_session_serialize_zero_rtt((struct lsquic_zero_rtt_storage *)buf, + version, enc_session->info, + enc_session->cert_item); + return sz; +} + + #ifdef NDEBUG const #endif @@ -1951,7 +1954,11 @@ struct enc_session_funcs lsquic_enc_session_gquic_1 = .esf_handle_chlo_reply = lsquic_enc_session_handle_chlo_reply, .esf_mem_used = lsquic_enc_session_mem_used, .esf_verify_reset_token = lsquic_enc_session_verify_reset_token, + .esf_did_zero_rtt_succeed = lsquic_enc_session_did_zero_rtt_succeed, + .esf_is_zero_rtt_enabled = lsquic_enc_session_is_zero_rtt_enabled, + .esf_get_cert_item = lsquic_enc_session_get_cert_item, .esf_get_server_cert_chain = lsquic_enc_session_get_server_cert_chain, + .esf_get_zero_rtt = lsquic_enc_session_get_zero_rtt, }; diff --git a/src/liblsquic/lsquic_handshake.h b/src/liblsquic/lsquic_handshake.h index f0b7a4e..8179045 100644 --- a/src/liblsquic/lsquic_handshake.h +++ b/src/liblsquic/lsquic_handshake.h @@ -8,6 +8,8 @@ struct stack_st_X509; typedef struct lsquic_enc_session lsquic_enc_session_t; +#define MAX_SCFG_LENGTH 512 +#define MAX_SPUBS_LENGTH 32 #define STK_LENGTH 60 #define SNO_LENGTH 56 #define SCID_LENGTH 16 @@ -37,6 +39,14 @@ enum enc_level extern const char *const lsquic_enclev2str[]; +/* client */ +typedef struct c_cert_item_st +{ + struct lsquic_str* crts; + struct lsquic_str* hashs; + int count; +} c_cert_item_t; + /* client side need to store 0rtt info per STK */ typedef struct lsquic_session_cache_info_st { @@ -55,6 +65,33 @@ typedef struct lsquic_session_cache_info_st } lsquic_session_cache_info_t; +struct lsquic_cert_storage +{ + uint32_t len; + uint8_t data[0]; +}; + +struct lsquic_zero_rtt_storage +{ + uint32_t quic_version_tag; + uint32_t serializer_version; + uint32_t ver; + uint32_t aead; + uint32_t kexs; + uint32_t pdmd; + uint64_t orbt; + uint64_t expy; + uint64_t sstk_len; + uint64_t scfg_len; + uint64_t scfg_flag; + uint8_t sstk[STK_LENGTH]; + uint8_t scfg[MAX_SCFG_LENGTH]; + uint8_t sscid[SCID_LENGTH]; + uint8_t spubs[MAX_SPUBS_LENGTH]; + uint32_t cert_count; + struct lsquic_cert_storage cert_storage[0]; +}; + #ifndef LSQUIC_KEEP_ENC_SESS_HISTORY # ifndef NDEBUG # define LSQUIC_KEEP_ENC_SESS_HISTORY 1 @@ -120,7 +157,8 @@ struct enc_session_funcs /* Create client session */ lsquic_enc_session_t * (*esf_create_client) (const char *domain, lsquic_cid_t cid, - const struct lsquic_engine_public *); + const struct lsquic_engine_public *, + const unsigned char *, size_t); /* Generate connection ID */ lsquic_cid_t (*esf_generate_cid) (void); @@ -141,8 +179,21 @@ struct enc_session_funcs (*esf_verify_reset_token) (lsquic_enc_session_t *, const unsigned char *, size_t); + int + (*esf_did_zero_rtt_succeed) (const lsquic_enc_session_t *); + + int + (*esf_is_zero_rtt_enabled) (const lsquic_enc_session_t *); + + c_cert_item_t * + (*esf_get_cert_item) (const lsquic_enc_session_t *); + struct stack_st_X509 * (*esf_get_server_cert_chain) (lsquic_enc_session_t *); + + ssize_t + (*esf_get_zero_rtt) (lsquic_enc_session_t *, enum lsquic_version, + void *, size_t); }; extern @@ -154,14 +205,4 @@ struct enc_session_funcs lsquic_enc_session_gquic_1; #define select_esf_by_ver(ver) \ (ver ? &lsquic_enc_session_gquic_1 : &lsquic_enc_session_gquic_1) -/* client side, certs and hashs - */ -typedef struct cert_hash_item_st -{ - struct lsquic_str* domain; /*with port, such as "xyz.com:8088" as the key */ - struct lsquic_str* crts; - struct lsquic_str* hashs; - int count; -} cert_hash_item_t; - #endif diff --git a/src/liblsquic/lsquic_packet_out.c b/src/liblsquic/lsquic_packet_out.c index 7cb02f6..1f0a660 100644 --- a/src/liblsquic/lsquic_packet_out.c +++ b/src/liblsquic/lsquic_packet_out.c @@ -323,7 +323,10 @@ lsquic_packet_out_elide_reset_stream_frames (lsquic_packet_out_t *packet_out, assert(n_stream_frames); if (n_elided == n_stream_frames) + { packet_out->po_frame_types &= ~(1 << QUIC_FRAME_STREAM); + packet_out->po_flags &= ~PO_STREAM_END; + } return adj; } diff --git a/src/liblsquic/lsquic_send_ctl.c b/src/liblsquic/lsquic_send_ctl.c index 3892707..8bde407 100644 --- a/src/liblsquic/lsquic_send_ctl.c +++ b/src/liblsquic/lsquic_send_ctl.c @@ -674,8 +674,6 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl, const struct ack_info *acki, lsquic_time_t ack_recv_time) { - struct lsquic_packets_tailq acked_acks = - TAILQ_HEAD_INITIALIZER(acked_acks); const struct lsquic_packno_range *range = &acki->ranges[ acki->n_ranges - 1 ]; lsquic_packet_out_t *packet_out, *next; @@ -1401,18 +1399,20 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id) packet_out; packet_out = next) { next = TAILQ_NEXT(packet_out, po_next); - assert(packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM)); - lsquic_packet_out_elide_reset_stream_frames(packet_out, stream_id); - if (0 == packet_out->po_frame_types) + if (packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM)) { - LSQ_DEBUG("cancel buffered packet in queue #%u after eliding " - "frames for stream %"PRIu32, n, stream_id); - TAILQ_REMOVE(&ctl->sc_buffered_packets[n].bpq_packets, - packet_out, po_next); - --ctl->sc_buffered_packets[n].bpq_count; - send_ctl_destroy_packet(ctl, packet_out); - LSQ_DEBUG("Elide packet from buffered queue #%u; count: %u", - n, ctl->sc_buffered_packets[n].bpq_count); + lsquic_packet_out_elide_reset_stream_frames(packet_out, stream_id); + if (0 == packet_out->po_frame_types) + { + LSQ_DEBUG("cancel buffered packet in queue #%u after eliding " + "frames for stream %"PRIu32, n, stream_id); + TAILQ_REMOVE(&ctl->sc_buffered_packets[n].bpq_packets, + packet_out, po_next); + --ctl->sc_buffered_packets[n].bpq_count; + send_ctl_destroy_packet(ctl, packet_out); + LSQ_DEBUG("Elide packet from buffered queue #%u; count: %u", + n, ctl->sc_buffered_packets[n].bpq_count); + } } } } @@ -1687,7 +1687,6 @@ send_ctl_get_buffered_packet (lsquic_send_ctl_t *ctl, if (packet_q->bpq_count >= send_ctl_max_bpq_count(ctl, packet_type)) return NULL; - bits = lsquic_send_ctl_guess_packno_bits(ctl); if (packet_q->bpq_count == 0) { /* If ACK was written to the low-priority queue first, steal it */ diff --git a/src/liblsquic/lsquic_spi.c b/src/liblsquic/lsquic_spi.c index dd1dd3f..0dbb881 100644 --- a/src/liblsquic/lsquic_spi.c +++ b/src/liblsquic/lsquic_spi.c @@ -109,7 +109,7 @@ find_and_set_lowest_priority (struct stream_prio_iter *iter) if (iter->spi_set[ set ]) break; - if (set == 4) + if (set >= 4) { //SPI_DEBUG("%s: cannot find any", __func__); return -1; diff --git a/src/lshpack/lshpack.c b/src/lshpack/lshpack.c index 715d781..aa15e83 100644 --- a/src/lshpack/lshpack.c +++ b/src/lshpack/lshpack.c @@ -5860,7 +5860,7 @@ henc_huffman_enc (const unsigned char *src, const unsigned char *const src_end, bits_left -= cur_enc_code.bits; while (bits_left <= 32) { - *p_dst++ = bits >> 32; + *p_dst++ = (unsigned char) (bits >> 32); bits <<= 8; bits_left += 8; if (p_dst == dst_end) @@ -5872,7 +5872,7 @@ henc_huffman_enc (const unsigned char *src, const unsigned char *const src_end, { assert(bits_left < 40 && bits_left > 0); bits |= ((uint64_t)1 << bits_left) - 1; - *p_dst++ = bits >> 32; + *p_dst++ = (unsigned char) (bits >> 32); } return p_dst - dst; @@ -5925,7 +5925,7 @@ lshpack_enc_enc_str (unsigned char *const dst, size_t dst_len, { if (str_len < 127) { - *dst = str_len; + *dst = (unsigned char) str_len; memcpy(dst + 1, str, str_len); return 1 + str_len; } diff --git a/test/http_client.c b/test/http_client.c index dc7fd7b..02db949 100644 --- a/test/http_client.c +++ b/test/http_client.c @@ -173,10 +173,17 @@ struct http_client_ctx { unsigned hcc_concurrency; unsigned hcc_cc_reqs_per_conn; unsigned hcc_n_open_conns; + + unsigned char *hcc_zero_rtt; + size_t hcc_zero_rtt_len; + size_t hcc_zero_rtt_max_len; + FILE *hcc_zero_rtt_file; + char *hcc_zero_rtt_file_name; enum { HCC_SEEN_FIN = (1 << 1), HCC_ABORT_ON_INCOMPLETE = (1 << 2), + HCC_RTT_INFO = (1 << 3), } hcc_flags; struct prog *prog; }; @@ -218,9 +225,18 @@ display_cert_chain (lsquic_conn_t *); static void create_connections (struct http_client_ctx *client_ctx) { + unsigned char *zero_rtt = NULL; + size_t zero_rtt_len = 0; + if (client_ctx->hcc_flags & HCC_RTT_INFO) + { + zero_rtt = client_ctx->hcc_zero_rtt; + zero_rtt_len = client_ctx->hcc_zero_rtt_len; + LSQ_INFO("create connection zero_rtt %ld bytes", zero_rtt_len); + } + while (client_ctx->hcc_n_open_conns < client_ctx->hcc_concurrency && client_ctx->hcc_total_n_reqs > 0) - if (0 != prog_connect(client_ctx->prog)) + if (0 != prog_connect(client_ctx->prog, zero_rtt, zero_rtt_len)) { LSQ_ERROR("connection failed"); exit(EXIT_FAILURE); @@ -330,15 +346,74 @@ http_client_on_conn_closed (lsquic_conn_t *conn) static void -http_client_on_hsk_done (lsquic_conn_t *conn, int ok) +http_client_on_hsk_done (lsquic_conn_t *conn, enum lsquic_hsk_status status) { lsquic_conn_ctx_t *conn_h; - LSQ_INFO("handshake %s", ok ? "completed successfully" : "failed"); + lsquic_conn_ctx_t *conn_ctx = lsquic_conn_get_ctx(conn); + struct http_client_ctx *client_ctx = conn_ctx->client_ctx; + ssize_t ret; + if (status == LSQ_HSK_FAIL) + LSQ_INFO("handshake failed"); + else + LSQ_INFO("handshake success %s", + status == LSQ_HSK_0RTT_OK ? "with 0-RTT" : ""); + if (!(client_ctx->hcc_flags & HCC_RTT_INFO) || + ((client_ctx->hcc_flags & HCC_RTT_INFO) && status != LSQ_HSK_0RTT_OK)) + { + ret = lsquic_conn_get_zero_rtt(conn, client_ctx->hcc_zero_rtt, + client_ctx->hcc_zero_rtt_max_len); + if (ret > 0) + { + client_ctx->hcc_zero_rtt_len = ret; + LSQ_INFO("get zero_rtt %ld bytes", client_ctx->hcc_zero_rtt_len); + client_ctx->hcc_flags |= HCC_RTT_INFO; + /* clear file and prepare to write */ + if (client_ctx->hcc_zero_rtt_file) + { + client_ctx->hcc_zero_rtt_file = freopen( + client_ctx->hcc_zero_rtt_file_name, + "wb", client_ctx->hcc_zero_rtt_file); + LSQ_DEBUG("reopen and clear zero_rtt file"); + } + /* open file for the first time */ + if (client_ctx->hcc_zero_rtt_file_name && + !client_ctx->hcc_zero_rtt_file) + { + client_ctx->hcc_zero_rtt_file = fopen( + client_ctx->hcc_zero_rtt_file_name, "wb+"); + if (client_ctx->hcc_zero_rtt_file) + LSQ_DEBUG("opened zero_rtt file"); + else + LSQ_DEBUG("zero_rtt file cannot be created"); + } + /* write to file */ + if (client_ctx->hcc_zero_rtt_file) + { + size_t ret2 = fwrite(client_ctx->hcc_zero_rtt, 1, + client_ctx->hcc_zero_rtt_len, + client_ctx->hcc_zero_rtt_file); + LSQ_DEBUG("wrote %ld bytes to zero_rtt file", ret2); + if (ret2 == client_ctx->hcc_zero_rtt_len) + { + fclose(client_ctx->hcc_zero_rtt_file); + client_ctx->hcc_zero_rtt_file = NULL; + LSQ_DEBUG("close zero_rtt file"); + } + else + LSQ_ERROR("did not write full blob to zero_rtt file"); + } + } + else if (ret == 0) + { + LSQ_INFO("zero_rtt not available"); + } else + LSQ_INFO("get_zero_rtt failed %s", strerror(errno)); + } - if (ok && s_display_cert_chain) + if ((status != LSQ_HSK_FAIL) && s_display_cert_chain) display_cert_chain(conn); - if (ok) + if (status != LSQ_HSK_FAIL) { conn_h = lsquic_conn_get_ctx(conn); ++s_stat_conns_ok; @@ -703,6 +778,7 @@ usage (const char *prog) " -I Abort on incomplete reponse from server\n" " -4 Prefer IPv4 when resolving hostname\n" " -6 Prefer IPv6 when resolving hostname\n" +" -0 FILE Provide RTT info file (reading or writing)\n" #ifndef WIN32 " -C DIR Certificate store. If specified, server certificate will\n" " be verified.\n" @@ -758,6 +834,7 @@ init_x509_cert_store (const char *path) X509 *cert; DIR *dir; char file_path[NAME_MAX]; + int ret; dir = opendir(path); if (!dir) @@ -772,7 +849,18 @@ init_x509_cert_store (const char *path) { if (ends_in_pem(ent->d_name)) { - snprintf(file_path, sizeof(file_path), "%s/%s", path, ent->d_name); + ret = snprintf(file_path, sizeof(file_path), "%s/%s", + path, ent->d_name); + if (ret < 0) + { + LSQ_WARN("file_path formatting error %s", strerror(errno)); + continue; + } + else if ((unsigned)ret >= sizeof(file_path)) + { + LSQ_WARN("file_path was truncated %s", strerror(errno)); + continue; + } cert = file2cert(file_path); if (cert) { @@ -945,6 +1033,7 @@ main (int argc, char **argv) struct path_elem *pe; struct sport_head sports; struct prog prog; + unsigned char zero_rtt[8192]; TAILQ_INIT(&sports); memset(&client_ctx, 0, sizeof(client_ctx)); @@ -955,6 +1044,9 @@ main (int argc, char **argv) client_ctx.hcc_cc_reqs_per_conn = 1; client_ctx.hcc_reqs_per_conn = 1; client_ctx.hcc_total_n_reqs = 1; + client_ctx.hcc_zero_rtt = (unsigned char *)zero_rtt; + client_ctx.hcc_zero_rtt_len = sizeof(zero_rtt); + client_ctx.hcc_zero_rtt_max_len = sizeof(zero_rtt); client_ctx.prog = &prog; #ifdef WIN32 WSADATA wsd; @@ -963,7 +1055,7 @@ main (int argc, char **argv) prog_init(&prog, LSENG_HTTP, &sports, &http_client_if, &client_ctx); - while (-1 != (opt = getopt(argc, argv, PROG_OPTS "46Br:R:IKu:EP:M:n:w:H:p:h" + while (-1 != (opt = getopt(argc, argv, PROG_OPTS "46Br:R:IKu:EP:M:n:w:H:p:0:h" #ifndef WIN32 "C:atT:" #endif @@ -1061,6 +1153,14 @@ main (int argc, char **argv) } } break; + case '0': + client_ctx.hcc_zero_rtt_file_name = optarg; + client_ctx.hcc_zero_rtt_file = fopen(optarg, "rb+"); + if (client_ctx.hcc_zero_rtt_file) + LSQ_DEBUG("opened zero_rtt file"); + else + LSQ_DEBUG("zero_rtt file is empty, opening later"); + break; default: if (0 != prog_set_opt(&prog, opt, optarg)) exit(1); @@ -1071,6 +1171,20 @@ main (int argc, char **argv) prog.prog_api.ea_stats_fh = stats_fh; #endif + if (client_ctx.hcc_zero_rtt_file) + { + size_t ret = fread(client_ctx.hcc_zero_rtt, 1, + client_ctx.hcc_zero_rtt_max_len, + client_ctx.hcc_zero_rtt_file); + if (ret) + { + client_ctx.hcc_flags |= HCC_RTT_INFO; + client_ctx.hcc_zero_rtt_len = ret; + LSQ_DEBUG("read %ld bytes from zero_rtt file", ret); + } + else + LSQ_DEBUG("zero_rtt file is empty"); + } if (TAILQ_EMPTY(&client_ctx.hcc_path_elems)) { fprintf(stderr, "Specify at least one path using -p option\n"); @@ -1090,6 +1204,13 @@ main (int argc, char **argv) s = prog_run(&prog); + if (client_ctx.hcc_zero_rtt_file) + { + fclose(client_ctx.hcc_zero_rtt_file); + client_ctx.hcc_zero_rtt_file = NULL; + LSQ_DEBUG("close zero_rtt file"); + } + if (stats_fh) { elapsed = (long double) (lsquic_time_now() - start_time) / 1000000; diff --git a/test/prog.c b/test/prog.c index e266192..4f12675 100644 --- a/test/prog.c +++ b/test/prog.c @@ -253,7 +253,7 @@ prog_eb (struct prog *prog) int -prog_connect (struct prog *prog) +prog_connect (struct prog *prog, unsigned char *zero_rtt, size_t zero_rtt_len) { struct service_port *sport; @@ -262,7 +262,7 @@ prog_connect (struct prog *prog) (struct sockaddr *) &sport->sp_local_addr, (struct sockaddr *) &sport->sas, sport, NULL, prog->prog_hostname ? prog->prog_hostname : sport->host, - prog->prog_max_packet_size)) + prog->prog_max_packet_size, zero_rtt, zero_rtt_len)) return -1; prog_process_conns(prog); diff --git a/test/prog.h b/test/prog.h index 5ffc70c..bf439b1 100644 --- a/test/prog.h +++ b/test/prog.h @@ -68,7 +68,7 @@ int prog_prep (struct prog *); int -prog_connect (struct prog *); +prog_connect (struct prog *, unsigned char *, size_t); void prog_print_common_options (const struct prog *, FILE *); diff --git a/test/unittests/CMakeLists.txt b/test/unittests/CMakeLists.txt index 30e0e22..a9e3f61 100644 --- a/test/unittests/CMakeLists.txt +++ b/test/unittests/CMakeLists.txt @@ -1,18 +1,13 @@ # Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. - INCLUDE_DIRECTORIES(../../src/liblsquic) ENABLE_TESTING() -SET(LIBS lsquic ${BORINGSSL_LIB_crypto} ${BORINGSSL_LIB_ssl} ${ZLIB_LIB}) - IF (MSVC) - LIST(APPEND LIBS ws2_32) SET(ADDL_SOURCES ../../wincompat/getopt.c ../../wincompat/getopt1.c) SET(LIB_FLAGS "-FORCE:MULTIPLE") ELSE() SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-value") - LIST(APPEND LIBS m pthread) ENDIF() SET(TESTS @@ -72,6 +67,7 @@ IF (CMAKE_SYSTEM_NAME STREQUAL "Linux") SET(TESTS ${TESTS} frame_rw) ENDIF() + FOREACH(TEST_NAME ${TESTS}) ADD_EXECUTABLE(test_${TEST_NAME} test_${TEST_NAME}.c ${ADDL_SOURCES}) TARGET_LINK_LIBRARIES(test_${TEST_NAME} ${LIBS} ${LIB_FLAGS}) @@ -87,3 +83,4 @@ ADD_TEST(stream_hash_A test_stream -A -h) ADD_EXECUTABLE(graph_cubic graph_cubic.c ${ADDL_SOURCES}) TARGET_LINK_LIBRARIES(graph_cubic ${LIBS}) + diff --git a/test/unittests/test_frame_writer.c b/test/unittests/test_frame_writer.c index dd84ff8..18fdd33 100644 --- a/test/unittests/test_frame_writer.c +++ b/test/unittests/test_frame_writer.c @@ -460,7 +460,6 @@ test_priority (void) } - static void test_errors (void) { @@ -596,7 +595,6 @@ perl tools/hpack.pl :method GET :path /index.html :authority www.example.com :sc } - int main (void) {