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 2019-01-28
- 1.18.0 - 1.18.0
- [API Change] Can specify clock granularity in engine settings. - [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}") MESSAGE(STATUS "Compiler flags: ${CMAKE_C_FLAGS}")
# This must be done before adding other include directories to take IF (NOT DEFINED BORINGSSL_INCLUDE AND DEFINED BORINGSSL_DIR)
# precedence over header files from other SSL installs. FIND_PATH(BORINGSSL_INCLUDE NAMES openssl/ssl.h
FIND_PATH(BORINGSSL_INCLUDE_DIR NAMES openssl/ssl.h
PATHS ${BORINGSSL_DIR}/include PATHS ${BORINGSSL_DIR}/include
NO_DEFAULT_PATH) NO_DEFAULT_PATH)
IF (BORINGSSL_INCLUDE_DIR) ENDIF()
MESSAGE(STATUS "BoringSSL include directory ${BORINGSSL_INCLUDE_DIR}") # This must be done before adding other include directories to take
INCLUDE_DIRECTORIES(${BORINGSSL_INCLUDE_DIR}) # precedence over header files from other SSL installs.
IF (BORINGSSL_INCLUDE)
MESSAGE(STATUS "BoringSSL include directory ${BORINGSSL_INCLUDE}")
INCLUDE_DIRECTORIES(${BORINGSSL_INCLUDE})
ELSE() ELSE()
MESSAGE(FATAL_ERROR "BoringSSL headers not found") MESSAGE(FATAL_ERROR "BoringSSL headers not found")
ENDIF() ENDIF()
FOREACH(BORINGSSL_LIB ssl crypto decrepit) IF (NOT DEFINED BORINGSSL_LIB AND DEFINED BORINGSSL_DIR)
FOREACH(LIB_NAME ssl crypto decrepit)
IF (CMAKE_SYSTEM_NAME STREQUAL Windows) IF (CMAKE_SYSTEM_NAME STREQUAL Windows)
FIND_LIBRARY(BORINGSSL_LIB_${BORINGSSL_LIB} FIND_LIBRARY(BORINGSSL_LIB_${LIB_NAME}
NAMES ${BORINGSSL_LIB} NAMES ${LIB_NAME}
PATHS ${BORINGSSL_DIR}/${BORINGSSL_LIB} PATHS ${BORINGSSL_DIR}/${LIB_NAME}
PATH_SUFFIXES Debug Release MinSizeRel RelWithDebInfo PATH_SUFFIXES Debug Release MinSizeRel RelWithDebInfo
NO_DEFAULT_PATH) NO_DEFAULT_PATH)
ELSE() ELSE()
FIND_LIBRARY(BORINGSSL_LIB_${BORINGSSL_LIB} FIND_LIBRARY(BORINGSSL_LIB_${LIB_NAME}
NAMES lib${BORINGSSL_LIB}.a NAMES lib${LIB_NAME}.a
PATHS ${BORINGSSL_DIR}/${BORINGSSL_LIB} PATHS ${BORINGSSL_DIR}/${LIB_NAME}
NO_DEFAULT_PATH) NO_DEFAULT_PATH)
ENDIF() ENDIF()
IF(BORINGSSL_LIB_${BORINGSSL_LIB}) IF(BORINGSSL_LIB_${LIB_NAME})
MESSAGE(STATUS "Found ${BORINGSSL_LIB} library: ${BORINGSSL_LIB_${BORINGSSL_LIB}}") 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() ELSE()
MESSAGE(STATUS "${BORINGSSL_LIB} library not found") MESSAGE(STATUS "${BORINGSSL_LIB} library not found")
ENDIF() ENDIF()
ENDFOREACH() 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") IF(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")
# Find libevent on FreeBSD: # Find libevent on FreeBSD:
INCLUDE_DIRECTORIES( /usr/local/include ) include_directories( /usr/local/include )
LINK_DIRECTORIES( /usr/local/lib ) link_directories( /usr/local/lib )
ENDIF() ENDIF()
# Find zlib and libevent header files and library files # Find zlib and libevent header files and library files
@ -157,8 +189,8 @@ ELSE()
MESSAGE(STATUS "libevent not found") MESSAGE(STATUS "libevent not found")
ENDIF() 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) IF (NOT MSVC)
add_executable(http_client add_executable(http_client
@ -166,8 +198,7 @@ add_executable(http_client
test/prog.c test/prog.c
test/test_common.c test/test_common.c
) )
TARGET_LINK_LIBRARIES(http_client lsquic ${EVENT_LIB} pthread LIST(APPEND LIBS pthread m)
${BORINGSSL_LIB_ssl} ${BORINGSSL_LIB_crypto} ${LIBS} ${ZLIB_LIB} m)
#MSVC #MSVC
ELSE() ELSE()
@ -178,19 +209,12 @@ add_executable(http_client
wincompat/getopt.c wincompat/getopt.c
wincompat/getopt1.c wincompat/getopt1.c
) )
LIST(APPEND LIBS ws2_32)
target_link_libraries(http_client
lsquic
${EVENT_LIB}
${ZLIB_LIB}
${BORINGSSL_LIB_ssl}
${BORINGSSL_LIB_crypto}
ws2_32
${LIBS} )
ENDIF() 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) add_subdirectory(src)

View file

