From a9979965bf2b74ca79e4bf3fa13539ab90728eeb Mon Sep 17 00:00:00 2001 From: Hugo Landau Date: Tue, 13 Dec 2022 12:28:54 +0000 Subject: [PATCH] QUIC Front End I/O API: Add support for signalling and detecting end-of-stream Reviewed-by: Tomas Mraz Reviewed-by: Matt Caswell (Merged from https://github.com/openssl/openssl/pull/19897) --- include/internal/quic_stream.h | 6 +++++ include/internal/quic_tserver.h | 14 +++++++++- include/openssl/ssl.h.in | 2 ++ ssl/quic/quic_impl.c | 46 ++++++++++++++++++++++++++------- ssl/quic/quic_sstream.c | 11 ++++++++ ssl/quic/quic_tserver.c | 25 +++++++++++++++++- ssl/ssl_lib.c | 14 ++++++++++ util/libssl.num | 1 + 8 files changed, 107 insertions(+), 12 deletions(-) diff --git a/include/internal/quic_stream.h b/include/internal/quic_stream.h index 7b48d0b304c..1fd7d85d3c4 100644 --- a/include/internal/quic_stream.h +++ b/include/internal/quic_stream.h @@ -247,6 +247,12 @@ int ossl_quic_sstream_append(QUIC_SSTREAM *qss, */ void ossl_quic_sstream_fin(QUIC_SSTREAM *qss); +/* + * If the stream has had ossl_quic_sstream_fin() called, returns 1 and writes + * the final size to *final_size. Otherwise, returns 0. + */ +int ossl_quic_sstream_get_final_size(QUIC_SSTREAM *qss, uint64_t *final_size); + /* * Resizes the internal ring buffer. All stream data is preserved safely. * diff --git a/include/internal/quic_tserver.h b/include/internal/quic_tserver.h index 4610627b5e1..438c68d20a9 100644 --- a/include/internal/quic_tserver.h +++ b/include/internal/quic_tserver.h @@ -52,13 +52,20 @@ int ossl_quic_tserver_is_connected(QUIC_TSERVER *srv); * *bytes_read and returns 1 on success. If no bytes are available, 0 is written * to *bytes_read and 1 is returned (this is considered a success case). * - * Returns 0 if connection is not currently active. + * Returns 0 if connection is not currently active. If the receive part of + * the stream has reached the end of stream condition, returns 0; call + * ossl_quic_tserver_has_read_ended() to identify this condition. */ int ossl_quic_tserver_read(QUIC_TSERVER *srv, unsigned char *buf, size_t buf_len, size_t *bytes_read); +/* + * Returns 1 if the read part of the stream has ended normally. + */ +int ossl_quic_tserver_has_read_ended(QUIC_TSERVER *srv); + /* * Attempts to write to stream 0. Writes the number of bytes consumed to * *bytes_written and returns 1 on success. If there is no space currently @@ -75,6 +82,11 @@ int ossl_quic_tserver_write(QUIC_TSERVER *srv, size_t buf_len, size_t *bytes_written); +/* + * Signals normal end of the stream. + */ +int ossl_quic_tserver_conclude(QUIC_TSERVER *srv); + # endif #endif diff --git a/include/openssl/ssl.h.in b/include/openssl/ssl.h.in index 095698ea9db..a8a28e26e78 100644 --- a/include/openssl/ssl.h.in +++ b/include/openssl/ssl.h.in @@ -2270,6 +2270,8 @@ __owur int SSL_shutdown_ex(SSL *ssl, uint64_t flags, const SSL_SHUTDOWN_EX_ARGS *args, size_t args_len); +__owur int SSL_stream_conclude(SSL *ssl, uint64_t flags); + # ifndef OPENSSL_NO_DEPRECATED_1_1_0 # define SSL_cache_hit(s) SSL_session_reused(s) # endif diff --git a/ssl/quic/quic_impl.c b/ssl/quic/quic_impl.c index e869e97ef54..8fe55758131 100644 --- a/ssl/quic/quic_impl.c +++ b/ssl/quic/quic_impl.c @@ -756,6 +756,8 @@ int ossl_quic_accept(SSL *s) * (BIO/)SSL_read => ossl_quic_read * (BIO/)SSL_write => ossl_quic_write * SSL_pending => ossl_quic_pending + * SSL_stream_conclude => ossl_quic_conn_stream_conclude + * */ /* SSL_get_error */ @@ -1053,6 +1055,10 @@ static int quic_read_actual(QUIC_CONNECTION *qc, { int is_fin = 0; + /* If the receive part of the stream is over, issue EOF. */ + if (stream->recv_fin_retired) + return QUIC_RAISE_NORMAL_ERROR(qc, SSL_ERROR_ZERO_RETURN); + if (stream->rstream == NULL) return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL); @@ -1099,9 +1105,11 @@ static int quic_read_again(void *arg) { struct quic_read_again_args *args = arg; - if (!ossl_quic_channel_is_active(args->qc->ch)) + if (!ossl_quic_channel_is_active(args->qc->ch)) { /* If connection is torn down due to an error while blocking, stop. */ + QUIC_RAISE_NON_NORMAL_ERROR(args->qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); return -1; + } if (!quic_read_actual(args->qc, args->stream, args->buf, args->len, args->bytes_read, @@ -1129,15 +1137,15 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek if (qc->ch != NULL && ossl_quic_channel_is_term_any(qc->ch)) return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); - /* If we haven't finished the handshake, try to advance it.*/ + /* If we haven't finished the handshake, try to advance it. */ if (ossl_quic_do_handshake(qc) < 1) - return 0; + return 0; /* ossl_quic_do_handshake raised error here */ if (qc->stream0 == NULL) return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL); if (!quic_read_actual(qc, qc->stream0, buf, len, bytes_read, peek)) - return 0; + return 0; /* quic_read_actual raised error here */ if (*bytes_read > 0) { /* @@ -1160,12 +1168,10 @@ static int quic_read(SSL *s, void *buf, size_t len, size_t *bytes_read, int peek args.peek = peek; res = block_until_pred(qc, quic_read_again, &args, 0); - if (res <= 0) { - if (!ossl_quic_channel_is_active(qc->ch)) - return QUIC_RAISE_NON_NORMAL_ERROR(qc, SSL_R_PROTOCOL_IS_SHUTDOWN, NULL); - else - return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL); - } + if (res == 0) + return QUIC_RAISE_NON_NORMAL_ERROR(qc, ERR_R_INTERNAL_ERROR, NULL); + else if (res < 0) + return 0; /* quic_read_again raised error here */ return 1; } else { @@ -1207,6 +1213,26 @@ size_t ossl_quic_pending(const SSL *s) return avail; } +/* + * SSL_stream_conclude + * ------------------- + */ +int ossl_quic_conn_stream_conclude(QUIC_CONNECTION *qc) +{ + QUIC_STREAM *qs = qc->stream0; + + if (qs == NULL || qs->sstream == NULL) + return 0; + + if (!ossl_quic_channel_is_active(qc->ch) + || ossl_quic_sstream_get_final_size(qs->sstream, NULL)) + return 1; + + ossl_quic_sstream_fin(qs->sstream); + quic_post_write(qc, 1, 1); + return 1; +} + /* * QUIC Front-End I/O API: SSL_CTX Management * ========================================== diff --git a/ssl/quic/quic_sstream.c b/ssl/quic/quic_sstream.c index a5c4b928238..8dc7002c413 100644 --- a/ssl/quic/quic_sstream.c +++ b/ssl/quic/quic_sstream.c @@ -442,6 +442,17 @@ void ossl_quic_sstream_fin(QUIC_SSTREAM *qss) qss->have_final_size = 1; } +int ossl_quic_sstream_get_final_size(QUIC_SSTREAM *qss, uint64_t *final_size) +{ + if (!qss->have_final_size) + return 0; + + if (final_size != NULL) + *final_size = qss->ring_buf.head_offset; + + return 1; +} + int ossl_quic_sstream_append(QUIC_SSTREAM *qss, const unsigned char *buf, size_t buf_len, diff --git a/ssl/quic/quic_tserver.c b/ssl/quic/quic_tserver.c index 6795d4ec465..5a03aa53270 100644 --- a/ssl/quic/quic_tserver.c +++ b/ssl/quic/quic_tserver.c @@ -142,11 +142,14 @@ int ossl_quic_tserver_read(QUIC_TSERVER *srv, size_t buf_len, size_t *bytes_read) { - int is_fin = 0; /* TODO(QUIC): Handle FIN in API */ + int is_fin = 0; if (!ossl_quic_channel_is_active(srv->ch)) return 0; + if (srv->stream0->recv_fin_retired) + return 0; + if (!ossl_quic_rstream_read(srv->stream0->rstream, buf, buf_len, bytes_read, &is_fin)) return 0; @@ -177,6 +180,11 @@ int ossl_quic_tserver_read(QUIC_TSERVER *srv, return 1; } +int ossl_quic_tserver_has_read_ended(QUIC_TSERVER *srv) +{ + return srv->stream0->recv_fin_retired; +} + int ossl_quic_tserver_write(QUIC_TSERVER *srv, const unsigned char *buf, size_t buf_len, @@ -201,3 +209,18 @@ int ossl_quic_tserver_write(QUIC_TSERVER *srv, ossl_quic_tserver_tick(srv); return 1; } + +int ossl_quic_tserver_conclude(QUIC_TSERVER *srv) +{ + if (!ossl_quic_channel_is_active(srv->ch)) + return 0; + + if (!ossl_quic_sstream_get_final_size(srv->stream0->sstream, NULL)) { + ossl_quic_sstream_fin(srv->stream0->sstream); + ossl_quic_stream_map_update_state(ossl_quic_channel_get_qsm(srv->ch), + srv->stream0); + } + + ossl_quic_tserver_tick(srv); + return 1; +} diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c index b927e283fee..25497985988 100644 --- a/ssl/ssl_lib.c +++ b/ssl/ssl_lib.c @@ -7197,3 +7197,17 @@ int SSL_shutdown_ex(SSL *ssl, uint64_t flags, return SSL_shutdown(ssl); #endif } + +int SSL_stream_conclude(SSL *ssl, uint64_t flags) +{ +#ifndef OPENSSL_NO_QUIC + QUIC_CONNECTION *qc = QUIC_CONNECTION_FROM_SSL(ssl); + + if (qc == NULL) + return 0; + + return ossl_quic_conn_stream_conclude(qc); +#else + return 0; +#endif +} diff --git a/util/libssl.num b/util/libssl.num index 16b4609c078..0c5e2f5d59a 100644 --- a/util/libssl.num +++ b/util/libssl.num @@ -542,3 +542,4 @@ SSL_set_initial_peer_addr ? 3_2_0 EXIST::FUNCTION: SSL_net_read_desired ? 3_2_0 EXIST::FUNCTION: SSL_net_write_desired ? 3_2_0 EXIST::FUNCTION: SSL_shutdown_ex ? 3_2_0 EXIST::FUNCTION: +SSL_stream_conclude ? 3_2_0 EXIST::FUNCTION: -- 2.47.2