Release 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.
This commit is contained in:
Dmitri Tikhonov 2019-02-04 08:59:11 -05:00
parent 03fb93526e
commit 8ca33e0e19
21 changed files with 631 additions and 381 deletions

View file

@ -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.

View file

@ -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)

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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 *);

View file

@ -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);

View file

@ -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;
}

View file

@ -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,

View file

@ -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 *);

View file

@ -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; i<count; ++i)
for (i = 0; i < count; ++i)
{
lsquic_str_copy(&item->crts[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; i<item->count; ++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; i<certs_count; ++i)
for (i=0; i<count; ++i)
{
if (lsquic_str_bcmp(certs[i], &cached_certs_item->crts[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; i<out_certs_count; ++i)
lsquic_str_delete(out_certs[i]);
free(out_certs);
@ -1476,8 +1429,6 @@ lsquic_enc_session_handle_chlo_reply (lsquic_enc_session_t *enc_session,
if (enc_session->hsk_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,
};

View file

@ -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

View file

@ -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;
}

View file

@ -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 */

View file

@ -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;

View file

@ -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;
}

View file

@ -174,9 +174,16 @@ struct http_client_ctx {
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;

View file

@ -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);

View file

@ -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 *);

View file

@ -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})

View file

@ -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)
{