Enable QUIC test server to find out the termination reason

We enable querying of the termination reason which is useful for tests.

Reviewed-by: Hugo Landau <hlandau@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/20030)
This commit is contained in:
Matt Caswell 2022-12-01 16:37:47 +00:00 committed by Hugo Landau
parent ce3106baba
commit 149a8e6c0a
7 changed files with 82 additions and 52 deletions

View File

@ -64,6 +64,34 @@ typedef struct quic_channel_args_st {
typedef struct quic_channel_st QUIC_CHANNEL;
/* Represents the cause for a connection's termination. */
typedef struct quic_terminate_cause_st {
/*
* If we are in a TERMINATING or TERMINATED state, this is the error code
* associated with the error. This field is valid iff we are in the
* TERMINATING or TERMINATED states.
*/
uint64_t error_code;
/*
* If terminate_app is set and this is nonzero, this is the frame type which
* caused the connection to be terminated.
*/
uint64_t frame_type;
/* Is this error code in the transport (0) or application (1) space? */
unsigned int app : 1;
/*
* If set, the cause of the termination is a received CONNECTION_CLOSE
* frame. Otherwise, we decided to terminate ourselves and sent a
* CONNECTION_CLOSE frame (regardless of whether the peer later also sends
* one).
*/
unsigned int remote : 1;
} QUIC_TERMINATE_CAUSE;
/*
* Create a new QUIC channel using the given arguments. The argument structure
* does not need to remain allocated. Returns NULL on failure.
@ -158,9 +186,12 @@ QUIC_STREAM *ossl_quic_channel_get_stream_by_id(QUIC_CHANNEL *ch,
uint64_t stream_id);
/* Returns 1 if channel is terminating or terminated. */
int ossl_quic_channel_is_term_any(const QUIC_CHANNEL *ch);
int ossl_quic_channel_is_terminating(const QUIC_CHANNEL *ch);
int ossl_quic_channel_is_terminated(const QUIC_CHANNEL *ch);
int ossl_quic_channel_is_term_any(const QUIC_CHANNEL *ch,
QUIC_TERMINATE_CAUSE *cause);
int ossl_quic_channel_is_terminating(const QUIC_CHANNEL *ch,
QUIC_TERMINATE_CAUSE *cause);
int ossl_quic_channel_is_terminated(const QUIC_CHANNEL *ch,
QUIC_TERMINATE_CAUSE *cause);
int ossl_quic_channel_is_active(const QUIC_CHANNEL *ch);
int ossl_quic_channel_is_handshake_complete(const QUIC_CHANNEL *ch);

View File

@ -12,6 +12,7 @@
# include <openssl/ssl.h>
# include "internal/quic_stream.h"
# include "internal/quic_channel.h"
# ifndef OPENSSL_NO_QUIC
@ -54,8 +55,12 @@ int ossl_quic_tserver_tick(QUIC_TSERVER *srv);
int ossl_quic_tserver_is_connected(QUIC_TSERVER *srv);
/* Returns 1 if the server is in any terminating or terminated state */
int ossl_quic_tserver_is_term_any(QUIC_TSERVER *srv);
int ossl_quic_tserver_is_term_any(QUIC_TSERVER *srv,
QUIC_TERMINATE_CAUSE *cause);
/* Returns 1 if the server is in a terminated state */
int ossl_quic_tserver_is_terminated(QUIC_TSERVER *srv,
QUIC_TERMINATE_CAUSE *cause);
/*
* Attempts to read from stream 0. Writes the number of bytes read to
* *bytes_read and returns 1 on success. If no bytes are available, 0 is written

View File

@ -398,21 +398,34 @@ int ossl_quic_channel_is_active(const QUIC_CHANNEL *ch)
return ch != NULL && ch->state == QUIC_CHANNEL_STATE_ACTIVE;
}
int ossl_quic_channel_is_terminating(const QUIC_CHANNEL *ch)
int ossl_quic_channel_is_terminating(const QUIC_CHANNEL *ch,
QUIC_TERMINATE_CAUSE *cause)
{
return ch->state == QUIC_CHANNEL_STATE_TERMINATING_CLOSING
|| ch->state == QUIC_CHANNEL_STATE_TERMINATING_DRAINING;
if (ch->state == QUIC_CHANNEL_STATE_TERMINATING_CLOSING
|| ch->state == QUIC_CHANNEL_STATE_TERMINATING_DRAINING) {
if (cause != NULL)
*cause = ch->terminate_cause;
return 1;
}
return 0;
}
int ossl_quic_channel_is_terminated(const QUIC_CHANNEL *ch)
int ossl_quic_channel_is_terminated(const QUIC_CHANNEL *ch,
QUIC_TERMINATE_CAUSE *cause)
{
return ch->state == QUIC_CHANNEL_STATE_TERMINATED;
if (ch->state == QUIC_CHANNEL_STATE_TERMINATED) {
if (cause != NULL)
*cause = ch->terminate_cause;
return 1;
}
return 0;
}
int ossl_quic_channel_is_term_any(const QUIC_CHANNEL *ch)
int ossl_quic_channel_is_term_any(const QUIC_CHANNEL *ch,
QUIC_TERMINATE_CAUSE *cause)
{
return ossl_quic_channel_is_terminating(ch)
|| ossl_quic_channel_is_terminated(ch);
return ossl_quic_channel_is_terminating(ch, cause)
|| ossl_quic_channel_is_terminated(ch, cause);
}
int ossl_quic_channel_is_handshake_complete(const QUIC_CHANNEL *ch)
@ -1191,7 +1204,7 @@ static void ch_tick(QUIC_TICK_RESULT *res, void *arg)
*/
/* If we are in the TERMINATED state, there is nothing to do. */
if (ossl_quic_channel_is_terminated(ch)) {
if (ossl_quic_channel_is_terminated(ch, NULL)) {
res->net_read_desired = 0;
res->net_write_desired = 0;
res->tick_deadline = ossl_time_infinite();
@ -1202,7 +1215,7 @@ static void ch_tick(QUIC_TICK_RESULT *res, void *arg)
* If we are in the TERMINATING state, check if the terminating timer has
* expired.
*/
if (ossl_quic_channel_is_terminating(ch)) {
if (ossl_quic_channel_is_terminating(ch, NULL)) {
now = ossl_time_now();
if (ossl_time_compare(now, ch->terminate_deadline) >= 0) {
@ -1273,11 +1286,11 @@ static void ch_tick(QUIC_TICK_RESULT *res, void *arg)
* errors in ch_rx_pre() or ch_tx() may have caused us to transition to the
* Terminated state.
*/
res->net_read_desired = !ossl_quic_channel_is_terminated(ch);
res->net_read_desired = !ossl_quic_channel_is_terminated(ch, NULL);
/* We want to write to the network if we have any in our queue. */
res->net_write_desired
= (!ossl_quic_channel_is_terminated(ch)
= (!ossl_quic_channel_is_terminated(ch, NULL)
&& ossl_qtx_get_queue_len_datagrams(ch->qtx) > 0);
}
@ -1581,7 +1594,7 @@ static OSSL_TIME ch_determine_next_tick_deadline(QUIC_CHANNEL *ch)
OSSL_TIME deadline;
uint32_t pn_space;
if (ossl_quic_channel_is_terminated(ch))
if (ossl_quic_channel_is_terminated(ch, NULL))
return ossl_time_infinite();
deadline = ossl_ackm_get_loss_detection_deadline(ch->ackm);
@ -1599,7 +1612,7 @@ static OSSL_TIME ch_determine_next_tick_deadline(QUIC_CHANNEL *ch)
ch->cc_method->get_next_credit_time(ch->cc_data));
/* Is the terminating timer armed? */
if (ossl_quic_channel_is_terminating(ch))
if (ossl_quic_channel_is_terminating(ch, NULL))
deadline = ossl_time_min(deadline,
ch->terminate_deadline);
else if (!ossl_time_is_infinite(ch->idle_deadline))
@ -1729,7 +1742,7 @@ void ossl_quic_channel_local_close(QUIC_CHANNEL *ch, uint64_t app_error_code)
{
QUIC_TERMINATE_CAUSE tcause = {0};
if (ossl_quic_channel_is_term_any(ch))
if (ossl_quic_channel_is_term_any(ch, NULL))
return;
tcause.app = 1;

View File

@ -5,33 +5,6 @@
# ifndef OPENSSL_NO_QUIC
/* Represents the cause for a connection's termination. */
typedef struct quic_terminate_cause_st {
/*
* If we are in a TERMINATING or TERMINATED state, this is the error code
* associated with the error. This field is valid iff we are in the
* TERMINATING or TERMINATED states.
*/
uint64_t error_code;
/*
* If terminate_app is set and this is nonzero, this is the frame type which
* caused the connection to be terminated.
*/
uint64_t frame_type;
/* Is this error code in the transport (0) or application (1) space? */
unsigned int app : 1;
/*
* If set, the cause of the termination is a received CONNECTION_CLOSE
* frame. Otherwise, we decided to terminate ourselves and sent a
* CONNECTION_CLOSE frame (regardless of whether the peer later also sends
* one).
*/
unsigned int remote : 1;
} QUIC_TERMINATE_CAUSE;
/*
* QUIC Channel Structure
* ======================

View File

@ -646,7 +646,7 @@ int ossl_quic_do_handshake(QUIC_CONNECTION *qc)
/* Handshake already completed. */
return 1;
if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch))
if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch, NULL))
return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
if (BIO_ADDR_family(&qc->init_peer_addr) == AF_UNSPEC) {
@ -1012,7 +1012,7 @@ int ossl_quic_write(SSL *s, const void *buf, size_t len, size_t *written)
if (!expect_quic_conn(qc))
return 0;
if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch))
if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch, NULL))
return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
/*
@ -1133,7 +1133,7 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek
if (!expect_quic_conn(qc))
return 0;
if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch))
if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch, NULL))
return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL);
/* If we haven't finished the handshake, try to advance it. */

