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