コード例 #1
0
/** run as a child thread by test_mongoc_tls_handshake_stall
 *
 * It:
 *    1. spins up
 *    2. waits on a condvar until the server is up
 *    3. connects to the server's port
 *    4. attempts handshake
 *    5. confirms that it times out
 *    6. shuts down
 */
static void *
handshake_stall_client (void *ptr)
{
   ssl_test_data_t *data = (ssl_test_data_t *)ptr;
   char *uri_str;
   mongoc_client_t *client;
   bson_t reply;
   bson_error_t error;
   int64_t connect_timeout_ms = data->handshake_stall_ms - 100;
   int64_t duration_ms;

   int64_t start_time;

   mongoc_mutex_lock (&data->cond_mutex);
   while (!data->server_port) {
      mongoc_cond_wait (&data->cond, &data->cond_mutex);
   }
   mongoc_mutex_unlock (&data->cond_mutex);

   uri_str = bson_strdup_printf (
      "mongodb://localhost:%u/?ssl=true&serverselectiontimeoutms=200&connecttimeoutms=%" PRId64,
      data->server_port, connect_timeout_ms);

   client = mongoc_client_new (uri_str);

   /* we should time out after about 200ms */
   start_time = bson_get_monotonic_time ();
   mongoc_client_get_server_status (client,
                                    NULL,
                                    &reply,
                                    &error);

   /* time is in microseconds */
   duration_ms = (bson_get_monotonic_time () - start_time) / 1000;

   if (llabs(duration_ms - connect_timeout_ms) > 100) {
      fprintf (stderr,
               "expected timeout after about 200ms, not %" PRId64 "\n",
               duration_ms);
      abort ();
   }

   data->client_result->result = SSL_TEST_SUCCESS;

   bson_destroy (&reply);
   mongoc_client_destroy (client);
   bson_free (uri_str);

   return NULL;
}
コード例 #2
0
mongoc_client_t *
mongoc_client_pool_pop (mongoc_client_pool_t *pool)
{
   mongoc_client_t *client;

   ENTRY;

   BSON_ASSERT (pool);

   mongoc_mutex_lock(&pool->mutex);

again:
   if (!(client = (mongoc_client_t *)_mongoc_queue_pop_head(&pool->queue))) {
      if (pool->size < pool->max_pool_size) {
         client = _mongoc_client_new_from_uri(pool->uri, pool->topology);
         client->error_api_version = pool->error_api_version;
         _mongoc_client_set_apm_callbacks_private (client,
                                                   &pool->apm_callbacks,
                                                   pool->apm_context);
#ifdef MONGOC_ENABLE_SSL
         if (pool->ssl_opts_set) {
            mongoc_client_set_ssl_opts (client, &pool->ssl_opts);
         }
#endif
         pool->size++;
      } else {
         mongoc_cond_wait(&pool->cond, &pool->mutex);
         GOTO(again);
      }
   }

   start_scanner_if_needed (pool);
   mongoc_mutex_unlock(&pool->mutex);

   RETURN(client);
}
コード例 #3
0
/** this function is meant to be run from ssl_test as a child thread
 *
 * It:
 *    1. spins up
 *    2. waits on a condvar until the server is up
 *    3. connects to the server's port
 *    4. writes a 4 bytes length
 *    5. writes a string of length size
 *    6. reads a response back of the given length
 *    7. confirms that its the same as what was written
 *    8. shuts down
 */
