From a9fc9bb627b4bbe61943fa2e9cdcd6631ab7ae2f Mon Sep 17 00:00:00 2001 From: Zihan Dai Date: Wed, 11 Nov 2026 13:41:21 +1100 Subject: [PATCH] Fix DomainNamingService rejecting IPv6 addresses The http://, https:// and redis:// naming services cut the host at the first colon, so an address such as `[::1]:8765` failed with "No port after colon" and a channel initialized with a load balancer could not select any server. Keep the bracketed address as the host and turn it into an endpoint directly, since there is nothing to resolve. --- src/brpc/policy/domain_naming_service.cpp | 42 ++++++++++++++++++----- test/brpc_naming_service_unittest.cpp | 28 +++++++++++++++ 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/src/brpc/policy/domain_naming_service.cpp b/src/brpc/policy/domain_naming_service.cpp index 8c2632cd6e..d1705575b5 100644 --- a/src/brpc/policy/domain_naming_service.cpp +++ b/src/brpc/policy/domain_naming_service.cpp @@ -19,6 +19,7 @@ #include "butil/build_config.h" // OS_MACOSX #include // gethostbyname_r #include // strtol +#include // strchr, memcpy #include // std::string #include "bthread/bthread.h" #include "brpc/log.h" @@ -44,15 +45,29 @@ int DomainNamingService::GetServers(const char* dns_name, // Should be enough to hold host name char buf[256]; size_t i = 0; - for (; i < sizeof(buf) - 1 && dns_name[i] != '\0' - && dns_name[i] != ':' && dns_name[i] != '/'; ++i) { - buf[i] = dns_name[i]; - } - if (i == sizeof(buf) - 1) { - LOG(ERROR) << "dns_name=`" << dns_name << "' is too long"; - return -1; + if (dns_name[0] == '[') { + // IPv6 address such as `[::1]:8080'. Keep the brackets, which + // butil::str2endpoint() expects. + const char* close = strchr(dns_name, ']'); + if (close == nullptr || + static_cast(close - dns_name) + 1 >= sizeof(buf) || + (close[1] != '\0' && close[1] != ':' && close[1] != '/')) { + LOG(ERROR) << "Invalid ipv6 address in `" << dns_name << '\''; + return -1; + } + i = close - dns_name + 1; + memcpy(buf, dns_name, i); + } else { + for (; i < sizeof(buf) - 1 && dns_name[i] != '\0' + && dns_name[i] != ':' && dns_name[i] != '/'; ++i) { + buf[i] = dns_name[i]; + } + if (i == sizeof(buf) - 1) { + LOG(ERROR) << "dns_name=`" << dns_name << "' is too long"; + return -1; + } } - + buf[i] = '\0'; int port = _default_port; if (dns_name[i] == ':') { @@ -79,6 +94,17 @@ int DomainNamingService::GetServers(const char* dns_name, return -1; } + if (buf[0] == '[') { + // An address, not a name: nothing to resolve. + butil::EndPoint point; + if (butil::str2endpoint(buf, port, &point) != 0) { + LOG(ERROR) << "Invalid ipv6 address in `" << dns_name << '\''; + return -1; + } + servers->push_back(ServerNode(point, std::string())); + return 0; + } + if (FLAGS_dns_support_ipv6) { struct addrinfo hints; struct addrinfo* addrResult; diff --git a/test/brpc_naming_service_unittest.cpp b/test/brpc_naming_service_unittest.cpp index a01e033f61..97d771017d 100644 --- a/test/brpc_naming_service_unittest.cpp +++ b/test/brpc_naming_service_unittest.cpp @@ -164,6 +164,34 @@ TEST(NamingServiceTest, sanity) { } } +TEST(NamingServiceTest, domain_with_ipv6_address) { + std::vector servers; + brpc::policy::DomainNamingService dns; + + ASSERT_EQ(0, dns.GetServers("[::1]:1234", &servers)); + ASSERT_EQ(1u, servers.size()); + ASSERT_STREQ("[::1]:1234", butil::endpoint2str(servers[0].addr).c_str()); + + ASSERT_EQ(0, dns.GetServers("[fdbd:dc03:ff:501:b783:830f:f0f5:aa6a]:2280", &servers)); + ASSERT_EQ(1u, servers.size()); + ASSERT_STREQ("[fdbd:dc03:ff:501:b783:830f:f0f5:aa6a]:2280", + butil::endpoint2str(servers[0].addr).c_str()); + + ASSERT_EQ(0, dns.GetServers("[::1]:1234/useless1/useless2", &servers)); + ASSERT_EQ(1u, servers.size()); + ASSERT_STREQ("[::1]:1234", butil::endpoint2str(servers[0].addr).c_str()); + + ASSERT_EQ(0, dns.GetServers("[::1]", &servers)); + ASSERT_EQ(1u, servers.size()); + ASSERT_STREQ("[::1]:80", butil::endpoint2str(servers[0].addr).c_str()); + + ASSERT_EQ(-1, dns.GetServers("[::1", &servers)); + ASSERT_EQ(-1, dns.GetServers("[::1]:", &servers)); + ASSERT_EQ(-1, dns.GetServers("[::1]1234", &servers)); + ASSERT_EQ(-1, dns.GetServers("[::1]:99999", &servers)); + ASSERT_EQ(-1, dns.GetServers("[not-an-ip]:1234", &servers)); +} + TEST(NamingServiceTest, invalid_port) { std::vector servers;