Fix folly::SocketAddress::reset
[folly.git] / folly / SocketAddress.cpp
1 /*
2  * Copyright 2016 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/Exception.h>
24 #include <folly/Hash.h>
25
26 #include <boost/functional/hash.hpp>
27 #include <string.h>
28 #include <stdio.h>
29 #include <errno.h>
30 #include <sstream>
31 #include <string>
32 #include <system_error>
33
34 namespace {
35
36 /**
37  * A structure to free a struct addrinfo when it goes out of scope.
38  */
39 struct ScopedAddrInfo {
40   explicit ScopedAddrInfo(struct addrinfo* addrinfo) : info(addrinfo) {}
41   ~ScopedAddrInfo() {
42     freeaddrinfo(info);
43   }
44
45   struct addrinfo* info;
46 };
47
48 /**
49  * A simple data structure for parsing a host-and-port string.
50  *
51  * Accepts a string of the form "<host>:<port>" or just "<port>",
52  * and contains two string pointers to the host and the port portion of the
53  * string.
54  *
55  * The HostAndPort may contain pointers into the original string.  It is
56  * responsible for the user to ensure that the input string is valid for the
57  * lifetime of the HostAndPort structure.
58  */
59 struct HostAndPort {
60   HostAndPort(const char* str, bool hostRequired)
61     : host(nullptr),
62       port(nullptr),
63       allocated(nullptr) {
64
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 } // unnamed 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(getAddrInfo(hp.host, hp.port,
168                                      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 = 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(const struct sockaddr* address,
276                                      socklen_t addrlen) {
277   // Check the length to make sure we can access address->sa_family
278   if (addrlen < (offsetof(struct sockaddr, sa_family) +
279                  sizeof(address->sa_family))) {
280     throw std::invalid_argument(
281       "SocketAddress::setFromSockaddr() called "
282       "with length too short for a sockaddr");
283   }
284
285   if (address->sa_family == AF_INET) {
286     if (addrlen < sizeof(struct sockaddr_in)) {
287       throw std::invalid_argument(
288         "SocketAddress::setFromSockaddr() called "
289         "with length too short for a sockaddr_in");
290     }
291     setFromSockaddr(reinterpret_cast<const struct sockaddr_in*>(address));
292   } else if (address->sa_family == AF_INET6) {
293     if (addrlen < sizeof(struct sockaddr_in6)) {
294       throw std::invalid_argument(
295         "SocketAddress::setFromSockaddr() called "
296         "with length too short for a sockaddr_in6");
297     }
298     setFromSockaddr(reinterpret_cast<const struct sockaddr_in6*>(address));
299   } else if (address->sa_family == AF_UNIX) {
300     setFromSockaddr(reinterpret_cast<const struct sockaddr_un*>(address),
301                     addrlen);
302   } else {
303     throw std::invalid_argument(
304       "SocketAddress::setFromSockaddr() called "
305       "with unsupported address type");
306   }
307 }
308
309 void SocketAddress::setFromSockaddr(const struct sockaddr_in* address) {
310   assert(address->sin_family == AF_INET);
311   setFromSockaddr((sockaddr*)address);
312 }
313
314 void SocketAddress::setFromSockaddr(const struct sockaddr_in6* address) {
315   assert(address->sin6_family == AF_INET6);
316   setFromSockaddr((sockaddr*)address);
317 }
318
319 void SocketAddress::setFromSockaddr(const struct sockaddr_un* address,
320                                      socklen_t addrlen) {
321   assert(address->sun_family == AF_UNIX);
322   if (addrlen > sizeof(struct sockaddr_un)) {
323     throw std::invalid_argument(
324       "SocketAddress::setFromSockaddr() called "
325       "with length too long for a sockaddr_un");
326   }
327
328   if (!external_) {
329     storage_.un.init();
330   }
331   external_ = true;
332   memcpy(storage_.un.addr, address, addrlen);
333   updateUnixAddressLength(addrlen);
334
335   // Fill the rest with 0s, just for safety
336   if (addrlen < sizeof(struct sockaddr_un)) {
337     char *p = reinterpret_cast<char*>(storage_.un.addr);
338     memset(p + addrlen, 0, sizeof(struct sockaddr_un) - addrlen);
339   }
340 }
341
342 const folly::IPAddress& SocketAddress::getIPAddress() const {
343   auto family = getFamily();
344   if (family != AF_INET && family != AF_INET6) {
345     throw InvalidAddressFamilyException(family);
346   }
347   return storage_.addr;
348 }
349
350 socklen_t SocketAddress::getActualSize() const {
351   if (external_) {
352     return storage_.un.len;
353   }
354   switch (getFamily()) {
355     case AF_UNSPEC:
356     case AF_INET:
357       return sizeof(struct sockaddr_in);
358     case AF_INET6:
359       return sizeof(struct sockaddr_in6);
360     default:
361       throw std::invalid_argument(
362         "SocketAddress::getActualSize() called "
363         "with unrecognized address family");
364   }
365 }
366
367 std::string SocketAddress::getFullyQualified() const {
368   auto family = getFamily();
369   if (family != AF_INET && family != AF_INET6) {
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   char buf[INET6_ADDRSTRLEN];
377   getAddressStr(buf, sizeof(buf));
378   return buf;
379 }
380
381 void SocketAddress::getAddressStr(char* buf, size_t buflen) const {
382   auto family = getFamily();
383   if (family != AF_INET && family != AF_INET6) {
384     throw std::invalid_argument("Can't get address str for non ip address");
385   }
386   std::string ret = storage_.addr.str();
387   size_t len = std::min(buflen, ret.size());
388   memcpy(buf, ret.data(), len);
389   buf[len] = '\0';
390 }
391
392 uint16_t SocketAddress::getPort() const {
393   switch (getFamily()) {
394     case AF_INET:
395     case AF_INET6:
396       return port_;
397     default:
398       throw std::invalid_argument(
399         "SocketAddress::getPort() called on non-IP "
400         "address");
401   }
402 }
403
404 void SocketAddress::setPort(uint16_t port) {
405   switch (getFamily()) {
406     case AF_INET:
407     case AF_INET6:
408       port_ = port;
409       return;
410     default:
411       throw std::invalid_argument(
412         "SocketAddress::setPort() called on non-IP "
413         "address");
414   }
415 }
416
417 void SocketAddress::convertToIPv4() {
418   if (!tryConvertToIPv4()) {
419     throw std::invalid_argument(
420       "convertToIPv4() called on an addresse that is "
421       "not an IPv4-mapped address");
422   }
423 }
424
425 bool SocketAddress::tryConvertToIPv4() {
426   if (!isIPv4Mapped()) {
427     return false;
428   }
429
430   storage_.addr = folly::IPAddress::createIPv4(storage_.addr);
431   return true;
432 }
433
434 bool SocketAddress::mapToIPv6() {
435   if (getFamily() != AF_INET) {
436     return false;
437   }
438
439   storage_.addr = folly::IPAddress::createIPv6(storage_.addr);
440   return true;
441 }
442
443 std::string SocketAddress::getHostStr() const {
444   return getIpString(0);
445 }
446
447 std::string SocketAddress::getPath() const {
448   if (!external_) {
449     throw std::invalid_argument(
450       "SocketAddress: attempting to get path "
451       "for a non-Unix address");
452   }
453
454   if (storage_.un.pathLength() == 0) {
455     // anonymous address
456     return std::string();
457   }
458   if (storage_.un.addr->sun_path[0] == '\0') {
459     // abstract namespace
460     return std::string(storage_.un.addr->sun_path, storage_.un.pathLength());
461   }
462
463   return std::string(storage_.un.addr->sun_path,
464                      strnlen(storage_.un.addr->sun_path,
465                              storage_.un.pathLength()));
466 }
467
468 std::string SocketAddress::describe() const {
469   if (external_) {
470     if (storage_.un.pathLength() == 0) {
471       return "<anonymous unix address>";
472     }
473
474     if (storage_.un.addr->sun_path[0] == '\0') {
475       // Linux supports an abstract namespace for unix socket addresses
476       return "<abstract unix address>";
477     }
478
479     return std::string(storage_.un.addr->sun_path,
480                        strnlen(storage_.un.addr->sun_path,
481                                storage_.un.pathLength()));
482   }
483   switch (getFamily()) {
484     case AF_UNSPEC:
485       return "<uninitialized address>";
486     case AF_INET:
487     {
488       char buf[NI_MAXHOST + 16];
489       getAddressStr(buf, sizeof(buf));
490       size_t iplen = strlen(buf);
491       snprintf(buf + iplen, sizeof(buf) - iplen, ":%" PRIu16, getPort());
492       return buf;
493     }
494     case AF_INET6:
495     {
496       char buf[NI_MAXHOST + 18];
497       buf[0] = '[';
498       getAddressStr(buf + 1, sizeof(buf) - 1);
499       size_t iplen = strlen(buf);
500       snprintf(buf + iplen, sizeof(buf) - iplen, "]:%" PRIu16, getPort());
501       return buf;
502     }
503     default:
504     {
505       char buf[64];
506       snprintf(buf, sizeof(buf), "<unknown address family %d>",
507                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 ||
520         other.storage_.un.pathLength() == 0) {
521       return false;
522     }
523
524     if (storage_.un.len != other.storage_.un.len) {
525       return false;
526     }
527     int cmp = memcmp(storage_.un.addr->sun_path,
528                      other.storage_.un.addr->sun_path,
529                      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) &&
537         (other.port_ == port_);
538     default:
539       throw std::invalid_argument(
540         "SocketAddress: unsupported address family "
541         "for comparison");
542   }
543 }
544
545 bool SocketAddress::prefixMatch(const SocketAddress& other,
546     unsigned prefixLength) const {
547   if (other.getFamily() != getFamily()) {
548     return false;
549   }
550   int mask_length = 128;
551   switch (getFamily()) {
552     case AF_INET:
553       mask_length = 32;
554       // fallthrough
555     case AF_INET6:
556     {
557       auto prefix = folly::IPAddress::longestCommonPrefix(
558         {storage_.addr, mask_length},
559         {other.storage_.addr, mask_length});
560       return prefix.second >= prefixLength;
561     }
562     default:
563       return false;
564   }
565 }
566
567
568 size_t SocketAddress::hash() const {
569   size_t seed = folly::hash::twang_mix64(getFamily());
570
571   if (external_) {
572     enum { kUnixPathMax = sizeof(storage_.un.addr->sun_path) };
573     const char *path = storage_.un.addr->sun_path;
574     size_t pathLength = storage_.un.pathLength();
575     // TODO: this probably could be made more efficient
576     for (unsigned int n = 0; n < pathLength; ++n) {
577       boost::hash_combine(seed, folly::hash::twang_mix64(path[n]));
578     }
579   }
580
581   switch (getFamily()) {
582     case AF_INET:
583     case AF_INET6: {
584       boost::hash_combine(seed, port_);
585       boost::hash_combine(seed, storage_.addr.hash());
586       break;
587     }
588     case AF_UNIX:
589       DCHECK(external_);
590       break;
591     case AF_UNSPEC:
592     default:
593       throw std::invalid_argument(
594         "SocketAddress: unsupported address family "
595         "for hashing");
596   }
597
598   return seed;
599 }
600
601 struct addrinfo* SocketAddress::getAddrInfo(const char* host,
602                                              uint16_t port,
603                                              int flags) {
604   // getaddrinfo() requires the port number as a string
605   char portString[sizeof("65535")];
606   snprintf(portString, sizeof(portString), "%" PRIu16, port);
607
608   return getAddrInfo(host, portString, flags);
609 }
610
611 struct addrinfo* SocketAddress::getAddrInfo(const char* host,
612                                              const char* port,
613                                              int flags) {
614   struct addrinfo hints;
615   memset(&hints, 0, sizeof(hints));
616   hints.ai_family = AF_UNSPEC;
617   hints.ai_socktype = SOCK_STREAM;
618   hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV | flags;
619
620   struct addrinfo *results;
621   int error = getaddrinfo(host, port, &hints, &results);
622   if (error != 0) {
623     auto os = folly::to<std::string>(
624       "Failed to resolve address for \"", host,  "\": ",
625       gai_strerror(error), " (error=", error,  ")");
626     throw std::system_error(error, std::generic_category(), os);
627   }
628
629   return results;
630 }
631
632 void SocketAddress::setFromAddrInfo(const struct addrinfo* info) {
633   setFromSockaddr(info->ai_addr, info->ai_addrlen);
634 }
635
636 void SocketAddress::setFromLocalAddr(const struct addrinfo* info) {
637   // If an IPv6 address is present, prefer to use it, since IPv4 addresses
638   // can be mapped into IPv6 space.
639   for (const struct addrinfo* ai = info; ai != nullptr; ai = ai->ai_next) {
640     if (ai->ai_family == AF_INET6) {
641       setFromSockaddr(ai->ai_addr, ai->ai_addrlen);
642       return;
643     }
644   }
645
646   // Otherwise, just use the first address in the list.
647   setFromSockaddr(info->ai_addr, info->ai_addrlen);
648 }
649
650 void SocketAddress::setFromSocket(
651     int socket,
652     int (*fn)(int, struct sockaddr*, socklen_t*)) {
653   // Try to put the address into a local storage buffer.
654   sockaddr_storage tmp_sock;
655   socklen_t addrLen = sizeof(tmp_sock);
656   if (fn(socket, (sockaddr*)&tmp_sock, &addrLen) != 0) {
657     folly::throwSystemError("setFromSocket() failed");
658   }
659
660   setFromSockaddr((sockaddr*)&tmp_sock, addrLen);
661 }
662
663 std::string SocketAddress::getIpString(int flags) const {
664   char addrString[NI_MAXHOST];
665   getIpString(addrString, sizeof(addrString), flags);
666   return std::string(addrString);
667 }
668
669 void SocketAddress::getIpString(char *buf, size_t buflen, int flags) const {
670   auto family = getFamily();
671   if (family != AF_INET &&
672       family != AF_INET6) {
673     throw std::invalid_argument(
674       "SocketAddress: attempting to get IP address "
675       "for a non-IP address");
676   }
677
678   sockaddr_storage tmp_sock;
679   storage_.addr.toSockaddrStorage(&tmp_sock, port_);
680   int rc = getnameinfo((sockaddr*)&tmp_sock, sizeof(sockaddr_storage),
681                        buf, buflen, nullptr, 0, flags);
682   if (rc != 0) {
683     auto os = folly::to<std::string>(
684       "getnameinfo() failed in getIpString() error = ",
685       gai_strerror(rc));
686     throw std::system_error(rc, std::generic_category(), os);
687   }
688 }
689
690 void SocketAddress::updateUnixAddressLength(socklen_t addrlen) {
691   if (addrlen < offsetof(struct sockaddr_un, sun_path)) {
692     throw std::invalid_argument(
693       "SocketAddress: attempted to set a Unix socket "
694       "with a length too short for a sockaddr_un");
695   }
696
697   storage_.un.len = addrlen;
698   if (storage_.un.pathLength() == 0) {
699     // anonymous address
700     return;
701   }
702
703   if (storage_.un.addr->sun_path[0] == '\0') {
704     // abstract namespace.  honor the specified length
705   } else {
706     // Call strnlen(), just in case the length was overspecified.
707     socklen_t maxLength = addrlen - offsetof(struct sockaddr_un, sun_path);
708     size_t pathLength = strnlen(storage_.un.addr->sun_path, maxLength);
709     storage_.un.len = offsetof(struct sockaddr_un, sun_path) + pathLength;
710   }
711 }
712
713 bool SocketAddress::operator<(const SocketAddress& other) const {
714   if (getFamily() != other.getFamily()) {
715     return getFamily() < other.getFamily();
716   }
717
718   if (external_) {
719     // Anonymous addresses can't be compared to anything else.
720     // Return that they are never less than anything.
721     //
722     // Note that this still meets the requirements for a strict weak
723     // ordering, so we can use this operator<() with standard C++ containers.
724     size_t thisPathLength = storage_.un.pathLength();
725     if (thisPathLength == 0) {
726       return false;
727     }
728     size_t otherPathLength = other.storage_.un.pathLength();
729     if (otherPathLength == 0) {
730       return true;
731     }
732
733     // Compare based on path length first, for efficiency
734     if (thisPathLength != otherPathLength) {
735       return thisPathLength < otherPathLength;
736     }
737     int cmp = memcmp(storage_.un.addr->sun_path,
738                      other.storage_.un.addr->sun_path,
739                      thisPathLength);
740     return cmp < 0;
741   }
742   switch (getFamily()) {
743     case AF_INET:
744     case AF_INET6: {
745       if (port_ != other.port_) {
746         return port_ < other.port_;
747       }
748
749       return
750         storage_.addr < other.storage_.addr;
751     }
752     case AF_UNSPEC:
753     default:
754       throw std::invalid_argument(
755         "SocketAddress: unsupported address family for comparing");
756   }
757 }
758
759 size_t hash_value(const SocketAddress& address) {
760   return address.hash();
761 }
762
763 std::ostream& operator<<(std::ostream& os, const SocketAddress& addr) {
764   os << addr.describe();
765   return os;
766 }
767
768 } // folly