From 65a8f4cf44195d6d042c9ed0a174c9c8ea0a1b05 Mon Sep 17 00:00:00 2001 From: yhirose Date: Fri, 31 Dec 2021 15:35:52 -0500 Subject: [PATCH] Added `hosted_at`. (Resolve #1113) --- httplib.h | 67 ++++++++++++++++++++++++++++++++++++++++++++-------- test/test.cc | 30 +++++++++++++++++++++++ 2 files changed, 87 insertions(+), 10 deletions(-) diff --git a/httplib.h b/httplib.h index 8a45b0c..0a6734b 100644 --- a/httplib.h +++ b/httplib.h @@ -1657,6 +1657,10 @@ Client::set_write_timeout(const std::chrono::duration &duration) { * .h + .cc. */ +std::string hosted_at(const char *hostname); + +void hosted_at(const char *hostname, std::vector &addrs); + std::string append_query_params(const char *path, const Params ¶ms); std::pair 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(&addr)->sin6_port); + } else { + return false; } std::array ipstr{}; - if (!getnameinfo(reinterpret_cast(&addr), addr_len, - ipstr.data(), static_cast(ipstr.size()), nullptr, - 0, NI_NUMERICHOST)) { - ip = ipstr.data(); + if (getnameinfo(reinterpret_cast(&addr), addr_len, + ipstr.data(), static_cast(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 addrs; + hosted_at(hostname, addrs); + if (addrs.empty()) { return std::string(); } + return addrs[0]; +} + +inline void hosted_at(const char *hostname, std::vector &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(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 ¶ms) { std::string path_with_query = path; const static std::regex re("[^?]+\\?.*"); diff --git a/test/test.cc b/test/test.cc index 28f728d..b00feb5 100644 --- a/test/test.cc +++ b/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 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 addrs; + hosted_at(host, addrs); + EXPECT_EQ(20u, addrs.size()); + } +} + TEST(ChunkedEncodingTest, WithContentReceiver_Online) { auto host = "www.httpwatch.com";