@ -24,7 +24,7 @@ extern "C" {
#endif #endif
#define LSQUIC_MAJOR_VERSION 1 #define LSQUIC_MAJOR_VERSION 1
#define LSQUIC_MINOR_VERSION 18 #define LSQUIC_MINOR_VERSION 19
#define LSQUIC_PATCH_VERSION 0 #define LSQUIC_PATCH_VERSION 0
/** /**
@ -137,6 +137,22 @@ enum lsquic_version
*/ */
#define LSQUIC_FORCED_TCID0_VERSIONS (1 << LSQVER_044) #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 * @struct lsquic_stream_if
* @brief The definition of callback functions call by lsquic_stream to * @brief The definition of callback functions call by lsquic_stream to
@ -178,7 +194,7 @@ struct lsquic_stream_if {
* *
* This callback is optional. * 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, lsquic_engine_connect (lsquic_engine_t *, const struct sockaddr *local_sa,
const struct sockaddr *peer_sa, const struct sockaddr *peer_sa,
void *peer_ctx, lsquic_conn_ctx_t *conn_ctx, 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 * 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 * struct stack_st_X509 *
lsquic_conn_get_server_cert_chain (lsquic_conn_t *); 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 */ /** Returns ID of the stream */
uint32_t uint32_t
lsquic_stream_id (const lsquic_stream_t *s); 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; struct client_hsk_ctx *const c_hsk = (struct client_hsk_ctx *) sh;
ssize_t nread; ssize_t nread;
int s; int s;
enum lsquic_hsk_status status;
if (!c_hsk->buf_in) 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); lsquic_mm_put_16k(c_hsk->mm, c_hsk->buf_in);
c_hsk->buf_in = NULL; c_hsk->buf_in = NULL;
lsquic_stream_wantread(stream, 0); 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); lsquic_conn_close(c_hsk->lconn);
} }
break; 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)) if (c_hsk->lconn->cn_esf->esf_is_hsk_done(c_hsk->lconn->cn_enc_session))
{ {
LSQ_DEBUG("handshake is successful, inform connection"); 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 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"); LSQ_INFO("lsquic_enc_session_handle_chlo_reply returned an error");
c_hsk->buf_in = NULL; c_hsk->buf_in = NULL;
lsquic_stream_wantread(stream, 0); 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); lsquic_conn_close(c_hsk->lconn);
break; break;
} }

View file

@ -150,3 +150,16 @@ lsquic_conn_get_server_cert_chain (struct lsquic_conn *lconn)
else else
return NULL; 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 *); (*ci_packet_not_sent) (struct lsquic_conn *, struct lsquic_packet_out *);
void void
(*ci_handshake_ok) (struct lsquic_conn *); (*ci_hsk_done) (struct lsquic_conn *, enum lsquic_hsk_status);
void
(*ci_handshake_failed) (struct lsquic_conn *);
void void
(*ci_destroy) (struct lsquic_conn *); (*ci_destroy) (struct lsquic_conn *);

View file

@ -453,7 +453,8 @@ maybe_grow_conn_heaps (struct lsquic_engine *engine)
static lsquic_conn_t * static lsquic_conn_t *
new_full_conn_client (lsquic_engine_t *engine, const char *hostname, 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; lsquic_conn_t *conn;
unsigned flags; unsigned flags;
@ -461,7 +462,8 @@ new_full_conn_client (lsquic_engine_t *engine, const char *hostname,
return NULL; return NULL;
flags = engine->flags & (ENG_SERVER|ENG_HTTP); flags = engine->flags & (ENG_SERVER|ENG_HTTP);
conn = full_conn_client_new(&engine->pub, engine->stream_if, 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) if (!conn)
return NULL; return NULL;
++engine->n_conns; ++engine->n_conns;
@ -662,7 +664,8 @@ lsquic_conn_t *
lsquic_engine_connect (lsquic_engine_t *engine, const struct sockaddr *local_sa, lsquic_engine_connect (lsquic_engine_t *engine, const struct sockaddr *local_sa,
const struct sockaddr *peer_sa, const struct sockaddr *peer_sa,
void *peer_ctx, lsquic_conn_ctx_t *conn_ctx, 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; lsquic_conn_t *conn;
ENGINE_IN(engine); 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) if (!conn)
goto err; goto err;
lsquic_conn_record_sockaddr(conn, local_sa, peer_sa); 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); headers_sz = calc_headers_size(headers);
if (extra_headers) if (extra_headers)
headers_sz += calc_headers_size(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)", LSQ_INFO("Headers size %u is larger than max allowed (%u)",
headers_sz, fw->fw_max_header_list_sz); headers_sz, fw->fw_max_header_list_sz);
errno = EMSGSIZE; errno = EMSGSIZE;
return -1; return -1;
} }
return 0;
} }

View file

