summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatt Caswell <matt@openssl.org>2023-08-21 15:11:17 +0100
committerMatt Caswell <matt@openssl.org>2023-08-25 11:42:51 +0100
commit02e36ed3525a2f0fda1b21e948ec5f522cf9379c (patch)
treebba75fcbd024109898a72749bec2be5381d51d54
parenta855ee857645614483976fb1be1b3907a2fe94d8 (diff)
Update demos/tutorial to distinguish between stream and connection errors
We can use SSL_get_stream_read_state() to distinguish these cases. Reviewed-by: Tomas Mraz <tomas@openssl.org> Reviewed-by: Hugo Landau <hlandau@openssl.org> (Merged from https://github.com/openssl/openssl/pull/21765)
-rw-r--r--demos/guide/quic-client-block.c32
-rw-r--r--demos/guide/quic-multi-stream.c61
-rw-r--r--doc/man7/ossl-guide-quic-client-block.pod69
-rw-r--r--doc/man7/ossl-guide-quic-multi-stream.pod47
4 files changed, 181 insertions, 28 deletions
diff --git a/demos/guide/quic-client-block.c b/demos/guide/quic-client-block.c
index be797707f1..54e52d5c28 100644
--- a/demos/guide/quic-client-block.c
+++ b/demos/guide/quic-client-block.c
@@ -251,13 +251,37 @@ int main(void)
* QUIC terms this means that the peer has sent FIN on the stream to
* indicate that no further data will be sent.
*/
- if (SSL_get_error(ssl, 0) != SSL_ERROR_ZERO_RETURN) {
+ switch (SSL_get_error(ssl, 0)) {
+ case SSL_ERROR_ZERO_RETURN:
+ /* Normal completion of the stream */
+ break;
+
+ case SSL_ERROR_SSL:
/*
- * Some error occurred other than a graceful close down by the
- * peer.
+ * Some stream fatal error occurred. This could be because of a stream
+ * reset - or some failure occurred on the underlying connection.
*/
+ switch (SSL_get_stream_read_state(ssl)) {
+ case SSL_STREAM_STATE_RESET_REMOTE:
+ printf("Stream reset occurred\n");
+ /* The stream has been reset but the connection is still healthy. */
+ break;
+
+ case SSL_STREAM_STATE_CONN_CLOSED:
+ printf("Connection closed\n");
+ /* Connection is already closed. Skip SSL_shutdown() */
+ goto end;
+
+ default:
+ printf("Unknown stream failure\n");
+ break;
+ }
+ break;
+
+ default:
+ /* Some other unexpected error occurred */
printf ("Failed reading remaining data\n");
- goto end;
+ break;
}
/*
diff --git a/demos/guide/quic-multi-stream.c b/demos/guide/quic-multi-stream.c
index 7a40d61ad4..86dc6e3502 100644
--- a/demos/guide/quic-multi-stream.c
+++ b/demos/guide/quic-multi-stream.c
@@ -288,13 +288,37 @@ int main(void)
* QUIC terms this means that the peer has sent FIN on the stream to
* indicate that no further data will be sent.
*/
- if (SSL_get_error(stream1, 0) != SSL_ERROR_ZERO_RETURN) {
+ switch (SSL_get_error(stream1, 0)) {
+ case SSL_ERROR_ZERO_RETURN:
+ /* Normal completion of the stream */
+ break;
+
+ case SSL_ERROR_SSL:
/*
- * Some error occurred other than a graceful close down by the
- * peer.
+ * Some stream fatal error occurred. This could be because of a stream
+ * reset - or some failure occurred on the underlying connection.
*/
- printf ("Failed reading remaining data from stream 1\n");
- goto end;
+ switch (SSL_get_stream_read_state(stream1)) {
+ case SSL_STREAM_STATE_RESET_REMOTE:
+ printf("Stream reset occurred\n");
+ /* The stream has been reset but the connection is still healthy. */
+ break;
+
+ case SSL_STREAM_STATE_CONN_CLOSED:
+ printf("Connection closed\n");
+ /* Connection is already closed. Skip SSL_shutdown() */
+ goto end;
+
+ default:
+ printf("Unknown stream failure\n");
+ break;
+ }
+ break;
+
+ default:
+ /* Some other unexpected error occurred */
+ printf ("Failed reading remaining data\n");
+ break;
}
/*
@@ -325,9 +349,30 @@ int main(void)
printf("\n");
/* Check for errors on the stream */
- if (SSL_get_error(stream3, 0) != SSL_ERROR_ZERO_RETURN) {
- printf ("Failed reading remaining data from stream 3\n");
- goto end;
+ switch (SSL_get_error(stream3, 0)) {
+ case SSL_ERROR_ZERO_RETURN:
+ /* Normal completion of the stream */
+ break;
+
+ case SSL_ERROR_SSL:
+ switch (SSL_get_stream_read_state(stream3)) {
+ case SSL_STREAM_STATE_RESET_REMOTE:
+ printf("Stream reset occurred\n");
+ break;
+
+ case SSL_STREAM_STATE_CONN_CLOSED:
+ printf("Connection closed\n");
+ goto end;
+
+ default:
+ printf("Unknown stream failure\n");
+ break;
+ }
+ break;
+
+ default:
+ printf ("Failed reading remaining data\n");
+ break;
}
/*
diff --git a/doc/man7/ossl-guide-quic-client-block.pod b/doc/man7/ossl-guide-quic-client-block.pod
index 6ae193567f..595135c696 100644
--- a/doc/man7/ossl-guide-quic-client-block.pod
+++ b/doc/man7/ossl-guide-quic-client-block.pod
@@ -241,8 +241,73 @@ client, so we won't repeat it here.
We can also perform data transfer using a default QUIC stream that is
automatically associated with the B<SSL> object for us. We can transmit data
-using L<SSL_write_ex(3)>, and receive data using L<SSL_read_ex(3)> in exactly
-the same way as for TLS. Again, we won't repeat it here.
+using L<SSL_write_ex(3)>, and receive data using L<SSL_read_ex(3)> in the same
+way as for TLS. The main difference is that we have to account for failures
+slightly differently. With QUIC the stream can be reset by the peer (which is
+fatal for that stream), but the underlying connection itself may still be
+healthy.
+
+ /*
+ * Get up to sizeof(buf) bytes of the response. We keep reading until the
+ * server closes the connection.
+ */
+ while (SSL_read_ex(ssl, buf, sizeof(buf), &readbytes)) {
+ /*
+ * OpenSSL does not guarantee that the returned data is a string or
+ * that it is NUL terminated so we use fwrite() to write the exact
+ * number of bytes that we read. The data could be non-printable or
+ * have NUL characters in the middle of it. For this simple example
+ * we're going to print it to stdout anyway.
+ */
+ fwrite(buf, 1, readbytes, stdout);
+ }
+ /* In case the response didn't finish with a newline we add one now */
+ printf("\n");
+
+ /*
+ * Check whether we finished the while loop above normally or as the
+ * result of an error. The 0 argument to SSL_get_error() is the return
+ * code we received from the SSL_read_ex() call. It must be 0 in order
+ * to get here. Normal completion is indicated by SSL_ERROR_ZERO_RETURN. In
+ * QUIC terms this means that the peer has sent FIN on the stream to
+ * indicate that no further data will be sent.
+ */
+ switch (SSL_get_error(ssl, 0)) {
+ case SSL_ERROR_ZERO_RETURN:
+ /* Normal completion of the stream */
+ break;
+
+ case SSL_ERROR_SSL:
+ /*
+ * Some stream fatal error occurred. This could be because of a stream
+ * reset - or some failure occurred on the underlying connection.
+ */
+ switch (SSL_get_stream_read_state(ssl)) {
+ case SSL_STREAM_STATE_RESET_REMOTE:
+ printf("Stream reset occurred\n");
+ /* The stream has been reset but the connection is still healthy. */
+ break;
+
+ case SSL_STREAM_STATE_CONN_CLOSED:
+ printf("Connection closed\n");
+ /* Connection is already closed. Skip SSL_shutdown() */
+ goto end;
+
+ default:
+ printf("Unknown stream failure\n");
+ break;
+ }
+ break;
+
+ default:
+ /* Some other unexpected error occurred */
+ printf ("Failed reading remaining data\n");
+ break;
+ }
+
+In the above code example you can see that B<SSL_ERROR_SSL> indicates a stream
+fatal error. We can use L<SSL_get_stream_read_state(3)> to determine whether the
+stream has been reset, or if some other fatal error has occurred.
=head2 Shutting down the connection
diff --git a/doc/man7/ossl-guide-quic-multi-stream.pod b/doc/man7/ossl-guide-quic-multi-stream.pod
index 9956fff094..4291c30fa7 100644
--- a/doc/man7/ossl-guide-quic-multi-stream.pod
+++ b/doc/man7/ossl-guide-quic-multi-stream.pod
@@ -131,14 +131,6 @@ send more data on a stream after L<SSL_stream_conclude(3)> has been called.
It is also possible to abandon a stream abnormally by calling
L<SSL_stream_reset(3)>.
-=begin comment
-
-TODO(QUIC): What does SSL_get_error() return in the event that the
-peer has reset a stream? If SSL_ERROR_SSL how does an application distinguish
-between a stream reset and a connection level fatal error?
-
-=end comment
-
Once a stream object is no longer needed it should be freed via a call to
L<SSL_free(3)>. An application should not call L<SSL_shutdown(3)> on it since
this is only meaningful for connection level B<SSL> objects. Freeing the stream
@@ -277,7 +269,10 @@ function to find out more details. Since this is a blocking application this
will either return B<SSL_ERROR_SYSCALL> or B<SSL_ERROR_SSL> indicating a
fundamental problem, or it will return B<SSL_ERROR_ZERO_RETURN> indicating that
the stream is concluded and there will be no more data available to read from
-it.
+it. Care must be taken to distinguish between an error at the stream level (i.e.
+a stream reset) and an error at the connection level (i.e. a connection closed).
+The L<SSL_get_stream_read_state(3)> function can be used to distinguish between
+these different cases.
/*
* Check whether we finished the while loop above normally or as the
@@ -287,13 +282,37 @@ it.
* QUIC terms this means that the peer has sent FIN on the stream to
* indicate that no further data will be sent.
*/
- if (SSL_get_error(stream1, 0) != SSL_ERROR_ZERO_RETURN) {
+ switch (SSL_get_error(ssl, 0)) {
+ case SSL_ERROR_ZERO_RETURN:
+ /* Normal completion of the stream */
+ break;
+
+ case SSL_ERROR_SSL:
/*
- * Some error occurred other than a graceful close down by the
- * peer.
+ * Some stream fatal error occurred. This could be because of a stream
+ * reset - or some failure occurred on the underlying connection.
*/
- printf ("Failed reading remaining data from stream 1\n");
- goto end;
+ switch (SSL_get_stream_read_state(ssl)) {
+ case SSL_STREAM_STATE_RESET_REMOTE:
+ printf("Stream reset occurred\n");
+ /* The stream has been reset but the connection is still healthy. */
+ break;
+
+ case SSL_STREAM_STATE_CONN_CLOSED:
+ printf("Connection closed\n");
+ /* Connection is already closed. Skip SSL_shutdown() */
+ goto end;
+
+ default:
+ printf("Unknown stream failure\n");
+ break;
+ }
+ break;
+
+ default:
+ /* Some other unexpected error occurred */
+ printf ("Failed reading remaining data\n");
+ break;
}
=head2 Accepting an incoming stream