mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-14 03:25:15 +00:00
multiple client connection attempts if hostname resolves to multiple addresses
This commit is contained in:
parent
5c725fe77c
commit
fc26505111
@ -93,37 +93,59 @@ void Connection::connect(const ConnectionTimeouts & timeouts)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (connected)
|
||||
disconnect();
|
||||
|
||||
LOG_TRACE(log_wrapper.get(), "Connecting. Database: {}. User: {}{}{}",
|
||||
default_database.empty() ? "(not specified)" : default_database,
|
||||
user,
|
||||
static_cast<bool>(secure) ? ". Secure" : "",
|
||||
static_cast<bool>(compression) ? "" : ". Uncompressed");
|
||||
|
||||
if (static_cast<bool>(secure))
|
||||
{
|
||||
#if USE_SSL
|
||||
socket = std::make_unique<Poco::Net::SecureStreamSocket>();
|
||||
|
||||
/// we resolve the ip when we open SecureStreamSocket, so to make Server Name Indication (SNI)
|
||||
/// work we need to pass host name separately. It will be send into TLS Hello packet to let
|
||||
/// the server know which host we want to talk with (single IP can process requests for multiple hosts using SNI).
|
||||
static_cast<Poco::Net::SecureStreamSocket*>(socket.get())->setPeerHostName(host);
|
||||
#else
|
||||
throw Exception{"tcp_secure protocol is disabled because poco library was built without NetSSL support.", ErrorCodes::SUPPORT_IS_DISABLED};
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
socket = std::make_unique<Poco::Net::StreamSocket>();
|
||||
}
|
||||
|
||||
current_resolved_address = DNSResolver::instance().resolveAddress(host, port);
|
||||
|
||||
auto addresses = DNSResolver::instance().resolveAddressList(host, port);
|
||||
const auto & connection_timeout = static_cast<bool>(secure) ? timeouts.secure_connection_timeout : timeouts.connection_timeout;
|
||||
socket->connect(*current_resolved_address, connection_timeout);
|
||||
|
||||
for (auto it = addresses.begin(); it != addresses.end();)
|
||||
{
|
||||
if (connected)
|
||||
disconnect();
|
||||
|
||||
current_resolved_address = *it;
|
||||
|
||||
if (static_cast<bool>(secure))
|
||||
{
|
||||
#if USE_SSL
|
||||
socket = std::make_unique<Poco::Net::SecureStreamSocket>();
|
||||
|
||||
/// we resolve the ip when we open SecureStreamSocket, so to make Server Name Indication (SNI)
|
||||
/// work we need to pass host name separately. It will be send into TLS Hello packet to let
|
||||
/// the server know which host we want to talk with (single IP can process requests for multiple hosts using SNI).
|
||||
static_cast<Poco::Net::SecureStreamSocket*>(socket.get())->setPeerHostName(host);
|
||||
#else
|
||||
throw Exception{"tcp_secure protocol is disabled because poco library was built without NetSSL support.", ErrorCodes::SUPPORT_IS_DISABLED};
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
socket = std::make_unique<Poco::Net::StreamSocket>();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
socket->connect(*current_resolved_address, connection_timeout);
|
||||
}
|
||||
catch (Poco::Net::NetException &)
|
||||
{
|
||||
if (++it == addresses.end())
|
||||
throw;
|
||||
continue;
|
||||
}
|
||||
catch (Poco::TimeoutException &)
|
||||
{
|
||||
if (++it == addresses.end())
|
||||
throw;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
socket->setReceiveTimeout(timeouts.receive_timeout);
|
||||
socket->setSendTimeout(timeouts.send_timeout);
|
||||
socket->setNoDelay(true);
|
||||
|
@ -83,25 +83,8 @@ static void splitHostAndPort(const std::string & host_and_port, std::string & ou
|
||||
throw Exception("Port must be numeric", ErrorCodes::BAD_ARGUMENTS);
|
||||
}
|
||||
|
||||
static DNSResolver::IPAddresses resolveIPAddressImpl(const std::string & host)
|
||||
static DNSResolver::IPAddresses hostByName(const std::string & host)
|
||||
{
|
||||
Poco::Net::IPAddress ip;
|
||||
|
||||
/// NOTE:
|
||||
/// - Poco::Net::DNS::resolveOne(host) doesn't work for IP addresses like 127.0.0.2
|
||||
/// - Poco::Net::IPAddress::tryParse() expect hex string for IPv6 (without brackets)
|
||||
if (host.starts_with('['))
|
||||
{
|
||||
assert(host.ends_with(']'));
|
||||
if (Poco::Net::IPAddress::tryParse(host.substr(1, host.size() - 2), ip))
|
||||
return DNSResolver::IPAddresses(1, ip);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Poco::Net::IPAddress::tryParse(host, ip))
|
||||
return DNSResolver::IPAddresses(1, ip);
|
||||
}
|
||||
|
||||
/// Family: AF_UNSPEC
|
||||
/// AI_ALL is required for checking if client is allowed to connect from an address
|
||||
auto flags = Poco::Net::DNS::DNS_HINT_AI_V4MAPPED | Poco::Net::DNS::DNS_HINT_AI_ALL;
|
||||
@ -131,6 +114,30 @@ static DNSResolver::IPAddresses resolveIPAddressImpl(const std::string & host)
|
||||
return addresses;
|
||||
}
|
||||
|
||||
static DNSResolver::IPAddresses resolveIPAddressImpl(const std::string & host)
|
||||
{
|
||||
Poco::Net::IPAddress ip;
|
||||
|
||||
/// NOTE:
|
||||
/// - Poco::Net::DNS::resolveOne(host) doesn't work for IP addresses like 127.0.0.2
|
||||
/// - Poco::Net::IPAddress::tryParse() expect hex string for IPv6 (without brackets)
|
||||
if (host.starts_with('['))
|
||||
{
|
||||
assert(host.ends_with(']'));
|
||||
if (Poco::Net::IPAddress::tryParse(host.substr(1, host.size() - 2), ip))
|
||||
return DNSResolver::IPAddresses(1, ip);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Poco::Net::IPAddress::tryParse(host, ip))
|
||||
return DNSResolver::IPAddresses(1, ip);
|
||||
}
|
||||
|
||||
DNSResolver::IPAddresses addresses = hostByName(host);
|
||||
|
||||
return addresses;
|
||||
}
|
||||
|
||||
static String reverseResolveImpl(const Poco::Net::IPAddress & address)
|
||||
{
|
||||
Poco::Net::SocketAddress sock_addr(address, 0);
|
||||
@ -208,6 +215,25 @@ Poco::Net::SocketAddress DNSResolver::resolveAddress(const std::string & host, U
|
||||
return Poco::Net::SocketAddress(impl->cache_host(host).front(), port);
|
||||
}
|
||||
|
||||
std::vector<Poco::Net::SocketAddress> DNSResolver::resolveAddressList(const std::string & host, UInt16 port)
|
||||
{
|
||||
if (Poco::Net::IPAddress ip; Poco::Net::IPAddress::tryParse(host, ip))
|
||||
return std::vector<Poco::Net::SocketAddress>{{ip, port}};
|
||||
|
||||
std::vector<Poco::Net::SocketAddress> addresses;
|
||||
|
||||
if (!impl->disable_cache)
|
||||
addToNewHosts(host);
|
||||
|
||||
std::vector<Poco::Net::IPAddress> ips = impl->disable_cache ? hostByName(host) : impl->cache_host(host);
|
||||
auto ips_end = std::unique(ips.begin(), ips.end());
|
||||
|
||||
for (auto ip = ips.begin(); ip != ips_end; ++ip)
|
||||
addresses.emplace_back(*ip, port);
|
||||
|
||||
return addresses;
|
||||
}
|
||||
|
||||
String DNSResolver::reverseResolve(const Poco::Net::IPAddress & address)
|
||||
{
|
||||
if (impl->disable_cache)
|
||||
|
@ -34,6 +34,8 @@ public:
|
||||
|
||||
Poco::Net::SocketAddress resolveAddress(const std::string & host, UInt16 port);
|
||||
|
||||
std::vector<Poco::Net::SocketAddress> resolveAddressList(const std::string & host, UInt16 port);
|
||||
|
||||
/// Accepts host IP and resolves its host name
|
||||
String reverseResolve(const Poco::Net::IPAddress & address);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user