static void *
ssl_test_client (void *ptr)
{
   ssl_test_data_t *data = (ssl_test_data_t *) ptr;
   mongoc_stream_t *sock_stream;
   mongoc_stream_t *ssl_stream;
   mongoc_socket_t *conn_sock;
   int i;
   char buf[1024];
   ssize_t r;
   mongoc_iovec_t riov;
   mongoc_iovec_t wiov;
   mongoc_iovec_t wiov_many[NUM_IOVECS];
   struct sockaddr_in server_addr = {0};
   int len;
   bson_error_t error;

   riov.iov_base = buf;
   riov.iov_len = sizeof buf;

   conn_sock = mongoc_socket_new (AF_INET, SOCK_STREAM, 0);
   BSON_ASSERT (conn_sock);

   mongoc_mutex_lock (&data->cond_mutex);
   while (!data->server_port) {
      mongoc_cond_wait (&data->cond, &data->cond_mutex);
   }
   mongoc_mutex_unlock (&data->cond_mutex);

   server_addr.sin_family = AF_INET;
   server_addr.sin_port = htons (data->server_port);
   server_addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);

   r = mongoc_socket_connect (
      conn_sock, (struct sockaddr *) &server_addr, sizeof (server_addr), -1);
   if (r != 0) {
      fprintf (stderr,
               "mongoc_socket_connect returned %zd: \"%s\"",
               r,
               strerror (errno));
      abort ();
   }

   sock_stream = mongoc_stream_socket_new (conn_sock);
   BSON_ASSERT (sock_stream);
   ssl_stream = mongoc_stream_tls_new_with_hostname (
      sock_stream, data->host, data->client, 1);
   if (!ssl_stream) {
#ifdef MONGOC_ENABLE_SSL_OPENSSL
      unsigned long err = ERR_get_error ();
#else
      unsigned long err = 44;
#endif
      BSON_ASSERT (err);

      data->client_result->ssl_err = err;
      data->client_result->result = SSL_TEST_SSL_INIT;
      MONGOC_ERROR ("ERRORED (line: %d): %s\n",
                    __LINE__,
                    "mongoc_stream_tls_new_with_hostname failed.");

      mongoc_stream_destroy (sock_stream);

      return NULL;
   }
   BSON_ASSERT (ssl_stream);

   r = mongoc_stream_tls_handshake_block (
      ssl_stream, data->host, TIMEOUT, &error);

   if (!r) {
      unsigned long err = 45;

      data->client_result->ssl_err = err;
      data->client_result->result = SSL_TEST_SSL_HANDSHAKE;
      MONGOC_ERROR ("ERRORED (line: %d): %s\n", __LINE__, error.message);

      mongoc_stream_destroy (ssl_stream);
      return NULL;
   }

   len = 4 * NUM_IOVECS;

   wiov.iov_base = (void *) &len;
   wiov.iov_len = 4;
   r = mongoc_stream_writev (ssl_stream, &wiov, 1, TIMEOUT);

   BSON_ASSERT (r == wiov.iov_len);

   for (i = 0; i < NUM_IOVECS; i++) {
      wiov_many[i].iov_base = (void *) "foo";
      wiov_many[i].iov_len = 4;
   }

   r = mongoc_stream_writev (ssl_stream, wiov_many, NUM_IOVECS, TIMEOUT);
   BSON_ASSERT (r == wiov_many[0].iov_len * NUM_IOVECS);

   riov.iov_len = 1;

   r = mongoc_stream_readv (ssl_stream, &riov, 1, 1, TIMEOUT);
   BSON_ASSERT (r == 1);
   BSON_ASSERT (memcmp (riov.iov_base, "f", 1) == 0);

   riov.iov_len = 3;

   r = mongoc_stream_readv (ssl_stream, &riov, 1, 3, TIMEOUT);
   BSON_ASSERT (r == 3);
   BSON_ASSERT (memcmp (riov.iov_base, "oo", 3) == 0);

   mongoc_stream_destroy (ssl_stream);

   data->client_result->result = SSL_TEST_SUCCESS;

   return NULL;
}
コード例 #4
0
uint16_t
mock_server_run (mock_server_t *server)
{
   mongoc_socket_t *ssock;
   struct sockaddr_in bind_addr;
   int optval;
   uint16_t bound_port;

   MONGOC_INFO ("Starting mock server on port %d.", server->port);

   ssock = mongoc_socket_new (AF_INET, SOCK_STREAM, 0);
   if (!ssock) {
      perror ("Failed to create socket.");
      return 0;
   }

   optval = 1;
   mongoc_socket_setsockopt (ssock, SOL_SOCKET, SO_REUSEADDR, &optval,
                             sizeof optval);

   memset (&bind_addr, 0, sizeof bind_addr);

   bind_addr.sin_family = AF_INET;
   bind_addr.sin_addr.s_addr = htonl (INADDR_ANY);

   /* bind to unused port */
   bind_addr.sin_port = htons (0);

   if (-1 == mongoc_socket_bind (ssock,
                                 (struct sockaddr *) &bind_addr,
                                 sizeof bind_addr)) {
      perror ("Failed to bind socket");
      return 0;
   }

   if (-1 == mongoc_socket_listen (ssock, 10)) {
      perror ("Failed to put socket into listen mode");
      return 0;
   }

   bound_port = get_port (ssock);
   if (!bound_port) {
      perror ("Failed to get bound port number");
      return 0;
   }

   mongoc_mutex_lock (&server->mutex);

   server->sock = ssock;
   server->port = bound_port;
   /* TODO: configurable timeouts, perhaps from env */
   server->uri_str = bson_strdup_printf (
         "mongodb://127.0.0.1:%hu/?serverselectiontimeoutms=10000&"
         "sockettimeoutms=10000",
         bound_port);
   server->uri = mongoc_uri_new (server->uri_str);

   mongoc_thread_create (&server->main_thread, main_thread, (void *) server);

   /* wait for main thread to start */
   mongoc_cond_wait (&server->cond, &server->mutex);
   mongoc_mutex_unlock (&server->mutex);

   if (mock_server_get_verbose (server)) {
      fprintf (stderr, "listening on port %hu\n", bound_port);
      fflush (stdout);
   }

   return (uint16_t) bound_port;
}
コード例 #5
0
ファイル: ssl-test.c プロジェクト: watsonsong/mongo-c-driver
/** this function is meant to be run from ssl_test as a child thread
 *
 * It:
 *    1. spins up
 *    2. waits on a condvar until the server is up
 *    3. connects to the servers port
 *    4. writes a 4 bytes length
 *    5. writes a string of length size
 *    6. reads a response back of the given length
 *    7. confirms that its the same as what was written
 *    8. shuts down
 */
