summaryrefslogtreecommitdiff
path: root/src/mongo/util/net/sockaddr.cpp
diff options
context:
space:
mode:
authorSara Golemon <sara.golemon@mongodb.com>2017-08-10 11:44:13 -0400
committerSara Golemon <sara.golemon@mongodb.com>2017-08-17 00:51:01 -0400
commit1158a6b9b1de13ef2dd809b4515d881422163b5e (patch)
treee7e9620a155db736c5078a63cedff2d780501c39 /src/mongo/util/net/sockaddr.cpp
parent3c2e253dd0a2cb9b5cee72205eafd48c4d78b324 (diff)
downloadmongo-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.cpp113
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);