Convert bio_test to C++.

Change-Id: I6416eb6b2474e4fbb974a7da8a9bc6d2b4c44adb
Reviewed-on: https://boringssl-review.googlesource.com/4128
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/bio/CMakeLists.txt b/crypto/bio/CMakeLists.txt
index 12bb749..f4122c4 100644
--- a/crypto/bio/CMakeLists.txt
+++ b/crypto/bio/CMakeLists.txt
@@ -21,7 +21,7 @@
 add_executable(
   bio_test
 
-  bio_test.c
+  bio_test.cc
 )
 
 target_link_libraries(bio_test crypto)
diff --git a/crypto/bio/bio_test.c b/crypto/bio/bio_test.c
deleted file mode 100644
index ee11acc..0000000
--- a/crypto/bio/bio_test.c
+++ /dev/null
@@ -1,362 +0,0 @@
-/* Copyright (c) 2014, Google Inc.
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
- * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
-
-#if !defined(_POSIX_C_SOURCE)
-#define _POSIX_C_SOURCE 201410L
-#endif
-
-#include <openssl/base.h>
-
-#if !defined(OPENSSL_WINDOWS)
-#include <arpa/inet.h>
-#include <fcntl.h>
-#include <netinet/in.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <unistd.h>
-#else
-#include <io.h>
-#pragma warning(push, 3)
-#include <winsock2.h>
-#include <ws2tcpip.h>
-#pragma warning(pop)
-#endif
-
-#include <openssl/bio.h>
-#include <openssl/crypto.h>
-#include <openssl/err.h>
-
-#define MIN(a, b) ((a < b) ? a : b)
-
-#if !defined(OPENSSL_WINDOWS)
-static int closesocket(int sock) {
-  return close(sock);
-}
-
-static void print_socket_error(const char *func) {
-  perror(func);
-}
-#else
-static void print_socket_error(const char *func) {
-  fprintf(stderr, "%s: %d\n", func, WSAGetLastError());
-}
-#endif
-
-static int test_socket_connect(void) {
-  int listening_sock = socket(AF_INET, SOCK_STREAM, 0);
-  int sock;
-  struct sockaddr_in sin;
-  socklen_t sockaddr_len = sizeof(sin);
-  static const char kTestMessage[] = "test";
-  char hostname[80], buf[5];
-  BIO *bio;
-
-  memset(&sin, 0, sizeof(sin));
-  sin.sin_family = AF_INET;
-  if (!inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr)) {
-    print_socket_error("inet_pton");
-    return 0;
-  }
-
-  if (bind(listening_sock, (struct sockaddr *)&sin, sizeof(sin)) != 0) {
-    print_socket_error("bind");
-    return 0;
-  }
-
-  if (listen(listening_sock, 1)) {
-    print_socket_error("listen");
-    return 0;
-  }
-
-  if (getsockname(listening_sock, (struct sockaddr *)&sin, &sockaddr_len) ||
-      sockaddr_len != sizeof(sin)) {
-    print_socket_error("getsockname");
-    return 0;
-  }
-
-  BIO_snprintf(hostname, sizeof(hostname), "%s:%d", "127.0.0.1",
-               ntohs(sin.sin_port));
-  bio = BIO_new_connect(hostname);
-  if (!bio) {
-    fprintf(stderr, "BIO_new_connect failed.\n");
-    return 0;
-  }
-
-  if (BIO_write(bio, kTestMessage, sizeof(kTestMessage)) !=
-      sizeof(kTestMessage)) {
-    fprintf(stderr, "BIO_write failed.\n");
-    BIO_print_errors_fp(stderr);
-    return 0;
-  }
-
-  sock = accept(listening_sock, (struct sockaddr *) &sin, &sockaddr_len);
-  if (sock < 0) {
-    print_socket_error("accept");
-    return 0;
-  }
-
-  if (recv(sock, buf, sizeof(buf), 0) != sizeof(kTestMessage)) {
-    print_socket_error("read");
-    return 0;
-  }
-
-  if (memcmp(buf, kTestMessage, sizeof(kTestMessage))) {
-    return 0;
-  }
-
-  closesocket(sock);
-  closesocket(listening_sock);
-  BIO_free(bio);
-
-  return 1;
-}
-
-
-/* bio_read_zero_copy_wrapper is a wrapper around the zero-copy APIs to make
- * testing easier. */
-static size_t bio_read_zero_copy_wrapper(BIO *bio, uint8_t *data, size_t len) {
-  uint8_t *read_buf;
-  size_t read_buf_offset;
-  size_t available_bytes;
-  size_t len_read = 0;
-
-  do {
-    if (!BIO_zero_copy_get_read_buf(bio, &read_buf, &read_buf_offset,
-                                    &available_bytes)) {
-      return 0;
-    }
-
-    available_bytes = MIN(available_bytes, len - len_read);
-    memmove(data + len_read, read_buf + read_buf_offset, available_bytes);
-
-    BIO_zero_copy_get_read_buf_done(bio, available_bytes);
-
-    len_read += available_bytes;
-  } while (len - len_read > 0 && available_bytes > 0);
-
-  return len_read;
-}
-
-/* bio_write_zero_copy_wrapper is a wrapper around the zero-copy APIs to make
- * testing easier. */
-static size_t bio_write_zero_copy_wrapper(BIO *bio, const uint8_t *data,
-                                          size_t len) {
-  uint8_t *write_buf;
-  size_t write_buf_offset;
-  size_t available_bytes;
-  size_t len_written = 0;
-
-  do {
-    if (!BIO_zero_copy_get_write_buf(bio, &write_buf, &write_buf_offset,
-                                     &available_bytes)) {
-      return 0;
-    }
-
-    available_bytes = MIN(available_bytes, len - len_written);
-    memmove(write_buf + write_buf_offset, data + len_written, available_bytes);
-
-    BIO_zero_copy_get_write_buf_done(bio, available_bytes);
-
-    len_written += available_bytes;
-  } while (len - len_written > 0 && available_bytes > 0);
-
-  return len_written;
-}
-
-static int test_zero_copy_bio_pairs(void) {
-  /* Test read and write, especially triggering the ring buffer wrap-around.*/
-  BIO* bio1;
-  BIO* bio2;
-  size_t i, j;
-  uint8_t bio1_application_send_buffer[1024];
-  uint8_t bio2_application_recv_buffer[1024];
-  size_t total_read = 0;
-  size_t total_write = 0;
-  uint8_t* write_buf;
-  size_t write_buf_offset;
-  size_t available_bytes;
-  size_t bytes_left;
-
-  const size_t kLengths[] = {254, 255, 256, 257, 510, 511, 512, 513};
-
-  /* These trigger ring buffer wrap around. */
-  const size_t kPartialLengths[] = {0, 1, 2, 3, 128, 255, 256, 257, 511, 512};
-
-  static const size_t kBufferSize = 512;
-
-  srand(1);
-  for (i = 0; i < sizeof(bio1_application_send_buffer); i++) {
-    bio1_application_send_buffer[i] = rand() & 255;
-  }
-
-  /* Transfer bytes from bio1_application_send_buffer to
-   * bio2_application_recv_buffer in various ways. */
-  for (i = 0; i < sizeof(kLengths) / sizeof(kLengths[0]); i++) {
-    for (j = 0; j < sizeof(kPartialLengths) / sizeof(kPartialLengths[0]); j++) {
-      total_write = 0;
-      total_read = 0;
-
-      BIO_new_bio_pair(&bio1, kBufferSize, &bio2, kBufferSize);
-
-      total_write += bio_write_zero_copy_wrapper(
-          bio1, bio1_application_send_buffer, kLengths[i]);
-
-      /* This tests interleaved read/write calls. Do a read between zero copy
-       * write calls. */
-      if (!BIO_zero_copy_get_write_buf(bio1, &write_buf, &write_buf_offset,
-                                       &available_bytes)) {
-        return 0;
-      }
-
-      /* Free kPartialLengths[j] bytes in the beginning of bio1 write buffer.
-       * This enables ring buffer wrap around for the next write. */
-      total_read += BIO_read(bio2, bio2_application_recv_buffer + total_read,
-                             kPartialLengths[j]);
-
-      size_t interleaved_write_len = MIN(kPartialLengths[j], available_bytes);
-
-      /* Write the data for the interleaved write call. If the buffer becomes
-       * empty after a read, the write offset is normally set to 0. Check that
-       * this does not happen for interleaved read/write and that
-       * |write_buf_offset| is still valid. */
-      memcpy(write_buf + write_buf_offset,
-             bio1_application_send_buffer + total_write, interleaved_write_len);
-      if (BIO_zero_copy_get_write_buf_done(bio1, interleaved_write_len)) {
-        total_write += interleaved_write_len;
-      }
-
-      /* Do another write in case |write_buf_offset| was wrapped */
-      total_write += bio_write_zero_copy_wrapper(
-          bio1, bio1_application_send_buffer + total_write,
-          kPartialLengths[j] - interleaved_write_len);
-
-      /* Drain the rest. */
-      bytes_left = BIO_pending(bio2);
-      total_read += bio_read_zero_copy_wrapper(
-          bio2, bio2_application_recv_buffer + total_read, bytes_left);
-
-      BIO_free(bio1);
-      BIO_free(bio2);
-
-      if (total_read != total_write) {
-        fprintf(stderr, "Lengths not equal in round (%u, %u)\n", (unsigned)i,
-                (unsigned)j);
-        return 0;
-      }
-      if (total_read > kLengths[i] + kPartialLengths[j]) {
-        fprintf(stderr, "Bad lengths in round (%u, %u)\n", (unsigned)i,
-                (unsigned)j);
-        return 0;
-      }
-      if (memcmp(bio1_application_send_buffer, bio2_application_recv_buffer,
-                 total_read) != 0) {
-        fprintf(stderr, "Buffers not equal in round (%u, %u)\n", (unsigned)i,
-                (unsigned)j);
-        return 0;
-      }
-    }
-  }
-
-  return 1;
-}
-
-static int test_printf(void) {
-  /* Test a short output, a very long one, and various sizes around
-   * 256 (the size of the buffer) to ensure edge cases are correct. */
-  static const size_t kLengths[] = { 5, 250, 251, 252, 253, 254, 1023 };
-  BIO *bio;
-  char string[1024];
-  int ret;
-  const uint8_t *contents;
-  size_t i, len;
-
-  bio = BIO_new(BIO_s_mem());
-  if (!bio) {
-    fprintf(stderr, "BIO_new failed\n");
-    return 0;
-  }
-
-  for (i = 0; i < sizeof(kLengths) / sizeof(kLengths[0]); i++) {
-    if (kLengths[i] >= sizeof(string)) {
-      fprintf(stderr, "Bad test string length\n");
-      return 0;
-    }
-    memset(string, 'a', sizeof(string));
-    string[kLengths[i]] = '\0';
-
-    ret = BIO_printf(bio, "test %s", string);
-    if (ret != 5 + kLengths[i]) {
-      fprintf(stderr, "BIO_printf failed: %d\n", ret);
-      return 0;
-    }
-    if (!BIO_mem_contents(bio, &contents, &len)) {
-      fprintf(stderr, "BIO_mem_contents failed\n");
-      return 0;
-    }
-    if (len != 5 + kLengths[i] ||
-        strncmp((const char *)contents, "test ", 5) != 0 ||
-        strncmp((const char *)contents + 5, string, kLengths[i]) != 0) {
-      fprintf(stderr, "Contents did not match: %.*s\n", (int)len, contents);
-      return 0;
-    }
-
-    if (!BIO_reset(bio)) {
-      fprintf(stderr, "BIO_reset failed\n");
-      return 0;
-    }
-  }
-
-  BIO_free(bio);
-  return 1;
-}
-
-int main(void) {
-#if defined(OPENSSL_WINDOWS)
-  WSADATA wsa_data;
-  WORD wsa_version;
-  int wsa_err;
-#endif
-
-  CRYPTO_library_init();
-  ERR_load_crypto_strings();
-
-#if defined(OPENSSL_WINDOWS)
-  /* Initialize Winsock. */
-  wsa_version = MAKEWORD(2, 2);
-  wsa_err = WSAStartup(wsa_version, &wsa_data);
-  if (wsa_err != 0) {
-    fprintf(stderr, "WSAStartup failed: %d\n", wsa_err);
-    return 1;
-  }
-  if (wsa_data.wVersion != wsa_version) {
-    fprintf(stderr, "Didn't get expected version: %x\n", wsa_data.wVersion);
-    return 1;
-  }
-#endif
-
-  if (!test_socket_connect()) {
-    return 1;
-  }
-
-  if (!test_printf()) {
-    return 1;
-  }
-
-  if (!test_zero_copy_bio_pairs()) {
-    return 1;
-  }
-
-  printf("PASS\n");
-  return 0;
-}
diff --git a/crypto/bio/bio_test.cc b/crypto/bio/bio_test.cc
new file mode 100644
index 0000000..00a9e32
--- /dev/null
+++ b/crypto/bio/bio_test.cc
@@ -0,0 +1,358 @@
+/* Copyright (c) 2014, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#if !defined(_POSIX_C_SOURCE)
+#define _POSIX_C_SOURCE 201410L
+#endif
+
+#include <openssl/base.h>
+
+#if !defined(OPENSSL_WINDOWS)
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#else
+#include <io.h>
+#pragma warning(push, 3)
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#pragma warning(pop)
+#endif
+
+#include <openssl/bio.h>
+#include <openssl/crypto.h>
+#include <openssl/err.h>
+
+#include <algorithm>
+
+#include "../test/scoped_types.h"
+
+
+#if !defined(OPENSSL_WINDOWS)
+static int closesocket(int sock) {
+  return close(sock);
+}
+
+static void PrintSocketError(const char *func) {
+  perror(func);
+}
+#else
+static void PrintSocketError(const char *func) {
+  fprintf(stderr, "%s: %d\n", func, WSAGetLastError());
+}
+#endif
+
+class ScopedSocket {
+ public:
+  ScopedSocket(int sock) : sock_(sock) {}
+  ~ScopedSocket() {
+    closesocket(sock_);
+  }
+
+ private:
+  const int sock_;
+};
+
+static bool TestSocketConnect() {
+  static const char kTestMessage[] = "test";
+
+  int listening_sock = socket(AF_INET, SOCK_STREAM, 0);
+  if (listening_sock == -1) {
+    PrintSocketError("socket");
+    return false;
+  }
+  ScopedSocket listening_sock_closer(listening_sock);
+
+  struct sockaddr_in sin;
+  memset(&sin, 0, sizeof(sin));
+  sin.sin_family = AF_INET;
+  if (!inet_pton(AF_INET, "127.0.0.1", &sin.sin_addr)) {
+    PrintSocketError("inet_pton");
+    return false;
+  }
+  if (bind(listening_sock, (struct sockaddr *)&sin, sizeof(sin)) != 0) {
+    PrintSocketError("bind");
+    return false;
+  }
+  if (listen(listening_sock, 1)) {
+    PrintSocketError("listen");
+    return false;
+  }
+  socklen_t sockaddr_len = sizeof(sin);
+  if (getsockname(listening_sock, (struct sockaddr *)&sin, &sockaddr_len) ||
+      sockaddr_len != sizeof(sin)) {
+    PrintSocketError("getsockname");
+    return false;
+  }
+
+  char hostname[80];
+  BIO_snprintf(hostname, sizeof(hostname), "%s:%d", "127.0.0.1",
+               ntohs(sin.sin_port));
+  ScopedBIO bio(BIO_new_connect(hostname));
+  if (!bio) {
+    fprintf(stderr, "BIO_new_connect failed.\n");
+    return false;
+  }
+
+  if (BIO_write(bio.get(), kTestMessage, sizeof(kTestMessage)) !=
+      sizeof(kTestMessage)) {
+    fprintf(stderr, "BIO_write failed.\n");
+    BIO_print_errors_fp(stderr);
+    return false;
+  }
+
+  int sock = accept(listening_sock, (struct sockaddr *) &sin, &sockaddr_len);
+  if (sock == -1) {
+    PrintSocketError("accept");
+    return false;
+  }
+  ScopedSocket sock_closer(sock);
+
+  char buf[5];
+  if (recv(sock, buf, sizeof(buf), 0) != sizeof(kTestMessage)) {
+    PrintSocketError("read");
+    return false;
+  }
+  if (memcmp(buf, kTestMessage, sizeof(kTestMessage))) {
+    return false;
+  }
+
+  return true;
+}
+
+
+// BioReadZeroCopyWrapper is a wrapper around the zero-copy APIs to make
+// testing easier.
+static size_t BioReadZeroCopyWrapper(BIO *bio, uint8_t *data, size_t len) {
+  uint8_t *read_buf;
+  size_t read_buf_offset;
+  size_t available_bytes;
+  size_t len_read = 0;
+
+  do {
+    if (!BIO_zero_copy_get_read_buf(bio, &read_buf, &read_buf_offset,
+                                    &available_bytes)) {
+      return 0;
+    }
+
+    available_bytes = std::min(available_bytes, len - len_read);
+    memmove(data + len_read, read_buf + read_buf_offset, available_bytes);
+
+    BIO_zero_copy_get_read_buf_done(bio, available_bytes);
+
+    len_read += available_bytes;
+  } while (len - len_read > 0 && available_bytes > 0);
+
+  return len_read;
+}
+
+// BioWriteZeroCopyWrapper is a wrapper around the zero-copy APIs to make
+// testing easier.
+static size_t BioWriteZeroCopyWrapper(BIO *bio, const uint8_t *data,
+                                      size_t len) {
+  uint8_t *write_buf;
+  size_t write_buf_offset;
+  size_t available_bytes;
+  size_t len_written = 0;
+
+  do {
+    if (!BIO_zero_copy_get_write_buf(bio, &write_buf, &write_buf_offset,
+                                     &available_bytes)) {
+      return 0;
+    }
+
+    available_bytes = std::min(available_bytes, len - len_written);
+    memmove(write_buf + write_buf_offset, data + len_written, available_bytes);
+
+    BIO_zero_copy_get_write_buf_done(bio, available_bytes);
+
+    len_written += available_bytes;
+  } while (len - len_written > 0 && available_bytes > 0);
+
+  return len_written;
+}
+
+static bool TestZeroCopyBioPairs() {
+  // Test read and write, especially triggering the ring buffer wrap-around.
+  uint8_t bio1_application_send_buffer[1024];
+  uint8_t bio2_application_recv_buffer[1024];
+
+  const size_t kLengths[] = {254, 255, 256, 257, 510, 511, 512, 513};
+
+  // These trigger ring buffer wrap around.
+  const size_t kPartialLengths[] = {0, 1, 2, 3, 128, 255, 256, 257, 511, 512};
+
+  static const size_t kBufferSize = 512;
+
+  srand(1);
+  for (size_t i = 0; i < sizeof(bio1_application_send_buffer); i++) {
+    bio1_application_send_buffer[i] = rand() & 255;
+  }
+
+  // Transfer bytes from bio1_application_send_buffer to
+  // bio2_application_recv_buffer in various ways.
+  for (size_t i = 0; i < sizeof(kLengths) / sizeof(kLengths[0]); i++) {
+    for (size_t j = 0; j < sizeof(kPartialLengths) / sizeof(kPartialLengths[0]);
+         j++) {
+      size_t total_write = 0;
+      size_t total_read = 0;
+
+      BIO *bio1, *bio2;
+      if (!BIO_new_bio_pair(&bio1, kBufferSize, &bio2, kBufferSize)) {
+        return false;
+      }
+      ScopedBIO bio1_scoper(bio1);
+      ScopedBIO bio2_scoper(bio2);
+
+      total_write += BioWriteZeroCopyWrapper(
+          bio1, bio1_application_send_buffer, kLengths[i]);
+
+      // This tests interleaved read/write calls. Do a read between zero copy
+      // write calls.
+      uint8_t *write_buf;
+      size_t write_buf_offset;
+      size_t available_bytes;
+      if (!BIO_zero_copy_get_write_buf(bio1, &write_buf, &write_buf_offset,
+                                       &available_bytes)) {
+        return false;
+      }
+
+      // Free kPartialLengths[j] bytes in the beginning of bio1 write buffer.
+      // This enables ring buffer wrap around for the next write.
+      total_read += BIO_read(bio2, bio2_application_recv_buffer + total_read,
+                             kPartialLengths[j]);
+
+      size_t interleaved_write_len = std::min(kPartialLengths[j],
+                                              available_bytes);
+
+      // Write the data for the interleaved write call. If the buffer becomes
+      // empty after a read, the write offset is normally set to 0. Check that
+      // this does not happen for interleaved read/write and that
+      // |write_buf_offset| is still valid.
+      memcpy(write_buf + write_buf_offset,
+             bio1_application_send_buffer + total_write, interleaved_write_len);
+      if (BIO_zero_copy_get_write_buf_done(bio1, interleaved_write_len)) {
+        total_write += interleaved_write_len;
+      }
+
+      // Do another write in case |write_buf_offset| was wrapped.
+      total_write += BioWriteZeroCopyWrapper(
+          bio1, bio1_application_send_buffer + total_write,
+          kPartialLengths[j] - interleaved_write_len);
+
+      // Drain the rest.
+      size_t bytes_left = BIO_pending(bio2);
+      total_read += BioReadZeroCopyWrapper(
+          bio2, bio2_application_recv_buffer + total_read, bytes_left);
+
+      if (total_read != total_write) {
+        fprintf(stderr, "Lengths not equal in round (%u, %u)\n", (unsigned)i,
+                (unsigned)j);
+        return false;
+      }
+      if (total_read > kLengths[i] + kPartialLengths[j]) {
+        fprintf(stderr, "Bad lengths in round (%u, %u)\n", (unsigned)i,
+                (unsigned)j);
+        return false;
+      }
+      if (memcmp(bio1_application_send_buffer, bio2_application_recv_buffer,
+                 total_read) != 0) {
+        fprintf(stderr, "Buffers not equal in round (%u, %u)\n", (unsigned)i,
+                (unsigned)j);
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
+static bool TestPrintf() {
+  // Test a short output, a very long one, and various sizes around
+  // 256 (the size of the buffer) to ensure edge cases are correct.
+  static const size_t kLengths[] = { 5, 250, 251, 252, 253, 254, 1023 };
+
+  ScopedBIO bio(BIO_new(BIO_s_mem()));
+  if (!bio) {
+    fprintf(stderr, "BIO_new failed\n");
+    return false;
+  }
+
+  for (size_t i = 0; i < sizeof(kLengths) / sizeof(kLengths[0]); i++) {
+    char string[1024];
+    if (kLengths[i] >= sizeof(string)) {
+      fprintf(stderr, "Bad test string length\n");
+      return false;
+    }
+    memset(string, 'a', sizeof(string));
+    string[kLengths[i]] = '\0';
+
+    int ret = BIO_printf(bio.get(), "test %s", string);
+    if (ret < 0 || static_cast<size_t>(ret) != 5 + kLengths[i]) {
+      fprintf(stderr, "BIO_printf failed: %d\n", ret);
+      return false;
+    }
+    const uint8_t *contents;
+    size_t len;
+    if (!BIO_mem_contents(bio.get(), &contents, &len)) {
+      fprintf(stderr, "BIO_mem_contents failed\n");
+      return false;
+    }
+    if (len != 5 + kLengths[i] ||
+        strncmp((const char *)contents, "test ", 5) != 0 ||
+        strncmp((const char *)contents + 5, string, kLengths[i]) != 0) {
+      fprintf(stderr, "Contents did not match: %.*s\n", (int)len, contents);
+      return false;
+    }
+
+    if (!BIO_reset(bio.get())) {
+      fprintf(stderr, "BIO_reset failed\n");
+      return false;
+    }
+  }
+
+  return true;
+}
+
+int main(void) {
+  CRYPTO_library_init();
+  ERR_load_crypto_strings();
+
+#if defined(OPENSSL_WINDOWS)
+  // Initialize Winsock.
+  WORD wsa_version = MAKEWORD(2, 2);
+  WSADATA wsa_data;
+  int wsa_err = WSAStartup(wsa_version, &wsa_data);
+  if (wsa_err != 0) {
+    fprintf(stderr, "WSAStartup failed: %d\n", wsa_err);
+    return 1;
+  }
+  if (wsa_data.wVersion != wsa_version) {
+    fprintf(stderr, "Didn't get expected version: %x\n", wsa_data.wVersion);
+    return 1;
+  }
+#endif
+
+  if (!TestSocketConnect() ||
+      !TestPrintf() ||
+      !TestZeroCopyBioPairs()) {
+    return 1;
+  }
+
+  printf("PASS\n");
+  return 0;
+}