/** * Copyright (C) 2018-present MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the Server Side Public License, version 1, * as published by MongoDB, Inc. * * 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 * Server Side Public License for more details. * * You should have received a copy of the Server Side 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 Server Side 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 #include #include #include #include #endif #include "mongo/util/log.h" #include "mongo/util/net/sockaddr.h" #include "mongo/util/scopeguard.h" #include "mongo/util/text.h" namespace mongo { std::vector getHostFQDNs(std::string hostName, HostnameCanonicalizationMode mode) { #ifndef _WIN32 using shim_char = char; using shim_addrinfo = struct addrinfo; shim_addrinfo* info = nullptr; const auto& shim_getaddrinfo = getaddrinfo; const auto& shim_freeaddrinfo = [&info] { freeaddrinfo(info); }; 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; shim_addrinfo* info = nullptr; const auto& shim_getaddrinfo = GetAddrInfoW; const auto& shim_freeaddrinfo = [&info] { FreeAddrInfoW(info); }; const auto& shim_getnameinfo = GetNameInfoW; const auto& shim_toNativeString = toWideString; const auto& shim_fromNativeString = toUtf8String; #endif std::vector 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; auto nativeHostName = shim_toNativeString(hostName.c_str()); if ((err = shim_getaddrinfo(nativeHostName.c_str(), nullptr, &hints, &info)) != 0) { LOG(3) << "Failed to obtain address information for hostname " << hostName << ": " << getAddrInfoStrError(err); return results; } const auto guard = makeGuard(shim_freeaddrinfo); if (mode == HostnameCanonicalizationMode::kForward) { results.emplace_back(shim_fromNativeString(info->ai_canonname)); return results; } bool encounteredErrors = false; std::stringstream getNameInfoErrors; getNameInfoErrors << "Failed to obtain name info for: [ "; 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 { if (encounteredErrors) { getNameInfoErrors << ", "; } encounteredErrors = true; // Format the addrinfo structure we have into a string for reporting char ip_str[INET6_ADDRSTRLEN]; struct sockaddr* addr = p->ai_addr; void* sin_addr = nullptr; if (p->ai_family == AF_INET) { struct sockaddr_in* addr_in = reinterpret_cast(addr); sin_addr = reinterpret_cast(&addr_in->sin_addr); } else if (p->ai_family == AF_INET6) { struct sockaddr_in6* addr_in6 = reinterpret_cast(addr); sin_addr = reinterpret_cast(&addr_in6->sin6_addr); } getNameInfoErrors << "("; if (sin_addr) { invariant(inet_ntop(p->ai_family, sin_addr, ip_str, sizeof(ip_str)) != nullptr); getNameInfoErrors << ip_str; } else { getNameInfoErrors << "Unknown address family: " << p->ai_family; } getNameInfoErrors << ", \"" << getAddrInfoStrError(err) << "\")"; } } if (encounteredErrors) { LOG(3) << getNameInfoErrors.str() << " ]"; } // 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