static void *
ssl_test_client (void * ptr)
{
   ssl_test_data_t *data = (ssl_test_data_t *)ptr;
   mongoc_stream_t *sock_stream;
   mongoc_stream_t *ssl_stream;
   mongoc_socket_t *conn_sock;
   char buf[1024];
   ssize_t r;
   mongoc_iovec_t riov;
   mongoc_iovec_t wiov;
   struct sockaddr_in server_addr = { 0 };
   int len;

   riov.iov_base = buf;
   riov.iov_len = sizeof buf;

   conn_sock = mongoc_socket_new (AF_INET, SOCK_STREAM, 0);
   assert (conn_sock);

   mongoc_mutex_lock(&data->cond_mutex);
   while (! data->server_port) {
      mongoc_cond_wait(&data->cond, &data->cond_mutex);
   }
   mongoc_mutex_unlock(&data->cond_mutex);

   server_addr.sin_family = AF_INET;
   server_addr.sin_port = htons(data->server_port);
   r = inet_pton(AF_INET, LOCALHOST, &server_addr.sin_addr);
   assert (r > 0);

   r = mongoc_socket_connect (conn_sock, (struct sockaddr *)&server_addr, sizeof(server_addr), -1);
   assert (r == 0);

   sock_stream = mongoc_stream_socket_new (conn_sock);
   assert(sock_stream);
   ssl_stream = mongoc_stream_tls_new(sock_stream, data->client, 1);
   if (! ssl_stream) {
      unsigned long err = ERR_get_error();
      assert(err);

      data->client_result->ssl_err = err;
      data->client_result->result = SSL_TEST_SSL_INIT;

      mongoc_stream_destroy(sock_stream);

      return NULL;
   }
   assert(ssl_stream);

   errno = 0;
   r = mongoc_stream_tls_do_handshake (ssl_stream, TIMEOUT);
   if (! r) {
      unsigned long err = ERR_get_error();
      assert(err || errno);

      if (err) {
         data->client_result->ssl_err = err;
      } else {
         data->client_result->err = errno;
      }

      data->client_result->result = SSL_TEST_SSL_HANDSHAKE;

      mongoc_stream_destroy(ssl_stream);
      return NULL;
   }

   r = mongoc_stream_tls_check_cert (ssl_stream, data->host);
   if (! r) {
      data->client_result->result = SSL_TEST_SSL_VERIFY;

      mongoc_stream_destroy(ssl_stream);
      return NULL;
   }

   len = 4;

   wiov.iov_base = (void *)&len;
   wiov.iov_len = 4;
   r = mongoc_stream_writev(ssl_stream, &wiov, 1, TIMEOUT);

   assert(r == wiov.iov_len);

   wiov.iov_base = "foo";
   wiov.iov_len = 4;
   r = mongoc_stream_writev(ssl_stream, &wiov, 1, TIMEOUT);
   assert(r == wiov.iov_len);

   r = mongoc_stream_readv(ssl_stream, &riov, 1, 4, TIMEOUT);
   assert(r > 0);
   assert(r == wiov.iov_len);
   assert(strcmp(riov.iov_base, wiov.iov_base) == 0);

   mongoc_stream_destroy(ssl_stream);

   data->client_result->result = SSL_TEST_SUCCESS;

   return NULL;
}
コード例 #6
0
static void *
socket_test_client (void *data_)
{
   socket_test_data_t *data = (socket_test_data_t *)data_;
   mongoc_socket_t *conn_sock;
   char buf[5];
   ssize_t r;
   bool closed;
   struct sockaddr_in server_addr = { 0 };
   mongoc_stream_t *stream;
   mongoc_iovec_t iov;

   iov.iov_base = buf;
   iov.iov_len = sizeof (buf);

   conn_sock = mongoc_socket_new (AF_INET, SOCK_STREAM, 0);
   assert (conn_sock);

   mongoc_mutex_lock(&data->cond_mutex);
   while (! data->server_port) {
      mongoc_cond_wait(&data->cond, &data->cond_mutex);
   }
   mongoc_mutex_unlock(&data->cond_mutex);

   server_addr.sin_family = AF_INET;
   server_addr.sin_port = htons(data->server_port);
   server_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

   r = mongoc_socket_connect (conn_sock, (struct sockaddr *)&server_addr, sizeof(server_addr), -1);
   assert (r == 0);

   stream = mongoc_stream_socket_new (conn_sock);

   strcpy (buf, "ping");

   closed = mongoc_stream_check_closed (stream);
   assert (closed == false);

   r = mongoc_stream_writev (stream, &iov, 1, TIMEOUT);
   assert (r == 5);

   closed = mongoc_stream_check_closed (stream);
   assert (closed == false);

   r = mongoc_stream_readv (stream, &iov, 1, 5, TIMEOUT);
   assert (r == 5);
   assert (strcmp (buf, "pong") == 0);

   mongoc_mutex_lock(&data->cond_mutex);
   while (! data->closed_socket) {
      mongoc_cond_wait(&data->cond, &data->cond_mutex);
   }
   mongoc_mutex_unlock(&data->cond_mutex);

   closed = mongoc_stream_check_closed (stream);
   assert (closed == true);

   mongoc_stream_destroy (stream);

   return NULL;
}
コード例 #7
0
static void *
sendv_test_client (void *data_)
{
   socket_test_data_t *data = (socket_test_data_t *)data_;
   mongoc_socket_t *conn_sock;
   ssize_t r;
   int i;
   int amount = 0;
   struct sockaddr_in server_addr = { 0 };
   mongoc_stream_t *stream;
   mongoc_iovec_t iov;
   bool done = false;
   char *buf = (char *)bson_malloc (gFourMB);

   memset (buf, 'a', (gFourMB)- 1);
   buf[gFourMB - 1] = '\0';

   iov.iov_base = buf;
   iov.iov_len = gFourMB;

   conn_sock = mongoc_socket_new (AF_INET, SOCK_STREAM, 0);
   assert (conn_sock);

   mongoc_mutex_lock(&data->cond_mutex);
   while (! data->server_port) {
      mongoc_cond_wait(&data->cond, &data->cond_mutex);
   }
   mongoc_mutex_unlock(&data->cond_mutex);

   server_addr.sin_family = AF_INET;
   server_addr.sin_port = htons(data->server_port);
   server_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

   r = mongoc_socket_connect (conn_sock, (struct sockaddr *)&server_addr, sizeof(server_addr), -1);
   assert (r == 0);

   stream = mongoc_stream_socket_new (conn_sock);

   for (i = 0; i < 5; i++)
   {
      r = mongoc_stream_writev (stream, &iov, 1, WAIT);
      if (r > 0) {
         amount += r;
      }
      if (r != gFourMB) {
         if (MONGOC_ERRNO_IS_AGAIN(conn_sock->errno_)) {
            if (!done) {
               mongoc_mutex_lock(&data->cond_mutex);
               data->amount = amount;
               amount = 0;
               mongoc_cond_signal(&data->cond);
               mongoc_mutex_unlock(&data->cond_mutex);
               done = true;
            }
         } else {
            assert (r == gFourMB);
         }
      }
   }
   assert(true == done);
   mongoc_mutex_lock(&data->cond_mutex);
   data->amount = amount;
   mongoc_cond_signal(&data->cond);
   mongoc_mutex_unlock(&data->cond_mutex);

   mongoc_stream_destroy (stream);

   return NULL;
}
コード例 #8
0
static void *
sendv_test_server (void *data_)
{
   socket_test_data_t *data = (socket_test_data_t *)data_;
   struct sockaddr_in server_addr = { 0 };
   mongoc_socket_t *listen_sock;
   mongoc_socket_t *conn_sock;
   mongoc_stream_t *stream;
   mongoc_iovec_t iov;
   socklen_t sock_len;
   int amount = 0;
   ssize_t r;
   char *buf = (char *)bson_malloc (gFourMB);

   iov.iov_base = buf;
   iov.iov_len = gFourMB;

   listen_sock = mongoc_socket_new (AF_INET, SOCK_STREAM, 0);
   assert (listen_sock);

   server_addr.sin_family = AF_INET;
   server_addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
   server_addr.sin_port = htons (0);

   r = mongoc_socket_bind (listen_sock,
                           (struct sockaddr *)&server_addr,
                           sizeof server_addr);
   assert (r == 0);

   sock_len = sizeof(server_addr);
   r = mongoc_socket_getsockname (listen_sock, (struct sockaddr *)&server_addr, &sock_len);
   assert(r == 0);

   r = mongoc_socket_listen (listen_sock, 10);
   assert(r == 0);

   mongoc_mutex_lock(&data->cond_mutex);
   data->server_port = ntohs(server_addr.sin_port);
   mongoc_cond_signal(&data->cond);
   mongoc_mutex_unlock(&data->cond_mutex);

   conn_sock = mongoc_socket_accept (listen_sock, -1);
   assert (conn_sock);

   stream = mongoc_stream_socket_new (conn_sock);
   assert (stream);

   /* Wait until the client has pushed so much data he can't write more */
   mongoc_mutex_lock(&data->cond_mutex);
   while (! data->amount) {
      mongoc_cond_wait(&data->cond, &data->cond_mutex);
   }
   amount = data->amount;
   data->amount = 0;
   mongoc_mutex_unlock(&data->cond_mutex);

   /* Start reading everything off the socket to unblock the client */
   do {
      r = mongoc_stream_readv (stream, &iov, 1, amount, WAIT);
      if (r > 0) {
         amount -= r;
      } else if (MONGOC_ERRNO_IS_AGAIN(errno)) {
         continue;
      } else {
         fprintf(stderr, "r: %"PRIu64" , errno: %d", (uint64_t)r, conn_sock->errno_);
         assert (r == amount);
      }
   } while (amount > 0);


   /* Allow the client to finish all its writes */
   mongoc_mutex_lock(&data->cond_mutex);
   while (! data->amount) {
      mongoc_cond_wait(&data->cond, &data->cond_mutex);
   }
   /* amount is likely negative value now, we've read more then caused the original blocker */
   amount += data->amount;
   data->amount = 0;
   mongoc_mutex_unlock(&data->cond_mutex);

   do {
      r = mongoc_stream_readv (stream, &iov, 1, amount, WAIT);
      if (r > 0) {
         amount -= r;
      } else if (MONGOC_ERRNO_IS_AGAIN(errno)) {
         continue;
      } else {
         fprintf(stderr, "r: %"PRIu64" , errno: %d", (uint64_t)r, errno);
         assert (r == amount);
      }
   } while (amount > 0);
   ASSERT_CMPINT(0, ==, amount);


   mongoc_stream_destroy (stream);

   mongoc_socket_destroy (listen_sock);

   return NULL;
}
コード例 #9
0
ファイル: ssl-test.c プロジェクト: lloydzhou/mongo-c-driver
/** this function is meant to be run from ssl_test as a child thread
 *
 * It:
 *    1. spins up
 *    2. waits on a condvar until the server is up
 *    3. connects to the servers port
 *    4. writes a 4 bytes length
 *    5. writes a string of length size
 *    6. reads a response back of the given length
 *    7. confirms that its the same as what was written
 *    8. shuts down
 */
