diff --git a/include/internal/quic_channel.h b/include/internal/quic_channel.h index 1f7ef71ed4..bba7256d03 100644 --- a/include/internal/quic_channel.h +++ b/include/internal/quic_channel.h @@ -111,6 +111,13 @@ typedef struct quic_channel_args_st { * mutex primitive or using its RW mutex primitive. */ CRYPTO_MUTEX *mutex; + + /* + * Optional function pointer to use to retrieve the current time. If NULL, + * ossl_time_now() is used. + */ + OSSL_TIME (*now_cb)(void *arg); + void *now_cb_arg; } QUIC_CHANNEL_ARGS; typedef struct quic_channel_st QUIC_CHANNEL; diff --git a/include/internal/quic_ssl.h b/include/internal/quic_ssl.h index 3cf32e0944..e4af2c57be 100644 --- a/include/internal/quic_ssl.h +++ b/include/internal/quic_ssl.h @@ -65,6 +65,14 @@ BIO *ossl_quic_conn_get_net_wbio(const QUIC_CONNECTION *qc); __owur int ossl_quic_conn_set_initial_peer_addr(QUIC_CONNECTION *qc, const BIO_ADDR *peer_addr); +/* + * Used to override ossl_time_now() for debug purposes. Must be called before + * connecting. + */ +void ossl_quic_conn_set_override_now_cb(SSL *s, + OSSL_TIME (*now_cb)(void *arg), + void *now_cb_arg); + # endif #endif diff --git a/include/internal/quic_tserver.h b/include/internal/quic_tserver.h index a7fbb393f2..4545630a90 100644 --- a/include/internal/quic_tserver.h +++ b/include/internal/quic_tserver.h @@ -37,6 +37,8 @@ typedef struct quic_tserver_args_st { OSSL_LIB_CTX *libctx; const char *propq; BIO *net_rbio, *net_wbio; + OSSL_TIME (*now_cb)(void *arg); + void *now_cb_arg; } QUIC_TSERVER_ARGS; QUIC_TSERVER *ossl_quic_tserver_new(const QUIC_TSERVER_ARGS *args, diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c index 16b158d8a9..2774329e5c 100644 --- a/ssl/quic/quic_channel.c +++ b/ssl/quic/quic_channel.c @@ -133,7 +133,7 @@ static int ch_init(QUIC_CHANNEL *ch) if (!ossl_quic_rxfc_init(&ch->conn_rxfc, NULL, 2 * 1024 * 1024, 10 * 1024 * 1024, - get_time, NULL)) + get_time, ch)) goto err; if (!ossl_statm_init(&ch->statm)) @@ -144,7 +144,7 @@ static int ch_init(QUIC_CHANNEL *ch) if ((ch->cc_data = ch->cc_method->new(NULL, NULL, NULL)) == NULL) goto err; - if ((ch->ackm = ossl_ackm_new(get_time, NULL, &ch->statm, + if ((ch->ackm = ossl_ackm_new(get_time, ch, &ch->statm, ch->cc_method, ch->cc_data)) == NULL) goto err; @@ -166,6 +166,7 @@ static int ch_init(QUIC_CHANNEL *ch) txp_args.cc_method = ch->cc_method; txp_args.cc_data = ch->cc_data; txp_args.now = get_time; + txp_args.now_arg = ch; for (pn_space = QUIC_PN_SPACE_INITIAL; pn_space < QUIC_PN_SPACE_NUM; ++pn_space) { ch->crypto_send[pn_space] = ossl_quic_sstream_new(INIT_CRYPTO_BUF_LEN); if (ch->crypto_send[pn_space] == NULL) @@ -180,7 +181,7 @@ static int ch_init(QUIC_CHANNEL *ch) if ((ch->demux = ossl_quic_demux_new(/*BIO=*/NULL, /*Short CID Len=*/rx_short_cid_len, - get_time, NULL)) == NULL) + get_time, ch)) == NULL) goto err; /* @@ -232,7 +233,7 @@ static int ch_init(QUIC_CHANNEL *ch) if (!ossl_quic_rxfc_init(&ch->stream0->rxfc, &ch->conn_rxfc, 1 * 1024 * 1024, 5 * 1024 * 1024, - get_time, NULL)) + get_time, ch)) goto err; /* Plug in the TLS handshake layer. */ @@ -332,6 +333,8 @@ QUIC_CHANNEL *ossl_quic_channel_new(const QUIC_CHANNEL_ARGS *args) ch->is_server = args->is_server; ch->tls = args->tls; ch->mutex = args->mutex; + ch->now_cb = args->now_cb; + ch->now_cb_arg = args->now_cb_arg; if (!ch_init(ch)) { OPENSSL_free(ch); @@ -457,7 +460,12 @@ CRYPTO_MUTEX *ossl_quic_channel_get_mutex(QUIC_CHANNEL *ch) /* Used by various components. */ static OSSL_TIME get_time(void *arg) { - return ossl_time_now(); + QUIC_CHANNEL *ch = arg; + + if (ch->now_cb == NULL) + return ossl_time_now(); + + return ch->now_cb(ch->now_cb_arg); } /* Used by QSM. */ @@ -1237,7 +1245,7 @@ static void ch_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags) * expired. */ if (ossl_quic_channel_is_terminating(ch)) { - now = ossl_time_now(); + now = get_time(ch); if (ossl_time_compare(now, ch->terminate_deadline) >= 0) { ch_on_terminating_timeout(ch); @@ -1279,7 +1287,7 @@ static void ch_tick(QUIC_TICK_RESULT *res, void *arg, uint32_t flags) * ACKM ACK generation deadline is polled by TXP, so we don't need to handle * it here. */ - now = ossl_time_now(); + now = get_time(ch); if (ossl_time_compare(now, ch->idle_deadline) >= 0) { /* * Idle timeout differs from normal protocol violation because we do not @@ -1934,7 +1942,7 @@ static void ch_start_terminating(QUIC_CHANNEL *ch, ch->state = tcause->remote ? QUIC_CHANNEL_STATE_TERMINATING_DRAINING : QUIC_CHANNEL_STATE_TERMINATING_CLOSING; ch->terminate_deadline - = ossl_time_add(ossl_time_now(), + = ossl_time_add(get_time(ch), ossl_time_multiply(ossl_ackm_get_pto_duration(ch->ackm), 3)); @@ -2038,7 +2046,7 @@ static void ch_update_idle(QUIC_CHANNEL *ch) if (ch->max_idle_timeout == 0) ch->idle_deadline = ossl_time_infinite(); else - ch->idle_deadline = ossl_time_add(ossl_time_now(), + ch->idle_deadline = ossl_time_add(get_time(ch), ossl_ms2time(ch->max_idle_timeout)); } diff --git a/ssl/quic/quic_channel_local.h b/ssl/quic/quic_channel_local.h index e1e60bf2b4..57db9792a9 100644 --- a/ssl/quic/quic_channel_local.h +++ b/ssl/quic/quic_channel_local.h @@ -33,6 +33,12 @@ struct quic_channel_st { */ CRYPTO_RWLOCK *mutex; + /* + * Callback used to get the current time. + */ + OSSL_TIME (*now_cb)(void *arg); + void *now_cb_arg; + /* * The associated TLS 1.3 connection data. Used to provide the handshake * layer; its 'network' side is plugged into the crypto stream for each EL diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c index b478e7c4f2..766a88633a 100644 --- a/ssl/quic/quic_impl.c +++ b/ssl/quic/quic_impl.c @@ -246,6 +246,16 @@ int ossl_quic_clear(SSL *s) return 1; } +void ossl_quic_conn_set_override_now_cb(SSL *s, + OSSL_TIME (*now_cb)(void *arg), + void *now_cb_arg) +{ + QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(s); + + qc->override_now_cb = now_cb; + qc->override_now_cb_arg = now_cb_arg; +} + /* * QUIC Front-End I/O API: Network BIO Configuration * ================================================= @@ -683,6 +693,8 @@ static int ensure_channel(QUIC_CONNECTION *qc) args.is_server = 0; args.tls = qc->tls; args.mutex = qc->mutex; + args.now_cb = qc->override_now_cb; + args.now_cb_arg = qc->override_now_cb_arg; qc->ch = ossl_quic_channel_new(&args); if (qc->ch == NULL) diff --git a/ssl/quic/quic_local.h b/ssl/quic/quic_local.h index d405052674..a20e92df2a 100644 --- a/ssl/quic/quic_local.h +++ b/ssl/quic/quic_local.h @@ -71,6 +71,10 @@ struct quic_conn_st { QUIC_THREAD_ASSIST thread_assist; #endif + /* If non-NULL, used instead of ossl_time_now(). Used for testing. */ + OSSL_TIME (*override_now_cb)(void *arg); + void *override_now_cb_arg; + /* Have we started? */ unsigned int started : 1; diff --git a/ssl/quic/quic_tserver.c b/ssl/quic/quic_tserver.c index 4278f13f09..110e34a837 100644 --- a/ssl/quic/quic_tserver.c +++ b/ssl/quic/quic_tserver.c @@ -94,6 +94,8 @@ QUIC_TSERVER *ossl_quic_tserver_new(const QUIC_TSERVER_ARGS *args, ch_args.tls = srv->tls; ch_args.mutex = srv->mutex; ch_args.is_server = 1; + ch_args.now_cb = srv->args.now_cb; + ch_args.now_cb_arg = srv->args.now_cb_arg; if ((srv->ch = ossl_quic_channel_new(&ch_args)) == NULL) goto err;