Consistent use of sformat in address-related files
[folly.git] / folly / SocketAddress.cpp
1 /*
2  * Copyright 2017 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.h>
36
37 namespace {
38
39 /**
40  * A structure to free a struct addrinfo when it goes out of scope.
41  */
42 struct ScopedAddrInfo {
43   explicit ScopedAddrInfo(struct addrinfo* addrinfo) : info(addrinfo) {}
44   ~ScopedAddrInfo() {
45     freeaddrinfo(info);
46   }
47
48   struct addrinfo* info;
49 };
50
51 /**
52  * A simple data structure for parsing a host-and-port string.
53  *
54  * Accepts a string of the form "<host>:<port>" or just "<port>",
55  * and contains two string pointers to the host and the port portion of the
56  * string.
57  *
58  * The HostAndPort may contain pointers into the original string.  It is
59  * responsible for the user to ensure that the input string is valid for the
60  * lifetime of the HostAndPort structure.
61  */
62 struct HostAndPort {
63   HostAndPort(const char* str, bool hostRequired)
64     : host(nullptr),
65       port(nullptr),
66       allocated(nullptr) {
67
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
112 namespace folly {
113
114 bool 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
128 bool 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
139 void SocketAddress::setFromHostPort(const char* host, uint16_t port) {
140   ScopedAddrInfo results(getAddrInfo(host, port, 0));
141   setFromAddrInfo(results.info);
142 }
143
144 void SocketAddress::setFromIpPort(const char* ip, uint16_t port) {
145   ScopedAddrInfo results(getAddrInfo(ip, port, AI_NUMERICHOST));
146   setFromAddrInfo(results.info);
147 }
148
149 void 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
158 void SocketAddress::setFromLocalPort(uint16_t port) {
159   ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG));
160   setFromLocalAddr(results.info);
161 }
162
163 void SocketAddress::setFromLocalPort(const char* port) {
164   ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG));
165   setFromLocalAddr(results.info);
166 }
167
168 void SocketAddress::setFromLocalIpPort(const char* addressAndPort) {
169   HostAndPort hp(addressAndPort, false);
170   ScopedAddrInfo results(getAddrInfo(hp.host, hp.port,
171                                      AI_NUMERICHOST | AI_ADDRCONFIG));
172   setFromLocalAddr(results.info);
173 }
174
175 void 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
181 void 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
187 int 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
200 const 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
220 void 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
246 void SocketAddress::setFromPeerAddress(int socket) {
247   setFromSocket(socket, getpeername);
248 }
249
250 void SocketAddress::setFromLocalAddress(int socket) {
251   setFromSocket(socket, getsockname);
252 }
253
254 void 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
278 void SocketAddress::setFromSockaddr(const struct sockaddr* address,
279                                      socklen_t addrlen) {
280   // Check the length to make sure we can access address->sa_family
281   if (addrlen < (offsetof(struct sockaddr, sa_family) +
282                  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(reinterpret_cast<const struct sockaddr_un*>(address),
304                     addrlen);
305   } else {
306     throw std::invalid_argument(
307       "SocketAddress::setFromSockaddr() called "
308       "with unsupported address type");
309   }
310 }
311
312 void SocketAddress::setFromSockaddr(const struct sockaddr_in* address) {
313   assert(address->sin_family == AF_INET);
314   setFromSockaddr((sockaddr*)address);
315 }
316
317 void SocketAddress::setFromSockaddr(const struct sockaddr_in6* address) {
318   assert(address->sin6_family == AF_INET6);
319   setFromSockaddr((sockaddr*)address);
320 }
321
322 void SocketAddress::setFromSockaddr(const struct sockaddr_un* address,
323                                      socklen_t addrlen) {
324   assert(address->sun_family == AF_UNIX);
325   if (addrlen > sizeof(struct sockaddr_un)) {
326     throw std::invalid_argument(
327       "SocketAddress::setFromSockaddr() called "
328       "with length too long for a sockaddr_un");
329   }
330
331   if (!external_) {
332     storage_.un.init();
333   }
334   external_ = true;
335   memcpy(storage_.un.addr, address, size_t(addrlen));
336   updateUnixAddressLength(addrlen);
337
338   // Fill the rest with 0s, just for safety
339   if (addrlen < sizeof(struct sockaddr_un)) {
340     char *p = reinterpret_cast<char*>(storage_.un.addr);
341     memset(p + addrlen, 0, sizeof(struct sockaddr_un) - addrlen);
342   }
343 }
344
345 const folly::IPAddress& SocketAddress::getIPAddress() const {
346   auto family = getFamily();
347   if (family != AF_INET && family != AF_INET6) {
348     throw InvalidAddressFamilyException(family);
349   }
350   return storage_.addr;
351 }
352
353 socklen_t SocketAddress::getActualSize() const {
354   if (external_) {
355     return storage_.un.len;
356   }
357   switch (getFamily()) {
358     case AF_UNSPEC:
359     case AF_INET:
360       return sizeof(struct sockaddr_in);
361     case AF_INET6:
362       return sizeof(struct sockaddr_in6);
363     default:
364       throw std::invalid_argument(
365         "SocketAddress::getActualSize() called "
366         "with unrecognized address family");
367   }
368 }
369
370 std::string SocketAddress::getFullyQualified() const {
371   if (!isFamilyInet()) {
372     throw std::invalid_argument("Can't get address str for non ip address");
373   }
374   return storage_.addr.toFullyQualified();
375 }
376
377 std::string SocketAddress::getAddressStr() const {
378   if (!isFamilyInet()) {
379     throw std::invalid_argument("Can't get address str for non ip address");
380   }
381   return storage_.addr.str();
382 }
383
384 bool SocketAddress::isFamilyInet() const {
385   auto family = getFamily();
386   return family == AF_INET || family == AF_INET6;
387 }
388
389 void SocketAddress::getAddressStr(char* buf, size_t buflen) const {
390   auto ret = getAddressStr();
391   size_t len = std::min(buflen - 1, ret.size());
392   memcpy(buf, ret.data(), len);
393   buf[len] = '\0';
394 }
395
396 uint16_t SocketAddress::getPort() const {
397   switch (getFamily()) {
398     case AF_INET:
399     case AF_INET6:
400       return port_;
401     default:
402       throw std::invalid_argument(
403         "SocketAddress::getPort() called on non-IP "
404         "address");
405   }
406 }
407
408 void SocketAddress::setPort(uint16_t port) {
409   switch (getFamily()) {
410     case AF_INET:
411     case AF_INET6:
412       port_ = port;
413       return;
414     default:
415       throw std::invalid_argument(
416         "SocketAddress::setPort() called on non-IP "
417         "address");
418   }
419 }
420
421 void SocketAddress::convertToIPv4() {
422   if (!tryConvertToIPv4()) {
423     throw std::invalid_argument(
424       "convertToIPv4() called on an addresse that is "
425       "not an IPv4-mapped address");
426   }
427 }
428
429 bool SocketAddress::tryConvertToIPv4() {
430   if (!isIPv4Mapped()) {
431     return false;
432   }
433
434   storage_.addr = folly::IPAddress::createIPv4(storage_.addr);
435   return true;
436 }
437
438 bool SocketAddress::mapToIPv6() {
439   if (getFamily() != AF_INET) {
440     return false;
441   }
442
443   storage_.addr = folly::IPAddress::createIPv6(storage_.addr);
444   return true;
445 }
446
447 std::string SocketAddress::getHostStr() const {
448   return getIpString(0);
449 }
450
451 std::string SocketAddress::getPath() const {
452   if (!external_) {
453     throw std::invalid_argument(
454       "SocketAddress: attempting to get path "
455       "for a non-Unix address");
456   }
457
458   if (storage_.un.pathLength() == 0) {
459     // anonymous address
460     return std::string();
461   }
462   if (storage_.un.addr->sun_path[0] == '\0') {
463     // abstract namespace
464     return std::string(
465         storage_.un.addr->sun_path, size_t(storage_.un.pathLength()));
466   }
467
468   return std::string(
469       storage_.un.addr->sun_path,
470       strnlen(storage_.un.addr->sun_path, size_t(storage_.un.pathLength())));
471 }
472
473 std::string SocketAddress::describe() const {
474   if (external_) {
475     if (storage_.un.pathLength() == 0) {
476       return "<anonymous unix address>";
477     }
478
479     if (storage_.un.addr->sun_path[0] == '\0') {
480       // Linux supports an abstract namespace for unix socket addresses
481       return "<abstract unix address>";
482     }
483
484     return std::string(
485         storage_.un.addr->sun_path,
486         strnlen(storage_.un.addr->sun_path, size_t(storage_.un.pathLength())));
487   }
488   switch (getFamily()) {
489     case AF_UNSPEC:
490       return "<uninitialized address>";
491     case AF_INET:
492     {
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     {
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     {
510       char buf[64];
511       snprintf(buf, sizeof(buf), "<unknown address family %d>",
512                getFamily());
513       return buf;
514     }
515   }
516 }
517
518 bool SocketAddress::operator==(const SocketAddress& other) const {
519   if (external_ != other.external_ || other.getFamily() != getFamily()) {
520     return false;
521   }
522   if (external_) {
523     // anonymous addresses are never equal to any other addresses
524     if (storage_.un.pathLength() == 0 ||
525         other.storage_.un.pathLength() == 0) {
526       return false;
527     }
528
529     if (storage_.un.len != other.storage_.un.len) {
530       return false;
531     }
532     int cmp = memcmp(
533         storage_.un.addr->sun_path,
534         other.storage_.un.addr->sun_path,
535         size_t(storage_.un.pathLength()));
536     return cmp == 0;
537   }
538
539   switch (getFamily()) {
540     case AF_INET:
541     case AF_INET6:
542       return (other.storage_.addr == storage_.addr) &&
543         (other.port_ == port_);
544     default:
545       throw std::invalid_argument(
546         "SocketAddress: unsupported address family "
547         "for comparison");
548   }
549 }
550
551 bool SocketAddress::prefixMatch(const SocketAddress& other,
552     unsigned prefixLength) const {
553   if (other.getFamily() != getFamily()) {
554     return false;
555   }
556   uint8_t mask_length = 128;
557   switch (getFamily()) {
558     case AF_INET:
559       mask_length = 32;
560       FOLLY_FALLTHROUGH;
561     case AF_INET6:
562     {
563       auto prefix = folly::IPAddress::longestCommonPrefix(
564         {storage_.addr, mask_length},
565         {other.storage_.addr, mask_length});
566       return prefix.second >= prefixLength;
567     }
568     default:
569       return false;
570   }
571 }
572
573
574 size_t SocketAddress::hash() const {
575   size_t seed = folly::hash::twang_mix64(getFamily());
576
577   if (external_) {
578     enum { kUnixPathMax = sizeof(storage_.un.addr->sun_path) };
579     const char *path = storage_.un.addr->sun_path;
580     auto pathLength = storage_.un.pathLength();
581     // TODO: this probably could be made more efficient
582     for (off_t n = 0; n < pathLength; ++n) {
583       boost::hash_combine(seed, folly::hash::twang_mix64(uint64_t(path[n])));
584     }
585   }
586
587   switch (getFamily()) {
588     case AF_INET:
589     case AF_INET6: {
590       boost::hash_combine(seed, port_);
591       boost::hash_combine(seed, storage_.addr.hash());
592       break;
593     }
594     case AF_UNIX:
595       DCHECK(external_);
596       break;
597     case AF_UNSPEC:
598     default:
599       throw std::invalid_argument(
600         "SocketAddress: unsupported address family "
601         "for hashing");
602   }
603
604   return seed;
605 }
606
607 struct addrinfo* SocketAddress::getAddrInfo(const char* host,
608                                              uint16_t port,
609                                              int flags) {
610   // getaddrinfo() requires the port number as a string
611   char portString[sizeof("65535")];
612   snprintf(portString, sizeof(portString), "%" PRIu16, port);
613
614   return getAddrInfo(host, portString, flags);
615 }
616
617 struct addrinfo* SocketAddress::getAddrInfo(const char* host,
618                                              const char* port,
619                                              int flags) {
620   struct addrinfo hints;
621   memset(&hints, 0, sizeof(hints));
622   hints.ai_family = AF_UNSPEC;
623   hints.ai_socktype = SOCK_STREAM;
624   hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | flags;
625
626   struct addrinfo *results;
627   int error = getaddrinfo(host, port, &hints, &results);
628   if (error != 0) {
629     auto os = folly::sformat(
630         "Failed to resolve address for '{}': {} (error={})",
631         host,
632         gai_strerror(error),
633         error);
634     throw std::system_error(error, std::generic_category(), os);
635   }
636
637   return results;
638 }
639
640 void SocketAddress::setFromAddrInfo(const struct addrinfo* info) {
641   setFromSockaddr(info->ai_addr, socklen_t(info->ai_addrlen));
642 }
643
644 void SocketAddress::setFromLocalAddr(const struct addrinfo* info) {
645   // If an IPv6 address is present, prefer to use it, since IPv4 addresses
646   // can be mapped into IPv6 space.
647   for (const struct addrinfo* ai = info; ai != nullptr; ai = ai->ai_next) {
648     if (ai->ai_family == AF_INET6) {
649       setFromSockaddr(ai->ai_addr, socklen_t(ai->ai_addrlen));
650       return;
651     }
652   }
653
654   // Otherwise, just use the first address in the list.
655   setFromSockaddr(info->ai_addr, socklen_t(info->ai_addrlen));
656 }
657
658 void SocketAddress::setFromSocket(
659     int socket,
660     int (*fn)(int, struct sockaddr*, socklen_t*)) {
661   // Try to put the address into a local storage buffer.
662   sockaddr_storage tmp_sock;
663   socklen_t addrLen = sizeof(tmp_sock);
664   if (fn(socket, (sockaddr*)&tmp_sock, &addrLen) != 0) {
665     folly::throwSystemError("setFromSocket() failed");
666   }
667
668   setFromSockaddr((sockaddr*)&tmp_sock, addrLen);
669 }
670
671 std::string SocketAddress::getIpString(int flags) const {
672   char addrString[NI_MAXHOST];
673   getIpString(addrString, sizeof(addrString), flags);
674   return std::string(addrString);
675 }
676
677 void SocketAddress::getIpString(char *buf, size_t buflen, int flags) const {
678   auto family = getFamily();
679   if (family != AF_INET &&
680       family != AF_INET6) {
681     throw std::invalid_argument(
682       "SocketAddress: attempting to get IP address "
683       "for a non-IP address");
684   }
685
686   sockaddr_storage tmp_sock;
687   storage_.addr.toSockaddrStorage(&tmp_sock, port_);
688   int rc = getnameinfo((sockaddr*)&tmp_sock, sizeof(sockaddr_storage),
689                        buf, buflen, nullptr, 0, 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
697 void 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
721 bool 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
759         storage_.addr < other.storage_.addr;
760     }
761     case AF_UNSPEC:
762     default:
763       throw std::invalid_argument(
764         "SocketAddress: unsupported address family for comparing");
765   }
766 }
767
768 size_t hash_value(const SocketAddress& address) {
769   return address.hash();
770 }
771
772 std::ostream& operator<<(std::ostream& os, const SocketAddress& addr) {
773   os << addr.describe();
774   return os;
775 }
776
777 } // namespace folly