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