Skip to content

Commit

Permalink
Allow domain names (DNS) when parsing TCP locators XML elements (#5429)
Browse files Browse the repository at this point in the history
* Refs #21506. Add regression tests

Signed-off-by: Juan Lopez Fernandez <juanlopez@eprosima.com>

* Refs #21506. Attempt DNS resolution when setting locator's IP

Signed-off-by: Juan Lopez Fernandez <juanlopez@eprosima.com>

* Refs #21506. Fix failing tests and apply suggestions

Signed-off-by: Juan Lopez Fernandez <juanlopez@eprosima.com>

* Refs #21506. Apply more suggestions

Signed-off-by: Juan Lopez Fernandez <juanlopez@eprosima.com>

---------

Signed-off-by: Juan Lopez Fernandez <juanlopez@eprosima.com>
  • Loading branch information
juanlofer-eprosima authored Nov 29, 2024
1 parent 8a99a07 commit 50c5848
Show file tree
Hide file tree
Showing 5 changed files with 408 additions and 60 deletions.
116 changes: 95 additions & 21 deletions src/cpp/utils/IPLocator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ namespace eprosima {
namespace fastdds {
namespace rtps {

static const std::regex IPv4_REGEX("^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}"
"(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
static const std::regex IPv4_REGEX("^(?:(?:0*25[0-5]|0*2[0-4][0-9]|0*[01]?[0-9][0-9]?)\\.){3}"
"(?:0*25[0-5]|0*2[0-4][0-9]|0*[01]?[0-9][0-9]?)$");
static const std::regex IPv6_QUARTET_REGEX("^(?:[A-Fa-f0-9]){0,4}$");

// Factory
Expand Down Expand Up @@ -106,7 +106,31 @@ bool IPLocator::setIPv4(
// This function do not set address to 0 in case it fails
// Be careful, do not set all IP to 0 because WAN and LAN could be set beforehand

std::stringstream ss(ipv4);
std::string s(ipv4);
if (!IPLocator::isIPv4(s))
{
// Attempt DNS resolution
auto response = IPLocator::resolveNameDNS(s);

// Use the first valid IPv4 address that we can find
if (response.first.size() > 0)
{
s = response.first.begin()->data();
// Redundant check for extra security (here a custom regex is used instead of asio's verification)
if (!IPLocator::isIPv4(s))
{
EPROSIMA_LOG_WARNING(IP_LOCATOR, "DNS name [" << ipv4 << "] resolved into wrong IPv4 format: " << s);
return false;
}
}
else
{
EPROSIMA_LOG_WARNING(IP_LOCATOR, "IPv4 " << s << " error format. Expected X.X.X.X or valid DNS name");
return false;
}
}

std::stringstream ss(s);
uint32_t a;
uint32_t b;
uint32_t c;
Expand All @@ -127,7 +151,7 @@ bool IPLocator::setIPv4(
// If there are more info to read, it fails
return ss.rdbuf()->in_avail() == 0;
}
EPROSIMA_LOG_WARNING(IP_LOCATOR, "IPv4 " << ipv4 << " error format. Expected X.X.X.X");
EPROSIMA_LOG_WARNING(IP_LOCATOR, "IPv4 " << s << " error format. Expected X.X.X.X or valid DNS name");
return false;
}

Expand Down Expand Up @@ -254,14 +278,33 @@ bool IPLocator::setIPv6(
return false;
}

if (!IPv6isCorrect(ipv6))
std::string s(ipv6);
if (!IPLocator::isIPv6(s))
{
EPROSIMA_LOG_WARNING(IP_LOCATOR, "IPv6 " << ipv6 << " is not well defined");
return false;
// Attempt DNS resolution
auto response = IPLocator::resolveNameDNS(s);

// Use the first valid IPv6 address that we can find
if (response.second.size() > 0)
{
s = response.second.begin()->data();
// Redundant check for extra security (here a custom regex is used instead of asio's verification)
if (!IPLocator::isIPv6(s))
{
EPROSIMA_LOG_WARNING(IP_LOCATOR, "DNS name [" << ipv6 << "] resolved into wrong IPv6 format: " << s);
return false;
}
}
else
{
EPROSIMA_LOG_WARNING(IP_LOCATOR,
"IPv6 " << s << " error format. Expected well defined address or valid DNS name");
return false;
}
}

LOCATOR_ADDRESS_INVALID(locator.address);
uint16_t count = (uint16_t) std::count_if( ipv6.begin(), ipv6.end(), []( char c )
uint16_t count = (uint16_t) std::count_if( s.begin(), s.end(), []( char c )
{
return c == ':';
}); // C type cast to avoid Windows warnings
Expand All @@ -274,10 +317,10 @@ bool IPLocator::setIPv6(
size_t aux_prev; // This must be size_t as string::npos could change value depending on size_t size

// Check whether is a zero block and where
if (ipv6.front() == ':')
if (s.front() == ':')
{
// First element equal : -> starts with zeros
if (ipv6.back() == ':')
if (s.back() == ':')
{
// Empty string (correct ipv6 format)
initial_zeros = 16;
Expand All @@ -291,7 +334,7 @@ bool IPLocator::setIPv6(
initial_zeros = (7 - (count - 2)) * 2;
}
}
else if (ipv6.back() == ':')
else if (s.back() == ':')
{
// Last element equal : -> ends with zeros
// It does not start with :: (previous if)
Expand All @@ -300,8 +343,8 @@ bool IPLocator::setIPv6(
else
{
// It does not starts or ends with zeros, but it could have :: in the middle or not have it
aux_prev = ipv6.size(); // Aux could be 1 so this number must be unreacheable
aux = ipv6.find(':'); // Index of first ':'
aux_prev = s.size(); // Aux could be 1 so this number must be unreacheable
aux = s.find(':'); // Index of first ':'

// Look for "::" will loop string twice
// Therefore, we use this loop that will go over less or equal once
Expand All @@ -317,13 +360,13 @@ bool IPLocator::setIPv6(
// Not "::" found, keep searching in next ':'
position_zeros += 2; // It stores the point where the 0 block is
aux_prev = aux;
aux = ipv6.find(':', aux + 1);
aux = s.find(':', aux + 1);
}
}

char punct;
std::stringstream ss;
ss << std::hex << ipv6;
ss << std::hex << s;
uint16_t i;
uint32_t input_aux; // It cannot be uint16_t or we could not find whether the input number is bigger than allowed

Expand All @@ -343,7 +386,7 @@ bool IPLocator::setIPv6(
ss >> punct >> input_aux;
if (input_aux >= 65536)
{
EPROSIMA_LOG_WARNING(IP_LOCATOR, "IPv6 " << ipv6 << " has values higher than expected (65536)");
EPROSIMA_LOG_WARNING(IP_LOCATOR, "IPv6 " << s << " has values higher than expected (65536)");
return false;
}
locator.address[i++] = octet(input_aux >> 8);
Expand All @@ -364,7 +407,7 @@ bool IPLocator::setIPv6(
ss >> input_aux >> punct;
if (input_aux >= 65536)
{
EPROSIMA_LOG_WARNING(IP_LOCATOR, "IPv6 " << ipv6 << " has values higher than expected (65536)");
EPROSIMA_LOG_WARNING(IP_LOCATOR, "IPv6 " << s << " has values higher than expected (65536)");
return false;
}
locator.address[i++] = octet(input_aux >> 8);
Expand All @@ -386,7 +429,7 @@ bool IPLocator::setIPv6(
ss >> input_aux >> punct;
if (input_aux >= 65536)
{
EPROSIMA_LOG_WARNING(IP_LOCATOR, "IPv6 " << ipv6 << " has values higher than expected (65536)");
EPROSIMA_LOG_WARNING(IP_LOCATOR, "IPv6 " << s << " has values higher than expected (65536)");
return false;
}
locator.address[i++] = octet(input_aux >> 8);
Expand All @@ -403,7 +446,7 @@ bool IPLocator::setIPv6(
ss >> punct >> input_aux;
if (input_aux >= 65536)
{
EPROSIMA_LOG_WARNING(IP_LOCATOR, "IPv6 " << ipv6 << " has values higher than expected (65536)");
EPROSIMA_LOG_WARNING(IP_LOCATOR, "IPv6 " << s << " has values higher than expected (65536)");
return false;
}
locator.address[i++] = octet(input_aux >> 8);
Expand All @@ -421,7 +464,7 @@ bool IPLocator::setIPv6(
ss >> punct >> input_aux;
if (input_aux >= 65536)
{
EPROSIMA_LOG_WARNING(IP_LOCATOR, "IPv6 " << ipv6 << " has values higher than expected (65536)");
EPROSIMA_LOG_WARNING(IP_LOCATOR, "IPv6 " << s << " has values higher than expected (65536)");
return false;
}
locator.address[i++] = octet(input_aux >> 8);
Expand Down Expand Up @@ -671,7 +714,37 @@ bool IPLocator::setWan(
Locator_t& locator,
const std::string& wan)
{
std::stringstream ss(wan);
if (locator.kind != LOCATOR_KIND_TCPv4)
{
EPROSIMA_LOG_WARNING(IP_LOCATOR, "Trying to set WAN address in a non TCP-IPv4 Locator");
return false;
}

std::string s(wan);
if (!IPLocator::isIPv4(s))
{
// Attempt DNS resolution
auto response = IPLocator::resolveNameDNS(s);

// Use the first valid IPv4 address that we can find
if (response.first.size() > 0)
{
s = response.first.begin()->data();
// Redundant check for extra security (here a custom regex is used instead of asio's verification)
if (!IPLocator::isIPv4(s))
{
EPROSIMA_LOG_WARNING(IP_LOCATOR, "DNS name [" << wan << "] resolved into wrong IPv4 format: " << s);
return false;
}
}
else
{
EPROSIMA_LOG_WARNING(IP_LOCATOR, "IPv4 " << s << " error format. Expected X.X.X.X or valid DNS name");
return false;
}
}

std::stringstream ss(s);
int a, b, c, d; //to store the 4 ints
char ch; //to temporarily store the '.'

Expand All @@ -683,6 +756,7 @@ bool IPLocator::setWan(
locator.address[11] = (octet)d;
return true;
}
EPROSIMA_LOG_WARNING(IP_LOCATOR, "IPv4 " << s << " error format. Expected X.X.X.X or valid DNS name");
return false;
}

Expand Down
56 changes: 21 additions & 35 deletions src/cpp/xmlparser/XMLElementParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3069,24 +3069,11 @@ XMLP_ret XMLParser::getXMLLocatorUDPv4(
{
return XMLP_ret::XML_ERROR;
}
// Check whether the address is IPv4
if (!IPLocator::isIPv4(s))
if (!IPLocator::setIPv4(locator, s))
{
auto response = rtps::IPLocator::resolveNameDNS(s);

// Add the first valid IPv4 address that we can find
if (response.first.size() > 0)
{
s = response.first.begin()->data();
}
else
{
EPROSIMA_LOG_ERROR(XMLPARSER,
"DNS server did not return any IPv4 address for: '" << s << "'. Name: " << name);
return XMLP_ret::XML_ERROR;
}
EPROSIMA_LOG_ERROR(XMLPARSER, "Failed to parse UDPv4 locator's " << ADDRESS << " tag");
return XMLP_ret::XML_ERROR;
}
IPLocator::setIPv4(locator, s);
}
else
{
Expand Down Expand Up @@ -3142,24 +3129,11 @@ XMLP_ret XMLParser::getXMLLocatorUDPv6(
{
return XMLP_ret::XML_ERROR;
}
// Check whether the address is IPv6
if (!IPLocator::isIPv6(s))
if (!IPLocator::setIPv6(locator, s))
{
auto response = rtps::IPLocator::resolveNameDNS(s);

// Add the first valid IPv6 address that we can find
if (response.second.size() > 0)
{
s = response.second.begin()->data();
}
else
{
EPROSIMA_LOG_ERROR(XMLPARSER,
"DNS server did not return any IPv6 address for: '" << s << "'. Name: " << name);
return XMLP_ret::XML_ERROR;
}
EPROSIMA_LOG_ERROR(XMLPARSER, "Failed to parse UDPv6 locator's " << ADDRESS << " tag");
return XMLP_ret::XML_ERROR;
}
IPLocator::setIPv6(locator, s);
}
else
{
Expand Down Expand Up @@ -3230,7 +3204,11 @@ XMLP_ret XMLParser::getXMLLocatorTCPv4(
{
return XMLP_ret::XML_ERROR;
}
IPLocator::setIPv4(locator, s);
if (!IPLocator::setIPv4(locator, s))
{
EPROSIMA_LOG_ERROR(XMLPARSER, "Failed to parse TCPv4 locator's " << ADDRESS << " tag");
return XMLP_ret::XML_ERROR;
}
}
else if (strcmp(name, WAN_ADDRESS) == 0)
{
Expand All @@ -3240,7 +3218,11 @@ XMLP_ret XMLParser::getXMLLocatorTCPv4(
{
return XMLP_ret::XML_ERROR;
}
IPLocator::setWan(locator, s);
if (!IPLocator::setWan(locator, s))
{
EPROSIMA_LOG_ERROR(XMLPARSER, "Failed to parse TCPv4 locator's " << WAN_ADDRESS << " tag");
return XMLP_ret::XML_ERROR;
}
}
else if (strcmp(name, UNIQUE_LAN_ID) == 0)
{
Expand Down Expand Up @@ -3319,7 +3301,11 @@ XMLP_ret XMLParser::getXMLLocatorTCPv6(
{
return XMLP_ret::XML_ERROR;
}
IPLocator::setIPv6(locator, s);
if (!IPLocator::setIPv6(locator, s))
{
EPROSIMA_LOG_ERROR(XMLPARSER, "Failed to parse TCPv6 locator's " << ADDRESS << " tag");
return XMLP_ret::XML_ERROR;
}
}
else
{
Expand Down
6 changes: 3 additions & 3 deletions test/unittest/utils/LocatorTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ class IPLocatorTests : public ::testing::Test

const std::string ipv4_any = "0.0.0.0";
const std::string ipv4_invalid = "0.0.0.0";
const std::string ipv4_invalid_format = "192.168.1.256.1";
const std::string ipv6_any = "::";
const std::string ipv6_invalid = "0:0:0:0:0:0:0:0";

Expand Down Expand Up @@ -161,7 +162,6 @@ TEST_F(IPLocatorTests, setIPv4_from_string)
{
// Error cases
ASSERT_FALSE(IPLocator::setIPv4(locator, "1.1.1.256")); // Too high number
ASSERT_FALSE(IPLocator::setIPv4(locator, "1.1.1")); // Too few args
ASSERT_FALSE(IPLocator::setIPv4(locator, "1.1.1.1.1")); // Too much args

// Change to IPv6
Expand Down Expand Up @@ -1318,8 +1318,8 @@ TEST_F(IPLocatorTests, setIPv4address)
}

ASSERT_FALSE(IPLocator::setIPv4address(locator, "1.2.3.4.5.6.7", "9.10.11.12", "13.14.15.16"));
ASSERT_FALSE(IPLocator::setIPv4address(locator, "1.2.3.4.5.6.7.8", "9.10.11", "13.14.15.16"));
ASSERT_FALSE(IPLocator::setIPv4address(locator, "1.2.3.4.5.6.7.8", "9.10.11.12", "13.14.15"));
ASSERT_FALSE(IPLocator::setIPv4address(locator, "1.2.3.4.5.6.7.8", ipv4_invalid_format, "13.14.15.16"));
ASSERT_FALSE(IPLocator::setIPv4address(locator, "1.2.3.4.5.6.7.8", "9.10.11.12", ipv4_invalid_format));

locator.kind = LOCATOR_KIND_TCPv6;
ASSERT_FALSE(IPLocator::setIPv4address(locator, "1.2.3.4.5.6.7.8", "9.10.11.12", "13.14.15.16"));
Expand Down
6 changes: 5 additions & 1 deletion test/unittest/xmlparser/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,11 @@ target_link_libraries(XMLParserTests
if(QNX)
target_link_libraries(XMLParserTests socket)
endif()
gtest_discover_tests(XMLParserTests)
if(EPROSIMA_TEST_DNS_NOT_SET_UP)
message(STATUS "Ignoring 'getXMLLocatorDNS*'")
set(IGNORE_COMMAND "-getXMLLocatorDNS*")
endif()
gtest_discover_tests(XMLParserTests TEST_FILTER ${IGNORE_COMMAND})
###################################### XMLParserTests ########################################################

####################################### XMLTreeTests #########################################################
Expand Down
Loading

0 comments on commit 50c5848

Please sign in to comment.