void ha_replica_set_wait_for_healthy (ha_replica_set_t *replica_set) { bson_iter_t iter; bson_iter_t ar; bson_iter_t member; const char *stateStr; bson_t status; again: sleep(1); if (!ha_replica_set_get_status(replica_set, &status)) { MONGOC_INFO("Failed to get replicaSet status. " "Sleeping 1 second."); goto again; } #if 0 { char *str; str = bson_as_json(&status, NULL); printf("%s\n", str); bson_free(str); } #endif if (!bson_iter_init_find(&iter, &status, "members") || !BSON_ITER_HOLDS_ARRAY(&iter) || !bson_iter_recurse(&iter, &ar)) { bson_destroy(&status); MONGOC_INFO("ReplicaSet has not yet come online. " "Sleeping 1 second."); goto again; } while (bson_iter_next(&ar)) { if (BSON_ITER_HOLDS_DOCUMENT(&ar) && bson_iter_recurse(&ar, &member) && bson_iter_find(&member, "stateStr") && (stateStr = bson_iter_utf8(&member, NULL))) { if (!!strcmp(stateStr, "PRIMARY") && !!strcmp(stateStr, "SECONDARY") && !!strcmp(stateStr, "ARBITER")) { bson_destroy(&status); MONGOC_INFO("Found unhealthy node. Sleeping 1 second."); goto again; } } } bson_destroy(&status); }
void mongoc_client_kill_cursor (mongoc_client_t *client, int64_t cursor_id) { mongoc_topology_t *topology; mongoc_server_description_t *selected_server; mongoc_read_prefs_t *read_prefs; uint32_t server_id = 0; topology = client->topology; read_prefs = mongoc_read_prefs_new (MONGOC_READ_PRIMARY); mongoc_mutex_lock (&topology->mutex); /* see if there's a known writable server - do no I/O or retries */ selected_server = mongoc_topology_description_select(&topology->description, MONGOC_SS_WRITE, read_prefs, 15); if (selected_server) { server_id = selected_server->id; } mongoc_mutex_unlock (&topology->mutex); if (server_id) { _mongoc_client_kill_cursor (client, selected_server->id, cursor_id); } else { MONGOC_INFO ("No server available for mongoc_client_kill_cursor"); } mongoc_read_prefs_destroy (read_prefs); }
bool mongoc_secure_transport_setup_ca (mongoc_stream_tls_secure_transport_t *secure_transport, mongoc_ssl_opt_t *opt) { if (opt->ca_file) { CFArrayRef items; SecExternalItemType type = kSecItemTypeCertificate; bool success = _mongoc_secure_transport_import_pem (opt->ca_file, NULL, &items, &type); if (!success) { MONGOC_ERROR ("Can't find certificate in \"%s\"", opt->ca_file); return false; } if (type == kSecItemTypeAggregate) { CFMutableArrayRef anchors = CFArrayCreateMutable (kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); for (CFIndex i = 0; i < CFArrayGetCount (items); ++i) { CFTypeID item_id = CFGetTypeID (CFArrayGetValueAtIndex (items, i)); if (item_id == SecCertificateGetTypeID()) { CFArrayAppendValue (anchors, CFArrayGetValueAtIndex (items, i)); } } secure_transport->anchors = CFRetain (anchors); CFRelease (items); } else if (type == kSecItemTypeCertificate) { secure_transport->anchors = CFRetain (items); } /* This should be SSLSetCertificateAuthorities But the /TLS/ tests fail when it is */ success = !SSLSetTrustedRoots (secure_transport->ssl_ctx_ref, secure_transport->anchors, true); MONGOC_DEBUG ("Setting certificate authority %s (%s)", success ? "succeeded" : "failed", opt->ca_file); return true; } MONGOC_INFO ("No CA provided, using defaults"); return false; }
int mock_server_run (mock_server_t *server) { struct sockaddr_in saddr; mongoc_stream_t *stream; mongoc_thread_t thread; mongoc_socket_t *ssock; mongoc_socket_t *csock; void **closure; int optval; bson_return_val_if_fail (server, -1); bson_return_val_if_fail (!server->sock, -1); 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 -1; } optval = 1; mongoc_socket_setsockopt (ssock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval); memset (&saddr, 0, sizeof saddr); saddr.sin_family = AF_INET; saddr.sin_port = htons(server->port); /* * TODO: Parse server->address. */ saddr.sin_addr.s_addr = htonl (INADDR_ANY); if (-1 == mongoc_socket_bind (ssock, (struct sockaddr *)&saddr, sizeof saddr)) { perror("Failed to bind socket"); return -1; } if (-1 == mongoc_socket_listen (ssock, 10)) { perror("Failed to put socket into listen mode"); return 3; } server->sock = ssock; mongoc_mutex_lock (&server->mutex); mongoc_cond_signal (&server->cond); mongoc_mutex_unlock (&server->mutex); for (;;) { csock = mongoc_socket_accept (server->sock, -1); if (!csock) { perror ("Failed to accept client socket"); break; } stream = mongoc_stream_socket_new (csock); closure = bson_malloc0 (sizeof(void*) * 2); closure[0] = server; closure[1] = stream; mongoc_thread_create (&thread, mock_server_worker, closure); } mongoc_socket_close (server->sock); server->sock = NULL; return 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; }
mongoc_cursor_t * _mongoc_cursor_new (mongoc_client_t *client, const char *db_and_collection, mongoc_query_flags_t flags, uint32_t skip, uint32_t limit, uint32_t batch_size, bool is_command, const bson_t *query, const bson_t *fields, const mongoc_read_prefs_t *read_prefs) { mongoc_read_prefs_t *local_read_prefs = NULL; mongoc_read_mode_t mode; mongoc_cursor_t *cursor; const bson_t *tags; bson_iter_t iter; const char *key; const char *mode_str; bson_t child; bool found = false; int i; ENTRY; BSON_ASSERT (client); BSON_ASSERT (db_and_collection); BSON_ASSERT (query); if (!read_prefs) { read_prefs = client->read_prefs; } cursor = bson_malloc0 (sizeof *cursor); /* * CDRIVER-244: * * If this is a command, we need to verify we can send it to the location * specified by the read preferences. Otherwise, log a warning that we * are rerouting to the primary instance. */ if (is_command && read_prefs && (mongoc_read_prefs_get_mode (read_prefs) != MONGOC_READ_PRIMARY) && bson_iter_init (&iter, query) && bson_iter_next (&iter) && (key = bson_iter_key (&iter))) { for (i = 0; gSecondaryOkCommands [i]; i++) { if (0 == strcasecmp (key, gSecondaryOkCommands [i])) { found = true; break; } } if (!found) { cursor->redir_primary = true; local_read_prefs = mongoc_read_prefs_new (MONGOC_READ_PRIMARY); read_prefs = local_read_prefs; MONGOC_INFO ("Database command \"%s\" rerouted to primary node", key); } } /* * Cursors execute their query lazily. This sadly means that we must copy * some extra data around between the bson_t structures. This should be * small in most cases, so it reduces to a pure memcpy. The benefit to this * design is simplified error handling by API consumers. */ cursor->client = client; bson_strncpy (cursor->ns, db_and_collection, sizeof cursor->ns); cursor->nslen = (uint32_t)strlen(cursor->ns); cursor->flags = flags; cursor->skip = skip; cursor->limit = limit; cursor->batch_size = batch_size; cursor->is_command = is_command; #define MARK_FAILED(c) \ do { \ (c)->failed = true; \ (c)->done = true; \ (c)->end_of_event = true; \ (c)->sent = true; \ } while (0) /* we can't have exhaust queries with limits */ if ((flags & MONGOC_QUERY_EXHAUST) && limit) { bson_set_error (&cursor->error, MONGOC_ERROR_CURSOR, MONGOC_ERROR_CURSOR_INVALID_CURSOR, "Cannot specify MONGOC_QUERY_EXHAUST and set a limit."); MARK_FAILED (cursor); GOTO (finish); } /* we can't have exhaust queries with sharded clusters */ if ((flags & MONGOC_QUERY_EXHAUST) && (client->cluster.mode == MONGOC_CLUSTER_SHARDED_CLUSTER)) { bson_set_error (&cursor->error, MONGOC_ERROR_CURSOR, MONGOC_ERROR_CURSOR_INVALID_CURSOR, "Cannot specify MONGOC_QUERY_EXHAUST with sharded cluster."); MARK_FAILED (cursor); GOTO (finish); } /* * Check types of various optional parameters. */ if (!is_command) { if (bson_iter_init_find (&iter, query, "$explain") && !(BSON_ITER_HOLDS_BOOL (&iter) || BSON_ITER_HOLDS_INT32 (&iter))) { bson_set_error (&cursor->error, MONGOC_ERROR_CURSOR, MONGOC_ERROR_CURSOR_INVALID_CURSOR, "$explain must be a boolean."); MARK_FAILED (cursor); GOTO (finish); } if (bson_iter_init_find (&iter, query, "$snapshot") && !BSON_ITER_HOLDS_BOOL (&iter) && !BSON_ITER_HOLDS_INT32 (&iter)) { bson_set_error (&cursor->error, MONGOC_ERROR_CURSOR, MONGOC_ERROR_CURSOR_INVALID_CURSOR, "$snapshot must be a boolean."); MARK_FAILED (cursor); GOTO (finish); } } if (!cursor->is_command && !bson_has_field (query, "$query")) { bson_init (&cursor->query); bson_append_document (&cursor->query, "$query", 6, query); } else { bson_copy_to (query, &cursor->query); } if (read_prefs) { cursor->read_prefs = mongoc_read_prefs_copy (read_prefs); mode = mongoc_read_prefs_get_mode (read_prefs); tags = mongoc_read_prefs_get_tags (read_prefs); if (mode != MONGOC_READ_PRIMARY) { flags |= MONGOC_QUERY_SLAVE_OK; if ((mode != MONGOC_READ_SECONDARY_PREFERRED) || tags) { bson_append_document_begin (&cursor->query, "$readPreference", 15, &child); mode_str = _mongoc_cursor_get_read_mode_string (mode); bson_append_utf8 (&child, "mode", 4, mode_str, -1); if (tags) { bson_append_array (&child, "tags", 4, tags); } bson_append_document_end (&cursor->query, &child); } } } if (fields) { bson_copy_to(fields, &cursor->fields); } else { bson_init(&cursor->fields); } _mongoc_buffer_init(&cursor->buffer, NULL, 0, NULL); finish: mongoc_counter_cursors_active_inc(); if (local_read_prefs) { mongoc_read_prefs_destroy (local_read_prefs); } RETURN (cursor); }
static void test1 (void) { mongoc_server_description_t *description; mongoc_collection_t *collection; mongoc_read_prefs_t *read_prefs; mongoc_cursor_t *cursor; mongoc_client_t *client; mongoc_client_pool_t *pool = NULL; const bson_t *doc; bson_error_t error; bool r; ha_node_t *replica; bson_t q; int i; bson_init(&q); if (use_pool) { pool = ha_replica_set_create_client_pool(replica_set); client = mongoc_client_pool_pop (pool); } else { client = ha_replica_set_create_client(replica_set); } collection = mongoc_client_get_collection(client, "test1", "test1"); MONGOC_DEBUG("Inserting test documents."); insert_test_docs(collection); MONGOC_INFO("Test documents inserted."); read_prefs = mongoc_read_prefs_new(MONGOC_READ_SECONDARY); MONGOC_DEBUG("Sending query to a SECONDARY."); cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 100, &q, NULL, read_prefs); BSON_ASSERT(cursor); BSON_ASSERT(!cursor->server_id); /* * Send OP_QUERY to server and get first document back. */ MONGOC_INFO("Sending OP_QUERY."); r = mongoc_cursor_next(cursor, &doc); BSON_ASSERT(r); BSON_ASSERT(cursor->server_id); BSON_ASSERT(cursor->sent); BSON_ASSERT(!cursor->done); BSON_ASSERT(cursor->rpc.reply.n_returned == 100); BSON_ASSERT(!cursor->end_of_event); /* * Make sure we queried a secondary. */ description = mongoc_topology_server_by_id(client->topology, cursor->server_id, &error); ASSERT_OR_PRINT (description, error); BSON_ASSERT (description->type != MONGOC_SERVER_RS_PRIMARY); mongoc_server_description_destroy(description); /* * Exhaust the items in our first OP_REPLY. */ MONGOC_DEBUG("Exhausting OP_REPLY."); for (i = 0; i < 98; i++) { r = mongoc_cursor_next(cursor, &doc); BSON_ASSERT(r); BSON_ASSERT(cursor->server_id); BSON_ASSERT(!cursor->done); BSON_ASSERT(!cursor->end_of_event); } /* * Finish off the last item in this OP_REPLY. */ MONGOC_INFO("Fetcing last doc from OP_REPLY."); r = mongoc_cursor_next(cursor, &doc); BSON_ASSERT(r); BSON_ASSERT(cursor->server_id); BSON_ASSERT(cursor->sent); BSON_ASSERT(!cursor->done); BSON_ASSERT(!cursor->end_of_event); /* * Determine which node we queried by using the server_id to * get the cluster information. */ BSON_ASSERT(cursor->server_id); replica = get_replica(client, cursor->server_id); /* * Kill the node we are communicating with. */ MONGOC_INFO("Killing replicaSet node to synthesize failure."); ha_node_kill(replica); /* * Try to fetch the next result set, expect failure. */ MONGOC_DEBUG("Checking for expected failure."); r = mongoc_cursor_next(cursor, &doc); BSON_ASSERT(!r); r = mongoc_cursor_error(cursor, &error); BSON_ASSERT(r); MONGOC_WARNING("%s", error.message); mongoc_cursor_destroy(cursor); mongoc_read_prefs_destroy(read_prefs); mongoc_collection_destroy(collection); if (use_pool) { mongoc_client_pool_push (pool, client); mongoc_client_pool_destroy (pool); } else { mongoc_client_destroy(client); } bson_destroy(&q); ha_node_restart(replica); }
bool mongoc_secure_transport_setup_certificate (mongoc_stream_tls_secure_transport_t *secure_transport, mongoc_ssl_opt_t *opt) { bool success; CFArrayRef items; SecIdentityRef id; SecKeyRef key = NULL; SecCertificateRef cert = NULL; SecExternalItemType type = kSecItemTypeCertificate; if (!opt->pem_file) { MONGOC_INFO ("No private key provided, the server won't be able to verify us"); return false; } success = _mongoc_secure_transport_import_pem (opt->pem_file, opt->pem_pwd, &items, &type); if (!success) { MONGOC_ERROR ("Can't find certificate in: '%s'", opt->pem_file); return false; } if (type != kSecItemTypeAggregate) { MONGOC_ERROR ("Cannot work with keys of type \"%d\". Please file a JIRA", type); CFRelease (items); return false; } for (CFIndex i = 0; i < CFArrayGetCount (items); ++i) { CFTypeID item_id = CFGetTypeID (CFArrayGetValueAtIndex (items, i)); if (item_id == SecCertificateGetTypeID()) { cert = (SecCertificateRef) CFArrayGetValueAtIndex (items, i); } else if (item_id == SecKeyGetTypeID()) { key = (SecKeyRef) CFArrayGetValueAtIndex (items, i); } } if (!cert || !key) { MONGOC_ERROR ("Couldn't find valid private key"); CFRelease (items); return false; } id = SecIdentityCreate (kCFAllocatorDefault, cert, key); secure_transport->my_cert = CFArrayCreateMutableCopy(kCFAllocatorDefault, (CFIndex)2, items); CFArraySetValueAtIndex(secure_transport->my_cert, 0, id); CFArraySetValueAtIndex(secure_transport->my_cert, 1, cert); /* * Secure Transport assumes the following: * * The certificate references remain valid for the lifetime of the session. * * The identity specified in certRefs[0] is capable of signing. */ success = !SSLSetCertificate (secure_transport->ssl_ctx_ref, secure_transport->my_cert); MONGOC_DEBUG("Setting client certificate %s", success ? "succeeded" : "failed"); CFRelease (items); return true; }
bool _mongoc_secure_transport_import_pem (const char *filename, const char *passphrase, CFArrayRef *items, SecExternalItemType *type) { SecExternalFormat format = kSecFormatPEMSequence; SecItemImportExportKeyParameters params; SecTransformRef sec_transform; CFReadStreamRef read_stream; CFDataRef dataref; CFErrorRef error; CFURLRef url; OSStatus res; if (!filename) { MONGOC_INFO ("%s", "No certificate provided"); return false; } params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION; params.flags = 0; params.passphrase = NULL; params.alertTitle = NULL; params.alertPrompt = NULL; params.accessRef = NULL; params.keyUsage = NULL; params.keyAttributes = NULL; if (passphrase) { params.passphrase = CFStringCreateWithCString (kCFAllocatorDefault, passphrase, kCFStringEncodingUTF8); } url = CFURLCreateFromFileSystemRepresentation (kCFAllocatorDefault, (const UInt8 *)filename, strlen (filename), false); read_stream = CFReadStreamCreateWithFile (kCFAllocatorDefault, url); sec_transform = SecTransformCreateReadTransformWithReadStream (read_stream); dataref = SecTransformExecute (sec_transform, &error); if (error) { CFStringRef str = CFErrorCopyDescription (error); MONGOC_ERROR ("Failed importing PEM '%s': %s", filename, CFStringGetCStringPtr (str, CFStringGetFastestEncoding(str))); CFRelease (str); CFRelease (sec_transform); CFRelease (read_stream); CFRelease (url); if (passphrase) { CFRelease (params.passphrase); } return false; } res = SecItemImport (dataref, CFSTR(".pem"), &format, type, 0, ¶ms, NULL, items); CFRelease (dataref); CFRelease (sec_transform); CFRelease (read_stream); CFRelease (url); if (passphrase) { CFRelease (params.passphrase); } if (res) { MONGOC_ERROR ("Failed importing PEM '%s' (code: %d)", filename, res); return false; } return true; }