// isself.cpp /** * Copyright (C) 2012 10gen 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/pch.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/db/commands.h" #include "mongo/db/jsobj.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__) 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; } 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" ) {} virtual bool slaveOk() const { return true; } virtual bool isWriteCommandForConfigServer() const { return false; } virtual void help( stringstream &help ) const { help << "{ _isSelf : 1 } INTERNAL ONLY"; } virtual void addRequiredPrivileges(const std::string& dbname, 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 ); return true; } void init() { scoped_lock lk( _cacheLock ); if ( ! _id.isSet() ) _id.init(); } OID _id; mongo::mutex _cacheLock; map _cache; } isSelfCommand; bool HostAndPort::isSelf() const { int _p = port(); int p = _p == -1 ? ServerGlobalParams::DefaultDBPort : _p; string host = str::stream() << this->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(_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 || ( str::startsWith( a , "127." ) && str::startsWith( b , "127." ) ) // 127. is all loopback ) { // 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( host , 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; } }