int main(void)
{
	char *hash1, *hash2;
	char *host, *db, *username, *auth_hash;
	int port, pid;

	mongo_server_def def1 = { "whisky", 13000, NULL, NULL, NULL };
	mongo_server_def def2 = { "whisky", 13000, "phpunit", "derick", "not!" };

	hash1 = mongo_server_create_hash(&def1);
	mongo_server_split_hash(hash1, &host, &port, &db, &username, &auth_hash, &pid);
	printf("HASH: %s; host: %s, port: %d, db: %s, username: %s, auth_hash: %s, pid: %d\n",
		hash1, host, port, db, username, auth_hash, pid);
	free(host);
	free(hash1);

	hash2 = mongo_server_create_hash(&def2);

	mongo_server_split_hash(hash2, &host, &port, &db, &username, &auth_hash, &pid);
	printf("HASH: %s; host: %s, port: %d, db: %s, username: %s, auth_hash: %s, pid: %d\n",
		hash2, host, port, db, username, auth_hash, pid);
	free(host); free(db); free(username); free(auth_hash);

	host = db = username = auth_hash = NULL; port = pid = 0;
	mongo_server_split_hash(hash2, &host, &port, NULL, &username, &auth_hash, &pid);
	printf("HASH: %s; host: %s, port: %d, db: %s, username: %s, auth_hash: %s, pid: %d\n",
		hash2, host, port, db, username, auth_hash, pid);
	free(host); free(username); free(auth_hash);

	host = db = username = auth_hash = NULL; port = pid = 0;
	mongo_server_split_hash(hash2, &host, &port, &db, NULL, &auth_hash, &pid);
	printf("HASH: %s; host: %s, port: %d, db: %s, username: %s, auth_hash: %s, pid: %d\n",
		hash2, host, port, db, username, auth_hash, pid);
	free(host); free(db); free(auth_hash);

	host = db = username = auth_hash = NULL; port = pid = 0;
	mongo_server_split_hash(hash2, &host, &port, &db, NULL, NULL, &pid);
	printf("HASH: %s; host: %s, port: %d, db: %s, username: %s, auth_hash: %s, pid: %d\n",
		hash2, host, port, db, username, auth_hash, pid);
	free(host); free(db);

	host = db = username = auth_hash = NULL; port = pid = 0;
	mongo_server_split_hash(hash2, &host, &port, NULL, &username, NULL, &pid);
	printf("HASH: %s; host: %s, port: %d, db: %s, username: %s, auth_hash: %s, pid: %d\n",
		hash2, host, port, db, username, auth_hash, pid);
	free(host); free(username);

	free(hash2);

	return 0;
}
示例#2
0
mongo_connection *mongo_manager_connection_find_by_server_definition(mongo_con_manager *manager, mongo_server_def *definition)
{
	char *hash = mongo_server_create_hash(definition);
	mongo_connection *con = mongo_manager_find_by_hash(manager, manager->connections, hash);

	free(hash);
	return con;
}
static mcon_collection *mongo_filter_candidates_by_seed(mongo_con_manager *manager, mcon_collection *candidates, mongo_servers *servers)
{
	int              i, j;
	mcon_collection *filtered;
	char            *server_hash;

	mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "limiting by seeded/discovered servers");
	filtered = mcon_init_collection(sizeof(mongo_connection*));

	for (j = 0; j < servers->count; j++) {
		server_hash = mongo_server_create_hash(servers->server[j]);
		for (i = 0; i < candidates->count; i++) {
			if (strcmp(((mongo_connection *) candidates->data[i])->hash, server_hash) == 0) {
				mongo_print_connection_info(manager, (mongo_connection *) candidates->data[i], MLOG_FINE);
				mcon_collection_add(filtered, (mongo_connection *) candidates->data[i]);
			}
		}
		free(server_hash);
	}

	mongo_manager_log(manager, MLOG_RS, MLOG_FINE, "limiting by seeded/discovered servers: done");

	return filtered;
}
示例#4
0
/* Helpers */
static mongo_connection *mongo_get_connection_single(mongo_con_manager *manager, mongo_server_def *server, mongo_server_options *options, int connection_flags, char **error_message)
{
	char *hash;
	mongo_connection *con = NULL;
	mongo_connection_blacklist *blacklist = NULL;

	hash = mongo_server_create_hash(server);

	/* See if a connection is in our blacklist to short-circut trying to
	 * connect to a node that is known to be down. This is done so we don't
	 * waste precious time in connecting to unreachable nodes */
	blacklist = mongo_manager_blacklist_find_by_hash(manager, hash);
	if (blacklist) {
		struct timeval start;
		/* It is blacklisted, but it may have been a long time again and
		 * chances are we should give it another try */
		if (mongo_connection_ping_check(manager, blacklist->last_ping, &start)) {
			/* The connection is blacklisted, but we've reached our ping
			 * interval so lets remove the blacklisting and pretend we didn't
			 * know about it */
			mongo_manager_blacklist_deregister(manager, blacklist, hash);
		} else {
			/* Otherwise short-circut the connection attempt, and say we failed
			 * right away */
			free(hash);
			*error_message = strdup("Previous connection attempts failed, server blacklisted");
			return NULL;
		}
	}

	con = mongo_manager_connection_find_by_hash(manager, hash);

	/* If we aren't about to (re-)connect then all we care about if it was a
	 * known connection or not */
	if (connection_flags & MONGO_CON_FLAG_DONT_CONNECT) {
		free(hash);
		return con;
	}

	/* If we found a valid connection check if we need to ping it */
	if (con) {
		/* Do the ping, if needed */
		if (!mongo_connection_ping(manager,  con, options, error_message)) {
			/* If the ping failed, deregister the connection */
			mongo_manager_connection_deregister(manager, con);
			/* Set the return value to NULL, as the connection is broken and
			 * has been removed */
			con = NULL;
		}

		free(hash);
		return con;
	}

	/* Since we didn't find an existing connection, lets make one! */
	con = mongo_connection_create(manager, hash, server, options, error_message);
	if (con) {
		/* When we make a connection, we need to figure out the server version it is */
		if (!mongo_connection_get_server_version(manager, con, options, error_message)) {
			mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "server_version: error while getting the server version %s:%d: %s", server->host, server->port, *error_message);
			mongo_connection_destroy(manager, con, MONGO_CLOSE_BROKEN);
			free(hash);
			return NULL;
		}

		/* Do authentication if requested */
		/* Note: Arbiters don't contain any data, including auth stuff, so you cannot authenticate on an arbiter */
		if (con->connection_type != MONGO_NODE_ARBITER) {
			if (!manager->authenticate(manager, con, options, server, error_message)) {
				mongo_connection_destroy(manager, con, MONGO_CLOSE_BROKEN);
				free(hash);
				return NULL;
			}
		}

		/* Do the first-time ping to record the latency of the connection */
		if (mongo_connection_ping(manager, con, options, error_message)) {
			/* Register the connection on successful pinging */
			mongo_manager_connection_register(manager, con);
		} else {
			/* Or kill it and reset the return value if the ping somehow failed */
			mongo_connection_destroy(manager, con, MONGO_CLOSE_BROKEN);
			con = NULL;
		}
	}

	free(hash);
	return con;
}
示例#5
0
/* Returns:
 * 1 on success
 * 0 on total failure (e.g. unsupported wire version) */
