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