Browse Source

Added `hosted_at`. (Resolve #1113)

yhirose 4 years ago
parent
commit
65a8f4cf44
2 changed files with 87 additions and 10 deletions
  1. 57 10
      httplib.h
  2. 30 0
      test/test.cc

+ 57 - 10
httplib.h

@@ -1657,6 +1657,10 @@ Client::set_write_timeout(const std::chrono::duration<Rep, Period> &duration) {
  * .h + .cc.
  */
 
+std::string hosted_at(const char *hostname);
+
+void hosted_at(const char *hostname, std::vector<std::string> &addrs);
+
 std::string append_query_params(const char *path, const Params &params);
 
 std::pair<std::string, std::string> make_range_header(Ranges ranges);
@@ -2499,25 +2503,28 @@ socket_t create_socket(const char *host, const char *ip, int port,
                        SocketOptions socket_options,
                        BindOrConnect bind_or_connect) {
   // Get address info
+  const char *node = nullptr;
   struct addrinfo hints;
   struct addrinfo *result;
 
   memset(&hints, 0, sizeof(struct addrinfo));
-  hints.ai_family = address_family;
   hints.ai_socktype = SOCK_STREAM;
-  hints.ai_flags = socket_flags;
   hints.ai_protocol = 0;
 
-  // Ask getaddrinfo to convert IP in c-string to address
   if (ip[0] != '\0') {
+    node = ip;
+    // Ask getaddrinfo to convert IP in c-string to address
     hints.ai_family = AF_UNSPEC;
     hints.ai_flags = AI_NUMERICHOST;
+  } else {
+    node = host;
+    hints.ai_family = address_family;
+    hints.ai_flags = socket_flags;
   }
 
   auto service = std::to_string(port);
 
-  if (ip[0] != '\0' ? getaddrinfo(ip, service.c_str(), &hints, &result)
-                    : getaddrinfo(host, service.c_str(), &hints, &result)) {
+  if (getaddrinfo(node, service.c_str(), &hints, &result)) {
 #if defined __linux__ && !defined __ANDROID__
     res_init();
 #endif
@@ -2728,7 +2735,7 @@ inline socket_t create_client_socket(
   return sock;
 }
 
-inline void get_remote_ip_and_port(const struct sockaddr_storage &addr,
+inline bool get_remote_ip_and_port(const struct sockaddr_storage &addr,
                                    socklen_t addr_len, std::string &ip,
                                    int &port) {
   if (addr.ss_family == AF_INET) {
@@ -2736,14 +2743,19 @@ inline void get_remote_ip_and_port(const struct sockaddr_storage &addr,
   } else if (addr.ss_family == AF_INET6) {
     port =
         ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);
+  } else {
+    return false;
   }
 
   std::array<char, NI_MAXHOST> ipstr{};
-  if (!getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,
-                   ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,
-                   0, NI_NUMERICHOST)) {
-    ip = ipstr.data();
+  if (getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,
+                  ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,
+                  0, NI_NUMERICHOST)) {
+    return false;
   }
+
+  ip = ipstr.data();
+  return true;
 }
 
 inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
@@ -4304,6 +4316,41 @@ private:
 
 } // namespace detail
 
+inline std::string hosted_at(const char *hostname) {
+  std::vector<std::string> addrs;
+  hosted_at(hostname, addrs);
+  if (addrs.empty()) { return std::string(); }
+  return addrs[0];
+}
+
+inline void hosted_at(const char *hostname, std::vector<std::string> &addrs) {
+  struct addrinfo hints;
+  struct addrinfo *result;
+
+  memset(&hints, 0, sizeof(struct addrinfo));
+  hints.ai_family = AF_UNSPEC;
+  hints.ai_socktype = SOCK_STREAM;
+  hints.ai_protocol = 0;
+
+  if (getaddrinfo(hostname, nullptr, &hints, &result)) {
+#if defined __linux__ && !defined __ANDROID__
+    res_init();
+#endif
+    return;
+  }
+
+  for (auto rp = result; rp; rp = rp->ai_next) {
+    const auto &addr =
+        *reinterpret_cast<struct sockaddr_storage *>(rp->ai_addr);
+    std::string ip;
+    int dummy = -1;
+    if (detail::get_remote_ip_and_port(addr, sizeof(struct sockaddr_storage),
+                                       ip, dummy)) {
+      addrs.push_back(ip);
+    }
+  }
+}
+
 inline std::string append_query_params(const char *path, const Params &params) {
   std::string path_with_query = path;
   const static std::regex re("[^?]+\\?.*");

+ 30 - 0
test/test.cc

@@ -401,6 +401,36 @@ TEST(ChunkedEncodingTest, FromHTTPWatch_Online) {
   EXPECT_EQ(out, res->body);
 }
 
+TEST(HostnameToIPConversionTest, HTTPWatch_Online) {
+  auto host = "www.httpwatch.com";
+
+  {
+    auto ip = hosted_at(host);
+    EXPECT_EQ("191.236.16.12", ip);
+  }
+
+  {
+    std::vector<std::string> addrs;
+    hosted_at(host, addrs);
+    EXPECT_EQ(1u, addrs.size());
+  }
+}
+
+TEST(HostnameToIPConversionTest, YouTube_Online) {
+  auto host = "www.youtube.com";
+
+  {
+    auto ip = hosted_at(host);
+    EXPECT_EQ("2607:f8b0:4006:809::200e", ip);
+  }
+
+  {
+    std::vector<std::string> addrs;
+    hosted_at(host, addrs);
+    EXPECT_EQ(20u, addrs.size());
+  }
+}
+
 TEST(ChunkedEncodingTest, WithContentReceiver_Online) {
   auto host = "www.httpwatch.com";