diff --git a/httplib.h b/httplib.h index e5cb13e..d696380 100644 --- a/httplib.h +++ b/httplib.h @@ -170,6 +170,7 @@ using socket_t = SOCKET; #include #include #include +#include #include #include #ifdef __linux__ @@ -2649,11 +2650,14 @@ inline bool bind_ip_address(socket_t sock, const char *host) { #endif #ifdef USE_IF2IP -inline std::string if2ip(const std::string &ifn) { +inline std::string if2ip(int address_family, const std::string &ifn) { struct ifaddrs *ifap; getifaddrs(&ifap); + std::string addr_candidate; for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) { - if (ifa->ifa_addr && ifn == ifa->ifa_name) { + if (ifa->ifa_addr && ifn == ifa->ifa_name && + (AF_UNSPEC == address_family || + ifa->ifa_addr->sa_family == address_family)) { if (ifa->ifa_addr->sa_family == AF_INET) { auto sa = reinterpret_cast(ifa->ifa_addr); char buf[INET_ADDRSTRLEN]; @@ -2661,11 +2665,26 @@ inline std::string if2ip(const std::string &ifn) { freeifaddrs(ifap); return std::string(buf, INET_ADDRSTRLEN); } + } else if (ifa->ifa_addr->sa_family == AF_INET6) { + auto sa = reinterpret_cast(ifa->ifa_addr); + if (!IN6_IS_ADDR_LINKLOCAL(&sa->sin6_addr)) { + char buf[INET6_ADDRSTRLEN] = {}; + if (inet_ntop(AF_INET6, &sa->sin6_addr, buf, INET6_ADDRSTRLEN)) { + // equivalent to mac's IN6_IS_ADDR_UNIQUE_LOCAL + auto s6_addr_head = sa->sin6_addr.s6_addr[0]; + if (s6_addr_head == 0xfc || s6_addr_head == 0xfd) { + addr_candidate = std::string(buf, INET6_ADDRSTRLEN); + } else { + freeifaddrs(ifap); + return std::string(buf, INET6_ADDRSTRLEN); + } + } + } } } } freeifaddrs(ifap); - return std::string(); + return addr_candidate; } #endif @@ -2680,7 +2699,7 @@ inline socket_t create_client_socket( [&](socket_t sock2, struct addrinfo &ai) -> bool { if (!intf.empty()) { #ifdef USE_IF2IP - auto ip = if2ip(intf); + auto ip = if2ip(address_family, intf); if (ip.empty()) { ip = intf; } if (!bind_ip_address(sock2, ip.c_str())) { error = Error::BindIPAddress; diff --git a/test/test.cc b/test/test.cc index b86ce08..b2ac051 100644 --- a/test/test.cc +++ b/test/test.cc @@ -1419,10 +1419,9 @@ TEST(InvalidFormatTest, StatusCode) { TEST(URLFragmentTest, WithFragment) { Server svr; - svr.Get("/hi", - [](const Request &req, Response &/*res*/) { - EXPECT_TRUE(req.target == "/hi"); - }); + svr.Get("/hi", [](const Request &req, Response & /*res*/) { + EXPECT_TRUE(req.target == "/hi"); + }); auto thread = std::thread([&]() { svr.listen(HOST, PORT); }); @@ -4369,6 +4368,20 @@ TEST(SSLClientTest, WildcardHostNameMatch_Online) { ASSERT_EQ(200, res->status); } +#if 0 +TEST(SSLClientTest, SetInterfaceWithINET6) { + auto cli = std::make_shared("https://httpbin.org"); + ASSERT_TRUE(cli != nullptr); + + cli->set_address_family(AF_INET6); + cli->set_interface("en0"); + + auto res = cli->Get("/get"); + ASSERT_TRUE(res); + ASSERT_EQ(200, res->status); +} +#endif + TEST(SSLClientServerTest, ClientCertPresent) { SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE, CLIENT_CA_CERT_DIR); @@ -4838,15 +4851,15 @@ TEST(MultipartFormDataTest, LargeData) { return true; }); - EXPECT_TRUE(std::string(files[0].name) == "document"); - EXPECT_EQ(size_t(1024 * 1024 * 2), files[0].content.size()); - EXPECT_TRUE(files[0].filename == "2MB_data"); - EXPECT_TRUE(files[0].content_type == "application/octet-stream"); + EXPECT_TRUE(std::string(files[0].name) == "document"); + EXPECT_EQ(size_t(1024 * 1024 * 2), files[0].content.size()); + EXPECT_TRUE(files[0].filename == "2MB_data"); + EXPECT_TRUE(files[0].content_type == "application/octet-stream"); - EXPECT_TRUE(files[1].name == "hello"); - EXPECT_TRUE(files[1].content == "world"); - EXPECT_TRUE(files[1].filename == ""); - EXPECT_TRUE(files[1].content_type == ""); + EXPECT_TRUE(files[1].name == "hello"); + EXPECT_TRUE(files[1].content == "world"); + EXPECT_TRUE(files[1].filename == ""); + EXPECT_TRUE(files[1].content_type == ""); } else { std::string body; content_reader([&](const char *data, size_t data_length) {