static void *
ssl_test_client (void * ptr)
{
   ssl_test_data_t *data = (ssl_test_data_t *)ptr;
   mongoc_stream_t *sock_stream;
   mongoc_stream_t *ssl_stream;
   mongoc_socket_t *conn_sock;
   int i;
   int errno_captured;
   char buf[1024];
   ssize_t r;
   mongoc_iovec_t riov;
   mongoc_iovec_t wiov;
   mongoc_iovec_t wiov_many[NUM_IOVECS];
   struct sockaddr_in server_addr = { 0 };
   int len;

   riov.iov_base = buf;
   riov.iov_len = sizeof buf;

   conn_sock = mongoc_socket_new (AF_INET, SOCK_STREAM, 0);
   assert (conn_sock);

   mongoc_mutex_lock(&data->cond_mutex);
   while (! data->server_port) {
      mongoc_cond_wait(&data->cond, &data->cond_mutex);
   }
   mongoc_mutex_unlock(&data->cond_mutex);

   server_addr.sin_family = AF_INET;
   server_addr.sin_port = htons(data->server_port);
   server_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

   r = mongoc_socket_connect (conn_sock, (struct sockaddr *)&server_addr, sizeof(server_addr), -1);
   assert (r == 0);

   sock_stream = mongoc_stream_socket_new (conn_sock);
   assert(sock_stream);
   ssl_stream = mongoc_stream_tls_new(sock_stream, data->client, 1);
   if (! ssl_stream) {
      unsigned long err = ERR_get_error();
      assert(err);

      data->client_result->ssl_err = err;
      data->client_result->result = SSL_TEST_SSL_INIT;

      mongoc_stream_destroy(sock_stream);

      return NULL;
   }
   assert(ssl_stream);

   errno = 0;
   r = mongoc_stream_tls_do_handshake (ssl_stream, TIMEOUT);
   errno_captured = errno;

   if (! r) {
      unsigned long err = ERR_get_error();
      assert(err || errno_captured);

      if (err) {
         data->client_result->ssl_err = err;
      } else {
         data->client_result->err = errno_captured;
      }

      data->client_result->result = SSL_TEST_SSL_HANDSHAKE;

      mongoc_stream_destroy(ssl_stream);
      return NULL;
   }

   r = mongoc_stream_tls_check_cert (ssl_stream, data->host);
   if (! r) {
      data->client_result->result = SSL_TEST_SSL_VERIFY;

      mongoc_stream_destroy(ssl_stream);
      return NULL;
   }

   len = 4 * NUM_IOVECS;

   wiov.iov_base = (void *)&len;
   wiov.iov_len = 4;
   r = mongoc_stream_writev(ssl_stream, &wiov, 1, TIMEOUT);

   assert(r == wiov.iov_len);

   for (i = 0; i < NUM_IOVECS; i++) {
      wiov_many[i].iov_base = "foo";
      wiov_many[i].iov_len = 4;
   }

   r = mongoc_stream_writev(ssl_stream, wiov_many, NUM_IOVECS, TIMEOUT);
   assert(r == wiov_many[0].iov_len * NUM_IOVECS);

   riov.iov_len = 1;

   r = mongoc_stream_readv(ssl_stream, &riov, 1, 1, TIMEOUT);
   assert(r == 1);
   assert(memcmp(riov.iov_base, "f", 1) == 0);

   riov.iov_len = 3;

   r = mongoc_stream_readv(ssl_stream, &riov, 1, 3, TIMEOUT);
   assert(r == 3);
   assert(memcmp(riov.iov_base, "oo", 3) == 0);

   mongoc_stream_destroy(ssl_stream);

   data->client_result->result = SSL_TEST_SUCCESS;

   return NULL;
}
コード例 #10
0
/** run as a child thread by test_mongoc_tls_hangup
 *
 * It:
 *    1. spins up
 *    2. waits on a condvar until the server is up
 *    3. connects to the server's port
 *    4. writes a byte
 *    5. confirms that the server hangs up promptly
 *    6. shuts down
 */
