diff options
Diffstat (limited to 'ssl/quic/quic_impl.c')
-rw-r--r-- | ssl/quic/quic_impl.c | 149 |
1 files changed, 124 insertions, 25 deletions
diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c index 41de167dd2..01b99c5461 100644 --- a/ssl/quic/quic_impl.c +++ b/ssl/quic/quic_impl.c @@ -19,6 +19,8 @@ static void aon_write_finish(QUIC_XSO *xso); static int create_channel(QUIC_CONNECTION *qc); +static QUIC_XSO *create_xso_from_stream(QUIC_CONNECTION *qc, QUIC_STREAM *qs); +static int qc_try_create_default_xso(QUIC_CONNECTION *qc, int remote_init); /* * QUIC Front-End I/O API: Common Utilities @@ -160,12 +162,21 @@ static int expect_quic(const SSL *s, QCTX *ctx) * Like expect_quic(), but requires a QUIC_XSO be contextually available. In * other words, requires that the passed QSO be a QSSO or a QCSO with a default * stream. + * + * remote_init determines if we expect the default XSO to be remotely created or + * not. If it is -1, do not instantiate a default XSO if one does not yet exist. */ -static int ossl_unused expect_quic_with_stream(const SSL *s, QCTX *ctx) +static int ossl_unused expect_quic_with_stream(const SSL *s, int remote_init, + QCTX *ctx) { if (!expect_quic(s, ctx)) return 0; + if (ctx->xso == NULL && remote_init >= 0) { + qc_try_create_default_xso(ctx->qc, remote_init); + ctx->xso = ctx->qc->default_xso; + } + if (ctx->xso == NULL) return QUIC_RAISE_NON_NORMAL_ERROR(ctx->qc, ERR_R_INTERNAL_ERROR, NULL); @@ -250,16 +261,23 @@ SSL *ossl_quic_new(SSL_CTX *ctx) qc->as_server = 0; /* TODO(QUIC): server support */ qc->as_server_state = qc->as_server; - qc->default_ssl_mode = qc->ssl.ctx->mode; - qc->default_blocking = 1; - qc->last_error = SSL_ERROR_NONE; + qc->default_ssl_mode = qc->ssl.ctx->mode; + qc->default_blocking = 1; + qc->last_error = SSL_ERROR_NONE; if (!create_channel(qc)) goto err; - if ((qc->default_xso = (QUIC_XSO *)ossl_quic_conn_stream_new(&qc->ssl, 0)) == NULL) - goto err; - + /* + * We do not create the default XSO yet. The reason for this is that the + * stream ID of the default XSO will depend on whether the stream is client + * or server-initiated, which depends on who transmits first. Since we do + * not know whether the application will be using a client-transmits-first + * or server-transmits-first protocol, we defer default XSO creation until + * the client calls SSL_read() or SSL_write(). If it calls SSL_read() first, + * we take that as a cue that the client is expecting a server-initiated + * stream, and vice versa if SSL_write() is called first. + */ return ssl_base; err: @@ -317,8 +335,13 @@ void ossl_quic_free(SSL *s) * Free the default XSO, if any. The QUIC_STREAM is not deleted at this * stage, but is freed during the channel free when the whole QSM is freed. */ - if (ctx.qc->default_xso != NULL) - SSL_free(&ctx.qc->default_xso->ssl); + if (ctx.qc->default_xso != NULL) { + QUIC_XSO *xso = ctx.qc->default_xso; + + quic_unlock(ctx.qc); + SSL_free(&xso->ssl); + quic_lock(ctx.qc); + } /* Ensure we have no remaining XSOs. */ assert(ctx.qc->num_xso == 0); @@ -1089,10 +1112,92 @@ int ossl_quic_accept(SSL *s) * SSL_stream_new => ossl_quic_conn_stream_new * */ + +/* + * Try to create the default XSO if it doesn't already exist. Returns 1 if the + * default XSO was created. Returns 0 if it was not (e.g. because it already + * exists). Note that this is NOT an error condition. + */ +QUIC_NEEDS_LOCK +static int qc_try_create_default_xso(QUIC_CONNECTION *qc, int remote_init) +{ + QUIC_STREAM *qs; + + if (qc->default_xso != NULL) + qc->default_xso_created = 1; + + if (qc->default_xso_created) + /* + * We only do this once. If the user detaches a previously created + * default XSO we don't auto-create another one. + */ + return 0; + + if (!remote_init) { + /* + * We are writing to the stream first, so this is a locally-initiated + * stream. + */ + qc->default_xso = (QUIC_XSO *)ossl_quic_conn_stream_new(&qc->ssl, 0); + if (qc->default_xso == NULL) + return 0; + } else { + /* + * Client is reading first. This means it is expecting to get data on a + * stream the peer writes to first, meaning this is a remotely-initiated + * stream. Ordinarily, we wait for the RXDP to handle a STREAM frame to + * create such a stream. But in this case, special case it and create + * the bookkeeping structures for the first peer-initiated bidirectional + * stream so the client can start to wait on it. + */ + qs = ossl_quic_channel_new_stream_remote(qc->ch, + QUIC_STREAM_INITIATOR_SERVER + | QUIC_STREAM_DIR_BIDI); + if (qs == NULL) + return 0; + + qc->default_xso = create_xso_from_stream(qc, qs); + if (qc->default_xso == NULL) { + ossl_quic_stream_map_release(ossl_quic_channel_get_qsm(qc->ch), qs); + return 0; + } + } + + qc->default_xso_created = 1; + return 1; +} + +QUIC_NEEDS_LOCK +static QUIC_XSO *create_xso_from_stream(QUIC_CONNECTION *qc, QUIC_STREAM *qs) +{ + QUIC_XSO *xso = NULL; + + if ((xso = OPENSSL_zalloc(sizeof(*xso))) == NULL) + goto err; + + if (!ossl_ssl_init(&xso->ssl, qc->ssl.ctx, qc->ssl.method, SSL_TYPE_QUIC_XSO)) + goto err; + + xso->conn = qc; + xso->blocking = qc->default_blocking; + xso->ssl_mode = qc->default_ssl_mode; + + xso->stream = qs; + + ++qc->num_xso; + return xso; + +err: + OPENSSL_free(xso); + return NULL; +} + +QUIC_TAKES_LOCK SSL *ossl_quic_conn_stream_new(SSL *s, uint64_t flags) { QCTX ctx; QUIC_XSO *xso = NULL; + QUIC_STREAM *qs = NULL; int is_uni = ((flags & SSL_STREAM_FLAG_UNI) != 0); if (!expect_quic_conn_only(s, &ctx)) @@ -1105,27 +1210,20 @@ SSL *ossl_quic_conn_stream_new(SSL *s, uint64_t flags) goto err; } - if ((xso = OPENSSL_zalloc(sizeof(*xso))) == NULL) + qs = ossl_quic_channel_new_stream_local(ctx.qc->ch, is_uni); + if (qs == NULL) goto err; - if (!ossl_ssl_init(&xso->ssl, s->ctx, s->method, SSL_TYPE_QUIC_XSO)) + xso = create_xso_from_stream(ctx.qc, qs); + if (xso == NULL) goto err; - xso->conn = ctx.qc; - xso->blocking = ctx.qc->default_blocking; - xso->ssl_mode = ctx.qc->default_ssl_mode; - - xso->stream = ossl_quic_channel_new_stream_local(ctx.qc->ch, is_uni); - if (xso->stream == NULL) - goto err; - - ++ctx.qc->num_xso; - quic_unlock(ctx.qc); return &xso->ssl; err: OPENSSL_free(xso); + ossl_quic_stream_map_release(ossl_quic_channel_get_qsm(ctx.qc->ch), qs); quic_unlock(ctx.qc); return NULL; } @@ -1410,8 +1508,9 @@ int ossl_quic_write(SSL *s, const void *buf, size_t len, size_t *written) *written = 0; - if (!expect_quic_with_stream(s, &ctx)) + if (!expect_quic_with_stream(s, /*remote_init=*/0, &ctx)) { return 0; + } quic_lock(ctx.qc); @@ -1548,7 +1647,7 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek *bytes_read = 0; - if (!expect_quic_with_stream(s, &ctx)) + if (!expect_quic_with_stream(s, /*remote_init=*/0, &ctx)) return 0; quic_lock(ctx.qc); @@ -1635,7 +1734,7 @@ static size_t ossl_quic_pending_int(const SSL *s) size_t avail = 0; int fin = 0; - if (!expect_quic_with_stream(s, &ctx)) + if (!expect_quic_with_stream(s, /*remote_init=*/-1, &ctx)) return 0; quic_lock(ctx.qc); @@ -1672,7 +1771,7 @@ int ossl_quic_conn_stream_conclude(SSL *s) QCTX ctx; QUIC_STREAM *qs; - if (!expect_quic_with_stream(s, &ctx)) + if (!expect_quic_with_stream(s, /*remote_init=*/0, &ctx)) return 0; quic_lock(ctx.qc); |