diff --git a/src/Makefile.seedertest.include b/src/Makefile.seedertest.include --- a/src/Makefile.seedertest.include +++ b/src/Makefile.seedertest.include @@ -9,7 +9,8 @@ $(SEEDER_INCLUDES) $(SEEDER_TEST_INCLUDES) $(TESTDEFS) seeder_test_test_bitcoin_seeder_SOURCES = \ - seeder/test/seeder_tests.cpp + seeder/test/seeder_tests.cpp \ + seeder/test/dns_tests.cpp seeder_test_test_bitcoin_seeder_LDADD = $(LIBBITCOIN_SEEDER) diff --git a/src/seeder/test/CMakeLists.txt b/src/seeder/test/CMakeLists.txt --- a/src/seeder/test/CMakeLists.txt +++ b/src/seeder/test/CMakeLists.txt @@ -11,6 +11,7 @@ add_boost_unit_tests_to_suite(bitcoin-seeder test_bitcoin-seeder TESTS seeder_tests.cpp + dns_tests.cpp ) target_link_libraries(test_bitcoin-seeder diff --git a/src/seeder/test/dns_tests.cpp b/src/seeder/test/dns_tests.cpp new file mode 100644 --- /dev/null +++ b/src/seeder/test/dns_tests.cpp @@ -0,0 +1,98 @@ +// Copyright (c) 2019 The Bitcoin developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include + +#include + +BOOST_AUTO_TEST_SUITE(dns_tests) + +static const int MAX_QUERY_NAME_LENGTH = 255; +// Max size of the null-terminated buffer parse_name() writes to. +static const int MAX_QUERY_NAME_BUFFER_LENGTH = MAX_QUERY_NAME_LENGTH + 1; +static const uint8_t END_OF_NAME_FIELD = 0; + +// Builds the name field of the question section of a DNS query +static std::vector +CreateDNSQuestionNameField(const std::string &queryName) { + std::vector nameField; + size_t i = 0; + size_t labelIndex = 0; + while (i < queryName.size()) { + if (queryName[i] == '.') { + // Push the length of the label and then the label + nameField.push_back(i - labelIndex); + while (labelIndex < i) { + nameField.push_back(queryName[labelIndex]); + labelIndex++; + } + labelIndex = i + 1; + } + i++; + } + // Push the length of the label and then the label + nameField.push_back(i - labelIndex); + while (labelIndex < i) { + nameField.push_back(queryName[labelIndex]); + labelIndex++; + } + nameField.push_back(END_OF_NAME_FIELD); + + return nameField; +} + +static void CheckParseName(const std::string &queryName) { + std::vector nameField = CreateDNSQuestionNameField(queryName); + + // Test when name field is too short to reach null-terminator + for (size_t nameFieldEndIndex = 0; nameFieldEndIndex < nameField.size(); + nameFieldEndIndex++) { + std::vector parsedQueryName(MAX_QUERY_NAME_BUFFER_LENGTH, 0); + const uint8_t *nameFieldBegin = nameField.data(); + int ret = parse_name( + &nameFieldBegin, nameFieldBegin + nameFieldEndIndex, + nameField.data(), parsedQueryName.data(), parsedQueryName.size()); + + BOOST_CHECK(ret != 0); + } + + // Test when the buffer size is too small + size_t outputBufferSize = 1; + while (outputBufferSize <= queryName.size()) { + std::vector parsedQueryName(outputBufferSize, 0); + const uint8_t *nameFieldBegin = nameField.data(); + int ret = parse_name(&nameFieldBegin, nameFieldBegin + nameField.size(), + nameField.data(), parsedQueryName.data(), + parsedQueryName.size()); + BOOST_CHECK(ret != 0); + outputBufferSize++; + } + + // Happy path + while (outputBufferSize <= MAX_QUERY_NAME_BUFFER_LENGTH) { + std::vector parsedQueryName(outputBufferSize, 0); + const uint8_t *nameFieldBegin = nameField.data(); + int ret = parse_name(&nameFieldBegin, nameFieldBegin + nameField.size(), + nameField.data(), parsedQueryName.data(), + parsedQueryName.size()); + BOOST_CHECK_EQUAL(ret, 0); + BOOST_CHECK_EQUAL(parsedQueryName.data(), queryName); + outputBufferSize++; + } +} + +BOOST_AUTO_TEST_CASE(parse_name_tests) { + CheckParseName("www.domain.com"); + CheckParseName("domain.com"); + CheckParseName("sub1.sub2.domain.co.uk"); + // Shortest valid domain name is 1 char followed by the extension + CheckParseName("a.co"); + // Domain name with valid non-alphanumeric character + CheckParseName("my-domain.com"); +} + +BOOST_AUTO_TEST_SUITE_END()