/*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2016 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <folly/SocketAddress.h>
+#include <folly/Exception.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>
+#include <system_error>
namespace {
* A structure to free a struct addrinfo when it goes out of scope.
*/
struct ScopedAddrInfo {
- explicit ScopedAddrInfo(struct addrinfo* info) : info(info) {}
+ explicit ScopedAddrInfo(struct addrinfo* addrinfo) : info(addrinfo) {}
~ScopedAddrInfo() {
freeaddrinfo(info);
}
if (family == AF_INET || family == AF_INET6) {
return storage_.addr.isPrivate() ||
(storage_.addr.isV6() && storage_.addr.asV6().isLinkLocal());
- } else if (family == AF_UNIX) {
+ } else if (external_) {
// Unix addresses are always local to a host. Return true,
// since this conforms to the semantics of returning true for IP loopback
// addresses.
auto family = getFamily();
if (family == AF_INET || family == AF_INET6) {
return storage_.addr.isLoopback();
- } else if (family == AF_UNIX) {
+ } else if (external_) {
// Return true for UNIX addresses, since they are always local to a host.
return true;
}
setFromAddrInfo(results.info);
}
+void SocketAddress::setFromIpAddrPort(const IPAddress& ipAddr, uint16_t port) {
+ if (external_) {
+ storage_.un.free();
+ external_ = false;
+ }
+ storage_.addr = ipAddr;
+ port_ = port;
+}
+
void SocketAddress::setFromLocalPort(uint16_t port) {
ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG));
setFromLocalAddr(results.info);
setFromAddrInfo(results.info);
}
-void SocketAddress::setFromPath(const char* path, size_t len) {
- if (getFamily() != AF_UNIX) {
+int SocketAddress::getPortFrom(const struct sockaddr* address) {
+ switch (address->sa_family) {
+ case AF_INET:
+ return ntohs(((sockaddr_in*)address)->sin_port);
+
+ case AF_INET6:
+ return ntohs(((sockaddr_in6*)address)->sin6_port);
+
+ default:
+ return -1;
+ }
+}
+
+const char* SocketAddress::getFamilyNameFrom(
+ const struct sockaddr* address,
+ const char* defaultResult) {
+#define GETFAMILYNAMEFROM_IMPL(Family) \
+ case Family: \
+ return #Family
+
+ switch (address->sa_family) {
+ GETFAMILYNAMEFROM_IMPL(AF_INET);
+ GETFAMILYNAMEFROM_IMPL(AF_INET6);
+ GETFAMILYNAMEFROM_IMPL(AF_UNIX);
+ GETFAMILYNAMEFROM_IMPL(AF_UNSPEC);
+
+ default:
+ return defaultResult;
+ }
+
+#undef GETFAMILYNAMEFROM_IMPL
+}
+
+void SocketAddress::setFromPath(StringPiece path) {
+ // Before we touch storage_, check to see if the length is too big.
+ // Note that "storage_.un.addr->sun_path" may not be safe to evaluate here,
+ // but sizeof() just uses its type, and does't evaluate it.
+ if (path.size() > sizeof(storage_.un.addr->sun_path)) {
+ throw std::invalid_argument(
+ "socket path too large to fit into sockaddr_un");
+ }
+
+ if (!external_) {
storage_.un.init();
external_ = true;
}
+ size_t len = path.size();
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);
+ memcpy(storage_.un.addr->sun_path, path.data(), len);
+ // If there is room, put a terminating NUL byte in sun_path. In general the
+ // path should be NUL terminated, although getsockname() and getpeername()
+ // may return Unix socket addresses with paths that fit exactly in sun_path
+ // with no terminating NUL.
+ if (len < sizeof(storage_.un.addr->sun_path)) {
+ storage_.un.addr->sun_path[len] = '\0';
}
}
void SocketAddress::setFromSockaddr(const struct sockaddr* address) {
uint16_t port;
+
if (address->sa_family == AF_INET) {
port = ntohs(((sockaddr_in*)address)->sin_port);
} else if (address->sa_family == AF_INET6) {
"SocketAddress::setFromSockaddr() called "
"with unsupported address type");
}
- if (getFamily() == AF_UNIX) {
- storage_.un.free();
- external_ = false;
- }
- storage_.addr = folly::IPAddress(address);
- port_ = port;
+
+ setFromIpAddrPort(folly::IPAddress(address), port);
}
void SocketAddress::setFromSockaddr(const struct sockaddr* address,
}
socklen_t SocketAddress::getActualSize() const {
+ if (external_) {
+ return storage_.un.len;
+ }
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 "
}
std::string SocketAddress::getPath() const {
- if (getFamily() != AF_UNIX) {
+ if (!external_) {
throw std::invalid_argument(
"SocketAddress: attempting to get path "
"for a non-Unix address");
}
std::string SocketAddress::describe() const {
+ if (external_) {
+ 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()));
+ }
switch (getFamily()) {
case AF_UNSPEC:
return "<uninitialized address>";
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];
}
bool SocketAddress::operator==(const SocketAddress& other) const {
- if (other.getFamily() != getFamily()) {
+ if (external_ != other.external_ || other.getFamily() != getFamily()) {
return false;
}
+ if (external_) {
+ // 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;
+ }
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 "
size_t SocketAddress::hash() const {
size_t seed = folly::hash::twang_mix64(getFamily());
+ if (external_) {
+ 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]));
+ }
+ }
+
switch (getFamily()) {
case AF_INET:
case AF_INET6: {
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]));
- }
+ DCHECK(external_);
break;
- }
case AF_UNSPEC:
default:
throw std::invalid_argument(
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;
- }
-
+void SocketAddress::setFromSocket(
+ int socket,
+ int (*fn)(int, struct sockaddr*, socklen_t*)) {
// Try to put the address into a local storage buffer.
sockaddr_storage tmp_sock;
socklen_t addrLen = sizeof(tmp_sock);
return getFamily() < other.getFamily();
}
+ if (external_) {
+ // 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;
+ }
switch (getFamily()) {
case AF_INET:
case AF_INET6: {
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(