static int mongo_discover_topology(mongo_con_manager *manager, mongo_servers *servers)
{
	int i, j;
	char *hash;
	mongo_connection *con;
	char *error_message;
	char *repl_set_name = servers->options.repl_set_name ? strdup(servers->options.repl_set_name) : NULL;
	int nr_hosts;
	char **found_hosts = NULL;
	char *tmp_hash;
	int   res;
	int found_supported_wire_version = 1; /* Innocent unless proven guilty */

	for (i = 0; i < servers->count; i++) {
		hash = mongo_server_create_hash(servers->server[i]);
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "discover_topology: checking ismaster for %s", hash);
		con = mongo_manager_connection_find_by_hash(manager, hash);

		if (!con) {
			mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "discover_topology: couldn't create a connection for %s", hash);
			free(hash);
			continue;
		}
		
		/* Run ismaster, if needed, to extract server flags - and fetch the other known hosts */
		res = mongo_connection_ismaster(manager, con, &servers->options, (char**) &repl_set_name, (int*) &nr_hosts, (char***) &found_hosts, (char**) &error_message, servers->server[i]);
		switch (res) {
			case 4:
				/* The server is running unsupported wire versions */
				found_supported_wire_version = 0;
				/* break omitted intentionally */
			case 0:
				/* Something is wrong with the connection, we need to remove
				 * this from our list */
				mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "discover_topology: ismaster return with an error for %s:%d: [%s]", servers->server[i]->host, servers->server[i]->port, error_message);
				free(error_message);
				mongo_manager_connection_deregister(manager, con);
				break;

			case 3:
				mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "discover_topology: ismaster worked, but we need to remove the seed host's connection");
				mongo_manager_connection_deregister(manager, con);
				/* Break intentionally missing */

			case 1:
				mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "discover_topology: ismaster worked");
				/* Update the replica set name in the parsed "servers" struct
				 * so that we can consistently compare it to the information
				 * that is stored in the connection hashes. */
				if (!servers->options.repl_set_name && repl_set_name) {
					servers->options.repl_set_name = strdup(repl_set_name);
				}

				/* Now loop over all the hosts that were found */
				for (j = 0; j < nr_hosts; j++) {
					mongo_server_def *tmp_def;
					mongo_connection *new_con;
					char *con_error_message = NULL;

					/* Create a temp server definition to create a new
					 * connection on-demand if we didn't have one already */
					tmp_def = calloc(1, sizeof(mongo_server_def));
					tmp_def->username = servers->server[i]->username ? strdup(servers->server[i]->username) : NULL;
					tmp_def->password = servers->server[i]->password ? strdup(servers->server[i]->password) : NULL;
					tmp_def->repl_set_name = servers->server[i]->repl_set_name ? strdup(servers->server[i]->repl_set_name) : NULL;
					tmp_def->db = servers->server[i]->db ? strdup(servers->server[i]->db) : NULL;
					tmp_def->authdb = servers->server[i]->authdb ? strdup(servers->server[i]->authdb) : NULL;
					tmp_def->host = mcon_strndup(found_hosts[j], strchr(found_hosts[j], ':') - found_hosts[j]);
					tmp_def->port = atoi(strchr(found_hosts[j], ':') + 1);
					tmp_def->mechanism = servers->server[i]->mechanism;
					
					/* Create a hash so that we can check whether we already have a
					 * connection for this server definition. If we don't create
					 * the connection, register it (done in
					 * mongo_get_connection_single) and add it to the list of
					 * servers that we're processing so we might use this host to
					 * find more servers. */
					tmp_hash = mongo_server_create_hash(tmp_def);
					if (!mongo_manager_connection_find_by_hash(manager, tmp_hash)) {
						mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "discover_topology: found new host: %s:%d", tmp_def->host, tmp_def->port);
						new_con = mongo_get_connection_single(manager, tmp_def, &servers->options, MONGO_CON_FLAG_WRITE, (char **) &con_error_message);

						if (new_con) {
							int ismaster_error = mongo_connection_ismaster(manager, new_con, &servers->options, NULL, NULL, NULL, &con_error_message, NULL);

							switch (ismaster_error) {
								case 1: /* Run just fine */
								case 2: /* ismaster() skipped due to interval */
									break;

								case 4: /* Danger danger, reported wire version does not overlap what we support */
									found_supported_wire_version = 0;
									/* break omitted intentionally */

								case 0: /* Some error */
								case 3: /* Run just fine, but hostname didn't match what we expected */
								default:

									mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "server_flags: error while getting the server configuration %s:%d: %s", servers->server[i]->host, servers->server[i]->port, con_error_message);
									mongo_manager_connection_deregister(manager, new_con);
									new_con = NULL;
							}
						}

						if (new_con) {
							servers->server[servers->count] = tmp_def;
							servers->count++;
						} else {
							mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "discover_topology: could not connect to new host: %s:%d: %s", tmp_def->host, tmp_def->port, con_error_message);
							free(con_error_message);
						}
					} else {
						mongo_server_def_dtor(tmp_def);
					}
					free(tmp_hash);

					/* Cleanup */
					free(found_hosts[j]);
				}
				free(found_hosts);
				found_hosts = NULL;
				break;

			case 2:
				mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "discover_topology: ismaster got skipped");
				break;
		}

		free(hash);
	}
	if (repl_set_name) {
		free(repl_set_name);
	}

	return found_supported_wire_version;
}
示例#6
0
void* php_mongo_io_stream_connect(mongo_con_manager *manager, mongo_server_def *server, mongo_server_options *options, char **error_message)
{
	char *errmsg;
	int errcode;
	php_stream *stream;
	char *hash = mongo_server_create_hash(server);
	struct timeval ctimeout = {0, 0};
	char *dsn;
	int dsn_len;
	int tcp_socket = 1;
	ERROR_HANDLER_DECLARATION(error_handler)

	TSRMLS_FETCH();

	if (server->host[0] == '/') {
		dsn_len = spprintf(&dsn, 0, "unix://%s", server->host);
		tcp_socket = 0;
	} else {
		dsn_len = spprintf(&dsn, 0, "tcp://%s:%d", server->host, server->port);
	}


	/* Connection timeout behavior varies based on the following:
	 * - Negative => no timeout (i.e. block indefinitely)
	 * - Zero => not specified (PHP will use default_socket_timeout)
	 * - Positive => used specified timeout */
	if (options->connectTimeoutMS) {
		/* Convert negative value to -1 second, which implies no timeout */
		int connectTimeoutMS = options->connectTimeoutMS < 0 ? -1000 : options->connectTimeoutMS;

		ctimeout.tv_sec = connectTimeoutMS / 1000;
		ctimeout.tv_usec = (connectTimeoutMS % 1000) * 1000;
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "Connecting to %s (%s) with connection timeout: %d.%06d", dsn, hash, ctimeout.tv_sec, ctimeout.tv_usec);
	} else {
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "Connecting to %s (%s) without connection timeout (default_socket_timeout will be used)", dsn, hash);
	}

	ERROR_HANDLER_REPLACE(error_handler, mongo_ce_ConnectionException);
	stream = php_stream_xport_create(dsn, dsn_len, 0, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, hash, options->connectTimeoutMS > 0 ? &ctimeout : NULL, (php_stream_context *)options->ctx, &errmsg, &errcode);
	ERROR_HANDLER_RESTORE(error_handler);

	efree(dsn);
	free(hash);

	if (!stream) {
		/* error_message will be free()d, but errmsg was allocated by PHP and needs efree() */
		*error_message = strdup(errmsg);
		efree(errmsg);
		return NULL;
	}

	if (tcp_socket) {
		int socket = ((php_netstream_data_t*)stream->abstract)->socket;
		int flag = 1;

		setsockopt(socket, IPPROTO_TCP,  TCP_NODELAY, (char *) &flag, sizeof(int));
	}

	if (options->ssl) {
		int crypto_enabled;

		ERROR_HANDLER_REPLACE(error_handler, mongo_ce_ConnectionException);

		if (php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL TSRMLS_CC) < 0) {
			ERROR_HANDLER_RESTORE(error_handler);
			*error_message = strdup("Cannot setup SSL, is ext/openssl loaded?");
			php_stream_close(stream);
			return NULL;
		}

		crypto_enabled = php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC);
		ERROR_HANDLER_RESTORE(error_handler);

		if (crypto_enabled < 0) {
			/* Setting up crypto failed. Thats only OK if we only preferred it */
			if (options->ssl == MONGO_SSL_PREFER) {
				/* FIXME: We can't actually get here because we reject setting
				 * this option to prefer in mcon/parse.c. This is however
				 * probably what we need to do in the future when mongod starts
				 * actually supporting this! :) */
				mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "stream_connect: Failed establishing SSL for %s:%d", server->host, server->port);
				php_stream_xport_crypto_enable(stream, 0 TSRMLS_CC);
			} else {
				*error_message = strdup("Can't connect over SSL, is mongod running with SSL?");
				php_stream_close(stream);
				return NULL;
			}
		} else {
			mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "stream_connect: Establish SSL for %s:%d", server->host, server->port);
		}
	} else {
		mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "stream_connect: Not establishing SSL for %s:%d", server->host, server->port);
	}

	/* Socket timeout behavior uses the same logic as connectTimeoutMS */
	if (options->socketTimeoutMS) {
		struct timeval rtimeout = {0, 0};

		/* Convert negative value to -1 second, which implies no timeout */
		int socketTimeoutMS = options->socketTimeoutMS < 0 ? -1000 : options->socketTimeoutMS;

		rtimeout.tv_sec = socketTimeoutMS / 1000;
		rtimeout.tv_usec = (socketTimeoutMS % 1000) * 1000;
		php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &rtimeout);
		mongo_manager_log(MonGlo(manager), MLOG_CON, MLOG_FINE, "Setting stream timeout to %d.%06d", rtimeout.tv_sec, rtimeout.tv_usec);
	}


	/* Avoid a weird leak warning in debug mode when freeing the stream */