View File

@ -148,9 +148,17 @@ int ossl_quic_tserver_is_connected(QUIC_TSERVER *srv)
}
/* Returns 1 if the server is in any terminating or terminated state */
int ossl_quic_tserver_is_term_any(QUIC_TSERVER *srv)
int ossl_quic_tserver_is_term_any(QUIC_TSERVER *srv,
QUIC_TERMINATE_CAUSE *cause)
{
return ossl_quic_channel_is_term_any(srv->ch);
return ossl_quic_channel_is_term_any(srv->ch, cause);
}
/* Returns 1 if the server is in a terminated state */
int ossl_quic_tserver_is_terminated(QUIC_TSERVER *srv,
QUIC_TERMINATE_CAUSE *cause)
{
return ossl_quic_channel_is_terminated(srv->ch, cause);
}
int ossl_quic_tserver_read(QUIC_TSERVER *srv,

View File

@ -144,7 +144,7 @@ int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl)
SSL_tick(clientssl);
if (!servererr) {
ossl_quic_tserver_tick(qtserv);
servererr = ossl_quic_tserver_is_term_any(qtserv);
servererr = ossl_quic_tserver_is_term_any(qtserv, NULL);
if (!servererr && !rets)
rets = ossl_quic_tserver_is_connected(qtserv);
}