make Range::size() constexpr
[folly.git] / folly / SocketAddress.cpp
index 6c83e033fe3b8178535aa70bd24e0255404085e8..dbeff61bf958dbc32666457d17b5bb4566764460 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2014 Facebook, Inc.
+ * Copyright 2016 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -23,7 +23,6 @@
 #include <folly/Hash.h>
 
 #include <boost/functional/hash.hpp>
-#include <boost/static_assert.hpp>
 #include <string.h>
 #include <stdio.h>
 #include <errno.h>
@@ -36,7 +35,7 @@ namespace {
  * A structure to free a struct addrinfo when it goes out of scope.
  */
 struct ScopedAddrInfo {
-  explicit ScopedAddrInfo(struct addrinfo* info) : info(info) {}
+  explicit ScopedAddrInfo(struct addrinfo* addrinfo) : info(addrinfo) {}
   ~ScopedAddrInfo() {
     freeaddrinfo(info);
   }
@@ -112,7 +111,7 @@ bool SocketAddress::isPrivateAddress() const {
   if (family == AF_INET || family == AF_INET6) {
     return storage_.addr.isPrivate() ||
       (storage_.addr.isV6() && storage_.addr.asV6().isLinkLocal());
-  } else if (family == AF_UNIX) {
+  } else if (external_) {
     // Unix addresses are always local to a host.  Return true,
     // since this conforms to the semantics of returning true for IP loopback
     // addresses.
@@ -125,7 +124,7 @@ bool SocketAddress::isLoopbackAddress() const {
   auto family = getFamily();
   if (family == AF_INET || family == AF_INET6) {
     return storage_.addr.isLoopback();
-  } else if (family == AF_UNIX) {
+  } else if (external_) {
     // Return true for UNIX addresses, since they are always local to a host.
     return true;
   }
@@ -142,6 +141,15 @@ void SocketAddress::setFromIpPort(const char* ip, uint16_t port) {
   setFromAddrInfo(results.info);
 }
 
+void SocketAddress::setFromIpAddrPort(const IPAddress& ipAddr, uint16_t port) {
+  if (external_) {
+    storage_.un.free();
+    external_ = false;
+  }
+  storage_.addr = ipAddr;
+  port_ = port;
+}
+
 void SocketAddress::setFromLocalPort(uint16_t port) {
   ScopedAddrInfo results(getAddrInfo(nullptr, port, AI_ADDRCONFIG));
   setFromLocalAddr(results.info);
@@ -171,37 +179,76 @@ void SocketAddress::setFromHostPort(const char* hostAndPort) {
   setFromAddrInfo(results.info);
 }
 
-void SocketAddress::setFromPath(const char* path, size_t len) {
-  if (getFamily() != AF_UNIX) {
+int SocketAddress::getPortFrom(const struct sockaddr* address) {
+  switch (address->sa_family) {
+    case AF_INET:
+      return ntohs(((sockaddr_in*)address)->sin_port);
+
+    case AF_INET6:
+      return ntohs(((sockaddr_in6*)address)->sin6_port);
+
+    default:
+      return -1;
+  }
+}
+
+const char* SocketAddress::getFamilyNameFrom(
+    const struct sockaddr* address,
+    const char* defaultResult) {
+#define GETFAMILYNAMEFROM_IMPL(Family) \
+  case Family:                         \
+    return #Family
+
+  switch (address->sa_family) {
+    GETFAMILYNAMEFROM_IMPL(AF_INET);
+    GETFAMILYNAMEFROM_IMPL(AF_INET6);
+    GETFAMILYNAMEFROM_IMPL(AF_UNIX);
+    GETFAMILYNAMEFROM_IMPL(AF_UNSPEC);
+
+    default:
+      return defaultResult;
+  }
+
+#undef GETFAMILYNAMEFROM_IMPL
+}
+
+void SocketAddress::setFromPath(StringPiece path) {
+  // Before we touch storage_, check to see if the length is too big.
+  // Note that "storage_.un.addr->sun_path" may not be safe to evaluate here,
+  // but sizeof() just uses its type, and does't evaluate it.
+  if (path.size() > sizeof(storage_.un.addr->sun_path)) {
+    throw std::invalid_argument(
+        "socket path too large to fit into sockaddr_un");
+  }
+
+  if (!external_) {
     storage_.un.init();
     external_ = true;
   }
 
+  size_t len = path.size();
   storage_.un.len = offsetof(struct sockaddr_un, sun_path) + len;
-  if (len > sizeof(storage_.un.addr->sun_path)) {
-    throw std::invalid_argument(
-      "socket path too large to fit into sockaddr_un");
-  } else if (len == sizeof(storage_.un.addr->sun_path)) {
-    // Note that there will be no terminating NUL in this case.
-    // We allow this since getsockname() and getpeername() may return
-    // Unix socket addresses with paths that fit exactly in sun_path with no
-    // terminating NUL.
-    memcpy(storage_.un.addr->sun_path, path, len);
-  } else {
-    memcpy(storage_.un.addr->sun_path, path, len + 1);
+  memcpy(storage_.un.addr->sun_path, path.data(), len);
+  // If there is room, put a terminating NUL byte in sun_path.  In general the
+  // path should be NUL terminated, although getsockname() and getpeername()
+  // may return Unix socket addresses with paths that fit exactly in sun_path
+  // with no terminating NUL.
+  if (len < sizeof(storage_.un.addr->sun_path)) {
+    storage_.un.addr->sun_path[len] = '\0';
   }
 }
 
-void SocketAddress::setFromPeerAddress(int socket) {
+void SocketAddress::setFromPeerAddress(SocketDesc socket) {
   setFromSocket(socket, getpeername);
 }
 
-void SocketAddress::setFromLocalAddress(int socket) {
+void SocketAddress::setFromLocalAddress(SocketDesc socket) {
   setFromSocket(socket, getsockname);
 }
 
 void SocketAddress::setFromSockaddr(const struct sockaddr* address) {
   uint16_t port;
+
   if (address->sa_family == AF_INET) {
     port = ntohs(((sockaddr_in*)address)->sin_port);
   } else if (address->sa_family == AF_INET6) {
@@ -219,12 +266,8 @@ void SocketAddress::setFromSockaddr(const struct sockaddr* address) {
       "SocketAddress::setFromSockaddr() called "
       "with unsupported address type");
   }
-  if (getFamily() == AF_UNIX) {
-    storage_.un.free();
-    external_ = false;
-  }
-  storage_.addr = folly::IPAddress(address);
-  port_ = port;
+
+  setFromIpAddrPort(folly::IPAddress(address), port);
 }
 
 void SocketAddress::setFromSockaddr(const struct sockaddr* address,
@@ -300,14 +343,15 @@ const folly::IPAddress& SocketAddress::getIPAddress() const {
 }
 
 socklen_t SocketAddress::getActualSize() const {
+  if (external_) {
+    return storage_.un.len;
+  }
   switch (getFamily()) {
     case AF_UNSPEC:
     case AF_INET:
       return sizeof(struct sockaddr_in);
     case AF_INET6:
       return sizeof(struct sockaddr_in6);
-    case AF_UNIX:
-      return storage_.un.len;
     default:
       throw std::invalid_argument(
         "SocketAddress::getActualSize() called "
@@ -396,7 +440,7 @@ std::string SocketAddress::getHostStr() const {
 }
 
 std::string SocketAddress::getPath() const {
-  if (getFamily() != AF_UNIX) {
+  if (!external_) {
     throw std::invalid_argument(
       "SocketAddress: attempting to get path "
       "for a non-Unix address");
@@ -417,6 +461,20 @@ std::string SocketAddress::getPath() const {
 }
 
 std::string SocketAddress::describe() const {
+  if (external_) {
+    if (storage_.un.pathLength() == 0) {
+      return "<anonymous unix address>";
+    }
+
+    if (storage_.un.addr->sun_path[0] == '\0') {
+      // Linux supports an abstract namespace for unix socket addresses
+      return "<abstract unix address>";
+    }
+
+    return std::string(storage_.un.addr->sun_path,
+                       strnlen(storage_.un.addr->sun_path,
+                               storage_.un.pathLength()));
+  }
   switch (getFamily()) {
     case AF_UNSPEC:
       return "<uninitialized address>";
@@ -437,21 +495,6 @@ std::string SocketAddress::describe() const {
       snprintf(buf + iplen, sizeof(buf) - iplen, "]:%" PRIu16, getPort());
       return buf;
     }
-    case AF_UNIX:
-    {
-      if (storage_.un.pathLength() == 0) {
-        return "<anonymous unix address>";
-      }
-
-      if (storage_.un.addr->sun_path[0] == '\0') {
-        // Linux supports an abstract namespace for unix socket addresses
-        return "<abstract unix address>";
-      }
-
-      return std::string(storage_.un.addr->sun_path,
-                         strnlen(storage_.un.addr->sun_path,
-                                 storage_.un.pathLength()));
-    }
     default:
     {
       char buf[64];
@@ -463,31 +506,30 @@ std::string SocketAddress::describe() const {
 }
 
 bool SocketAddress::operator==(const SocketAddress& other) const {
-  if (other.getFamily() != getFamily()) {
+  if (external_ != other.external_ || other.getFamily() != getFamily()) {
     return false;
   }
+  if (external_) {
+    // anonymous addresses are never equal to any other addresses
+    if (storage_.un.pathLength() == 0 ||
+        other.storage_.un.pathLength() == 0) {
+      return false;
+    }
+
+    if (storage_.un.len != other.storage_.un.len) {
+      return false;
+    }
+    int cmp = memcmp(storage_.un.addr->sun_path,
+                     other.storage_.un.addr->sun_path,
+                     storage_.un.pathLength());
+    return cmp == 0;
+  }
 
   switch (getFamily()) {
     case AF_INET:
     case AF_INET6:
       return (other.storage_.addr == storage_.addr) &&
         (other.port_ == port_);
-    case AF_UNIX:
-    {
-      // anonymous addresses are never equal to any other addresses
-      if (storage_.un.pathLength() == 0 ||
-          other.storage_.un.pathLength() == 0) {
-        return false;
-      }
-
-      if (storage_.un.len != other.storage_.un.len) {
-        return false;
-      }
-      int cmp = memcmp(storage_.un.addr->sun_path,
-                       other.storage_.un.addr->sun_path,
-                       storage_.un.pathLength());
-      return cmp == 0;
-    }
     default:
       throw std::invalid_argument(
         "SocketAddress: unsupported address family "
@@ -521,6 +563,16 @@ bool SocketAddress::prefixMatch(const SocketAddress& other,
 size_t SocketAddress::hash() const {
   size_t seed = folly::hash::twang_mix64(getFamily());
 
+  if (external_) {
+    enum { kUnixPathMax = sizeof(storage_.un.addr->sun_path) };
+    const char *path = storage_.un.addr->sun_path;
+    size_t pathLength = storage_.un.pathLength();
+    // TODO: this probably could be made more efficient
+    for (unsigned int n = 0; n < pathLength; ++n) {
+      boost::hash_combine(seed, folly::hash::twang_mix64(path[n]));
+    }
+  }
+
   switch (getFamily()) {
     case AF_INET:
     case AF_INET6: {
@@ -529,16 +581,8 @@ size_t SocketAddress::hash() const {
       break;
     }
     case AF_UNIX:
-    {
-      enum { kUnixPathMax = sizeof(storage_.un.addr->sun_path) };
-      const char *path = storage_.un.addr->sun_path;
-      size_t pathLength = storage_.un.pathLength();
-      // TODO: this probably could be made more efficient
-      for (unsigned int n = 0; n < pathLength; ++n) {
-        boost::hash_combine(seed, folly::hash::twang_mix64(path[n]));
-      }
+      DCHECK(external_);
       break;
-    }
     case AF_UNSPEC:
     default:
       throw std::invalid_argument(
@@ -598,17 +642,7 @@ void SocketAddress::setFromLocalAddr(const struct addrinfo* info) {
   setFromSockaddr(info->ai_addr, info->ai_addrlen);
 }
 
-void SocketAddress::setFromSocket(int socket,
-                                  int (*fn)(int, sockaddr*, socklen_t*)) {
-  // If this was previously an AF_UNIX socket, free the external buffer.
-  // TODO: It would be smarter to just remember the external buffer, and then
-  // re-use it or free it depending on if the new address is also a unix
-  // socket.
-  if (getFamily() == AF_UNIX) {
-    storage_.un.free();
-    external_ = false;
-  }
-
+void SocketAddress::setFromSocket(SocketDesc socket, GetPeerNameFunc fn) {
   // Try to put the address into a local storage buffer.
   sockaddr_storage tmp_sock;
   socklen_t addrLen = sizeof(tmp_sock);
@@ -674,6 +708,30 @@ bool SocketAddress::operator<(const SocketAddress& other) const {
     return getFamily() < other.getFamily();
   }
 
+  if (external_) {
+    // Anonymous addresses can't be compared to anything else.
+    // Return that they are never less than anything.
+    //
+    // Note that this still meets the requirements for a strict weak
+    // ordering, so we can use this operator<() with standard C++ containers.
+    size_t thisPathLength = storage_.un.pathLength();
+    if (thisPathLength == 0) {
+      return false;
+    }
+    size_t otherPathLength = other.storage_.un.pathLength();
+    if (otherPathLength == 0) {
+      return true;
+    }
+
+    // Compare based on path length first, for efficiency
+    if (thisPathLength != otherPathLength) {
+      return thisPathLength < otherPathLength;
+    }
+    int cmp = memcmp(storage_.un.addr->sun_path,
+                     other.storage_.un.addr->sun_path,
+                     thisPathLength);
+    return cmp < 0;
+  }
   switch (getFamily()) {
     case AF_INET:
     case AF_INET6: {
@@ -684,30 +742,6 @@ bool SocketAddress::operator<(const SocketAddress& other) const {
       return
         storage_.addr < other.storage_.addr;
     }
-    case AF_UNIX: {
-      // Anonymous addresses can't be compared to anything else.
-      // Return that they are never less than anything.
-      //
-      // Note that this still meets the requirements for a strict weak
-      // ordering, so we can use this operator<() with standard C++ containers.
-      size_t thisPathLength = storage_.un.pathLength();
-      if (thisPathLength == 0) {
-        return false;
-      }
-      size_t otherPathLength = other.storage_.un.pathLength();
-      if (otherPathLength == 0) {
-        return true;
-      }
-
-      // Compare based on path length first, for efficiency
-      if (thisPathLength != otherPathLength) {
-        return thisPathLength < otherPathLength;
-      }
-      int cmp = memcmp(storage_.un.addr->sun_path,
-                       other.storage_.un.addr->sun_path,
-                       thisPathLength);
-      return cmp < 0;
-    }
     case AF_UNSPEC:
     default:
       throw std::invalid_argument(