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