mirror of
https://github.com/mongodb/mongo.git
synced 2024-11-30 17:10:48 +01:00
SERVER-14165 port isSelf fastpath to windows
Closes #736 Signed-off-by: Benety Goh <benety@mongodb.com>
This commit is contained in:
parent
0adbb451f7
commit
daab6fae49
@ -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:
|
||||
|
@ -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",
|
||||
|
@ -28,155 +28,17 @@
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/pch.h"
|
||||
#include "mongo/platform/basic.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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 <ifaddrs.h>
|
||||
# endif
|
||||
# include <sys/resource.h>
|
||||
# include <sys/stat.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#ifdef __openbsd__
|
||||
# include <sys/uio.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
namespace mongo {
|
||||
|
||||
#if !defined(_WIN32) && !defined(__sunos__)
|
||||
|
||||
static vector<string> getMyAddrs() {
|
||||
vector<string> 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<string>::const_iterator it=out.begin(), end=out.end(); it!=end; ++it) {
|
||||
builder << " [" << *it << ']';
|
||||
}
|
||||
builder << endl;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static vector<string> 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<string> 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<string>::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<Privilege>* 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<string,bool> _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<string,bool>::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<string> myaddrs = getMyAddrs();
|
||||
const vector<string> addrs = getAllIPs(hostAndPort.host());
|
||||
|
||||
for (vector<string>::const_iterator i=myaddrs.begin(), iend=myaddrs.end();
|
||||
i!=iend; ++i) {
|
||||
for (vector<string>::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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
|
359
src/mongo/db/repl/isself.cpp
Normal file
359
src/mongo/db/repl/isself.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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 <boost/algorithm/string.hpp>
|
||||
|
||||
#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 <ifaddrs.h>
|
||||
#include <netdb.h>
|
||||
#elif defined(_WIN32)
|
||||
#include <boost/asio/detail/socket_ops.hpp>
|
||||
#include <boost/scoped_array.hpp>
|
||||
#include <boost/system/error_code.hpp>
|
||||
#include <iphlpapi.h>
|
||||
#include <winsock2.h>
|
||||
#include <Ws2tcpip.h>
|
||||
#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<std::string> 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<std::string> 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<std::string>::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<std::string> myAddrs = serverGlobalParams.bind_ip.empty() ?
|
||||
getBoundAddrs(IPv6Enabled()) :
|
||||
std::vector<std::string>();
|
||||
|
||||
if (!serverGlobalParams.bind_ip.empty()) {
|
||||
boost::split(myAddrs, serverGlobalParams.bind_ip, boost::is_any_of(", "));
|
||||
}
|
||||
|
||||
const std::vector<std::string> hostAddrs = getAddrsForHost(hostAndPort.host(),
|
||||
hostAndPort.port(),
|
||||
IPv6Enabled());
|
||||
|
||||
for (std::vector<std::string>::const_iterator i = myAddrs.begin();
|
||||
i != myAddrs.end(); ++i) {
|
||||
for (std::vector<std::string>::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<std::string> getBoundAddrs(const bool ipv6enabled) {
|
||||
#ifdef FASTPATH
|
||||
std::vector<std::string> 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<char> buf(new char[adaptersLen]);
|
||||
IP_ADAPTER_ADDRESSES* adapters = reinterpret_cast<IP_ADAPTER_ADDRESSES*>(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<IP_ADAPTER_ADDRESSES*>(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<SOCKADDR_STORAGE*>(addr->Address.lpSockaddr)->ss_family;
|
||||
|
||||
if (family == AF_INET) {
|
||||
// IPv4
|
||||
SOCKADDR_IN* sock = reinterpret_cast<SOCKADDR_IN*>(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<SOCKADDR_IN6*>(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<std::string>::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
|
@ -26,15 +26,38 @@
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/util/net/hostandport.h"
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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<std::string> getBoundAddrs(const bool ipv6enabled);
|
||||
|
||||
} // namespace repl
|
||||
} // namespace mongo
|
||||
|
84
src/mongo/db/repl/isself_test.cpp
Normal file
84
src/mongo/db/repl/isself_test.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* 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<std::string> addrs = getBoundAddrs(false);
|
||||
// Fastpath should agree with the result of getBoundAddrs
|
||||
// since it uses it...
|
||||
for (std::vector<string>::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<std::string> addrs = getBoundAddrs(true);
|
||||
// Fastpath should agree with the result of getBoundAddrs
|
||||
// since it uses it...
|
||||
for (std::vector<string>::const_iterator it = addrs.begin();
|
||||
it != addrs.end(); ++it) {
|
||||
|
||||
ASSERT(isSelf(HostAndPort(*it, serverGlobalParams.port)));
|
||||
}
|
||||
#else
|
||||
ASSERT(true);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace repl
|
||||
} // namespace mongo
|
Loading…
Reference in New Issue
Block a user