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

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