diff options
author | Spencer Jackson <spencer.jackson@mongodb.com> | 2015-10-14 18:15:45 -0400 |
---|---|---|
committer | Spencer Jackson <spencer.jackson@mongodb.com> | 2015-10-14 18:15:45 -0400 |
commit | e7189d7af091983e9eedc1ca30bc7f1d8e136951 (patch) | |
tree | 7dc501fd140033c0388fef8031284b24d23304c9 | |
parent | a8242b99c2455d2c95ae529d8717b268d1fbf024 (diff) | |
download | mongo-e7189d7af091983e9eedc1ca30bc7f1d8e136951.tar.gz |
SERVER-2421 SERVER-17739 Add FQDN canonicalization for serverStatus and SPNs
-rw-r--r-- | src/mongo/client/SConscript | 3 | ||||
-rw-r--r-- | src/mongo/client/sasl_sspi.cpp | 33 | ||||
-rw-r--r-- | src/mongo/client/sasl_sspi_options.cpp | 85 | ||||
-rw-r--r-- | src/mongo/client/sasl_sspi_options.h | 54 | ||||
-rw-r--r-- | src/mongo/db/commands/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/commands/server_status.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/db.cpp | 3 | ||||
-rw-r--r-- | src/mongo/s/server.cpp | 3 | ||||
-rw-r--r-- | src/mongo/util/net/SConscript | 13 | ||||
-rw-r--r-- | src/mongo/util/net/hostname_canonicalization.cpp | 128 | ||||
-rw-r--r-- | src/mongo/util/net/hostname_canonicalization.h | 57 | ||||
-rw-r--r-- | src/mongo/util/net/hostname_canonicalization_worker.cpp | 92 | ||||
-rw-r--r-- | src/mongo/util/net/hostname_canonicalization_worker.h | 72 | ||||
-rw-r--r-- | src/mongo/util/net/sock.h | 3 |
14 files changed, 551 insertions, 6 deletions
diff --git a/src/mongo/client/SConscript b/src/mongo/client/SConscript index 2e68512e5ce..ca130091830 100644 --- a/src/mongo/client/SConscript +++ b/src/mongo/client/SConscript @@ -69,6 +69,7 @@ if env['MONGO_BUILD_SASL_CLIENT']: saslClientSource.extend([ 'cyrus_sasl_client_session.cpp', 'sasl_sspi.cpp', + 'sasl_sspi_options.cpp', ]) saslLibs.extend(['sasl2']) @@ -86,7 +87,7 @@ saslClientEnv.Library( '$BUILD_DIR/mongo/rpc/metadata', '$BUILD_DIR/mongo/util/foundation', '$BUILD_DIR/mongo/util/md5', - '$BUILD_DIR/mongo/util/net/hostandport', + '$BUILD_DIR/mongo/util/net/network', ], SYSLIBDEPS=saslLibs ) diff --git a/src/mongo/client/sasl_sspi.cpp b/src/mongo/client/sasl_sspi.cpp index fff8364958a..a9635320dd0 100644 --- a/src/mongo/client/sasl_sspi.cpp +++ b/src/mongo/client/sasl_sspi.cpp @@ -28,6 +28,8 @@ #ifdef _WIN32 +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kNetwork + #define SECURITY_WIN32 1 // Required for SSPI support. #include "mongo/platform/basic.h" @@ -37,8 +39,10 @@ #include <sspi.h> #include "mongo/base/init.h" -#include "mongo/util/mongoutils/str.h" #include "mongo/base/status.h" +#include "mongo/client/sasl_sspi_options.h" +#include "mongo/util/log.h" +#include "mongo/util/mongoutils/str.h" #include "mongo/util/scopeguard.h" #include "mongo/util/text.h" @@ -190,13 +194,34 @@ int sspiClientMechNew(void* glob_context, pcctx->haveCred = true; - // Compose target name token. + // Compose target name token. First, verify that a hostname has been provided. if (cparams->serverFQDN == NULL || strlen(cparams->serverFQDN) == 0) { cparams->utils->seterror(cparams->utils->conn, 0, "SSPI: no serverFQDN"); return SASL_FAIL; } - pcctx->nameToken = - toWideString((std::string(cparams->service) + "/" + cparams->serverFQDN).c_str()); + + // Then obtain all potential FQDNs for the hostname. + std::string canonName = cparams->serverFQDN; + auto fqdns = getHostFQDNs(cparams->serverFQDN, saslSSPIGlobalParams.canonicalization); + if (!fqdns.empty()) { + // PTR records should point to the canonical name. If there's more than one, warn and + // arbitrarily use the last entry. + if (fqdns.size() > 1) { + std::stringstream ss; + ss << "Found multiple PTR records while performing reverse DNS: [ "; + for (const std::string& fqdn : fqdns) { + ss << fqdn << " "; + } + ss << "]"; + warning() << ss.str(); + } + canonName = std::move(fqdns.back()); + fqdns.pop_back(); + } else if (saslSSPIGlobalParams.canonicalization != HostnameCanonicalizationMode::kNone) { + warning() << "Was unable to acquire an FQDN"; + } + + pcctx->nameToken = toWideString(cparams->service) + L'/' + toWideString(canonName.c_str()); *conn_context = pcctx.release(); diff --git a/src/mongo/client/sasl_sspi_options.cpp b/src/mongo/client/sasl_sspi_options.cpp new file mode 100644 index 00000000000..f392f5b5412 --- /dev/null +++ b/src/mongo/client/sasl_sspi_options.cpp @@ -0,0 +1,85 @@ +/** + * Copyright (C) 2015 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. + */ + +#ifdef _WIN32 + +#include "mongo/platform/basic.h" + +#include "mongo/client/sasl_sspi_options.h" + +#include <string> +#include <vector> + +#include "mongo/base/status.h" +#include "mongo/util/options_parser/startup_options.h" +#include "mongo/util/options_parser/startup_option_init.h" + +namespace mongo { + +SASLSSPIGlobalParams saslSSPIGlobalParams; + +Status addSASLSSPIOptions(moe::OptionSection* options) { + moe::OptionSection sspiOptions("Kerberos Options"); + sspiOptions.addOptionChaining("security.sspiHostnameCanonicalization", + "sspiHostnameCanonicalization", + moe::String, + "DNS resolution strategy to use for hostname canonicalization. " + "May be one of: {none, forward, forwardAndReverse}") + .setDefault(moe::Value(std::string("none"))); + return options->addSection(sspiOptions); +} + +Status storeSASLSSPIOptions(const moe::Environment& params) { + if (params.count("security.sspiHostnameCanonicalization")) { + if (params["security.sspiHostnameCanonicalization"].as<std::string>() == "none") { + saslSSPIGlobalParams.canonicalization = HostnameCanonicalizationMode::kNone; + } else if (params["security.sspiHostnameCanonicalization"].as<std::string>() == "forward") { + saslSSPIGlobalParams.canonicalization = HostnameCanonicalizationMode::kForward; + } else if (params["security.sspiHostnameCanonicalization"].as<std::string>() == + "forwardAndReverse") { + saslSSPIGlobalParams.canonicalization = + HostnameCanonicalizationMode::kForwardAndReverse; + } else { + return Status(ErrorCodes::InvalidOptions, + "Unrecognized sspiHostnameCanonicalization option"); + } + } + return Status::OK(); +} + +MONGO_MODULE_STARTUP_OPTIONS_REGISTER(SASLSSPIOptions)(InitializerContext* context) { + return addSASLSSPIOptions(&moe::startupOptions); +} + +MONGO_STARTUP_OPTIONS_STORE(SASLSSPIOptions)(InitializerContext* context) { + return storeSASLSSPIOptions(moe::startupOptionsParsed); +} + +} // namespace mongo + +#endif // ifdef _WIN32 diff --git a/src/mongo/client/sasl_sspi_options.h b/src/mongo/client/sasl_sspi_options.h new file mode 100644 index 00000000000..e0dca3516ec --- /dev/null +++ b/src/mongo/client/sasl_sspi_options.h @@ -0,0 +1,54 @@ +/** + * Copyright (C) 2015 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. + */ + +#pragma once + +#include "mongo/util/net/hostname_canonicalization.h" + +namespace mongo { + +class Status; + +namespace optionenvironment { +class OptionSection; +class Environment; +} // namespace optionenvironment + +namespace moe = mongo::optionenvironment; + +struct SASLSSPIGlobalParams { + // HostnameCanonicalizationMode to use for resolving SASL hostname into the SPN's hostname + HostnameCanonicalizationMode canonicalization = HostnameCanonicalizationMode::kNone; +}; + +extern SASLSSPIGlobalParams saslSSPIGlobalParams; + +Status addSASLSSPIOptions(moe::OptionSection* options); +Status storeSASLSSPIOptions(const moe::Environment& params); + +} // namespace mongo diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript index e52ace00852..625e922bb79 100644 --- a/src/mongo/db/commands/SConscript +++ b/src/mongo/db/commands/SConscript @@ -16,6 +16,7 @@ env.Library( 'server_status_metric.cpp', ], LIBDEPS=[ + '$BUILD_DIR/mongo/util/net/hostname_canonicalization_worker', '$BUILD_DIR/mongo/base' ] ) diff --git a/src/mongo/db/commands/server_status.cpp b/src/mongo/db/commands/server_status.cpp index d7b3324efe6..c4b475f6a40 100644 --- a/src/mongo/db/commands/server_status.cpp +++ b/src/mongo/db/commands/server_status.cpp @@ -43,9 +43,12 @@ #include "mongo/db/commands/server_status.h" #include "mongo/db/commands/server_status_internal.h" #include "mongo/db/commands/server_status_metric.h" +#include "mongo/db/operation_context.h" +#include "mongo/db/service_context.h" #include "mongo/db/stats/counters.h" #include "mongo/platform/process_id.h" #include "mongo/util/log.h" +#include "mongo/util/net/hostname_canonicalization_worker.h" #include "mongo/util/net/listen.h" #include "mongo/util/net/ssl_manager.h" #include "mongo/util/processinfo.h" @@ -93,10 +96,14 @@ public: BSONObjBuilder timeBuilder(256); const auto authSession = AuthorizationSession::get(ClientBasic::getCurrent()); + auto service = txn->getServiceContext(); + auto canonicalizer = HostnameCanonicalizationWorker::get(service); // --- basic fields that are global result.append("host", prettyHostName()); + result.append("advisoryHostFQDNs", canonicalizer->getCanonicalizedFQDNs()); + result.append("version", versionString); result.append("process", serverGlobalParams.binaryName); result.append("pid", ProcessId::getCurrent().asLongLong()); @@ -319,4 +326,5 @@ public: } } memBase; } -} + +} // namespace mongo diff --git a/src/mongo/db/db.cpp b/src/mongo/db/db.cpp index c8a8e0eb382..e1e01c9b89b 100644 --- a/src/mongo/db/db.cpp +++ b/src/mongo/db/db.cpp @@ -107,6 +107,7 @@ #include "mongo/util/exception_filter_win32.h" #include "mongo/util/exit.h" #include "mongo/util/log.h" +#include "mongo/util/net/hostname_canonicalization_worker.h" #include "mongo/util/net/message_server.h" #include "mongo/util/net/ssl_manager.h" #include "mongo/util/ntservice.h" @@ -617,6 +618,8 @@ static void _initAndListen(int listenPort) { PeriodicTask::startRunningPeriodicTasks(); + HostnameCanonicalizationWorker::start(getGlobalServiceContext()); + startFTDC(); if (!repl::getGlobalReplicationCoordinator()->isReplEnabled()) { diff --git a/src/mongo/s/server.cpp b/src/mongo/s/server.cpp index 5d594099067..02a6a6f6857 100644 --- a/src/mongo/s/server.cpp +++ b/src/mongo/s/server.cpp @@ -76,6 +76,7 @@ #include "mongo/util/exception_filter_win32.h" #include "mongo/util/exit.h" #include "mongo/util/log.h" +#include "mongo/util/net/hostname_canonicalization_worker.h" #include "mongo/util/net/message.h" #include "mongo/util/net/message_server.h" #include "mongo/util/net/ssl_manager.h" @@ -238,6 +239,8 @@ static ExitCode runMongosServer() { web.detach(); } + HostnameCanonicalizationWorker::start(getGlobalServiceContext()); + Status status = getGlobalAuthorizationManager()->initialize(NULL); if (!status.isOK()) { error() << "Initializing authorization data failed: " << status; diff --git a/src/mongo/util/net/SConscript b/src/mongo/util/net/SConscript index dfdeca9c352..72d3384fb33 100644 --- a/src/mongo/util/net/SConscript +++ b/src/mongo/util/net/SConscript @@ -25,6 +25,7 @@ env.CppUnitTest( env.Library( target='network', source=[ + "hostname_canonicalization.cpp", "httpclient.cpp", "listen.cpp", "message.cpp", @@ -72,6 +73,18 @@ env.CppUnitTest( ) env.Library( + target="hostname_canonicalization_worker", + source=[ + "hostname_canonicalization_worker.cpp", + ], + LIBDEPS=[ + '$BUILD_DIR/mongo/base', + '$BUILD_DIR/mongo/db/service_context', + 'network', + ], +) + +env.Library( target="message_server_port", source=[ "message_server_port.cpp", diff --git a/src/mongo/util/net/hostname_canonicalization.cpp b/src/mongo/util/net/hostname_canonicalization.cpp new file mode 100644 index 00000000000..b2a5821e129 --- /dev/null +++ b/src/mongo/util/net/hostname_canonicalization.cpp @@ -0,0 +1,128 @@ +/** + * Copyright (C) 2015 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. + */ + +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kNetwork + +#include "mongo/platform/basic.h" + +#include "mongo/util/net/hostname_canonicalization.h" + +#if !defined(_WIN32) +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +#endif + +#include "mongo/util/log.h" +#include "mongo/util/net/sock.h" +#include "mongo/util/scopeguard.h" +#include "mongo/util/text.h" + +namespace mongo { + +std::vector<std::string> getHostFQDNs(std::string hostName, HostnameCanonicalizationMode mode) { +#ifndef _WIN32 + using shim_char = char; + using shim_addrinfo = struct addrinfo; + const auto& shim_getaddrinfo = getaddrinfo; + const auto& shim_freeaddrinfo = freeaddrinfo; + const auto& shim_getnameinfo = getnameinfo; + const auto& shim_toNativeString = [](const char* str) { return std::string(str); }; + const auto& shim_fromNativeString = [](const std::string& str) { return str; }; +#else + using shim_char = wchar_t; + using shim_addrinfo = struct addrinfoW; + const auto& shim_getaddrinfo = GetAddrInfoW; + const auto& shim_freeaddrinfo = FreeAddrInfoW; + const auto& shim_getnameinfo = GetNameInfoW; + const auto& shim_toNativeString = toWideString; + const auto& shim_fromNativeString = toUtf8String; +#endif + + std::vector<std::string> results; + + if (hostName.empty()) + return results; + + if (mode == HostnameCanonicalizationMode::kNone) { + results.emplace_back(std::move(hostName)); + return results; + } + + shim_addrinfo hints = {}; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = 0; + hints.ai_protocol = 0; + if (mode == HostnameCanonicalizationMode::kForward) { + hints.ai_flags = AI_CANONNAME; + } + + int err; + shim_addrinfo* info; + auto nativeHostName = shim_toNativeString(hostName.c_str()); + if ((err = shim_getaddrinfo(nativeHostName.c_str(), nullptr, &hints, &info)) != 0) { + ONCE { + warning() << "Failed to obtain address info for hostname " << hostName << ": " + << getAddrInfoStrError(err); + } + return results; + } + const auto guard = MakeGuard([&shim_freeaddrinfo, &info] { shim_freeaddrinfo(info); }); + + if (mode == HostnameCanonicalizationMode::kForward) { + results.emplace_back(shim_fromNativeString(info->ai_canonname)); + return results; + } + + for (shim_addrinfo* p = info; p; p = p->ai_next) { + shim_char host[NI_MAXHOST] = {}; + if ((err = shim_getnameinfo( + p->ai_addr, p->ai_addrlen, host, sizeof(host), nullptr, 0, NI_NAMEREQD)) == 0) { + results.emplace_back(shim_fromNativeString(host)); + } else { + // We can't do much better than this without writting some specific IPv4/6 address + // handling logic. + warning() << "Failed to obtain name info for an address: " << getAddrInfoStrError(err); + } + } + + // Deduplicate the results list + std::sort(results.begin(), results.end()); + results.erase(std::unique(results.begin(), results.end()), results.end()); + + // Remove any name that doesn't have a '.', since A records are illegal in TLDs + results.erase( + std::remove_if(results.begin(), + results.end(), + [](const std::string& str) { return str.find('.') == std::string::npos; }), + results.end()); + + return results; +} + +} // namespace mongo diff --git a/src/mongo/util/net/hostname_canonicalization.h b/src/mongo/util/net/hostname_canonicalization.h new file mode 100644 index 00000000000..5bdc6d356a7 --- /dev/null +++ b/src/mongo/util/net/hostname_canonicalization.h @@ -0,0 +1,57 @@ +/** + * Copyright (C) 2015 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. + */ + +#pragma once + +#include <string> +#include <vector> + +namespace mongo { + +/** + * DNS canonicalization converts a hostname into another potentially more globally useful hostname. + * + * This involves issuing some combination of IP and name lookups. + * This enum controls which canonicalization process a client will perform on a + * hostname it is canonicalizing. + */ +enum class HostnameCanonicalizationMode { + kNone, // Perform no canonicalization at all + kForward, // Perform a DNS lookup on the hostname, follow CNAMEs to find the A record + kForwardAndReverse // Forward resolve to get an IP, then perform reverse lookup on it +}; + +/** + * Returns zero or more fully qualified hostnames associated with the provided hostname. + * + * May return an empty vector if no FQDNs can be determined, or if the underlying + * implementation returns an error. The returned information is advisory only. + */ +std::vector<std::string> getHostFQDNs(std::string hostName, HostnameCanonicalizationMode mode); + +} // namespace mongo diff --git a/src/mongo/util/net/hostname_canonicalization_worker.cpp b/src/mongo/util/net/hostname_canonicalization_worker.cpp new file mode 100644 index 00000000000..b35cef4a686 --- /dev/null +++ b/src/mongo/util/net/hostname_canonicalization_worker.cpp @@ -0,0 +1,92 @@ +/** + * Copyright (C) 2015 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. + */ +#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kNetwork + +#include "mongo/platform/basic.h" + +#include "mongo/util/net/hostname_canonicalization_worker.h" + +#include "mongo/db/service_context.h" +#include "mongo/stdx/memory.h" +#include "mongo/util/concurrency/thread_name.h" +#include "mongo/util/log.h" +#include "mongo/util/net/hostname_canonicalization.h" +#include "mongo/util/net/sock.h" +#include "mongo/util/time_support.h" + +namespace mongo { + +namespace { +const auto getCanonicalizerTask = + ServiceContext::declareDecoration<std::unique_ptr<HostnameCanonicalizationWorker>>(); +} // namespace + +void HostnameCanonicalizationWorker::start(ServiceContext* service) { + auto& task = getCanonicalizerTask(service); + task = stdx::make_unique<HostnameCanonicalizationWorker>(); +} + +HostnameCanonicalizationWorker::HostnameCanonicalizationWorker() { + _canonicalizationThread = stdx::thread(&HostnameCanonicalizationWorker::_doWork, this); +} + +void HostnameCanonicalizationWorker::_doWork() { + setThreadName("HostnameCanonicalizationWorker"); + log() << "Starting hostname canonicalization worker"; + try { + while (true) { + LOG(3) << "Hostname Canonicalizer is acquiring host FQDNs"; + auto fqdns = + getHostFQDNs(getHostNameCached(), HostnameCanonicalizationMode::kForwardAndReverse); + { + stdx::lock_guard<stdx::mutex> lock(_canonicalizationMutex); + _cachedFQDNs = std::move(fqdns); + } + LOG(3) << "Hostname Canonicalizer acquired FQDNs"; + + sleepsecs(60); + } + } catch (...) { + stdx::lock_guard<stdx::mutex> lock(_canonicalizationMutex); + error() << "Unexpected fault has terminated hostname canonicalization worker: " + << exceptionToStatus(); + error() << "serverStatus will not report FQDNs until next server restart"; + _cachedFQDNs.clear(); + } +} + +std::vector<std::string> HostnameCanonicalizationWorker::getCanonicalizedFQDNs() const { + stdx::lock_guard<stdx::mutex> lock(_canonicalizationMutex); + return _cachedFQDNs; +} + +HostnameCanonicalizationWorker* HostnameCanonicalizationWorker::get(ServiceContext* context) { + return getCanonicalizerTask(context).get(); +} + +} // namespace mongo diff --git a/src/mongo/util/net/hostname_canonicalization_worker.h b/src/mongo/util/net/hostname_canonicalization_worker.h new file mode 100644 index 00000000000..e1934466ab8 --- /dev/null +++ b/src/mongo/util/net/hostname_canonicalization_worker.h @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2015 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. + */ + +#pragma once + +#include <string> +#include <vector> + +#include "mongo/stdx/mutex.h" +#include "mongo/stdx/thread.h" + +namespace mongo { +class ServiceContext; + +/** Provides access to system's FQDNs acquired via forward and reverse resolution of its hostname. + * A backgrounded worker thread periodically acquires the most current results and stores them + * in an interal cache. + */ +class HostnameCanonicalizationWorker { +public: + /** Spawn a new worker thread to acquire FQDNs. */ + HostnameCanonicalizationWorker(); + + /** Return a copy of the FQDNs currently in the cache. */ + std::vector<std::string> getCanonicalizedFQDNs() const; + + /** Obtain the HostnameCanonicalizationWorker for a provided ServiceContext. */ + static HostnameCanonicalizationWorker* get(ServiceContext* context); + + /** Spawn the worker thread. */ + static void start(ServiceContext* context); + +private: + // This is the main loop of worker thread, which runs forever. + void _doWork(); + + // Protects _cachedFQDNs + mutable stdx::mutex _canonicalizationMutex; + + // All FQDNs found in the last pass of the background thread + std::vector<std::string> _cachedFQDNs; + + // Worker thread + stdx::thread _canonicalizationThread; +}; + +} // namespace mongo diff --git a/src/mongo/util/net/sock.h b/src/mongo/util/net/sock.h index 5393b6cca72..74a36c38c09 100644 --- a/src/mongo/util/net/sock.h +++ b/src/mongo/util/net/sock.h @@ -69,6 +69,9 @@ const int SOCK_FAMILY_UNKNOWN_ERROR = 13078; void disableNagle(int sock); +// Generate a string representation for getaddrinfo return codes +std::string getAddrInfoStrError(int code); + #if defined(_WIN32) typedef short sa_family_t; |