diff options
author | David Benjamin <davidben@google.com> | 2021-11-12 14:43:21 -0500 |
---|---|---|
committer | Boringssl LUCI CQ <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2021-11-18 21:08:27 +0000 |
commit | b3ed071ecc4efb77afd0a025ea1078da19578bfd (patch) | |
tree | bb3f097f6dc76593ce2200bbaa86e7ce95ef9ed6 | |
parent | ea57bcbd66ffc13b5ebad2767412e35931aa17ff (diff) | |
download | boringssl-b3ed071ecc4efb77afd0a025ea1078da19578bfd.zip boringssl-b3ed071ecc4efb77afd0a025ea1078da19578bfd.tar.gz boringssl-b3ed071ecc4efb77afd0a025ea1078da19578bfd.tar.bz2 |
Add SSL_has_pending.
This was added in OpenSSL 1.1.x. It is slightly different from
SSL_pending in that it also reports buffered transport data.
Change-Id: I81e217aad1ceb6f4c31c36634a546e12b6dc8dfc
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50445
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
-rw-r--r-- | include/openssl/ssl.h | 25 | ||||
-rw-r--r-- | ssl/ssl_lib.cc | 4 | ||||
-rw-r--r-- | ssl/ssl_test.cc | 20 |
3 files changed, 47 insertions, 2 deletions
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 057fbc0..ca7ae14 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h @@ -362,10 +362,31 @@ OPENSSL_EXPORT int SSL_read(SSL *ssl, void *buf, int num); // SSL_peek behaves like |SSL_read| but does not consume any bytes returned. OPENSSL_EXPORT int SSL_peek(SSL *ssl, void *buf, int num); -// SSL_pending returns the number of bytes available in |ssl|. It does not read -// from the transport. +// SSL_pending returns the number of buffered, decrypted bytes available for +// read in |ssl|. It does not read from the transport. +// +// In DTLS, it is possible for this function to return zero while there is +// buffered, undecrypted data from the transport in |ssl|. For example, +// |SSL_read| may read a datagram with two records, decrypt the first, and leave +// the second buffered for a subsequent call to |SSL_read|. Callers that wish to +// detect this case can use |SSL_has_pending|. OPENSSL_EXPORT int SSL_pending(const SSL *ssl); +// SSL_has_pending returns one if |ssl| has buffered, decrypted bytes available +// for read, or if |ssl| has buffered data from the transport that has not yet +// been decrypted. If |ssl| has neither, this function returns zero. +// +// In TLS, BoringSSL does not implement read-ahead, so this function returns one +// if and only if |SSL_pending| would return a non-zero value. In DTLS, it is +// possible for this function to return one while |SSL_pending| returns zero. +// For example, |SSL_read| may read a datagram with two records, decrypt the +// first, and leave the second buffered for a subsequent call to |SSL_read|. +// +// As a result, if this function returns one, the next call to |SSL_read| may +// still fail, read from the transport, or both. The buffered, undecrypted data +// may be invalid or incomplete. +OPENSSL_EXPORT int SSL_has_pending(const SSL *ssl); + // SSL_write writes up to |num| bytes from |buf| into |ssl|. It implicitly runs // any pending handshakes, including renegotiations when enabled. On success, it // returns the number of bytes written. Otherwise, it returns <= 0. The caller diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc index 03864e1..e2e2436 100644 --- a/ssl/ssl_lib.cc +++ b/ssl/ssl_lib.cc @@ -1697,6 +1697,10 @@ int SSL_pending(const SSL *ssl) { return static_cast<int>(ssl->s3->pending_app_data.size()); } +int SSL_has_pending(const SSL *ssl) { + return SSL_pending(ssl) != 0 || !ssl->s3->read_buffer.empty(); +} + int SSL_CTX_check_private_key(const SSL_CTX *ctx) { return ssl_cert_check_private_key(ctx->cert.get(), ctx->cert->privatekey.get()); diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc index a2ca07a..4169671 100644 --- a/ssl/ssl_test.cc +++ b/ssl/ssl_test.cc @@ -4966,23 +4966,43 @@ TEST_P(SSLVersionTest, SSLPending) { ASSERT_TRUE(Connect()); EXPECT_EQ(0, SSL_pending(client_.get())); + EXPECT_EQ(0, SSL_has_pending(client_.get())); ASSERT_EQ(5, SSL_write(server_.get(), "hello", 5)); ASSERT_EQ(5, SSL_write(server_.get(), "world", 5)); EXPECT_EQ(0, SSL_pending(client_.get())); + EXPECT_EQ(0, SSL_has_pending(client_.get())); char buf[10]; ASSERT_EQ(1, SSL_peek(client_.get(), buf, 1)); EXPECT_EQ(5, SSL_pending(client_.get())); + EXPECT_EQ(1, SSL_has_pending(client_.get())); ASSERT_EQ(1, SSL_read(client_.get(), buf, 1)); EXPECT_EQ(4, SSL_pending(client_.get())); + EXPECT_EQ(1, SSL_has_pending(client_.get())); ASSERT_EQ(4, SSL_read(client_.get(), buf, 10)); EXPECT_EQ(0, SSL_pending(client_.get())); + if (is_dtls()) { + // In DTLS, the two records would have been read as a single datagram and + // buffered inside |client_|. Thus, |SSL_has_pending| should return true. + // + // This test is slightly unrealistic. It relies on |ConnectClientAndServer| + // using a |BIO| pair, which does not preserve datagram boundaries. Reading + // 1 byte, then 4 bytes, from the first record also relies on + // https://crbug.com/boringssl/65. But it does test the codepaths. When + // fixing either of these bugs, this test may need to be redone. + EXPECT_EQ(1, SSL_has_pending(client_.get())); + } else { + // In TLS, we do not overread, so |SSL_has_pending| should report no data is + // buffered. + EXPECT_EQ(0, SSL_has_pending(client_.get())); + } ASSERT_EQ(2, SSL_read(client_.get(), buf, 2)); EXPECT_EQ(3, SSL_pending(client_.get())); + EXPECT_EQ(1, SSL_has_pending(client_.get())); } // Test that post-handshake tickets consumed by |SSL_shutdown| are ignored. |