summaryrefslogtreecommitdiff
path: root/ssl
diff options
context:
space:
mode:
authorHugo Landau <hlandau@openssl.org>2023-04-18 19:30:54 +0100
committerHugo Landau <hlandau@openssl.org>2023-05-12 14:47:11 +0100
commitf20fdd16d817a095f58f9c016044abef24e50e58 (patch)
tree24d8ac2709db424c03f1825f02a9ab4ba25a25c7 /ssl
parent2dbc39deacf9d5850eecef515cbc50331750dd22 (diff)
downloadopenssl-new-f20fdd16d817a095f58f9c016044abef24e50e58.tar.gz
QUIC CHANNEL: Handle incoming remotely-created streams
Reviewed-by: Matt Caswell <matt@openssl.org> Reviewed-by: Tomas Mraz <tomas@openssl.org> (Merged from https://github.com/openssl/openssl/pull/20765)
Diffstat (limited to 'ssl')
-rw-r--r--ssl/quic/quic_channel.c25
-rw-r--r--ssl/quic/quic_impl.c11
-rw-r--r--ssl/quic/quic_rx_depack.c80
-rw-r--r--ssl/quic/quic_stream_map.c41
4 files changed, 146 insertions, 11 deletions
diff --git a/ssl/quic/quic_channel.c b/ssl/quic/quic_channel.c
index 8e1d415935..b11393955d 100644
--- a/ssl/quic/quic_channel.c
+++ b/ssl/quic/quic_channel.c
@@ -2214,7 +2214,7 @@ SSL *ossl_quic_channel_get0_ssl(QUIC_CHANNEL *ch)
return ch->tls;
}
-QUIC_STREAM *ossl_quic_channel_new_stream(QUIC_CHANNEL *ch, int is_uni)
+QUIC_STREAM *ossl_quic_channel_new_stream_local(QUIC_CHANNEL *ch, int is_uni)
{
QUIC_STREAM *qs;
int type = 0;
@@ -2242,3 +2242,26 @@ QUIC_STREAM *ossl_quic_channel_new_stream(QUIC_CHANNEL *ch, int is_uni)
++*p_next_ordinal;
return qs;
}
+
+QUIC_STREAM *ossl_quic_channel_new_stream_remote(QUIC_CHANNEL *ch,
+ uint64_t stream_id)
+{
+ uint64_t peer_role;
+ QUIC_STREAM *qs;
+
+ peer_role = ch->is_server
+ ? QUIC_STREAM_INITIATOR_CLIENT
+ : QUIC_STREAM_INITIATOR_SERVER;
+
+ if ((stream_id & QUIC_STREAM_INITIATOR_MASK) != peer_role)
+ return NULL;
+
+ qs = ossl_quic_stream_map_alloc(&ch->qsm, stream_id,
+ stream_id & (QUIC_STREAM_INITIATOR_MASK
+ | QUIC_STREAM_DIR_MASK));
+ if (qs == NULL)
+ return NULL;
+
+ ossl_quic_stream_map_push_accept_queue(&ch->qsm, qs);
+ return qs;
+}
diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c
index 0dd6576633..41de167dd2 100644
--- a/ssl/quic/quic_impl.c
+++ b/ssl/quic/quic_impl.c
@@ -296,6 +296,15 @@ void ossl_quic_free(SSL *s)
ctx.xso->stream->deleted = 1;
+ /* Auto-conclude stream. */
+ /* TODO(QUIC): Do RESET_STREAM here instead of auto-conclude */
+ if (ctx.xso->stream->sstream != NULL)
+ ossl_quic_sstream_fin(ctx.xso->stream->sstream);
+
+ /* Update stream state. */
+ ossl_quic_stream_map_update_state(ossl_quic_channel_get_qsm(ctx.xso->conn->ch),
+ ctx.xso->stream);
+
quic_unlock(ctx.qc);
/* Note: SSL_free calls OPENSSL_free(xso) for us */
@@ -1106,7 +1115,7 @@ SSL *ossl_quic_conn_stream_new(SSL *s, uint64_t flags)
xso->blocking = ctx.qc->default_blocking;
xso->ssl_mode = ctx.qc->default_ssl_mode;
- xso->stream = ossl_quic_channel_new_stream(ctx.qc->ch, is_uni);
+ xso->stream = ossl_quic_channel_new_stream_local(ctx.qc->ch, is_uni);
if (xso->stream == NULL)
goto err;
diff --git a/ssl/quic/quic_rx_depack.c b/ssl/quic/quic_rx_depack.c
index ea54bc3c08..7adec6d7b0 100644
--- a/ssl/quic/quic_rx_depack.c
+++ b/ssl/quic/quic_rx_depack.c
@@ -254,12 +254,80 @@ static int depack_do_frame_stream(PACKET *pkt, QUIC_CHANNEL *ch,
stream = ossl_quic_stream_map_get_by_id(&ch->qsm, frame_data.stream_id);
if (stream == NULL) {
- ossl_quic_channel_raise_protocol_error(ch,
- QUIC_ERR_STREAM_STATE_ERROR,
- frame_type,
- "STREAM frame for nonexistent "
- "stream");
- return 0;
+ uint64_t peer_role, stream_ordinal, *p_next_ordinal;
+ int is_uni;
+
+ /*
+ * If we do not yet have a stream with the given ID, there are three
+ * possibilities:
+ *
+ * (a) The stream ID is for a remotely-created stream and the peer
+ * is creating a stream.
+ *
+ * (b) The stream ID is for a locally-created stream which has
+ * previously been deleted.
+ *
+ * (c) The stream ID is for a locally-created stream which does
+ * not exist yet. This is a protocol violation and we must
+ * terminate the connection in this case.
+ *
+ * We distinguish between (b) and (c) using the stream ID allocator
+ * variable. Since stream ordinals are allocated monotonically, we
+ * simply determine if the stream ordinal is in the future.
+ */
+
+ peer_role = ch->is_server
+ ? QUIC_STREAM_INITIATOR_CLIENT
+ : QUIC_STREAM_INITIATOR_SERVER;
+
+ is_uni = ((frame_data.stream_id & QUIC_STREAM_DIR_MASK)
+ == QUIC_STREAM_DIR_UNI);
+
+ stream_ordinal = frame_data.stream_id >> 2;
+
+ if ((frame_data.stream_id & QUIC_STREAM_INITIATOR_MASK) == peer_role) {
+ /* Peer-created stream which does not yet exist. Create it. */
+ stream = ossl_quic_channel_new_stream_remote(ch, frame_data.stream_id);
+ if (stream == NULL) {
+ ossl_quic_channel_raise_protocol_error(ch,
+ QUIC_ERR_INTERNAL_ERROR,
+ frame_type,
+ "internal error (stream allocation)");
+ return 0;
+ }
+
+ /*
+ * Fallthrough to processing of stream data for newly created
+ * stream.
+ */
+ } else {
+ /* Locally-created stream which does not yet exist. */
+
+ p_next_ordinal = is_uni
+ ? &ch->next_local_stream_ordinal_uni
+ : &ch->next_local_stream_ordinal_bidi;
+
+ if (stream_ordinal >= *p_next_ordinal) {
+ /*
+ * We never created this stream yet, this is a protocol
+ * violation.
+ */
+ ossl_quic_channel_raise_protocol_error(ch,
+ QUIC_ERR_STREAM_STATE_ERROR,
+ frame_type,
+ "STREAM frame for nonexistent "
+ "stream");
+ return 0;
+ }
+
+ /*
+ * Otherwise this is for an old locally-initiated stream which we
+ * have subsequently deleted. Ignore the data; it may simply be a
+ * retransmission. We already take care of notifying the peer of the
+ * termination of the stream during the stream deletion lifecycle.
+ */
+ return 1;
+ }
}
if (stream->rstream == NULL) {
diff --git a/ssl/quic/quic_stream_map.c b/ssl/quic/quic_stream_map.c
index 52e8e14bf8..06bf1cfa34 100644
--- a/ssl/quic/quic_stream_map.c
+++ b/ssl/quic/quic_stream_map.c
@@ -66,17 +66,26 @@ static void list_remove(QUIC_STREAM_LIST_NODE *l,
n->next = n->prev = NULL;
}
-static QUIC_STREAM *active_next(QUIC_STREAM_LIST_NODE *l, QUIC_STREAM *s)
+static QUIC_STREAM *list_next(QUIC_STREAM_LIST_NODE *l, QUIC_STREAM_LIST_NODE *n,
+ size_t off)
{
- QUIC_STREAM_LIST_NODE *n = s->active_node.next;
+ n = n->next;
if (n == l)
n = n->next;
if (n == l)
return NULL;
- return (QUIC_STREAM *)n;
+
+ return (QUIC_STREAM *)(((char *)n) - off);
}
+#define active_next(l, s) list_next((l), &(s)->active_node, \
+ offsetof(QUIC_STREAM, active_node))
+#define accept_next(l, s) list_next((l), &(s)->accept_node, \
+ offsetof(QUIC_STREAM, accept_node))
+#define accept_head(l) list_next((l), (l), \
+ offsetof(QUIC_STREAM, accept_node))
+
static unsigned long hash_stream(const QUIC_STREAM *s)
{
return (unsigned long)s->id;
@@ -97,9 +106,11 @@ int ossl_quic_stream_map_init(QUIC_STREAM_MAP *qsm,
{
qsm->map = lh_QUIC_STREAM_new(hash_stream, cmp_stream);
qsm->active_list.prev = qsm->active_list.next = &qsm->active_list;
+ qsm->accept_list.prev = qsm->accept_list.next = &qsm->accept_list;
qsm->rr_stepping = 1;
qsm->rr_counter = 0;
qsm->rr_cur = NULL;
+ qsm->num_accept = 0;
qsm->get_stream_limit_cb = get_stream_limit_cb;
qsm->get_stream_limit_cb_arg = get_stream_limit_cb_arg;
@@ -271,6 +282,30 @@ void ossl_quic_stream_map_update_state(QUIC_STREAM_MAP *qsm, QUIC_STREAM *s)
stream_map_mark_inactive(qsm, s);
}
+QUIC_STREAM *ossl_quic_stream_map_peek_accept_queue(QUIC_STREAM_MAP *qsm)
+{
+ return accept_head(&qsm->accept_list);
+}
+
+void ossl_quic_stream_map_push_accept_queue(QUIC_STREAM_MAP *qsm,
+ QUIC_STREAM *s)
+{
+ list_insert_tail(&qsm->accept_list, &s->accept_node);
+ ++qsm->num_accept;
+}
+
+void ossl_quic_stream_map_remove_from_accept_queue(QUIC_STREAM_MAP *qsm,
+ QUIC_STREAM *s)
+{
+ list_remove(&qsm->accept_list, &s->accept_node);
+ --qsm->num_accept;
+}
+
+size_t ossl_quic_stream_map_get_accept_queue_len(QUIC_STREAM_MAP *qsm)
+{
+ return qsm->num_accept;
+}
+
/*
* QUIC Stream Iterator
* ====================