mirror of https://github.com/openssl/openssl.git
Make SSL_poll() and SSL_shutdown() better friends
Current QUIC stack may leave connection monitored by SSL_poll() to stale during regular shutdown. The issue is triggered when ACK for client's FIN gets delayed. The sequeance of operations to trigger the stale of QUIC connection at client goes as follows: - application calls SSL_shutdown() on connection, the shutdown can not proceed, because bi-directional stream must be flushed. The client awaits ACK from server acknowledging reception of FIN on client's stream - the stream object gets destroyed, because application received all data from server. - application updates poll set and passes to SSL_poll() - ssl poll ticks the engine. Engine receives delayed ACK and marks stream as flushed. At this point the SSL_shutdown() operation may proceed given the application calls the SSL_shutdown(). However there is no mechanism to make SSL_poll() return so application is unable to proceed with its event loop where SSL_shutdown() may get called. This change introduces ossl_quic_channel_notify_flush_done() function which notifies channel when all streams are flushed (all FINs got ACKed). The first thing SSL_shudown() does it calls ossl_quic_stream_map_begin_shutdown_flush(). The function walks list of all streams attached to channel and notes how many streams is missing ACK for their FIN. In our test case it finds one such stream. Call to SSL_shutdown() returns and application destroys the SSL stream object and updates a poll set. SSL_poll() gets called. The QUIC stack (engine) gets ticked and reads data from socket. It processes delayed ACK now. The ACK-manager updates the stream notifying the server ACKs the FIN sent by client. The stream is flushed now. Thw shutdown_flush_done() for stream gets called on behalf of ACK manager. The shutdown_flush_done() does two things: - it marks stream as flushed - it decrements the num_shutdown_flush counter initialized be earlier call to ossl_quic_stream_map_begin_shutdown_flush() called by SSL_shutdown() The change here calls ossl_quic_channel_notify_flush_done() when num_shutdown_flush reaches zero. The ossl_quic_channel_notify_flush_done() then calls function ossl_quic_channel_notify_flush_done(), which just moves the state of the channel (connection) from active to terminating state. The change of channel state is sufficent for SSL_poll() to signal _EC event on connection. Once application receives _EC event on connection it should check the state of the channel/reason of error. In regular case the error/channel state hints application to call SSL_shutdown() so connection object can proceed with connection shutdown. The SSL_shutdown() call done now moves channel to terminated state. So the next call to SSL_poll() can signal _ECD which tells application it's time to stop polling on SSL connection object and destroy it. Fixes openssl/project#1291 Reviewed-by: Neil Horman <nhorman@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/28116)
This commit is contained in:
parent
07f65e16c2
commit
1d92f3b8b0
|
@ -345,6 +345,8 @@ int ossl_quic_channel_is_terminated(const QUIC_CHANNEL *ch);
|
|||
int ossl_quic_channel_is_active(const QUIC_CHANNEL *ch);
|
||||
int ossl_quic_channel_is_handshake_complete(const QUIC_CHANNEL *ch);
|
||||
int ossl_quic_channel_is_handshake_confirmed(const QUIC_CHANNEL *ch);
|
||||
int ossl_quic_channel_is_server(const QUIC_CHANNEL *ch);
|
||||
void ossl_quic_channel_notify_flush_done(QUIC_CHANNEL *ch);
|
||||
|
||||
QUIC_PORT *ossl_quic_channel_get0_port(QUIC_CHANNEL *ch);
|
||||
QUIC_ENGINE *ossl_quic_channel_get0_engine(QUIC_CHANNEL *ch);
|
||||
|
@ -470,6 +472,8 @@ int ossl_quic_bind_channel(QUIC_CHANNEL *ch, const BIO_ADDR *peer,
|
|||
const QUIC_CONN_ID *scid, const QUIC_CONN_ID *dcid,
|
||||
const QUIC_CONN_ID *odcid);
|
||||
|
||||
void ossl_quic_channel_set_tcause(QUIC_CHANNEL *ch, uint64_t app_error_code,
|
||||
const char *app_reason);
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -554,6 +554,7 @@ static ossl_inline ossl_unused size_t ossl_quic_stream_recv_pending(const QUIC_S
|
|||
*/
|
||||
struct quic_stream_map_st {
|
||||
LHASH_OF(QUIC_STREAM) *map;
|
||||
QUIC_CHANNEL *ch;
|
||||
QUIC_STREAM_LIST_NODE active_list;
|
||||
QUIC_STREAM_LIST_NODE accept_list;
|
||||
QUIC_STREAM_LIST_NODE ready_for_gc_list;
|
||||
|
@ -564,7 +565,6 @@ struct quic_stream_map_st {
|
|||
void *get_stream_limit_cb_arg;
|
||||
QUIC_RXFC *max_streams_bidi_rxfc;
|
||||
QUIC_RXFC *max_streams_uni_rxfc;
|
||||
int is_server;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -585,7 +585,7 @@ int ossl_quic_stream_map_init(QUIC_STREAM_MAP *qsm,
|
|||
void *get_stream_limit_cb_arg,
|
||||
QUIC_RXFC *max_streams_bidi_rxfc,
|
||||
QUIC_RXFC *max_streams_uni_rxfc,
|
||||
int is_server);
|
||||
QUIC_CHANNEL *ch);
|
||||
|
||||
/*
|
||||
* Any streams still in the map will be released as though
|
||||
|
|
|
@ -249,7 +249,7 @@ static int ch_init(QUIC_CHANNEL *ch)
|
|||
if (!ossl_quic_stream_map_init(&ch->qsm, get_stream_limit, ch,
|
||||
&ch->max_streams_bidi_rxfc,
|
||||
&ch->max_streams_uni_rxfc,
|
||||
ch->is_server))
|
||||
ch))
|
||||
goto err;
|
||||
|
||||
ch->have_qsm = 1;
|
||||
|
@ -398,8 +398,12 @@ static void ch_cleanup(QUIC_CHANNEL *ch)
|
|||
++pn_space)
|
||||
ossl_ackm_on_pkt_space_discarded(ch->ackm, pn_space);
|
||||
|
||||
ossl_quic_lcidm_cull(ch->lcidm, ch);
|
||||
ossl_quic_srtm_cull(ch->srtm, ch);
|
||||
if (ch->lcidm != NULL)
|
||||
ossl_quic_lcidm_cull(ch->lcidm, ch);
|
||||
|
||||
if (ch->srtm != NULL)
|
||||
ossl_quic_srtm_cull(ch->srtm, ch);
|
||||
|
||||
ossl_quic_tx_packetiser_free(ch->txp);
|
||||
ossl_quic_txpim_free(ch->txpim);
|
||||
ossl_quic_cfq_free(ch->cfq);
|
||||
|
@ -649,6 +653,45 @@ int ossl_quic_channel_is_term_any(const QUIC_CHANNEL *ch)
|
|||
|| ossl_quic_channel_is_terminated(ch);
|
||||
}
|
||||
|
||||
int ossl_quic_channel_is_server(const QUIC_CHANNEL *ch)
|
||||
{
|
||||
return ch->is_server;
|
||||
}
|
||||
|
||||
void ossl_quic_channel_notify_flush_done(QUIC_CHANNEL *ch)
|
||||
{
|
||||
ch_record_state_transition(ch, ch->terminate_cause.remote
|
||||
? QUIC_CHANNEL_STATE_TERMINATING_DRAINING
|
||||
: QUIC_CHANNEL_STATE_TERMINATING_CLOSING);
|
||||
/*
|
||||
* RFC 9000 s. 10.2 Immediate Close
|
||||
* These states SHOULD persist for at least three times
|
||||
* the current PTO interval as defined in [QUIC-RECOVERY].
|
||||
*/
|
||||
ch->terminate_deadline
|
||||
= ossl_time_add(get_time(ch),
|
||||
ossl_time_multiply(ossl_ackm_get_pto_duration(ch->ackm), 3));
|
||||
if (!ch->terminate_cause.remote) {
|
||||
OSSL_QUIC_FRAME_CONN_CLOSE f = {0};
|
||||
|
||||
/* best effort */
|
||||
f.error_code = ch->terminate_cause.error_code;
|
||||
f.frame_type = ch->terminate_cause.frame_type;
|
||||
f.is_app = ch->terminate_cause.app;
|
||||
f.reason = (char *)ch->terminate_cause.reason;
|
||||
f.reason_len = ch->terminate_cause.reason_len;
|
||||
ossl_quic_tx_packetiser_schedule_conn_close(ch->txp, &f);
|
||||
/*
|
||||
* RFC 9000 s. 10.2.2 Draining Connection State:
|
||||
* An endpoint that receives a CONNECTION_CLOSE frame MAY
|
||||
* send a single packet containing a CONNECTION_CLOSE
|
||||
* frame before entering the draining state, using a
|
||||
* NO_ERROR code if appropriate
|
||||
*/
|
||||
ch->conn_close_queued = 1;
|
||||
}
|
||||
}
|
||||
|
||||
const QUIC_TERMINATE_CAUSE *
|
||||
ossl_quic_channel_get_terminate_cause(const QUIC_CHANNEL *ch)
|
||||
{
|
||||
|
@ -3156,14 +3199,17 @@ int ossl_quic_channel_on_handshake_confirmed(QUIC_CHANNEL *ch)
|
|||
static void copy_tcause(QUIC_TERMINATE_CAUSE *dst,
|
||||
const QUIC_TERMINATE_CAUSE *src)
|
||||
{
|
||||
/*
|
||||
* do not override reason once it got set.
|
||||
*/
|
||||
if (dst->reason != NULL)
|
||||
return;
|
||||
|
||||
dst->error_code = src->error_code;
|
||||
dst->frame_type = src->frame_type;
|
||||
dst->app = src->app;
|
||||
dst->remote = src->remote;
|
||||
|
||||
dst->reason = NULL;
|
||||
dst->reason_len = 0;
|
||||
|
||||
if (src->reason != NULL && src->reason_len > 0) {
|
||||
size_t l = src->reason_len;
|
||||
char *r;
|
||||
|
@ -3184,6 +3230,18 @@ static void copy_tcause(QUIC_TERMINATE_CAUSE *dst,
|
|||
}
|
||||
}
|
||||
|
||||
void ossl_quic_channel_set_tcause(QUIC_CHANNEL *ch, uint64_t app_error_code,
|
||||
const char *app_reason)
|
||||
{
|
||||
QUIC_TERMINATE_CAUSE tcause = {0};
|
||||
|
||||
tcause.app = 1;
|
||||
tcause.error_code = app_error_code;
|
||||
tcause.reason = app_reason;
|
||||
tcause.reason_len = app_reason != NULL ? strlen(app_reason) : 0;
|
||||
copy_tcause(&ch->terminate_cause, &tcause);
|
||||
}
|
||||
|
||||
static void ch_start_terminating(QUIC_CHANNEL *ch,
|
||||
const QUIC_TERMINATE_CAUSE *tcause,
|
||||
int force_immediate)
|
||||
|
@ -3205,38 +3263,7 @@ static void ch_start_terminating(QUIC_CHANNEL *ch,
|
|||
ossl_qlog_event_connectivity_connection_closed(ch_get_qlog(ch), tcause);
|
||||
|
||||
if (!force_immediate) {
|
||||
ch_record_state_transition(ch, tcause->remote
|
||||
? QUIC_CHANNEL_STATE_TERMINATING_DRAINING
|
||||
: QUIC_CHANNEL_STATE_TERMINATING_CLOSING);
|
||||
/*
|
||||
* RFC 9000 s. 10.2 Immediate Close
|
||||
* These states SHOULD persist for at least three times
|
||||
* the current PTO interval as defined in [QUIC-RECOVERY].
|
||||
*/
|
||||
ch->terminate_deadline
|
||||
= ossl_time_add(get_time(ch),
|
||||
ossl_time_multiply(ossl_ackm_get_pto_duration(ch->ackm),
|
||||
3));
|
||||
|
||||
if (!tcause->remote) {
|
||||
OSSL_QUIC_FRAME_CONN_CLOSE f = {0};
|
||||
|
||||
/* best effort */
|
||||
f.error_code = ch->terminate_cause.error_code;
|
||||
f.frame_type = ch->terminate_cause.frame_type;
|
||||
f.is_app = ch->terminate_cause.app;
|
||||
f.reason = (char *)ch->terminate_cause.reason;
|
||||
f.reason_len = ch->terminate_cause.reason_len;
|
||||
ossl_quic_tx_packetiser_schedule_conn_close(ch->txp, &f);
|
||||
/*
|
||||
* RFC 9000 s. 10.2.2 Draining Connection State:
|
||||
* An endpoint that receives a CONNECTION_CLOSE frame MAY
|
||||
* send a single packet containing a CONNECTION_CLOSE
|
||||
* frame before entering the draining state, using a
|
||||
* NO_ERROR code if appropriate
|
||||
*/
|
||||
ch->conn_close_queued = 1;
|
||||
}
|
||||
ossl_quic_channel_notify_flush_done(ch);
|
||||
} else {
|
||||
ch_on_terminating_timeout(ch);
|
||||
}
|
||||
|
|
|
@ -1501,6 +1501,36 @@ static int quic_shutdown_peer_wait(void *arg)
|
|||
return ossl_quic_channel_is_term_any(qc->ch);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function deals with local shutdown.
|
||||
* Function must consider those scenarios:
|
||||
* - blocking mode (1)
|
||||
* - non-blocking mode (2)
|
||||
* - non-blocking mode with assistance from SSL_poll() (3)
|
||||
* (1) The function completes shutdown then returns back to caller.
|
||||
* To complete shutdown we must do:
|
||||
* - flush all streams, unless we got SSL_SHUTDOWN_FLAG_NO_STREAM_FLUSH,
|
||||
* which means the connection is closed without waiting for streams
|
||||
* to deliver data written by application.
|
||||
* - let remote peer know local application is going to close connection,
|
||||
* unless we got SSL_SHUTDOWN_FLAG_WAIT_PEER in which case we await
|
||||
* until remote peer closes the connection
|
||||
* - wait for peer to confirm connection close
|
||||
*
|
||||
* (2) The function does not block waiting for streams to be flushed
|
||||
* nor for peer to close connection (when running with SSL_SHUTDOWN_FLAG_WAIT_PEER)
|
||||
* Application is supposed to call SSL_shutdown() repeatedly as long as
|
||||
* function returns 0 which indicates the operation is still in progress.
|
||||
*
|
||||
* (3) In this case application uses SSL_poll() to wait for completion
|
||||
* of each step of shutdown process. Application calls SSL_shutdown()
|
||||
* to start with connection shutdown. The function does not block.
|
||||
* Application then uses SSL_poll() on connection object to monitor
|
||||
* progress of shutdown. The SSL_poll() indicates progress by signaling
|
||||
* SSL_POLL_EVENT_EC event. Application must check connection object
|
||||
* for error. If no error is indicated, then application must call
|
||||
* SSL_shutdown() to move to the next stop in shutdown process.
|
||||
*/
|
||||
QUIC_TAKES_LOCK
|
||||
int ossl_quic_conn_shutdown(SSL *s, uint64_t flags,
|
||||
const SSL_SHUTDOWN_EX_ARGS *args,
|
||||
|
@ -1527,6 +1557,19 @@ int ossl_quic_conn_shutdown(SSL *s, uint64_t flags,
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (!wait_peer) {
|
||||
/*
|
||||
* Set shutdown reason now when local application wants to do
|
||||
* active close (does not waant to wait for peer to close th
|
||||
* connection). The reason will be sent to peer with connection
|
||||
* close notification as soon as streams will be flushed.
|
||||
*/
|
||||
if (args != NULL) {
|
||||
ossl_quic_channel_set_tcause(ctx.qc->ch, args->quic_error_code,
|
||||
args->quic_reason);
|
||||
}
|
||||
}
|
||||
|
||||
/* Phase 1: Stream Flushing */
|
||||
if (!wait_peer && stream_flush) {
|
||||
qc_shutdown_flush_init(ctx.qc);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "internal/quic_stream_map.h"
|
||||
#include "internal/nelem.h"
|
||||
#include "internal/quic_channel.h"
|
||||
|
||||
/*
|
||||
* QUIC Stream Map
|
||||
|
@ -92,7 +93,7 @@ int ossl_quic_stream_map_init(QUIC_STREAM_MAP *qsm,
|
|||
void *get_stream_limit_cb_arg,
|
||||
QUIC_RXFC *max_streams_bidi_rxfc,
|
||||
QUIC_RXFC *max_streams_uni_rxfc,
|
||||
int is_server)
|
||||
QUIC_CHANNEL *ch)
|
||||
{
|
||||
qsm->map = lh_QUIC_STREAM_new(hash_stream, cmp_stream);
|
||||
qsm->active_list.prev = qsm->active_list.next = &qsm->active_list;
|
||||
|
@ -111,7 +112,7 @@ int ossl_quic_stream_map_init(QUIC_STREAM_MAP *qsm,
|
|||
qsm->get_stream_limit_cb_arg = get_stream_limit_cb_arg;
|
||||
qsm->max_streams_bidi_rxfc = max_streams_bidi_rxfc;
|
||||
qsm->max_streams_uni_rxfc = max_streams_uni_rxfc;
|
||||
qsm->is_server = is_server;
|
||||
qsm->ch = ch;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -156,7 +157,7 @@ QUIC_STREAM *ossl_quic_stream_map_alloc(QUIC_STREAM_MAP *qsm,
|
|||
|
||||
s->id = stream_id;
|
||||
s->type = type;
|
||||
s->as_server = qsm->is_server;
|
||||
s->as_server = ossl_quic_channel_is_server(qsm->ch);
|
||||
s->send_state = (ossl_quic_stream_is_local_init(s)
|
||||
|| ossl_quic_stream_is_bidi(s))
|
||||
? QUIC_SSTREAM_STATE_READY
|
||||
|
@ -329,7 +330,7 @@ void ossl_quic_stream_map_update_state(QUIC_STREAM_MAP *qsm, QUIC_STREAM *s)
|
|||
{
|
||||
int should_be_active, allowed_by_stream_limit = 1;
|
||||
|
||||
if (ossl_quic_stream_is_server_init(s) == qsm->is_server) {
|
||||
if (ossl_quic_stream_is_server_init(s) == ossl_quic_channel_is_server(qsm->ch)) {
|
||||
int is_uni = !ossl_quic_stream_is_bidi(s);
|
||||
uint64_t stream_ordinal = s->id >> 2;
|
||||
|
||||
|
@ -425,6 +426,14 @@ static void shutdown_flush_done(QUIC_STREAM_MAP *qsm, QUIC_STREAM *qs)
|
|||
assert(qsm->num_shutdown_flush > 0);
|
||||
qs->shutdown_flush = 0;
|
||||
--qsm->num_shutdown_flush;
|
||||
|
||||
/*
|
||||
* when num_shutdown_flush becomes zero we need to poke
|
||||
* SSL_poll() it's time to poke to SSL_shutdown() to proceed
|
||||
* with shutdown process as all streams are gone (flushed).
|
||||
*/
|
||||
if (qsm->num_shutdown_flush == 0)
|
||||
ossl_quic_channel_notify_flush_done(qsm->ch);
|
||||
}
|
||||
|
||||
int ossl_quic_stream_map_notify_totally_acked(QUIC_STREAM_MAP *qsm,
|
||||
|
|
|
@ -198,6 +198,7 @@ struct script_op {
|
|||
#define OPK_C_WRITE_EX2 52
|
||||
#define OPK_SKIP_IF_BLOCKING 53
|
||||
#define OPK_C_STREAM_RESET_FAIL 54
|
||||
#define OPK_C_SHUTDOWN 55
|
||||
|
||||
#define EXPECT_CONN_CLOSE_APP (1U << 0)
|
||||
#define EXPECT_CONN_CLOSE_REMOTE (1U << 1)
|
||||
|
@ -270,6 +271,8 @@ struct script_op {
|
|||
{OPK_C_SET_DEFAULT_STREAM_MODE, NULL, (mode), NULL, NULL},
|
||||
#define OP_C_SET_INCOMING_STREAM_POLICY(policy) \
|
||||
{OPK_C_SET_INCOMING_STREAM_POLICY, NULL, (policy), NULL, NULL},
|
||||
#define OP_C_SHUTDOWN(reason, flags) \
|
||||
{OPK_C_SHUTDOWN, (reason), (flags), NULL, NULL},
|
||||
#define OP_C_SHUTDOWN_WAIT(reason, flags) \
|
||||
{OPK_C_SHUTDOWN_WAIT, (reason), (flags), NULL, NULL},
|
||||
#define OP_C_EXPECT_CONN_CLOSE_INFO(ec, app, remote) \
|
||||
|
@ -1658,6 +1661,25 @@ static int run_script_worker(struct helper *h, const struct script_op *script,
|
|||
}
|
||||
break;
|
||||
|
||||
case OPK_C_SHUTDOWN:
|
||||
{
|
||||
int ret;
|
||||
QUIC_CHANNEL *ch = ossl_quic_conn_get_channel(h->c_conn);
|
||||
SSL_SHUTDOWN_EX_ARGS args = {0};
|
||||
|
||||
ossl_quic_engine_set_inhibit_tick(ossl_quic_channel_get0_engine(ch), 0);
|
||||
|
||||
if (!TEST_ptr(c_tgt))
|
||||
goto out;
|
||||
|
||||
args.quic_reason = (const char *)op->arg0;
|
||||
|
||||
ret = SSL_shutdown_ex(c_tgt, op->arg1, &args, sizeof(args));
|
||||
if (!TEST_int_ge(ret, 0))
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case OPK_C_SHUTDOWN_WAIT:
|
||||
{
|
||||
int ret;
|
||||
|
@ -5787,6 +5809,189 @@ static const struct script_op script_85[] = {
|
|||
OP_END
|
||||
};
|
||||
|
||||
#define POLL_FMT "%s%s%s%s%s%s%s%s%s%s%s%s%s"
|
||||
#define POLL_PRINTA(_revents_) \
|
||||
(_revents_) & SSL_POLL_EVENT_F ? "SSL_POLL_EVENT_F " : "", \
|
||||
(_revents_) & SSL_POLL_EVENT_EL ? "SSL_POLL_EVENT_EL " : "", \
|
||||
(_revents_) & SSL_POLL_EVENT_EC ? "SSL_POLL_EVENT_EC " : "", \
|
||||
(_revents_) & SSL_POLL_EVENT_ECD ? "SSL_POLL_EVENT_ECD " : "", \
|
||||
(_revents_) & SSL_POLL_EVENT_ER ? "SSL_POLL_EVENT_ER " : "", \
|
||||
(_revents_) & SSL_POLL_EVENT_EW ? "SSL_POLL_EVENT_EW " : "", \
|
||||
(_revents_) & SSL_POLL_EVENT_R ? "SSL_POLL_EVENT_R " : "", \
|
||||
(_revents_) & SSL_POLL_EVENT_W ? "SSL_POLL_EVENT_W " : "", \
|
||||
(_revents_) & SSL_POLL_EVENT_IC ? "SSL_POLL_EVENT_IC " : "", \
|
||||
(_revents_) & SSL_POLL_EVENT_ISB ? "SSL_POLL_EVENT_ISB " : "", \
|
||||
(_revents_) & SSL_POLL_EVENT_ISU ? "SSL_POLL_EVENT_ISU " : "", \
|
||||
(_revents_) & SSL_POLL_EVENT_OSB ? "SSL_POLL_EVENT_OSB " : "", \
|
||||
(_revents_) & SSL_POLL_EVENT_OSU ? "SSL_POLL_EVENT_OSU " : ""
|
||||
|
||||
/* 88. Test SSL_poll (lite, non-blocking) */
|
||||
ossl_unused static int script_88_poll(struct helper *h, struct helper_local *hl)
|
||||
{
|
||||
int ok = 1, ret, expected_ret = 1;
|
||||
static const struct timeval timeout = {0};
|
||||
size_t result_count, processed;
|
||||
SSL_POLL_ITEM items[2] = {0}, *item = items;
|
||||
SSL *c_a;
|
||||
size_t i;
|
||||
uint64_t mode, expected_revents[2] = {0};
|
||||
|
||||
if (!TEST_ptr(c_a = helper_local_get_c_stream(hl, "a")))
|
||||
return 0;
|
||||
|
||||
item->desc = SSL_as_poll_descriptor(c_a);
|
||||
item->events = UINT64_MAX;
|
||||
item->revents = UINT64_MAX;
|
||||
++item;
|
||||
|
||||
item->desc = SSL_as_poll_descriptor(h->c_conn);
|
||||
item->events = UINT64_MAX;
|
||||
item->revents = UINT64_MAX;
|
||||
++item;
|
||||
|
||||
result_count = SIZE_MAX;
|
||||
ret = SSL_poll(items, OSSL_NELEM(items), sizeof(SSL_POLL_ITEM),
|
||||
&timeout, 0,
|
||||
&result_count);
|
||||
|
||||
mode = hl->check_op->arg2;
|
||||
switch (mode) {
|
||||
case 0:
|
||||
/* No incoming data yet */
|
||||
expected_revents[0] = SSL_POLL_EVENT_W;
|
||||
expected_revents[1] = SSL_POLL_EVENT_OS;
|
||||
break;
|
||||
case 1:
|
||||
/* Expect more events */
|
||||
expected_revents[0] = SSL_POLL_EVENT_R;
|
||||
expected_revents[1] = SSL_POLL_EVENT_OS;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!TEST_int_eq(ret, expected_ret))
|
||||
ok = 0;
|
||||
|
||||
/*
|
||||
* Unlike script 85 which always expects all objects
|
||||
* get signaled in single call to SSL_poll() we must
|
||||
* assume here we can get notification for only one.
|
||||
*/
|
||||
processed = 0;
|
||||
for (i = 0; i < OSSL_NELEM(items); ++i) {
|
||||
if (items[i].revents == 0)
|
||||
continue;
|
||||
|
||||
processed++;
|
||||
if (!TEST_uint64_t_eq(items[i].revents, expected_revents[i])) {
|
||||
TEST_info("wanted: " POLL_FMT " got: " POLL_FMT,
|
||||
POLL_PRINTA(expected_revents[i]),
|
||||
POLL_PRINTA(items[i].revents));
|
||||
TEST_error("mismatch at index %zu in poll results, mode %d",
|
||||
i, (int)mode);
|
||||
ok = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!TEST_size_t_eq(processed, result_count))
|
||||
ok = 0;
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
ossl_unused static int script_88_poll_conly(struct helper *h, struct helper_local *hl)
|
||||
{
|
||||
int ok = 1;
|
||||
static const struct timeval timeout = {0};
|
||||
size_t result_count;
|
||||
SSL_POLL_ITEM items[1] = {0};
|
||||
int done = 0;
|
||||
OSSL_TIME t_limit;
|
||||
|
||||
result_count = SIZE_MAX;
|
||||
|
||||
items[0].desc = SSL_as_poll_descriptor(h->c_conn);
|
||||
items[0].events = UINT64_MAX;
|
||||
items[0].revents = UINT64_MAX;
|
||||
|
||||
t_limit = ossl_time_add(ossl_time_now(),
|
||||
ossl_ticks2time(5 * OSSL_TIME_SECOND));
|
||||
while (done == 0 && ok == 1) {
|
||||
ok = SSL_poll(items, OSSL_NELEM(items), sizeof(SSL_POLL_ITEM),
|
||||
&timeout, 0,
|
||||
&result_count);
|
||||
|
||||
if (!TEST_int_eq(ok, 1))
|
||||
continue;
|
||||
|
||||
if (result_count == 0)
|
||||
OSSL_sleep(10);
|
||||
|
||||
TEST_info("received event " POLL_FMT, POLL_PRINTA(items[0].revents));
|
||||
|
||||
if ((items[0].revents & SSL_POLL_EVENT_EC) == SSL_POLL_EVENT_EC)
|
||||
SSL_shutdown(h->c_conn);
|
||||
done =
|
||||
((items[0].revents & SSL_POLL_EVENT_ECD) == SSL_POLL_EVENT_ECD);
|
||||
|
||||
if (ossl_time_compare(ossl_time_now(), t_limit) == 1) {
|
||||
TEST_error("shutdown time exceeded 5sec");
|
||||
ok = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
/*
|
||||
* verify SSL_poll() signals SSL_POLL_EVENT_EC event
|
||||
* to notify client it's time to call SSL_shutdown().
|
||||
*/
|
||||
static const struct script_op script_88[] = {
|
||||
OP_SKIP_IF_BLOCKING (16)
|
||||
OP_C_SET_ALPN ("ossltest")
|
||||
OP_C_CONNECT_WAIT ()
|
||||
|
||||
OP_C_SET_DEFAULT_STREAM_MODE(SSL_DEFAULT_STREAM_MODE_NONE)
|
||||
|
||||
OP_C_NEW_STREAM_BIDI (a, C_BIDI_ID(0))
|
||||
OP_S_BIND_STREAM_ID (a, C_BIDI_ID(0))
|
||||
|
||||
/* Check nothing readable yet. */
|
||||
OP_CHECK (script_88_poll, 0 /* ->arg2 */)
|
||||
|
||||
OP_C_WRITE (a, "flamingo", 8)
|
||||
OP_C_CONCLUDE (a)
|
||||
|
||||
/* Send something that will make client sockets readable. */
|
||||
OP_S_READ_EXPECT (a, "flamingo", 8)
|
||||
OP_S_WRITE (a, "flamingo", 8)
|
||||
OP_S_CONCLUDE (a)
|
||||
|
||||
OP_CHECK (script_88_poll, 1 /* ->arg2 */)
|
||||
|
||||
OP_C_READ_EXPECT (a, "flamingo", 8)
|
||||
|
||||
/*
|
||||
* client calls non-blocking SSL_shutdown() and gives
|
||||
* server chance to run by calling sleep.
|
||||
*/
|
||||
OP_C_SHUTDOWN (NULL, 0)
|
||||
OP_SLEEP(100)
|
||||
|
||||
/*
|
||||
* Here we call SSL_poll() and handle SSL_POLL_EVENT_EC
|
||||
* and SSL_POLL_EVENT_ECD on connection object. Whenever
|
||||
* _EC event comes we call SSL_shutdown() to keep connection
|
||||
* draining. We keep calling SSL_poll()/SSL_shutdown() until
|
||||
* SSL_poll() signals SSL_POLL_EVENT_ECD to let us know connection
|
||||
* has dried out and con be closed.
|
||||
*/
|
||||
OP_CHECK (script_88_poll_conly, 0)
|
||||
|
||||
OP_END
|
||||
};
|
||||
/* 86. Event Handling Mode Configuration */
|
||||
static int set_event_handling_mode_conn(struct helper *h, struct helper_local *hl)
|
||||
{
|
||||
|
@ -5979,7 +6184,8 @@ static const struct script_op *const scripts[] = {
|
|||
script_84,
|
||||
script_85,
|
||||
script_86,
|
||||
script_87
|
||||
script_87,
|
||||
script_88,
|
||||
};
|
||||
|
||||
static int test_script(int idx)
|
||||
|
@ -6009,6 +6215,7 @@ static int test_script(int idx)
|
|||
|
||||
TEST_info("Running script %d (order=%d, blocking=%d)", script_idx + 1,
|
||||
free_order, blocking);
|
||||
|
||||
return run_script(scripts[script_idx], script_name, free_order, blocking);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "internal/quic_statm.h"
|
||||
#include "internal/quic_demux.h"
|
||||
#include "internal/quic_record_rx.h"
|
||||
#include "internal/quic_channel.h"
|
||||
#include "testutil.h"
|
||||
#include "quic_record_test_util.h"
|
||||
|
||||
|
@ -74,6 +75,7 @@ struct helper {
|
|||
OSSL_QUIC_FRAME_CONN_CLOSE conn_close;
|
||||
} frame;
|
||||
OSSL_QUIC_ACK_RANGE ack_ranges[16];
|
||||
QUIC_CHANNEL *client_ch;
|
||||
};
|
||||
|
||||
static void helper_cleanup(struct helper *h)
|
||||
|
@ -106,6 +108,7 @@ static void helper_cleanup(struct helper *h)
|
|||
ossl_quic_demux_free(h->demux);
|
||||
BIO_free(h->bio1);
|
||||
BIO_free(h->bio2);
|
||||
ossl_quic_channel_free(h->client_ch);
|
||||
}
|
||||
|
||||
static void demux_default_handler(QUIC_URXE *e, void *arg,
|
||||
|
@ -123,8 +126,10 @@ static int helper_init(struct helper *h)
|
|||
{
|
||||
int rc = 0;
|
||||
size_t i;
|
||||
QUIC_CHANNEL_ARGS client_ch_args;
|
||||
|
||||
memset(h, 0, sizeof(*h));
|
||||
memset(&client_ch_args, 0, sizeof(client_ch_args));
|
||||
|
||||
/* Initialisation */
|
||||
if (!TEST_true(BIO_new_bio_dgram_pair(&h->bio1, 0, &h->bio2, 0)))
|
||||
|
@ -186,10 +191,13 @@ static int helper_init(struct helper *h)
|
|||
/* is_server */0)))
|
||||
goto err;
|
||||
|
||||
h->client_ch = ossl_quic_channel_alloc(&client_ch_args);
|
||||
if (!TEST_ptr(h->client_ch))
|
||||
goto err;
|
||||
if (!TEST_true(ossl_quic_stream_map_init(&h->qsm, NULL, NULL,
|
||||
&h->max_streams_bidi_rxfc,
|
||||
&h->max_streams_uni_rxfc,
|
||||
/*is_server=*/0)))
|
||||
h->client_ch)))
|
||||
goto err;
|
||||
|
||||
h->have_qsm = 1;
|
||||
|
|
Loading…
Reference in New Issue