Release 2.23.0
- [FEATURE] IETF Client 0-RTT support. - [BUGFIX] Do not schedule MTU probe on first tick. - [BUGFIX] Parsing DATAGRAM frame. - [BUGFIX] If push promise fails, do not invoke hset destructor. - [BUGFIX] Client: When connections are IDed by port number, check DCID. Fixes issue #176. - Revert the 2.22.1 lsquic_is_valid_hs_packet change. All that was necessary is a change to the way we call it in lsquic_engine. No change to the function itself is required.
This commit is contained in:
parent
f3d781aa59
commit
04f8f447b2
12
CHANGELOG
12
CHANGELOG
|
@ -1,3 +1,15 @@
|
|||
2020-10-13
|
||||
- 2.23.0
|
||||
- [FEATURE] IETF Client 0-RTT support.
|
||||
- [BUGFIX] Do not schedule MTU probe on first tick.
|
||||
- [BUGFIX] Parsing DATAGRAM frame.
|
||||
- [BUGFIX] If push promise fails, do not invoke hset destructor.
|
||||
- [BUGFIX] Client: When connections are IDed by port number, check DCID.
|
||||
Fixes issue #176.
|
||||
- Revert the 2.22.1 lsquic_is_valid_hs_packet change. All that was
|
||||
necessary is a change to the way we call it in lsquic_engine. No
|
||||
change to the function itself is required.
|
||||
|
||||
2020-10-08
|
||||
- 2.22.1
|
||||
- [BUGFIX] Function that checks validity of handshake packets.
|
||||
|
|
|
@ -1506,8 +1506,14 @@ idle_on_write (lsquic_stream_t *stream, lsquic_stream_ctx_t *st_h)
|
|||
headers.count = sizeof(header_arr) / sizeof(header_arr[0]);
|
||||
req = new_req(GET, push_path->path, st_h->req->authority_str);
|
||||
if (req)
|
||||
(void) lsquic_conn_push_stream(lsquic_stream_conn(stream),
|
||||
req, stream, &headers);
|
||||
{
|
||||
if (0 != lsquic_conn_push_stream(lsquic_stream_conn(stream),
|
||||
req, stream, &headers))
|
||||
{
|
||||
LSQ_WARN("stream push failed");
|
||||
interop_server_hset_destroy(req);
|
||||
}
|
||||
}
|
||||
else
|
||||
LSQ_WARN("cannot allocate req for push");
|
||||
free(push_path);
|
||||
|
|
|
@ -93,7 +93,11 @@ load_cert (struct lsquic_hash *certs, const char *optarg)
|
|||
SSL_CTX_set_max_proto_version(cert->ce_ssl_ctx, TLS1_3_VERSION);
|
||||
SSL_CTX_set_default_verify_paths(cert->ce_ssl_ctx);
|
||||
SSL_CTX_set_alpn_select_cb(cert->ce_ssl_ctx, select_alpn, NULL);
|
||||
SSL_CTX_set_early_data_enabled(cert->ce_ssl_ctx, 1); /* XXX */
|
||||
{
|
||||
const char *const s = getenv("LSQUIC_ENABLE_EARLY_DATA");
|
||||
if (!s || atoi(s))
|
||||
SSL_CTX_set_early_data_enabled(cert->ce_ssl_ctx, 1); /* XXX */
|
||||
}
|
||||
if (1 != SSL_CTX_use_certificate_chain_file(cert->ce_ssl_ctx, cert_file))
|
||||
{
|
||||
LSQ_ERROR("SSL_CTX_use_certificate_chain_file failed: %s", cert_file);
|
||||
|
|
|
@ -1993,6 +1993,11 @@ set_engine_option (struct lsquic_engine_settings *settings,
|
|||
settings->es_qpack_dec_max_blocked = atoi(val);
|
||||
return 0;
|
||||
}
|
||||
if (0 == strncmp(name, "init_max_streams_bidi", 21))
|
||||
{
|
||||
settings->es_init_max_streams_bidi = atoi(val);
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case 23:
|
||||
if (0 == strncmp(name, "max_udp_payload_size_rx", 23))
|
||||
|
|
|
@ -58,9 +58,9 @@ developed by the IETF. Both types are included in a single enum:
|
|||
|
||||
IETF QUIC version ID 29
|
||||
|
||||
.. member:: LSQVER_ID30; this version is deprecated.
|
||||
.. member:: LSQVER_ID30
|
||||
|
||||
IETF QUIC version ID 30
|
||||
IETF QUIC version ID 30; this version is deprecated.
|
||||
|
||||
.. member:: LSQVER_ID31
|
||||
|
||||
|
|
|
@ -24,9 +24,9 @@ copyright = u'2020, LiteSpeed Technologies'
|
|||
author = u'LiteSpeed Technologies'
|
||||
|
||||
# The short X.Y version
|
||||
version = u'2.22'
|
||||
version = u'2.23'
|
||||
# The full version, including alpha/beta/rc tags
|
||||
release = u'2.22.1'
|
||||
release = u'2.23.0'
|
||||
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
|
|
|
@ -18,7 +18,7 @@ connections.
|
|||
|
||||
To aid development, there is a :macro:`LSQUIC_FORCED_TCID0_VERSIONS` that
|
||||
specifies the list of versions with 0-sized connections. (If you, for
|
||||
example, want to turn them.)
|
||||
example, want to turn them off.)
|
||||
|
||||
Once gQUIC becomes deprecated in the future, there will remain no technical
|
||||
reason why a single engine instance could not be used both for client and
|
||||
|
|
|
@ -24,8 +24,8 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
#define LSQUIC_MAJOR_VERSION 2
|
||||
#define LSQUIC_MINOR_VERSION 22
|
||||
#define LSQUIC_PATCH_VERSION 1
|
||||
#define LSQUIC_MINOR_VERSION 23
|
||||
#define LSQUIC_PATCH_VERSION 0
|
||||
|
||||
/**
|
||||
* Engine flags:
|
||||
|
@ -1923,8 +1923,7 @@ lsquic_get_h3_alpns (unsigned versions);
|
|||
* been established: it will return incorrect result.
|
||||
*/
|
||||
int
|
||||
lsquic_is_valid_hs_packet (lsquic_engine_t *, const unsigned char *,
|
||||
size_t bufsz, size_t packet_in_sz);
|
||||
lsquic_is_valid_hs_packet (lsquic_engine_t *, const unsigned char *, size_t);
|
||||
|
||||
/**
|
||||
* Parse cid from packet stored in `buf' and store it to `cid'. Returns 0
|
||||
|
|
|
@ -282,6 +282,10 @@ struct conn_iface
|
|||
/* Optional method */
|
||||
size_t
|
||||
(*ci_get_min_datagram_size) (struct lsquic_conn *);
|
||||
|
||||
/* Optional method */
|
||||
void
|
||||
(*ci_early_data_failed) (struct lsquic_conn *);
|
||||
};
|
||||
|
||||
#define LSCONN_CCE_BITS 3
|
||||
|
|
|
@ -15,6 +15,8 @@ struct ssl_stream_method_st;
|
|||
struct ssl_st;
|
||||
struct sockaddr;
|
||||
struct conn_cid_elem;
|
||||
struct lsquic_engine_settings;
|
||||
enum lsquic_version;
|
||||
|
||||
#define DNONC_LENGTH 32
|
||||
#define SRST_LENGTH 16
|
||||
|
@ -242,12 +244,6 @@ struct enc_session_funcs_gquic
|
|||
void (*esf_reset_cid) (enc_session_t *, const lsquic_cid_t *);
|
||||
};
|
||||
|
||||
enum iquic_handshake_status {
|
||||
IHS_WANT_READ,
|
||||
IHS_WANT_WRITE,
|
||||
IHS_STOP,
|
||||
};
|
||||
|
||||
struct crypto_stream_if
|
||||
{
|
||||
ssize_t (*csi_write) (void *stream, const void *buf, size_t len);
|
||||
|
@ -371,4 +367,10 @@ lsquic_sess_resume_version (const unsigned char *, size_t);
|
|||
*/
|
||||
#define IQUIC_TAG_LEN 16
|
||||
|
||||
/* Return number of bytes written to `buf' or -1 on error */
|
||||
int
|
||||
lsquic_enc_sess_ietf_gen_quic_ctx (
|
||||
const struct lsquic_engine_settings *settings,
|
||||
enum lsquic_version version, unsigned char *buf, size_t bufsz);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -107,6 +107,20 @@ static void
|
|||
no_sess_ticket (enum alarm_id alarm_id, void *ctx,
|
||||
lsquic_time_t expiry, lsquic_time_t now);
|
||||
|
||||
static int
|
||||
iquic_new_session_cb (SSL *, SSL_SESSION *);
|
||||
|
||||
static void
|
||||
keylog_callback (const SSL *, const char *);
|
||||
|
||||
static enum ssl_verify_result_t
|
||||
verify_server_cert_callback (SSL *, uint8_t *out_alert);
|
||||
|
||||
static void
|
||||
maybe_setup_key_logging (struct enc_sess_iquic *);
|
||||
|
||||
static void
|
||||
iquic_esfi_destroy (enc_session_t *);
|
||||
|
||||
#define SAMPLE_SZ 16
|
||||
|
||||
|
@ -223,7 +237,7 @@ struct enc_sess_iquic
|
|||
lsquic_cid_t esi_iscid; /* Initial SCID */
|
||||
unsigned esi_key_phase;
|
||||
enum {
|
||||
ESI_INITIALIZED = 1 << 0,
|
||||
ESI_UNUSED0 = 1 << 0,
|
||||
ESI_LOG_SECRETS = 1 << 1,
|
||||
ESI_HANDSHAKE_OK = 1 << 2,
|
||||
ESI_ODCID = 1 << 3,
|
||||
|
@ -243,17 +257,15 @@ struct enc_sess_iquic
|
|||
ESI_MAX_PACKNO_INIT = 1 << 17,
|
||||
ESI_MAX_PACKNO_HSK = ESI_MAX_PACKNO_INIT << PNS_HSK,
|
||||
ESI_MAX_PACKNO_APP = ESI_MAX_PACKNO_INIT << PNS_APP,
|
||||
ESI_HAVE_0RTT_TP = 1 << 20,
|
||||
} esi_flags;
|
||||
enum enc_level esi_last_w;
|
||||
unsigned esi_trasec_sz;
|
||||
char *esi_hostname;
|
||||
void *esi_keylog_handle;
|
||||
#ifndef NDEBUG
|
||||
char *esi_sni_bypass;
|
||||
#endif
|
||||
const unsigned char *esi_alpn;
|
||||
unsigned char *esi_sess_resume_buf;
|
||||
size_t esi_sess_resume_sz;
|
||||
/* Need MD and AEAD for key rotation */
|
||||
const EVP_MD *esi_md;
|
||||
const EVP_AEAD *esi_aead;
|
||||
|
@ -279,6 +291,7 @@ struct enc_sess_iquic
|
|||
esi_hp_batch_packets[HP_BATCH_SIZE];
|
||||
unsigned char esi_hp_batch_samples[HP_BATCH_SIZE][SAMPLE_SZ];
|
||||
unsigned char esi_grease;
|
||||
signed char esi_have_forw;
|
||||
};
|
||||
|
||||
|
||||
|
@ -696,24 +709,23 @@ gen_trans_params (struct enc_sess_iquic *enc_sess, unsigned char *buf,
|
|||
|
||||
static SSL_SESSION *
|
||||
maybe_create_SSL_SESSION (struct enc_sess_iquic *enc_sess,
|
||||
const SSL_CTX *ssl_ctx)
|
||||
const SSL_CTX *ssl_ctx, const unsigned char *sess_resume,
|
||||
size_t sess_resume_sz)
|
||||
{
|
||||
SSL_SESSION *ssl_session;
|
||||
lsquic_ver_tag_t ver_tag;
|
||||
enum lsquic_version quic_ver;
|
||||
uint32_t rtt_ver, ticket_sz, trapa_sz;
|
||||
const unsigned char *ticket_buf, *trapa_buf, *p;
|
||||
const unsigned char *const end
|
||||
= enc_sess->esi_sess_resume_buf + enc_sess->esi_sess_resume_sz;
|
||||
const unsigned char *const end = sess_resume + sess_resume_sz;
|
||||
|
||||
if (enc_sess->esi_sess_resume_sz
|
||||
< sizeof(ver_tag) + sizeof(rtt_ver) + sizeof(ticket_sz))
|
||||
if (sess_resume_sz < sizeof(ver_tag) + sizeof(rtt_ver) + sizeof(ticket_sz))
|
||||
{
|
||||
LSQ_DEBUG("rtt buf too short");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = enc_sess->esi_sess_resume_buf;
|
||||
p = sess_resume;
|
||||
memcpy(&ver_tag, p, sizeof(ver_tag));
|
||||
p += sizeof(ver_tag);
|
||||
quic_ver = lsquic_tag2ver(ver_tag);
|
||||
|
@ -760,8 +772,6 @@ maybe_create_SSL_SESSION (struct enc_sess_iquic *enc_sess,
|
|||
p += trapa_sz;
|
||||
assert(p == end);
|
||||
|
||||
(void) /* TODO */ trapa_buf;
|
||||
|
||||
ssl_session = SSL_SESSION_from_bytes(ticket_buf, ticket_sz, ssl_ctx);
|
||||
if (!ssl_session)
|
||||
{
|
||||
|
@ -769,6 +779,22 @@ maybe_create_SSL_SESSION (struct enc_sess_iquic *enc_sess,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (SSL_SESSION_early_data_capable(ssl_session))
|
||||
{
|
||||
if (0 > (quic_ver == LSQVER_ID27 ? lsquic_tp_decode_27
|
||||
: lsquic_tp_decode)(trapa_buf, trapa_sz, 1,
|
||||
&enc_sess->esi_peer_tp))
|
||||
{
|
||||
SSL_SESSION_free(ssl_session);
|
||||
LSQ_WARN("cannot parse stored transport parameters");
|
||||
return NULL;
|
||||
}
|
||||
LSQ_DEBUG("early data capable, will try 0-RTT");
|
||||
enc_sess->esi_flags |= ESI_HAVE_0RTT_TP;
|
||||
}
|
||||
else
|
||||
LSQ_DEBUG("early data not capable -- not trying 0-RTT");
|
||||
|
||||
LSQ_INFO("instantiated SSL_SESSION from serialized buffer");
|
||||
return ssl_session;
|
||||
}
|
||||
|
@ -795,6 +821,16 @@ iquic_esfi_create_client (const char *hostname,
|
|||
struct lsquic_alarmset *alset, unsigned max_streams_uni)
|
||||
{
|
||||
struct enc_sess_iquic *enc_sess;
|
||||
SSL_CTX *ssl_ctx;
|
||||
SSL_SESSION *ssl_session;
|
||||
const struct alpn_map *am;
|
||||
int transpa_len;
|
||||
char errbuf[ERR_ERROR_STRING_BUF_LEN];
|
||||
unsigned char trans_params[0x80
|
||||
#if LSQUIC_TEST_QUANTUM_READINESS
|
||||
+ 4 + lsquic_tp_get_quantum_sz()
|
||||
#endif
|
||||
];
|
||||
|
||||
fiu_return_on("enc_sess_ietf/create_client", NULL);
|
||||
|
||||
|
@ -802,18 +838,6 @@ iquic_esfi_create_client (const char *hostname,
|
|||
if (!enc_sess)
|
||||
return NULL;
|
||||
|
||||
if (hostname)
|
||||
{
|
||||
enc_sess->esi_hostname = strdup(hostname);
|
||||
if (!enc_sess->esi_hostname)
|
||||
{
|
||||
free(enc_sess);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
enc_sess->esi_hostname = NULL;
|
||||
|
||||
enc_sess->esi_enpub = enpub;
|
||||
enc_sess->esi_streams = crypto_streams;
|
||||
enc_sess->esi_cryst_if = cryst_if;
|
||||
|
@ -844,35 +868,123 @@ iquic_esfi_create_client (const char *hostname,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* Have to wait until the call to init_client() -- this is when the
|
||||
* result of version negotiation is known.
|
||||
*/
|
||||
enc_sess->esi_max_streams_uni = max_streams_uni;
|
||||
|
||||
if (enc_sess->esi_enpub->enp_alpn)
|
||||
enc_sess->esi_alpn = enc_sess->esi_enpub->enp_alpn;
|
||||
else if (enc_sess->esi_enpub->enp_flags & ENPUB_HTTP)
|
||||
{
|
||||
for (am = s_h3_alpns; am < s_h3_alpns + sizeof(s_h3_alpns)
|
||||
/ sizeof(s_h3_alpns[0]); ++am)
|
||||
if (am->version == enc_sess->esi_ver_neg->vn_ver)
|
||||
goto alpn_selected;
|
||||
LSQ_ERROR("version %s has no matching ALPN",
|
||||
lsquic_ver2str[enc_sess->esi_ver_neg->vn_ver]);
|
||||
goto err;
|
||||
alpn_selected:
|
||||
enc_sess->esi_alpn = am->alpn;
|
||||
}
|
||||
|
||||
LSQ_DEBUG("Create new SSL_CTX");
|
||||
ssl_ctx = SSL_CTX_new(TLS_method());
|
||||
if (!ssl_ctx)
|
||||
{
|
||||
LSQ_ERROR("cannot create SSL context: %s",
|
||||
ERR_error_string(ERR_get_error(), errbuf));
|
||||
goto err;
|
||||
}
|
||||
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
|
||||
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
|
||||
SSL_CTX_set_default_verify_paths(ssl_ctx);
|
||||
SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_CLIENT);
|
||||
if (enc_sess->esi_enpub->enp_stream_if->on_sess_resume_info)
|
||||
SSL_CTX_sess_set_new_cb(ssl_ctx, iquic_new_session_cb);
|
||||
if (enc_sess->esi_enpub->enp_kli)
|
||||
SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
|
||||
if (enc_sess->esi_enpub->enp_verify_cert
|
||||
|| LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)
|
||||
|| LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_QLOG))
|
||||
SSL_CTX_set_custom_verify(ssl_ctx, SSL_VERIFY_PEER,
|
||||
verify_server_cert_callback);
|
||||
SSL_CTX_set_early_data_enabled(ssl_ctx, 1);
|
||||
|
||||
enc_sess->esi_ssl = SSL_new(ssl_ctx);
|
||||
if (!enc_sess->esi_ssl)
|
||||
{
|
||||
LSQ_ERROR("cannot create SSL object: %s",
|
||||
ERR_error_string(ERR_get_error(), errbuf));
|
||||
goto err;
|
||||
}
|
||||
|
||||
transpa_len = gen_trans_params(enc_sess, trans_params,
|
||||
sizeof(trans_params));
|
||||
if (transpa_len < 0)
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
if (1 != SSL_set_quic_transport_params(enc_sess->esi_ssl, trans_params,
|
||||
transpa_len))
|
||||
{
|
||||
LSQ_ERROR("cannot set QUIC transport params: %s",
|
||||
ERR_error_string(ERR_get_error(), errbuf));
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (!(SSL_set_quic_method(enc_sess->esi_ssl, &cry_quic_method)))
|
||||
{
|
||||
LSQ_INFO("could not set stream method");
|
||||
goto err;
|
||||
}
|
||||
|
||||
maybe_setup_key_logging(enc_sess);
|
||||
|
||||
if (enc_sess->esi_alpn &&
|
||||
0 != SSL_set_alpn_protos(enc_sess->esi_ssl, enc_sess->esi_alpn,
|
||||
enc_sess->esi_alpn[0] + 1))
|
||||
{
|
||||
LSQ_ERROR("cannot set ALPN: %s",
|
||||
ERR_error_string(ERR_get_error(), errbuf));
|
||||
goto err;
|
||||
}
|
||||
if (1 != SSL_set_tlsext_host_name(enc_sess->esi_ssl, hostname))
|
||||
{
|
||||
LSQ_ERROR("cannot set hostname: %s",
|
||||
ERR_error_string(ERR_get_error(), errbuf));
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (sess_resume && sess_resume_sz)
|
||||
{
|
||||
enc_sess->esi_sess_resume_buf = malloc(sess_resume_sz);
|
||||
if (enc_sess->esi_sess_resume_buf)
|
||||
ssl_session = maybe_create_SSL_SESSION(enc_sess, ssl_ctx,
|
||||
sess_resume, sess_resume_sz);
|
||||
if (ssl_session)
|
||||
{
|
||||
memcpy(enc_sess->esi_sess_resume_buf, sess_resume, sess_resume_sz);
|
||||
enc_sess->esi_sess_resume_sz = sess_resume_sz;
|
||||
(void) /* This only ever returns 1: */
|
||||
SSL_set_session(enc_sess->esi_ssl, ssl_session);
|
||||
SSL_SESSION_free(ssl_session);
|
||||
ssl_session = NULL;
|
||||
enc_sess->esi_flags |= ESI_USE_SSL_TICKET;
|
||||
}
|
||||
else
|
||||
enc_sess->esi_sess_resume_sz = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
enc_sess->esi_sess_resume_buf = NULL;
|
||||
enc_sess->esi_sess_resume_sz = 0;
|
||||
}
|
||||
|
||||
SSL_set_ex_data(enc_sess->esi_ssl, s_idx, enc_sess);
|
||||
SSL_set_connect_state(enc_sess->esi_ssl);
|
||||
|
||||
if (enc_sess->esi_enpub->enp_stream_if->on_sess_resume_info)
|
||||
enc_sess->esi_flags |= ESI_WANT_TICKET;
|
||||
enc_sess->esi_alset = alset;
|
||||
lsquic_alarmset_init_alarm(enc_sess->esi_alset, AL_SESS_TICKET,
|
||||
no_sess_ticket, enc_sess);
|
||||
|
||||
enc_sess->esi_max_streams_uni = max_streams_uni;
|
||||
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
return enc_sess;
|
||||
|
||||
err:
|
||||
if (enc_sess)
|
||||
iquic_esfi_destroy(enc_sess);
|
||||
if (ssl_ctx)
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1242,6 +1354,7 @@ iquic_esfi_init_server (enc_session_t *enc_session_p)
|
|||
{
|
||||
struct enc_sess_iquic *const enc_sess = enc_session_p;
|
||||
const struct alpn_map *am;
|
||||
unsigned quic_ctx_idx;
|
||||
int transpa_len;
|
||||
SSL_CTX *ssl_ctx = NULL;
|
||||
union {
|
||||
|
@ -1287,9 +1400,10 @@ iquic_esfi_init_server (enc_session_t *enc_session_p)
|
|||
LSQ_INFO("could not set stream method");
|
||||
return -1;
|
||||
}
|
||||
/* TODO: set to transport parameter string instead of the constant string */
|
||||
quic_ctx_idx = enc_sess->esi_conn->cn_version == LSQVER_ID27 ? 0 : 1;
|
||||
if (!SSL_set_quic_early_data_context(enc_sess->esi_ssl,
|
||||
(unsigned char *) "lsquic", 6))
|
||||
enc_sess->esi_enpub->enp_quic_ctx_buf[quic_ctx_idx],
|
||||
enc_sess->esi_enpub->enp_quic_ctx_sz[quic_ctx_idx]))
|
||||
{
|
||||
LSQ_INFO("could not set early data context");
|
||||
return -1;
|
||||
|
@ -1317,7 +1431,6 @@ iquic_esfi_init_server (enc_session_t *enc_session_p)
|
|||
SSL_set_ex_data(enc_sess->esi_ssl, s_idx, enc_sess);
|
||||
SSL_set_accept_state(enc_sess->esi_ssl);
|
||||
LSQ_DEBUG("initialized server enc session");
|
||||
enc_sess->esi_flags |= ESI_INITIALIZED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1415,129 +1528,6 @@ iquic_new_session_cb (SSL *ssl, SSL_SESSION *session)
|
|||
}
|
||||
|
||||
|
||||
static int
|
||||
init_client (struct enc_sess_iquic *const enc_sess)
|
||||
{
|
||||
SSL_CTX *ssl_ctx;
|
||||
SSL_SESSION *ssl_session;
|
||||
const struct alpn_map *am;
|
||||
int transpa_len;
|
||||
char errbuf[ERR_ERROR_STRING_BUF_LEN];
|
||||
#define hexbuf errbuf /* This is a dual-purpose buffer */
|
||||
unsigned char trans_params[0x80
|
||||
#if LSQUIC_TEST_QUANTUM_READINESS
|
||||
+ 4 + lsquic_tp_get_quantum_sz()
|
||||
#endif
|
||||
];
|
||||
|
||||
if (enc_sess->esi_enpub->enp_alpn)
|
||||
enc_sess->esi_alpn = enc_sess->esi_enpub->enp_alpn;
|
||||
else if (enc_sess->esi_enpub->enp_flags & ENPUB_HTTP)
|
||||
{
|
||||
for (am = s_h3_alpns; am < s_h3_alpns + sizeof(s_h3_alpns)
|
||||
/ sizeof(s_h3_alpns[0]); ++am)
|
||||
if (am->version == enc_sess->esi_ver_neg->vn_ver)
|
||||
goto ok;
|
||||
LSQ_ERROR("version %s has no matching ALPN",
|
||||
lsquic_ver2str[enc_sess->esi_ver_neg->vn_ver]);
|
||||
return -1;
|
||||
ok: enc_sess->esi_alpn = am->alpn;
|
||||
}
|
||||
|
||||
LSQ_DEBUG("Create new SSL_CTX");
|
||||
ssl_ctx = SSL_CTX_new(TLS_method());
|
||||
if (!ssl_ctx)
|
||||
{
|
||||
LSQ_ERROR("cannot create SSL context: %s",
|
||||
ERR_error_string(ERR_get_error(), errbuf));
|
||||
goto err;
|
||||
}
|
||||
SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
|
||||
SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
|
||||
SSL_CTX_set_default_verify_paths(ssl_ctx);
|
||||
SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_CLIENT);
|
||||
if (enc_sess->esi_enpub->enp_stream_if->on_sess_resume_info)
|
||||
SSL_CTX_sess_set_new_cb(ssl_ctx, iquic_new_session_cb);
|
||||
if (enc_sess->esi_enpub->enp_kli)
|
||||
SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
|
||||
if (enc_sess->esi_enpub->enp_verify_cert
|
||||
|| LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_EVENT)
|
||||
|| LSQ_LOG_ENABLED_EXT(LSQ_LOG_DEBUG, LSQLM_QLOG))
|
||||
SSL_CTX_set_custom_verify(ssl_ctx, SSL_VERIFY_PEER,
|
||||
verify_server_cert_callback);
|
||||
SSL_CTX_set_early_data_enabled(ssl_ctx, 1);
|
||||
|
||||
transpa_len = gen_trans_params(enc_sess, trans_params,
|
||||
sizeof(trans_params));
|
||||
if (transpa_len < 0)
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
|
||||
enc_sess->esi_ssl = SSL_new(ssl_ctx);
|
||||
if (!enc_sess->esi_ssl)
|
||||
{
|
||||
LSQ_ERROR("cannot create SSL object: %s",
|
||||
ERR_error_string(ERR_get_error(), errbuf));
|
||||
goto err;
|
||||
}
|
||||
if (!(SSL_set_quic_method(enc_sess->esi_ssl, &cry_quic_method)))
|
||||
{
|
||||
LSQ_INFO("could not set stream method");
|
||||
goto err;
|
||||
}
|
||||
maybe_setup_key_logging(enc_sess);
|
||||
if (1 != SSL_set_quic_transport_params(enc_sess->esi_ssl, trans_params,
|
||||
transpa_len))
|
||||
{
|
||||
LSQ_ERROR("cannot set QUIC transport params: %s",
|
||||
ERR_error_string(ERR_get_error(), errbuf));
|
||||
goto err;
|
||||
}
|
||||
if (enc_sess->esi_alpn &&
|
||||
0 != SSL_set_alpn_protos(enc_sess->esi_ssl, enc_sess->esi_alpn,
|
||||
enc_sess->esi_alpn[0] + 1))
|
||||
{
|
||||
LSQ_ERROR("cannot set ALPN: %s",
|
||||
ERR_error_string(ERR_get_error(), errbuf));
|
||||
goto err;
|
||||
}
|
||||
if (1 != SSL_set_tlsext_host_name(enc_sess->esi_ssl,
|
||||
enc_sess->esi_hostname))
|
||||
{
|
||||
LSQ_ERROR("cannot set hostname: %s",
|
||||
ERR_error_string(ERR_get_error(), errbuf));
|
||||
goto err;
|
||||
}
|
||||
free(enc_sess->esi_hostname);
|
||||
enc_sess->esi_hostname = NULL;
|
||||
if (enc_sess->esi_sess_resume_buf)
|
||||
{
|
||||
ssl_session = maybe_create_SSL_SESSION(enc_sess, ssl_ctx);
|
||||
if (ssl_session)
|
||||
{
|
||||
if (SSL_set_session(enc_sess->esi_ssl, ssl_session))
|
||||
enc_sess->esi_flags |= ESI_USE_SSL_TICKET;
|
||||
else
|
||||
LSQ_WARN("cannot set session");
|
||||
}
|
||||
}
|
||||
|
||||
SSL_set_ex_data(enc_sess->esi_ssl, s_idx, enc_sess);
|
||||
SSL_set_connect_state(enc_sess->esi_ssl);
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
LSQ_DEBUG("initialized client enc session");
|
||||
enc_sess->esi_flags |= ESI_INITIALIZED;
|
||||
return 0;
|
||||
|
||||
err:
|
||||
if (ssl_ctx)
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
return -1;
|
||||
#undef hexbuf
|
||||
}
|
||||
|
||||
|
||||
struct crypto_params
|
||||
{
|
||||
const EVP_AEAD *aead;
|
||||
|
@ -1607,14 +1597,72 @@ get_crypto_params (const struct enc_sess_iquic *enc_sess,
|
|||
}
|
||||
|
||||
|
||||
/* [draft-ietf-quic-transport-31] Section 7.4.1:
|
||||
" If 0-RTT data is accepted by the server, the server MUST NOT reduce
|
||||
" any limits or alter any values that might be violated by the client
|
||||
" with its 0-RTT data. In particular, a server that accepts 0-RTT data
|
||||
" MUST NOT set values for the following parameters (Section 18.2) that
|
||||
" are smaller than the remembered value of the parameters.
|
||||
"
|
||||
" * active_connection_id_limit
|
||||
"
|
||||
" * initial_max_data
|
||||
"
|
||||
" * initial_max_stream_data_bidi_local
|
||||
"
|
||||
" * initial_max_stream_data_bidi_remote
|
||||
"
|
||||
" * initial_max_stream_data_uni
|
||||
"
|
||||
" * initial_max_streams_bidi
|
||||
"
|
||||
" * initial_max_streams_uni
|
||||
*/
|
||||
#define REDUCTION_PROHIBITED_TPS (0 \
|
||||
| (1 << TPI_ACTIVE_CONNECTION_ID_LIMIT) \
|
||||
| (1 << TPI_INIT_MAX_DATA) \
|
||||
| (1 << TPI_INIT_MAX_STREAMS_UNI) \
|
||||
| (1 << TPI_INIT_MAX_STREAMS_BIDI) \
|
||||
| (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL) \
|
||||
| (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE) \
|
||||
| (1 << TPI_INIT_MAX_STREAM_DATA_UNI) \
|
||||
)
|
||||
|
||||
|
||||
static int
|
||||
check_server_tps_for_violations (const struct enc_sess_iquic *enc_sess,
|
||||
const struct transport_params *params_0rtt,
|
||||
const struct transport_params *new_params)
|
||||
{
|
||||
enum transport_param_id tpi;
|
||||
|
||||
for (tpi = 0; tpi <= MAX_NUMERIC_TPI; ++tpi)
|
||||
if ((1 << tpi) & REDUCTION_PROHIBITED_TPS)
|
||||
if (new_params->tp_numerics[tpi] > params_0rtt->tp_numerics[tpi])
|
||||
{
|
||||
LSQ_INFO("server's new TP %s increased in value from %"PRIu64
|
||||
" to %"PRIu64, lsquic_tpi2str[tpi],
|
||||
params_0rtt->tp_numerics[tpi],
|
||||
new_params->tp_numerics[tpi]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
LSQ_DEBUG("server's new transport parameters do not violate save 0-RTT "
|
||||
"parameters");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
get_peer_transport_params (struct enc_sess_iquic *enc_sess)
|
||||
{
|
||||
struct transport_params *const trans_params = &enc_sess->esi_peer_tp;
|
||||
struct transport_params params_0rtt;
|
||||
const uint8_t *params_buf;
|
||||
size_t bufsz;
|
||||
char *params_str;
|
||||
const enum lsquic_version version = enc_sess->esi_conn->cn_version;
|
||||
int have_0rtt_tp;
|
||||
|
||||
SSL_get_peer_quic_transport_params(enc_sess->esi_ssl, ¶ms_buf, &bufsz);
|
||||
if (!params_buf)
|
||||
|
@ -1623,6 +1671,13 @@ get_peer_transport_params (struct enc_sess_iquic *enc_sess)
|
|||
return -1;
|
||||
}
|
||||
|
||||
have_0rtt_tp = !!(enc_sess->esi_flags & ESI_HAVE_0RTT_TP);
|
||||
if (have_0rtt_tp)
|
||||
{
|
||||
params_0rtt = enc_sess->esi_peer_tp;
|
||||
enc_sess->esi_flags &= ~ESI_HAVE_0RTT_TP;
|
||||
}
|
||||
|
||||
LSQ_DEBUG("have peer transport parameters (%zu bytes)", bufsz);
|
||||
if (0 > (version == LSQVER_ID27 ? lsquic_tp_decode_27
|
||||
: lsquic_tp_decode)(params_buf, bufsz,
|
||||
|
@ -1646,6 +1701,10 @@ get_peer_transport_params (struct enc_sess_iquic *enc_sess)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (have_0rtt_tp && 0 != check_server_tps_for_violations(enc_sess,
|
||||
¶ms_0rtt, trans_params))
|
||||
return -1;
|
||||
|
||||
const lsquic_cid_t *const cids[LAST_TPI + 1] = {
|
||||
[TP_CID_IDX(TPI_ORIGINAL_DEST_CID)] = enc_sess->esi_flags & ESI_ODCID ? &enc_sess->esi_odcid : NULL,
|
||||
[TP_CID_IDX(TPI_RETRY_SOURCE_CID)] = enc_sess->esi_flags & ESI_RSCID ? &enc_sess->esi_rscid : NULL,
|
||||
|
@ -1771,6 +1830,14 @@ maybe_get_peer_transport_params (struct enc_sess_iquic *enc_sess)
|
|||
}
|
||||
|
||||
|
||||
enum iquic_handshake_status {
|
||||
IHS_WANT_READ,
|
||||
IHS_WANT_WRITE,
|
||||
IHS_WANT_RW,
|
||||
IHS_STOP,
|
||||
};
|
||||
|
||||
|
||||
static enum iquic_handshake_status
|
||||
iquic_esfi_handshake (struct enc_sess_iquic *enc_sess)
|
||||
{
|
||||
|
@ -1791,9 +1858,12 @@ iquic_esfi_handshake (struct enc_sess_iquic *enc_sess)
|
|||
LSQ_DEBUG("retry write");
|
||||
return IHS_WANT_WRITE;
|
||||
case SSL_ERROR_EARLY_DATA_REJECTED:
|
||||
LSQ_DEBUG("early data rejected");
|
||||
hsk_status = LSQ_HSK_RESUMED_FAIL;
|
||||
goto err;
|
||||
LSQ_DEBUG("early data rejected: reset");
|
||||
SSL_reset_early_data_reject(enc_sess->esi_ssl);
|
||||
if (enc_sess->esi_conn->cn_if->ci_early_data_failed)
|
||||
enc_sess->esi_conn->cn_if->ci_early_data_failed(
|
||||
enc_sess->esi_conn);
|
||||
return IHS_WANT_RW;
|
||||
/* fall through */
|
||||
default:
|
||||
LSQ_DEBUG("handshake: %s", ERR_error_string(err, errbuf));
|
||||
|
@ -1866,7 +1936,9 @@ iquic_esfi_get_peer_transport_params (enc_session_t *enc_session_p)
|
|||
{
|
||||
struct enc_sess_iquic *const enc_sess = enc_session_p;
|
||||
|
||||
if (0 == maybe_get_peer_transport_params(enc_sess))
|
||||
if (enc_sess->esi_flags & ESI_HAVE_0RTT_TP)
|
||||
return &enc_sess->esi_peer_tp;
|
||||
else if (0 == maybe_get_peer_transport_params(enc_sess))
|
||||
return &enc_sess->esi_peer_tp;
|
||||
else
|
||||
return NULL;
|
||||
|
@ -1892,8 +1964,6 @@ iquic_esfi_destroy (enc_session_t *enc_session_p)
|
|||
free_handshake_keys(enc_sess);
|
||||
cleanup_hp(&enc_sess->esi_hp);
|
||||
|
||||
free(enc_sess->esi_sess_resume_buf);
|
||||
free(enc_sess->esi_hostname);
|
||||
free(enc_sess);
|
||||
}
|
||||
|
||||
|
@ -1910,11 +1980,18 @@ static const enum enc_level hety2el[] =
|
|||
};
|
||||
|
||||
|
||||
static const enum enc_level pns2enc_level[] =
|
||||
static const enum enc_level pns2enc_level[2][N_PNS] =
|
||||
{
|
||||
[PNS_INIT] = ENC_LEV_CLEAR,
|
||||
[PNS_HSK] = ENC_LEV_INIT,
|
||||
[PNS_APP] = ENC_LEV_FORW,
|
||||
[0] = {
|
||||
[PNS_INIT] = ENC_LEV_CLEAR,
|
||||
[PNS_HSK] = ENC_LEV_INIT,
|
||||
[PNS_APP] = ENC_LEV_EARLY,
|
||||
},
|
||||
[1] = {
|
||||
[PNS_INIT] = ENC_LEV_CLEAR,
|
||||
[PNS_HSK] = ENC_LEV_INIT,
|
||||
[PNS_APP] = ENC_LEV_FORW,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
@ -1941,8 +2018,7 @@ iquic_esf_encrypt_packet (enc_session_t *enc_session_p,
|
|||
char errbuf[ERR_ERROR_STRING_BUF_LEN];
|
||||
|
||||
pns = lsquic_packet_out_pns(packet_out);
|
||||
/* TODO Obviously, will need more logic for 0-RTT */
|
||||
enc_level = pns2enc_level[ pns ];
|
||||
enc_level = pns2enc_level[ enc_sess->esi_have_forw ][ pns ];
|
||||
|
||||
if (enc_level == ENC_LEV_FORW)
|
||||
{
|
||||
|
@ -2458,7 +2534,7 @@ static int
|
|||
iquic_esf_sess_resume_enabled (enc_session_t *enc_session_p)
|
||||
{
|
||||
struct enc_sess_iquic *const enc_sess = enc_session_p;
|
||||
return enc_sess->esi_sess_resume_buf != NULL;
|
||||
return !!(enc_sess->esi_flags & ESI_USE_SSL_TICKET);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2817,6 +2893,9 @@ set_secret (SSL *ssl, enum ssl_encryption_level_t level,
|
|||
key_len, hexbuf));
|
||||
}
|
||||
|
||||
if (rw && enc_level == ENC_LEV_FORW)
|
||||
enc_sess->esi_have_forw = 1;
|
||||
|
||||
return 1;
|
||||
|
||||
err:
|
||||
|
@ -2955,25 +3034,11 @@ chsk_ietf_on_new_stream (void *stream_if_ctx, struct lsquic_stream *stream)
|
|||
enum enc_level enc_level;
|
||||
|
||||
enc_level = enc_sess->esi_cryst_if->csi_enc_level(stream);
|
||||
if (enc_level != ENC_LEV_CLEAR)
|
||||
{
|
||||
LSQ_DEBUG("skip initialization of stream at level %u", enc_level);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (
|
||||
(enc_sess->esi_flags & ESI_SERVER) == 0 &&
|
||||
0 != init_client(enc_sess))
|
||||
{
|
||||
LSQ_WARN("enc session could not be initialized");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enc_sess->esi_cryst_if->csi_wantwrite(stream, 1);
|
||||
if (enc_level == ENC_LEV_CLEAR)
|
||||
enc_sess->esi_cryst_if->csi_wantwrite(stream, 1);
|
||||
|
||||
LSQ_DEBUG("handshake stream created successfully");
|
||||
|
||||
end:
|
||||
return stream_if_ctx;
|
||||
}
|
||||
|
||||
|
@ -3006,6 +3071,7 @@ chsk_ietf_on_close (struct lsquic_stream *stream, lsquic_stream_ctx_t *ctx)
|
|||
static const char *const ihs2str[] = {
|
||||
[IHS_WANT_READ] = "want read",
|
||||
[IHS_WANT_WRITE] = "want write",
|
||||
[IHS_WANT_RW] = "want rw",
|
||||
[IHS_STOP] = "stop",
|
||||
};
|
||||
|
||||
|
@ -3036,6 +3102,10 @@ iquic_esfi_shake_stream (enc_session_t *sess,
|
|||
enc_sess->esi_cryst_if->csi_wantwrite(stream, 1);
|
||||
enc_sess->esi_cryst_if->csi_wantread(stream, 0);
|
||||
break;
|
||||
case IHS_WANT_RW:
|
||||
enc_sess->esi_cryst_if->csi_wantwrite(stream, 1);
|
||||
enc_sess->esi_cryst_if->csi_wantread(stream, 1);
|
||||
break;
|
||||
default:
|
||||
assert(st == IHS_STOP);
|
||||
write = !lsquic_frab_list_empty(&enc_sess->esi_frals[enc_level]);
|
||||
|
@ -3209,3 +3279,96 @@ const unsigned char *const lsquic_retry_nonce_buf[N_IETF_RETRY_VERSIONS] =
|
|||
/* [draft-ietf-quic-tls-29] Section 5.8 */
|
||||
(unsigned char *) "\xe5\x49\x30\xf9\x7f\x21\x36\xf0\x53\x0a\x8c\x1c",
|
||||
};
|
||||
|
||||
|
||||
int
|
||||
lsquic_enc_sess_ietf_gen_quic_ctx (
|
||||
const struct lsquic_engine_settings *settings,
|
||||
enum lsquic_version version, unsigned char *buf, size_t bufsz)
|
||||
{
|
||||
struct transport_params params;
|
||||
int len;
|
||||
|
||||
/* This code is pretty much copied from gen_trans_params(), with
|
||||
* small (but important) exceptions.
|
||||
*/
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.tp_init_max_data = settings->es_init_max_data;
|
||||
params.tp_init_max_stream_data_bidi_local
|
||||
= settings->es_init_max_stream_data_bidi_local;
|
||||
params.tp_init_max_stream_data_bidi_remote
|
||||
= settings->es_init_max_stream_data_bidi_remote;
|
||||
params.tp_init_max_stream_data_uni
|
||||
= settings->es_init_max_stream_data_uni;
|
||||
params.tp_init_max_streams_uni
|
||||
= settings->es_init_max_streams_uni;
|
||||
params.tp_init_max_streams_bidi
|
||||
= settings->es_init_max_streams_bidi;
|
||||
params.tp_ack_delay_exponent
|
||||
= TP_DEF_ACK_DELAY_EXP;
|
||||
params.tp_max_idle_timeout = settings->es_idle_timeout * 1000;
|
||||
params.tp_max_ack_delay = TP_DEF_MAX_ACK_DELAY;
|
||||
params.tp_active_connection_id_limit = MAX_IETF_CONN_DCIDS;
|
||||
params.tp_set |= (1 << TPI_INIT_MAX_DATA)
|
||||
| (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL)
|
||||
| (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE)
|
||||
| (1 << TPI_INIT_MAX_STREAM_DATA_UNI)
|
||||
| (1 << TPI_INIT_MAX_STREAMS_UNI)
|
||||
| (1 << TPI_INIT_MAX_STREAMS_BIDI)
|
||||
| (1 << TPI_ACK_DELAY_EXPONENT)
|
||||
| (1 << TPI_MAX_IDLE_TIMEOUT)
|
||||
| (1 << TPI_MAX_ACK_DELAY)
|
||||
| (1 << TPI_ACTIVE_CONNECTION_ID_LIMIT)
|
||||
;
|
||||
if (settings->es_max_udp_payload_size_rx)
|
||||
{
|
||||
params.tp_max_udp_payload_size = settings->es_max_udp_payload_size_rx;
|
||||
params.tp_set |= 1 << TPI_MAX_UDP_PAYLOAD_SIZE;
|
||||
}
|
||||
if (!settings->es_allow_migration)
|
||||
params.tp_set |= 1 << TPI_DISABLE_ACTIVE_MIGRATION;
|
||||
if (settings->es_ql_bits)
|
||||
{
|
||||
params.tp_loss_bits = settings->es_ql_bits - 1;
|
||||
params.tp_set |= 1 << TPI_LOSS_BITS;
|
||||
}
|
||||
if (settings->es_delayed_acks)
|
||||
{
|
||||
params.tp_numerics[TPI_MIN_ACK_DELAY] = 10000; /* TODO: make into a constant? make configurable? */
|
||||
params.tp_set |= 1 << TPI_MIN_ACK_DELAY;
|
||||
}
|
||||
if (settings->es_timestamps)
|
||||
{
|
||||
params.tp_numerics[TPI_TIMESTAMPS] = TS_GENERATE_THEM;
|
||||
params.tp_set |= 1 << TPI_TIMESTAMPS;
|
||||
}
|
||||
if (settings->es_datagrams)
|
||||
{
|
||||
if (params.tp_set & (1 << TPI_MAX_UDP_PAYLOAD_SIZE))
|
||||
params.tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE]
|
||||
= params.tp_max_udp_payload_size;
|
||||
else
|
||||
params.tp_numerics[TPI_MAX_DATAGRAM_FRAME_SIZE]
|
||||
= TP_DEF_MAX_UDP_PAYLOAD_SIZE;
|
||||
params.tp_set |= 1 << TPI_MAX_DATAGRAM_FRAME_SIZE;
|
||||
}
|
||||
|
||||
params.tp_set &= SERVER_0RTT_TPS;
|
||||
|
||||
len = (version == LSQVER_ID27 ? lsquic_tp_encode_27 : lsquic_tp_encode)(
|
||||
¶ms, 1, buf, bufsz);
|
||||
if (len >= 0)
|
||||
{
|
||||
char str[MAX_TP_STR_SZ];
|
||||
LSQ_LOG1(LSQ_LOG_DEBUG, "generated QUIC server context of %d bytes "
|
||||
"for version %s", len, lsquic_ver2str[version]);
|
||||
LSQ_LOG1(LSQ_LOG_DEBUG, "%s", ((version == LSQVER_ID27
|
||||
? lsquic_tp_to_str_27 : lsquic_tp_to_str)(¶ms, str,
|
||||
sizeof(str)), str));
|
||||
}
|
||||
else
|
||||
LSQ_LOG1(LSQ_LOG_WARN, "cannot generate QUIC server context: %d",
|
||||
errno);
|
||||
return len;
|
||||
}
|
||||
|
|
|
@ -631,6 +631,22 @@ lsquic_engine_new (unsigned flags,
|
|||
engine->pub.enp_tokgen = lsquic_tg_new(&engine->pub);
|
||||
if (!engine->pub.enp_tokgen)
|
||||
return NULL;
|
||||
if (engine->flags & ENG_SERVER)
|
||||
for (i = 0; i < sizeof(engine->pub.enp_quic_ctx_sz)
|
||||
/ sizeof(engine->pub.enp_quic_ctx_sz[0]); ++i)
|
||||
{
|
||||
int sz = lsquic_enc_sess_ietf_gen_quic_ctx(
|
||||
&engine->pub.enp_settings,
|
||||
i == 0 ? LSQVER_ID27 : LSQVER_ID28,
|
||||
engine->pub.enp_quic_ctx_buf[i],
|
||||
sizeof(engine->pub.enp_quic_ctx_buf));
|
||||
if (sz < 0)
|
||||
{
|
||||
free(engine);
|
||||
return NULL;
|
||||
}
|
||||
engine->pub.enp_quic_ctx_sz[i] = (unsigned) sz;
|
||||
}
|
||||
engine->pub.enp_crand = &engine->crand;
|
||||
if (engine->pub.enp_settings.es_noprogress_timeout)
|
||||
engine->pub.enp_noprog_timeout
|
||||
|
@ -1106,6 +1122,34 @@ find_conn_by_addr (struct lsquic_hash *hash, const struct sockaddr *sa)
|
|||
}
|
||||
|
||||
|
||||
/* When connections are identified by the local address, we need to drop
|
||||
* packets that use DCIDs that do not correspond to any of SCIDs. This
|
||||
* can happen when peer retires a SCID. This mimics the normal behavior,
|
||||
* when connections are looked up in engine->conns_hash by ID: when there
|
||||
* is no match, the packet is dropped.
|
||||
*/
|
||||
static int
|
||||
dcid_checks_out (const struct lsquic_conn *conn, const lsquic_cid_t *dcid)
|
||||
{
|
||||
const struct conn_cid_elem *cce;
|
||||
|
||||
if (LSQUIC_CIDS_EQ(CN_SCID(conn), dcid))
|
||||
return 1;
|
||||
|
||||
/* Slow check for those rare cases */
|
||||
for (cce = conn->cn_cces; cce < END_OF_CCES(conn); ++cce)
|
||||
if ((conn->cn_cces_mask & (1 << (cce - conn->cn_cces)))
|
||||
&& !(cce->cce_flags & CCE_PORT)
|
||||
&& LSQUIC_CIDS_EQ(&cce->cce_cid, dcid))
|
||||
{
|
||||
LSQ_DEBUG("connection checks out");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static lsquic_conn_t *
|
||||
find_conn (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in,
|
||||
struct packin_parse_state *ppstate, const struct sockaddr *sa_local)
|
||||
|
@ -1114,7 +1158,17 @@ find_conn (lsquic_engine_t *engine, lsquic_packet_in_t *packet_in,
|
|||
lsquic_conn_t *conn;
|
||||
|
||||
if (engine->flags & ENG_CONNS_BY_ADDR)
|
||||
{
|
||||
el = find_conn_by_addr(engine->conns_hash, sa_local);
|
||||
if ((packet_in->pi_flags & PI_CONN_ID)
|
||||
&& !dcid_checks_out(lsquic_hashelem_getdata(el),
|
||||
&packet_in->pi_conn_id))
|
||||
{
|
||||
LSQ_DEBUGC("DCID matches no SCID in connection %"CID_FMT": drop it",
|
||||
CID_BITS(&packet_in->pi_conn_id));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (packet_in->pi_flags & PI_CONN_ID)
|
||||
el = lsquic_hash_find(engine->conns_hash,
|
||||
packet_in->pi_conn_id.idbuf, packet_in->pi_conn_id.len);
|
||||
|
|
|
@ -78,6 +78,11 @@ struct lsquic_engine_public {
|
|||
struct lsquic_hash *enp_server_certs;
|
||||
/* gQUIC server configuration: */
|
||||
struct lsquic_server_config *enp_server_config;
|
||||
/* Serialized subset of server engine transport parameters that is used
|
||||
* as SSL QUIC context. 0 is for version <= LSQVER_ID27, 1 is for others.
|
||||
*/
|
||||
unsigned char enp_quic_ctx_buf[2][200];
|
||||
unsigned enp_quic_ctx_sz[2];
|
||||
};
|
||||
|
||||
/* Put connection onto the Tickable Queue if it is not already on it. If
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include "lsquic_frab_list.h"
|
||||
#include "lsquic_ev_log.h"
|
||||
|
||||
#include "fiu-local.h"
|
||||
|
||||
#define LSQUIC_LOGGER_MODULE LSQLM_FRAME_WRITER
|
||||
#define LSQUIC_LOG_CONN_ID lsquic_conn_log_cid(\
|
||||
lsquic_stream_conn(fw->fw_stream))
|
||||
|
@ -497,6 +499,8 @@ lsquic_frame_writer_write_promise (struct lsquic_frame_writer *fw,
|
|||
unsigned char *buf;
|
||||
int s;
|
||||
|
||||
fiu_return_on("frame_writer/writer_promise", -1);
|
||||
|
||||
if (fw->fw_max_header_list_sz && 0 != check_headers_size(fw, headers))
|
||||
return -1;
|
||||
|
||||
|
|
|
@ -4156,10 +4156,15 @@ full_conn_ci_push_stream (struct lsquic_conn *lconn, void *hset,
|
|||
if (0 != lsquic_headers_stream_push_promise(conn->fc_pub.u.gquic.hs, dep_stream->id,
|
||||
pushed_stream->id, headers))
|
||||
{
|
||||
/* Since the failure to write to HEADERS stream results in aborting
|
||||
* the connection, we do not bother rolling back.
|
||||
*/
|
||||
LSQ_ERROR("could not send push promise");
|
||||
/* If forget we ever had the hset pointer: */
|
||||
lsquic_stream_drop_hset_ref(pushed_stream);
|
||||
/* Now roll back stream creation and return stream ID: */
|
||||
if (pushed_stream->sm_hash_el.qhe_flags & QHE_HASHED)
|
||||
lsquic_hash_erase(conn->fc_pub.all_streams,
|
||||
&pushed_stream->sm_hash_el);
|
||||
lsquic_stream_destroy(pushed_stream);
|
||||
conn->fc_last_stream_id -= 2;
|
||||
LSQ_INFO("could not send push promise");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ enum ifull_conn_flags
|
|||
IFC_IGNORE_HSK = 1 << 25,
|
||||
IFC_PROC_CRYPTO = 1 << 26,
|
||||
IFC_MIGRA = 1 << 27,
|
||||
IFC_UNUSED28 = 1 << 28, /* Unused */
|
||||
IFC_HTTP_INITED = 1 << 28, /* HTTP initialized */
|
||||
IFC_DELAYED_ACKS = 1 << 29, /* Delayed ACKs are enabled */
|
||||
IFC_TIMESTAMPS = 1 << 30, /* Timestamps are enabled */
|
||||
IFC_DATAGRAMS = 1u<< 31, /* Datagrams are enabled */
|
||||
|
@ -153,6 +153,7 @@ enum more_flags
|
|||
MF_CONN_CLOSE_PACK = 1 << 4, /* CONNECTION_CLOSE has been packetized */
|
||||
MF_SEND_WRONG_COUNTS= 1 << 5, /* Send wrong ECN counts to peer */
|
||||
MF_WANT_DATAGRAM_WRITE = 1 << 6,
|
||||
MF_DOING_0RTT = 1 << 7,
|
||||
};
|
||||
|
||||
|
||||
|
@ -568,6 +569,12 @@ find_cce_by_cid (struct ietf_full_conn *, const lsquic_cid_t *);
|
|||
static void
|
||||
mtu_probe_too_large (struct ietf_full_conn *, const struct lsquic_packet_out *);
|
||||
|
||||
static int
|
||||
apply_trans_params (struct ietf_full_conn *, const struct transport_params *);
|
||||
|
||||
static int
|
||||
init_http (struct ietf_full_conn *);
|
||||
|
||||
static unsigned
|
||||
highest_bit_set (unsigned sz)
|
||||
{
|
||||
|
@ -1252,7 +1259,7 @@ ietf_full_conn_init (struct ietf_full_conn *conn,
|
|||
conn->ifc_peer_hq_settings.header_table_size = HQ_DF_QPACK_MAX_TABLE_CAPACITY;
|
||||
conn->ifc_peer_hq_settings.qpack_blocked_streams = HQ_DF_QPACK_BLOCKED_STREAMS;
|
||||
|
||||
conn->ifc_flags = flags | IFC_CREATED_OK | IFC_FIRST_TICK;
|
||||
conn->ifc_flags = flags | IFC_FIRST_TICK;
|
||||
conn->ifc_max_ack_packno[PNS_INIT] = IQUIC_INVALID_PACKNO;
|
||||
conn->ifc_max_ack_packno[PNS_HSK] = IQUIC_INVALID_PACKNO;
|
||||
conn->ifc_max_ack_packno[PNS_APP] = IQUIC_INVALID_PACKNO;
|
||||
|
@ -1282,6 +1289,7 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
|
|||
const unsigned char *sess_resume, size_t sess_resume_sz,
|
||||
const unsigned char *token, size_t token_sz)
|
||||
{
|
||||
const struct transport_params *params;
|
||||
const struct enc_session_funcs_iquic *esfi;
|
||||
struct ietf_full_conn *conn;
|
||||
enum lsquic_version ver, sess_resume_version;
|
||||
|
@ -1397,6 +1405,18 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
|
|||
conn->ifc_created = now;
|
||||
LSQ_DEBUG("logging using %s SCID",
|
||||
LSQUIC_LOG_CONN_ID == CN_SCID(&conn->ifc_conn) ? "client" : "server");
|
||||
if (sess_resume && (params
|
||||
= conn->ifc_conn.cn_esf.i->esfi_get_peer_transport_params(
|
||||
conn->ifc_conn.cn_enc_session), params != NULL))
|
||||
{
|
||||
LSQ_DEBUG("initializing transport parameters for 0RTT");
|
||||
if (0 != apply_trans_params(conn, params))
|
||||
goto full_err;
|
||||
if ((conn->ifc_flags & IFC_HTTP) && 0 != init_http(conn))
|
||||
goto full_err;
|
||||
conn->ifc_mflags |= MF_DOING_0RTT;
|
||||
}
|
||||
conn->ifc_flags |= IFC_CREATED_OK;
|
||||
return &conn->ifc_conn;
|
||||
|
||||
err4:
|
||||
|
@ -1411,6 +1431,10 @@ lsquic_ietf_full_conn_client_new (struct lsquic_engine_public *enpub,
|
|||
free(conn);
|
||||
err0:
|
||||
return NULL;
|
||||
|
||||
full_err:
|
||||
ietf_full_conn_ci_destroy(&conn->ifc_conn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1635,6 +1659,7 @@ lsquic_ietf_full_conn_server_new (struct lsquic_engine_public *enpub,
|
|||
|
||||
LSQ_DEBUG("logging using %s SCID",
|
||||
LSQUIC_LOG_CONN_ID == CN_SCID(&conn->ifc_conn) ? "server" : "client");
|
||||
conn->ifc_flags |= IFC_CREATED_OK;
|
||||
return &conn->ifc_conn;
|
||||
|
||||
err3:
|
||||
|
@ -3317,35 +3342,13 @@ maybe_start_migration (struct ietf_full_conn *conn)
|
|||
|
||||
|
||||
static int
|
||||
handshake_ok (struct lsquic_conn *lconn)
|
||||
apply_trans_params (struct ietf_full_conn *conn,
|
||||
const struct transport_params *params)
|
||||
{
|
||||
struct ietf_full_conn *const conn = (struct ietf_full_conn *) lconn;
|
||||
struct lsquic_stream *stream;
|
||||
struct lsquic_hash_elem *el;
|
||||
struct dcid_elem *dce;
|
||||
const struct transport_params *params;
|
||||
enum stream_id_type sit;
|
||||
uint64_t limit;
|
||||
char buf[MAX_TP_STR_SZ];
|
||||
|
||||
fiu_return_on("full_conn_ietf/handshake_ok", -1);
|
||||
|
||||
/* Need to set this flag even we hit an error in the rest of this funciton.
|
||||
* This is because this flag is used to calculate packet out header size
|
||||
*/
|
||||
lconn->cn_flags |= LSCONN_HANDSHAKE_DONE;
|
||||
|
||||
params = lconn->cn_esf.i->esfi_get_peer_transport_params(
|
||||
lconn->cn_enc_session);
|
||||
if (!params)
|
||||
{
|
||||
ABORT_WARN("could not get transport parameters");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LSQ_DEBUG("peer transport parameters: %s",
|
||||
((lconn->cn_version == LSQVER_ID27 ? lsquic_tp_to_str_27
|
||||
: lsquic_tp_to_str)(params, buf, sizeof(buf)), buf));
|
||||
|
||||
if ((params->tp_set & (1 << TPI_LOSS_BITS))
|
||||
&& conn->ifc_settings->es_ql_bits == 2)
|
||||
|
@ -3487,6 +3490,104 @@ handshake_ok (struct lsquic_conn *lconn)
|
|||
CUR_NPATH(conn)->np_pack_size);
|
||||
}
|
||||
|
||||
if (params->tp_active_connection_id_limit > conn->ifc_conn.cn_n_cces)
|
||||
conn->ifc_active_cids_limit = conn->ifc_conn.cn_n_cces;
|
||||
else
|
||||
conn->ifc_active_cids_limit = params->tp_active_connection_id_limit;
|
||||
conn->ifc_first_active_cid_seqno = conn->ifc_scid_seqno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
init_http (struct ietf_full_conn *conn)
|
||||
{
|
||||
fiu_return_on("full_conn_ietf/init_http", -1);
|
||||
lsquic_qeh_init(&conn->ifc_qeh, &conn->ifc_conn);
|
||||
if (0 == avail_streams_count(conn, conn->ifc_flags & IFC_SERVER,
|
||||
SD_UNI))
|
||||
{
|
||||
ABORT_QUIETLY(1, HEC_GENERAL_PROTOCOL_ERROR, "cannot create "
|
||||
"control stream due to peer-imposed limit");
|
||||
conn->ifc_error = CONN_ERR(1, HEC_GENERAL_PROTOCOL_ERROR);
|
||||
return -1;
|
||||
}
|
||||
if (0 != create_ctl_stream_out(conn))
|
||||
{
|
||||
ABORT_WARN("cannot create outgoing control stream");
|
||||
return -1;
|
||||
}
|
||||
if (0 != lsquic_hcso_write_settings(&conn->ifc_hcso,
|
||||
&conn->ifc_enpub->enp_settings, conn->ifc_flags & IFC_SERVER))
|
||||
{
|
||||
ABORT_WARN("cannot write SETTINGS");
|
||||
return -1;
|
||||
}
|
||||
if (!(conn->ifc_flags & IFC_SERVER)
|
||||
&& (conn->ifc_u.cli.ifcli_flags & IFCLI_PUSH_ENABLED)
|
||||
&& 0 != lsquic_hcso_write_max_push_id(&conn->ifc_hcso,
|
||||
conn->ifc_u.cli.ifcli_max_push_id))
|
||||
{
|
||||
ABORT_WARN("cannot write MAX_PUSH_ID");
|
||||
return -1;
|
||||
}
|
||||
if (0 != lsquic_qdh_init(&conn->ifc_qdh, &conn->ifc_conn,
|
||||
conn->ifc_flags & IFC_SERVER, conn->ifc_enpub,
|
||||
conn->ifc_settings->es_qpack_dec_max_size,
|
||||
conn->ifc_settings->es_qpack_dec_max_blocked))
|
||||
{
|
||||
ABORT_WARN("cannot initialize QPACK decoder");
|
||||
return -1;
|
||||
}
|
||||
if (avail_streams_count(conn, conn->ifc_flags & IFC_SERVER, SD_UNI) > 0)
|
||||
{
|
||||
if (0 != create_qdec_stream_out(conn))
|
||||
{
|
||||
ABORT_WARN("cannot create outgoing QPACK decoder stream");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
queue_streams_blocked_frame(conn, SD_UNI);
|
||||
LSQ_DEBUG("cannot create outgoing QPACK decoder stream due to "
|
||||
"unidir limits");
|
||||
}
|
||||
conn->ifc_flags |= IFC_HTTP_INITED;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
handshake_ok (struct lsquic_conn *lconn)
|
||||
{
|
||||
struct ietf_full_conn *const conn = (struct ietf_full_conn *) lconn;
|
||||
struct dcid_elem *dce;
|
||||
const struct transport_params *params;
|
||||
char buf[MAX_TP_STR_SZ];
|
||||
|
||||
fiu_return_on("full_conn_ietf/handshake_ok", -1);
|
||||
|
||||
/* Need to set this flag even we hit an error in the rest of this funciton.
|
||||
* This is because this flag is used to calculate packet out header size
|
||||
*/
|
||||
lconn->cn_flags |= LSCONN_HANDSHAKE_DONE;
|
||||
|
||||
params = lconn->cn_esf.i->esfi_get_peer_transport_params(
|
||||
lconn->cn_enc_session);
|
||||
if (!params)
|
||||
{
|
||||
ABORT_WARN("could not get transport parameters");
|
||||
return -1;
|
||||
}
|
||||
|
||||
LSQ_DEBUG("peer transport parameters: %s",
|
||||
((lconn->cn_version == LSQVER_ID27 ? lsquic_tp_to_str_27
|
||||
: lsquic_tp_to_str)(params, buf, sizeof(buf)), buf));
|
||||
if (0 != apply_trans_params(conn, params))
|
||||
return -1;
|
||||
|
||||
dce = get_new_dce(conn);
|
||||
if (!dce)
|
||||
{
|
||||
|
@ -3518,65 +3619,9 @@ handshake_ok (struct lsquic_conn *lconn)
|
|||
|
||||
LSQ_INFO("applied peer transport parameters");
|
||||
|
||||
if (conn->ifc_flags & IFC_HTTP)
|
||||
{
|
||||
lsquic_qeh_init(&conn->ifc_qeh, &conn->ifc_conn);
|
||||
if (0 == avail_streams_count(conn, conn->ifc_flags & IFC_SERVER,
|
||||
SD_UNI))
|
||||
{
|
||||
ABORT_QUIETLY(1, HEC_GENERAL_PROTOCOL_ERROR, "cannot create "
|
||||
"control stream due to peer-imposed limit");
|
||||
conn->ifc_error = CONN_ERR(1, HEC_GENERAL_PROTOCOL_ERROR);
|
||||
if ((conn->ifc_flags & (IFC_HTTP|IFC_HTTP_INITED)) == IFC_HTTP)
|
||||
if (0 != init_http(conn))
|
||||
return -1;
|
||||
}
|
||||
if (0 != create_ctl_stream_out(conn))
|
||||
{
|
||||
ABORT_WARN("cannot create outgoing control stream");
|
||||
return -1;
|
||||
}
|
||||
if (0 != lsquic_hcso_write_settings(&conn->ifc_hcso,
|
||||
&conn->ifc_enpub->enp_settings, conn->ifc_flags & IFC_SERVER))
|
||||
{
|
||||
ABORT_WARN("cannot write SETTINGS");
|
||||
return -1;
|
||||
}
|
||||
if (!(conn->ifc_flags & IFC_SERVER)
|
||||
&& (conn->ifc_u.cli.ifcli_flags & IFCLI_PUSH_ENABLED)
|
||||
&& 0 != lsquic_hcso_write_max_push_id(&conn->ifc_hcso,
|
||||
conn->ifc_u.cli.ifcli_max_push_id))
|
||||
{
|
||||
ABORT_WARN("cannot write MAX_PUSH_ID");
|
||||
return -1;
|
||||
}
|
||||
if (0 != lsquic_qdh_init(&conn->ifc_qdh, &conn->ifc_conn,
|
||||
conn->ifc_flags & IFC_SERVER, conn->ifc_enpub,
|
||||
conn->ifc_settings->es_qpack_dec_max_size,
|
||||
conn->ifc_settings->es_qpack_dec_max_blocked))
|
||||
{
|
||||
ABORT_WARN("cannot initialize QPACK decoder");
|
||||
return -1;
|
||||
}
|
||||
if (avail_streams_count(conn, conn->ifc_flags & IFC_SERVER, SD_UNI) > 0)
|
||||
{
|
||||
if (0 != create_qdec_stream_out(conn))
|
||||
{
|
||||
ABORT_WARN("cannot create outgoing QPACK decoder stream");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
queue_streams_blocked_frame(conn, SD_UNI);
|
||||
LSQ_DEBUG("cannot create outgoing QPACK decoder stream due to "
|
||||
"unidir limits");
|
||||
}
|
||||
}
|
||||
|
||||
if (params->tp_active_connection_id_limit > conn->ifc_conn.cn_n_cces)
|
||||
conn->ifc_active_cids_limit = conn->ifc_conn.cn_n_cces;
|
||||
else
|
||||
conn->ifc_active_cids_limit = params->tp_active_connection_id_limit;
|
||||
conn->ifc_first_active_cid_seqno = conn->ifc_scid_seqno;
|
||||
|
||||
if (conn->ifc_settings->es_dplpmtud)
|
||||
conn->ifc_mflags |= MF_CHECK_MTU_PROBE;
|
||||
|
@ -3585,6 +3630,9 @@ handshake_ok (struct lsquic_conn *lconn)
|
|||
conn->ifc_send_flags |= SF_SEND_NEW_CID;
|
||||
maybe_create_delayed_streams(conn);
|
||||
|
||||
if (!(conn->ifc_flags & IFC_SERVER))
|
||||
lsquic_send_ctl_0rtt_to_1rtt(&conn->ifc_send_ctl);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -3615,10 +3663,10 @@ ietf_full_conn_ci_hsk_done (struct lsquic_conn *lconn,
|
|||
}
|
||||
break;
|
||||
default:
|
||||
case LSQ_HSK_RESUMED_FAIL: /* IETF crypto never returns this */
|
||||
assert(0);
|
||||
/* fall-through */
|
||||
case LSQ_HSK_FAIL:
|
||||
case LSQ_HSK_RESUMED_FAIL:
|
||||
handshake_failed(lconn);
|
||||
break;
|
||||
}
|
||||
|
@ -7406,6 +7454,7 @@ check_or_schedule_mtu_probe (struct ietf_full_conn *conn, lsquic_time_t now)
|
|||
|
||||
if (!(conn->ifc_conn.cn_flags & LSCONN_HANDSHAKE_DONE)
|
||||
|| (conn->ifc_flags & IFC_CLOSING)
|
||||
|| ~0ull == lsquic_senhist_largest(&conn->ifc_send_ctl.sc_senhist)
|
||||
|| lsquic_senhist_largest(&conn->ifc_send_ctl.sc_senhist) < 30
|
||||
|| lsquic_send_ctl_in_recovery(&conn->ifc_send_ctl)
|
||||
|| !lsquic_send_ctl_can_send_probe(&conn->ifc_send_ctl,
|
||||
|
@ -7594,6 +7643,16 @@ ietf_full_conn_ci_retx_timeout (struct lsquic_conn *lconn)
|
|||
}
|
||||
|
||||
|
||||
static void
|
||||
ietf_full_conn_ci_early_data_failed (struct lsquic_conn *lconn)
|
||||
{
|
||||
struct ietf_full_conn *conn = (struct ietf_full_conn *) lconn;
|
||||
|
||||
LSQ_DEBUG("early data failed");
|
||||
lsquic_send_ctl_stash_0rtt_packets(&conn->ifc_send_ctl);
|
||||
}
|
||||
|
||||
|
||||
static size_t
|
||||
ietf_full_conn_ci_get_min_datagram_size (struct lsquic_conn *lconn)
|
||||
{
|
||||
|
@ -7830,7 +7889,8 @@ ietf_full_conn_ci_tick (struct lsquic_conn *lconn, lsquic_time_t now)
|
|||
conn->ifc_flags |= (s < 0) << IFC_BIT_ERROR;
|
||||
if (0 == s)
|
||||
process_crypto_stream_write_events(conn);
|
||||
goto end_write;
|
||||
if (!(conn->ifc_mflags & MF_DOING_0RTT))
|
||||
goto end_write;
|
||||
}
|
||||
|
||||
maybe_conn_flush_special_streams(conn);
|
||||
|
@ -8279,6 +8339,7 @@ ietf_full_conn_ci_count_garbage (struct lsquic_conn *lconn, size_t garbage_sz)
|
|||
.ci_destroy = ietf_full_conn_ci_destroy, \
|
||||
.ci_drain_time = ietf_full_conn_ci_drain_time, \
|
||||
.ci_drop_crypto_streams = ietf_full_conn_ci_drop_crypto_streams, \
|
||||
.ci_early_data_failed = ietf_full_conn_ci_early_data_failed, \
|
||||
.ci_get_engine = ietf_full_conn_ci_get_engine, \
|
||||
.ci_get_log_cid = ietf_full_conn_ci_get_log_cid, \
|
||||
.ci_get_min_datagram_size= ietf_full_conn_ci_get_min_datagram_size, \
|
||||
|
|
|
@ -88,7 +88,7 @@ is_valid_gquic_hs_packet (const unsigned char *buf, size_t bufsz,
|
|||
|
||||
int
|
||||
lsquic_is_valid_hs_packet (struct lsquic_engine *engine,
|
||||
const unsigned char *buf, size_t bufsz, size_t packet_in_sz)
|
||||
const unsigned char *buf, size_t bufsz)
|
||||
{
|
||||
lsquic_ver_tag_t tag;
|
||||
int is_valid;
|
||||
|
@ -104,7 +104,7 @@ lsquic_is_valid_hs_packet (struct lsquic_engine *engine,
|
|||
case 0x80|0x00|0x20|0x10|0x08:
|
||||
case 0x80|0x40|0x20|0x10|0x00:
|
||||
case 0x80|0x00|0x20|0x10|0x00:
|
||||
is_valid = packet_in_sz >= IQUIC_MIN_INIT_PACKET_SZ
|
||||
is_valid = bufsz >= IQUIC_MIN_INIT_PACKET_SZ
|
||||
&& lsquic_is_valid_iquic_hs_packet(buf, bufsz, &tag);
|
||||
break;
|
||||
/* 1X00 XGGG: ID-22 long header */
|
||||
|
@ -122,7 +122,7 @@ lsquic_is_valid_hs_packet (struct lsquic_engine *engine,
|
|||
case 0x80|0x00|0x20|0x00|0x08:
|
||||
case 0x80|0x40|0x20|0x00|0x00:
|
||||
case 0x80|0x00|0x20|0x00|0x00:
|
||||
is_valid = packet_in_sz >= IQUIC_MIN_INIT_PACKET_SZ
|
||||
is_valid = bufsz >= IQUIC_MIN_INIT_PACKET_SZ
|
||||
&& lsquic_is_valid_ietf_v1_or_Q046plus_hs_packet(buf, bufsz, &tag);
|
||||
break;
|
||||
/* 01XX XGGG: ID-22 short header */
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
@ -560,6 +561,16 @@ lsquic_mini_conn_ietf_new (struct lsquic_engine_public *enpub,
|
|||
TAILQ_INIT(&conn->imc_crypto_frames);
|
||||
if (odcid)
|
||||
imico_peer_addr_validated(conn, "odcid");
|
||||
#if LSQUIC_DEVEL
|
||||
{
|
||||
const char *const s = getenv("LSQUIC_LOSE_0RTT");
|
||||
if (s && atoi(s))
|
||||
{
|
||||
LSQ_DEBUG("will lose 0-RTT packets (via env variable)");
|
||||
conn->imc_delayed_packets_count = UCHAR_MAX;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
LSQ_DEBUG("created mini connection object %p; max packet size=%hu",
|
||||
conn, conn->imc_path.np_pack_size);
|
||||
|
|
|
@ -2163,11 +2163,11 @@ ietf_v1_parse_datagram_frame (const unsigned char *buf, size_t buf_len,
|
|||
if (buf[0] & 1)
|
||||
{
|
||||
s = vint_read(buf + 1, buf + buf_len, &len);
|
||||
if (s > 0 && 1 + s + len >= buf_len)
|
||||
if (s > 0 && 1 + s + len <= buf_len)
|
||||
{
|
||||
*data = buf + 1 + s;
|
||||
*data_len = len;
|
||||
return 1 + buf_len + len;
|
||||
return 1 + s + len;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
|
|
|
@ -338,6 +338,7 @@ lsquic_send_ctl_init (lsquic_send_ctl_t *ctl, struct lsquic_alarmset *alset,
|
|||
TAILQ_INIT(&ctl->sc_unacked_packets[PNS_HSK]);
|
||||
TAILQ_INIT(&ctl->sc_unacked_packets[PNS_APP]);
|
||||
TAILQ_INIT(&ctl->sc_lost_packets);
|
||||
TAILQ_INIT(&ctl->sc_0rtt_stash);
|
||||
ctl->sc_enpub = enpub;
|
||||
ctl->sc_alset = alset;
|
||||
ctl->sc_ver_neg = ver_neg;
|
||||
|
@ -1463,6 +1464,11 @@ lsquic_send_ctl_cleanup (lsquic_send_ctl_t *ctl)
|
|||
packet_out->po_flags &= ~PO_LOST;
|
||||
send_ctl_destroy_packet(ctl, packet_out);
|
||||
}
|
||||
while ((packet_out = TAILQ_FIRST(&ctl->sc_0rtt_stash)))
|
||||
{
|
||||
TAILQ_REMOVE(&ctl->sc_0rtt_stash, packet_out, po_next);
|
||||
send_ctl_destroy_packet(ctl, packet_out);
|
||||
}
|
||||
for (n = 0; n < sizeof(ctl->sc_buffered_packets) /
|
||||
sizeof(ctl->sc_buffered_packets[0]); ++n)
|
||||
{
|
||||
|
@ -3795,3 +3801,72 @@ lsquic_send_ctl_rollback (struct lsquic_send_ctl *ctl,
|
|||
if (buffered && (lost_types & QUIC_FTBIT_ACK))
|
||||
lconn->cn_if->ci_ack_rollback(lconn, &ctl_state->ack_state);
|
||||
}
|
||||
|
||||
|
||||
/* Find 0-RTT packets and change them to 1-RTT packets */
|
||||
void
|
||||
lsquic_send_ctl_0rtt_to_1rtt (struct lsquic_send_ctl *ctl)
|
||||
{
|
||||
struct lsquic_packet_out *packet_out;
|
||||
unsigned count;
|
||||
struct lsquic_packets_tailq *const *q;
|
||||
struct lsquic_packets_tailq *const queues[] = {
|
||||
&ctl->sc_scheduled_packets,
|
||||
&ctl->sc_unacked_packets[PNS_APP],
|
||||
&ctl->sc_lost_packets,
|
||||
&ctl->sc_buffered_packets[0].bpq_packets,
|
||||
&ctl->sc_buffered_packets[1].bpq_packets,
|
||||
};
|
||||
|
||||
assert(ctl->sc_flags & SC_IETF);
|
||||
|
||||
while (packet_out = TAILQ_FIRST(&ctl->sc_0rtt_stash), packet_out != NULL)
|
||||
{
|
||||
TAILQ_REMOVE(&ctl->sc_0rtt_stash, packet_out, po_next);
|
||||
TAILQ_INSERT_TAIL(&ctl->sc_lost_packets, packet_out, po_next);
|
||||
packet_out->po_flags |= PO_LOST;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
for (q = queues; q < queues + sizeof(queues) / sizeof(queues[0]); ++q)
|
||||
TAILQ_FOREACH(packet_out, *q, po_next)
|
||||
if (packet_out->po_header_type == HETY_0RTT)
|
||||
{
|
||||
++count;
|
||||
packet_out->po_header_type = HETY_NOT_SET;
|
||||
if (packet_out->po_flags & PO_ENCRYPTED)
|
||||
send_ctl_return_enc_data(ctl, packet_out);
|
||||
}
|
||||
|
||||
LSQ_DEBUG("handshake ok: changed %u packet%.*s from 0-RTT to 1-RTT",
|
||||
count, count != 1, "s");
|
||||
}
|
||||
|
||||
|
||||
/* Remove 0-RTT packets from the unacked queue and wait to retransmit them
|
||||
* after handshake succeeds. This is the most common case. There could
|
||||
* (theoretically) be some corner cases where 0-RTT packets are in the
|
||||
* scheduled queue, but we let those be lost naturally if that occurs.
|
||||
*/
|
||||
void
|
||||
lsquic_send_ctl_stash_0rtt_packets (struct lsquic_send_ctl *ctl)
|
||||
{
|
||||
struct lsquic_packet_out *packet_out, *next;
|
||||
unsigned count, packet_sz;
|
||||
|
||||
count = 0;
|
||||
for (packet_out = TAILQ_FIRST(&ctl->sc_unacked_packets[PNS_APP]);
|
||||
packet_out; packet_out = next)
|
||||
{
|
||||
next = TAILQ_NEXT(packet_out, po_next);
|
||||
if (packet_out->po_header_type == HETY_0RTT)
|
||||
{
|
||||
packet_sz = packet_out_sent_sz(packet_out);
|
||||
send_ctl_unacked_remove(ctl, packet_out, packet_sz);
|
||||
TAILQ_INSERT_TAIL(&ctl->sc_0rtt_stash, packet_out, po_next);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
LSQ_DEBUG("stashed %u 0-RTT packet%.*s", count, count != 1, "s");
|
||||
}
|
||||
|
|
|
@ -87,6 +87,7 @@ typedef struct lsquic_send_ctl {
|
|||
|
||||
/* Second section: everything else. */
|
||||
struct lsquic_packets_tailq sc_scheduled_packets,
|
||||
sc_0rtt_stash,
|
||||
sc_lost_packets;
|
||||
struct buf_packet_q sc_buffered_packets[BPT_OTHER_PRIO + 1];
|
||||
const struct ver_neg *sc_ver_neg;
|
||||
|
@ -431,4 +432,10 @@ void
|
|||
lsquic_send_ctl_rollback (struct lsquic_send_ctl *, struct send_ctl_state *,
|
||||
const struct iovec *, size_t);
|
||||
|
||||
void
|
||||
lsquic_send_ctl_0rtt_to_1rtt (struct lsquic_send_ctl *);
|
||||
|
||||
void
|
||||
lsquic_send_ctl_stash_0rtt_packets (struct lsquic_send_ctl *);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -373,6 +373,39 @@ stream_is_hsk (const struct lsquic_stream *stream)
|
|||
}
|
||||
|
||||
|
||||
/* This function's only job is to change the allocated packet's header
|
||||
* type to HETY_0RTT when stream frames are written before handshake
|
||||
* is complete.
|
||||
*/
|
||||
static struct lsquic_packet_out *
|
||||
stream_get_packet_for_stream_0rtt (struct lsquic_send_ctl *ctl,
|
||||
unsigned need_at_least, const struct network_path *path,
|
||||
const struct lsquic_stream *stream)
|
||||
{
|
||||
struct lsquic_packet_out *packet_out;
|
||||
|
||||
if (stream->conn_pub->lconn->cn_flags & LSCONN_HANDSHAKE_DONE)
|
||||
{
|
||||
LSQ_DEBUG("switch to regular \"get packet for stream\" function");
|
||||
/* Here we drop the "const" because this is a static function.
|
||||
* Otherwise, we would not condone such sorcery.
|
||||
*/
|
||||
((struct lsquic_stream *) stream)->sm_get_packet_for_stream
|
||||
= lsquic_send_ctl_get_packet_for_stream;
|
||||
return lsquic_send_ctl_get_packet_for_stream(ctl, need_at_least,
|
||||
path, stream);
|
||||
}
|
||||
else
|
||||
{
|
||||
packet_out = lsquic_send_ctl_get_packet_for_stream(ctl, need_at_least,
|
||||
path, stream);
|
||||
if (packet_out)
|
||||
packet_out->po_header_type = HETY_0RTT;
|
||||
return packet_out;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static struct lsquic_stream *
|
||||
stream_new_common (lsquic_stream_id_t id, struct lsquic_conn_public *conn_pub,
|
||||
const struct lsquic_stream_if *stream_if, void *stream_if_ctx,
|
||||
|
@ -405,6 +438,7 @@ stream_new_common (lsquic_stream_id_t id, struct lsquic_conn_public *conn_pub,
|
|||
stream->sm_bflags |= ctor_flags & ((1 << N_SMBF_FLAGS) - 1);
|
||||
if (conn_pub->lconn->cn_flags & LSCONN_SERVER)
|
||||
stream->sm_bflags |= SMBF_SERVER;
|
||||
stream->sm_get_packet_for_stream = lsquic_send_ctl_get_packet_for_stream;
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
@ -479,6 +513,13 @@ lsquic_stream_new (lsquic_stream_id_t id,
|
|||
}
|
||||
}
|
||||
|
||||
if ((stream->sm_bflags & (SMBF_SERVER|SMBF_IETF)) == SMBF_IETF
|
||||
&& !(conn_pub->lconn->cn_flags & LSCONN_HANDSHAKE_DONE))
|
||||
{
|
||||
LSQ_DEBUG("use wrapper \"get packet for stream\" function");
|
||||
stream->sm_get_packet_for_stream = stream_get_packet_for_stream_0rtt;
|
||||
}
|
||||
|
||||
lsquic_sfcw_init(&stream->fc, initial_window, cfcw, conn_pub, id);
|
||||
stream->max_send_off = initial_send_off;
|
||||
LSQ_DEBUG("created stream");
|
||||
|
@ -584,6 +625,14 @@ drop_buffered_data (struct lsquic_stream *stream)
|
|||
}
|
||||
|
||||
|
||||
void
|
||||
lsquic_stream_drop_hset_ref (struct lsquic_stream *stream)
|
||||
{
|
||||
if (stream->uh)
|
||||
stream->uh->uh_hset = NULL;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
destroy_uh (struct lsquic_stream *stream)
|
||||
{
|
||||
|
@ -3087,7 +3136,7 @@ stream_write_to_packet_std (struct frame_gen_ctx *fg_ctx, const size_t size)
|
|||
else
|
||||
need_at_least += size > 0;
|
||||
get_packet:
|
||||
packet_out = lsquic_send_ctl_get_packet_for_stream(send_ctl,
|
||||
packet_out = stream->sm_get_packet_for_stream(send_ctl,
|
||||
need_at_least, stream->conn_pub->path, stream);
|
||||
if (packet_out)
|
||||
{
|
||||
|
|
|
@ -17,6 +17,9 @@ struct data_frame;
|
|||
enum quic_frame_type;
|
||||
struct push_promise;
|
||||
union hblock_ctx;
|
||||
struct lsquic_packet_out;
|
||||
struct lsquic_send_ctl;
|
||||
struct network_path;
|
||||
|
||||
TAILQ_HEAD(lsquic_streams_tailq, lsquic_stream);
|
||||
|
||||
|
@ -311,6 +314,11 @@ struct lsquic_stream
|
|||
size_t (*sm_write_avail)(struct lsquic_stream *);
|
||||
int (*sm_readable)(struct lsquic_stream *);
|
||||
|
||||
struct lsquic_packet_out * (*sm_get_packet_for_stream)(
|
||||
struct lsquic_send_ctl *,
|
||||
unsigned, const struct network_path *,
|
||||
const struct lsquic_stream *);
|
||||
|
||||
/* This element is optional */
|
||||
const struct stream_filter_if *sm_sfi;
|
||||
|
||||
|
@ -622,4 +630,7 @@ void
|
|||
lsquic_stream_set_pwritev_params (unsigned iovecs, unsigned frames);
|
||||
#endif
|
||||
|
||||
void
|
||||
lsquic_stream_drop_hset_ref (struct lsquic_stream *);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -184,4 +184,26 @@ size_t
|
|||
lsquic_tp_get_quantum_sz (void);
|
||||
#endif
|
||||
|
||||
#define SERVER_0RTT_TPS (0 \
|
||||
/* [draft-ietf-quic-transport-31] Section 7.4.1: */ \
|
||||
| (1 << TPI_ACTIVE_CONNECTION_ID_LIMIT) \
|
||||
| (1 << TPI_INIT_MAX_DATA) \
|
||||
| (1 << TPI_INIT_MAX_STREAMS_UNI) \
|
||||
| (1 << TPI_INIT_MAX_STREAMS_BIDI) \
|
||||
| (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_LOCAL) \
|
||||
| (1 << TPI_INIT_MAX_STREAM_DATA_BIDI_REMOTE) \
|
||||
| (1 << TPI_INIT_MAX_STREAM_DATA_UNI) \
|
||||
| (1 << TPI_MAX_IDLE_TIMEOUT) \
|
||||
| (1 << TPI_MAX_UDP_PAYLOAD_SIZE) \
|
||||
| (1 << TPI_DISABLE_ACTIVE_MIGRATION) \
|
||||
/* Not including TPI_LOSS_BITS, see */ \
|
||||
/* draft-ferrieuxhamchaoui-quic-lossbits-03, Section 5.1 */ \
|
||||
/* [draft-ietf-quic-datagram-01] Section 3: */ \
|
||||
| (1 << TPI_MAX_DATAGRAM_FRAME_SIZE) \
|
||||
/* [draft-iyengar-quic-delayed-ack-01] does not specfiy, store: */ \
|
||||
| (1 << TPI_MIN_ACK_DELAY) \
|
||||
/* [draft-huitema-quic-ts-03] does not specfiy, store: */ \
|
||||
| (1 << TPI_TIMESTAMPS) \
|
||||
)
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue