1/*
2 * Copyright 2014-present Facebook, Inc.
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17#ifndef __STDC_FORMAT_MACROS
18#define __STDC_FORMAT_MACROS
19#endif
20
21#include <folly/SocketAddress.h>
22
23#include <cerrno>
24#include <cstdio>
25#include <cstring>
26#include <sstream>
27#include <string>
28#include <system_error>
29
30#include <boost/functional/hash.hpp>
31
32#include <folly/CppAttributes.h>
33#include <folly/Exception.h>
34#include <folly/Format.h>
35#include <folly/hash/Hash.h>
36#include <folly/net/NetOps.h>
37#include <folly/net/NetworkSocket.h>
38
39namespace {
40
41/**
42 * A structure to free a struct addrinfo when it goes out of scope.
43 */
44struct ScopedAddrInfo {
45 explicit ScopedAddrInfo(struct addrinfo* addrinfo) : info(addrinfo) {}
46 ~ScopedAddrInfo() {
47 freeaddrinfo(info);
48 }
49
50 struct addrinfo* info;
51};
52
53/**
54 * A simple data structure for parsing a host-and-port string.
55 *
56 * Accepts a string of the form "<host>:<port>" or just "<port>",
57 * and contains two string pointers to the host and the port portion of the
58 * string.
59 *
60 * The HostAndPort may contain pointers into the original string. It is
61 * responsible for the user to ensure that the input string is valid for the
62 * lifetime of the HostAndPort structure.
63 */
64struct HostAndPort {
65 HostAndPort(const char* str, bool hostRequired)
66 : host(nullptr), port(nullptr), allocated(nullptr) {
67 // Look for the last colon
68 const char* colon = strrchr(str, ':');
69 if (colon == nullptr) {
70 // No colon, just a port number.
71 if (hostRequired) {
72 throw std::invalid_argument(
73 "expected a host and port string of the "
74 "form \"<host>:<port>\"");
75 }
76 port = str;
77 return;
78 }
79
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);
83 if (!allocated) {
84 throw std::bad_alloc();
85 }
86
87 char* allocatedColon = allocated + (colon - str);
88 *allocatedColon = '\0';
89 host = allocated;
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';
96 ++host;
97 }
98 }
99
100 ~HostAndPort() {
101 free(allocated);
102 }
103
104 const char* host;
105 const char* port;
106 char* allocated;
107};
108
109} // namespace
110
111namespace folly {
112
113bool 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
121 // addresses.
122 return true;
123 }
124 return false;
125}
126
127bool 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.
133 return true;
134 }
135 return false;
136}
137
138void SocketAddress::setFromHostPort(const char* host, uint16_t port) {
139 ScopedAddrInfo results(getAddrInfo(host, port, 0));
140 setFromAddrInfo(results.info);
141}
142
143void SocketAddress::setFromIpPort(const char* ip, uint16_t port) {
144 ScopedAddrInfo results(getAddrInfo(ip, port, AI_NUMERICHOST));
145 setFromAddrInfo(results.info);
146}
147
148void SocketAddress::setFromIpAddrPort(const IPAddress& ipAddr, uint16_t port) {
149 if (external_) {
150 storage_.un.free();
151 external_ = false;
152 }
153 storage_.addr = ipAddr;
154 port_ = port;
155}
156
157void SocketAddress::setFromLocalPort(uint16_t port) {
158 ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG));
159 setFromLocalAddr(results.info);
160}
161
162void SocketAddress::setFromLocalPort(const char* port) {
163 ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG));
164 setFromLocalAddr(results.info);
165}
166
167void SocketAddress::setFromLocalIpPort(const char* addressAndPort) {
168 HostAndPort hp(addressAndPort, false);
169 ScopedAddrInfo results(
170 getAddrInfo(hp.host, hp.port, AI_NUMERICHOST | AI_ADDRCONFIG));
171 setFromLocalAddr(results.info);
172}
173
174void SocketAddress::setFromIpPort(const char* addressAndPort) {
175 HostAndPort hp(addressAndPort, true);
176 ScopedAddrInfo results(getAddrInfo(hp.host, hp.port, AI_NUMERICHOST));
177 setFromAddrInfo(results.info);
178}
179
180void SocketAddress::setFromHostPort(const char* hostAndPort) {
181 HostAndPort hp(hostAndPort, true);
182 ScopedAddrInfo results(getAddrInfo(hp.host, hp.port, 0));
183 setFromAddrInfo(results.info);
184}
185
186int SocketAddress::getPortFrom(const struct sockaddr* address) {
187 switch (address->sa_family) {
188 case AF_INET:
189 return ntohs(((sockaddr_in*)address)->sin_port);
190
191 case AF_INET6:
192 return ntohs(((sockaddr_in6*)address)->sin6_port);
193
194 default:
195 return -1;
196 }
197}
198
199const char* SocketAddress::getFamilyNameFrom(
200 const struct sockaddr* address,
201 const char* defaultResult) {
202#define GETFAMILYNAMEFROM_IMPL(Family) \
203 case Family: \
204 return #Family
205
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);
211
212 default:
213 return defaultResult;
214 }
215
216#undef GETFAMILYNAMEFROM_IMPL
217}
218
219void 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");
226 }
227
228 if (!external_) {
229 storage_.un.init();
230 external_ = true;
231 }
232
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';
242 }
243}
244
245void SocketAddress::setFromPeerAddress(NetworkSocket socket) {
246 setFromSocket(socket, netops::getpeername);
247}
248
249void SocketAddress::setFromLocalAddress(NetworkSocket socket) {
250 setFromSocket(socket, netops::getsockname);
251}
252
253void SocketAddress::setFromSockaddr(const struct sockaddr* address) {
254 uint16_t port;
255
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");
268 } else {
269 throw std::invalid_argument(
270 "SocketAddress::setFromSockaddr() called "
271 "with unsupported address type");
272 }
273
274 setFromIpAddrPort(folly::IPAddress(address), port);
275}
276
277void SocketAddress::setFromSockaddr(
278 const struct sockaddr* address,
279 socklen_t addrlen) {
280 // Check the length to make sure we can access address->sa_family
281 if (addrlen <
282 (offsetof(struct sockaddr, sa_family) + sizeof(address->sa_family))) {
283 throw std::invalid_argument(
284 "SocketAddress::setFromSockaddr() called "
285 "with length too short for a sockaddr");
286 }
287
288 if (address->sa_family == AF_INET) {
289 if (addrlen < sizeof(struct sockaddr_in)) {
290 throw std::invalid_argument(
291 "SocketAddress::setFromSockaddr() called "
292 "with length too short for a sockaddr_in");
293 }
294 setFromSockaddr(reinterpret_cast<const struct sockaddr_in*>(address));
295 } else if (address->sa_family == AF_INET6) {
296 if (addrlen < sizeof(struct sockaddr_in6)) {
297 throw std::invalid_argument(
298 "SocketAddress::setFromSockaddr() called "
299 "with length too short for a sockaddr_in6");
300 }
301 setFromSockaddr(reinterpret_cast<const struct sockaddr_in6*>(address));
302 } else if (address->sa_family == AF_UNIX) {
303 setFromSockaddr(
304 reinterpret_cast<const struct sockaddr_un*>(address), addrlen);
305 } else {
306 throw std::invalid_argument(
307 "SocketAddress::setFromSockaddr() called "
308 "with unsupported address type");
309 }
310}
311
312void SocketAddress::setFromSockaddr(const struct sockaddr_in* address) {
313 assert(address->sin_family == AF_INET);
314 setFromSockaddr((sockaddr*)address);
315}
316
317void SocketAddress::setFromSockaddr(const struct sockaddr_in6* address) {
318 assert(address->sin6_family == AF_INET6);
319 setFromSockaddr((sockaddr*)address);
320}
321
322void SocketAddress::setFromSockaddr(
323 const struct sockaddr_un* address,
324 socklen_t addrlen) {
325 assert(address->sun_family == AF_UNIX);
326 if (addrlen > sizeof(struct sockaddr_un)) {
327 throw std::invalid_argument(
328 "SocketAddress::setFromSockaddr() called "
329 "with length too long for a sockaddr_un");
330 }
331
332 if (!external_) {
333 storage_.un.init();
334 }
335 external_ = true;
336 memcpy(storage_.un.addr, address, size_t(addrlen));
337 updateUnixAddressLength(addrlen);
338
339 // Fill the rest with 0s, just for safety
340 if (addrlen < sizeof(struct sockaddr_un)) {
341 char* p = reinterpret_cast<char*>(storage_.un.addr);
342 memset(p + addrlen, 0, sizeof(struct sockaddr_un) - addrlen);
343 }
344}
345
346const folly::IPAddress& SocketAddress::getIPAddress() const {
347 auto family = getFamily();
348 if (family != AF_INET && family != AF_INET6) {
349 throw InvalidAddressFamilyException(family);
350 }
351 return storage_.addr;
352}
353
354socklen_t SocketAddress::getActualSize() const {
355 if (external_) {
356 return storage_.un.len;
357 }
358 switch (getFamily()) {
359 case AF_UNSPEC:
360 case AF_INET:
361 return sizeof(struct sockaddr_in);
362 case AF_INET6:
363 return sizeof(struct sockaddr_in6);
364 default:
365 throw std::invalid_argument(
366 "SocketAddress::getActualSize() called "
367 "with unrecognized address family");
368 }
369}
370
371std::string SocketAddress::getFullyQualified() const {
372 if (!isFamilyInet()) {
373 throw std::invalid_argument("Can't get address str for non ip address");
374 }
375 return storage_.addr.toFullyQualified();
376}
377
378std::string SocketAddress::getAddressStr() const {
379 if (!isFamilyInet()) {
380 throw std::invalid_argument("Can't get address str for non ip address");
381 }
382 return storage_.addr.str();
383}
384
385bool SocketAddress::isFamilyInet() const {
386 auto family = getFamily();
387 return family == AF_INET || family == AF_INET6;
388}
389
390void SocketAddress::getAddressStr(char* buf, size_t buflen) const {
391 auto ret = getAddressStr();
392 size_t len = std::min(buflen - 1, ret.size());
393 memcpy(buf, ret.data(), len);
394 buf[len] = '\0';
395}
396
397uint16_t SocketAddress::getPort() const {
398 switch (getFamily()) {
399 case AF_INET:
400 case AF_INET6:
401 return port_;
402 default:
403 throw std::invalid_argument(
404 "SocketAddress::getPort() called on non-IP "
405 "address");
406 }
407}
408
409void SocketAddress::setPort(uint16_t port) {
410 switch (getFamily()) {
411 case AF_INET:
412 case AF_INET6:
413 port_ = port;
414 return;
415 default:
416 throw std::invalid_argument(
417 "SocketAddress::setPort() called on non-IP "
418 "address");
419 }
420}
421
422void SocketAddress::convertToIPv4() {
423 if (!tryConvertToIPv4()) {
424 throw std::invalid_argument(
425 "convertToIPv4() called on an addresse that is "
426 "not an IPv4-mapped address");
427 }
428}
429
430bool SocketAddress::tryConvertToIPv4() {
431 if (!isIPv4Mapped()) {
432 return false;
433 }
434
435 storage_.addr = folly::IPAddress::createIPv4(storage_.addr);
436 return true;
437}
438
439bool SocketAddress::mapToIPv6() {
440 if (getFamily() != AF_INET) {
441 return false;
442 }
443
444 storage_.addr = folly::IPAddress::createIPv6(storage_.addr);
445 return true;
446}
447
448std::string SocketAddress::getHostStr() const {
449 return getIpString(0);
450}
451
452std::string SocketAddress::getPath() const {
453 if (!external_) {
454 throw std::invalid_argument(
455 "SocketAddress: attempting to get path "
456 "for a non-Unix address");
457 }
458
459 if (storage_.un.pathLength() == 0) {
460 // anonymous address
461 return std::string();
462 }
463 if (storage_.un.addr->sun_path[0] == '\0') {
464 // abstract namespace
465 return std::string(
466 storage_.un.addr->sun_path, size_t(storage_.un.pathLength()));
467 }
468
469 return std::string(
470 storage_.un.addr->sun_path,
471 strnlen(storage_.un.addr->sun_path, size_t(storage_.un.pathLength())));
472}
473
474std::string SocketAddress::describe() const {
475 if (external_) {
476 if (storage_.un.pathLength() == 0) {
477 return "<anonymous unix address>";
478 }
479
480 if (storage_.un.addr->sun_path[0] == '\0') {
481 // Linux supports an abstract namespace for unix socket addresses
482 return "<abstract unix address>";
483 }
484
485 return std::string(
486 storage_.un.addr->sun_path,
487 strnlen(storage_.un.addr->sun_path, size_t(storage_.un.pathLength())));
488 }
489 switch (getFamily()) {
490 case AF_UNSPEC:
491 return "<uninitialized address>";
492 case AF_INET: {
493 char buf[NI_MAXHOST + 16];
494 getAddressStr(buf, sizeof(buf));
495 size_t iplen = strlen(buf);
496 snprintf(buf + iplen, sizeof(buf) - iplen, ":%" PRIu16, getPort());
497 return buf;
498 }
499 case AF_INET6: {
500 char buf[NI_MAXHOST + 18];
501 buf[0] = '[';
502 getAddressStr(buf + 1, sizeof(buf) - 1);
503 size_t iplen = strlen(buf);
504 snprintf(buf + iplen, sizeof(buf) - iplen, "]:%" PRIu16, getPort());
505 return buf;
506 }
507 default: {
508 char buf[64];
509 snprintf(buf, sizeof(buf), "<unknown address family %d>", getFamily());
510 return buf;
511 }
512 }
513}
514
515bool SocketAddress::operator==(const SocketAddress& other) const {
516 if (external_ != other.external_ || other.getFamily() != getFamily()) {
517 return false;
518 }
519 if (external_) {
520 // anonymous addresses are never equal to any other addresses
521 if (storage_.un.pathLength() == 0 || other.storage_.un.pathLength() == 0) {
522 return false;
523 }
524
525 if (storage_.un.len != other.storage_.un.len) {
526 return false;
527 }
528 int cmp = memcmp(
529 storage_.un.addr->sun_path,
530 other.storage_.un.addr->sun_path,
531 size_t(storage_.un.pathLength()));
532 return cmp == 0;
533 }
534
535 switch (getFamily()) {
536 case AF_INET:
537 case AF_INET6:
538 return (other.storage_.addr == storage_.addr) && (other.port_ == port_);
539 default:
540 throw std::invalid_argument(
541 "SocketAddress: unsupported address family "
542 "for comparison");
543 }
544}
545
546bool SocketAddress::prefixMatch(
547 const SocketAddress& other,
548 unsigned prefixLength) const {
549 if (other.getFamily() != getFamily()) {
550 return false;
551 }
552 uint8_t mask_length = 128;
553 switch (getFamily()) {
554 case AF_INET:
555 mask_length = 32;
556 FOLLY_FALLTHROUGH;
557 case AF_INET6: {
558 auto prefix = folly::IPAddress::longestCommonPrefix(
559 {storage_.addr, mask_length}, {other.storage_.addr, mask_length});
560 return prefix.second >= prefixLength;
561 }
562 default:
563 return false;
564 }
565}
566
567size_t SocketAddress::hash() const {
568 size_t seed = folly::hash::twang_mix64(getFamily());
569
570 if (external_) {
571 enum { kUnixPathMax = sizeof(storage_.un.addr->sun_path) };
572 const char* path = storage_.un.addr->sun_path;
573 auto pathLength = storage_.un.pathLength();
574 // TODO: this probably could be made more efficient
575 for (off_t n = 0; n < pathLength; ++n) {
576 boost::hash_combine(seed, folly::hash::twang_mix64(uint64_t(path[n])));
577 }
578 }
579
580 switch (getFamily()) {
581 case AF_INET:
582 case AF_INET6: {
583 boost::hash_combine(seed, port_);
584 boost::hash_combine(seed, storage_.addr.hash());
585 break;
586 }
587 case AF_UNIX:
588 DCHECK(external_);
589 break;
590 case AF_UNSPEC:
591 default:
592 throw std::invalid_argument(
593 "SocketAddress: unsupported address family "
594 "for hashing");
595 }
596
597 return seed;
598}
599
600struct addrinfo*
601SocketAddress::getAddrInfo(const char* host, uint16_t port, int flags) {
602 // getaddrinfo() requires the port number as a string
603 char portString[sizeof("65535")];
604 snprintf(portString, sizeof(portString), "%" PRIu16, port);
605
606 return getAddrInfo(host, portString, flags);
607}
608
609struct addrinfo*
610SocketAddress::getAddrInfo(const char* host, const char* port, int flags) {
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;
616
617 struct addrinfo* results;
618 int error = getaddrinfo(host, port, &hints, &results);
619 if (error != 0) {
620 auto os = folly::sformat(
621 "Failed to resolve address for '{}': {} (error={})",
622 host,
623 gai_strerror(error),
624 error);
625 throw std::system_error(error, std::generic_category(), os);
626 }
627
628 return results;
629}
630
631void SocketAddress::setFromAddrInfo(const struct addrinfo* info) {
632 setFromSockaddr(info->ai_addr, socklen_t(info->ai_addrlen));
633}
634
635void SocketAddress::setFromLocalAddr(const struct addrinfo* info) {
636 // If an IPv6 address is present, prefer to use it, since IPv4 addresses
637 // can be mapped into IPv6 space.
638 for (const struct addrinfo* ai = info; ai != nullptr; ai = ai->ai_next) {
639 if (ai->ai_family == AF_INET6) {
640 setFromSockaddr(ai->ai_addr, socklen_t(ai->ai_addrlen));
641 return;
642 }
643 }
644
645 // Otherwise, just use the first address in the list.
646 setFromSockaddr(info->ai_addr, socklen_t(info->ai_addrlen));
647}
648
649void SocketAddress::setFromSocket(
650 NetworkSocket socket,
651 int (*fn)(NetworkSocket, struct sockaddr*, socklen_t*)) {
652 // Try to put the address into a local storage buffer.
653 sockaddr_storage tmp_sock;
654 socklen_t addrLen = sizeof(tmp_sock);
655 if (fn(socket, (sockaddr*)&tmp_sock, &addrLen) != 0) {
656 folly::throwSystemError("setFromSocket() failed");
657 }
658
659 setFromSockaddr((sockaddr*)&tmp_sock, addrLen);
660}
661
662std::string SocketAddress::getIpString(int flags) const {
663 char addrString[NI_MAXHOST];
664 getIpString(addrString, sizeof(addrString), flags);
665 return std::string(addrString);
666}
667
668void SocketAddress::getIpString(char* buf, size_t buflen, int flags) const {
669 auto family = getFamily();
670 if (family != AF_INET && family != AF_INET6) {
671 throw std::invalid_argument(
672 "SocketAddress: attempting to get IP address "
673 "for a non-IP address");
674 }
675
676 sockaddr_storage tmp_sock;
677 storage_.addr.toSockaddrStorage(&tmp_sock, port_);
678 int rc = getnameinfo(
679 (sockaddr*)&tmp_sock,
680 sizeof(sockaddr_storage),
681 buf,
682 buflen,
683 nullptr,
684 0,
685 flags);
686 if (rc != 0) {
687 auto os = sformat(
688 "getnameinfo() failed in getIpString() error = {}", gai_strerror(rc));
689 throw std::system_error(rc, std::generic_category(), os);
690 }
691}
692
693void SocketAddress::updateUnixAddressLength(socklen_t addrlen) {
694 if (addrlen < offsetof(struct sockaddr_un, sun_path)) {
695 throw std::invalid_argument(
696 "SocketAddress: attempted to set a Unix socket "
697 "with a length too short for a sockaddr_un");
698 }
699
700 storage_.un.len = addrlen;
701 if (storage_.un.pathLength() == 0) {
702 // anonymous address
703 return;
704 }
705
706 if (storage_.un.addr->sun_path[0] == '\0') {
707 // abstract namespace. honor the specified length
708 } else {
709 // Call strnlen(), just in case the length was overspecified.
710 size_t maxLength = addrlen - offsetof(struct sockaddr_un, sun_path);
711 size_t pathLength = strnlen(storage_.un.addr->sun_path, maxLength);
712 storage_.un.len =
713 socklen_t(offsetof(struct sockaddr_un, sun_path) + pathLength);
714 }
715}
716
717bool SocketAddress::operator<(const SocketAddress& other) const {
718 if (getFamily() != other.getFamily()) {
719 return getFamily() < other.getFamily();
720 }
721
722 if (external_) {
723 // Anonymous addresses can't be compared to anything else.
724 // Return that they are never less than anything.
725 //
726 // Note that this still meets the requirements for a strict weak
727 // ordering, so we can use this operator<() with standard C++ containers.
728 auto thisPathLength = storage_.un.pathLength();
729 if (thisPathLength == 0) {
730 return false;
731 }
732 auto otherPathLength = other.storage_.un.pathLength();
733 if (otherPathLength == 0) {
734 return true;
735 }
736
737 // Compare based on path length first, for efficiency
738 if (thisPathLength != otherPathLength) {
739 return thisPathLength < otherPathLength;
740 }
741 int cmp = memcmp(
742 storage_.un.addr->sun_path,
743 other.storage_.un.addr->sun_path,
744 size_t(thisPathLength));
745 return cmp < 0;
746 }
747 switch (getFamily()) {
748 case AF_INET:
749 case AF_INET6: {
750 if (port_ != other.port_) {
751 return port_ < other.port_;
752 }
753
754 return storage_.addr < other.storage_.addr;
755 }
756 case AF_UNSPEC:
757 default:
758 throw std::invalid_argument(
759 "SocketAddress: unsupported address family for comparing");
760 }
761}
762
763size_t hash_value(const SocketAddress& address) {
764 return address.hash();
765}
766
767std::ostream& operator<<(std::ostream& os, const SocketAddress& addr) {
768 os << addr.describe();
769 return os;
770}
771
772} // namespace folly
773