/** * Authenticates a connection using MONGODB-CR * * Returns: * 0: when it didn't work - with the error_message set. * 1: when it worked */ int mongo_connection_authenticate_mongodb_cr(mongo_con_manager *manager, mongo_connection *con, mongo_server_options *options, char *database, char *username, char *password, char *nonce, char **error_message) { mcon_str *packet; char *salted; int length; char *hash, *key; mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "authenticate: start"); /* Calculate hash=md5(${username}:mongo:${password}) */ length = strlen(username) + 7 + strlen(password) + 1; salted = malloc(length); snprintf(salted, length, "%s:mongo:%s", username, password); hash = mongo_util_md5_hex(salted, length - 1); /* -1 to chop off \0 */ free(salted); mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "authenticate: hash=md5(%s:mongo:%s) = %s", username, password, hash); /* Calculate key=md5(${nonce}${username}${hash}) */ length = strlen(nonce) + strlen(username) + strlen(hash) + 1; salted = malloc(length); snprintf(salted, length, "%s%s%s", nonce, username, hash); key = mongo_util_md5_hex(salted, length - 1); /* -1 to chop off \0 */ free(salted); mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "authenticate: key=md5(%s%s%s) = %s", nonce, username, hash, key); /* BC: Do not send MONGODB-CR as mechanism, won't work on older mongod */ packet = bson_create_authenticate_packet(con, NULL, database, username, nonce, key); free(hash); free(key); return mongo_connection_authenticate_cmd(manager, con, options, database, username, packet, error_message); }
mongo_connection *mongo_connection_create(mongo_con_manager *manager, char *hash, mongo_server_def *server_def, mongo_server_options *options, char **error_message) { mongo_connection *tmp; /* Init struct */ tmp = malloc(sizeof(mongo_connection)); memset(tmp, 0, sizeof(mongo_connection)); tmp->last_reqid = rand(); tmp->connection_type = MONGO_NODE_STANDALONE; /* Store hash */ tmp->hash = strdup(hash); /* Connect */ mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "connection_create: creating new connection for %s:%d", server_def->host, server_def->port); tmp->socket = manager->connect(manager, server_def, options, error_message); if (!tmp->socket) { mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "connection_create: error while creating connection for %s:%d: %s", server_def->host, server_def->port, *error_message); mongo_manager_blacklist_register(manager, tmp); free(tmp->hash); free(tmp); return NULL; } /* We call get_server_flags to the maxBsonObjectSize data */ if (!mongo_connection_get_server_flags(manager, tmp, options, error_message)) { mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "server_flags: error while getting the server configuration %s:%d: %s", server_def->host, server_def->port, *error_message); free(tmp); return NULL; } return tmp; }
static mcon_collection *mongo_filter_candidates_by_replicaset_name(mongo_con_manager *manager, mcon_collection *candidates, mongo_servers *servers) { int i; mcon_collection *filtered; char *candidate_hash; char *candidate_replsetname; mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "limiting to servers with same replicaset name"); filtered = mcon_init_collection(sizeof(mongo_connection*)); for (i = 0; i < candidates->count; i++) { candidate_hash = ((mongo_connection *) candidates->data[i])->hash; mongo_server_split_hash(candidate_hash, NULL, NULL, (char**) &candidate_replsetname, NULL, NULL, NULL, NULL); /* Filter out all servers that don't have the replicaset name the same * as what we have in the server definition struct. But only when the * repl_set_name in the server definition struct is actually *set*. If * not, we allow all connections. This make sure we can sort of handle * [ replicaset => true ], although it would not support one PHP worker * process connecting to multiple replicasets correctly. */ if (candidate_replsetname) { if (!servers->repl_set_name || strcmp(candidate_replsetname, servers->repl_set_name) == 0) { mongo_print_connection_info(manager, (mongo_connection *) candidates->data[i], MLOG_FINE); mcon_collection_add(filtered, (mongo_connection *) candidates->data[i]); } free(candidate_replsetname); } } mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "limiting to servers with same replicaset name: done"); return filtered; }
/** * Sends a ping command to the server and stores the result. * * Returns 1 when it worked, and 0 when an error was encountered. */ int mongo_connection_ping(mongo_con_manager *manager, mongo_connection *con, char **error_message) { mcon_str *packet; struct timeval start, end; char *data_buffer; gettimeofday(&start, NULL); if ((con->last_ping + manager->ping_interval) > start.tv_sec) { mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "is_ping: skipping: last ran at %ld, now: %ld, time left: %ld", con->last_ping, start.tv_sec, con->last_ping + manager->ping_interval - start.tv_sec); return 2; } mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "is_ping: pinging %s", con->hash); packet = bson_create_ping_packet(con); if (!mongo_connect_send_packet(manager, con, packet, &data_buffer, error_message)) { return 0; } gettimeofday(&end, NULL); free(data_buffer); con->last_ping = end.tv_sec; con->ping_ms = (end.tv_sec - start.tv_sec) * 1000 + (end.tv_usec - start.tv_usec) / 1000; if (con->ping_ms < 0) { /* some clocks do weird stuff */ con->ping_ms = 0; } mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "is_ping: last pinged at %ld; time: %dms", con->last_ping, con->ping_ms); return 1; }
/** * Sends a ping command to the server and stores the result. * * Returns 1 when it worked, and 0 when an error was encountered. */ int mongo_connection_ping(mongo_con_manager *manager, mongo_connection *con, mongo_server_options *options, char **error_message) { mcon_str *packet; struct timeval start, end; char *data_buffer; /* If we haven't hit the ping_interval yet, then there is no need to do a roundtrip to the server */ if (!mongo_connection_ping_check(manager, con->last_ping, &start)) { return 1; } mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "is_ping: pinging %s", con->hash); packet = bson_create_ping_packet(con); if (!mongo_connect_send_packet(manager, con, options, packet, &data_buffer, error_message)) { return 0; } gettimeofday(&end, NULL); free(data_buffer); con->last_ping = end.tv_sec; con->ping_ms = (end.tv_sec - start.tv_sec) * 1000 + (end.tv_usec - start.tv_usec) / 1000; if (con->ping_ms < 0) { /* some clocks do weird stuff */ con->ping_ms = 0; } mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "is_ping: last pinged at %ld; time: %dms", con->last_ping, con->ping_ms); return 1; }
mongo_connection *mongo_connection_create(mongo_con_manager *manager, char *hash, mongo_server_def *server_def, mongo_server_options *options, char **error_message) { mongo_connection *tmp; /* Init struct */ tmp = malloc(sizeof(mongo_connection)); memset(tmp, 0, sizeof(mongo_connection)); tmp->last_reqid = rand(); tmp->connection_type = MONGO_NODE_STANDALONE; /* Default server options */ /* MongoDB pre-1.8; Spec says default to 4 MB */ tmp->max_bson_size = 4194304; /* MongoDB pre-2.4; Spec says default to 2 * the maxBsonSize */ tmp->max_message_size = 2 * tmp->max_bson_size; /* Store hash */ tmp->hash = strdup(hash); /* Connect */ mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "connection_create: creating new connection for %s:%d", server_def->host, server_def->port); tmp->socket = manager->connect(manager, server_def, options, error_message); if (!tmp->socket) { mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "connection_create: error while creating connection for %s:%d: %s", server_def->host, server_def->port, *error_message); mongo_manager_blacklist_register(manager, tmp); free(tmp->hash); free(tmp); return NULL; } return tmp; }
/* API interface to fetch a connection */ mongo_connection *mongo_get_read_write_connection(mongo_con_manager *manager, mongo_servers *servers, int connection_flags, char **error_message) { /* In some cases we won't actually have a manager or servers initialized, for example when extending PHP objects without calling the constructor, * and then var_dump() it or access the properties, for example the "connected" property */ if (!manager || !servers) { return NULL; } /* Which connection we return depends on the type of connection we want */ switch (servers->options.con_type) { case MONGO_CON_TYPE_STANDALONE: mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "mongo_get_read_write_connection: finding a STANDALONE connection"); return mongo_get_connection_multiple(manager, servers, connection_flags, error_message); case MONGO_CON_TYPE_REPLSET: mongo_manager_log( manager, MLOG_CON, MLOG_INFO, "mongo_get_read_write_connection: finding a REPLSET connection (%s)", connection_flags & MONGO_CON_FLAG_WRITE ? "write" : "read" ); return mongo_get_read_write_connection_replicaset(manager, servers, connection_flags, error_message); case MONGO_CON_TYPE_MULTIPLE: mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "mongo_get_read_write_connection: finding a MULTIPLE connection"); return mongo_get_connection_multiple(manager, servers, connection_flags, error_message); default: mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "mongo_get_read_write_connection: connection type %d is not supported", servers->options.con_type); *error_message = strdup("mongo_get_read_write_connection: Unknown connection type requested"); } return NULL; }
/** * Sends the db.authenticate() command packet * * Returns: * 0: when it didn't work - with the error_message set (extracted from the 'errmsg' field if possible) * 1: when it worked */ int mongo_connection_authenticate_cmd(mongo_con_manager *manager, mongo_connection *con, mongo_server_options *options, char *database, char *username, mcon_str *packet, char **error_message) { char *data_buffer, *errmsg; double ok; char *ptr; if (!mongo_connect_send_packet(manager, con, options, packet, &data_buffer, error_message)) { return 0; } /* Find data fields */ ptr = data_buffer + sizeof(int32_t); /* Skip the length */ /* Find errmsg */ if (bson_find_field_as_double(ptr, "ok", &ok)) { if (ok > 0) { mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "authentication successful"); } else { mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "authentication failed"); } } if (bson_find_field_as_string(ptr, "errmsg", &errmsg)) { *error_message = malloc(256); snprintf(*error_message, 256, "Authentication failed on database '%s' with username '%s': %s", database, username, errmsg); free(data_buffer); return 0; } free(data_buffer); return 1; }
/* API interface to fetch a connection */ mongo_connection *mongo_get_read_write_connection(mongo_con_manager *manager, mongo_servers *servers, int connection_flags, char **error_message) { /* Which connection we return depends on the type of connection we want */ switch (servers->options.con_type) { case MONGO_CON_TYPE_STANDALONE: mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "mongo_get_read_write_connection: finding a STANDALONE connection"); return mongo_get_connection_multiple(manager, servers, connection_flags, error_message); case MONGO_CON_TYPE_REPLSET: mongo_manager_log( manager, MLOG_CON, MLOG_INFO, "mongo_get_read_write_connection: finding a REPLSET connection (%s)", connection_flags & MONGO_CON_FLAG_WRITE ? "write" : "read" ); return mongo_get_read_write_connection_replicaset(manager, servers, connection_flags, error_message); case MONGO_CON_TYPE_MULTIPLE: mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "mongo_get_read_write_connection: finding a MULTIPLE connection"); return mongo_get_connection_multiple(manager, servers, connection_flags, error_message); default: mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "mongo_get_read_write_connection: connection type %d is not supported", servers->options.con_type); *error_message = strdup("mongo_get_read_write_connection: Unknown connection type requested"); } return NULL; }
/* Processes a single option/value pair. * Returns: * -1 if it worked, but the option really shouldn't be used * 0 if it worked * 1 if either name or value was missing * 2 if the option didn't exist * 3 on logic errors */ static int mongo_process_option(mongo_con_manager *manager, mongo_servers *servers, char *name, char *value, char *pos, char **error_message) { char *tmp_name; char *tmp_value; int retval = 0; if (!name || strcmp(name, "") == 0 || (name + 1 == value)) { *error_message = strdup("- Found an empty option name"); mongo_manager_log(manager, MLOG_PARSE, MLOG_WARN, "- Found an empty option name"); return 1; } if (!value) { *error_message = strdup("- Found an empty option value"); mongo_manager_log(manager, MLOG_PARSE, MLOG_WARN, "- Found an empty option value"); return 1; } tmp_name = mcon_strndup(name, value - name - 1); tmp_value = mcon_strndup(value, pos - value); retval = mongo_store_option(manager, servers, tmp_name, tmp_value, error_message); free(tmp_name); free(tmp_value); return retval; }
/** * Sends a ping command to the server and stores the result. * * Returns 1 when it worked, and 0 when an error was encountered. */ int mongo_connection_ping(mongo_con_manager *manager, mongo_connection *con) { mcon_str *packet; char *error_message = NULL; struct timeval start, end; char *data_buffer; mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "is_ping: start"); packet = bson_create_ping_packet(con); gettimeofday(&start, NULL); if (!mongo_connect_send_packet(manager, con, packet, &data_buffer, (char **) &error_message)) { return 0; } gettimeofday(&end, NULL); free(data_buffer); con->last_ping = end.tv_sec; con->ping_ms = (end.tv_sec - start.tv_sec) * 1000 + (end.tv_usec - start.tv_usec) / 1000; if (con->ping_ms < 0) { /* some clocks do weird stuff */ con->ping_ms = 0; } mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "is_ping: last pinged at %ld; time: %dms", con->last_ping, con->ping_ms); return 1; }
/** * Authenticates a connection * * Returns 1 when it worked, or 0 when it didn't - with the error_message set. */ int mongo_connection_authenticate(mongo_con_manager *manager, mongo_connection *con, char *database, char *username, char *password, char *nonce, char **error_message) { mcon_str *packet; char *data_buffer, *errmsg; double ok; char *ptr; char *salted; int length; char *hash, *key; mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "authenticate: start"); /* Calculate hash=md5(${username}:mongo:${password}) */ length = strlen(username) + 7 + strlen(password) + 1; salted = malloc(length); snprintf(salted, length, "%s:mongo:%s", username, password); hash = mongo_util_md5_hex(salted, length - 1); /* -1 to chop off \0 */ free(salted); mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "authenticate: hash=md5(%s:mongo:%s) = %s", username, password, hash); /* Calculate key=md5(${nonce}${username}${hash}) */ length = strlen(nonce) + strlen(username) + strlen(hash) + 1; salted = malloc(length); snprintf(salted, length, "%s%s%s", nonce, username, hash); key = mongo_util_md5_hex(salted, length - 1); /* -1 to chop off \0 */ free(salted); mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "authenticate: key=md5(%s%s%s) = %s", nonce, username, hash, key); packet = bson_create_authenticate_packet(con, database, username, nonce, key); free(hash); free(key); if (!mongo_connect_send_packet(manager, con, packet, &data_buffer, error_message)) { free(data_buffer); return 0; } /* Find data fields */ ptr = data_buffer + sizeof(int32_t); /* Skip the length */ /* Find errmsg */ if (bson_find_field_as_double(ptr, "ok", &ok)) { if (ok > 0) { mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "authentication successful"); } else { mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "authentication failed"); } } if (bson_find_field_as_string(ptr, "errmsg", &errmsg)) { *error_message = malloc(256); snprintf(*error_message, 256, "Authentication failed on database '%s' with username '%s': %s", database, username, errmsg); free(data_buffer); return 0; } free(data_buffer); return 1; }
void mongo_connection_destroy(mongo_con_manager *manager, mongo_connection *con) { int current_pid, connection_pid; int i; current_pid = getpid(); connection_pid = mongo_server_hash_to_pid(con->hash); /* Only close the connection if it matches the current PID */ if (current_pid != connection_pid) { mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "mongo_connection_destroy: The process pid (%d) for %s doesn't match the connection pid (%d).", current_pid, con->hash, connection_pid); } else { mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "mongo_connection_destroy: Closing socket for %s.", con->hash); #ifdef WIN32 shutdown(con->socket, SD_BOTH); closesocket(con->socket); WSACleanup(); #else shutdown(con->socket, SHUT_RDWR); close(con->socket); #endif for (i = 0; i < con->tag_count; i++) { free(con->tags[i]); } free(con->tags); free(con->hash); free(con); } }
/** * Sends a getnonce command to the server for authentication * * Returns the nonsense when it worked, or NULL if it didn't. */ char *mongo_connection_getnonce(mongo_con_manager *manager, mongo_connection *con, char **error_message) { mcon_str *packet; char *data_buffer; char *ptr; char *nonce; char *retval = NULL; mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "getnonce: start"); packet = bson_create_getnonce_packet(con); if (!mongo_connect_send_packet(manager, con, packet, &data_buffer, error_message)) { return NULL; } /* Find data fields */ ptr = data_buffer + sizeof(int32_t); /* Skip the length */ /* Find getnonce */ if (bson_find_field_as_string(ptr, "nonce", &nonce)) { mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "getnonce: found nonce '%s'", nonce); } else { *error_message = strdup("Couldn't find the nonce field"); free(data_buffer); return NULL; } retval = strdup(nonce); free(data_buffer); return retval; }
/* Collecting the correct servers */ static mcon_collection *filter_connections(mongo_con_manager *manager, int types, mongo_read_preference *rp) { mcon_collection *col; mongo_con_manager_item *ptr = manager->connections; int current_pid, connection_pid; current_pid = getpid(); col = mcon_init_collection(sizeof(mongo_connection*)); mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "filter_connections: adding connections:"); while (ptr) { connection_pid = mongo_server_hash_to_pid(ptr->connection->hash); if (connection_pid != current_pid) { mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "filter_connections: skipping %s as it doesn't match the current pid (%d)", ptr->connection->hash, current_pid); } else if (ptr->connection->connection_type & types) { mongo_print_connection_info(manager, ptr->connection, MLOG_FINE); mcon_collection_add(col, ptr->connection); } ptr = ptr->next; } mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "filter_connections: done"); return col; }
int mongo_connection_authenticate_saslcontinue(mongo_con_manager *manager, mongo_connection *con, mongo_server_options *options, mongo_server_def *server_def, int32_t conversation_id, char *payload, int payload_len, char **out_payload, int *out_payload_len, unsigned char *done, char **error_message) { mcon_str *packet; char *data_buffer; char *ptr; double ok; char *errmsg; int32_t out_conversation_id; mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "connection_authenticate_saslcontinue: continuing SASL authentication to '%s'", con->hash); packet = bson_create_saslcontinue_packet(con, conversation_id, payload, payload_len); if (!mongo_connect_send_packet(manager, con, options, packet, &data_buffer, error_message)) { return 0; } /* Find data fields */ ptr = data_buffer + sizeof(int32_t); /* Skip the length */ if (bson_find_field_as_double(ptr, "ok", &ok)) { if (ok > 0) { mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "SASL continue successful"); } else { mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "SASL continue failed"); if (bson_find_field_as_string(ptr, "errmsg", &errmsg)) { int errlen = strlen("SASL Authentication failed on database '': ") + strlen(server_def->db) + strlen(errmsg); *error_message = malloc(errlen); snprintf(*error_message, errlen, "SASL Authentication failed on database '%s': %s", server_def->db, errmsg); } else { int errlen = strlen("SASL Authentication failed on database ''") + strlen(server_def->db); *error_message = malloc(errlen); snprintf(*error_message, errlen, "SASL Authentication failed on database '%s'", server_def->db); } free(data_buffer); return 0; } } if (bson_find_field_as_int32(ptr, "conversationId", &out_conversation_id)) { if (out_conversation_id != conversation_id) { mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "SASL continue failed: Got wrong conversation_id back! Expected %d but got %d", conversation_id, out_conversation_id); free(data_buffer); return 0; } bson_find_field_as_stringl(ptr, "payload", out_payload, out_payload_len, 1); bson_find_field_as_bool(ptr, "done", done); } free(data_buffer); return (int)ok; }
int mongo_connection_authenticate_saslstart(mongo_con_manager *manager, mongo_connection *con, mongo_server_options *options, mongo_server_def *server_def, char *mechanism, char *payload, unsigned int payload_len, char **out_payload, int *out_payload_len, int32_t *out_conversation_id, char **error_message) { mcon_str *packet; char *data_buffer; char *ptr; char *smechanism; double ok; char *errmsg; mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "connection_authenticate_sasl: Starting SASL authentication process to '%s'", con->hash); if (server_def->mechanism == MONGO_AUTH_MECHANISM_MONGODB_CR) { *error_message = strdup("Invalid authentication mechanism. Expected SASL mechanism, got MongoDB-CR"); return 0; } packet = bson_create_saslstart_packet(con, server_def->authdb ? server_def->authdb : server_def->db, mechanism, payload, payload_len); if (!mongo_connect_send_packet(manager, con, options, packet, &data_buffer, error_message)) { return 0; } /* Find data fields */ ptr = data_buffer + sizeof(int32_t); /* Skip the length */ if (bson_find_field_as_double(ptr, "ok", &ok)) { if (ok > 0) { mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "SASL request successful"); } else { mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "SASL request failed"); if (bson_find_field_as_string(ptr, "errmsg", &errmsg)) { *error_message = malloc(256); snprintf(*error_message, 256, "SASL Authentication failed on database '%s': %s", server_def->db, errmsg); } else { *error_message = "SASL Authentication failed"; } if (bson_find_field_as_document(ptr, "supportedMechanisms", (char**) &smechanism)) { /* TODO: Retrieve a list of supportedMechanisms and return it somehow */ } free(data_buffer); return 0; } } if (bson_find_field_as_int32(ptr, "conversationId", out_conversation_id)) { bson_find_field_as_stringl(ptr, "payload", out_payload, out_payload_len, 1); } free(data_buffer); return 1; }
void mongo_connection_destroy(mongo_con_manager *manager, void *data, int why) { int current_pid, connection_pid; int i; mongo_connection *con = (mongo_connection *)data; current_pid = getpid(); connection_pid = mongo_server_hash_to_pid(con->hash); /* Only close the connection if it matches the current PID */ if (current_pid == connection_pid) { mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "mongo_connection_destroy: Destroying connection object for %s", con->hash); if (con->socket) { mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "mongo_connection_destroy: Closing socket for %s.", con->hash); manager->close(con, why); con->socket = NULL; for (i = 0; i < con->tag_count; i++) { free(con->tags[i]); } free(con->tags); if (con->cleanup_list) { mongo_connection_deregister_callback *ptr = con->cleanup_list; mongo_connection_deregister_callback *prev; do { if (ptr->callback_data) { ptr->mongo_cleanup_cb(ptr->callback_data); } if (!ptr->next) { free(ptr); ptr = NULL; break; } prev = ptr; ptr = ptr->next; free(prev); prev = NULL; } while(1); con->cleanup_list = NULL; } free(con->hash); free(con); } } else { mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "mongo_connection_destroy: The process pid (%d) for %s doesn't match the connection pid (%d).", current_pid, con->hash, connection_pid); } }
void mongo_connection_destroy(mongo_con_manager *manager, mongo_connection *con) { int current_pid, connection_pid; int i; current_pid = getpid(); connection_pid = mongo_server_hash_to_pid(con->hash); /* Only close the connection if it matches the current PID */ if (current_pid != connection_pid) { mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "mongo_connection_destroy: The process pid (%d) for %s doesn't match the connection pid (%d).", current_pid, con->hash, connection_pid); } else { mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "mongo_connection_destroy: Closing socket for %s.", con->hash); #ifdef WIN32 shutdown(con->socket, SD_BOTH); closesocket(con->socket); WSACleanup(); #else shutdown(con->socket, SHUT_RDWR); close(con->socket); #endif for (i = 0; i < con->tag_count; i++) { free(con->tags[i]); } if (con->cleanup_list) { mongo_connection_deregister_callback *ptr = con->cleanup_list; mongo_connection_deregister_callback *prev; do { if (ptr->callback_data) { ptr->mongo_cleanup_cb(ptr->callback_data); } if (!ptr->next) { free(ptr); ptr = NULL; break; } prev = ptr; ptr = ptr->next; free(prev); prev = NULL; } while(1); con->cleanup_list = NULL; } free(con->tags); free(con->hash); free(con); } }
static mcon_collection *mongo_filter_candidates_by_credentials(mongo_con_manager *manager, mcon_collection *candidates, mongo_servers *servers) { int i; char *db, *username, *auth_hash, *hashed = NULL; mcon_collection *filtered; mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "limiting by credentials"); filtered = mcon_init_collection(sizeof(mongo_connection*)); for (i = 0; i < candidates->count; i++) { db = username = auth_hash = hashed = NULL; mongo_server_split_hash(((mongo_connection *) candidates->data[i])->hash, NULL, NULL, NULL, &db, &username, &auth_hash, NULL); if (servers->server[0]->username && servers->server[0]->password && servers->server[0]->db) { if (strcmp(db, servers->server[0]->db) != 0) { mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "- skipping '%s', database didn't match ('%s' vs '%s')", ((mongo_connection *) candidates->data[i])->hash, db, servers->server[0]->db); goto skip; } if (strcmp(username, servers->server[0]->username) != 0) { mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "- skipping '%s', username didn't match ('%s' vs '%s')", ((mongo_connection *) candidates->data[i])->hash, username, servers->server[0]->username); goto skip; } hashed = mongo_server_create_hashed_password(username, servers->server[0]->password); if (strcmp(auth_hash, hashed) != 0) { mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "- skipping '%s', authentication hash didn't match ('%s' vs '%s')", ((mongo_connection *) candidates->data[i])->hash, auth_hash, hashed); goto skip; } } mcon_collection_add(filtered, (mongo_connection *) candidates->data[i]); mongo_print_connection_info(manager, (mongo_connection *) candidates->data[i], MLOG_FINE); skip: if (db) { free(db); } if (username) { free(username); } if (auth_hash) { free(auth_hash); } if (hashed) { free(hashed); } } mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "limiting by credentials: done"); return filtered; }
static void mongo_print_connection_info(mongo_con_manager *manager, mongo_connection *con, int level) { int i; mongo_manager_log(manager, MLOG_RS, level, "- connection: type: %s, socket: %d, ping: %d, hash: %s", mongo_connection_type(con->connection_type), 42, /* FIXME: STREAMS: Maybe we do need a union here: con->socket, */ con->ping_ms, con->hash ); for (i = 0; i < con->tag_count; i++) { mongo_manager_log(manager, MLOG_RS, level, " - tag: %s", con->tags[i] ); } }
static void mongo_print_connection_info(mongo_con_manager *manager, mongo_connection *con, int level) { int i; mongo_manager_log(manager, MLOG_RS, level, "- connection: type: %s, socket: %d, ping: %d, hash: %s", mongo_connection_type(con->connection_type), con->socket, con->ping_ms, con->hash ); for (i = 0; i < con->tag_count; i++) { mongo_manager_log(manager, MLOG_RS, level, " - tag: %s", con->tags[i] ); } }
int mongo_connection_ping_check(mongo_con_manager *manager, int last_ping, struct timeval *start) { gettimeofday(start, NULL); if ((last_ping + manager->ping_interval) > start->tv_sec) { mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "is_ping: skipping: last ran at %ld, now: %ld, time left: %ld", last_ping, start->tv_sec, last_ping + manager->ping_interval - start->tv_sec); return 0; } return 1; }
/* Collecting the correct servers */ static mcon_collection *filter_connections(mongo_con_manager *manager, int types, mongo_read_preference *rp) { mcon_collection *col; mongo_con_manager_item *ptr = manager->connections; col = mcon_init_collection(sizeof(mongo_connection*)); mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "filter_connections: adding connections:"); while (ptr) { if (ptr->connection->connection_type & types) { mongo_print_connection_info(manager, ptr->connection, MLOG_FINE); mcon_collection_add(col, ptr->connection); } ptr = ptr->next; } mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "filter_connections: done"); return col; }
mcon_collection* mongo_find_candidate_servers(mongo_con_manager *manager, mongo_read_preference *rp, mongo_servers *servers) { int i; mcon_collection *all, *filtered; mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "finding candidate servers"); all = mongo_find_all_candidate_servers(manager, rp); if (servers->con_type == MONGO_CON_TYPE_REPLSET) { filtered = mongo_filter_candidates_by_replicaset_name(manager, all, servers); } else { filtered = mongo_filter_candidates_by_seed(manager, all, servers); } mcon_collection_free(all); all = filtered; filtered = mongo_filter_candidates_by_credentials(manager, all, servers); mcon_collection_free(all); all = filtered; if (rp->tagset_count != 0) { mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "limiting by tagsets"); /* If we have tagsets configured for the replicaset then we need to do * some more filtering */ for (i = 0; i < rp->tagset_count; i++) { char *tmp_ts = mongo_read_preference_squash_tagset(rp->tagsets[i]); mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "checking tagset: %s", tmp_ts); filtered = mongo_filter_candidates_by_tagset(manager, all, rp->tagsets[i]); mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "tagset %s matched %d candidates", tmp_ts, filtered->count); free(tmp_ts); if (filtered->count > 0) { mcon_collection_free(all); return filtered; } } mcon_collection_free(filtered); mcon_collection_free(all); return NULL; } else { return all; } }
mongo_connection *mongo_pick_server_from_set(mongo_con_manager *manager, mcon_collection *col, mongo_read_preference *rp) { mongo_connection *con = NULL; int entry = rand() % col->count; if (rp->type == MONGO_RP_PRIMARY_PREFERRED) { if (((mongo_connection*)col->data[0])->connection_type == MONGO_NODE_PRIMARY) { mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "pick server: the primary"); con = (mongo_connection*)col->data[0]; mongo_print_connection_info(manager, con, MLOG_INFO); return con; } } /* For now, we just pick a random server from the set */ mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "pick server: random element %d", entry); con = (mongo_connection*)col->data[entry]; mongo_print_connection_info(manager, con, MLOG_INFO); return con; }
/** * Authenticates a connection using MONGODB-X509 * * Returns: * 0: when it didn't work - with the error_message set. * 1: when it worked */ int mongo_connection_authenticate_mongodb_x509(mongo_con_manager *manager, mongo_connection *con, mongo_server_options *options, char *database, char *username, char **error_message) { mcon_str *packet; mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "authenticate (X509): start"); packet = bson_create_authenticate_packet(con, "MONGODB-X509", database, username, NULL, NULL); return mongo_connection_authenticate_cmd(manager, con, options, database, username, packet, error_message); }
static mongo_connection *mongo_get_read_write_connection_replicaset(mongo_con_manager *manager, mongo_servers *servers, int connection_flags, char **error_message) { mongo_connection *con = NULL; mongo_connection *tmp; mcon_collection *collection; char *con_error_message = NULL; int i; int found_connected_server = 0; /* Create a connection to all of the servers in the seed list */ for (i = 0; i < servers->count; i++) { tmp = mongo_get_connection_single(manager, servers->server[i], &servers->options, connection_flags, (char **) &con_error_message); if (tmp) { found_connected_server = 1; } else if (!(connection_flags & MONGO_CON_FLAG_DONT_CONNECT)) { mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "Couldn't connect to '%s:%d': %s", servers->server[i]->host, servers->server[i]->port, con_error_message); free(con_error_message); } } if (!found_connected_server && (connection_flags & MONGO_CON_FLAG_DONT_CONNECT)) { return NULL; } /* Discover more nodes. This also adds a connection to "servers" for each * new node */ mongo_discover_topology(manager, servers); /* Depending on whether we want a read or a write connection, run the correct algorithms */ if (connection_flags & MONGO_CON_FLAG_WRITE) { mongo_read_preference tmp_rp; mongo_read_preference_copy(&servers->read_pref, &tmp_rp); tmp_rp.type = MONGO_RP_PRIMARY; collection = mongo_find_candidate_servers(manager, &tmp_rp, servers); mongo_read_preference_dtor(&tmp_rp); } else { collection = mongo_find_candidate_servers(manager, &servers->read_pref, servers); } if (!collection) { *error_message = strdup("No candidate servers found"); return NULL; } if (collection->count == 0) { *error_message = strdup("No candidate servers found"); mcon_collection_free(collection); return NULL; } collection = mongo_sort_servers(manager, collection, &servers->read_pref); collection = mongo_select_nearest_servers(manager, collection, &servers->read_pref); con = mongo_pick_server_from_set(manager, collection, &servers->read_pref); /* Cleaning up */ mcon_collection_free(collection); return con; }
/* Option parser helpers */ static int parse_read_preference_tags(mongo_con_manager *manager, mongo_servers *servers, char *value, char **error_message) { mongo_read_preference_tagset *tmp_ts = calloc(1, sizeof(mongo_read_preference_tagset)); /* format = dc:ny,rack:1 - empty is allowed! */ if (strlen(value) == 0) { mongo_read_preference_add_tagset(&servers->read_pref, tmp_ts); } else { char *start, *end, *colon, *tmp_name, *tmp_value; start = value; while (1) { end = strchr(start, ','); colon = strchr(start, ':'); if (!colon) { int len = strlen(start) + sizeof("Error while trying to parse tags: No separator for ''"); *error_message = malloc(len + 1); snprintf(*error_message, len, "Error while trying to parse tags: No separator for '%s'", start); mongo_read_preference_tagset_dtor(tmp_ts); return 3; } tmp_name = mcon_strndup(start, colon - start); if (end) { tmp_value = mcon_strndup(colon + 1, end - colon - 1); start = end + 1; mongo_manager_log(manager, MLOG_PARSE, MLOG_INFO, "- Found tag '%s': '%s'", tmp_name, tmp_value); mongo_read_preference_add_tag(tmp_ts, tmp_name, tmp_value); free(tmp_value); free(tmp_name); } else { mongo_manager_log(manager, MLOG_PARSE, MLOG_INFO, "- Found tag '%s': '%s'", tmp_name, colon + 1); mongo_read_preference_add_tag(tmp_ts, tmp_name, colon + 1); free(tmp_name); break; } } mongo_read_preference_add_tagset(&servers->read_pref, tmp_ts); } return 0; }
void *mongo_manager_find_by_hash(mongo_con_manager *manager, mongo_con_manager_item *ptr, char *hash) { while (ptr) { if (strcmp(ptr->hash, hash) == 0) { mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "found connection %s (looking for %s)", ptr->hash, hash); return ptr->data; } ptr = ptr->next; } return NULL; }