2018-04-19 13:56:14 +00:00
|
|
|
#include "DNSResolver.h"
|
2017-08-07 17:01:04 +00:00
|
|
|
#include <Common/SimpleCache.h>
|
2017-08-31 12:55:19 +00:00
|
|
|
#include <Common/Exception.h>
|
2017-08-07 17:01:04 +00:00
|
|
|
#include <Core/Types.h>
|
2017-08-31 12:55:19 +00:00
|
|
|
#include <Poco/Net/DNS.h>
|
2017-08-28 18:39:57 +00:00
|
|
|
#include <Poco/Net/NetException.h>
|
2017-08-31 12:55:19 +00:00
|
|
|
#include <Poco/NumberParser.h>
|
|
|
|
#include <arpa/inet.h>
|
2018-03-29 20:21:01 +00:00
|
|
|
#include <atomic>
|
2018-06-15 13:45:19 +00:00
|
|
|
#include <optional>
|
2017-08-07 17:01:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
2017-09-01 18:36:03 +00:00
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int BAD_ARGUMENTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-08-31 12:55:19 +00:00
|
|
|
/// Slightly altered implementation from https://github.com/pocoproject/poco/blob/poco-1.6.1/Net/src/SocketAddress.cpp#L86
|
|
|
|
static void splitHostAndPort(const std::string & host_and_port, std::string & out_host, UInt16 & out_port)
|
|
|
|
{
|
|
|
|
String port_str;
|
|
|
|
out_host.clear();
|
|
|
|
|
|
|
|
auto it = host_and_port.begin();
|
|
|
|
auto end = host_and_port.end();
|
|
|
|
|
|
|
|
if (*it == '[') /// Try parse case '[<IPv6 or something else>]:<port>'
|
|
|
|
{
|
|
|
|
++it;
|
|
|
|
while (it != end && *it != ']')
|
|
|
|
out_host += *it++;
|
|
|
|
if (it == end)
|
|
|
|
throw Exception("Malformed IPv6 address", ErrorCodes::BAD_ARGUMENTS);
|
|
|
|
++it;
|
|
|
|
}
|
|
|
|
else /// Case '<IPv4 or domain name or something else>:<port>'
|
|
|
|
{
|
|
|
|
while (it != end && *it != ':')
|
|
|
|
out_host += *it++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (it != end && *it == ':')
|
|
|
|
{
|
|
|
|
++it;
|
|
|
|
while (it != end)
|
|
|
|
port_str += *it++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
throw Exception("Missing port number", ErrorCodes::BAD_ARGUMENTS);
|
|
|
|
|
|
|
|
unsigned port;
|
|
|
|
if (Poco::NumberParser::tryParseUnsigned(port_str, port) && port <= 0xFFFF)
|
|
|
|
{
|
|
|
|
out_port = static_cast<UInt16>(port);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
struct servent * se = getservbyname(port_str.c_str(), nullptr);
|
|
|
|
if (se)
|
2017-09-06 02:42:17 +00:00
|
|
|
out_port = ntohs(static_cast<UInt16>(se->s_port));
|
2017-08-31 12:55:19 +00:00
|
|
|
else
|
|
|
|
throw Exception("Service not found", ErrorCodes::BAD_ARGUMENTS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-08-07 17:01:04 +00:00
|
|
|
static Poco::Net::IPAddress resolveIPAddressImpl(const std::string & host)
|
|
|
|
{
|
2017-08-28 18:39:57 +00:00
|
|
|
/// NOTE: Poco::Net::DNS::resolveOne(host) doesn't work for IP addresses like 127.0.0.2
|
|
|
|
/// Therefore we use SocketAddress constructor with dummy port to resolve IP
|
|
|
|
return Poco::Net::SocketAddress(host, 0U).host();
|
2017-08-07 17:01:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-19 13:56:14 +00:00
|
|
|
struct DNSResolver::Impl
|
2017-08-07 17:01:04 +00:00
|
|
|
{
|
|
|
|
SimpleCache<decltype(resolveIPAddressImpl), &resolveIPAddressImpl> cache_host;
|
2018-03-29 20:21:01 +00:00
|
|
|
|
2018-06-15 13:45:19 +00:00
|
|
|
/// Cached server host name
|
|
|
|
std::mutex mutex;
|
|
|
|
std::optional<String> host_name;
|
|
|
|
|
2018-03-29 20:21:01 +00:00
|
|
|
/// If disabled, will not make cache lookups, will resolve addresses manually on each call
|
2018-04-19 13:56:14 +00:00
|
|
|
std::atomic<bool> disable_cache{false};
|
2017-08-07 17:01:04 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2018-04-19 13:56:14 +00:00
|
|
|
DNSResolver::DNSResolver() : impl(std::make_unique<DNSResolver::Impl>()) {}
|
2017-08-07 17:01:04 +00:00
|
|
|
|
2018-04-19 13:56:14 +00:00
|
|
|
Poco::Net::IPAddress DNSResolver::resolveHost(const std::string & host)
|
2017-08-07 17:01:04 +00:00
|
|
|
{
|
2018-04-19 13:56:14 +00:00
|
|
|
return !impl->disable_cache ? impl->cache_host(host) : resolveIPAddressImpl(host);
|
2017-08-07 17:01:04 +00:00
|
|
|
}
|
|
|
|
|
2018-04-19 13:56:14 +00:00
|
|
|
Poco::Net::SocketAddress DNSResolver::resolveAddress(const std::string & host_and_port)
|
2017-08-07 17:01:04 +00:00
|
|
|
{
|
2017-08-31 12:55:19 +00:00
|
|
|
String host;
|
|
|
|
UInt16 port;
|
|
|
|
splitHostAndPort(host_and_port, host, port);
|
|
|
|
|
2018-04-19 13:56:14 +00:00
|
|
|
return !impl->disable_cache ? Poco::Net::SocketAddress(impl->cache_host(host), port) : Poco::Net::SocketAddress(host_and_port);
|
2018-03-29 20:21:01 +00:00
|
|
|
}
|
|
|
|
|
2018-04-19 13:56:14 +00:00
|
|
|
Poco::Net::SocketAddress DNSResolver::resolveAddress(const std::string & host, UInt16 port)
|
2018-03-29 20:21:01 +00:00
|
|
|
{
|
2018-04-19 13:56:14 +00:00
|
|
|
return !impl->disable_cache ? Poco::Net::SocketAddress(impl->cache_host(host), port) : Poco::Net::SocketAddress(host, port);
|
2017-08-07 17:01:04 +00:00
|
|
|
}
|
|
|
|
|
2018-04-19 13:56:14 +00:00
|
|
|
void DNSResolver::dropCache()
|
2017-08-07 17:01:04 +00:00
|
|
|
{
|
2017-08-31 12:55:19 +00:00
|
|
|
impl->cache_host.drop();
|
2018-06-15 13:45:19 +00:00
|
|
|
|
|
|
|
std::unique_lock lock(impl->mutex);
|
|
|
|
impl->host_name.reset();
|
2017-08-07 17:01:04 +00:00
|
|
|
}
|
|
|
|
|
2018-04-19 13:56:14 +00:00
|
|
|
void DNSResolver::setDisableCacheFlag(bool is_disabled)
|
2018-03-29 20:21:01 +00:00
|
|
|
{
|
2018-04-19 13:56:14 +00:00
|
|
|
impl->disable_cache = is_disabled;
|
2018-03-29 20:21:01 +00:00
|
|
|
}
|
|
|
|
|
2018-06-15 13:45:19 +00:00
|
|
|
String DNSResolver::getHostName()
|
|
|
|
{
|
|
|
|
if (impl->disable_cache)
|
|
|
|
return Poco::Net::DNS::hostName();
|
|
|
|
|
|
|
|
std::unique_lock lock(impl->mutex);
|
|
|
|
|
|
|
|
if (!impl->host_name.has_value())
|
|
|
|
impl->host_name.emplace(Poco::Net::DNS::hostName());
|
|
|
|
|
|
|
|
return *impl->host_name;
|
|
|
|
}
|
|
|
|
|
2018-04-19 13:56:14 +00:00
|
|
|
DNSResolver::~DNSResolver() = default;
|
2017-08-07 17:01:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
}
|