#if ZEND_DEBUG
	stream->__exposed = 1;
#endif

	return stream;

}
示例#7
0
void* php_mongo_io_stream_connect(mongo_con_manager *manager, mongo_server_def *server, mongo_server_options *options, char **error_message)
{
	char *errmsg;
	int errcode;
	php_stream *stream;
	char *hash = mongo_server_create_hash(server);
	struct timeval ctimeout = {0};
	char *dsn;
	int dsn_len;
	TSRMLS_FETCH();

	dsn_len = spprintf(&dsn, 0, "tcp://%s:%d", server->host, server->port);

	if (options->connectTimeoutMS) {
		ctimeout.tv_sec = options->connectTimeoutMS / 1000;
		ctimeout.tv_usec = (options->connectTimeoutMS % 1000) * 1000;
	}

	stream = php_stream_xport_create(dsn, dsn_len, 0, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, hash, options->connectTimeoutMS ? &ctimeout : NULL, (php_stream_context *)options->ctx, &errmsg, &errcode);
	efree(dsn);
	free(hash);

	if (!stream) {
		/* error_message will be free()d, but errmsg was allocated by PHP and needs efree() */
		*error_message = strdup(errmsg);
		efree(errmsg);
		return NULL;
	}

	if (options->ssl) {
		if (
			(php_stream_xport_crypto_setup(stream, STREAM_CRYPTO_METHOD_TLS_CLIENT, NULL TSRMLS_CC) < 0) ||
			(php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0))
		{
			/* Setting up crypto failed. Thats only OK if we only preferred it */
			if (options->ssl == MONGO_SSL_PREFER) {
				/* FIXME: We can't actually get here because we reject setting this optino to prefer in mcon/parse.c
				 * This is however probably what we need to do in the future when mongod starts actually supporting this! :)
				 */
				mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "stream_connect: Failed establishing SSL for %s:%d", server->host, server->port);
				php_stream_xport_crypto_enable(stream, 0 TSRMLS_CC);
			} else {
				*error_message = strdup("Can't connect over SSL, is mongod running with SSL?");
				php_stream_close(stream);
				return NULL;
			}
		} else {
			mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "stream_connect: Establish SSL for %s:%d", server->host, server->port);
		}
	} else {
		mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "stream_connect: Not establishing SSL for %s:%d", server->host, server->port);
	}

	php_stream_notify_progress_init(stream->context, 0, 0);

	if (options->socketTimeoutMS) {
		struct timeval rtimeout = {0};
		rtimeout.tv_sec = options->socketTimeoutMS / 1000;
		rtimeout.tv_usec = (options->socketTimeoutMS % 1000) * 1000;
		php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &rtimeout);
	}


	/* Avoid a weird leak warning in debug mode when freeing the stream */
