diff --git a/crypto/bio/bio.c b/crypto/bio/bio.c index 0e3a0d737d0..17f2afdea33 100644 --- a/crypto/bio/bio.c +++ b/crypto/bio/bio.c @@ -186,6 +186,26 @@ int BIO_read(BIO *bio, void *buf, int len) { return ret; } +int BIO_read_ex(BIO *bio, void *data, size_t data_len, size_t *read_bytes) { + if (bio == NULL || read_bytes == NULL) { + OPENSSL_PUT_ERROR(BIO, BIO_R_NULL_PARAMETER); + return 0; + } + + int read_len = (int)data_len; + if (data_len > INT_MAX) { + read_len = INT_MAX; + } + + int ret = BIO_read(bio, data, read_len); + *read_bytes = ret; + if (ret > 0) { + return 1; + } else { + return 0; + } +} + int BIO_gets(BIO *bio, char *buf, int len) { if (bio == NULL || bio->method == NULL || bio->method->bgets == NULL) { OPENSSL_PUT_ERROR(BIO, BIO_R_UNSUPPORTED_METHOD); @@ -236,6 +256,28 @@ int BIO_write(BIO *bio, const void *in, int inl) { return ret; } +int BIO_write_ex(BIO *bio, const void *data, size_t data_len, size_t *written_bytes) { + if (bio == NULL) { + OPENSSL_PUT_ERROR(BIO, BIO_R_NULL_PARAMETER); + return 0; + } + + int write_len = (int)data_len; + if (data_len > INT_MAX) { + write_len = INT_MAX; + } + + int ret = BIO_write(bio, data, write_len); + if (written_bytes != NULL) { + *written_bytes = ret; + } + if (ret > 0) { + return 1; + } else { + return 0; + } +} + int BIO_write_all(BIO *bio, const void *data, size_t len) { const uint8_t *data_u8 = data; while (len > 0) { diff --git a/crypto/bio/bio_test.cc b/crypto/bio/bio_test.cc index e76f3eb32fa..479d66c53e5 100644 --- a/crypto/bio/bio_test.cc +++ b/crypto/bio/bio_test.cc @@ -89,7 +89,7 @@ class OwnedSocket { }; struct SockaddrStorage { - SockaddrStorage() : storage() , len(sizeof(storage)) {} + SockaddrStorage() : storage(), len(sizeof(storage)) {} int family() const { return storage.ss_family; } @@ -1063,3 +1063,36 @@ TEST(BIOTest, InvokeConnectCallback) { } // namespace INSTANTIATE_TEST_SUITE_P(All, BIOPairTest, testing::Values(false, true)); + +TEST(BIOTest, ReadWriteEx) { + bssl::UniquePtr bio(BIO_new(BIO_s_mem())); + ASSERT_TRUE(bio); + + size_t written = 0; + ASSERT_TRUE(BIO_write_ex(bio.get(), "abcdef", 6, &written)); + EXPECT_EQ(written, (size_t)6); + + char buf[32]; + size_t read = 0; + ASSERT_TRUE(BIO_read_ex(bio.get(), buf, sizeof(buf), &read)); + EXPECT_GT(read, (size_t)0); + EXPECT_EQ(Bytes(buf, read), Bytes("abcdef")); + + // Test NULL |written_bytes| behavior works. + read = 0; + ASSERT_TRUE(BIO_write_ex(bio.get(), "ghilmnop", 8, nullptr)); + ASSERT_TRUE(BIO_read_ex(bio.get(), buf, sizeof(buf), &read)); + EXPECT_GT(read, (size_t)0); + EXPECT_EQ(Bytes(buf, read), Bytes("ghilmnop")); + + // Test NULL |read_bytes| behavior fails. + ASSERT_TRUE(BIO_write_ex(bio.get(), "ghilmnop", 8, nullptr)); + ASSERT_FALSE(BIO_read_ex(bio.get(), buf, sizeof(buf), nullptr)); + + // Test that |BIO_write/read_ex| align with their non-ex counterparts, when + // encountering NULL data. + EXPECT_FALSE(BIO_write(bio.get(), nullptr, 0)); + EXPECT_FALSE(BIO_write_ex(bio.get(), nullptr, 0, &written)); + EXPECT_FALSE(BIO_read(bio.get(), nullptr, 0)); + EXPECT_FALSE(BIO_read_ex(bio.get(), nullptr, 0, &read)); +} diff --git a/include/openssl/bio.h b/include/openssl/bio.h index 262707f5414..3f6a5a2a8b1 100644 --- a/include/openssl/bio.h +++ b/include/openssl/bio.h @@ -110,6 +110,11 @@ OPENSSL_EXPORT int BIO_up_ref(BIO *bio); // of bytes read, zero on EOF, or a negative number on error. OPENSSL_EXPORT int BIO_read(BIO *bio, void *data, int len); +// |BIO_read_ex| calls |BIO_read| and stores the number of bytes read in +// |read_bytes|. It returns one on success and zero otherwise. +OPENSSL_EXPORT int BIO_read_ex(BIO *bio, void *data, size_t data_len, + size_t *read_bytes); + // BIO_gets reads a line from |bio| and writes at most |size| bytes into |buf|. // It returns the number of bytes read or a negative number on error. This // function's output always includes a trailing NUL byte, so it will read at @@ -127,6 +132,12 @@ OPENSSL_EXPORT int BIO_gets(BIO *bio, char *buf, int size); // number of bytes written, or a negative number on error. OPENSSL_EXPORT int BIO_write(BIO *bio, const void *data, int len); +// |BIO_write_ex| calls |BIO_write| and stores the number of bytes written in +// |written_bytes|, unless |written_bytes| is NULL. It returns one on success +// and zero otherwise. +OPENSSL_EXPORT int BIO_write_ex(BIO *bio, const void *data, size_t data_len, + size_t *written_bytes); + // BIO_write_all writes |len| bytes from |data| to |bio|, looping as necessary. // It returns one if all bytes were successfully written and zero on error. OPENSSL_EXPORT int BIO_write_all(BIO *bio, const void *data, size_t len); @@ -880,7 +891,7 @@ OPENSSL_EXPORT int (*BIO_meth_get_puts(const BIO_METHOD *method)) (BIO *, const // does not support secure heaps. OPENSSL_EXPORT OPENSSL_DEPRECATED const BIO_METHOD *BIO_s_secmem(void); - + // General No-op Functions [Deprecated]. // BIO_set_write_buffer_size returns zero.