diff options
author | Sara Golemon <sara.golemon@mongodb.com> | 2017-08-10 11:44:13 -0400 |
---|---|---|
committer | Sara Golemon <sara.golemon@mongodb.com> | 2017-08-17 00:51:01 -0400 |
commit | 1158a6b9b1de13ef2dd809b4515d881422163b5e (patch) | |
tree | e7e9620a155db736c5078a63cedff2d780501c39 /src/mongo/util/net/sockaddr.cpp | |
parent | 3c2e253dd0a2cb9b5cee72205eafd48c4d78b324 (diff) | |
download | mongo-1158a6b9b1de13ef2dd809b4515d881422163b5e.tar.gz |
SERVER-30588 Bind all addresses returned by getaddroinfo()
Diffstat (limited to 'src/mongo/util/net/sockaddr.cpp')
-rw-r--r-- | src/mongo/util/net/sockaddr.cpp | 113 |
1 files changed, 76 insertions, 37 deletions
diff --git a/src/mongo/util/net/sockaddr.cpp b/src/mongo/util/net/sockaddr.cpp index 0b3ad9c0ec0..ca7a391399c 100644 --- a/src/mongo/util/net/sockaddr.cpp +++ b/src/mongo/util/net/sockaddr.cpp @@ -53,6 +53,37 @@ namespace mongo { namespace { constexpr int SOCK_FAMILY_UNKNOWN_ERROR = 13078; + +using AddrInfo = std::unique_ptr<addrinfo, decltype(&freeaddrinfo)>; + +std::pair<int, AddrInfo> resolveAddrInfo(const std::string& hostOrIp, + int port, + sa_family_t familyHint) { + addrinfo hints; + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags |= AI_NUMERICHOST; // first pass tries w/o DNS lookup + hints.ai_family = familyHint; + addrinfo* addrs = nullptr; + + ItoA portStr(port); + int ret = getaddrinfo(hostOrIp.c_str(), StringData(portStr).rawData(), &hints, &addrs); + +// old C compilers on IPv6-capable hosts return EAI_NODATA error +#ifdef EAI_NODATA + int nodata = (ret == EAI_NODATA); +#else + int nodata = false; +#endif + if ((ret == EAI_NONAME) || nodata) { + // iporhost isn't an IP address, allow DNS lookup + hints.ai_flags &= ~AI_NUMERICHOST; + ret = getaddrinfo(hostOrIp.c_str(), StringData(portStr).rawData(), &hints, &addrs); + } + + return {ret, AddrInfo(addrs, &freeaddrinfo)}; +} + } // namespace std::string getAddrInfoStrError(int code) { @@ -80,6 +111,18 @@ SockAddr::SockAddr(int sourcePort) { _isValid = true; } +void SockAddr::initUnixDomainSocket(const std::string& path, int port) { +#ifdef _WIN32 + uassert(13080, "no unix socket support on windows", false); +#endif + uassert( + 13079, "path to unix socket too long", path.size() < sizeof(as<sockaddr_un>().sun_path)); + as<sockaddr_un>().sun_family = AF_UNIX; + strcpy(as<sockaddr_un>().sun_path, path.c_str()); + addressSize = sizeof(sockaddr_un); + _isValid = true; +} + SockAddr::SockAddr(StringData target, int port, sa_family_t familyHint) : _hostOrIp(target.toString()) { if (_hostOrIp == "localhost") { @@ -87,48 +130,18 @@ SockAddr::SockAddr(StringData target, int port, sa_family_t familyHint) } if (mongoutils::str::contains(_hostOrIp, '/')) { -#ifdef _WIN32 - uassert(13080, "no unix socket support on windows", false); -#endif - uassert(13079, - "path to unix socket too long", - _hostOrIp.size() < sizeof(as<sockaddr_un>().sun_path)); - as<sockaddr_un>().sun_family = AF_UNIX; - strcpy(as<sockaddr_un>().sun_path, _hostOrIp.c_str()); - addressSize = sizeof(sockaddr_un); - _isValid = true; + initUnixDomainSocket(_hostOrIp, port); return; } - addrinfo* addrs = NULL; - addrinfo hints; - memset(&hints, 0, sizeof(addrinfo)); - hints.ai_socktype = SOCK_STREAM; - // hints.ai_flags = AI_ADDRCONFIG; // This is often recommended but don't do it. - // SERVER-1579 - hints.ai_flags |= AI_NUMERICHOST; // first pass tries w/o DNS lookup - hints.ai_family = familyHint; - - ItoA portStr(port); - int ret = getaddrinfo(_hostOrIp.c_str(), StringData(portStr).rawData(), &hints, &addrs); - -// old C compilers on IPv6-capable hosts return EAI_NODATA error -#ifdef EAI_NODATA - int nodata = (ret == EAI_NODATA); -#else - int nodata = false; -#endif - if ((ret == EAI_NONAME || nodata)) { - // iporhost isn't an IP address, allow DNS lookup - hints.ai_flags &= ~AI_NUMERICHOST; - ret = getaddrinfo(_hostOrIp.c_str(), StringData(portStr).rawData(), &hints, &addrs); - } + auto addrErr = resolveAddrInfo(_hostOrIp, port, familyHint); - if (ret) { + if (addrErr.first) { // we were unsuccessful if (_hostOrIp != "0.0.0.0") { // don't log if this as it is a // CRT construction and log() may not work yet. - log() << "getaddrinfo(\"" << _hostOrIp << "\") failed: " << getAddrInfoStrError(ret); + log() << "getaddrinfo(\"" << _hostOrIp + << "\") failed: " << getAddrInfoStrError(addrErr.first); _isValid = false; return; } @@ -136,14 +149,40 @@ SockAddr::SockAddr(StringData target, int port, sa_family_t familyHint) return; } - // TODO: handle other addresses in linked list; + // This throws away all but the first address. + // Use SockAddr::createAll() to get all addresses. + const auto* addrs = addrErr.second.get(); fassert(16501, addrs->ai_addrlen <= sizeof(sa)); memcpy(&sa, addrs->ai_addr, addrs->ai_addrlen); addressSize = addrs->ai_addrlen; - freeaddrinfo(addrs); _isValid = true; } +std::vector<SockAddr> SockAddr::createAll(StringData target, int port, sa_family_t familyHint) { + std::string hostOrIp = target.toString(); + if (mongoutils::str::contains(hostOrIp, '/')) { + std::vector<SockAddr> ret = {SockAddr()}; + ret[0].initUnixDomainSocket(hostOrIp, port); + // Currently, this is always valid since initUnixDomainSocket() + // will uassert() on failure. Be defensive against future changes. + return ret[0].isValid() ? ret : std::vector<SockAddr>(); + } + + auto addrErr = resolveAddrInfo(hostOrIp, port, familyHint); + if (addrErr.first) { + log() << "getaddrinfo(\"" << hostOrIp + << "\") failed: " << getAddrInfoStrError(addrErr.first); + return {}; + } + + std::vector<SockAddr> ret; + for (const auto* addrs = addrErr.second.get(); addrs; addrs = addrs->ai_next) { + fassert(40594, addrs->ai_addrlen <= sizeof(struct sockaddr_storage)); + ret.emplace_back(*(struct sockaddr_storage*)addrs->ai_addr, addrs->ai_addrlen); + } + return ret; +} + SockAddr::SockAddr(struct sockaddr_storage& other, socklen_t size) : addressSize(size), _hostOrIp(), sa(other), _isValid(true) { _hostOrIp = toString(true); |