static void *
ssl_hangup_client (void *ptr)
{
   ssl_test_data_t *data = (ssl_test_data_t *) ptr;
   mongoc_stream_t *sock_stream;
   mongoc_stream_t *ssl_stream;
   mongoc_socket_t *conn_sock;
   char buf = 'b';
   ssize_t r;
   mongoc_iovec_t riov;
   mongoc_iovec_t wiov;
   struct sockaddr_in server_addr = {0};
   int64_t start_time;
   bson_error_t error;

   conn_sock = mongoc_socket_new (AF_INET, SOCK_STREAM, 0);
   assert (conn_sock);

   mongoc_mutex_lock (&data->cond_mutex);
   while (!data->server_port) {
      mongoc_cond_wait (&data->cond, &data->cond_mutex);
   }
   mongoc_mutex_unlock (&data->cond_mutex);

   server_addr.sin_family = AF_INET;
   server_addr.sin_port = htons (data->server_port);
   server_addr.sin_addr.s_addr = htonl (INADDR_LOOPBACK);

   r = mongoc_socket_connect (
      conn_sock, (struct sockaddr *) &server_addr, sizeof (server_addr), -1);
   assert (r == 0);

   sock_stream = mongoc_stream_socket_new (conn_sock);
   assert (sock_stream);

   ssl_stream = mongoc_stream_tls_new_with_hostname (
      sock_stream, data->host, data->client, 1);
   assert (ssl_stream);

   r = mongoc_stream_tls_handshake_block (
      ssl_stream, data->host, TIMEOUT, &error);
   assert (r);

   wiov.iov_base = (void *) &buf;
   wiov.iov_len = 1;
   r = mongoc_stream_writev (ssl_stream, &wiov, 1, TIMEOUT);
   assert (r == 1);

   riov.iov_base = (void *) &buf;
   riov.iov_len = 1;

   /* we should notice promptly that the server hangs up */
   start_time = bson_get_monotonic_time ();
   r = mongoc_stream_readv (ssl_stream, &riov, 1, 1, TIMEOUT);
   /* time is in microseconds */
   assert (bson_get_monotonic_time () - start_time < 1000 * 1000);
   assert (r == -1);
   mongoc_stream_destroy (ssl_stream);
   data->client_result->result = SSL_TEST_SUCCESS;
   return NULL;
}