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