@ -435,7 +435,6 @@ send_smhl (const struct full_conn *conn)
{ {
uint32_t smhl; uint32_t smhl;
return conn->fc_conn.cn_enc_session 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( && 0 == conn->fc_conn.cn_esf->esf_get_peer_setting(
conn->fc_conn.cn_enc_session, QTAG_SMHL, &smhl) conn->fc_conn.cn_enc_session, QTAG_SMHL, &smhl)
&& 1 == smhl; && 1 == smhl;
@ -509,8 +508,6 @@ apply_peer_settings (struct full_conn *conn)
LSQ_DEBUG("peer settings: CFCW: %u; SFCW: %u; MIDS: %u", LSQ_DEBUG("peer settings: CFCW: %u; SFCW: %u; MIDS: %u",
cfcw, sfcw, mids); cfcw, sfcw, mids);
conn_on_peer_config(conn, cfcw, sfcw, mids); conn_on_peer_config(conn, cfcw, sfcw, mids);
if (conn->fc_flags & FC_HTTP)
maybe_send_settings(conn);
return 0; return 0;
} }
@ -640,7 +637,8 @@ struct lsquic_conn *
full_conn_client_new (struct lsquic_engine_public *enpub, full_conn_client_new (struct lsquic_engine_public *enpub,
const struct lsquic_stream_if *stream_if, const struct lsquic_stream_if *stream_if,
void *stream_if_ctx, unsigned flags, 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; struct full_conn *conn;
enum lsquic_version version; enum lsquic_version version;
@ -656,7 +654,8 @@ full_conn_client_new (struct lsquic_engine_public *enpub,
return NULL; return NULL;
conn->fc_conn.cn_esf = esf; conn->fc_conn.cn_esf = esf;
conn->fc_conn.cn_enc_session = 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) if (!conn->fc_conn.cn_enc_session)
{ {
LSQ_WARN("could not create enc session: %s", strerror(errno)); 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); 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); process_hsk_stream_write_events(conn);
goto end_write; goto end_write;
@ -3145,29 +3146,30 @@ full_conn_ci_packet_not_sent (lsquic_conn_t *lconn, lsquic_packet_out_t *packet_
static void 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; struct full_conn *conn = (struct full_conn *) lconn;
LSQ_DEBUG("handshake reportedly done");
lsquic_alarmset_unset(&conn->fc_alset, AL_HANDSHAKE); lsquic_alarmset_unset(&conn->fc_alset, AL_HANDSHAKE);
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 (0 == apply_peer_settings(conn))
{
if (conn->fc_flags & FC_HTTP)
maybe_send_settings(conn);
lconn->cn_flags |= LSCONN_HANDSHAKE_DONE; lconn->cn_flags |= LSCONN_HANDSHAKE_DONE;
}
else else
conn->fc_flags |= FC_ERROR; conn->fc_flags |= FC_ERROR;
if (conn->fc_stream_ifs[STREAM_IF_STD].stream_if->on_hsk_done) break;
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) 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; return 1;
if (!TAILQ_EMPTY(&conn->fc_pub.sending_streams)) if (!TAILQ_EMPTY(&conn->fc_pub.sending_streams))
return 1; 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, TAILQ_FOREACH(stream, &conn->fc_pub.write_streams,
next_write_stream) next_write_stream)
@ -3617,8 +3621,7 @@ static const struct conn_iface full_conn_iface = {
#if LSQUIC_CONN_STATS #if LSQUIC_CONN_STATS
.ci_get_stats = full_conn_ci_get_stats, .ci_get_stats = full_conn_ci_get_stats,
#endif #endif
.ci_handshake_failed = full_conn_ci_handshake_failed, .ci_hsk_done = full_conn_ci_hsk_done,
.ci_handshake_ok = full_conn_ci_handshake_ok,
.ci_is_tickable = full_conn_ci_is_tickable, .ci_is_tickable = full_conn_ci_is_tickable,
.ci_next_packet_to_send = full_conn_ci_next_packet_to_send, .ci_next_packet_to_send = full_conn_ci_next_packet_to_send,
.ci_next_tick_time = full_conn_ci_next_tick_time, .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 *, const struct lsquic_stream_if *,
void *stream_if_ctx, void *stream_if_ctx,
unsigned flags /* Only FC_SERVER and FC_HTTP */, 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 void
full_conn_client_call_on_new (struct lsquic_conn *); full_conn_client_call_on_new (struct lsquic_conn *);

View file

@ -115,6 +115,9 @@ typedef struct hs_ctx_st
struct lsquic_enc_session struct lsquic_enc_session
{ {
enum handshake_state hsk_state; 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 have_key; /* 0, no 1, I, 2, D, 3, F */
uint8_t peer_have_final_key; uint8_t peer_have_final_key;
@ -138,6 +141,7 @@ struct lsquic_enc_session
hs_ctx_t hs_ctx; hs_ctx_t hs_ctx;
lsquic_session_cache_info_t *info; lsquic_session_cache_info_t *info;
c_cert_item_t *cert_item;
SSL_CTX * ssl_ctx; SSL_CTX * ssl_ctx;
const struct lsquic_engine_public *enpub; const struct lsquic_engine_public *enpub;
struct lsquic_str * cert_ptr; /* pointer to the leaf cert of the server, not real copy */ 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 */ /* client */
static cert_hash_item_t *make_cert_hash_item(struct lsquic_str *domain, struct lsquic_str **certs, int count); static c_cert_item_t *make_c_cert_item(struct lsquic_str **certs, int count);
static int c_insert_certs(cert_hash_item_t *item); static void free_c_cert_item(c_cert_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 int get_tag_val_u32 (unsigned char *v, int len, uint32_t *val); static int get_tag_val_u32 (unsigned char *v, int len, uint32_t *val);
static int init_hs_hash_tables(int flags); 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 static void
lsquic_handshake_cleanup (void) lsquic_handshake_cleanup (void)
{ {
cleanup_hs_hash_tables();
lsquic_crt_cleanup(); lsquic_crt_cleanup();
} }
@ -250,63 +202,26 @@ lsquic_handshake_cleanup (void)
/* return -1 for fail, 0 OK*/ /* return -1 for fail, 0 OK*/
static int init_hs_hash_tables(int flags) 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; return 0;
} }
/* client */ /* client */
struct lsquic_hash_elem * static c_cert_item_t *
c_get_certs_elem (const lsquic_str_t *domain) make_c_cert_item (lsquic_str_t **certs, int count)
{
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)
{ {
int i; int i;
uint64_t hash; 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->crts = (lsquic_str_t *)malloc(count * sizeof(lsquic_str_t));
item->domain = lsquic_str_new(NULL, 0);
item->hashs = lsquic_str_new(NULL, 0); item->hashs = lsquic_str_new(NULL, 0);
lsquic_str_copy(item->domain, domain);
item->count = count; item->count = count;
for (i = 0; i < count; ++i) for (i = 0; i < count; ++i)
{ {
lsquic_str_copy(&item->crts[i], certs[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); lsquic_str_append(item->hashs, (char *)&hash, 8);
} }
return item; return item;
@ -315,13 +230,12 @@ make_cert_hash_item (lsquic_str_t *domain, lsquic_str_t **certs, int count)
/* client */ /* client */
static void static void
c_free_cert_hash_item (cert_hash_item_t *item) free_c_cert_item (c_cert_item_t *item)
{ {
int i; int i;
if (item) if (item)
{ {
lsquic_str_delete(item->hashs); lsquic_str_delete(item->hashs);
lsquic_str_delete(item->domain);
for(i=0; i<item->count; ++i) for(i=0; i<item->count; ++i)
lsquic_str_d(&item->crts[i]); lsquic_str_d(&item->crts[i]);
free(item->crts); free(item->crts);
@ -330,112 +244,134 @@ c_free_cert_hash_item (cert_hash_item_t *item)
} }
/* client */ enum rtt_deserialize_return_type
static int
c_insert_certs (cert_hash_item_t *item)
{ {
if (lsquic_hash_insert(s_cached_client_certs, RTT_DESERIALIZE_OK = 0,
lsquic_str_cstr(item->domain), RTT_DESERIALIZE_BAD_QUIC_VER = 1,
lsquic_str_len(item->domain), item) == NULL) RTT_DESERIALIZE_BAD_SERIAL_VER = 2,
return -1; RTT_DESERIALIZE_BAD_CERT_SIZE = 3,
else };
return 0;
}
#define RTT_SERIALIZER_VERSION (1 << 0)
/* client */
static void 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) uint32_t i;
lsquic_hash_erase(s_cached_client_certs, el); uint8_t *next_cert;
} struct lsquic_cert_storage *cert_storage;
/*
* assign versions
static int save_session_info_entry(lsquic_str_t *key, lsquic_session_cache_info_t *entry) */
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_setto(&entry->sni_key, lsquic_str_cstr(key), lsquic_str_len(key)); cert_storage = (struct lsquic_cert_storage *)next_cert;
if (lsquic_hash_insert(s_cached_client_session_infos, cert_storage->len = lsquic_str_len(&cert_item->crts[i]);
lsquic_str_cstr(&entry->sni_key), memcpy(cert_storage->data, lsquic_str_buf(&cert_item->crts[i]),
lsquic_str_len(&entry->sni_key), entry) == NULL) cert_storage->len);
{ next_cert += sizeof(struct lsquic_cert_storage) + cert_storage->len;
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);
} }
} }
/* client */ #define CHECK_SPACE(need, start, end) \
static lsquic_session_cache_info_t * do { if ((intptr_t) (need) > ( (intptr_t) (end) - (intptr_t) (start))) \
retrieve_session_info_entry (const char *key) { 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; uint32_t i, len;
struct lsquic_hash_elem *el; uint64_t hash;
uint8_t *next_cert;
if (!s_cached_client_session_infos) struct lsquic_cert_storage *cert_storage;
return NULL; void *storage_end = (uint8_t *)storage + storage_size;
/*
if (!key) * check versions
return NULL; */
if (lsquic_tag2ver(storage->quic_version_tag) & ~settings->es_versions)
el = lsquic_hash_find(s_cached_client_session_infos, key, strlen(key)); return RTT_DESERIALIZE_BAD_QUIC_VER;
if (el == NULL) if (storage->serializer_version != RTT_SERIALIZER_VERSION)
return NULL; return RTT_DESERIALIZE_BAD_SERIAL_VER;
/*
entry = lsquic_hashelem_getdata(el); * server config
LSQ_DEBUG("[QUIC]retrieve_session_info_entry find cached session info %p.\n", entry); */
return entry; info->ver = storage->ver;
} info->aead = storage->aead;
info->kexs = storage->kexs;
info->pdmd = storage->pdmd;
/* call it in timer() */ info->orbt = storage->orbt;
#if __GNUC__ info->expy = storage->expy;
__attribute__((unused)) info->scfg_flag = storage->scfg_flag;
#endif lsquic_str_setto(&info->sstk, storage->sstk, storage->sstk_len);
static void lsquic_str_setto(&info->scfg, storage->scfg, storage->scfg_len);
remove_expire_session_info_entry (void) memcpy(&info->sscid, storage->sscid, SCID_LENGTH);
{ memcpy(&info->spubs, storage->spubs, MAX_SPUBS_LENGTH);
time_t tm = time(NULL); /*
struct lsquic_hash_elem *el; * certificate chain
*/
for (el = lsquic_hash_first(s_cached_client_session_infos); el; cert_item->count = storage->cert_count;
el = lsquic_hash_next(s_cached_client_session_infos)) cert_item->crts = malloc(cert_item->count * sizeof(lsquic_str_t));
{ cert_item->hashs = lsquic_str_new(NULL, 0);
lsquic_session_cache_info_t *entry = lsquic_hashelem_getdata(el); next_cert = (uint8_t *)storage->cert_storage;
if ((uint64_t)tm > entry->expy) for (i = 0; i < storage->cert_count; i++)
{ {
free_info(entry); CHECK_SPACE(sizeof(struct lsquic_cert_storage), next_cert, storage_end);
lsquic_hash_erase(s_cached_client_session_infos, el); 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 * static lsquic_enc_session_t *
lsquic_enc_session_create_client (const char *domain, lsquic_cid_t cid, 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_session_cache_info_t *info;
lsquic_enc_session_t *enc_session; lsquic_enc_session_t *enc_session;
c_cert_item_t *item;
const struct lsquic_zero_rtt_storage *zero_rtt_storage;
if (!domain) if (!domain)
{ {
@ -447,19 +383,47 @@ lsquic_enc_session_create_client (const char *domain, lsquic_cid_t cid,
if (!enc_session) if (!enc_session)
return NULL; return NULL;
info = retrieve_session_info_entry(domain); /* have to allocate every time */
if (info)
memcpy(enc_session->hs_ctx.pubs, info->spubs, 32);
else
{
info = calloc(1, sizeof(*info)); info = calloc(1, sizeof(*info));
if (!info) if (!info)
{ {
free(enc_session); free(enc_session);
return NULL; 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->enpub = enpub;
enc_session->cid = cid; enc_session->cid = cid;
enc_session->info = info; 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); EVP_AEAD_CTX_cleanup(enc_session->enc_ctx_f);
free(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); 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) static int get_hs_state(lsquic_enc_session_t *enc_session)
{ {
return enc_session->hsk_state; return enc_session->hsk_state;
@ -663,8 +629,6 @@ static int parse_hs_data (lsquic_enc_session_t *enc_session, uint32_t tag,
break; break;
case QTAG_STK: 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); lsquic_str_setto(&enc_session->info->sstk, val, len);
ESHIST_APPEND(enc_session, ESHE_SET_STK); ESHIST_APPEND(enc_session, ESHE_SET_STK);
break; break;
@ -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 lsquic_str_t *const ccs = get_common_certs_hash();
const struct lsquic_engine_settings *const settings = const struct lsquic_engine_settings *const settings =
&enc_session->enpub->enp_settings; &enc_session->enpub->enp_settings;
cert_hash_item_t *const cached_certs_item = c_cert_item_t *const cert_item = enc_session->cert_item;
c_find_certs(&enc_session->hs_ctx.sni);
unsigned char pub_key[32]; unsigned char pub_key[32];
size_t ua_len; size_t ua_len;
uint32_t opts[1]; /* Only NSTP is supported for now */ 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)); MSG_LEN_ADD(msg_len, lsquic_str_len(&enc_session->hs_ctx.sni));
++n_tags; /* SNI */ ++n_tags; /* SNI */
MSG_LEN_ADD(msg_len, lsquic_str_len(ccs)); ++n_tags; /* CCS */ 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]; enc_session->cert_ptr = &cert_item->crts[0];
MSG_LEN_ADD(msg_len, lsquic_str_len(cached_certs_item->hashs)); MSG_LEN_ADD(msg_len, lsquic_str_len(cert_item->hashs));
++n_tags; /* CCRT */ ++n_tags; /* CCRT */
MSG_LEN_ADD(msg_len, 8); ++n_tags; /* XLCT */ 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_MIDS, settings->es_max_streams_in);
MW_WRITE_UINT32(&mw, QTAG_SCLS, settings->es_silent_close); MW_WRITE_UINT32(&mw, QTAG_SCLS, settings->es_silent_close);
MW_WRITE_UINT32(&mw, QTAG_KEXS, settings->es_kexs); MW_WRITE_UINT32(&mw, QTAG_KEXS, settings->es_kexs);
if (cached_certs_item) if (cert_item)
MW_WRITE_BUFFER(&mw, QTAG_XLCT, lsquic_str_buf(cached_certs_item->hashs), 8); MW_WRITE_BUFFER(&mw, QTAG_XLCT, lsquic_str_buf(cert_item->hashs), 8);
/* CSCT is empty on purpose (retained from original code) */ /* CSCT is empty on purpose (retained from original code) */
MW_WRITE_TABLE_ENTRY(&mw, QTAG_CSCT, 0); MW_WRITE_TABLE_ENTRY(&mw, QTAG_CSCT, 0);
if (n_opts > 0) if (n_opts > 0)
MW_WRITE_BUFFER(&mw, QTAG_COPT, opts, n_opts * sizeof(opts[0])); MW_WRITE_BUFFER(&mw, QTAG_COPT, opts, n_opts * sizeof(opts[0]));
if (cached_certs_item) if (cert_item)
MW_WRITE_LS_STR(&mw, QTAG_CCRT, cached_certs_item->hashs); MW_WRITE_LS_STR(&mw, QTAG_CCRT, cert_item->hashs);
MW_WRITE_UINT32(&mw, QTAG_CFCW, settings->es_cfcw); MW_WRITE_UINT32(&mw, QTAG_CFCW, settings->es_cfcw);
MW_WRITE_UINT32(&mw, QTAG_SFCW, settings->es_sfcw); MW_WRITE_UINT32(&mw, QTAG_SFCW, settings->es_sfcw);
MW_END(&mw); MW_END(&mw);
@ -1337,16 +1300,16 @@ static int determine_keys(lsquic_enc_session_t *enc_session)
/* 0 Match */ /* 0 Match */
static int cached_certs_match(cert_hash_item_t *cached_certs_item, lsquic_str_t **certs, static int cached_certs_match(c_cert_item_t *item,
int certs_count) lsquic_str_t **certs, int count)
{ {
int i; int i;
if (!cached_certs_item || cached_certs_item->count != certs_count) if (!item || item->count != count)
return -1; 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; return -1;
} }
@ -1383,12 +1346,7 @@ lsquic_enc_session_handle_chlo_reply (lsquic_enc_session_t *enc_session,
uint32_t head_tag; uint32_t head_tag;
int ret; int ret;
lsquic_session_cache_info_t *info = enc_session->info; lsquic_session_cache_info_t *info = enc_session->info;
hs_ctx_t * hs_ctx = &enc_session->hs_ctx; c_cert_item_t *cert_item = enc_session->cert_item;
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);
/* FIXME get the number first */ /* FIXME get the number first */
lsquic_str_t **out_certs = NULL; 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) if (head_tag == QTAG_SREJ || head_tag == QTAG_REJ)
{
enc_session->hsk_state = HSK_CHLO_REJ; enc_session->hsk_state = HSK_CHLO_REJ;
enc_session->es_flags |= ES_RECV_REJ;
}
else if(head_tag == QTAG_SHLO) else if(head_tag == QTAG_SHLO)
{ {
enc_session->hsk_state = HSK_COMPLETED; 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); out_certs_count = get_certs_count(&enc_session->hs_ctx.crt);
if (out_certs_count > 0) 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) if (!out_certs)
{ {
ret = -1; 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, ret = handle_chlo_reply_verify_prof(enc_session, out_certs,
&out_certs_count, &out_certs_count,
(cached_certs_item ? cached_certs_item->crts : NULL), (cert_item ? cert_item->crts : NULL),
(cached_certs_item ? cached_certs_item->count : 0)); (cert_item ? cert_item->count : 0));
if (ret == 0) if (ret == 0)
{ {
if (out_certs_count > 0) if (out_certs_count > 0)
{ {
if (cached_certs_item && if (cached_certs_match(cert_item, out_certs,
cached_certs_match(cached_certs_item, out_certs, out_certs_count) == 0) out_certs_count) != 0)
;
else
{ {
if (el) cert_item = make_c_cert_item(out_certs,
c_erase_certs(el); out_certs_count);
if (cached_certs_item) enc_session->cert_item = cert_item;
c_free_cert_hash_item(cached_certs_item); enc_session->cert_ptr = &cert_item->crts[0];
}
cached_certs_item = make_cert_hash_item(&hs_ctx->sni,
out_certs, out_certs_count);
c_insert_certs(cached_certs_item);
}
enc_session->cert_ptr = &cached_certs_item->crts[0];
} }
} }
for (i=0; i<out_certs_count; ++i) for (i=0; i<out_certs_count; ++i)
lsquic_str_delete(out_certs[i]); lsquic_str_delete(out_certs[i]);
free(out_certs); 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 (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 ret = determine_keys(enc_session
); /* FIXME: check ret */ ); /* FIXME: check ret */
enc_session->have_key = 3; 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) * static STACK_OF(X509) *
lsquic_enc_session_get_server_cert_chain (lsquic_enc_session_t *enc_session) 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; STACK_OF(X509) *chain;
X509 *cert; X509 *cert;
int i; int i;
item = c_find_certs(&enc_session->hs_ctx.sni); item = enc_session->cert_item;
if (!item) if (!item)
{ {
LSQ_WARN("could not find certificates for `%.*s'", 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 #ifdef NDEBUG
const const
#endif #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_handle_chlo_reply = lsquic_enc_session_handle_chlo_reply,
.esf_mem_used = lsquic_enc_session_mem_used, .esf_mem_used = lsquic_enc_session_mem_used,
.esf_verify_reset_token = lsquic_enc_session_verify_reset_token, .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_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; typedef struct lsquic_enc_session lsquic_enc_session_t;
#define MAX_SCFG_LENGTH 512
#define MAX_SPUBS_LENGTH 32
#define STK_LENGTH 60 #define STK_LENGTH 60
#define SNO_LENGTH 56 #define SNO_LENGTH 56
#define SCID_LENGTH 16 #define SCID_LENGTH 16
@ -37,6 +39,14 @@ enum enc_level
extern const char *const lsquic_enclev2str[]; 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 */ /* client side need to store 0rtt info per STK */
typedef struct lsquic_session_cache_info_st typedef struct lsquic_session_cache_info_st
{ {
@ -55,6 +65,33 @@ typedef struct lsquic_session_cache_info_st
} lsquic_session_cache_info_t; } 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 LSQUIC_KEEP_ENC_SESS_HISTORY
# ifndef NDEBUG # ifndef NDEBUG
# define LSQUIC_KEEP_ENC_SESS_HISTORY 1 # define LSQUIC_KEEP_ENC_SESS_HISTORY 1
@ -120,7 +157,8 @@ struct enc_session_funcs
/* Create client session */ /* Create client session */
lsquic_enc_session_t * lsquic_enc_session_t *
(*esf_create_client) (const char *domain, lsquic_cid_t cid, (*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 */ /* Generate connection ID */
lsquic_cid_t (*esf_generate_cid) (void); 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 *, (*esf_verify_reset_token) (lsquic_enc_session_t *, const unsigned char *,
size_t); 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 * struct stack_st_X509 *
(*esf_get_server_cert_chain) (lsquic_enc_session_t *); (*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 extern
@ -154,14 +205,4 @@ struct enc_session_funcs lsquic_enc_session_gquic_1;
#define select_esf_by_ver(ver) \ #define select_esf_by_ver(ver) \
(ver ? &lsquic_enc_session_gquic_1 : &lsquic_enc_session_gquic_1) (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 #endif

View file

@ -323,7 +323,10 @@ lsquic_packet_out_elide_reset_stream_frames (lsquic_packet_out_t *packet_out,
assert(n_stream_frames); assert(n_stream_frames);
if (n_elided == n_stream_frames) if (n_elided == n_stream_frames)
{
packet_out->po_frame_types &= ~(1 << QUIC_FRAME_STREAM); packet_out->po_frame_types &= ~(1 << QUIC_FRAME_STREAM);
packet_out->po_flags &= ~PO_STREAM_END;
}
return adj; return adj;
} }

View file

@ -674,8 +674,6 @@ lsquic_send_ctl_got_ack (lsquic_send_ctl_t *ctl,
const struct ack_info *acki, const struct ack_info *acki,
lsquic_time_t ack_recv_time) lsquic_time_t ack_recv_time)
{ {
struct lsquic_packets_tailq acked_acks =
TAILQ_HEAD_INITIALIZER(acked_acks);
const struct lsquic_packno_range *range = const struct lsquic_packno_range *range =
&acki->ranges[ acki->n_ranges - 1 ]; &acki->ranges[ acki->n_ranges - 1 ];
lsquic_packet_out_t *packet_out, *next; lsquic_packet_out_t *packet_out, *next;
@ -1401,7 +1399,8 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
packet_out; packet_out = next) packet_out; packet_out = next)
{ {
next = TAILQ_NEXT(packet_out, po_next); next = TAILQ_NEXT(packet_out, po_next);
assert(packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM)); if (packet_out->po_frame_types & (1 << QUIC_FRAME_STREAM))
{
lsquic_packet_out_elide_reset_stream_frames(packet_out, stream_id); lsquic_packet_out_elide_reset_stream_frames(packet_out, stream_id);
if (0 == packet_out->po_frame_types) if (0 == packet_out->po_frame_types)
{ {
@ -1417,6 +1416,7 @@ lsquic_send_ctl_elide_stream_frames (lsquic_send_ctl_t *ctl, uint32_t stream_id)
} }
} }
} }
}
/* Count how many packets will remain after the squeezing performed by /* Count how many packets will remain after the squeezing performed by
@ -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)) if (packet_q->bpq_count >= send_ctl_max_bpq_count(ctl, packet_type))
return NULL; return NULL;
bits = lsquic_send_ctl_guess_packno_bits(ctl);
if (packet_q->bpq_count == 0) if (packet_q->bpq_count == 0)
{ {
/* If ACK was written to the low-priority queue first, steal it */ /* 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 ]) if (iter->spi_set[ set ])
break; break;
if (set == 4) if (set >= 4)
{ {
//SPI_DEBUG("%s: cannot find any", __func__); //SPI_DEBUG("%s: cannot find any", __func__);
return -1; 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; bits_left -= cur_enc_code.bits;
while (bits_left <= 32) while (bits_left <= 32)
{ {
*p_dst++ = bits >> 32; *p_dst++ = (unsigned char) (bits >> 32);
bits <<= 8; bits <<= 8;
bits_left += 8; bits_left += 8;
if (p_dst == dst_end) 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); assert(bits_left < 40 && bits_left > 0);
bits |= ((uint64_t)1 << bits_left) - 1; bits |= ((uint64_t)1 << bits_left) - 1;
*p_dst++ = bits >> 32; *p_dst++ = (unsigned char) (bits >> 32);
} }
return p_dst - dst; return p_dst - dst;
@ -5925,7 +5925,7 @@ lshpack_enc_enc_str (unsigned char *const dst, size_t dst_len,
{ {
if (str_len < 127) if (str_len < 127)
{ {
*dst = str_len; *dst = (unsigned char) str_len;
memcpy(dst + 1, str, str_len); memcpy(dst + 1, str, str_len);
return 1 + str_len; return 1 + str_len;
} }

View file

@ -174,9 +174,16 @@ struct http_client_ctx {
unsigned hcc_cc_reqs_per_conn; unsigned hcc_cc_reqs_per_conn;
unsigned hcc_n_open_conns; 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 { enum {
HCC_SEEN_FIN = (1 << 1), HCC_SEEN_FIN = (1 << 1),
HCC_ABORT_ON_INCOMPLETE = (1 << 2), HCC_ABORT_ON_INCOMPLETE = (1 << 2),
HCC_RTT_INFO = (1 << 3),
} hcc_flags; } hcc_flags;
struct prog *prog; struct prog *prog;
}; };
@ -218,9 +225,18 @@ display_cert_chain (lsquic_conn_t *);
static void static void
create_connections (struct http_client_ctx *client_ctx) 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 && while (client_ctx->hcc_n_open_conns < client_ctx->hcc_concurrency &&
client_ctx->hcc_total_n_reqs > 0) 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"); LSQ_ERROR("connection failed");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -330,15 +346,74 @@ http_client_on_conn_closed (lsquic_conn_t *conn)
static void 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; 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); display_cert_chain(conn);
if (ok) if (status != LSQ_HSK_FAIL)
{ {
conn_h = lsquic_conn_get_ctx(conn); conn_h = lsquic_conn_get_ctx(conn);
++s_stat_conns_ok; ++s_stat_conns_ok;
@ -703,6 +778,7 @@ usage (const char *prog)
" -I Abort on incomplete reponse from server\n" " -I Abort on incomplete reponse from server\n"
" -4 Prefer IPv4 when resolving hostname\n" " -4 Prefer IPv4 when resolving hostname\n"
" -6 Prefer IPv6 when resolving hostname\n" " -6 Prefer IPv6 when resolving hostname\n"
" -0 FILE Provide RTT info file (reading or writing)\n"
#ifndef WIN32 #ifndef WIN32
" -C DIR Certificate store. If specified, server certificate will\n" " -C DIR Certificate store. If specified, server certificate will\n"
" be verified.\n" " be verified.\n"
@ -758,6 +834,7 @@ init_x509_cert_store (const char *path)
X509 *cert; X509 *cert;
DIR *dir; DIR *dir;
char file_path[NAME_MAX]; char file_path[NAME_MAX];
int ret;
dir = opendir(path); dir = opendir(path);
if (!dir) if (!dir)
@ -772,7 +849,18 @@ init_x509_cert_store (const char *path)
{ {
if (ends_in_pem(ent->d_name)) 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); cert = file2cert(file_path);
if (cert) if (cert)
{ {
@ -945,6 +1033,7 @@ main (int argc, char **argv)
struct path_elem *pe; struct path_elem *pe;
struct sport_head sports; struct sport_head sports;
struct prog prog; struct prog prog;
unsigned char zero_rtt[8192];
TAILQ_INIT(&sports); TAILQ_INIT(&sports);
memset(&client_ctx, 0, sizeof(client_ctx)); 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_cc_reqs_per_conn = 1;
client_ctx.hcc_reqs_per_conn = 1; client_ctx.hcc_reqs_per_conn = 1;
client_ctx.hcc_total_n_reqs = 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; client_ctx.prog = &prog;
#ifdef WIN32 #ifdef WIN32
WSADATA wsd; WSADATA wsd;
@ -963,7 +1055,7 @@ main (int argc, char **argv)
prog_init(&prog, LSENG_HTTP, &sports, &http_client_if, &client_ctx); 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 #ifndef WIN32
"C:atT:" "C:atT:"
#endif #endif
@ -1061,6 +1153,14 @@ main (int argc, char **argv)
} }
} }
break; 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: default:
if (0 != prog_set_opt(&prog, opt, optarg)) if (0 != prog_set_opt(&prog, opt, optarg))
exit(1); exit(1);
@ -1071,6 +1171,20 @@ main (int argc, char **argv)
prog.prog_api.ea_stats_fh = stats_fh; prog.prog_api.ea_stats_fh = stats_fh;
#endif #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)) if (TAILQ_EMPTY(&client_ctx.hcc_path_elems))
{ {
fprintf(stderr, "Specify at least one path using -p option\n"); fprintf(stderr, "Specify at least one path using -p option\n");
@ -1090,6 +1204,13 @@ main (int argc, char **argv)
s = prog_run(&prog); 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) if (stats_fh)
{ {
elapsed = (long double) (lsquic_time_now() - start_time) / 1000000; elapsed = (long double) (lsquic_time_now() - start_time) / 1000000;

View file

@ -253,7 +253,7 @@ prog_eb (struct prog *prog)
int int
prog_connect (struct prog *prog) prog_connect (struct prog *prog, unsigned char *zero_rtt, size_t zero_rtt_len)
{ {
struct service_port *sport; struct service_port *sport;
@ -262,7 +262,7 @@ prog_connect (struct prog *prog)
(struct sockaddr *) &sport->sp_local_addr, (struct sockaddr *) &sport->sp_local_addr,
(struct sockaddr *) &sport->sas, sport, NULL, (struct sockaddr *) &sport->sas, sport, NULL,
prog->prog_hostname ? prog->prog_hostname : sport->host, 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; return -1;
prog_process_conns(prog); prog_process_conns(prog);

View file

@ -68,7 +68,7 @@ int
prog_prep (struct prog *); prog_prep (struct prog *);
int int
prog_connect (struct prog *); prog_connect (struct prog *, unsigned char *, size_t);
void void
prog_print_common_options (const struct prog *, FILE *); prog_print_common_options (const struct prog *, FILE *);

View file

@ -1,18 +1,13 @@
# Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE. # Copyright (c) 2017 - 2019 LiteSpeed Technologies Inc. See LICENSE.
INCLUDE_DIRECTORIES(../../src/liblsquic) INCLUDE_DIRECTORIES(../../src/liblsquic)
ENABLE_TESTING() ENABLE_TESTING()
SET(LIBS lsquic ${BORINGSSL_LIB_crypto} ${BORINGSSL_LIB_ssl} ${ZLIB_LIB})
IF (MSVC) IF (MSVC)
LIST(APPEND LIBS ws2_32)
SET(ADDL_SOURCES ../../wincompat/getopt.c ../../wincompat/getopt1.c) SET(ADDL_SOURCES ../../wincompat/getopt.c ../../wincompat/getopt1.c)
SET(LIB_FLAGS "-FORCE:MULTIPLE") SET(LIB_FLAGS "-FORCE:MULTIPLE")
ELSE() ELSE()
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-value") SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-value")
LIST(APPEND LIBS m pthread)
ENDIF() ENDIF()
SET(TESTS SET(TESTS
@ -72,6 +67,7 @@ IF (CMAKE_SYSTEM_NAME STREQUAL "Linux")
SET(TESTS ${TESTS} frame_rw) SET(TESTS ${TESTS} frame_rw)
ENDIF() ENDIF()
FOREACH(TEST_NAME ${TESTS}) FOREACH(TEST_NAME ${TESTS})
ADD_EXECUTABLE(test_${TEST_NAME} test_${TEST_NAME}.c ${ADDL_SOURCES}) ADD_EXECUTABLE(test_${TEST_NAME} test_${TEST_NAME}.c ${ADDL_SOURCES})
TARGET_LINK_LIBRARIES(test_${TEST_NAME} ${LIBS} ${LIB_FLAGS}) 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}) ADD_EXECUTABLE(graph_cubic graph_cubic.c ${ADDL_SOURCES})
TARGET_LINK_LIBRARIES(graph_cubic ${LIBS}) TARGET_LINK_LIBRARIES(graph_cubic ${LIBS})

View file

@ -460,7 +460,6 @@ test_priority (void)
} }
static void static void
test_errors (void) test_errors (void)
{ {
@ -596,7 +595,6 @@ perl tools/hpack.pl :method GET :path /index.html :authority www.example.com :sc
} }
int int
main (void) main (void)
{ {