Skip to content

Commit

Permalink
Fix error with IPv6 in host param
Browse files Browse the repository at this point in the history
  • Loading branch information
df530 committed Feb 7, 2022
1 parent 87933e4 commit c1df291
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 16 deletions.
21 changes: 5 additions & 16 deletions src/Client/ClientBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,22 +246,11 @@ class ClientBase : public Poco::Util::Application, public IHints<2, ClientBase>
String host_with_port;
in >> host_with_port;
DB::DNSResolver & resolver = DB::DNSResolver::instance();
try
{
Poco::Net::SocketAddress address = resolver.resolveAddress(host_with_port);
hostPort.host = address.host().toString();
hostPort.port = address.port();
}
catch (const Exception & e)
{
if (e.message() == "Missing port number")
{
hostPort.host = resolver.resolveHost(host_with_port).toString();
hostPort.port = std::nullopt;
return in;
}
throw;
}
std::pair<Poco::Net::IPAddress, std::optional<UInt16>>
host_and_port = resolver.resolveHostOrAddress(host_with_port);
hostPort.host = host_and_port.first.toString();
hostPort.port = host_and_port.second;

return in;
}
};
Expand Down
39 changes: 39 additions & 0 deletions src/Common/DNSResolver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,45 @@ Poco::Net::SocketAddress DNSResolver::resolveAddress(const std::string & host, U
return Poco::Net::SocketAddress(impl->cache_host(host).front(), port);
}

std::pair<Poco::Net::IPAddress, std::optional<UInt16>> DNSResolver::resolveHostOrAddress(const std::string & host_and_port)
{
Poco::Net::IPAddress ip;

size_t number_of_colons = std::count(host_and_port.begin(), host_and_port.end(), ':');
if (number_of_colons > 1)
{
/// IPv6 host
if (host_and_port.starts_with('['))
{
size_t close_bracket_pos = host_and_port.find(']');
assert(close_bracket_pos != std::string::npos);
ip = resolveHost(host_and_port.substr(0, close_bracket_pos));

if (close_bracket_pos == host_and_port.size() - 1)
return {ip, std::nullopt};
if (host_and_port[close_bracket_pos + 1] != ':')
throw Exception("Missing delimiter between host and port", ErrorCodes::BAD_ARGUMENTS);

unsigned int port;
if (!Poco::NumberParser::tryParseUnsigned(host_and_port.substr(close_bracket_pos + 2), port))
throw Exception("Port must be numeric", ErrorCodes::BAD_ARGUMENTS);
if (port > 0xFFFF)
throw Exception("Port must be less 0xFFFF", ErrorCodes::BAD_ARGUMENTS);
return {ip, port};
}
return {resolveHost(host_and_port), std::nullopt};
}
else if (number_of_colons == 1)
{
/// IPv4 host with port
Poco::Net::SocketAddress socket = resolveAddress(host_and_port);
return {socket.host(), socket.port()};
}

/// IPv4 host
return {resolveHost(host_and_port), std::nullopt};
}

String DNSResolver::reverseResolve(const Poco::Net::IPAddress & address)
{
if (impl->disable_cache)
Expand Down
4 changes: 4 additions & 0 deletions src/Common/DNSResolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ class DNSResolver : private boost::noncopyable

Poco::Net::SocketAddress resolveAddress(const std::string & host, UInt16 port);

/// Accepts host names like 'example.com'/'example.com:port' or '127.0.0.1'/'127.0.0.1:port' or '::1'/'[::1]:port'
/// and resolves its IP and port, if port is set
std::pair<Poco::Net::IPAddress, std::optional<UInt16>> resolveHostOrAddress(const std::string & host_and_port);

/// Accepts host IP and resolves its host name
String reverseResolve(const Poco::Net::IPAddress & address);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@
1
1
1
1
1
1
16 changes: 16 additions & 0 deletions tests/queries/0_stateless/02100_multiple_hosts_command_line_set.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,19 @@ error="$(${CLICKHOUSE_CLIENT} --host "${CLICKHOUSE_HOST}" --port "${not_alive_po
${CLICKHOUSE_CLIENT} --host "${CLICKHOUSE_HOST}:${not_alive_port}" "${CLICKHOUSE_HOST}" --query "SELECT 1";
${CLICKHOUSE_CLIENT} --host "${CLICKHOUSE_HOST}:${CLICKHOUSE_PORT_TCP}" --port "${not_alive_port}" --query "SELECT 1";

ipv6_host_without_brackets="2001:3984:3989::1:1000"
exception_msg="Code: 210. DB::NetException: Connection refused (${ipv6_host_without_brackets}). (NETWORK_ERROR)
"
error="$(${CLICKHOUSE_CLIENT} --host "${ipv6_host_without_brackets}" --query "SELECT 1" 2>&1 > /dev/null)"
[ "${error}" == "${exception_msg}" ]; echo "$?"

ipv6_host_with_brackets="[2001:3984:3989::1:1000]"
exception_msg="Code: 210. DB::NetException: Connection refused (${ipv6_host_with_brackets}). (NETWORK_ERROR)
"
error="$(${CLICKHOUSE_CLIENT} --host "${ipv6_host_with_brackets}" --query "SELECT 1" 2>&1 > /dev/null)"
[ "${error}" == "${exception_msg}" ]; echo "$?"

exception_msg="Code: 210. DB::NetException: Connection refused (${ipv6_host_with_brackets}:${not_alive_port}). (NETWORK_ERROR)
"
error="$(${CLICKHOUSE_CLIENT} --host "${ipv6_host_with_brackets}:${not_alive_port}" --query "SELECT 1" 2>&1 > /dev/null)"
[ "${error}" == "${exception_msg}" ]; echo "$?"

0 comments on commit c1df291

Please sign in to comment.