Commit 45c8738b authored by Dave Watson's avatar Dave Watson Committed by dcsommer

move socketaddress to folly

Summary:
Goes with IPAddress and MACAddress already in folly.

* change namespace, add typedef in thrift.  Had to update forward decls to folly namespace.
* change exception types to std:: types instead of TTransportException.  I fbgs'd for everywhere I could find trying to catch these and updated the type, there looked like there were only ~9 files or so.

Test Plan: contbuild.

@override-unit-failures

Tests already passed in last run

Reviewed By: simpkins@fb.com

Subscribers: doug, ps, alandau, everstore-dev@, njormrod, fugalh, alikhtarov, bmatheny, jsedgwick

FB internal diff: D1556916
parent e3e4ab20
...@@ -148,6 +148,7 @@ nobase_follyinclude_HEADERS = \ ...@@ -148,6 +148,7 @@ nobase_follyinclude_HEADERS = \
ScopeGuard.h \ ScopeGuard.h \
SmallLocks.h \ SmallLocks.h \
small_vector.h \ small_vector.h \
SocketAddress.h \
sorted_vector_types.h \ sorted_vector_types.h \
SpookyHashV1.h \ SpookyHashV1.h \
SpookyHashV2.h \ SpookyHashV2.h \
...@@ -246,6 +247,7 @@ libfolly_la_SOURCES = \ ...@@ -246,6 +247,7 @@ libfolly_la_SOURCES = \
MemoryMapping.cpp \ MemoryMapping.cpp \
Random.cpp \ Random.cpp \
SafeAssert.cpp \ SafeAssert.cpp \
SocketAddress.cpp \
SpookyHashV1.cpp \ SpookyHashV1.cpp \
SpookyHashV2.cpp \ SpookyHashV2.cpp \
stats/Instantiations.cpp \ stats/Instantiations.cpp \
......
/*
* 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
/*
* 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();
}
};
}
/*
* 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(), "");
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment