summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSpencer Jackson <spencer.jackson@mongodb.com>2015-10-14 18:15:45 -0400
committerSpencer Jackson <spencer.jackson@mongodb.com>2015-10-14 18:15:45 -0400
commite7189d7af091983e9eedc1ca30bc7f1d8e136951 (patch)
tree7dc501fd140033c0388fef8031284b24d23304c9
parenta8242b99c2455d2c95ae529d8717b268d1fbf024 (diff)
downloadmongo-e7189d7af091983e9eedc1ca30bc7f1d8e136951.tar.gz
SERVER-2421 SERVER-17739 Add FQDN canonicalization for serverStatus and SPNs
-rw-r--r--src/mongo/client/SConscript3
-rw-r--r--src/mongo/client/sasl_sspi.cpp33
-rw-r--r--src/mongo/client/sasl_sspi_options.cpp85
-rw-r--r--src/mongo/client/sasl_sspi_options.h54
-rw-r--r--src/mongo/db/commands/SConscript1
-rw-r--r--src/mongo/db/commands/server_status.cpp10
-rw-r--r--src/mongo/db/db.cpp3
-rw-r--r--src/mongo/s/server.cpp3
-rw-r--r--src/mongo/util/net/SConscript13
-rw-r--r--src/mongo/util/net/hostname_canonicalization.cpp128
-rw-r--r--src/mongo/util/net/hostname_canonicalization.h57
-rw-r--r--src/mongo/util/net/hostname_canonicalization_worker.cpp92
-rw-r--r--src/mongo/util/net/hostname_canonicalization_worker.h72
-rw-r--r--src/mongo/util/net/sock.h3
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;