mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-19 16:20:50 +00:00
Merge pull request #69130 from m4xxx1m/hostname-validation-config
Add validation of IP addresses and domains in settings
This commit is contained in:
commit
dc0b491f11
@ -18,4 +18,4 @@ target_compile_options (_poco_util
|
|||||||
-Wno-zero-as-null-pointer-constant
|
-Wno-zero-as-null-pointer-constant
|
||||||
)
|
)
|
||||||
target_include_directories (_poco_util SYSTEM PUBLIC "include")
|
target_include_directories (_poco_util SYSTEM PUBLIC "include")
|
||||||
target_link_libraries (_poco_util PUBLIC Poco::JSON Poco::XML)
|
target_link_libraries (_poco_util PUBLIC Poco::JSON Poco::XML Poco::Net)
|
||||||
|
@ -241,6 +241,20 @@ namespace Util
|
|||||||
/// If the value contains references to other properties (${<property>}), these
|
/// If the value contains references to other properties (${<property>}), these
|
||||||
/// are expanded.
|
/// are expanded.
|
||||||
|
|
||||||
|
std::string getHost(const std::string & key) const;
|
||||||
|
/// Returns the string value of the host property with the given name.
|
||||||
|
/// Throws a NotFoundException if the key does not exist.
|
||||||
|
/// Throws a SyntaxException if the property is not a valid host (IP address or domain).
|
||||||
|
/// If the value contains references to other properties (${<property>}), these
|
||||||
|
/// are expanded.
|
||||||
|
|
||||||
|
std::string getHost(const std::string & key, const std::string & defaultValue) const;
|
||||||
|
/// If a property with the given key exists, returns the host property's string value,
|
||||||
|
/// otherwise returns the given default value.
|
||||||
|
/// Throws a SyntaxException if the property is not a valid host (IP address or domain).
|
||||||
|
/// If the value contains references to other properties (${<property>}), these
|
||||||
|
/// are expanded.
|
||||||
|
|
||||||
virtual void setString(const std::string & key, const std::string & value);
|
virtual void setString(const std::string & key, const std::string & value);
|
||||||
/// Sets the property with the given key to the given value.
|
/// Sets the property with the given key to the given value.
|
||||||
/// An already existing value for the key is overwritten.
|
/// An already existing value for the key is overwritten.
|
||||||
@ -339,12 +353,35 @@ namespace Util
|
|||||||
static bool parseBool(const std::string & value);
|
static bool parseBool(const std::string & value);
|
||||||
void setRawWithEvent(const std::string & key, std::string value);
|
void setRawWithEvent(const std::string & key, std::string value);
|
||||||
|
|
||||||
|
static void checkHostValidity(const std::string & value);
|
||||||
|
/// Throws a SyntaxException if the value is not a valid host (IP address or domain).
|
||||||
|
|
||||||
virtual ~AbstractConfiguration();
|
virtual ~AbstractConfiguration();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string internalExpand(const std::string & value) const;
|
std::string internalExpand(const std::string & value) const;
|
||||||
std::string uncheckedExpand(const std::string & value) const;
|
std::string uncheckedExpand(const std::string & value) const;
|
||||||
|
|
||||||
|
static bool isValidIPv4Address(const std::string & value);
|
||||||
|
/// IPv4 address considered valid if it is "0.0.0.0" or one of those,
|
||||||
|
/// defined by inet_aton() or inet_addr()
|
||||||
|
|
||||||
|
static bool isValidIPv6Address(const std::string & value);
|
||||||
|
/// IPv6 address considered valid if it is "::" or one of those,
|
||||||
|
/// defined by inet_pton() with AF_INET6 flag
|
||||||
|
/// (in this case it may have scope id and may be surrounded by '[', ']')
|
||||||
|
|
||||||
|
static bool isValidDomainName(const std::string & value);
|
||||||
|
/// <domain> ::= <subdomain> [ "." ]
|
||||||
|
/// <subdomain> ::= <label> | <subdomain> "." <label>
|
||||||
|
/// <label> ::= <letter> [ [ <ldh-str> ] <let-dig> ]
|
||||||
|
/// <ldh-str> ::= <let-dig-hyp> | <let-dig-hyp> <ldh-str>
|
||||||
|
/// <let-dig-hyp> ::= <let-dig> | "-"
|
||||||
|
/// <let-dig> ::= <letter> | <digit>
|
||||||
|
/// <letter> ::= any one of the 52 alphabetic characters A through Z in
|
||||||
|
/// upper case and a through z in lower case
|
||||||
|
/// <digit> ::= any one of the ten digits 0 through 9
|
||||||
|
|
||||||
AbstractConfiguration(const AbstractConfiguration &);
|
AbstractConfiguration(const AbstractConfiguration &);
|
||||||
AbstractConfiguration & operator=(const AbstractConfiguration &);
|
AbstractConfiguration & operator=(const AbstractConfiguration &);
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "Poco/NumberParser.h"
|
#include "Poco/NumberParser.h"
|
||||||
#include "Poco/NumberFormatter.h"
|
#include "Poco/NumberFormatter.h"
|
||||||
#include "Poco/String.h"
|
#include "Poco/String.h"
|
||||||
|
#include "Poco/Net/IPAddressImpl.h"
|
||||||
|
|
||||||
|
|
||||||
using Poco::Mutex;
|
using Poco::Mutex;
|
||||||
@ -263,6 +264,41 @@ bool AbstractConfiguration::getBool(const std::string& key, bool defaultValue) c
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string AbstractConfiguration::getHost(const std::string& key) const
|
||||||
|
{
|
||||||
|
Mutex::ScopedLock lock(_mutex);
|
||||||
|
|
||||||
|
std::string value;
|
||||||
|
if (getRaw(key, value))
|
||||||
|
{
|
||||||
|
std::string expandedValue = internalExpand(value);
|
||||||
|
checkHostValidity(expandedValue);
|
||||||
|
return expandedValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw NotFoundException(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string AbstractConfiguration::getHost(const std::string& key, const std::string& defaultValue) const
|
||||||
|
{
|
||||||
|
Mutex::ScopedLock lock(_mutex);
|
||||||
|
|
||||||
|
std::string value;
|
||||||
|
if (getRaw(key, value))
|
||||||
|
{
|
||||||
|
std::string expandedValue = internalExpand(value);
|
||||||
|
checkHostValidity(expandedValue);
|
||||||
|
return expandedValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
checkHostValidity(defaultValue);
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void AbstractConfiguration::setString(const std::string& key, const std::string& value)
|
void AbstractConfiguration::setString(const std::string& key, const std::string& value)
|
||||||
{
|
{
|
||||||
setRawWithEvent(key, value);
|
setRawWithEvent(key, value);
|
||||||
@ -529,4 +565,68 @@ void AbstractConfiguration::setRawWithEvent(const std::string& key, std::string
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void AbstractConfiguration::checkHostValidity(const std::string& value)
|
||||||
|
{
|
||||||
|
if (!isValidIPv4Address(value) && !isValidIPv6Address(value) && !isValidDomainName(value))
|
||||||
|
{
|
||||||
|
throw SyntaxException("Property is not a valid host name", value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool AbstractConfiguration::isValidIPv4Address(const std::string& value)
|
||||||
|
{
|
||||||
|
using Poco::Net::Impl::IPv4AddressImpl;
|
||||||
|
IPv4AddressImpl empty4 = IPv4AddressImpl();
|
||||||
|
|
||||||
|
IPv4AddressImpl ipAddress = IPv4AddressImpl::parse(value);
|
||||||
|
return ipAddress != empty4 || value == "0.0.0.0";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool AbstractConfiguration::isValidIPv6Address(const std::string& value)
|
||||||
|
{
|
||||||
|
#if defined(POCO_HAVE_IPv6)
|
||||||
|
using Poco::Net::Impl::IPv6AddressImpl;
|
||||||
|
IPv6AddressImpl empty6 = IPv6AddressImpl();
|
||||||
|
|
||||||
|
IPv6AddressImpl ipAddress = IPv6AddressImpl::parse(value);
|
||||||
|
return ipAddress != empty6 || value == "::";
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool AbstractConfiguration::isValidDomainName(const std::string& value)
|
||||||
|
{
|
||||||
|
if (value.empty() || value == "." || value.length() > 253)
|
||||||
|
return false;
|
||||||
|
int labelLength = 0;
|
||||||
|
char oldChar = 0;
|
||||||
|
|
||||||
|
for (char ch : value)
|
||||||
|
{
|
||||||
|
if (ch == '.')
|
||||||
|
{
|
||||||
|
if (labelLength == 0 || labelLength > 63 || oldChar == '-')
|
||||||
|
return false;
|
||||||
|
labelLength = 0;
|
||||||
|
}
|
||||||
|
else if (isalnum(ch) || ch == '-')
|
||||||
|
{
|
||||||
|
if (labelLength == 0 && (ch == '-' || isdigit(ch)))
|
||||||
|
return false;
|
||||||
|
++labelLength;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
oldChar = ch;
|
||||||
|
}
|
||||||
|
return oldChar == '.' || (labelLength > 0 && labelLength <= 63 && oldChar != '-');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} } // namespace Poco::Util
|
} } // namespace Poco::Util
|
||||||
|
69
src/Common/tests/gtest_config_host_validation.cpp
Normal file
69
src/Common/tests/gtest_config_host_validation.cpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#include <Poco/AutoPtr.h>
|
||||||
|
#include <Poco/DOM/DOMParser.h>
|
||||||
|
#include <Poco/Util/XMLConfiguration.h>
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
TEST(Common, ConfigHostValidation)
|
||||||
|
{
|
||||||
|
std::string xml(R"CONFIG(<clickhouse>
|
||||||
|
<IPv4_1>0.0.0.0</IPv4_1>
|
||||||
|
<IPv4_2>192.168.0.1</IPv4_2>
|
||||||
|
<IPv4_3>127.0.0.1</IPv4_3>
|
||||||
|
<IPv4_4>255.255.255.255</IPv4_4>
|
||||||
|
<IPv6_1>2001:0db8:85a3:0000:0000:8a2e:0370:7334</IPv6_1>
|
||||||
|
<IPv6_2>2001:DB8::8a2e:370:7334</IPv6_2>
|
||||||
|
<IPv6_3>::1</IPv6_3>
|
||||||
|
<IPv6_4>::</IPv6_4>
|
||||||
|
<Domain_1>www.example.com.</Domain_1>
|
||||||
|
<Domain_2>a.co</Domain_2>
|
||||||
|
<Domain_3>localhost</Domain_3>
|
||||||
|
<Domain_4>xn--fiqs8s.xn--fiqz9s</Domain_4>
|
||||||
|
<IPv4_Invalid_1>192.168.1.256</IPv4_Invalid_1>
|
||||||
|
<IPv4_Invalid_2>192.168.1.1.1</IPv4_Invalid_2>
|
||||||
|
<IPv4_Invalid_3>192.168.1.99999999999999999999</IPv4_Invalid_3>
|
||||||
|
<IPv4_Invalid_4>192.168.1.a</IPv4_Invalid_4>
|
||||||
|
<IPv6_Invalid_1>2001:0db8:85a3:::8a2e:0370:7334</IPv6_Invalid_1>
|
||||||
|
<IPv6_Invalid_2>1200::AB00:1234::2552:7777:1313</IPv6_Invalid_2>
|
||||||
|
<IPv6_Invalid_3>1200::AB00:1234:Q000:2552:7777:1313</IPv6_Invalid_3>
|
||||||
|
<IPv6_Invalid_4>1200:AB00:1234:2552:7777:1313:FFFF</IPv6_Invalid_4>
|
||||||
|
<Domain_Invalid_1>example.com..</Domain_Invalid_1>
|
||||||
|
<Domain_Invalid_2>5example.com</Domain_Invalid_2>
|
||||||
|
<Domain_Invalid_3>example.com-</Domain_Invalid_3>
|
||||||
|
<Domain_Invalid_4>exa_mple.com</Domain_Invalid_4>
|
||||||
|
</clickhouse>)CONFIG");
|
||||||
|
|
||||||
|
Poco::XML::DOMParser dom_parser;
|
||||||
|
Poco::AutoPtr<Poco::XML::Document> document = dom_parser.parseString(xml);
|
||||||
|
Poco::AutoPtr<Poco::Util::XMLConfiguration> config = new Poco::Util::XMLConfiguration(document);
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(config->getHost("IPv4_1"));
|
||||||
|
EXPECT_NO_THROW(config->getHost("IPv4_2"));
|
||||||
|
EXPECT_NO_THROW(config->getHost("IPv4_3"));
|
||||||
|
EXPECT_NO_THROW(config->getHost("IPv4_4"));
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(config->getHost("IPv6_1"));
|
||||||
|
EXPECT_NO_THROW(config->getHost("IPv6_2"));
|
||||||
|
EXPECT_NO_THROW(config->getHost("IPv6_3"));
|
||||||
|
EXPECT_NO_THROW(config->getHost("IPv6_4"));
|
||||||
|
|
||||||
|
EXPECT_NO_THROW(config->getHost("Domain_1"));
|
||||||
|
EXPECT_NO_THROW(config->getHost("Domain_2"));
|
||||||
|
EXPECT_NO_THROW(config->getHost("Domain_3"));
|
||||||
|
EXPECT_NO_THROW(config->getHost("Domain_4"));
|
||||||
|
|
||||||
|
EXPECT_THROW(config->getHost("IPv4_Invalid_1"), Poco::SyntaxException);
|
||||||
|
EXPECT_THROW(config->getHost("IPv4_Invalid_2"), Poco::SyntaxException);
|
||||||
|
EXPECT_THROW(config->getHost("IPv4_Invalid_3"), Poco::SyntaxException);
|
||||||
|
EXPECT_THROW(config->getHost("IPv4_Invalid_4"), Poco::SyntaxException);
|
||||||
|
|
||||||
|
EXPECT_THROW(config->getHost("IPv6_Invalid_1"), Poco::SyntaxException);
|
||||||
|
EXPECT_THROW(config->getHost("IPv6_Invalid_2"), Poco::SyntaxException);
|
||||||
|
EXPECT_THROW(config->getHost("IPv6_Invalid_3"), Poco::SyntaxException);
|
||||||
|
EXPECT_THROW(config->getHost("IPv6_Invalid_4"), Poco::SyntaxException);
|
||||||
|
|
||||||
|
EXPECT_THROW(config->getHost("Domain_Invalid_1"), Poco::SyntaxException);
|
||||||
|
EXPECT_THROW(config->getHost("Domain_Invalid_2"), Poco::SyntaxException);
|
||||||
|
EXPECT_THROW(config->getHost("Domain_Invalid_3"), Poco::SyntaxException);
|
||||||
|
EXPECT_THROW(config->getHost("Domain_Invalid_4"), Poco::SyntaxException);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user