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>
23 #include <folly/CppAttributes.h>
24 #include <folly/Exception.h>
25 #include <folly/Hash.h>
27 #include <boost/functional/hash.hpp>
33 #include <system_error>
38 * A structure to free a struct addrinfo when it goes out of scope.
40 struct ScopedAddrInfo {
41 explicit ScopedAddrInfo(struct addrinfo* addrinfo) : info(addrinfo) {}
46 struct addrinfo* info;
50 * A simple data structure for parsing a host-and-port string.
52 * Accepts a string of the form "<host>:<port>" or just "<port>",
53 * and contains two string pointers to the host and the port portion of the
56 * The HostAndPort may contain pointers into the original string. It is
57 * responsible for the user to ensure that the input string is valid for the
58 * lifetime of the HostAndPort structure.
61 HostAndPort(const char* str, bool hostRequired)
66 // Look for the last colon
67 const char* colon = strrchr(str, ':');
68 if (colon == nullptr) {
69 // No colon, just a port number.
71 throw std::invalid_argument(
72 "expected a host and port string of the "
73 "form \"<host>:<port>\"");
79 // We have to make a copy of the string so we can modify it
80 // and change the colon to a NUL terminator.
81 allocated = strdup(str);
83 throw std::bad_alloc();
86 char *allocatedColon = allocated + (colon - str);
87 *allocatedColon = '\0';
89 port = allocatedColon + 1;
90 // bracketed IPv6 address, remove the brackets
91 // allocatedColon[-1] is fine, as allocatedColon >= host and
92 // *allocatedColon != *host therefore allocatedColon > host
93 if (*host == '[' && allocatedColon[-1] == ']') {
94 allocatedColon[-1] = '\0';
108 } // unnamed namespace
112 bool SocketAddress::isPrivateAddress() const {
113 auto family = getFamily();
114 if (family == AF_INET || family == AF_INET6) {
115 return storage_.addr.isPrivate() ||
116 (storage_.addr.isV6() && storage_.addr.asV6().isLinkLocal());
117 } else if (external_) {
118 // Unix addresses are always local to a host. Return true,
119 // since this conforms to the semantics of returning true for IP loopback
126 bool SocketAddress::isLoopbackAddress() const {
127 auto family = getFamily();
128 if (family == AF_INET || family == AF_INET6) {
129 return storage_.addr.isLoopback();
130 } else if (external_) {
131 // Return true for UNIX addresses, since they are always local to a host.
137 void SocketAddress::setFromHostPort(const char* host, uint16_t port) {
138 ScopedAddrInfo results(getAddrInfo(host, port, 0));
139 setFromAddrInfo(results.info);
142 void SocketAddress::setFromIpPort(const char* ip, uint16_t port) {
143 ScopedAddrInfo results(getAddrInfo(ip, port, AI_NUMERICHOST));
144 setFromAddrInfo(results.info);
147 void SocketAddress::setFromIpAddrPort(const IPAddress& ipAddr, uint16_t port) {
152 storage_.addr = ipAddr;
156 void SocketAddress::setFromLocalPort(uint16_t port) {
157 ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG));
158 setFromLocalAddr(results.info);
161 void SocketAddress::setFromLocalPort(const char* port) {
162 ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG));
163 setFromLocalAddr(results.info);
166 void SocketAddress::setFromLocalIpPort(const char* addressAndPort) {
167 HostAndPort hp(addressAndPort, false);
168 ScopedAddrInfo results(getAddrInfo(hp.host, hp.port,
169 AI_NUMERICHOST | AI_ADDRCONFIG));
170 setFromLocalAddr(results.info);
173 void SocketAddress::setFromIpPort(const char* addressAndPort) {
174 HostAndPort hp(addressAndPort, true);
175 ScopedAddrInfo results(getAddrInfo(hp.host, hp.port, AI_NUMERICHOST));
176 setFromAddrInfo(results.info);
179 void SocketAddress::setFromHostPort(const char* hostAndPort) {
180 HostAndPort hp(hostAndPort, true);
181 ScopedAddrInfo results(getAddrInfo(hp.host, hp.port, 0));
182 setFromAddrInfo(results.info);
185 int SocketAddress::getPortFrom(const struct sockaddr* address) {
186 switch (address->sa_family) {
188 return ntohs(((sockaddr_in*)address)->sin_port);
191 return ntohs(((sockaddr_in6*)address)->sin6_port);
198 const char* SocketAddress::getFamilyNameFrom(
199 const struct sockaddr* address,
200 const char* defaultResult) {
201 #define GETFAMILYNAMEFROM_IMPL(Family) \
205 switch (address->sa_family) {
206 GETFAMILYNAMEFROM_IMPL(AF_INET);
207 GETFAMILYNAMEFROM_IMPL(AF_INET6);
208 GETFAMILYNAMEFROM_IMPL(AF_UNIX);
209 GETFAMILYNAMEFROM_IMPL(AF_UNSPEC);
212 return defaultResult;
215 #undef GETFAMILYNAMEFROM_IMPL
218 void SocketAddress::setFromPath(StringPiece path) {
219 // Before we touch storage_, check to see if the length is too big.
220 // Note that "storage_.un.addr->sun_path" may not be safe to evaluate here,
221 // but sizeof() just uses its type, and does't evaluate it.
222 if (path.size() > sizeof(storage_.un.addr->sun_path)) {
223 throw std::invalid_argument(
224 "socket path too large to fit into sockaddr_un");
232 size_t len = path.size();
233 storage_.un.len = socklen_t(offsetof(struct sockaddr_un, sun_path) + len);
234 memcpy(storage_.un.addr->sun_path, path.data(), len);
235 // If there is room, put a terminating NUL byte in sun_path. In general the
236 // path should be NUL terminated, although getsockname() and getpeername()
237 // may return Unix socket addresses with paths that fit exactly in sun_path
238 // with no terminating NUL.
239 if (len < sizeof(storage_.un.addr->sun_path)) {
240 storage_.un.addr->sun_path[len] = '\0';
244 void SocketAddress::setFromPeerAddress(int socket) {
245 setFromSocket(socket, getpeername);
248 void SocketAddress::setFromLocalAddress(int socket) {
249 setFromSocket(socket, getsockname);
252 void SocketAddress::setFromSockaddr(const struct sockaddr* address) {
255 if (address->sa_family == AF_INET) {
256 port = ntohs(((sockaddr_in*)address)->sin_port);
257 } else if (address->sa_family == AF_INET6) {
258 port = ntohs(((sockaddr_in6*)address)->sin6_port);
259 } else if (address->sa_family == AF_UNIX) {
260 // We need an explicitly specified length for AF_UNIX addresses,
261 // to be able to distinguish anonymous addresses from addresses
262 // in Linux's abstract namespace.
263 throw std::invalid_argument(
264 "SocketAddress::setFromSockaddr(): the address "
265 "length must be explicitly specified when "
266 "setting AF_UNIX addresses");
268 throw std::invalid_argument(
269 "SocketAddress::setFromSockaddr() called "
270 "with unsupported address type");
273 setFromIpAddrPort(folly::IPAddress(address), port);
276 void SocketAddress::setFromSockaddr(const struct sockaddr* address,
278 // Check the length to make sure we can access address->sa_family
279 if (addrlen < (offsetof(struct sockaddr, sa_family) +
280 sizeof(address->sa_family))) {
281 throw std::invalid_argument(
282 "SocketAddress::setFromSockaddr() called "
283 "with length too short for a sockaddr");
286 if (address->sa_family == AF_INET) {
287 if (addrlen < sizeof(struct sockaddr_in)) {
288 throw std::invalid_argument(
289 "SocketAddress::setFromSockaddr() called "
290 "with length too short for a sockaddr_in");
292 setFromSockaddr(reinterpret_cast<const struct sockaddr_in*>(address));
293 } else if (address->sa_family == AF_INET6) {
294 if (addrlen < sizeof(struct sockaddr_in6)) {
295 throw std::invalid_argument(
296 "SocketAddress::setFromSockaddr() called "
297 "with length too short for a sockaddr_in6");
299 setFromSockaddr(reinterpret_cast<const struct sockaddr_in6*>(address));
300 } else if (address->sa_family == AF_UNIX) {
301 setFromSockaddr(reinterpret_cast<const struct sockaddr_un*>(address),
304 throw std::invalid_argument(
305 "SocketAddress::setFromSockaddr() called "
306 "with unsupported address type");
310 void SocketAddress::setFromSockaddr(const struct sockaddr_in* address) {
311 assert(address->sin_family == AF_INET);
312 setFromSockaddr((sockaddr*)address);
315 void SocketAddress::setFromSockaddr(const struct sockaddr_in6* address) {
316 assert(address->sin6_family == AF_INET6);
317 setFromSockaddr((sockaddr*)address);
320 void SocketAddress::setFromSockaddr(const struct sockaddr_un* address,
322 assert(address->sun_family == AF_UNIX);
323 if (addrlen > sizeof(struct sockaddr_un)) {
324 throw std::invalid_argument(
325 "SocketAddress::setFromSockaddr() called "
326 "with length too long for a sockaddr_un");
333 memcpy(storage_.un.addr, address, size_t(addrlen));
334 updateUnixAddressLength(addrlen);
336 // Fill the rest with 0s, just for safety
337 if (addrlen < sizeof(struct sockaddr_un)) {
338 char *p = reinterpret_cast<char*>(storage_.un.addr);
339 memset(p + addrlen, 0, sizeof(struct sockaddr_un) - addrlen);
343 const folly::IPAddress& SocketAddress::getIPAddress() const {
344 auto family = getFamily();
345 if (family != AF_INET && family != AF_INET6) {
346 throw InvalidAddressFamilyException(family);
348 return storage_.addr;
351 socklen_t SocketAddress::getActualSize() const {
353 return storage_.un.len;
355 switch (getFamily()) {
358 return sizeof(struct sockaddr_in);
360 return sizeof(struct sockaddr_in6);
362 throw std::invalid_argument(
363 "SocketAddress::getActualSize() called "
364 "with unrecognized address family");
368 std::string SocketAddress::getFullyQualified() const {
369 if (!isFamilyInet()) {
370 throw std::invalid_argument("Can't get address str for non ip address");
372 return storage_.addr.toFullyQualified();
375 std::string SocketAddress::getAddressStr() const {
376 if (!isFamilyInet()) {
377 throw std::invalid_argument("Can't get address str for non ip address");
379 return storage_.addr.str();
382 bool SocketAddress::isFamilyInet() const {
383 auto family = getFamily();
384 return family == AF_INET || family == AF_INET6;
387 void SocketAddress::getAddressStr(char* buf, size_t buflen) const {
388 auto ret = getAddressStr();
389 size_t len = std::min(buflen - 1, ret.size());
390 memcpy(buf, ret.data(), len);
394 uint16_t SocketAddress::getPort() const {
395 switch (getFamily()) {
400 throw std::invalid_argument(
401 "SocketAddress::getPort() called on non-IP "
406 void SocketAddress::setPort(uint16_t port) {
407 switch (getFamily()) {
413 throw std::invalid_argument(
414 "SocketAddress::setPort() called on non-IP "
419 void SocketAddress::convertToIPv4() {
420 if (!tryConvertToIPv4()) {
421 throw std::invalid_argument(
422 "convertToIPv4() called on an addresse that is "
423 "not an IPv4-mapped address");
427 bool SocketAddress::tryConvertToIPv4() {
428 if (!isIPv4Mapped()) {
432 storage_.addr = folly::IPAddress::createIPv4(storage_.addr);
436 bool SocketAddress::mapToIPv6() {
437 if (getFamily() != AF_INET) {
441 storage_.addr = folly::IPAddress::createIPv6(storage_.addr);
445 std::string SocketAddress::getHostStr() const {
446 return getIpString(0);
449 std::string SocketAddress::getPath() const {
451 throw std::invalid_argument(
452 "SocketAddress: attempting to get path "
453 "for a non-Unix address");
456 if (storage_.un.pathLength() == 0) {
458 return std::string();
460 if (storage_.un.addr->sun_path[0] == '\0') {
461 // abstract namespace
463 storage_.un.addr->sun_path, size_t(storage_.un.pathLength()));
467 storage_.un.addr->sun_path,
468 strnlen(storage_.un.addr->sun_path, size_t(storage_.un.pathLength())));
471 std::string SocketAddress::describe() const {
473 if (storage_.un.pathLength() == 0) {
474 return "<anonymous unix address>";
477 if (storage_.un.addr->sun_path[0] == '\0') {
478 // Linux supports an abstract namespace for unix socket addresses
479 return "<abstract unix address>";
483 storage_.un.addr->sun_path,
484 strnlen(storage_.un.addr->sun_path, size_t(storage_.un.pathLength())));
486 switch (getFamily()) {
488 return "<uninitialized address>";
491 char buf[NI_MAXHOST + 16];
492 getAddressStr(buf, sizeof(buf));
493 size_t iplen = strlen(buf);
494 snprintf(buf + iplen, sizeof(buf) - iplen, ":%" PRIu16, getPort());
499 char buf[NI_MAXHOST + 18];
501 getAddressStr(buf + 1, sizeof(buf) - 1);
502 size_t iplen = strlen(buf);
503 snprintf(buf + iplen, sizeof(buf) - iplen, "]:%" PRIu16, getPort());
509 snprintf(buf, sizeof(buf), "<unknown address family %d>",
516 bool SocketAddress::operator==(const SocketAddress& other) const {
517 if (external_ != other.external_ || other.getFamily() != getFamily()) {
521 // anonymous addresses are never equal to any other addresses
522 if (storage_.un.pathLength() == 0 ||
523 other.storage_.un.pathLength() == 0) {
527 if (storage_.un.len != other.storage_.un.len) {
531 storage_.un.addr->sun_path,
532 other.storage_.un.addr->sun_path,
533 size_t(storage_.un.pathLength()));
537 switch (getFamily()) {
540 return (other.storage_.addr == storage_.addr) &&
541 (other.port_ == port_);
543 throw std::invalid_argument(
544 "SocketAddress: unsupported address family "
549 bool SocketAddress::prefixMatch(const SocketAddress& other,
550 unsigned prefixLength) const {
551 if (other.getFamily() != getFamily()) {
554 uint8_t mask_length = 128;
555 switch (getFamily()) {
561 auto prefix = folly::IPAddress::longestCommonPrefix(
562 {storage_.addr, mask_length},
563 {other.storage_.addr, mask_length});
564 return prefix.second >= prefixLength;
572 size_t SocketAddress::hash() const {
573 size_t seed = folly::hash::twang_mix64(getFamily());
576 enum { kUnixPathMax = sizeof(storage_.un.addr->sun_path) };
577 const char *path = storage_.un.addr->sun_path;
578 auto pathLength = storage_.un.pathLength();
579 // TODO: this probably could be made more efficient
580 for (off_t n = 0; n < pathLength; ++n) {
581 boost::hash_combine(seed, folly::hash::twang_mix64(uint64_t(path[n])));
585 switch (getFamily()) {
588 boost::hash_combine(seed, port_);
589 boost::hash_combine(seed, storage_.addr.hash());
597 throw std::invalid_argument(
598 "SocketAddress: unsupported address family "
605 struct addrinfo* SocketAddress::getAddrInfo(const char* host,
608 // getaddrinfo() requires the port number as a string
609 char portString[sizeof("65535")];
610 snprintf(portString, sizeof(portString), "%" PRIu16, port);
612 return getAddrInfo(host, portString, flags);
615 struct addrinfo* SocketAddress::getAddrInfo(const char* host,
618 struct addrinfo hints;
619 memset(&hints, 0, sizeof(hints));
620 hints.ai_family = AF_UNSPEC;
621 hints.ai_socktype = SOCK_STREAM;
622 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | flags;
624 struct addrinfo *results;
625 int error = getaddrinfo(host, port, &hints, &results);
627 auto os = folly::to<std::string>(
628 "Failed to resolve address for \"", host, "\": ",
629 gai_strerror(error), " (error=", error, ")");
630 throw std::system_error(error, std::generic_category(), os);
636 void SocketAddress::setFromAddrInfo(const struct addrinfo* info) {
637 setFromSockaddr(info->ai_addr, socklen_t(info->ai_addrlen));
640 void SocketAddress::setFromLocalAddr(const struct addrinfo* info) {
641 // If an IPv6 address is present, prefer to use it, since IPv4 addresses
642 // can be mapped into IPv6 space.
643 for (const struct addrinfo* ai = info; ai != nullptr; ai = ai->ai_next) {
644 if (ai->ai_family == AF_INET6) {
645 setFromSockaddr(ai->ai_addr, socklen_t(ai->ai_addrlen));
650 // Otherwise, just use the first address in the list.
651 setFromSockaddr(info->ai_addr, socklen_t(info->ai_addrlen));
654 void SocketAddress::setFromSocket(
656 int (*fn)(int, struct sockaddr*, socklen_t*)) {
657 // Try to put the address into a local storage buffer.
658 sockaddr_storage tmp_sock;
659 socklen_t addrLen = sizeof(tmp_sock);
660 if (fn(socket, (sockaddr*)&tmp_sock, &addrLen) != 0) {
661 folly::throwSystemError("setFromSocket() failed");
664 setFromSockaddr((sockaddr*)&tmp_sock, addrLen);
667 std::string SocketAddress::getIpString(int flags) const {
668 char addrString[NI_MAXHOST];
669 getIpString(addrString, sizeof(addrString), flags);
670 return std::string(addrString);
673 void SocketAddress::getIpString(char *buf, size_t buflen, int flags) const {
674 auto family = getFamily();
675 if (family != AF_INET &&
676 family != AF_INET6) {
677 throw std::invalid_argument(
678 "SocketAddress: attempting to get IP address "
679 "for a non-IP address");
682 sockaddr_storage tmp_sock;
683 storage_.addr.toSockaddrStorage(&tmp_sock, port_);
684 int rc = getnameinfo((sockaddr*)&tmp_sock, sizeof(sockaddr_storage),
685 buf, buflen, nullptr, 0, flags);
687 auto os = folly::to<std::string>(
688 "getnameinfo() failed in getIpString() error = ",
690 throw std::system_error(rc, std::generic_category(), os);
694 void SocketAddress::updateUnixAddressLength(socklen_t addrlen) {
695 if (addrlen < offsetof(struct sockaddr_un, sun_path)) {
696 throw std::invalid_argument(
697 "SocketAddress: attempted to set a Unix socket "
698 "with a length too short for a sockaddr_un");
701 storage_.un.len = addrlen;
702 if (storage_.un.pathLength() == 0) {
707 if (storage_.un.addr->sun_path[0] == '\0') {
708 // abstract namespace. honor the specified length
710 // Call strnlen(), just in case the length was overspecified.
711 size_t maxLength = addrlen - offsetof(struct sockaddr_un, sun_path);
712 size_t pathLength = strnlen(storage_.un.addr->sun_path, maxLength);
714 socklen_t(offsetof(struct sockaddr_un, sun_path) + pathLength);
718 bool SocketAddress::operator<(const SocketAddress& other) const {
719 if (getFamily() != other.getFamily()) {
720 return getFamily() < other.getFamily();
724 // Anonymous addresses can't be compared to anything else.
725 // Return that they are never less than anything.
727 // Note that this still meets the requirements for a strict weak
728 // ordering, so we can use this operator<() with standard C++ containers.
729 auto thisPathLength = storage_.un.pathLength();
730 if (thisPathLength == 0) {
733 auto otherPathLength = other.storage_.un.pathLength();
734 if (otherPathLength == 0) {
738 // Compare based on path length first, for efficiency
739 if (thisPathLength != otherPathLength) {
740 return thisPathLength < otherPathLength;
743 storage_.un.addr->sun_path,
744 other.storage_.un.addr->sun_path,
745 size_t(thisPathLength));
748 switch (getFamily()) {
751 if (port_ != other.port_) {
752 return port_ < other.port_;
756 storage_.addr < other.storage_.addr;
760 throw std::invalid_argument(
761 "SocketAddress: unsupported address family for comparing");
765 size_t hash_value(const SocketAddress& address) {
766 return address.hash();
769 std::ostream& operator<<(std::ostream& os, const SocketAddress& addr) {
770 os << addr.describe();