From daab6fae496e1aca6b8f7c709bd18307745df1dd Mon Sep 17 00:00:00 2001 From: Adam Midvidy Date: Thu, 31 Jul 2014 16:42:21 -0400 Subject: [PATCH] SERVER-14165 port isSelf fastpath to windows Closes #736 Signed-off-by: Benety Goh --- SConstruct | 8 +- src/mongo/SConscript | 1 + src/mongo/db/commands/isself.cpp | 243 +------------------- src/mongo/db/repl/SConscript | 11 + src/mongo/db/repl/isself.cpp | 359 ++++++++++++++++++++++++++++++ src/mongo/db/repl/isself.h | 25 ++- src/mongo/db/repl/isself_test.cpp | 84 +++++++ 7 files changed, 496 insertions(+), 235 deletions(-) create mode 100644 src/mongo/db/repl/isself.cpp create mode 100644 src/mongo/db/repl/isself_test.cpp diff --git a/SConstruct b/SConstruct index 16e9edde0c8..945f908b96f 100644 --- a/SConstruct +++ b/SConstruct @@ -842,7 +842,13 @@ elif windows: # This gives 32-bit programs 4 GB of user address space in WOW64, ignored in 64-bit builds env.Append( LINKFLAGS=["/LARGEADDRESSAWARE"] ) - env.Append(LIBS=['ws2_32.lib', 'kernel32.lib', 'advapi32.lib', 'Psapi.lib', 'DbgHelp.lib', 'shell32.lib']) + env.Append(LIBS=['ws2_32.lib', + 'kernel32.lib', + 'advapi32.lib', + 'Psapi.lib', + 'DbgHelp.lib', + 'shell32.lib', + 'Iphlpapi.lib']) # v8 calls timeGetTime() if usev8: diff --git a/src/mongo/SConscript b/src/mongo/SConscript index 0d075373fa8..acf01e19f33 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -444,6 +444,7 @@ coredbEnv.Library("coredb", [ "db/commands/find_and_modify_common.cpp", "db/commands/hashcmd.cpp", "db/commands/isself.cpp", + "db/repl/isself.cpp", "db/commands/mr_common.cpp", "db/commands/rename_collection_common.cpp", "db/commands/server_status.cpp", diff --git a/src/mongo/db/commands/isself.cpp b/src/mongo/db/commands/isself.cpp index 00b2ac0b56e..1f9e154b07c 100644 --- a/src/mongo/db/commands/isself.cpp +++ b/src/mongo/db/commands/isself.cpp @@ -28,155 +28,17 @@ * it in the license file. */ -#include "mongo/pch.h" +#include "mongo/platform/basic.h" -#include -#include -#include - -#include "mongo/db/auth/action_set.h" -#include "mongo/db/auth/action_type.h" -#include "mongo/db/auth/authorization_manager.h" -#include "mongo/db/auth/authorization_manager_global.h" -#include "mongo/db/auth/privilege.h" -#include "mongo/db/auth/security_key.h" +#include "mongo/base/init.h" #include "mongo/db/commands.h" -#include "mongo/db/jsobj.h" #include "mongo/db/repl/isself.h" -#include "mongo/db/server_options.h" -#include "mongo/util/net/listen.h" -#include "mongo/util/net/hostandport.h" -#include "mongo/client/dbclientinterface.h" - -#ifndef _WIN32 -# ifndef __sunos__ -# include -# endif -# include -# include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef __openbsd__ -# include -#endif - -#endif - namespace mongo { -#if !defined(_WIN32) && !defined(__sunos__) - - static vector getMyAddrs() { - vector out; - ifaddrs * addrs; - - if (!serverGlobalParams.bind_ip.empty()) { - boost::split(out, serverGlobalParams.bind_ip, boost::is_any_of(", ")); - return out; - } - - int status = getifaddrs(&addrs); - massert(13469, "getifaddrs failure: " + errnoWithDescription(errno), status == 0); - - // based on example code from linux getifaddrs manpage - for (ifaddrs * addr = addrs; addr != NULL; addr = addr->ifa_next) { - if ( addr->ifa_addr == NULL ) continue; - int family = addr->ifa_addr->sa_family; - char host[NI_MAXHOST]; - - if (family == AF_INET || family == AF_INET6) { - status = getnameinfo(addr->ifa_addr, - (family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)), - host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); - if ( status != 0 ) { - freeifaddrs( addrs ); - addrs = NULL; - msgasserted( 13470, string("getnameinfo() failed: ") + gai_strerror(status) ); - } - - out.push_back(host); - } - - } - - freeifaddrs( addrs ); - addrs = NULL; - - if (logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1))) { - LogstreamBuilder builder(logger::globalLogDomain(), - getThreadName(), - logger::LogSeverity::Debug(1)); - builder << "getMyAddrs():"; - for (vector::const_iterator it=out.begin(), end=out.end(); it!=end; ++it) { - builder << " [" << *it << ']'; - } - builder << endl; - } - - return out; - } - - static vector getAllIPs(const string& iporhost) { - addrinfo* addrs = NULL; - addrinfo hints; - memset(&hints, 0, sizeof(addrinfo)); - hints.ai_socktype = SOCK_STREAM; - hints.ai_family = (IPv6Enabled() ? AF_UNSPEC : AF_INET); - - static string portNum = BSONObjBuilder::numStr(serverGlobalParams.port); - - vector out; - - int ret = getaddrinfo(iporhost.c_str(), portNum.c_str(), &hints, &addrs); - if ( ret ) { - warning() << "getaddrinfo(\"" << iporhost << "\") failed: " << gai_strerror(ret) << endl; - return out; - } - - for (addrinfo* addr = addrs; addr != NULL; addr = addr->ai_next) { - int family = addr->ai_family; - char host[NI_MAXHOST]; - - if (family == AF_INET || family == AF_INET6) { - int status = getnameinfo(addr->ai_addr, addr->ai_addrlen, host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); - - massert(13472, string("getnameinfo() failed: ") + gai_strerror(status), status == 0); - - out.push_back(host); - } - - } - - freeaddrinfo(addrs); - - if (logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(1))) { - LogstreamBuilder builder(logger::globalLogDomain(), - getThreadName(), - logger::LogSeverity::Debug(1)); - builder << "getallIPs(\"" << iporhost << "\"):"; - for (vector::const_iterator it=out.begin(), end=out.end(); it!=end; ++it) { - builder << " [" << *it << ']'; - } - builder << endl; - } - - return out; - } -#endif - - class IsSelfCommand : public Command { public: - IsSelfCommand() : Command("_isSelf") , _cacheLock( "IsSelfCommand::_cacheLock" ) {} + IsSelfCommand() : Command("_isSelf") {} virtual bool slaveOk() const { return true; } virtual bool isWriteCommandForConfigServer() const { return false; } virtual void help( stringstream &help ) const { @@ -186,101 +48,16 @@ namespace mongo { const BSONObj& cmdObj, std::vector* out) {} // No auth required bool run(OperationContext* txn, const string& dbname, BSONObj& cmdObj, int, string& errmsg, BSONObjBuilder& result, bool fromRepl ) { - init(); - result.append( "id" , _id ); + result.append( "id" , repl::instanceId ); return true; } + }; - void init() { - scoped_lock lk( _cacheLock ); - if ( ! _id.isSet() ) - _id.init(); - } - - OID _id; - - mongo::mutex _cacheLock; - map _cache; - } isSelfCommand; - - bool repl::isSelf(const HostAndPort& hostAndPort) { - - int p = hostAndPort.port(); - - string host = str::stream() << hostAndPort.host() << ":" << p; - - { - // check cache for this host - // debatably something _could_ change, but I'm not sure right now (erh 10/14/2010) - scoped_lock lk( isSelfCommand._cacheLock ); - map::const_iterator i = isSelfCommand._cache.find( host ); - if ( i != isSelfCommand._cache.end() ) - return i->second; - } - -#if !defined(_WIN32) && !defined(__sunos__) - // on linux and os x we can do a quick check for an ip match - - // no need for ip match if the ports do not match - if (p == serverGlobalParams.port) { - const vector myaddrs = getMyAddrs(); - const vector addrs = getAllIPs(hostAndPort.host()); - - for (vector::const_iterator i=myaddrs.begin(), iend=myaddrs.end(); - i!=iend; ++i) { - for (vector::const_iterator j=addrs.begin(), jend=addrs.end(); - j!=jend; ++j) { - string a = *i; - string b = *j; - - if (a == b) { - // add to cache - scoped_lock lk( isSelfCommand._cacheLock ); - isSelfCommand._cache[host] = true; - return true; - } - } - } - } - -#endif - - if ( ! Listener::getTimeTracker() ) { - // this ensures we are actually running a server - // this may return true later, so may want to retry - return false; - } - - try { - isSelfCommand.init(); - DBClientConnection conn; - string errmsg; - if ( ! conn.connect( hostAndPort , errmsg ) ) { - // should this go in the cache? - return false; - } - - if (getGlobalAuthorizationManager()->isAuthEnabled() && isInternalAuthSet()) { - if (!authenticateInternalUser(&conn)) { - return false; - } - } - - BSONObj out; - bool ok = conn.simpleCommand( "admin" , &out , "_isSelf" ); - bool me = ok && out["id"].type() == jstOID && isSelfCommand._id == out["id"].OID(); - - // add to cache - scoped_lock lk( isSelfCommand._cacheLock ); - isSelfCommand._cache[host] = me; - - return me; - } - catch ( std::exception& e ) { - warning() << "could't check isSelf (" << host << ") " << e.what() << endl; - } - - return false; + MONGO_INITIALIZER_WITH_PREREQUISITES(RegisterIsSelfCommand, ("GenerateInstanceId")) + (InitializerContext* context) { + // Leaked intentionally: a Command registers itself when constructed + new IsSelfCommand(); + return Status::OK(); } } diff --git a/src/mongo/db/repl/SConscript b/src/mongo/db/repl/SConscript index 3941b4d6551..0d17be5942d 100644 --- a/src/mongo/db/repl/SConscript +++ b/src/mongo/db/repl/SConscript @@ -118,3 +118,14 @@ env.CppUnitTest('replica_set_config_test', 'replica_set_tag_test.cpp', ], LIBDEPS=['replica_set_messages']) + +env.CppUnitTest('isself_test', + [ + 'isself_test.cpp', + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/serveronly', + '$BUILD_DIR/mongo/coreserver', + '$BUILD_DIR/mongo/coredb', + ], + NO_CRUTCH = True) diff --git a/src/mongo/db/repl/isself.cpp b/src/mongo/db/repl/isself.cpp new file mode 100644 index 00000000000..39809cbc01f --- /dev/null +++ b/src/mongo/db/repl/isself.cpp @@ -0,0 +1,359 @@ +/** +* Copyright (C) 2014 MongoDB Inc. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License, version 3, +* as published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +* +* As a special exception, the copyright holders give permission to link the +* code of portions of this program with the OpenSSL library under certain +* conditions as described in each individual source file and distribute +* linked combinations including the program with the OpenSSL library. You +* must comply with the GNU Affero General Public License in all respects for +* all of the code used other than as permitted herein. If you modify file(s) +* with this exception, you may extend this exception to your version of the +* file(s), but you are not obligated to do so. If you do not wish to do so, +* delete this exception statement from your version. If you delete this +* exception statement from all source files in the program, then also delete +* it in the license file. +*/ + +#include "mongo/platform/basic.h" + +#include "mongo/db/repl/isself.h" + +#include + +#include "mongo/base/init.h" +#include "mongo/client/dbclientinterface.h" +#include "mongo/db/commands.h" +#include "mongo/db/auth/action_set.h" +#include "mongo/db/auth/action_type.h" +#include "mongo/db/auth/authorization_manager.h" +#include "mongo/db/auth/authorization_manager_global.h" +#include "mongo/db/auth/privilege.h" +#include "mongo/db/auth/security_key.h" +#include "mongo/util/scopeguard.h" +#include "mongo/util/log.h" + +#ifdef __linux__ +#include +#include +#elif defined(_WIN32) +#include +#include +#include +#include +#include +#include +#endif // defined(_WIN32) + +#if defined(_WIN32) || defined(__linux__) +#define FASTPATH 1 +#endif + +namespace mongo { +namespace repl { + + OID instanceId; + + MONGO_INITIALIZER(GenerateInstanceId)(InitializerContext*) { + instanceId = OID::gen(); + return Status::OK(); + } + +#ifdef FASTPATH + +namespace { + + /** + * Helper to convert a message from a networking function to a string. + * Needed because errnoWithDescription uses strerror on linux, when + * we need gai_strerror. + */ + std::string stringifyError(int code) { +#ifdef __linux__ + return gai_strerror(code); +#elif defined(_WIN32) + // FormatMessage in errnoWithDescription works here on windows + return errnoWithDescription(code); +#endif + } + + /** + * Resolves a host and port to a list of IP addresses. This requires a syscall. If the + * ipv6enabled parameter is true, both IPv6 and IPv4 addresses will be returned. + */ + std::vector getAddrsForHost(const std::string& iporhost, + const int port, + const bool ipv6enabled) { + addrinfo* addrs = NULL; + addrinfo hints = {0}; + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = (ipv6enabled ? AF_UNSPEC : AF_INET); + + const std::string portNum = BSONObjBuilder::numStr(port); + + std::vector out; + + int err = getaddrinfo(iporhost.c_str(), portNum.c_str(), &hints, &addrs); + + if (err) { + warning() << "getaddrinfo(\"" << iporhost << "\") failed: " + << stringifyError(err) << std::endl; + return out; + } + + ON_BLOCK_EXIT(freeaddrinfo, addrs); + + for (addrinfo* addr = addrs; addr != NULL; addr = addr->ai_next) { + int family = addr->ai_family; + char host[NI_MAXHOST]; + + if (family == AF_INET || family == AF_INET6) { + err = getnameinfo(addr->ai_addr, addr->ai_addrlen, host, + NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (err) { + warning() << "getnameinfo() failed: " << stringifyError(err) << std::endl; + continue; + } + out.push_back(host); + } + + } + + if (logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(2))) { + LogstreamBuilder builder(logger::globalLogDomain(), + getThreadName(), + logger::LogSeverity::Debug(2)); + builder << "getAddrsForHost(\"" << iporhost << ":" << port << "\"):"; + for (std::vector::const_iterator o = out.begin(); o != out.end(); ++o) { + builder << " [ " << *o << "]"; + } + builder << std::endl; + } + + return out; + } + +} // namespace + +#endif // ifdef FASTPATH + + + bool isSelf(const HostAndPort& hostAndPort) { + +#ifdef FASTPATH + + // Fastpath: check if the host&port in question is bound to one + // of the interfaces on this machine. + // No need for ip match if the ports do not match + if (hostAndPort.port() == serverGlobalParams.port) { + std::vector myAddrs = serverGlobalParams.bind_ip.empty() ? + getBoundAddrs(IPv6Enabled()) : + std::vector(); + + if (!serverGlobalParams.bind_ip.empty()) { + boost::split(myAddrs, serverGlobalParams.bind_ip, boost::is_any_of(", ")); + } + + const std::vector hostAddrs = getAddrsForHost(hostAndPort.host(), + hostAndPort.port(), + IPv6Enabled()); + + for (std::vector::const_iterator i = myAddrs.begin(); + i != myAddrs.end(); ++i) { + for (std::vector::const_iterator j = hostAddrs.begin(); + j != hostAddrs.end(); ++j) { + if (*i == *j) { + return true; + } + } + } + } + +#endif // ifdef FASTPATH + + if (!Listener::getTimeTracker()) { + // this ensures we are actually running a server + // this may return true later, so may want to retry + return false; + } + + try { + DBClientConnection conn; + std::string errmsg; + if (!conn.connect(hostAndPort, errmsg)) { + return false; + } + + if (getGlobalAuthorizationManager()->isAuthEnabled() && isInternalAuthSet()) { + if (!authenticateInternalUser(&conn)) { + return false; + } + } + BSONObj out; + bool ok = conn.simpleCommand("admin" , &out, "_isSelf"); + bool me = ok && out["id"].type() == jstOID && instanceId == out["id"].OID(); + + return me; + } + catch (const std::exception& e) { + warning() << "could't check isSelf (" << hostAndPort << ") " << e.what() << std::endl; + } + + return false; + } + + /** + * Returns all the IP addresses bound to the network interfaces of this machine. + * This requires a syscall. If the ipv6enabled parameter is true, both IPv6 AND IPv4 + * addresses will be returned. + */ + std::vector getBoundAddrs(const bool ipv6enabled) { +#ifdef FASTPATH + std::vector out; +#ifdef __linux__ + + ifaddrs* addrs; + + int err = getifaddrs(&addrs); + if (err) { + warning() << "getifaddrs failure: " << errnoWithDescription(err) << std::endl; + return out; + } + ON_BLOCK_EXIT(freeifaddrs, addrs); + + // based on example code from linux getifaddrs manpage + for (ifaddrs* addr = addrs; addr != NULL; addr = addr->ifa_next) { + if (addr->ifa_addr == NULL) continue; + int family = addr->ifa_addr->sa_family; + char host[NI_MAXHOST]; + + if (family == AF_INET || (ipv6enabled && (family == AF_INET6))) { + err = getnameinfo(addr->ifa_addr, + (family == AF_INET ? sizeof(struct sockaddr_in) + : sizeof(struct sockaddr_in6)), + host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + if (err) { + warning() << "getnameinfo() failed: " << gai_strerror(err) << std::endl; + continue; + } + out.push_back(host); + } + } + +#elif defined(_WIN32) + + // Start with the MS recommended 15KB buffer. Use multiple attempts + // for the rare case that the adapter config changes between calls + + ULONG adaptersLen = 15 * 1024; + boost::scoped_array buf(new char[adaptersLen]); + IP_ADAPTER_ADDRESSES* adapters = reinterpret_cast(buf.get()); + DWORD err; + + ULONG family = ipv6enabled ? AF_UNSPEC : AF_INET; + + for (int tries = 0; tries < 3; ++tries) { + err = GetAdaptersAddresses(family, + GAA_FLAG_SKIP_ANYCAST | // only want unicast addrs + GAA_FLAG_SKIP_MULTICAST | + GAA_FLAG_SKIP_DNS_SERVER, + NULL, + adapters, + &adaptersLen); + + if (err == ERROR_BUFFER_OVERFLOW) { + // in this case, adaptersLen will be set to the size we need to allocate + buf.reset(new char[adaptersLen]); + adapters = reinterpret_cast(buf.get()); + } + else { + break; // only retry for incorrectly sized buffer + } + } + + if (err != NO_ERROR) { + warning() << "GetAdaptersAddresses() failed: " << errnoWithDescription(err) + << std::endl; + return out; + } + + for (IP_ADAPTER_ADDRESSES* adapter = adapters; + adapter != NULL; adapter = adapter->Next) { + for (IP_ADAPTER_UNICAST_ADDRESS* addr = adapter->FirstUnicastAddress; + addr != NULL; addr = addr->Next) { + + short family = + reinterpret_cast(addr->Address.lpSockaddr)->ss_family; + + if (family == AF_INET) { + // IPv4 + SOCKADDR_IN* sock = reinterpret_cast(addr->Address.lpSockaddr); + char addrstr[INET_ADDRSTRLEN] = {0}; + boost::system::error_code ec; + // Not all windows versions have inet_ntop + boost::asio::detail::socket_ops::inet_ntop(AF_INET, + &(sock->sin_addr), + addrstr, + INET_ADDRSTRLEN, + 0, + ec); + if (ec) { + warning() << "inet_ntop failed during IPv4 address conversion: " + << ec.message() << std::endl; + continue; + } + out.push_back(addrstr); + } + else if (family == AF_INET6) { + // IPv6 + SOCKADDR_IN6* sock = reinterpret_cast(addr->Address.lpSockaddr); + char addrstr[INET6_ADDRSTRLEN] = {0}; + boost::system::error_code ec; + boost::asio::detail::socket_ops::inet_ntop(AF_INET6, + &(sock->sin6_addr), + addrstr, + INET6_ADDRSTRLEN, + 0, + ec); + if (ec) { + warning() << "inet_ntop failed during IPv6 address conversion: " + << ec.message() << std::endl; + continue; + } + out.push_back(addrstr); + } + } + } + +#endif // defined(_WIN32) + if (logger::globalLogDomain()->shouldLog(logger::LogSeverity::Debug(2))) { + LogstreamBuilder builder(logger::globalLogDomain(), + getThreadName(), + logger::LogSeverity::Debug(2)); + builder << "getBoundAddrs():"; + for (std::vector::const_iterator o = out.begin(); o != out.end(); ++o) { + builder << " [ " << *o << "]"; + } + builder << std::endl; + } + return out; +#else // ifdef FASTPATH + invariant(false); +#endif + } + +#undef FASTPATH + +} // namespace repl +} // namespace mongo diff --git a/src/mongo/db/repl/isself.h b/src/mongo/db/repl/isself.h index ea297218056..cbcbbd9f031 100644 --- a/src/mongo/db/repl/isself.h +++ b/src/mongo/db/repl/isself.h @@ -26,15 +26,38 @@ * it in the license file. */ -#include "mongo/util/net/hostandport.h" +#pragma once + +#include +#include + +#include "mongo/bson/oid.h" namespace mongo { + struct HostAndPort; + namespace repl { + /** + * An identifier unique to this instance. Used by isSelf to see if we are talking + * to ourself or someone else. + */ + extern OID instanceId; + /** * Returns true if "hostAndPort" identifies this instance. */ bool isSelf(const HostAndPort& hostAndPort); + /** + * Returns all the IP addresses bound to the network interfaces of this machine. + * This requires a syscall. If the ipv6enabled parameter is true, both IPv6 AND IPv4 + * addresses will be returned. + * + * Note: this only works on Linux and Windows. All calls should be properly ifdef'd, + * otherwise an invariant will be triggered. + */ + std::vector getBoundAddrs(const bool ipv6enabled); + } // namespace repl } // namespace mongo diff --git a/src/mongo/db/repl/isself_test.cpp b/src/mongo/db/repl/isself_test.cpp new file mode 100644 index 00000000000..ef1d130d112 --- /dev/null +++ b/src/mongo/db/repl/isself_test.cpp @@ -0,0 +1,84 @@ +/** + * Copyright 2014 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/db/repl/isself.h" +#include "mongo/db/server_options.h" +#include "mongo/unittest/unittest.h" +#include "mongo/util/net/hostandport.h" +#include "mongo/util/net/sock.h" +#include "mongo/util/scopeguard.h" + +namespace mongo { +namespace repl { + +namespace { + + TEST(IsSelf, DetectsSameHostIPv4) { +#if defined(_WIN32) || defined(__linux__) + bool wasEnabled = IPv6Enabled(); + enableIPv6(false); + ON_BLOCK_EXIT(enableIPv6, wasEnabled); + // first we get the addrs bound on this host + const std::vector addrs = getBoundAddrs(false); + // Fastpath should agree with the result of getBoundAddrs + // since it uses it... + for (std::vector::const_iterator it = addrs.begin(); + it != addrs.end(); ++it) { + + ASSERT(isSelf(HostAndPort(*it, serverGlobalParams.port))); + } +#else + ASSERT(true); +#endif + } + + TEST(IsSelf, DetectsSameHostIPv6) { +#if defined(_WIN32) || defined(__linux__) + bool wasEnabled = IPv6Enabled(); + enableIPv6(true); + ON_BLOCK_EXIT(enableIPv6, wasEnabled); + // first we get the addrs bound on this host + const std::vector addrs = getBoundAddrs(true); + // Fastpath should agree with the result of getBoundAddrs + // since it uses it... + for (std::vector::const_iterator it = addrs.begin(); + it != addrs.end(); ++it) { + + ASSERT(isSelf(HostAndPort(*it, serverGlobalParams.port))); + } +#else + ASSERT(true); +#endif + } + +} // namespace + +} // namespace repl +} // namespace mongo