2 * Copyright 2016 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/Exception.h>
24 #include <folly/Hash.h>
26 #include <boost/functional/hash.hpp>
32 #include <system_error>
37 * A structure to free a struct addrinfo when it goes out of scope.
39 struct ScopedAddrInfo {
40 explicit ScopedAddrInfo(struct addrinfo* addrinfo) : info(addrinfo) {}
45 struct addrinfo* info;
49 * A simple data structure for parsing a host-and-port string.
51 * Accepts a string of the form "<host>:<port>" or just "<port>",
52 * and contains two string pointers to the host and the port portion of the
55 * The HostAndPort may contain pointers into the original string. It is
56 * responsible for the user to ensure that the input string is valid for the
57 * lifetime of the HostAndPort structure.
60 HostAndPort(const char* str, bool hostRequired)
65 // Look for the last colon
66 const char* colon = strrchr(str, ':');
67 if (colon == nullptr) {
68 // No colon, just a port number.
70 throw std::invalid_argument(
71 "expected a host and port string of the "
72 "form \"<host>:<port>\"");
78 // We have to make a copy of the string so we can modify it
79 // and change the colon to a NUL terminator.
80 allocated = strdup(str);
82 throw std::bad_alloc();
85 char *allocatedColon = allocated + (colon - str);
86 *allocatedColon = '\0';
88 port = allocatedColon + 1;
89 // bracketed IPv6 address, remove the brackets
90 // allocatedColon[-1] is fine, as allocatedColon >= host and
91 // *allocatedColon != *host therefore allocatedColon > host
92 if (*host == '[' && allocatedColon[-1] == ']') {
93 allocatedColon[-1] = '\0';
107 } // unnamed namespace
111 bool SocketAddress::isPrivateAddress() const {
112 auto family = getFamily();
113 if (family == AF_INET || family == AF_INET6) {
114 return storage_.addr.isPrivate() ||
115 (storage_.addr.isV6() && storage_.addr.asV6().isLinkLocal());
116 } else if (external_) {
117 // Unix addresses are always local to a host. Return true,
118 // since this conforms to the semantics of returning true for IP loopback
125 bool SocketAddress::isLoopbackAddress() const {
126 auto family = getFamily();
127 if (family == AF_INET || family == AF_INET6) {
128 return storage_.addr.isLoopback();
129 } else if (external_) {
130 // Return true for UNIX addresses, since they are always local to a host.
136 void SocketAddress::setFromHostPort(const char* host, uint16_t port) {
137 ScopedAddrInfo results(getAddrInfo(host, port, 0));
138 setFromAddrInfo(results.info);
141 void SocketAddress::setFromIpPort(const char* ip, uint16_t port) {
142 ScopedAddrInfo results(getAddrInfo(ip, port, AI_NUMERICHOST));
143 setFromAddrInfo(results.info);
146 void SocketAddress::setFromIpAddrPort(const IPAddress& ipAddr, uint16_t port) {
151 storage_.addr = ipAddr;
155 void SocketAddress::setFromLocalPort(uint16_t port) {
156 ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG));
157 setFromLocalAddr(results.info);
160 void SocketAddress::setFromLocalPort(const char* port) {
161 ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG));
162 setFromLocalAddr(results.info);
165 void SocketAddress::setFromLocalIpPort(const char* addressAndPort) {
166 HostAndPort hp(addressAndPort, false);
167 ScopedAddrInfo results(getAddrInfo(hp.host, hp.port,
168 AI_NUMERICHOST | AI_ADDRCONFIG));
169 setFromLocalAddr(results.info);
172 void SocketAddress::setFromIpPort(const char* addressAndPort) {
173 HostAndPort hp(addressAndPort, true);
174 ScopedAddrInfo results(getAddrInfo(hp.host, hp.port, AI_NUMERICHOST));
175 setFromAddrInfo(results.info);
178 void SocketAddress::setFromHostPort(const char* hostAndPort) {
179 HostAndPort hp(hostAndPort, true);
180 ScopedAddrInfo results(getAddrInfo(hp.host, hp.port, 0));
181 setFromAddrInfo(results.info);
184 int SocketAddress::getPortFrom(const struct sockaddr* address) {
185 switch (address->sa_family) {
187 return ntohs(((sockaddr_in*)address)->sin_port);
190 return ntohs(((sockaddr_in6*)address)->sin6_port);
197 const char* SocketAddress::getFamilyNameFrom(
198 const struct sockaddr* address,
199 const char* defaultResult) {
200 #define GETFAMILYNAMEFROM_IMPL(Family) \
204 switch (address->sa_family) {
205 GETFAMILYNAMEFROM_IMPL(AF_INET);
206 GETFAMILYNAMEFROM_IMPL(AF_INET6);
207 GETFAMILYNAMEFROM_IMPL(AF_UNIX);
208 GETFAMILYNAMEFROM_IMPL(AF_UNSPEC);
211 return defaultResult;
214 #undef GETFAMILYNAMEFROM_IMPL
217 void SocketAddress::setFromPath(StringPiece path) {
218 // Before we touch storage_, check to see if the length is too big.
219 // Note that "storage_.un.addr->sun_path" may not be safe to evaluate here,
220 // but sizeof() just uses its type, and does't evaluate it.
221 if (path.size() > sizeof(storage_.un.addr->sun_path)) {
222 throw std::invalid_argument(
223 "socket path too large to fit into sockaddr_un");
231 size_t len = path.size();
232 storage_.un.len = offsetof(struct sockaddr_un, sun_path) + len;
233 memcpy(storage_.un.addr->sun_path, path.data(), len);
234 // If there is room, put a terminating NUL byte in sun_path. In general the
235 // path should be NUL terminated, although getsockname() and getpeername()
236 // may return Unix socket addresses with paths that fit exactly in sun_path
237 // with no terminating NUL.
238 if (len < sizeof(storage_.un.addr->sun_path)) {
239 storage_.un.addr->sun_path[len] = '\0';
243 void SocketAddress::setFromPeerAddress(SocketDesc socket) {
244 setFromSocket(socket, getpeername);
247 void SocketAddress::setFromLocalAddress(SocketDesc socket) {
248 setFromSocket(socket, getsockname);
251 void SocketAddress::setFromSockaddr(const struct sockaddr* address) {
254 if (address->sa_family == AF_INET) {
255 port = ntohs(((sockaddr_in*)address)->sin_port);
256 } else if (address->sa_family == AF_INET6) {
257 port = ntohs(((sockaddr_in6*)address)->sin6_port);
258 } else if (address->sa_family == AF_UNIX) {
259 // We need an explicitly specified length for AF_UNIX addresses,
260 // to be able to distinguish anonymous addresses from addresses
261 // in Linux's abstract namespace.
262 throw std::invalid_argument(
263 "SocketAddress::setFromSockaddr(): the address "
264 "length must be explicitly specified when "
265 "setting AF_UNIX addresses");
267 throw std::invalid_argument(
268 "SocketAddress::setFromSockaddr() called "
269 "with unsupported address type");
272 setFromIpAddrPort(folly::IPAddress(address), port);
275 void SocketAddress::setFromSockaddr(const struct sockaddr* address,
277 // Check the length to make sure we can access address->sa_family
278 if (addrlen < (offsetof(struct sockaddr, sa_family) +
279 sizeof(address->sa_family))) {
280 throw std::invalid_argument(
281 "SocketAddress::setFromSockaddr() called "
282 "with length too short for a sockaddr");
285 if (address->sa_family == AF_INET) {
286 if (addrlen < sizeof(struct sockaddr_in)) {
287 throw std::invalid_argument(
288 "SocketAddress::setFromSockaddr() called "
289 "with length too short for a sockaddr_in");
291 setFromSockaddr(reinterpret_cast<const struct sockaddr_in*>(address));
292 } else if (address->sa_family == AF_INET6) {
293 if (addrlen < sizeof(struct sockaddr_in6)) {
294 throw std::invalid_argument(
295 "SocketAddress::setFromSockaddr() called "
296 "with length too short for a sockaddr_in6");
298 setFromSockaddr(reinterpret_cast<const struct sockaddr_in6*>(address));
299 } else if (address->sa_family == AF_UNIX) {
300 setFromSockaddr(reinterpret_cast<const struct sockaddr_un*>(address),
303 throw std::invalid_argument(
304 "SocketAddress::setFromSockaddr() called "
305 "with unsupported address type");
309 void SocketAddress::setFromSockaddr(const struct sockaddr_in* address) {
310 assert(address->sin_family == AF_INET);
311 setFromSockaddr((sockaddr*)address);
314 void SocketAddress::setFromSockaddr(const struct sockaddr_in6* address) {
315 assert(address->sin6_family == AF_INET6);
316 setFromSockaddr((sockaddr*)address);
319 void SocketAddress::setFromSockaddr(const struct sockaddr_un* address,
321 assert(address->sun_family == AF_UNIX);
322 if (addrlen > sizeof(struct sockaddr_un)) {
323 throw std::invalid_argument(
324 "SocketAddress::setFromSockaddr() called "
325 "with length too long for a sockaddr_un");
328 prepFamilyChange(AF_UNIX);
329 memcpy(storage_.un.addr, address, addrlen);
330 updateUnixAddressLength(addrlen);
332 // Fill the rest with 0s, just for safety
333 if (addrlen < sizeof(struct sockaddr_un)) {
334 char *p = reinterpret_cast<char*>(storage_.un.addr);
335 memset(p + addrlen, 0, sizeof(struct sockaddr_un) - addrlen);
339 const folly::IPAddress& SocketAddress::getIPAddress() const {
340 auto family = getFamily();
341 if (family != AF_INET && family != AF_INET6) {
342 throw InvalidAddressFamilyException(family);
344 return storage_.addr;
347 socklen_t SocketAddress::getActualSize() const {
349 return storage_.un.len;
351 switch (getFamily()) {
354 return sizeof(struct sockaddr_in);
356 return sizeof(struct sockaddr_in6);
358 throw std::invalid_argument(
359 "SocketAddress::getActualSize() called "
360 "with unrecognized address family");
364 std::string SocketAddress::getFullyQualified() const {
365 auto family = getFamily();
366 if (family != AF_INET && family != AF_INET6) {
367 throw std::invalid_argument("Can't get address str for non ip address");
369 return storage_.addr.toFullyQualified();
372 std::string SocketAddress::getAddressStr() const {
373 char buf[INET6_ADDRSTRLEN];
374 getAddressStr(buf, sizeof(buf));
378 void SocketAddress::getAddressStr(char* buf, size_t buflen) const {
379 auto family = getFamily();
380 if (family != AF_INET && family != AF_INET6) {
381 throw std::invalid_argument("Can't get address str for non ip address");
383 std::string ret = storage_.addr.str();
384 size_t len = std::min(buflen, ret.size());
385 memcpy(buf, ret.data(), len);
389 uint16_t SocketAddress::getPort() const {
390 switch (getFamily()) {
395 throw std::invalid_argument(
396 "SocketAddress::getPort() called on non-IP "
401 void SocketAddress::setPort(uint16_t port) {
402 switch (getFamily()) {
408 throw std::invalid_argument(
409 "SocketAddress::setPort() called on non-IP "
414 void SocketAddress::convertToIPv4() {
415 if (!tryConvertToIPv4()) {
416 throw std::invalid_argument(
417 "convertToIPv4() called on an addresse that is "
418 "not an IPv4-mapped address");
422 bool SocketAddress::tryConvertToIPv4() {
423 if (!isIPv4Mapped()) {
427 storage_.addr = folly::IPAddress::createIPv4(storage_.addr);
431 bool SocketAddress::mapToIPv6() {
432 if (getFamily() != AF_INET) {
436 storage_.addr = folly::IPAddress::createIPv6(storage_.addr);
440 std::string SocketAddress::getHostStr() const {
441 return getIpString(0);
444 std::string SocketAddress::getPath() const {
446 throw std::invalid_argument(
447 "SocketAddress: attempting to get path "
448 "for a non-Unix address");
451 if (storage_.un.pathLength() == 0) {
453 return std::string();
455 if (storage_.un.addr->sun_path[0] == '\0') {
456 // abstract namespace
457 return std::string(storage_.un.addr->sun_path, storage_.un.pathLength());
460 return std::string(storage_.un.addr->sun_path,
461 strnlen(storage_.un.addr->sun_path,
462 storage_.un.pathLength()));
465 std::string SocketAddress::describe() const {
467 if (storage_.un.pathLength() == 0) {
468 return "<anonymous unix address>";
471 if (storage_.un.addr->sun_path[0] == '\0') {
472 // Linux supports an abstract namespace for unix socket addresses
473 return "<abstract unix address>";
476 return std::string(storage_.un.addr->sun_path,
477 strnlen(storage_.un.addr->sun_path,
478 storage_.un.pathLength()));
480 switch (getFamily()) {
482 return "<uninitialized address>";
485 char buf[NI_MAXHOST + 16];
486 getAddressStr(buf, sizeof(buf));
487 size_t iplen = strlen(buf);
488 snprintf(buf + iplen, sizeof(buf) - iplen, ":%" PRIu16, getPort());
493 char buf[NI_MAXHOST + 18];
495 getAddressStr(buf + 1, sizeof(buf) - 1);
496 size_t iplen = strlen(buf);
497 snprintf(buf + iplen, sizeof(buf) - iplen, "]:%" PRIu16, getPort());
503 snprintf(buf, sizeof(buf), "<unknown address family %d>",
510 bool SocketAddress::operator==(const SocketAddress& other) const {
511 if (external_ != other.external_ || other.getFamily() != getFamily()) {
515 // anonymous addresses are never equal to any other addresses
516 if (storage_.un.pathLength() == 0 ||
517 other.storage_.un.pathLength() == 0) {
521 if (storage_.un.len != other.storage_.un.len) {
524 int cmp = memcmp(storage_.un.addr->sun_path,
525 other.storage_.un.addr->sun_path,
526 storage_.un.pathLength());
530 switch (getFamily()) {
533 return (other.storage_.addr == storage_.addr) &&
534 (other.port_ == port_);
536 throw std::invalid_argument(
537 "SocketAddress: unsupported address family "
542 bool SocketAddress::prefixMatch(const SocketAddress& other,
543 unsigned prefixLength) const {
544 if (other.getFamily() != getFamily()) {
547 int mask_length = 128;
548 switch (getFamily()) {
554 auto prefix = folly::IPAddress::longestCommonPrefix(
555 {storage_.addr, mask_length},
556 {other.storage_.addr, mask_length});
557 return prefix.second >= prefixLength;
565 size_t SocketAddress::hash() const {
566 size_t seed = folly::hash::twang_mix64(getFamily());
569 enum { kUnixPathMax = sizeof(storage_.un.addr->sun_path) };
570 const char *path = storage_.un.addr->sun_path;
571 size_t pathLength = storage_.un.pathLength();
572 // TODO: this probably could be made more efficient
573 for (unsigned int n = 0; n < pathLength; ++n) {
574 boost::hash_combine(seed, folly::hash::twang_mix64(path[n]));
578 switch (getFamily()) {
581 boost::hash_combine(seed, port_);
582 boost::hash_combine(seed, storage_.addr.hash());
590 throw std::invalid_argument(
591 "SocketAddress: unsupported address family "
598 struct addrinfo* SocketAddress::getAddrInfo(const char* host,
601 // getaddrinfo() requires the port number as a string
602 char portString[sizeof("65535")];
603 snprintf(portString, sizeof(portString), "%" PRIu16, port);
605 return getAddrInfo(host, portString, flags);
608 struct addrinfo* SocketAddress::getAddrInfo(const char* host,
611 struct addrinfo hints;
612 memset(&hints, 0, sizeof(hints));
613 hints.ai_family = AF_UNSPEC;
614 hints.ai_socktype = SOCK_STREAM;
615 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | flags;
617 struct addrinfo *results;
618 int error = getaddrinfo(host, port, &hints, &results);
620 auto os = folly::to<std::string>(
621 "Failed to resolve address for \"", host, "\": ",
622 gai_strerror(error), " (error=", error, ")");
623 throw std::system_error(error, std::generic_category(), os);
629 void SocketAddress::setFromAddrInfo(const struct addrinfo* info) {
630 setFromSockaddr(info->ai_addr, info->ai_addrlen);
633 void SocketAddress::setFromLocalAddr(const struct addrinfo* info) {
634 // If an IPv6 address is present, prefer to use it, since IPv4 addresses
635 // can be mapped into IPv6 space.
636 for (const struct addrinfo* ai = info; ai != nullptr; ai = ai->ai_next) {
637 if (ai->ai_family == AF_INET6) {
638 setFromSockaddr(ai->ai_addr, ai->ai_addrlen);
643 // Otherwise, just use the first address in the list.
644 setFromSockaddr(info->ai_addr, info->ai_addrlen);
647 void SocketAddress::setFromSocket(SocketDesc socket, GetPeerNameFunc fn) {
648 // Try to put the address into a local storage buffer.
649 sockaddr_storage tmp_sock;
650 socklen_t addrLen = sizeof(tmp_sock);
651 if (fn(socket, (sockaddr*)&tmp_sock, &addrLen) != 0) {
652 folly::throwSystemError("setFromSocket() failed");
655 setFromSockaddr((sockaddr*)&tmp_sock, addrLen);
658 std::string SocketAddress::getIpString(int flags) const {
659 char addrString[NI_MAXHOST];
660 getIpString(addrString, sizeof(addrString), flags);
661 return std::string(addrString);
664 void SocketAddress::getIpString(char *buf, size_t buflen, int flags) const {
665 auto family = getFamily();
666 if (family != AF_INET &&
667 family != AF_INET6) {
668 throw std::invalid_argument(
669 "SocketAddress: attempting to get IP address "
670 "for a non-IP address");
673 sockaddr_storage tmp_sock;
674 storage_.addr.toSockaddrStorage(&tmp_sock, port_);
675 int rc = getnameinfo((sockaddr*)&tmp_sock, sizeof(sockaddr_storage),
676 buf, buflen, nullptr, 0, flags);
678 auto os = folly::to<std::string>(
679 "getnameinfo() failed in getIpString() error = ",
681 throw std::system_error(rc, std::generic_category(), os);
685 void SocketAddress::updateUnixAddressLength(socklen_t addrlen) {
686 if (addrlen < offsetof(struct sockaddr_un, sun_path)) {
687 throw std::invalid_argument(
688 "SocketAddress: attempted to set a Unix socket "
689 "with a length too short for a sockaddr_un");
692 storage_.un.len = addrlen;
693 if (storage_.un.pathLength() == 0) {
698 if (storage_.un.addr->sun_path[0] == '\0') {
699 // abstract namespace. honor the specified length
701 // Call strnlen(), just in case the length was overspecified.
702 socklen_t maxLength = addrlen - offsetof(struct sockaddr_un, sun_path);
703 size_t pathLength = strnlen(storage_.un.addr->sun_path, maxLength);
704 storage_.un.len = offsetof(struct sockaddr_un, sun_path) + pathLength;
708 bool SocketAddress::operator<(const SocketAddress& other) const {
709 if (getFamily() != other.getFamily()) {
710 return getFamily() < other.getFamily();
714 // Anonymous addresses can't be compared to anything else.
715 // Return that they are never less than anything.
717 // Note that this still meets the requirements for a strict weak
718 // ordering, so we can use this operator<() with standard C++ containers.
719 size_t thisPathLength = storage_.un.pathLength();
720 if (thisPathLength == 0) {
723 size_t otherPathLength = other.storage_.un.pathLength();
724 if (otherPathLength == 0) {
728 // Compare based on path length first, for efficiency
729 if (thisPathLength != otherPathLength) {
730 return thisPathLength < otherPathLength;
732 int cmp = memcmp(storage_.un.addr->sun_path,
733 other.storage_.un.addr->sun_path,
737 switch (getFamily()) {
740 if (port_ != other.port_) {
741 return port_ < other.port_;
745 storage_.addr < other.storage_.addr;
749 throw std::invalid_argument(
750 "SocketAddress: unsupported address family for comparing");
754 size_t hash_value(const SocketAddress& address) {
755 return address.hash();
758 std::ostream& operator<<(std::ostream& os, const SocketAddress& addr) {
759 os << addr.describe();