From 662573b6bf9f96f6f5dedcb4fa028b17081b6891 Mon Sep 17 00:00:00 2001 From: "zhangxiaoyu.york" Date: Tue, 12 Nov 2024 10:13:53 +0800 Subject: [PATCH] feature:support ipv6 address (#280) Problem Summary: Now the etcd client address do not support host format such as http://[ipv6]:port. and [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986#section-3.2.2) ``` A host identified by an Internet Protocol literal address, version 6 [[RFC3513](https://datatracker.ietf.org/doc/html/rfc3513)] or later, is distinguished by enclosing the IP literal within square brackets ("[" and "]"). This is the only place where square bracket characters are allowed in the URI syntax. In anticipation of future, as-yet-undefined IP literal address formats, an implementation may use an optional version flag to indicate such a format explicitly rather than rely on heuristic determination. IP-literal = "[" ( IPv6address / IPvFuture ) "]" ``` the ipv6 address is distinguished by enclosing the IP literal within square brackets ("[" and "]"). What is changed and how it works: if we find the host part of a URL starts with a "[" and ends with a "]". it will be specifically judged whether it is a valid IPv6 address. If it is a valid address, it can be returned directly without call the `getaddrinfo` interface. --- src/SyncClient.cpp | 24 +++++++++++++++++++++++- tst/EtcdResolverTest.cpp | 7 +++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/SyncClient.cpp b/src/SyncClient.cpp index 953106a..679be12 100644 --- a/src/SyncClient.cpp +++ b/src/SyncClient.cpp @@ -8,6 +8,7 @@ #include #endif +#include #include #include #include @@ -101,6 +102,7 @@ static std::string string_join(std::vector const& srcs, static bool dns_resolve(std::string const& target, std::vector& endpoints, bool ipv4 = true) { std::vector target_parts; + bool ipv6_url{false}; { size_t rindex = target.rfind(':'); if (rindex == target.npos) { @@ -110,7 +112,17 @@ static bool dns_resolve(std::string const& target, #endif return false; } - target_parts.push_back(target.substr(0, rindex)); + + std::string host(target.substr(0, rindex)); + + // host format is [ipv6] + if (!ipv4 && !host.empty() && host[0] == '[' && + host[host.size() - 1] == ']') { + host = target.substr(1, rindex - 2); + ipv6_url = true; + } + + target_parts.push_back(host); target_parts.push_back(target.substr(rindex + 1)); } @@ -132,6 +144,16 @@ static bool dns_resolve(std::string const& target, } #endif + if (ipv6_url) { + // check valid ipv6 + struct sockaddr_in6 sa6; + if (inet_pton(AF_INET6, target_parts[0].c_str(), &(sa6.sin6_addr)) == 1) { + endpoints.emplace_back(target); + return true; + } + return false; + } + struct addrinfo hints = {}, *addrs; hints.ai_family = ipv4 ? AF_INET : AF_INET6; hints.ai_socktype = SOCK_STREAM; diff --git a/tst/EtcdResolverTest.cpp b/tst/EtcdResolverTest.cpp index 2d00bf6..14f7ad1 100644 --- a/tst/EtcdResolverTest.cpp +++ b/tst/EtcdResolverTest.cpp @@ -9,6 +9,9 @@ static const std::string etcd_v4_url = etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379"); static const std::string etcd_v6_url = etcdv3::detail::resolve_etcd_endpoints("http://::1:2379"); +// http://[ipv6]:port url +static const std::string etcd_ipv6_url = + etcdv3::detail::resolve_etcd_endpoints("http://[::1]:2379"); TEST_CASE("test ipv4 connection") { std::cout << "ipv4 endpoints: " << etcd_v4_url << std::endl; @@ -20,4 +23,8 @@ TEST_CASE("test ipv6 connection") { std::cout << "ipv6 endpoints: " << etcd_v6_url << std::endl; etcd::Client etcd(etcd_v6_url); REQUIRE(etcd.head().get().is_ok()); + + std::cout << "ipv6 endpoints: " << etcd_ipv6_url << std::endl; + etcd::Client etcd1(etcd_ipv6_url); + REQUIRE(etcd1.head().get().is_ok()); }