diff --git a/src/seeder/test/seeder_tests.cpp b/src/seeder/test/seeder_tests.cpp --- a/src/seeder/test/seeder_tests.cpp +++ b/src/seeder/test/seeder_tests.cpp @@ -4,12 +4,122 @@ #define BOOST_TEST_MODULE Bitcoin Seeder Test Suite +#include + +#include +#include +#include + #include BOOST_AUTO_TEST_SUITE(seeder_tests) -BOOST_AUTO_TEST_CASE(test_stub) { - BOOST_CHECK_EQUAL(true, true); +static const int MAX_QUERY_NAME_LENGTH = 255; +static const uint8_t END_OF_NAME_FIELD = 0; +static const size_t MAX_LABEL_LENGTH = 63; + +// 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); + + // Input buffer tests + // parse_name() does not support input strings of length 0 + for (size_t nameFieldEndIndex = 1; + nameFieldEndIndex < MAX_QUERY_NAME_LENGTH; nameFieldEndIndex++) { + std::array parsedQueryName; + parsedQueryName.fill(0); + const uint8_t *nameFieldBegin = nameField.data(); + int ret = parse_name( + &nameFieldBegin, nameFieldBegin + nameFieldEndIndex, + nameField.data(), parsedQueryName.data(), parsedQueryName.size()); + + if (nameFieldEndIndex < nameField.size()) { + BOOST_CHECK(ret != 0); + } else { + BOOST_CHECK(ret == 0); + BOOST_CHECK_EQUAL(parsedQueryName.data(), + queryName.substr(0, nameFieldEndIndex - 1)); + } + } + + // Output buffer size test + std::array parsedQueryName; + parsedQueryName.fill(0); + const uint8_t *nameFieldBegin = nameField.data(); + int ret = + parse_name(&nameFieldBegin, nameFieldBegin + nameField.size(), + nameField.data(), parsedQueryName.data(), queryName.size()); + BOOST_CHECK(ret != 0); +} + +BOOST_AUTO_TEST_CASE(parse_name_tests) { + CheckParseName("www.domain.com"); + CheckParseName("domain.com"); + // Shortest valid domain name is 1 char followed by the extension + CheckParseName("a.co"); + CheckParseName("sub1.sub2.domain.co.uk"); + CheckParseName("my-domain.com"); + + // Check behavior for name with maximum length label + std::string maxLengthLabel; + for (size_t i = 0; i < MAX_LABEL_LENGTH; i++) { + maxLengthLabel += 'a'; + } + BOOST_CHECK_EQUAL(maxLengthLabel.size(), MAX_LABEL_LENGTH); + CheckParseName("www." + maxLengthLabel + ".com"); + + // Check behavior for a name that is the maximum length + std::string maxLengthQName = maxLengthLabel + '.' + maxLengthLabel + '.' + + maxLengthLabel + '.' + maxLengthLabel; + BOOST_CHECK_EQUAL(maxLengthQName.size(), MAX_QUERY_NAME_LENGTH); + CheckParseName(maxLengthQName); +} + +// Test for when a label in the name field is too long +BOOST_AUTO_TEST_CASE(parse_name_label_too_long) { + // Verify that MAX_LABEL_LENGTH + 1 fails + std::string maxLengthLabel; + for (size_t i = 0; i < MAX_LABEL_LENGTH; i++) { + maxLengthLabel += 'a'; + } + BOOST_CHECK_EQUAL(maxLengthLabel.size(), MAX_LABEL_LENGTH); + std::string queryName = "www." + maxLengthLabel + "a.com"; + std::vector nameField = CreateDNSQuestionNameField(queryName); + std::array parsedQueryName; + parsedQueryName.fill(0); + const uint8_t *nameFieldBegin = nameField.data(); + int ret = parse_name(&nameFieldBegin, nameFieldBegin + nameField.size(), + nameField.data(), parsedQueryName.data(), + MAX_QUERY_NAME_LENGTH); + BOOST_CHECK_EQUAL(ret, -1); } BOOST_AUTO_TEST_SUITE_END()