/**
 * 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);
}
Example #2
0
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;
}
Example #4
0
/**
 * 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;
}
Example #5
0
/**
 * 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;
}
Example #6
0
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;
}
Example #7
0
/* 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;
}
Example #9
0
/* 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;
}
Example #10
0
/* 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;
}
Example #12
0
/**
 * 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);
	}
}
Example #14
0
/**
 * 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;
}
Example #18
0
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);
	}
}
Example #19
0
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]
		);
	}
}
Example #23
0
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);
}
Example #28
0
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;
}
Example #29
0
/* 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;
}
Example #30
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;
}