#if ZEND_DEBUG
	stream->__exposed = 1;
#endif
	return stream;

}
示例#8
0
static mongo_connection *mongo_get_connection_single(mongo_con_manager *manager, mongo_server_def *server, mongo_server_options *options, int connection_flags, char **error_message)
{
	char *hash;
	mongo_connection *con = NULL;
	mongo_connection_blacklist *blacklist = NULL;

	hash = mongo_server_create_hash(server);

	/* See if a connection is in our blacklist to short-circut trying to connect
	 * to a node that is known to be down. This is done so we don't waste
	 * precious time in connecting to unreachable nodes */
	blacklist = mongo_manager_blacklist_find_by_hash(manager, hash);
	if (blacklist) {
		struct timeval start;
		/* It is blacklisted, but it may have been a long time again and chances are
		 * we should give it another try */
		if (mongo_connection_ping_check(manager, blacklist->last_ping, &start)) {
			/* The connection is blacklisted, but we've reached our ping interval
			 * so lets remove the blacklisting and pretend we didn't know about it
			 */
			mongo_manager_blacklist_deregister(manager, blacklist, hash);
			con = NULL;
		} else {
			/* Otherwise short-circut the connection attempt, and say we failed right away */
			free(hash);
			return NULL;
		}
	}

	con = mongo_manager_connection_find_by_hash(manager, hash);

	/* If we aren't about to (re-)connect then all we care about if it was a known connection or not */
	if (connection_flags & MONGO_CON_FLAG_DONT_CONNECT) {
		free(hash);
		return con;
	}

	/* If we found a valid connection check if we need to ping it */
	if (con) {
		/* Do the ping, if needed */
		if (!mongo_connection_ping(manager,  con, options, error_message)) {
			/* If the ping failed, deregister the connection */
			mongo_manager_connection_deregister(manager, con);
			/* Set the return value to NULL, as the connection is broken and has been removed */
			con = NULL;
		}

		free(hash);
		return con;
	}

	/* Since we didn't find an existing connection, lets make one! */
	con = mongo_connection_create(manager, hash, server, options, error_message);
	if (con) {
		/* Do authentication if requested */
		if (server->db && server->username && server->password) {
			mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "get_connection_single: authenticating %s", hash);
			if (!authenticate_connection(manager, con, options, server->authdb ? server->authdb : server->db, server->username, server->password, error_message)) {
				mongo_connection_destroy(manager, con, MONGO_CLOSE_BROKEN);
				free(hash);
				return NULL;
			}
		}
		/* Do the first-time ping to record the latency of the connection */
		if (mongo_connection_ping(manager, con, options, error_message)) {
			/* Register the connection on successful pinging */
			mongo_manager_connection_register(manager, con);
		} else {
			/* Or kill it and reset the return value if the ping somehow failed */
			mongo_connection_destroy(manager, con, MONGO_CLOSE_BROKEN);
			con = NULL;
		}
	}

	free(hash);
	return con;
}
示例#9
0
/* - Helpers */
static void mongo_discover_topology(mongo_con_manager *manager, mongo_servers *servers)
{
	int i, j;
	char *hash;
	mongo_connection *con;
	char *error_message;
	char *repl_set_name = servers->options.repl_set_name ? strdup(servers->options.repl_set_name) : NULL;
	int nr_hosts;
	char **found_hosts = NULL;
	char *tmp_hash;
	int   res;

	for (i = 0; i < servers->count; i++) {
		hash = mongo_server_create_hash(servers->server[i]);
		mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "discover_topology: checking ismaster for %s", hash);
		con = mongo_manager_connection_find_by_hash(manager, hash);

		if (!con) {
			mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "discover_topology: couldn't create a connection for %s", hash);
			free(hash);
			continue;
		}
		
		res = mongo_connection_ismaster(manager, con, &servers->options, (char**) &repl_set_name, (int*) &nr_hosts, (char***) &found_hosts, (char**) &error_message, servers->server[i]);
		switch (res) {
			case 0:
				/* Something is wrong with the connection, we need to remove
				 * this from our list */
				mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "discover_topology: ismaster return with an error for %s:%d: [%s]", servers->server[i]->host, servers->server[i]->port, error_message);
				free(error_message);
				mongo_manager_connection_deregister(manager, con);
				break;

			case 3:
				mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "discover_topology: ismaster worked, but we need to remove the seed host's connection");
				mongo_manager_connection_deregister(manager, con);
				/* Break intentionally missing */

			case 1:
				mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "discover_topology: ismaster worked");
				/* Update the replica set name in the parsed "servers" struct
				 * so that we can consistently compare it to the information
				 * that is stored in the connection hashes. */
				if (!servers->options.repl_set_name && repl_set_name) {
					servers->options.repl_set_name = strdup(repl_set_name);
				}

				/* Now loop over all the hosts that were found */
				for (j = 0; j < nr_hosts; j++) {
					mongo_server_def *tmp_def;
					mongo_connection *new_con;
					char *con_error_message = NULL;

					/* Create a temp server definition to create a new connection on-demand if we didn't have one already */
					tmp_def = calloc(1, sizeof(mongo_server_def));
					tmp_def->username = servers->server[i]->username ? strdup(servers->server[i]->username) : NULL;
					tmp_def->password = servers->server[i]->password ? strdup(servers->server[i]->password) : NULL;
					tmp_def->repl_set_name = servers->server[i]->repl_set_name ? strdup(servers->server[i]->repl_set_name) : NULL;
					tmp_def->db = servers->server[i]->db ? strdup(servers->server[i]->db) : NULL;
					tmp_def->authdb = servers->server[i]->authdb ? strdup(servers->server[i]->authdb) : NULL;
					tmp_def->host = mcon_strndup(found_hosts[j], strchr(found_hosts[j], ':') - found_hosts[j]);
					tmp_def->port = atoi(strchr(found_hosts[j], ':') + 1);
					
					/* Create a hash so that we can check whether we already have a
					 * connection for this server definition. If we don't create
					 * the connection, register it (done in
					 * mongo_get_connection_single) and add it to the list of
					 * servers that we're processing so we might use this host to
					 * find more servers. */
					tmp_hash = mongo_server_create_hash(tmp_def);
					if (!mongo_manager_connection_find_by_hash(manager, tmp_hash)) {
						mongo_manager_log(manager, MLOG_CON, MLOG_INFO, "discover_topology: found new host: %s:%d", tmp_def->host, tmp_def->port);
						new_con = mongo_get_connection_single(manager, tmp_def, &servers->options, MONGO_CON_FLAG_WRITE, (char **) &con_error_message);
						if (new_con) {
							servers->server[servers->count] = tmp_def;
							servers->count++;
						} else {
							mongo_manager_log(manager, MLOG_CON, MLOG_WARN, "discover_topology: could not connect to new host: %s:%d: %s", tmp_def->host, tmp_def->port, con_error_message);
							free(con_error_message);
						}
					} else {
						mongo_server_def_dtor(tmp_def);
					}
					free(tmp_hash);

					/* Cleanup */
					free(found_hosts[j]);
				}
				free(found_hosts);
				found_hosts = NULL;
				break;

			case 2:
				mongo_manager_log(manager, MLOG_CON, MLOG_FINE, "discover_topology: ismaster got skipped");
				break;
		}

		free(hash);
	}
	if (repl_set_name) {
		free(repl_set_name);
	}
}