ScopeGuard.h \
SmallLocks.h \
small_vector.h \
+ SocketAddress.h \
sorted_vector_types.h \
SpookyHashV1.h \
SpookyHashV2.h \
MemoryMapping.cpp \
Random.cpp \
SafeAssert.cpp \
+ SocketAddress.cpp \
SpookyHashV1.cpp \
SpookyHashV2.cpp \
stats/Instantiations.cpp \
--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __STDC_FORMAT_MACROS
+ #define __STDC_FORMAT_MACROS
+#endif
+
+#include <folly/SocketAddress.h>
+
+#include <folly/Hash.h>
+
+#include <boost/functional/hash.hpp>
+#include <boost/static_assert.hpp>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sstream>
+#include <string>
+
+namespace {
+
+/**
+ * A structure to free a struct addrinfo when it goes out of scope.
+ */
+struct ScopedAddrInfo {
+ explicit ScopedAddrInfo(struct addrinfo* info) : info(info) {}
+ ~ScopedAddrInfo() {
+ freeaddrinfo(info);
+ }
+
+ struct addrinfo* info;
+};
+
+/**
+ * A simple data structure for parsing a host-and-port string.
+ *
+ * Accepts a string of the form "<host>:<port>" or just "<port>",
+ * and contains two string pointers to the host and the port portion of the
+ * string.
+ *
+ * The HostAndPort may contain pointers into the original string. It is
+ * responsible for the user to ensure that the input string is valid for the
+ * lifetime of the HostAndPort structure.
+ */
+struct HostAndPort {
+ HostAndPort(const char* str, bool hostRequired)
+ : host(nullptr),
+ port(nullptr),
+ allocated(nullptr) {
+
+ // Look for the last colon
+ const char* colon = strrchr(str, ':');
+ if (colon == nullptr) {
+ // No colon, just a port number.
+ if (hostRequired) {
+ throw std::invalid_argument(
+ "expected a host and port string of the "
+ "form \"<host>:<port>\"");
+ }
+ port = str;
+ return;
+ }
+
+ // We have to make a copy of the string so we can modify it
+ // and change the colon to a NUL terminator.
+ allocated = strdup(str);
+ if (!allocated) {
+ throw std::bad_alloc();
+ }
+
+ char *allocatedColon = allocated + (colon - str);
+ *allocatedColon = '\0';
+ host = allocated;
+ port = allocatedColon + 1;
+ // bracketed IPv6 address, remove the brackets
+ // allocatedColon[-1] is fine, as allocatedColon >= host and
+ // *allocatedColon != *host therefore allocatedColon > host
+ if (*host == '[' && allocatedColon[-1] == ']') {
+ allocatedColon[-1] = '\0';
+ ++host;
+ }
+ }
+
+ ~HostAndPort() {
+ free(allocated);
+ }
+
+ const char* host;
+ const char* port;
+ char* allocated;
+};
+
+} // unnamed namespace
+
+namespace folly {
+
+bool SocketAddress::isPrivateAddress() const {
+ auto family = getFamily();
+ if (family == AF_INET || family == AF_INET6) {
+ return storage_.addr.isPrivate() ||
+ (storage_.addr.isV6() && storage_.addr.asV6().isLinkLocal());
+ } else if (family == AF_UNIX) {
+ // Unix addresses are always local to a host. Return true,
+ // since this conforms to the semantics of returning true for IP loopback
+ // addresses.
+ return true;
+ }
+ return false;
+}
+
+bool SocketAddress::isLoopbackAddress() const {
+ auto family = getFamily();
+ if (family == AF_INET || family == AF_INET6) {
+ return storage_.addr.isLoopback();
+ } else if (family == AF_UNIX) {
+ // Return true for UNIX addresses, since they are always local to a host.
+ return true;
+ }
+ return false;
+}
+
+void SocketAddress::setFromHostPort(const char* host, uint16_t port) {
+ ScopedAddrInfo results(getAddrInfo(host, port, 0));
+ setFromAddrInfo(results.info);
+}
+
+void SocketAddress::setFromIpPort(const char* ip, uint16_t port) {
+ ScopedAddrInfo results(getAddrInfo(ip, port, AI_NUMERICHOST));
+ setFromAddrInfo(results.info);
+}
+
+void SocketAddress::setFromLocalPort(uint16_t port) {
+ ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG));
+ setFromLocalAddr(results.info);
+}
+
+void SocketAddress::setFromLocalPort(const char* port) {
+ ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG));
+ setFromLocalAddr(results.info);
+}
+
+void SocketAddress::setFromLocalIpPort(const char* addressAndPort) {
+ HostAndPort hp(addressAndPort, false);
+ ScopedAddrInfo results(getAddrInfo(hp.host, hp.port,
+ AI_NUMERICHOST | AI_ADDRCONFIG));
+ setFromLocalAddr(results.info);
+}
+
+void SocketAddress::setFromIpPort(const char* addressAndPort) {
+ HostAndPort hp(addressAndPort, true);
+ ScopedAddrInfo results(getAddrInfo(hp.host, hp.port, AI_NUMERICHOST));
+ setFromAddrInfo(results.info);
+}
+
+void SocketAddress::setFromHostPort(const char* hostAndPort) {
+ HostAndPort hp(hostAndPort, true);
+ ScopedAddrInfo results(getAddrInfo(hp.host, hp.port, 0));
+ setFromAddrInfo(results.info);
+}
+
+void SocketAddress::setFromPath(const char* path, size_t len) {
+ if (getFamily() != AF_UNIX) {
+ storage_.un.init();
+ external_ = true;
+ }
+
+ storage_.un.len = offsetof(struct sockaddr_un, sun_path) + len;
+ if (len > sizeof(storage_.un.addr->sun_path)) {
+ throw std::invalid_argument(
+ "socket path too large to fit into sockaddr_un");
+ } else if (len == sizeof(storage_.un.addr->sun_path)) {
+ // Note that there will be no terminating NUL in this case.
+ // We allow this since getsockname() and getpeername() may return
+ // Unix socket addresses with paths that fit exactly in sun_path with no
+ // terminating NUL.
+ memcpy(storage_.un.addr->sun_path, path, len);
+ } else {
+ memcpy(storage_.un.addr->sun_path, path, len + 1);
+ }
+}
+
+void SocketAddress::setFromPeerAddress(int socket) {
+ setFromSocket(socket, getpeername);
+}
+
+void SocketAddress::setFromLocalAddress(int socket) {
+ setFromSocket(socket, getsockname);
+}
+
+void SocketAddress::setFromSockaddr(const struct sockaddr* address) {
+ if (address->sa_family == AF_INET) {
+ storage_.addr = folly::IPAddress(address);
+ port_ = ntohs(((sockaddr_in*)address)->sin_port);
+ } else if (address->sa_family == AF_INET6) {
+ storage_.addr = folly::IPAddress(address);
+ port_ = ntohs(((sockaddr_in6*)address)->sin6_port);
+ } else if (address->sa_family == AF_UNIX) {
+ // We need an explicitly specified length for AF_UNIX addresses,
+ // to be able to distinguish anonymous addresses from addresses
+ // in Linux's abstract namespace.
+ throw std::invalid_argument(
+ "SocketAddress::setFromSockaddr(): the address "
+ "length must be explicitly specified when "
+ "setting AF_UNIX addresses");
+ } else {
+ throw std::invalid_argument(
+ "SocketAddress::setFromSockaddr() called "
+ "with unsupported address type");
+ }
+ external_ = false;
+}
+
+void SocketAddress::setFromSockaddr(const struct sockaddr* address,
+ socklen_t addrlen) {
+ // Check the length to make sure we can access address->sa_family
+ if (addrlen < (offsetof(struct sockaddr, sa_family) +
+ sizeof(address->sa_family))) {
+ throw std::invalid_argument(
+ "SocketAddress::setFromSockaddr() called "
+ "with length too short for a sockaddr");
+ }
+
+ if (address->sa_family == AF_INET) {
+ if (addrlen < sizeof(struct sockaddr_in)) {
+ throw std::invalid_argument(
+ "SocketAddress::setFromSockaddr() called "
+ "with length too short for a sockaddr_in");
+ }
+ setFromSockaddr(reinterpret_cast<const struct sockaddr_in*>(address));
+ } else if (address->sa_family == AF_INET6) {
+ if (addrlen < sizeof(struct sockaddr_in6)) {
+ throw std::invalid_argument(
+ "SocketAddress::setFromSockaddr() called "
+ "with length too short for a sockaddr_in6");
+ }
+ setFromSockaddr(reinterpret_cast<const struct sockaddr_in6*>(address));
+ } else if (address->sa_family == AF_UNIX) {
+ setFromSockaddr(reinterpret_cast<const struct sockaddr_un*>(address),
+ addrlen);
+ } else {
+ throw std::invalid_argument(
+ "SocketAddress::setFromSockaddr() called "
+ "with unsupported address type");
+ }
+}
+
+void SocketAddress::setFromSockaddr(const struct sockaddr_in* address) {
+ assert(address->sin_family == AF_INET);
+ setFromSockaddr((sockaddr*)address);
+}
+
+void SocketAddress::setFromSockaddr(const struct sockaddr_in6* address) {
+ assert(address->sin6_family == AF_INET6);
+ setFromSockaddr((sockaddr*)address);
+}
+
+void SocketAddress::setFromSockaddr(const struct sockaddr_un* address,
+ socklen_t addrlen) {
+ assert(address->sun_family == AF_UNIX);
+ if (addrlen > sizeof(struct sockaddr_un)) {
+ throw std::invalid_argument(
+ "SocketAddress::setFromSockaddr() called "
+ "with length too long for a sockaddr_un");
+ }
+
+ prepFamilyChange(AF_UNIX);
+ memcpy(storage_.un.addr, address, addrlen);
+ updateUnixAddressLength(addrlen);
+
+ // Fill the rest with 0s, just for safety
+ if (addrlen < sizeof(struct sockaddr_un)) {
+ char *p = reinterpret_cast<char*>(storage_.un.addr);
+ memset(p + addrlen, 0, sizeof(struct sockaddr_un) - addrlen);
+ }
+}
+
+const folly::IPAddress& SocketAddress::getIPAddress() const {
+ auto family = getFamily();
+ if (family != AF_INET && family != AF_INET6) {
+ throw std::invalid_argument("getIPAddress called on a non-ip address");
+ }
+ return storage_.addr;
+}
+
+socklen_t SocketAddress::getActualSize() const {
+ switch (getFamily()) {
+ case AF_UNSPEC:
+ case AF_INET:
+ return sizeof(struct sockaddr_in);
+ case AF_INET6:
+ return sizeof(struct sockaddr_in6);
+ case AF_UNIX:
+ return storage_.un.len;
+ default:
+ throw std::invalid_argument(
+ "SocketAddress::getActualSize() called "
+ "with unrecognized address family");
+ }
+}
+
+std::string SocketAddress::getFullyQualified() const {
+ auto family = getFamily();
+ if (family != AF_INET && family != AF_INET6) {
+ throw std::invalid_argument("Can't get address str for non ip address");
+ }
+ return storage_.addr.toFullyQualified();
+}
+
+std::string SocketAddress::getAddressStr() const {
+ char buf[INET6_ADDRSTRLEN];
+ getAddressStr(buf, sizeof(buf));
+ return buf;
+}
+
+void SocketAddress::getAddressStr(char* buf, size_t buflen) const {
+ auto family = getFamily();
+ if (family != AF_INET && family != AF_INET6) {
+ throw std::invalid_argument("Can't get address str for non ip address");
+ }
+ std::string ret = storage_.addr.str();
+ size_t len = std::min(buflen, ret.size());
+ memcpy(buf, ret.data(), len);
+ buf[len] = '\0';
+}
+
+uint16_t SocketAddress::getPort() const {
+ switch (getFamily()) {
+ case AF_INET:
+ case AF_INET6:
+ return port_;
+ default:
+ throw std::invalid_argument(
+ "SocketAddress::getPort() called on non-IP "
+ "address");
+ }
+}
+
+void SocketAddress::setPort(uint16_t port) {
+ switch (getFamily()) {
+ case AF_INET:
+ case AF_INET6:
+ port_ = port;
+ return;
+ default:
+ throw std::invalid_argument(
+ "SocketAddress::setPort() called on non-IP "
+ "address");
+ }
+}
+
+void SocketAddress::convertToIPv4() {
+ if (!tryConvertToIPv4()) {
+ throw std::invalid_argument(
+ "convertToIPv4() called on an addresse that is "
+ "not an IPv4-mapped address");
+ }
+}
+
+bool SocketAddress::tryConvertToIPv4() {
+ if (!isIPv4Mapped()) {
+ return false;
+ }
+
+ storage_.addr = folly::IPAddress::createIPv4(storage_.addr);
+ return true;
+}
+
+bool SocketAddress::mapToIPv6() {
+ if (getFamily() != AF_INET) {
+ return false;
+ }
+
+ storage_.addr = folly::IPAddress::createIPv6(storage_.addr);
+ return true;
+}
+
+std::string SocketAddress::getHostStr() const {
+ return getIpString(0);
+}
+
+std::string SocketAddress::getPath() const {
+ if (getFamily() != AF_UNIX) {
+ throw std::invalid_argument(
+ "SocketAddress: attempting to get path "
+ "for a non-Unix address");
+ }
+
+ if (storage_.un.pathLength() == 0) {
+ // anonymous address
+ return std::string();
+ }
+ if (storage_.un.addr->sun_path[0] == '\0') {
+ // abstract namespace
+ return std::string(storage_.un.addr->sun_path, storage_.un.pathLength());
+ }
+
+ return std::string(storage_.un.addr->sun_path,
+ strnlen(storage_.un.addr->sun_path,
+ storage_.un.pathLength()));
+}
+
+std::string SocketAddress::describe() const {
+ switch (getFamily()) {
+ case AF_UNSPEC:
+ return "<uninitialized address>";
+ case AF_INET:
+ {
+ char buf[NI_MAXHOST + 16];
+ getAddressStr(buf, sizeof(buf));
+ size_t iplen = strlen(buf);
+ snprintf(buf + iplen, sizeof(buf) - iplen, ":%" PRIu16, getPort());
+ return buf;
+ }
+ case AF_INET6:
+ {
+ char buf[NI_MAXHOST + 18];
+ buf[0] = '[';
+ getAddressStr(buf + 1, sizeof(buf) - 1);
+ size_t iplen = strlen(buf);
+ snprintf(buf + iplen, sizeof(buf) - iplen, "]:%" PRIu16, getPort());
+ return buf;
+ }
+ case AF_UNIX:
+ {
+ if (storage_.un.pathLength() == 0) {
+ return "<anonymous unix address>";
+ }
+
+ if (storage_.un.addr->sun_path[0] == '\0') {
+ // Linux supports an abstract namespace for unix socket addresses
+ return "<abstract unix address>";
+ }
+
+ return std::string(storage_.un.addr->sun_path,
+ strnlen(storage_.un.addr->sun_path,
+ storage_.un.pathLength()));
+ }
+ default:
+ {
+ char buf[64];
+ snprintf(buf, sizeof(buf), "<unknown address family %d>",
+ getFamily());
+ return buf;
+ }
+ }
+}
+
+bool SocketAddress::operator==(const SocketAddress& other) const {
+ if (other.getFamily() != getFamily()) {
+ return false;
+ }
+
+ switch (getFamily()) {
+ case AF_INET:
+ case AF_INET6:
+ return (other.storage_.addr == storage_.addr) &&
+ (other.port_ == port_);
+ case AF_UNIX:
+ {
+ // anonymous addresses are never equal to any other addresses
+ if (storage_.un.pathLength() == 0 ||
+ other.storage_.un.pathLength() == 0) {
+ return false;
+ }
+
+ if (storage_.un.len != other.storage_.un.len) {
+ return false;
+ }
+ int cmp = memcmp(storage_.un.addr->sun_path,
+ other.storage_.un.addr->sun_path,
+ storage_.un.pathLength());
+ return cmp == 0;
+ }
+ default:
+ throw std::invalid_argument(
+ "SocketAddress: unsupported address family "
+ "for comparison");
+ }
+}
+
+bool SocketAddress::prefixMatch(const SocketAddress& other,
+ unsigned prefixLength) const {
+ if (other.getFamily() != getFamily()) {
+ return false;
+ }
+ int mask_length = 128;
+ switch (getFamily()) {
+ case AF_INET:
+ mask_length = 32;
+ // fallthrough
+ case AF_INET6:
+ {
+ auto prefix = folly::IPAddress::longestCommonPrefix(
+ {storage_.addr, mask_length},
+ {other.storage_.addr, mask_length});
+ return prefix.second >= prefixLength;
+ }
+ default:
+ return false;
+ }
+}
+
+
+size_t SocketAddress::hash() const {
+ size_t seed = folly::hash::twang_mix64(getFamily());
+
+ switch (getFamily()) {
+ case AF_INET:
+ case AF_INET6: {
+ boost::hash_combine(seed, port_);
+ boost::hash_combine(seed, storage_.addr.hash());
+ break;
+ }
+ case AF_UNIX:
+ {
+ enum { kUnixPathMax = sizeof(storage_.un.addr->sun_path) };
+ const char *path = storage_.un.addr->sun_path;
+ size_t pathLength = storage_.un.pathLength();
+ // TODO: this probably could be made more efficient
+ for (unsigned int n = 0; n < pathLength; ++n) {
+ boost::hash_combine(seed, folly::hash::twang_mix64(path[n]));
+ }
+ break;
+ }
+ case AF_UNSPEC:
+ default:
+ throw std::invalid_argument(
+ "SocketAddress: unsupported address family "
+ "for hashing");
+ }
+
+ return seed;
+}
+
+struct addrinfo* SocketAddress::getAddrInfo(const char* host,
+ uint16_t port,
+ int flags) {
+ // getaddrinfo() requires the port number as a string
+ char portString[sizeof("65535")];
+ snprintf(portString, sizeof(portString), "%" PRIu16, port);
+
+ return getAddrInfo(host, portString, flags);
+}
+
+struct addrinfo* SocketAddress::getAddrInfo(const char* host,
+ const char* port,
+ int flags) {
+ struct addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | flags;
+
+ struct addrinfo *results;
+ int error = getaddrinfo(host, port, &hints, &results);
+ if (error != 0) {
+ auto os = folly::to<std::string>(
+ "Failed to resolve address for \"", host, "\": ",
+ gai_strerror(error), " (error=", error, ")");
+ throw std::system_error(error, std::generic_category(), os);
+ }
+
+ return results;
+}
+
+void SocketAddress::setFromAddrInfo(const struct addrinfo* info) {
+ setFromSockaddr(info->ai_addr, info->ai_addrlen);
+}
+
+void SocketAddress::setFromLocalAddr(const struct addrinfo* info) {
+ // If an IPv6 address is present, prefer to use it, since IPv4 addresses
+ // can be mapped into IPv6 space.
+ for (const struct addrinfo* ai = info; ai != nullptr; ai = ai->ai_next) {
+ if (ai->ai_family == AF_INET6) {
+ setFromSockaddr(ai->ai_addr, ai->ai_addrlen);
+ return;
+ }
+ }
+
+ // Otherwise, just use the first address in the list.
+ setFromSockaddr(info->ai_addr, info->ai_addrlen);
+}
+
+void SocketAddress::setFromSocket(int socket,
+ int (*fn)(int, sockaddr*, socklen_t*)) {
+ // If this was previously an AF_UNIX socket, free the external buffer.
+ // TODO: It would be smarter to just remember the external buffer, and then
+ // re-use it or free it depending on if the new address is also a unix
+ // socket.
+ if (getFamily() == AF_UNIX) {
+ storage_.un.free();
+ external_ = false;
+ }
+
+ // Try to put the address into a local storage buffer.
+ sockaddr_storage tmp_sock;
+ socklen_t addrLen = sizeof(tmp_sock);
+ if (fn(socket, (sockaddr*)&tmp_sock, &addrLen) != 0) {
+ folly::throwSystemError("setFromSocket() failed");
+ }
+
+ setFromSockaddr((sockaddr*)&tmp_sock, addrLen);
+}
+
+std::string SocketAddress::getIpString(int flags) const {
+ char addrString[NI_MAXHOST];
+ getIpString(addrString, sizeof(addrString), flags);
+ return std::string(addrString);
+}
+
+void SocketAddress::getIpString(char *buf, size_t buflen, int flags) const {
+ auto family = getFamily();
+ if (family != AF_INET &&
+ family != AF_INET6) {
+ throw std::invalid_argument(
+ "SocketAddress: attempting to get IP address "
+ "for a non-IP address");
+ }
+
+ sockaddr_storage tmp_sock;
+ storage_.addr.toSockaddrStorage(&tmp_sock, port_);
+ int rc = getnameinfo((sockaddr*)&tmp_sock, sizeof(sockaddr_storage),
+ buf, buflen, nullptr, 0, flags);
+ if (rc != 0) {
+ auto os = folly::to<std::string>(
+ "getnameinfo() failed in getIpString() error = ",
+ gai_strerror(rc));
+ throw std::system_error(rc, std::generic_category(), os);
+ }
+}
+
+void SocketAddress::updateUnixAddressLength(socklen_t addrlen) {
+ if (addrlen < offsetof(struct sockaddr_un, sun_path)) {
+ throw std::invalid_argument(
+ "SocketAddress: attempted to set a Unix socket "
+ "with a length too short for a sockaddr_un");
+ }
+
+ storage_.un.len = addrlen;
+ if (storage_.un.pathLength() == 0) {
+ // anonymous address
+ return;
+ }
+
+ if (storage_.un.addr->sun_path[0] == '\0') {
+ // abstract namespace. honor the specified length
+ } else {
+ // Call strnlen(), just in case the length was overspecified.
+ socklen_t maxLength = addrlen - offsetof(struct sockaddr_un, sun_path);
+ size_t pathLength = strnlen(storage_.un.addr->sun_path, maxLength);
+ storage_.un.len = offsetof(struct sockaddr_un, sun_path) + pathLength;
+ }
+}
+
+bool SocketAddress::operator<(const SocketAddress& other) const {
+ if (getFamily() != other.getFamily()) {
+ return getFamily() < other.getFamily();
+ }
+
+ switch (getFamily()) {
+ case AF_INET:
+ case AF_INET6: {
+ if (port_ != other.port_) {
+ return port_ < other.port_;
+ }
+
+ return
+ storage_.addr < other.storage_.addr;
+ }
+ case AF_UNIX: {
+ // Anonymous addresses can't be compared to anything else.
+ // Return that they are never less than anything.
+ //
+ // Note that this still meets the requirements for a strict weak
+ // ordering, so we can use this operator<() with standard C++ containers.
+ size_t thisPathLength = storage_.un.pathLength();
+ if (thisPathLength == 0) {
+ return false;
+ }
+ size_t otherPathLength = other.storage_.un.pathLength();
+ if (otherPathLength == 0) {
+ return true;
+ }
+
+ // Compare based on path length first, for efficiency
+ if (thisPathLength != otherPathLength) {
+ return thisPathLength < otherPathLength;
+ }
+ int cmp = memcmp(storage_.un.addr->sun_path,
+ other.storage_.un.addr->sun_path,
+ thisPathLength);
+ return cmp < 0;
+ }
+ case AF_UNSPEC:
+ default:
+ throw std::invalid_argument(
+ "SocketAddress: unsupported address family for comparing");
+ }
+}
+
+size_t hash_value(const SocketAddress& address) {
+ return address.hash();
+}
+
+std::ostream& operator<<(std::ostream& os, const SocketAddress& addr) {
+ os << addr.describe();
+ return os;
+}
+
+} // folly
--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <features.h>
+#include <netdb.h>
+#include <cstddef>
+#include <iostream>
+#include <string>
+
+#include <folly/IPAddress.h>
+
+namespace folly {
+
+class SocketAddress {
+ public:
+ SocketAddress() {
+ storage_.addr = folly::IPAddress();
+ }
+
+ /**
+ * Construct a SocketAddress from a hostname and port.
+ *
+ * Note: If the host parameter is not a numeric IP address, hostname
+ * resolution will be performed, which can be quite slow.
+ *
+ * Raises std::system_error on error.
+ *
+ * @param host The IP address (or hostname, if allowNameLookup is true)
+ * @param port The port (in host byte order)
+ * @pram allowNameLookup If true, attempt to perform hostname lookup
+ * if the hostname does not appear to be a numeric IP address.
+ * This is potentially a very slow operation, so is disabled by
+ * default.
+ */
+ SocketAddress(const char* host, uint16_t port,
+ bool allowNameLookup = false) {
+ // Initialize the address family first,
+ // since setFromHostPort() and setFromIpPort() will check it.
+
+ if (allowNameLookup) {
+ setFromHostPort(host, port);
+ } else {
+ setFromIpPort(host, port);
+ }
+ }
+
+ SocketAddress(const std::string& host, uint16_t port,
+ bool allowNameLookup = false) {
+ // Initialize the address family first,
+ // since setFromHostPort() and setFromIpPort() will check it.
+
+ if (allowNameLookup) {
+ setFromHostPort(host.c_str(), port);
+ } else {
+ setFromIpPort(host.c_str(), port);
+ }
+ }
+
+ SocketAddress(const SocketAddress& addr) {
+ storage_ = addr.storage_;
+ port_ = addr.port_;
+ if (addr.getFamily() == AF_UNIX) {
+ storage_.un.init(addr.storage_.un);
+ }
+ external_ = addr.external_;
+ }
+
+ SocketAddress& operator=(const SocketAddress& addr) {
+ if (getFamily() != AF_UNIX) {
+ if (addr.getFamily() != AF_UNIX) {
+ storage_ = addr.storage_;
+ } else {
+ storage_ = addr.storage_;
+ storage_.un.init(addr.storage_.un);
+ }
+ } else {
+ if (addr.getFamily() == AF_UNIX) {
+ storage_.un.copy(addr.storage_.un);
+ } else {
+ storage_.un.free();
+ storage_ = addr.storage_;
+ }
+ }
+ port_ = addr.port_;
+ external_ = addr.external_;
+ return *this;
+ }
+
+ SocketAddress(SocketAddress&& addr) {
+ storage_ = addr.storage_;
+ port_ = addr.port_;
+ external_ = addr.external_;
+ addr.external_ = false;
+ }
+
+ SocketAddress& operator=(SocketAddress&& addr) {
+ std::swap(storage_, addr.storage_);
+ std::swap(port_, addr.port_);
+ std::swap(external_, addr.external_);
+ return *this;
+ }
+
+ ~SocketAddress() {
+ if (getFamily() == AF_UNIX) {
+ storage_.un.free();
+ }
+ }
+
+ bool isInitialized() const {
+ return (getFamily() != AF_UNSPEC);
+ }
+
+ /**
+ * Return whether this address is within private network.
+ *
+ * According to RFC1918, the 10/8 prefix, 172.16/12 prefix, and 192.168/16
+ * prefix are reserved for private networks.
+ * fc00::/7 is the IPv6 version, defined in RFC4139. IPv6 link-local
+ * addresses (fe80::/10) are also considered private addresses.
+ *
+ * The loopback addresses 127/8 and ::1 are also regarded as private networks
+ * for the purpose of this function.
+ *
+ * Returns true if this is a private network address, and false otherwise.
+ */
+ bool isPrivateAddress() const;
+
+ /**
+ * Return whether this address is a loopback address.
+ */
+ bool isLoopbackAddress() const;
+
+ void reset() {
+ prepFamilyChange(AF_UNSPEC);
+ }
+
+ /**
+ * Initialize this SocketAddress from a hostname and port.
+ *
+ * Note: If the host parameter is not a numeric IP address, hostname
+ * resolution will be performed, which can be quite slow.
+ *
+ * If the hostname resolves to multiple addresses, only the first will be
+ * returned.
+ *
+ * Raises std::system_error on error.
+ *
+ * @param host The hostname or IP address
+ * @param port The port (in host byte order)
+ */
+ void setFromHostPort(const char* host, uint16_t port);
+
+ void setFromHostPort(const std::string& host, uint16_t port) {
+ setFromHostPort(host.c_str(), port);
+ }
+
+ /**
+ * Initialize this SocketAddress from an IP address and port.
+ *
+ * This is similar to setFromHostPort(), but only accepts numeric IP
+ * addresses. If the IP string does not look like an IP address, it throws a
+ * std::invalid_argument rather than trying to perform a hostname resolution.
+ *
+ * Raises std::system_error on error.
+ *
+ * @param ip The IP address, as a human-readable string.
+ * @param port The port (in host byte order)
+ */
+ void setFromIpPort(const char* ip, uint16_t port);
+
+ void setFromIpPort(const std::string& ip, uint16_t port) {
+ setFromIpPort(ip.c_str(), port);
+ }
+
+ /**
+ * Initialize this SocketAddress from a local port number.
+ *
+ * This is intended to be used by server code to determine the address to
+ * listen on.
+ *
+ * If the current machine has any IPv6 addresses configured, an IPv6 address
+ * will be returned (since connections from IPv4 clients can be mapped to the
+ * IPv6 address). If the machine does not have any IPv6 addresses, an IPv4
+ * address will be returned.
+ */
+ void setFromLocalPort(uint16_t port);
+
+ /**
+ * Initialize this SocketAddress from a local port number.
+ *
+ * This version of setFromLocalPort() accepts the port as a string. A
+ * std::invalid_argument will be raised if the string does not refer to a port
+ * number. Non-numeric service port names are not accepted.
+ */
+ void setFromLocalPort(const char* port);
+ void setFromLocalPort(const std::string& port) {
+ return setFromLocalPort(port.c_str());
+ }
+
+ /**
+ * Initialize this SocketAddress from a local port number and optional IP
+ * address.
+ *
+ * The addressAndPort string may be specified either as "<ip>:<port>", or
+ * just as "<port>". If the IP is not specified, the address will be
+ * initialized to 0, so that a server socket bound to this address will
+ * accept connections on all local IP addresses.
+ *
+ * Both the IP address and port number must be numeric. DNS host names and
+ * non-numeric service port names are not accepted.
+ */
+ void setFromLocalIpPort(const char* addressAndPort);
+ void setFromLocalIpPort(const std::string& addressAndPort) {
+ return setFromLocalIpPort(addressAndPort.c_str());
+ }
+
+ /**
+ * Initialize this SocketAddress from an IP address and port number.
+ *
+ * The addressAndPort string must be of the form "<ip>:<port>". E.g.,
+ * "10.0.0.1:1234".
+ *
+ * Both the IP address and port number must be numeric. DNS host names and
+ * non-numeric service port names are not accepted.
+ */
+ void setFromIpPort(const char* addressAndPort);
+ void setFromIpPort(const std::string& addressAndPort) {
+ return setFromIpPort(addressAndPort.c_str());
+ }
+
+ /**
+ * Initialize this SocketAddress from a host name and port number.
+ *
+ * The addressAndPort string must be of the form "<host>:<port>". E.g.,
+ * "www.facebook.com:443".
+ *
+ * If the host name is not a numeric IP address, a DNS lookup will be
+ * performed. Beware that the DNS lookup may be very slow. The port number
+ * must be numeric; non-numeric service port names are not accepted.
+ */
+ void setFromHostPort(const char* hostAndPort);
+ void setFromHostPort(const std::string& hostAndPort) {
+ return setFromHostPort(hostAndPort.c_str());
+ }
+
+ /**
+ * Initialize this SocketAddress from a local unix path.
+ *
+ * Raises std::invalid_argument on error.
+ */
+ void setFromPath(const char* path) {
+ setFromPath(path, strlen(path));
+ }
+
+ void setFromPath(const std::string& path) {
+ setFromPath(path.data(), path.length());
+ }
+
+ void setFromPath(const char* path, size_t length);
+
+ /**
+ * Initialize this SocketAddress from a socket's peer address.
+ *
+ * Raises std::system_error on error.
+ */
+ void setFromPeerAddress(int socket);
+
+ /**
+ * Initialize this SocketAddress from a socket's local address.
+ *
+ * Raises std::system_error on error.
+ */
+ void setFromLocalAddress(int socket);
+
+ /**
+ * Initialize this TSocketAddress from a struct sockaddr.
+ *
+ * Raises std::system_error on error.
+ *
+ * This method is not supported for AF_UNIX addresses. For unix addresses,
+ * the address length must be explicitly specified.
+ *
+ * @param address A struct sockaddr. The size of the address is implied
+ * from address->sa_family.
+ */
+ void setFromSockaddr(const struct sockaddr* address);
+
+ /**
+ * Initialize this SocketAddress from a struct sockaddr.
+ *
+ * Raises std::system_error on error.
+ *
+ * @param address A struct sockaddr.
+ * @param addrlen The length of address data available. This must be long
+ * enough for the full address type required by
+ * address->sa_family.
+ */
+ void setFromSockaddr(const struct sockaddr* address,
+ socklen_t addrlen);
+
+ /**
+ * Initialize this SocketAddress from a struct sockaddr_in.
+ */
+ void setFromSockaddr(const struct sockaddr_in* address);
+
+ /**
+ * Initialize this SocketAddress from a struct sockaddr_in6.
+ */
+ void setFromSockaddr(const struct sockaddr_in6* address);
+
+ /**
+ * Initialize this SocketAddress from a struct sockaddr_un.
+ *
+ * Note that the addrlen parameter is necessary to properly detect anonymous
+ * addresses, which have 0 valid path bytes, and may not even have a NUL
+ * character at the start of the path.
+ *
+ * @param address A struct sockaddr_un.
+ * @param addrlen The length of address data. This should include all of
+ * the valid bytes of sun_path, not including any NUL
+ * terminator.
+ */
+ void setFromSockaddr(const struct sockaddr_un* address,
+ socklen_t addrlen);
+
+
+ /**
+ * Fill in a given sockaddr_storage with the ip or unix address.
+ *
+ * Returns the actual size of the storage used.
+ */
+ socklen_t getAddress(sockaddr_storage* addr) const {
+ if (getFamily() != AF_UNIX) {
+ return storage_.addr.toSockaddrStorage(addr, htons(port_));
+ } else {
+ memcpy(addr, storage_.un.addr, sizeof(*storage_.un.addr));
+ return storage_.un.len;
+ }
+ }
+
+ const folly::IPAddress& getIPAddress() const;
+
+ // Deprecated: getAddress() above returns the same size as getActualSize()
+ socklen_t getActualSize() const;
+
+ sa_family_t getFamily() const {
+ return external_ ? AF_UNIX : storage_.addr.family();
+ }
+
+ bool empty() const {
+ return getFamily() == AF_UNSPEC;
+ }
+
+ /**
+ * Get a string representation of the IPv4 or IPv6 address.
+ *
+ * Raises std::invalid_argument if an error occurs (for example, if
+ * the address is not an IPv4 or IPv6 address).
+ */
+ std::string getAddressStr() const;
+
+ /**
+ * Get a string representation of the IPv4 or IPv6 address.
+ *
+ * Raises std::invalid_argument if an error occurs (for example, if
+ * the address is not an IPv4 or IPv6 address).
+ */
+ void getAddressStr(char* buf, size_t buflen) const;
+
+ /**
+ * For v4 & v6 addresses, return the fully qualified address string
+ */
+ std::string getFullyQualified() const;
+
+ /**
+ * Get the IPv4 or IPv6 port for this address.
+ *
+ * Raises std::invalid_argument if this is not an IPv4 or IPv6 address.
+ *
+ * @return Returns the port, in host byte order.
+ */
+ uint16_t getPort() const;
+
+ /**
+ * Set the IPv4 or IPv6 port for this address.
+ *
+ * Raises std::invalid_argument if this is not an IPv4 or IPv6 address.
+ */
+ void setPort(uint16_t port);
+
+ /**
+ * Return true if this is an IPv4-mapped IPv6 address.
+ */
+ bool isIPv4Mapped() const {
+ return (getFamily() == AF_INET6 &&
+ storage_.addr.isIPv4Mapped());
+ }
+
+ /**
+ * Convert an IPv4-mapped IPv6 address to an IPv4 address.
+ *
+ * Raises std::invalid_argument if this is not an IPv4-mapped IPv6 address.
+ */
+ void convertToIPv4();
+
+ /**
+ * Try to convert an address to IPv4.
+ *
+ * This attempts to convert an address to an IPv4 address if possible.
+ * If the address is an IPv4-mapped IPv6 address, it is converted to an IPv4
+ * address and true is returned. Otherwise nothing is done, and false is
+ * returned.
+ */
+ bool tryConvertToIPv4();
+
+ /**
+ * Convert an IPv4 address to IPv6 [::ffff:a.b.c.d]
+ */
+
+ bool mapToIPv6();
+
+ /**
+ * Get string representation of the host name (or IP address if the host name
+ * cannot be resolved).
+ *
+ * Warning: Using this method is strongly discouraged. It performs a
+ * DNS lookup, which may block for many seconds.
+ *
+ * Raises std::invalid_argument if an error occurs.
+ */
+ std::string getHostStr() const;
+
+ /**
+ * Get the path name for a Unix domain socket.
+ *
+ * Returns a std::string containing the path. For anonymous sockets, an
+ * empty string is returned.
+ *
+ * For addresses in the abstract namespace (Linux-specific), a std::string
+ * containing binary data is returned. In this case the first character will
+ * always be a NUL character.
+ *
+ * Raises std::invalid_argument if called on a non-Unix domain socket.
+ */
+ std::string getPath() const;
+
+ /**
+ * Get human-readable string representation of the address.
+ *
+ * This prints a string representation of the address, for human consumption.
+ * For IP addresses, the string is of the form "<IP>:<port>".
+ */
+ std::string describe() const;
+
+ bool operator==(const SocketAddress& other) const;
+ bool operator!=(const SocketAddress& other) const {
+ return !(*this == other);
+ }
+
+ /**
+ * Check whether the first N bits of this address match the first N
+ * bits of another address.
+ * @note returns false if the addresses are not from the same
+ * address family or if the family is neither IPv4 nor IPv6
+ */
+ bool prefixMatch(const SocketAddress& other, unsigned prefixLength) const;
+
+ /**
+ * Use this operator for storing maps based on SocketAddress.
+ */
+ bool operator<(const SocketAddress& other) const;
+
+ /**
+ * Compuate a hash of a SocketAddress.
+ */
+ size_t hash() const;
+
+ private:
+ /**
+ * Unix socket addresses require more storage than IPv4 and IPv6 addresses,
+ * and are comparatively little-used.
+ *
+ * Therefore SocketAddress' internal storage_ member variable doesn't
+ * contain room for a full unix address, to avoid wasting space in the common
+ * case. When we do need to store a Unix socket address, we use this
+ * ExternalUnixAddr structure to allocate a struct sockaddr_un separately on
+ * the heap.
+ */
+ struct ExternalUnixAddr {
+ struct sockaddr_un *addr;
+ socklen_t len;
+
+ socklen_t pathLength() const {
+ return len - offsetof(struct sockaddr_un, sun_path);
+ }
+
+ void init() {
+ addr = new sockaddr_un;
+ addr->sun_family = AF_UNIX;
+ len = 0;
+ }
+ void init(const ExternalUnixAddr &other) {
+ addr = new sockaddr_un;
+ len = other.len;
+ memcpy(addr, other.addr, len);
+ // Fill the rest with 0s, just for safety
+ memset(reinterpret_cast<char*>(addr) + len, 0,
+ sizeof(struct sockaddr_un) - len);
+ }
+ void copy(const ExternalUnixAddr &other) {
+ len = other.len;
+ memcpy(addr, other.addr, len);
+ }
+ void free() {
+ delete addr;
+ }
+ };
+
+ struct addrinfo* getAddrInfo(const char* host, uint16_t port, int flags);
+ struct addrinfo* getAddrInfo(const char* host, const char* port, int flags);
+ void setFromAddrInfo(const struct addrinfo* results);
+ void setFromLocalAddr(const struct addrinfo* results);
+ void setFromSocket(int socket, int (*fn)(int, struct sockaddr*, socklen_t*));
+ std::string getIpString(int flags) const;
+ void getIpString(char *buf, size_t buflen, int flags) const;
+
+ void updateUnixAddressLength(socklen_t addrlen);
+
+ void prepFamilyChange(sa_family_t newFamily) {
+ if (newFamily != AF_UNIX) {
+ if (getFamily() == AF_UNIX) {
+ storage_.un.free();
+ }
+ external_ = false;
+ } else {
+ if (getFamily() != AF_UNIX) {
+ storage_.un.init();
+ }
+ external_ = true;
+ }
+ }
+
+ /*
+ * storage_ contains room for a full IPv4 or IPv6 address, so they can be
+ * stored inline without a separate allocation on the heap.
+ *
+ * If we need to store a Unix socket address, ExternalUnixAddr is a shim to
+ * track a struct sockaddr_un allocated separately on the heap.
+ */
+ union {
+ folly::IPAddress addr{};
+ ExternalUnixAddr un;
+ } storage_;
+ // IPAddress class does nto save zone or port, and must be saved here
+ uint16_t port_;
+
+ bool external_{false};
+};
+
+/**
+ * Hash a SocketAddress object.
+ *
+ * boost::hash uses hash_value(), so this allows boost::hash to automatically
+ * work for SocketAddress.
+ */
+size_t hash_value(const SocketAddress& address);
+
+std::ostream& operator<<(std::ostream& os, const SocketAddress& addr);
+
+}
+
+namespace std {
+
+// Provide an implementation for std::hash<SocketAddress>
+template<>
+struct hash<folly::SocketAddress> {
+ size_t operator()(
+ const folly::SocketAddress& addr) const {
+ return addr.hash();
+ }
+};
+
+}
--- /dev/null
+/*
+ * Copyright 2014 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/SocketAddress.h>
+
+#include <gtest/gtest.h>
+#include <iostream>
+#include <sstream>
+
+using namespace boost;
+using std::string;
+using std::cerr;
+using std::endl;
+using folly::SocketAddress;
+
+TEST(SocketAddress, Size) {
+ SocketAddress addr;
+ EXPECT_EQ(sizeof(addr), 32);
+}
+
+TEST(SocketAddress, ConstructFromIpv4) {
+ SocketAddress addr("1.2.3.4", 4321);
+ EXPECT_EQ(addr.getFamily(), AF_INET);
+ EXPECT_EQ(addr.getAddressStr(), "1.2.3.4");
+ EXPECT_EQ(addr.getPort(), 4321);
+ sockaddr_storage addrStorage;
+ addr.getAddress(&addrStorage);
+ const sockaddr_in* inaddr = reinterpret_cast<sockaddr_in*>(&addrStorage);
+ EXPECT_EQ(inaddr->sin_addr.s_addr, htonl(0x01020304));
+ EXPECT_EQ(inaddr->sin_port, htons(4321));
+}
+
+TEST(SocketAddress, IPv4ToStringConversion) {
+ // testing addresses *.5.5.5, 5.*.5.5, 5.5.*.5, 5.5.5.*
+ SocketAddress addr;
+ for (int pos = 0; pos < 4; ++pos) {
+ for (int i = 0; i < 256; ++i) {
+ int fragments[] = {5,5,5,5};
+ fragments[pos] = i;
+ std::ostringstream ss;
+ ss << fragments[0] << "." << fragments[1] << "."
+ << fragments[2] << "." << fragments[3];
+ string ipString = ss.str();
+ addr.setFromIpPort(ipString, 1234);
+ EXPECT_EQ(addr.getAddressStr(), ipString);
+ }
+ }
+}
+
+TEST(SocketAddress, SetFromIpv4) {
+ SocketAddress addr;
+ addr.setFromIpPort("255.254.253.252", 8888);
+ EXPECT_EQ(addr.getFamily(), AF_INET);
+ EXPECT_EQ(addr.getAddressStr(), "255.254.253.252");
+ EXPECT_EQ(addr.getPort(), 8888);
+ sockaddr_storage addrStorage;
+ addr.getAddress(&addrStorage);
+ const sockaddr_in* inaddr = reinterpret_cast<sockaddr_in*>(&addrStorage);
+ EXPECT_EQ(inaddr->sin_addr.s_addr, htonl(0xfffefdfc));
+ EXPECT_EQ(inaddr->sin_port, htons(8888));
+}
+
+TEST(SocketAddress, ConstructFromInvalidIpv4) {
+ EXPECT_THROW(SocketAddress("1.2.3.256", 1234), std::runtime_error);
+}
+
+TEST(SocketAddress, SetFromInvalidIpv4) {
+ SocketAddress addr("12.34.56.78", 80);
+
+ // Try setting to an invalid value
+ // Since setFromIpPort() shouldn't allow hostname lookups, setting to
+ // "localhost" should fail, even if localhost is resolvable
+ EXPECT_THROW(addr.setFromIpPort("localhost", 1234),
+ std::runtime_error);
+
+ // Make sure the address still has the old contents
+ EXPECT_EQ(addr.getFamily(), AF_INET);
+ EXPECT_EQ(addr.getAddressStr(), "12.34.56.78");
+ EXPECT_EQ(addr.getPort(), 80);
+ sockaddr_storage addrStorage;
+ addr.getAddress(&addrStorage);
+ const sockaddr_in* inaddr = reinterpret_cast<sockaddr_in*>(&addrStorage);
+ EXPECT_EQ(inaddr->sin_addr.s_addr, htonl(0x0c22384e));
+}
+
+TEST(SocketAddress, SetFromHostname) {
+ // hopefully "localhost" is resolvable on any system that will run the tests
+ EXPECT_THROW(SocketAddress("localhost", 80), std::runtime_error);
+ SocketAddress addr("localhost", 80, true);
+
+ SocketAddress addr2;
+ EXPECT_THROW(addr2.setFromIpPort("localhost", 0), std::runtime_error);
+ addr2.setFromHostPort("localhost", 0);
+}
+
+TEST(SocketAddress, SetFromStrings) {
+ SocketAddress addr;
+
+ // Set from a numeric port string
+ addr.setFromLocalPort("1234");
+ EXPECT_EQ(addr.getPort(), 1234);
+
+ // setFromLocalPort() should not accept service names
+ EXPECT_THROW(addr.setFromLocalPort("http"), std::runtime_error);
+
+ // Call setFromLocalIpPort() with just a port, no IP
+ addr.setFromLocalIpPort("80");
+ EXPECT_EQ(addr.getPort(), 80);
+
+ // Call setFromLocalIpPort() with an IP and port.
+ addr.setFromLocalIpPort("127.0.0.1:4321");
+ EXPECT_EQ(addr.getAddressStr(), "127.0.0.1");
+ EXPECT_EQ(addr.getPort(), 4321);
+
+ // setFromIpPort() without an address should fail
+ EXPECT_THROW(addr.setFromIpPort("4321"), std::invalid_argument);
+
+ // Call setFromIpPort() with an IPv6 address and port
+ addr.setFromIpPort("2620:0:1cfe:face:b00c::3:65535");
+ EXPECT_EQ(addr.getFamily(), AF_INET6);
+ EXPECT_EQ(addr.getAddressStr(), "2620:0:1cfe:face:b00c::3");
+ EXPECT_EQ(addr.getPort(), 65535);
+
+ // Call setFromIpPort() with an IPv4 address and port
+ addr.setFromIpPort("1.2.3.4:9999");
+ EXPECT_EQ(addr.getFamily(), AF_INET);
+ EXPECT_EQ(addr.getAddressStr(), "1.2.3.4");
+ EXPECT_EQ(addr.getPort(), 9999);
+}
+
+TEST(SocketAddress, EqualityAndHash) {
+ // IPv4
+ SocketAddress local1("127.0.0.1", 1234);
+ EXPECT_EQ(local1, local1);
+ EXPECT_EQ(local1.hash(), local1.hash());
+
+ SocketAddress local2("127.0.0.1", 1234);
+ EXPECT_EQ(local1, local2);
+ EXPECT_EQ(local1.hash(), local2.hash());
+
+ SocketAddress local3("127.0.0.1", 4321);
+ EXPECT_NE(local1, local3);
+ EXPECT_NE(local1.hash(), local3.hash());
+
+ SocketAddress other1("1.2.3.4", 1234);
+ EXPECT_EQ(other1, other1);
+ EXPECT_EQ(other1.hash(), other1.hash());
+ EXPECT_NE(local1, other1);
+ EXPECT_NE(local1.hash(), other1.hash());
+
+ SocketAddress other2("4.3.2.1", 1234);
+ EXPECT_NE(other1.hash(), other2.hash());
+ EXPECT_NE(other1.hash(), other2.hash());
+
+ other2.setFromIpPort("1.2.3.4", 0);
+ EXPECT_NE(other1.hash(), other2.hash());
+ EXPECT_NE(other1.hash(), other2.hash());
+ other2.setPort(1234);
+ EXPECT_EQ(other1.hash(), other2.hash());
+ EXPECT_EQ(other1.hash(), other2.hash());
+
+ // IPv6
+ SocketAddress v6_1("2620:0:1c00:face:b00c:0:0:abcd", 1234);
+ SocketAddress v6_2("2620:0:1c00:face:b00c::abcd", 1234);
+ SocketAddress v6_3("2620:0:1c00:face:b00c::bcda", 1234);
+ EXPECT_EQ(v6_1, v6_2);
+ EXPECT_EQ(v6_1.hash(), v6_2.hash());
+ EXPECT_NE(v6_1, v6_3);
+ EXPECT_NE(v6_1.hash(), v6_3.hash());
+
+ // IPv4 versus IPv6 comparison
+ SocketAddress localIPv6("::1", 1234);
+ // Even though these both refer to localhost,
+ // IPv4 and IPv6 addresses are never treated as the same address
+ EXPECT_NE(local1, localIPv6);
+ EXPECT_NE(local1.hash(), localIPv6.hash());
+
+ // IPv4-mapped IPv6 addresses are not treated as equal
+ // to the equivalent IPv4 address
+ SocketAddress v4("10.0.0.3", 99);
+ SocketAddress v6_mapped1("::ffff:10.0.0.3", 99);
+ SocketAddress v6_mapped2("::ffff:0a00:0003", 99);
+ EXPECT_NE(v4, v6_mapped1);
+ EXPECT_NE(v4, v6_mapped2);
+ EXPECT_EQ(v6_mapped1, v6_mapped2);
+
+ // However, after calling convertToIPv4(), the mapped address should now be
+ // equal to the v4 version.
+ EXPECT_TRUE(v6_mapped1.isIPv4Mapped());
+ v6_mapped1.convertToIPv4();
+ EXPECT_EQ(v6_mapped1, v4);
+ EXPECT_NE(v6_mapped1, v6_mapped2);
+
+ // Unix
+ SocketAddress unix1;
+ unix1.setFromPath("/foo");
+ SocketAddress unix2;
+ unix2.setFromPath("/foo");
+ SocketAddress unix3;
+ unix3.setFromPath("/bar");
+ SocketAddress unixAnon;
+ unixAnon.setFromPath("");
+
+ EXPECT_EQ(unix1, unix2);
+ EXPECT_EQ(unix1.hash(), unix2.hash());
+ EXPECT_NE(unix1, unix3);
+ EXPECT_NE(unix1, unixAnon);
+ EXPECT_NE(unix2, unix3);
+ EXPECT_NE(unix2, unixAnon);
+ // anonymous addresses aren't equal to any other address,
+ // including themselves
+ EXPECT_NE(unixAnon, unixAnon);
+
+ // It isn't strictly required that hashes for different addresses be
+ // different, but we should have very few collisions. It generally indicates
+ // a problem if these collide
+ EXPECT_NE(unix1.hash(), unix3.hash());
+ EXPECT_NE(unix1.hash(), unixAnon.hash());
+ EXPECT_NE(unix3.hash(), unixAnon.hash());
+}
+
+TEST(SocketAddress, IsPrivate) {
+ // IPv4
+ SocketAddress addr("9.255.255.255", 0);
+ EXPECT_TRUE(!addr.isPrivateAddress());
+ addr.setFromIpPort("10.0.0.0", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("10.255.255.255", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("11.0.0.0", 0);
+ EXPECT_TRUE(!addr.isPrivateAddress());
+
+ addr.setFromIpPort("172.15.255.255", 0);
+ EXPECT_TRUE(!addr.isPrivateAddress());
+ addr.setFromIpPort("172.16.0.0", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("172.31.255.255", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("172.32.0.0", 0);
+ EXPECT_TRUE(!addr.isPrivateAddress());
+
+ addr.setFromIpPort("192.167.255.255", 0);
+ EXPECT_TRUE(!addr.isPrivateAddress());
+ addr.setFromIpPort("192.168.0.0", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("192.168.255.255", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("192.169.0.0", 0);
+ EXPECT_TRUE(!addr.isPrivateAddress());
+
+ addr.setFromIpPort("126.255.255.255", 0);
+ EXPECT_TRUE(!addr.isPrivateAddress());
+ addr.setFromIpPort("127.0.0.0", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("127.0.0.1", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("127.255.255.255", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("128.0.0.0", 0);
+ EXPECT_TRUE(!addr.isPrivateAddress());
+
+ addr.setFromIpPort("1.2.3.4", 0);
+ EXPECT_TRUE(!addr.isPrivateAddress());
+ addr.setFromIpPort("69.171.239.10", 0);
+ EXPECT_TRUE(!addr.isPrivateAddress());
+
+ // IPv6
+ addr.setFromIpPort("fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0);
+ EXPECT_TRUE(!addr.isPrivateAddress());
+ addr.setFromIpPort("fc00::", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("fe00::", 0);
+ EXPECT_TRUE(!addr.isPrivateAddress());
+
+ addr.setFromIpPort("fe7f:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0);
+ EXPECT_TRUE(!addr.isPrivateAddress());
+ addr.setFromIpPort("fe80::", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("fe80::ffff:ffff:ffff:ffff", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("fec0::", 0);
+ EXPECT_TRUE(!addr.isPrivateAddress());
+
+ addr.setFromIpPort("::0", 0);
+ EXPECT_TRUE(!addr.isPrivateAddress());
+ addr.setFromIpPort("2620:0:1c00:face:b00c:0:0:abcd", 0);
+ EXPECT_TRUE(!addr.isPrivateAddress());
+
+ // IPv4-mapped IPv6
+ addr.setFromIpPort("::ffff:127.0.0.1", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("::ffff:10.1.2.3", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("::ffff:172.24.0.115", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("::ffff:192.168.0.1", 0);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ addr.setFromIpPort("::ffff:69.171.239.10", 0);
+ EXPECT_TRUE(!addr.isPrivateAddress());
+
+ // Unix sockets are considered private addresses
+ addr.setFromPath("/tmp/mysock");
+ EXPECT_TRUE(addr.isPrivateAddress());
+}
+
+TEST(SocketAddress, IsLoopback) {
+ // IPv4
+ SocketAddress addr("127.0.0.1", 0);
+ EXPECT_TRUE(addr.isLoopbackAddress());
+ addr.setFromIpPort("127.0.0.0", 0xffff);
+ EXPECT_TRUE(addr.isLoopbackAddress());
+ addr.setFromIpPort("127.1.1.1", 0xffff);
+ EXPECT_TRUE(addr.isLoopbackAddress());
+ addr.setFromIpPort("127.255.255.255", 80);
+ EXPECT_TRUE(addr.isLoopbackAddress());
+
+ addr.setFromIpPort("128.0.0.0", 0);
+ EXPECT_TRUE(!addr.isLoopbackAddress());
+ addr.setFromIpPort("126.255.255.255", 0);
+ EXPECT_TRUE(!addr.isLoopbackAddress());
+ addr.setFromIpPort("10.1.2.3", 0);
+ EXPECT_TRUE(!addr.isLoopbackAddress());
+
+ // IPv6
+ addr.setFromIpPort("::1", 0);
+ EXPECT_TRUE(addr.isLoopbackAddress());
+ addr.setFromIpPort("::0", 0);
+ EXPECT_TRUE(!addr.isLoopbackAddress());
+ addr.setFromIpPort("::2", 0);
+ EXPECT_TRUE(!addr.isLoopbackAddress());
+
+ // IPv4-mapped IPv6
+ addr.setFromIpPort("::ffff:127.0.0.1", 0);
+ EXPECT_TRUE(addr.isLoopbackAddress());
+ addr.setFromIpPort("::ffff:7f0a:141e", 0);
+ EXPECT_TRUE(addr.isLoopbackAddress());
+ addr.setFromIpPort("::ffff:169.254.0.13", 0);
+ EXPECT_TRUE(!addr.isLoopbackAddress());
+
+ // Unix sockets are considered loopback addresses
+ addr.setFromPath("/tmp/mysock");
+ EXPECT_TRUE(addr.isLoopbackAddress());
+}
+
+void CheckPrefixMatch(const SocketAddress& first,
+ const SocketAddress& second, unsigned matchingPrefixLen) {
+ unsigned i;
+ for (i = 0; i <= matchingPrefixLen; i++) {
+ EXPECT_TRUE(first.prefixMatch(second, i));
+ }
+ unsigned addrLen = (first.getFamily() == AF_INET6) ? 128 : 32;
+ for (; i <= addrLen; i++) {
+ EXPECT_TRUE(!first.prefixMatch(second, i));
+ }
+}
+
+TEST(SocketAddress, PrefixMatch) {
+ // IPv4
+ SocketAddress addr1("127.0.0.1", 0);
+ SocketAddress addr2("127.0.0.1", 0);
+ CheckPrefixMatch(addr1, addr2, 32);
+
+ addr2.setFromIpPort("127.0.1.1", 0);
+ CheckPrefixMatch(addr1, addr2, 23);
+
+ addr2.setFromIpPort("1.1.0.127", 0);
+ CheckPrefixMatch(addr1, addr2, 1);
+
+ // Address family mismatch
+ addr2.setFromIpPort("::ffff:127.0.0.1", 0);
+ EXPECT_TRUE(!addr1.prefixMatch(addr2, 1));
+
+ // IPv6
+ addr1.setFromIpPort("2a03:2880:10:8f02:face:b00c:0:25", 0);
+ CheckPrefixMatch(addr1, addr2, 2);
+
+ addr2.setFromIpPort("2a03:2880:10:8f02:face:b00c:0:25", 0);
+ CheckPrefixMatch(addr1, addr2, 128);
+
+ addr2.setFromIpPort("2a03:2880:30:8f02:face:b00c:0:25", 0);
+ CheckPrefixMatch(addr1, addr2, 42);
+}
+
+void CheckFirstLessThanSecond(SocketAddress first, SocketAddress second) {
+ EXPECT_TRUE(!(first < first));
+ EXPECT_TRUE(!(second < second));
+ EXPECT_TRUE(first < second);
+ EXPECT_TRUE(!(first == second));
+ EXPECT_TRUE(!(second < first));
+}
+
+TEST(SocketAddress, CheckComparatorBehavior) {
+ SocketAddress first, second;
+ // The following comparison are strict (so if first and second were
+ // inverted that is ok.
+
+ //IP V4
+
+ // port comparisions
+ first.setFromIpPort("128.0.0.0", 0);
+ second.setFromIpPort("128.0.0.0", 0xFFFF);
+ CheckFirstLessThanSecond(first, second);
+ first.setFromIpPort("128.0.0.100", 0);
+ second.setFromIpPort("128.0.0.0", 0xFFFF);
+ CheckFirstLessThanSecond(first, second);
+
+ // Address comparisons
+ first.setFromIpPort("128.0.0.0", 10);
+ second.setFromIpPort("128.0.0.100", 10);
+ CheckFirstLessThanSecond(first, second);
+
+ // Comaprision between IPV4 and IPV6
+ first.setFromIpPort("128.0.0.0", 0);
+ second.setFromIpPort("::ffff:127.0.0.1", 0);
+ CheckFirstLessThanSecond(first, second);
+ first.setFromIpPort("128.0.0.0", 100);
+ second.setFromIpPort("::ffff:127.0.0.1", 0);
+ CheckFirstLessThanSecond(first, second);
+
+ // IPV6 comparisons
+
+ // port comparisions
+ first.setFromIpPort("::0", 0);
+ second.setFromIpPort("::0", 0xFFFF);
+ CheckFirstLessThanSecond(first, second);
+ first.setFromIpPort("::0", 0);
+ second.setFromIpPort("::1", 0xFFFF);
+ CheckFirstLessThanSecond(first, second);
+
+ // Address comparisons
+ first.setFromIpPort("::0", 10);
+ second.setFromIpPort("::1", 10);
+ CheckFirstLessThanSecond(first, second);
+
+ // Unix
+ first.setFromPath("/foo");
+ second.setFromPath("/1234");
+ // The exact comparison order doesn't really matter, as long as
+ // (a < b), (b < a), and (a == b) are consistent.
+ // This checks our current comparison rules, which checks the path length
+ // before the path contents.
+ CheckFirstLessThanSecond(first, second);
+ first.setFromPath("/1234");
+ second.setFromPath("/5678");
+ CheckFirstLessThanSecond(first, second);
+
+ // IPv4 vs Unix.
+ // We currently compare the address family values, and AF_UNIX < AF_INET
+ first.setFromPath("/foo");
+ second.setFromIpPort("127.0.0.1", 80);
+ CheckFirstLessThanSecond(first, second);
+}
+
+TEST(SocketAddress, Unix) {
+ SocketAddress addr;
+
+ // Test a small path
+ addr.setFromPath("foo");
+ EXPECT_EQ(addr.getFamily(), AF_UNIX);
+ EXPECT_EQ(addr.describe(), "foo");
+ EXPECT_THROW(addr.getAddressStr(), std::invalid_argument);
+ EXPECT_THROW(addr.getPort(), std::invalid_argument);
+ EXPECT_TRUE(addr.isPrivateAddress());
+ EXPECT_TRUE(addr.isLoopbackAddress());
+
+ // Test a path that is too large
+ const char longPath[] =
+ "abcdefghijklmnopqrstuvwxyz0123456789"
+ "abcdefghijklmnopqrstuvwxyz0123456789"
+ "abcdefghijklmnopqrstuvwxyz0123456789"
+ "abcdefghijklmnopqrstuvwxyz0123456789";
+ EXPECT_THROW(addr.setFromPath(longPath), std::invalid_argument);
+ // The original address should still be the same
+ EXPECT_EQ(addr.getFamily(), AF_UNIX);
+ EXPECT_EQ(addr.describe(), "foo");
+
+ // Test a path that exactly fits in sockaddr_un
+ // (not including the NUL terminator)
+ const char exactLengthPath[] =
+ "abcdefghijklmnopqrstuvwxyz0123456789"
+ "abcdefghijklmnopqrstuvwxyz0123456789"
+ "abcdefghijklmnopqrstuvwxyz0123456789";
+ addr.setFromPath(exactLengthPath);
+ EXPECT_EQ(addr.describe(), exactLengthPath);
+
+ // Test converting a unix socket address to an IPv4 one, then back
+ addr.setFromHostPort("127.0.0.1", 1234);
+ EXPECT_EQ(addr.getFamily(), AF_INET);
+ EXPECT_EQ(addr.describe(), "127.0.0.1:1234");
+ addr.setFromPath("/i/am/a/unix/address");
+ EXPECT_EQ(addr.getFamily(), AF_UNIX);
+ EXPECT_EQ(addr.describe(), "/i/am/a/unix/address");
+
+ // Test copy constructor and assignment operator
+ {
+ SocketAddress copy(addr);
+ EXPECT_EQ(copy, addr);
+ copy.setFromPath("/abc");
+ EXPECT_NE(copy, addr);
+ copy = addr;
+ EXPECT_EQ(copy, addr);
+ copy.setFromIpPort("127.0.0.1", 80);
+ EXPECT_NE(copy, addr);
+ copy = addr;
+ EXPECT_EQ(copy, addr);
+ }
+
+ {
+ SocketAddress copy(addr);
+ EXPECT_EQ(copy, addr);
+ EXPECT_EQ(copy.describe(), "/i/am/a/unix/address");
+ EXPECT_EQ(copy.getPath(), "/i/am/a/unix/address");
+
+ SocketAddress other("127.0.0.1", 80);
+ EXPECT_NE(other, addr);
+ other = copy;
+ EXPECT_EQ(other, copy);
+ EXPECT_EQ(other, addr);
+ EXPECT_EQ(copy, addr);
+ }
+
+#if __GXX_EXPERIMENTAL_CXX0X__
+ {
+ SocketAddress copy;
+ {
+ // move a unix address into a non-unix address
+ SocketAddress tmpCopy(addr);
+ copy = std::move(tmpCopy);
+ }
+ EXPECT_EQ(copy, addr);
+
+ copy.setFromPath("/another/path");
+ {
+ // move a unix address into a unix address
+ SocketAddress tmpCopy(addr);
+ copy = std::move(tmpCopy);
+ }
+ EXPECT_EQ(copy, addr);
+
+ {
+ // move a non-unix address into a unix address
+ SocketAddress tmp("127.0.0.1", 80);
+ copy = std::move(tmp);
+ }
+ EXPECT_EQ(copy.getAddressStr(), "127.0.0.1");
+ EXPECT_EQ(copy.getPort(), 80);
+
+ copy = addr;
+ // move construct a unix address
+ SocketAddress other(std::move(copy));
+ EXPECT_EQ(other, addr);
+ EXPECT_EQ(other.getPath(), addr.getPath());
+ }
+#endif
+}
+
+TEST(SocketAddress, AnonymousUnix) {
+ // Create a unix socket pair, and get the addresses.
+ int fds[2];
+ int rc = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
+ EXPECT_EQ(rc, 0);
+
+ SocketAddress addr0;
+ SocketAddress peer0;
+ SocketAddress addr1;
+ SocketAddress peer1;
+ addr0.setFromLocalAddress(fds[0]);
+ peer0.setFromPeerAddress(fds[0]);
+ addr1.setFromLocalAddress(fds[1]);
+ peer1.setFromPeerAddress(fds[1]);
+ close(fds[0]);
+ close(fds[1]);
+
+ EXPECT_EQ(addr0.describe(), "<anonymous unix address>");
+ EXPECT_EQ(addr1.describe(), "<anonymous unix address>");
+ EXPECT_EQ(peer0.describe(), "<anonymous unix address>");
+ EXPECT_EQ(peer1.describe(), "<anonymous unix address>");
+
+ // Anonymous addresses should never compare equal
+ EXPECT_NE(addr0, addr1);
+ EXPECT_NE(peer0, peer1);
+
+ // Note that logically addr0 and peer1 are the same,
+ // but since they are both anonymous we have no way to determine this
+ EXPECT_NE(addr0, peer1);
+ // We can't even tell if an anonymous address is equal to itself
+ EXPECT_NE(addr0, addr0);
+}
+
+#define REQUIRE_ERRNO(cond, msg) \
+ if (!(cond)) { \
+ int _requireErrnoCopy_ = errno; \
+ std::ostringstream _requireMsg_; \
+ _requireMsg_ << (msg) << ": " << strerror(_requireErrnoCopy_); \
+ ADD_FAILURE(); \
+ }
+
+void testSetFromSocket(const SocketAddress *serverBindAddr,
+ const SocketAddress *clientBindAddr,
+ SocketAddress *listenAddrRet,
+ SocketAddress *acceptAddrRet,
+ SocketAddress *serverAddrRet,
+ SocketAddress *serverPeerAddrRet,
+ SocketAddress *clientAddrRet,
+ SocketAddress *clientPeerAddrRet) {
+ int listenSock = socket(serverBindAddr->getFamily(), SOCK_STREAM, 0);
+ REQUIRE_ERRNO(listenSock > 0, "failed to create listen socket");
+ sockaddr_storage laddr;
+ serverBindAddr->getAddress(&laddr);
+ socklen_t laddrLen = serverBindAddr->getActualSize();
+ int rc = bind(listenSock, reinterpret_cast<sockaddr*>(&laddr), laddrLen);
+ REQUIRE_ERRNO(rc == 0, "failed to bind to server socket");
+ rc = listen(listenSock, 10);
+ REQUIRE_ERRNO(rc == 0, "failed to listen");
+
+ listenAddrRet->setFromLocalAddress(listenSock);
+
+ SocketAddress listenPeerAddr;
+ EXPECT_THROW(listenPeerAddr.setFromPeerAddress(listenSock),
+ std::runtime_error);
+
+ // Note that we use the family from serverBindAddr here, since we allow
+ // clientBindAddr to be nullptr.
+ int clientSock = socket(serverBindAddr->getFamily(), SOCK_STREAM, 0);
+ REQUIRE_ERRNO(clientSock > 0, "failed to create client socket");
+ if (clientBindAddr != nullptr) {
+ sockaddr_storage clientAddr;
+ clientBindAddr->getAddress(&clientAddr);
+
+ rc = bind(clientSock, reinterpret_cast<sockaddr*>(&clientAddr),
+ clientBindAddr->getActualSize());
+ REQUIRE_ERRNO(rc == 0, "failed to bind to client socket");
+ }
+
+ sockaddr_storage listenAddr;
+ listenAddrRet->getAddress(&listenAddr);
+ rc = connect(clientSock, reinterpret_cast<sockaddr*>(&listenAddr),
+ listenAddrRet->getActualSize());
+ REQUIRE_ERRNO(rc == 0, "failed to connect");
+
+ sockaddr_storage acceptAddr;
+ socklen_t acceptAddrLen = sizeof(acceptAddr);
+ int serverSock = accept(listenSock, reinterpret_cast<sockaddr*>(&acceptAddr), &acceptAddrLen);
+ REQUIRE_ERRNO(serverSock > 0, "failed to accept");
+ acceptAddrRet->setFromSockaddr(reinterpret_cast<sockaddr*>(&acceptAddr), acceptAddrLen);
+
+ serverAddrRet->setFromLocalAddress(serverSock);
+ serverPeerAddrRet->setFromPeerAddress(serverSock);
+ clientAddrRet->setFromLocalAddress(clientSock);
+ clientPeerAddrRet->setFromPeerAddress(clientSock);
+
+ close(clientSock);
+ close(serverSock);
+ close(listenSock);
+}
+
+TEST(SocketAddress, SetFromSocketIPv4) {
+ SocketAddress serverBindAddr("0.0.0.0", 0);
+ SocketAddress clientBindAddr("0.0.0.0", 0);
+ SocketAddress listenAddr;
+ SocketAddress acceptAddr;
+ SocketAddress serverAddr;
+ SocketAddress serverPeerAddr;
+ SocketAddress clientAddr;
+ SocketAddress clientPeerAddr;
+
+ testSetFromSocket(&serverBindAddr, &clientBindAddr,
+ &listenAddr, &acceptAddr,
+ &serverAddr, &serverPeerAddr,
+ &clientAddr, &clientPeerAddr);
+
+ // The server socket's local address should have the same port as the listen
+ // address. The IP will be different, since the listening socket is
+ // listening on INADDR_ANY, but the server socket will have a concrete IP
+ // address assigned to it.
+ EXPECT_EQ(serverAddr.getPort(), listenAddr.getPort());
+
+ // The client's peer address should always be the same as the server
+ // socket's address.
+ EXPECT_EQ(clientPeerAddr, serverAddr);
+ // The address returned by getpeername() on the server socket should
+ // be the same as the address returned by accept()
+ EXPECT_EQ(serverPeerAddr, acceptAddr);
+ EXPECT_EQ(serverPeerAddr, clientAddr);
+ EXPECT_EQ(acceptAddr, clientAddr);
+}
+
+/*
+ * Note this test exercises Linux-specific Unix socket behavior
+ */
+TEST(SocketAddress, SetFromSocketUnixAbstract) {
+ // Explicitly binding to an empty path results in an abstract socket
+ // name being picked for us automatically.
+ SocketAddress serverBindAddr;
+ string path(1, 0);
+ path.append("test address");
+ serverBindAddr.setFromPath(path);
+ SocketAddress clientBindAddr;
+ clientBindAddr.setFromPath("");
+
+ SocketAddress listenAddr;
+ SocketAddress acceptAddr;
+ SocketAddress serverAddr;
+ SocketAddress serverPeerAddr;
+ SocketAddress clientAddr;
+ SocketAddress clientPeerAddr;
+
+ testSetFromSocket(&serverBindAddr, &clientBindAddr,
+ &listenAddr, &acceptAddr,
+ &serverAddr, &serverPeerAddr,
+ &clientAddr, &clientPeerAddr);
+
+ // The server socket's local address should be the same as the listen
+ // address.
+ EXPECT_EQ(serverAddr, listenAddr);
+
+ // The client's peer address should always be the same as the server
+ // socket's address.
+ EXPECT_EQ(clientPeerAddr, serverAddr);
+
+ EXPECT_EQ(serverPeerAddr, clientAddr);
+ // Oddly, the address returned by accept() does not seem to match the address
+ // returned by getpeername() on the server socket or getsockname() on the
+ // client socket.
+ // EXPECT_EQ(serverPeerAddr, acceptAddr);
+ // EXPECT_EQ(acceptAddr, clientAddr);
+}
+
+TEST(SocketAddress, SetFromSocketUnixExplicit) {
+ // Pick two temporary path names.
+ // We use mkstemp() just to avoid warnings about mktemp,
+ // but we need to remove the file to let the socket code bind to it.
+ char serverPath[] = "/tmp/SocketAddressTest.server.XXXXXX";
+ int serverPathFd = mkstemp(serverPath);
+ EXPECT_GE(serverPathFd, 0);
+ char clientPath[] = "/tmp/SocketAddressTest.client.XXXXXX";
+ int clientPathFd = mkstemp(clientPath);
+ EXPECT_GE(clientPathFd, 0);
+
+ int rc = unlink(serverPath);
+ EXPECT_EQ(rc, 0);
+ rc = unlink(clientPath);
+ EXPECT_EQ(rc, 0);
+
+ SocketAddress serverBindAddr;
+ SocketAddress clientBindAddr;
+ SocketAddress listenAddr;
+ SocketAddress acceptAddr;
+ SocketAddress serverAddr;
+ SocketAddress serverPeerAddr;
+ SocketAddress clientAddr;
+ SocketAddress clientPeerAddr;
+ try {
+ serverBindAddr.setFromPath(serverPath);
+ clientBindAddr.setFromPath(clientPath);
+
+ testSetFromSocket(&serverBindAddr, &clientBindAddr,
+ &listenAddr, &acceptAddr,
+ &serverAddr, &serverPeerAddr,
+ &clientAddr, &clientPeerAddr);
+ } catch (...) {
+ // Remove the socket files after we are done
+ unlink(serverPath);
+ unlink(clientPath);
+ throw;
+ }
+ unlink(serverPath);
+ unlink(clientPath);
+
+ // The server socket's local address should be the same as the listen
+ // address.
+ EXPECT_EQ(serverAddr, listenAddr);
+
+ // The client's peer address should always be the same as the server
+ // socket's address.
+ EXPECT_EQ(clientPeerAddr, serverAddr);
+
+ EXPECT_EQ(serverPeerAddr, clientAddr);
+ EXPECT_EQ(serverPeerAddr, acceptAddr);
+ EXPECT_EQ(acceptAddr, clientAddr);
+}
+
+TEST(SocketAddress, SetFromSocketUnixAnonymous) {
+ // Test an anonymous client talking to a fixed-path unix socket.
+ char serverPath[] = "/tmp/SocketAddressTest.server.XXXXXX";
+ int serverPathFd = mkstemp(serverPath);
+ EXPECT_GE(serverPathFd, 0);
+ int rc = unlink(serverPath);
+ EXPECT_EQ(rc, 0);
+
+ SocketAddress serverBindAddr;
+ SocketAddress listenAddr;
+ SocketAddress acceptAddr;
+ SocketAddress serverAddr;
+ SocketAddress serverPeerAddr;
+ SocketAddress clientAddr;
+ SocketAddress clientPeerAddr;
+ try {
+ serverBindAddr.setFromPath(serverPath);
+
+ testSetFromSocket(&serverBindAddr, nullptr,
+ &listenAddr, &acceptAddr,
+ &serverAddr, &serverPeerAddr,
+ &clientAddr, &clientPeerAddr);
+ } catch (...) {
+ // Remove the socket file after we are done
+ unlink(serverPath);
+ throw;
+ }
+ unlink(serverPath);
+
+ // The server socket's local address should be the same as the listen
+ // address.
+ EXPECT_EQ(serverAddr, listenAddr);
+
+ // The client's peer address should always be the same as the server
+ // socket's address.
+ EXPECT_EQ(clientPeerAddr, serverAddr);
+
+ // Since the client is using an anonymous address, it won't compare equal to
+ // any other anonymous addresses. Make sure the addresses are anonymous.
+ EXPECT_EQ(serverPeerAddr.getPath(), "");
+ EXPECT_EQ(clientAddr.getPath(), "");
+ EXPECT_EQ(acceptAddr.getPath(), "");
+}