777 lines
22 KiB
C++
777 lines
22 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* 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 <cassert>
|
|
#include <cerrno>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <system_error>
|
|
|
|
#include <boost/functional/hash.hpp>
|
|
|
|
#include <folly/CppAttributes.h>
|
|
#include <folly/Exception.h>
|
|
#include <folly/Format.h>
|
|
#include <folly/hash/Hash.h>
|
|
#include <folly/net/NetOps.h>
|
|
#include <folly/net/NetworkSocket.h>
|
|
|
|
namespace {
|
|
|
|
/**
|
|
* A structure to free a struct addrinfo when it goes out of scope.
|
|
*/
|
|
struct ScopedAddrInfo {
|
|
explicit ScopedAddrInfo(struct addrinfo* addrinfo) : info(addrinfo) {}
|
|
~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;
|
|
};
|
|
|
|
} // 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 (external_) {
|
|
// 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 (external_) {
|
|
// 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::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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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 = socklen_t(offsetof(struct sockaddr_un, sun_path) + len);
|
|
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::setFromPeerAddress(NetworkSocket socket) {
|
|
setFromSocket(socket, netops::getpeername);
|
|
}
|
|
|
|
void SocketAddress::setFromLocalAddress(NetworkSocket socket) {
|
|
setFromSocket(socket, netops::getsockname);
|
|
}
|
|
|
|
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) {
|
|
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");
|
|
}
|
|
|
|
setFromIpAddrPort(folly::IPAddress(address), port);
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
if (!external_) {
|
|
storage_.un.init();
|
|
}
|
|
external_ = true;
|
|
memcpy(storage_.un.addr, address, size_t(addrlen));
|
|
updateUnixAddressLength(addrlen);
|
|
|
|
// Fill the rest with 0s, just for safety
|
|
if (addrlen < sizeof(struct sockaddr_un)) {
|
|
auto 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 InvalidAddressFamilyException(family);
|
|
}
|
|
return storage_.addr;
|
|
}
|
|
|
|
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);
|
|
default:
|
|
throw std::invalid_argument(
|
|
"SocketAddress::getActualSize() called "
|
|
"with unrecognized address family");
|
|
}
|
|
}
|
|
|
|
std::string SocketAddress::getFullyQualified() const {
|
|
if (!isFamilyInet()) {
|
|
throw std::invalid_argument("Can't get address str for non ip address");
|
|
}
|
|
return storage_.addr.toFullyQualified();
|
|
}
|
|
|
|
std::string SocketAddress::getAddressStr() const {
|
|
if (!isFamilyInet()) {
|
|
throw std::invalid_argument("Can't get address str for non ip address");
|
|
}
|
|
return storage_.addr.str();
|
|
}
|
|
|
|
bool SocketAddress::isFamilyInet() const {
|
|
auto family = getFamily();
|
|
return family == AF_INET || family == AF_INET6;
|
|
}
|
|
|
|
void SocketAddress::getAddressStr(char* buf, size_t buflen) const {
|
|
auto ret = getAddressStr();
|
|
size_t len = std::min(buflen - 1, 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 (!external_) {
|
|
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, size_t(storage_.un.pathLength()));
|
|
}
|
|
|
|
return std::string(
|
|
storage_.un.addr->sun_path,
|
|
strnlen(storage_.un.addr->sun_path, size_t(storage_.un.pathLength())));
|
|
}
|
|
|
|
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, size_t(storage_.un.pathLength())));
|
|
}
|
|
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;
|
|
}
|
|
default: {
|
|
char buf[64];
|
|
snprintf(buf, sizeof(buf), "<unknown address family %d>", getFamily());
|
|
return buf;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SocketAddress::operator==(const SocketAddress& other) const {
|
|
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,
|
|
size_t(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_UNSPEC:
|
|
return other.storage_.addr.empty();
|
|
default:
|
|
throw_exception<std::invalid_argument>(
|
|
"SocketAddress: unsupported address family for comparison");
|
|
}
|
|
}
|
|
|
|
bool SocketAddress::prefixMatch(
|
|
const SocketAddress& other,
|
|
unsigned prefixLength) const {
|
|
if (other.getFamily() != getFamily()) {
|
|
return false;
|
|
}
|
|
uint8_t mask_length = 128;
|
|
switch (getFamily()) {
|
|
case AF_INET:
|
|
mask_length = 32;
|
|
FOLLY_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());
|
|
|
|
if (external_) {
|
|
enum { kUnixPathMax = sizeof(storage_.un.addr->sun_path) };
|
|
const char* path = storage_.un.addr->sun_path;
|
|
auto pathLength = storage_.un.pathLength();
|
|
// TODO: this probably could be made more efficient
|
|
for (off_t n = 0; n < pathLength; ++n) {
|
|
boost::hash_combine(seed, folly::hash::twang_mix64(uint64_t(path[n])));
|
|
}
|
|
}
|
|
|
|
switch (getFamily()) {
|
|
case AF_INET:
|
|
case AF_INET6: {
|
|
boost::hash_combine(seed, port_);
|
|
boost::hash_combine(seed, storage_.addr.hash());
|
|
break;
|
|
}
|
|
case AF_UNIX:
|
|
assert(external_);
|
|
break;
|
|
case AF_UNSPEC:
|
|
assert(storage_.addr.empty());
|
|
boost::hash_combine(seed, storage_.addr.hash());
|
|
break;
|
|
default:
|
|
throw_exception<std::invalid_argument>(
|
|
"SocketAddress: unsupported address family for comparison");
|
|
}
|
|
|
|
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::sformat(
|
|
"Failed to resolve address for '{}': {} (error={})",
|
|
host,
|
|
gai_strerror(error),
|
|
error);
|
|
throw std::system_error(error, std::generic_category(), os);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
void SocketAddress::setFromAddrInfo(const struct addrinfo* info) {
|
|
setFromSockaddr(info->ai_addr, socklen_t(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, socklen_t(ai->ai_addrlen));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Otherwise, just use the first address in the list.
|
|
setFromSockaddr(info->ai_addr, socklen_t(info->ai_addrlen));
|
|
}
|
|
|
|
void SocketAddress::setFromSocket(
|
|
NetworkSocket socket,
|
|
int (*fn)(NetworkSocket, struct sockaddr*, socklen_t*)) {
|
|
// 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 = sformat(
|
|
"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.
|
|
size_t maxLength = addrlen - offsetof(struct sockaddr_un, sun_path);
|
|
size_t pathLength = strnlen(storage_.un.addr->sun_path, maxLength);
|
|
storage_.un.len =
|
|
socklen_t(offsetof(struct sockaddr_un, sun_path) + pathLength);
|
|
}
|
|
}
|
|
|
|
bool SocketAddress::operator<(const SocketAddress& other) const {
|
|
if (getFamily() != other.getFamily()) {
|
|
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.
|
|
auto thisPathLength = storage_.un.pathLength();
|
|
if (thisPathLength == 0) {
|
|
return false;
|
|
}
|
|
auto 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,
|
|
size_t(thisPathLength));
|
|
return cmp < 0;
|
|
}
|
|
switch (getFamily()) {
|
|
case AF_INET:
|
|
case AF_INET6: {
|
|
if (port_ != other.port_) {
|
|
return port_ < other.port_;
|
|
}
|
|
|
|
return storage_.addr < other.storage_.addr;
|
|
}
|
|
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;
|
|
}
|
|
|
|
} // namespace folly
|