/** 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; }
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); }
/** 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; }
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; }
/** 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; }
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; }
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; }
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; }
/** 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; }
/** 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; }