2 * Copyright 2017 Facebook, Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #ifndef __STDC_FORMAT_MACROS
18 #define __STDC_FORMAT_MACROS
21 #include <folly/SocketAddress.h>
28 #include <system_error>
30 #include <boost/functional/hash.hpp>
32 #include <folly/CppAttributes.h>
33 #include <folly/Exception.h>
34 #include <folly/Hash.h>
39 * A structure to free a struct addrinfo when it goes out of scope.
41 struct ScopedAddrInfo {
42 explicit ScopedAddrInfo(struct addrinfo* addrinfo) : info(addrinfo) {}
47 struct addrinfo* info;
51 * A simple data structure for parsing a host-and-port string.
53 * Accepts a string of the form "<host>:<port>" or just "<port>",
54 * and contains two string pointers to the host and the port portion of the
57 * The HostAndPort may contain pointers into the original string. It is
58 * responsible for the user to ensure that the input string is valid for the
59 * lifetime of the HostAndPort structure.
62 HostAndPort(const char* str, bool hostRequired)
67 // Look for the last colon
68 const char* colon = strrchr(str, ':');
69 if (colon == nullptr) {
70 // No colon, just a port number.
72 throw std::invalid_argument(
73 "expected a host and port string of the "
74 "form \"<host>:<port>\"");
80 // We have to make a copy of the string so we can modify it
81 // and change the colon to a NUL terminator.
82 allocated = strdup(str);
84 throw std::bad_alloc();
87 char *allocatedColon = allocated + (colon - str);
88 *allocatedColon = '\0';
90 port = allocatedColon + 1;
91 // bracketed IPv6 address, remove the brackets
92 // allocatedColon[-1] is fine, as allocatedColon >= host and
93 // *allocatedColon != *host therefore allocatedColon > host
94 if (*host == '[' && allocatedColon[-1] == ']') {
95 allocatedColon[-1] = '\0';
109 } // unnamed namespace
113 bool SocketAddress::isPrivateAddress() const {
114 auto family = getFamily();
115 if (family == AF_INET || family == AF_INET6) {
116 return storage_.addr.isPrivate() ||
117 (storage_.addr.isV6() && storage_.addr.asV6().isLinkLocal());
118 } else if (external_) {
119 // Unix addresses are always local to a host. Return true,
120 // since this conforms to the semantics of returning true for IP loopback
127 bool SocketAddress::isLoopbackAddress() const {
128 auto family = getFamily();
129 if (family == AF_INET || family == AF_INET6) {
130 return storage_.addr.isLoopback();
131 } else if (external_) {
132 // Return true for UNIX addresses, since they are always local to a host.
138 void SocketAddress::setFromHostPort(const char* host, uint16_t port) {
139 ScopedAddrInfo results(getAddrInfo(host, port, 0));
140 setFromAddrInfo(results.info);
143 void SocketAddress::setFromIpPort(const char* ip, uint16_t port) {
144 ScopedAddrInfo results(getAddrInfo(ip, port, AI_NUMERICHOST));
145 setFromAddrInfo(results.info);
148 void SocketAddress::setFromIpAddrPort(const IPAddress& ipAddr, uint16_t port) {
153 storage_.addr = ipAddr;
157 void SocketAddress::setFromLocalPort(uint16_t port) {
158 ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG));
159 setFromLocalAddr(results.info);
162 void SocketAddress::setFromLocalPort(const char* port) {
163 ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG));
164 setFromLocalAddr(results.info);
167 void SocketAddress::setFromLocalIpPort(const char* addressAndPort) {
168 HostAndPort hp(addressAndPort, false);
169 ScopedAddrInfo results(getAddrInfo(hp.host, hp.port,
170 AI_NUMERICHOST | AI_ADDRCONFIG));
171 setFromLocalAddr(results.info);
174 void SocketAddress::setFromIpPort(const char* addressAndPort) {
175 HostAndPort hp(addressAndPort, true);
176 ScopedAddrInfo results(getAddrInfo(hp.host, hp.port, AI_NUMERICHOST));
177 setFromAddrInfo(results.info);
180 void SocketAddress::setFromHostPort(const char* hostAndPort) {
181 HostAndPort hp(hostAndPort, true);
182 ScopedAddrInfo results(getAddrInfo(hp.host, hp.port, 0));
183 setFromAddrInfo(results.info);
186 int SocketAddress::getPortFrom(const struct sockaddr* address) {
187 switch (address->sa_family) {
189 return ntohs(((sockaddr_in*)address)->sin_port);
192 return ntohs(((sockaddr_in6*)address)->sin6_port);
199 const char* SocketAddress::getFamilyNameFrom(
200 const struct sockaddr* address,
201 const char* defaultResult) {
202 #define GETFAMILYNAMEFROM_IMPL(Family) \
206 switch (address->sa_family) {
207 GETFAMILYNAMEFROM_IMPL(AF_INET);
208 GETFAMILYNAMEFROM_IMPL(AF_INET6);
209 GETFAMILYNAMEFROM_IMPL(AF_UNIX);
210 GETFAMILYNAMEFROM_IMPL(AF_UNSPEC);
213 return defaultResult;
216 #undef GETFAMILYNAMEFROM_IMPL
219 void SocketAddress::setFromPath(StringPiece path) {
220 // Before we touch storage_, check to see if the length is too big.
221 // Note that "storage_.un.addr->sun_path" may not be safe to evaluate here,
222 // but sizeof() just uses its type, and does't evaluate it.
223 if (path.size() > sizeof(storage_.un.addr->sun_path)) {
224 throw std::invalid_argument(
225 "socket path too large to fit into sockaddr_un");
233 size_t len = path.size();
234 storage_.un.len = socklen_t(offsetof(struct sockaddr_un, sun_path) + len);
235 memcpy(storage_.un.addr->sun_path, path.data(), len);
236 // If there is room, put a terminating NUL byte in sun_path. In general the
237 // path should be NUL terminated, although getsockname() and getpeername()
238 // may return Unix socket addresses with paths that fit exactly in sun_path
239 // with no terminating NUL.
240 if (len < sizeof(storage_.un.addr->sun_path)) {
241 storage_.un.addr->sun_path[len] = '\0';
245 void SocketAddress::setFromPeerAddress(int socket) {
246 setFromSocket(socket, getpeername);
249 void SocketAddress::setFromLocalAddress(int socket) {
250 setFromSocket(socket, getsockname);
253 void SocketAddress::setFromSockaddr(const struct sockaddr* address) {
256 if (address->sa_family == AF_INET) {
257 port = ntohs(((sockaddr_in*)address)->sin_port);
258 } else if (address->sa_family == AF_INET6) {
259 port = ntohs(((sockaddr_in6*)address)->sin6_port);
260 } else if (address->sa_family == AF_UNIX) {
261 // We need an explicitly specified length for AF_UNIX addresses,
262 // to be able to distinguish anonymous addresses from addresses
263 // in Linux's abstract namespace.
264 throw std::invalid_argument(
265 "SocketAddress::setFromSockaddr(): the address "
266 "length must be explicitly specified when "
267 "setting AF_UNIX addresses");
269 throw std::invalid_argument(
270 "SocketAddress::setFromSockaddr() called "
271 "with unsupported address type");
274 setFromIpAddrPort(folly::IPAddress(address), port);
277 void SocketAddress::setFromSockaddr(const struct sockaddr* address,
279 // Check the length to make sure we can access address->sa_family
280 if (addrlen < (offsetof(struct sockaddr, sa_family) +
281 sizeof(address->sa_family))) {
282 throw std::invalid_argument(
283 "SocketAddress::setFromSockaddr() called "
284 "with length too short for a sockaddr");
287 if (address->sa_family == AF_INET) {
288 if (addrlen < sizeof(struct sockaddr_in)) {
289 throw std::invalid_argument(
290 "SocketAddress::setFromSockaddr() called "
291 "with length too short for a sockaddr_in");
293 setFromSockaddr(reinterpret_cast<const struct sockaddr_in*>(address));
294 } else if (address->sa_family == AF_INET6) {
295 if (addrlen < sizeof(struct sockaddr_in6)) {
296 throw std::invalid_argument(
297 "SocketAddress::setFromSockaddr() called "
298 "with length too short for a sockaddr_in6");
300 setFromSockaddr(reinterpret_cast<const struct sockaddr_in6*>(address));
301 } else if (address->sa_family == AF_UNIX) {
302 setFromSockaddr(reinterpret_cast<const struct sockaddr_un*>(address),
305 throw std::invalid_argument(
306 "SocketAddress::setFromSockaddr() called "
307 "with unsupported address type");
311 void SocketAddress::setFromSockaddr(const struct sockaddr_in* address) {
312 assert(address->sin_family == AF_INET);
313 setFromSockaddr((sockaddr*)address);
316 void SocketAddress::setFromSockaddr(const struct sockaddr_in6* address) {
317 assert(address->sin6_family == AF_INET6);
318 setFromSockaddr((sockaddr*)address);
321 void SocketAddress::setFromSockaddr(const struct sockaddr_un* address,
323 assert(address->sun_family == AF_UNIX);
324 if (addrlen > sizeof(struct sockaddr_un)) {
325 throw std::invalid_argument(
326 "SocketAddress::setFromSockaddr() called "
327 "with length too long for a sockaddr_un");
334 memcpy(storage_.un.addr, address, size_t(addrlen));
335 updateUnixAddressLength(addrlen);
337 // Fill the rest with 0s, just for safety
338 if (addrlen < sizeof(struct sockaddr_un)) {
339 char *p = reinterpret_cast<char*>(storage_.un.addr);
340 memset(p + addrlen, 0, sizeof(struct sockaddr_un) - addrlen);
344 const folly::IPAddress& SocketAddress::getIPAddress() const {
345 auto family = getFamily();
346 if (family != AF_INET && family != AF_INET6) {
347 throw InvalidAddressFamilyException(family);
349 return storage_.addr;
352 socklen_t SocketAddress::getActualSize() const {
354 return storage_.un.len;
356 switch (getFamily()) {
359 return sizeof(struct sockaddr_in);
361 return sizeof(struct sockaddr_in6);
363 throw std::invalid_argument(
364 "SocketAddress::getActualSize() called "
365 "with unrecognized address family");
369 std::string SocketAddress::getFullyQualified() const {
370 if (!isFamilyInet()) {
371 throw std::invalid_argument("Can't get address str for non ip address");
373 return storage_.addr.toFullyQualified();
376 std::string SocketAddress::getAddressStr() const {
377 if (!isFamilyInet()) {
378 throw std::invalid_argument("Can't get address str for non ip address");
380 return storage_.addr.str();
383 bool SocketAddress::isFamilyInet() const {
384 auto family = getFamily();
385 return family == AF_INET || family == AF_INET6;
388 void SocketAddress::getAddressStr(char* buf, size_t buflen) const {
389 auto ret = getAddressStr();
390 size_t len = std::min(buflen - 1, ret.size());
391 memcpy(buf, ret.data(), len);
395 uint16_t SocketAddress::getPort() const {
396 switch (getFamily()) {
401 throw std::invalid_argument(
402 "SocketAddress::getPort() called on non-IP "
407 void SocketAddress::setPort(uint16_t port) {
408 switch (getFamily()) {
414 throw std::invalid_argument(
415 "SocketAddress::setPort() called on non-IP "
420 void SocketAddress::convertToIPv4() {
421 if (!tryConvertToIPv4()) {
422 throw std::invalid_argument(
423 "convertToIPv4() called on an addresse that is "
424 "not an IPv4-mapped address");
428 bool SocketAddress::tryConvertToIPv4() {
429 if (!isIPv4Mapped()) {
433 storage_.addr = folly::IPAddress::createIPv4(storage_.addr);
437 bool SocketAddress::mapToIPv6() {
438 if (getFamily() != AF_INET) {
442 storage_.addr = folly::IPAddress::createIPv6(storage_.addr);
446 std::string SocketAddress::getHostStr() const {
447 return getIpString(0);
450 std::string SocketAddress::getPath() const {
452 throw std::invalid_argument(
453 "SocketAddress: attempting to get path "
454 "for a non-Unix address");
457 if (storage_.un.pathLength() == 0) {
459 return std::string();
461 if (storage_.un.addr->sun_path[0] == '\0') {
462 // abstract namespace
464 storage_.un.addr->sun_path, size_t(storage_.un.pathLength()));
468 storage_.un.addr->sun_path,
469 strnlen(storage_.un.addr->sun_path, size_t(storage_.un.pathLength())));
472 std::string SocketAddress::describe() const {
474 if (storage_.un.pathLength() == 0) {
475 return "<anonymous unix address>";
478 if (storage_.un.addr->sun_path[0] == '\0') {
479 // Linux supports an abstract namespace for unix socket addresses
480 return "<abstract unix address>";
484 storage_.un.addr->sun_path,
485 strnlen(storage_.un.addr->sun_path, size_t(storage_.un.pathLength())));
487 switch (getFamily()) {
489 return "<uninitialized address>";
492 char buf[NI_MAXHOST + 16];
493 getAddressStr(buf, sizeof(buf));
494 size_t iplen = strlen(buf);
495 snprintf(buf + iplen, sizeof(buf) - iplen, ":%" PRIu16, getPort());
500 char buf[NI_MAXHOST + 18];
502 getAddressStr(buf + 1, sizeof(buf) - 1);
503 size_t iplen = strlen(buf);
504 snprintf(buf + iplen, sizeof(buf) - iplen, "]:%" PRIu16, getPort());
510 snprintf(buf, sizeof(buf), "<unknown address family %d>",
517 bool SocketAddress::operator==(const SocketAddress& other) const {
518 if (external_ != other.external_ || other.getFamily() != getFamily()) {
522 // anonymous addresses are never equal to any other addresses
523 if (storage_.un.pathLength() == 0 ||
524 other.storage_.un.pathLength() == 0) {
528 if (storage_.un.len != other.storage_.un.len) {
532 storage_.un.addr->sun_path,
533 other.storage_.un.addr->sun_path,
534 size_t(storage_.un.pathLength()));
538 switch (getFamily()) {
541 return (other.storage_.addr == storage_.addr) &&
542 (other.port_ == port_);
544 throw std::invalid_argument(
545 "SocketAddress: unsupported address family "
550 bool SocketAddress::prefixMatch(const SocketAddress& other,
551 unsigned prefixLength) const {
552 if (other.getFamily() != getFamily()) {
555 uint8_t mask_length = 128;
556 switch (getFamily()) {
562 auto prefix = folly::IPAddress::longestCommonPrefix(
563 {storage_.addr, mask_length},
564 {other.storage_.addr, mask_length});
565 return prefix.second >= prefixLength;
573 size_t SocketAddress::hash() const {
574 size_t seed = folly::hash::twang_mix64(getFamily());
577 enum { kUnixPathMax = sizeof(storage_.un.addr->sun_path) };
578 const char *path = storage_.un.addr->sun_path;
579 auto pathLength = storage_.un.pathLength();
580 // TODO: this probably could be made more efficient
581 for (off_t n = 0; n < pathLength; ++n) {
582 boost::hash_combine(seed, folly::hash::twang_mix64(uint64_t(path[n])));
586 switch (getFamily()) {
589 boost::hash_combine(seed, port_);
590 boost::hash_combine(seed, storage_.addr.hash());
598 throw std::invalid_argument(
599 "SocketAddress: unsupported address family "
606 struct addrinfo* SocketAddress::getAddrInfo(const char* host,
609 // getaddrinfo() requires the port number as a string
610 char portString[sizeof("65535")];
611 snprintf(portString, sizeof(portString), "%" PRIu16, port);
613 return getAddrInfo(host, portString, flags);
616 struct addrinfo* SocketAddress::getAddrInfo(const char* host,
619 struct addrinfo hints;
620 memset(&hints, 0, sizeof(hints));
621 hints.ai_family = AF_UNSPEC;
622 hints.ai_socktype = SOCK_STREAM;
623 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | flags;
625 struct addrinfo *results;
626 int error = getaddrinfo(host, port, &hints, &results);
628 auto os = folly::to<std::string>(
629 "Failed to resolve address for \"", host, "\": ",
630 gai_strerror(error), " (error=", error, ")");
631 throw std::system_error(error, std::generic_category(), os);
637 void SocketAddress::setFromAddrInfo(const struct addrinfo* info) {
638 setFromSockaddr(info->ai_addr, socklen_t(info->ai_addrlen));
641 void SocketAddress::setFromLocalAddr(const struct addrinfo* info) {
642 // If an IPv6 address is present, prefer to use it, since IPv4 addresses
643 // can be mapped into IPv6 space.
644 for (const struct addrinfo* ai = info; ai != nullptr; ai = ai->ai_next) {
645 if (ai->ai_family == AF_INET6) {
646 setFromSockaddr(ai->ai_addr, socklen_t(ai->ai_addrlen));
651 // Otherwise, just use the first address in the list.
652 setFromSockaddr(info->ai_addr, socklen_t(info->ai_addrlen));
655 void SocketAddress::setFromSocket(
657 int (*fn)(int, struct sockaddr*, socklen_t*)) {
658 // Try to put the address into a local storage buffer.
659 sockaddr_storage tmp_sock;
660 socklen_t addrLen = sizeof(tmp_sock);
661 if (fn(socket, (sockaddr*)&tmp_sock, &addrLen) != 0) {
662 folly::throwSystemError("setFromSocket() failed");
665 setFromSockaddr((sockaddr*)&tmp_sock, addrLen);
668 std::string SocketAddress::getIpString(int flags) const {
669 char addrString[NI_MAXHOST];
670 getIpString(addrString, sizeof(addrString), flags);
671 return std::string(addrString);
674 void SocketAddress::getIpString(char *buf, size_t buflen, int flags) const {
675 auto family = getFamily();
676 if (family != AF_INET &&
677 family != AF_INET6) {
678 throw std::invalid_argument(
679 "SocketAddress: attempting to get IP address "
680 "for a non-IP address");
683 sockaddr_storage tmp_sock;
684 storage_.addr.toSockaddrStorage(&tmp_sock, port_);
685 int rc = getnameinfo((sockaddr*)&tmp_sock, sizeof(sockaddr_storage),
686 buf, buflen, nullptr, 0, flags);
688 auto os = folly::to<std::string>(
689 "getnameinfo() failed in getIpString() error = ",
691 throw std::system_error(rc, std::generic_category(), os);
695 void SocketAddress::updateUnixAddressLength(socklen_t addrlen) {
696 if (addrlen < offsetof(struct sockaddr_un, sun_path)) {
697 throw std::invalid_argument(
698 "SocketAddress: attempted to set a Unix socket "
699 "with a length too short for a sockaddr_un");
702 storage_.un.len = addrlen;
703 if (storage_.un.pathLength() == 0) {
708 if (storage_.un.addr->sun_path[0] == '\0') {
709 // abstract namespace. honor the specified length
711 // Call strnlen(), just in case the length was overspecified.
712 size_t maxLength = addrlen - offsetof(struct sockaddr_un, sun_path);
713 size_t pathLength = strnlen(storage_.un.addr->sun_path, maxLength);
715 socklen_t(offsetof(struct sockaddr_un, sun_path) + pathLength);
719 bool SocketAddress::operator<(const SocketAddress& other) const {
720 if (getFamily() != other.getFamily()) {
721 return getFamily() < other.getFamily();
725 // Anonymous addresses can't be compared to anything else.
726 // Return that they are never less than anything.
728 // Note that this still meets the requirements for a strict weak
729 // ordering, so we can use this operator<() with standard C++ containers.
730 auto thisPathLength = storage_.un.pathLength();
731 if (thisPathLength == 0) {
734 auto otherPathLength = other.storage_.un.pathLength();
735 if (otherPathLength == 0) {
739 // Compare based on path length first, for efficiency
740 if (thisPathLength != otherPathLength) {
741 return thisPathLength < otherPathLength;
744 storage_.un.addr->sun_path,
745 other.storage_.un.addr->sun_path,
746 size_t(thisPathLength));
749 switch (getFamily()) {
752 if (port_ != other.port_) {
753 return port_ < other.port_;
757 storage_.addr < other.storage_.addr;
761 throw std::invalid_argument(
762 "SocketAddress: unsupported address family for comparing");
766 size_t hash_value(const SocketAddress& address) {
767 return address.hash();
770 std::ostream& operator<<(std::ostream& os, const SocketAddress& addr) {
771 os << addr.describe();