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.
This commit is contained in:
zhangxiaoyu.york 2024-11-12 10:13:53 +08:00 committed by GitHub
parent 1f6a0726d9
commit 662573b6bf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 30 additions and 1 deletions

View File

@ -8,6 +8,7 @@
#include <netinet/in.h> #include <netinet/in.h>
#endif #endif
#include <arpa/inet.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <sys/types.h> #include <sys/types.h>
@ -101,6 +102,7 @@ static std::string string_join(std::vector<std::string> const& srcs,
static bool dns_resolve(std::string const& target, static bool dns_resolve(std::string const& target,
std::vector<std::string>& endpoints, bool ipv4 = true) { std::vector<std::string>& endpoints, bool ipv4 = true) {
std::vector<std::string> target_parts; std::vector<std::string> target_parts;
bool ipv6_url{false};
{ {
size_t rindex = target.rfind(':'); size_t rindex = target.rfind(':');
if (rindex == target.npos) { if (rindex == target.npos) {
@ -110,7 +112,17 @@ static bool dns_resolve(std::string const& target,
#endif #endif
return false; 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)); target_parts.push_back(target.substr(rindex + 1));
} }
@ -132,6 +144,16 @@ static bool dns_resolve(std::string const& target,
} }
#endif #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; struct addrinfo hints = {}, *addrs;
hints.ai_family = ipv4 ? AF_INET : AF_INET6; hints.ai_family = ipv4 ? AF_INET : AF_INET6;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;

View File

@ -9,6 +9,9 @@ static const std::string etcd_v4_url =
etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379"); etcdv3::detail::resolve_etcd_endpoints("http://127.0.0.1:2379");
static const std::string etcd_v6_url = static const std::string etcd_v6_url =
etcdv3::detail::resolve_etcd_endpoints("http://::1:2379"); 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") { TEST_CASE("test ipv4 connection") {
std::cout << "ipv4 endpoints: " << etcd_v4_url << std::endl; 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; std::cout << "ipv6 endpoints: " << etcd_v6_url << std::endl;
etcd::Client etcd(etcd_v6_url); etcd::Client etcd(etcd_v6_url);
REQUIRE(etcd.head().get().is_ok()); 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());
} }