diff options
318 files changed, 15426 insertions, 7498 deletions
diff --git a/configure.ac b/configure.ac index 94198c8cc1..cf289880f4 100644 --- a/configure.ac +++ b/configure.ac @@ -1618,8 +1618,6 @@ AC_CONFIG_FILES([src/lib/dhcpsrv/tests/Makefile]) AC_CONFIG_FILES([src/lib/dhcpsrv/tests/test_libraries.h]) AC_CONFIG_FILES([src/lib/dhcpsrv/testutils/Makefile]) AC_CONFIG_FILES([src/lib/dns/Makefile]) -AC_CONFIG_FILES([src/lib/dns/gen-rdatacode.py], - [chmod +x src/lib/dns/gen-rdatacode.py]) AC_CONFIG_FILES([src/lib/dns/tests/Makefile]) AC_CONFIG_FILES([src/lib/dns/tests/testdata/Makefile]) AC_CONFIG_FILES([src/lib/eval/Makefile]) diff --git a/src/bin/dhcp6/dhcp6_lexer.cc b/src/bin/dhcp6/dhcp6_lexer.cc index 5c433f0ab3..7e427df457 100644 --- a/src/bin/dhcp6/dhcp6_lexer.cc +++ b/src/bin/dhcp6/dhcp6_lexer.cc @@ -1,6 +1,6 @@ -#line 2 "dhcp6_lexer.cc" +#line 1 "dhcp6_lexer.cc" -#line 4 "dhcp6_lexer.cc" +#line 3 "dhcp6_lexer.cc" #define YY_INT_ALIGNED short int @@ -2245,7 +2245,7 @@ using namespace isc::dhcp; /* To avoid the call to exit... oops! */ #define YY_FATAL_ERROR(msg) isc::dhcp::Parser6Context::fatal(msg) -#line 2249 "dhcp6_lexer.cc" +#line 2248 "dhcp6_lexer.cc" /* noyywrap disables automatic rewinding for the next file to parse. Since we always parse only a single string, there's no need to do any wraps. And using yywrap requires linking with -lfl, which provides the default yywrap @@ -2271,8 +2271,8 @@ using namespace isc::dhcp; by moving it ahead by yyleng bytes. yyleng specifies the length of the currently matched token. */ #define YY_USER_ACTION driver.loc_.columns(yyleng); +#line 2274 "dhcp6_lexer.cc" #line 2275 "dhcp6_lexer.cc" -#line 2276 "dhcp6_lexer.cc" #define INITIAL 0 #define COMMENT 1 @@ -2602,7 +2602,7 @@ YY_DECL } -#line 2606 "dhcp6_lexer.cc" +#line 2605 "dhcp6_lexer.cc" while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { @@ -5774,7 +5774,7 @@ YY_RULE_SETUP #line 2512 "dhcp6_lexer.ll" ECHO; YY_BREAK -#line 5778 "dhcp6_lexer.cc" +#line 5777 "dhcp6_lexer.cc" case YY_END_OF_BUFFER: { diff --git a/src/lib/asiodns/io_fetch.cc b/src/lib/asiodns/io_fetch.cc index b0c3b46d1b..8256fa2de6 100644 --- a/src/lib/asiodns/io_fetch.cc +++ b/src/lib/asiodns/io_fetch.cc @@ -18,7 +18,7 @@ #include <asiodns/logger.h> #include <dns/messagerenderer.h> #include <dns/opcode.h> -#include <dns/qid_gen.h> +#include <cryptolink/crypto_rng.h> #include <dns/rcode.h> #include <util/buffer.h> @@ -83,7 +83,7 @@ struct IOFetchData : boost::noncopyable { isc::log::MessageID origin; ///< Origin of last asynchronous I/O uint8_t staging[IOFetch::STAGING_LENGTH]; ///< Temporary array for received data - isc::dns::qid_t qid; ///< The QID set in the query + isc::dns::qid_t qid; ///< The QID set in the query /// \brief Constructor /// @@ -132,7 +132,7 @@ struct IOFetchData : boost::noncopyable { packet(false), origin(ASIODNS_UNKNOWN_ORIGIN), staging(), - qid(QidGenerator::getInstance().generateQid()) { + qid(cryptolink::generateQid()) { } /// \brief Destructor diff --git a/src/lib/cryptolink/crypto_rng.cc b/src/lib/cryptolink/crypto_rng.cc index e6b4ee0f29..13155fbcbb 100644 --- a/src/lib/cryptolink/crypto_rng.cc +++ b/src/lib/cryptolink/crypto_rng.cc @@ -28,5 +28,12 @@ random(size_t len) { return (rng->random(len)); } +uint16_t generateQid() { + uint16_t val; + std::vector<uint8_t> rnd = random(sizeof(uint16_t)); + memmove(&val, &rnd[0], sizeof(uint16_t)); + return (val); +} + } // namespace cryptolink } // namespace isc diff --git a/src/lib/cryptolink/crypto_rng.h b/src/lib/cryptolink/crypto_rng.h index cfada09a96..1a2c96bd9f 100644 --- a/src/lib/cryptolink/crypto_rng.h +++ b/src/lib/cryptolink/crypto_rng.h @@ -57,6 +57,11 @@ private: /// \param len The length of the data std::vector<uint8_t> random(size_t len); +/// \brief Generate a Qid +/// +/// \return A random Qid +uint16_t generateQid(); + } // namespace cryptolink } // namespace isc diff --git a/src/lib/cryptolink/openssl_hash.cc b/src/lib/cryptolink/openssl_hash.cc index 68f2285e15..1dd935c85b 100644 --- a/src/lib/cryptolink/openssl_hash.cc +++ b/src/lib/cryptolink/openssl_hash.cc @@ -145,8 +145,7 @@ private: EVP_MD_CTX* md_; }; -Hash::Hash(const HashAlgorithm hash_algorithm) -{ +Hash::Hash(const HashAlgorithm hash_algorithm) { impl_ = new HashImpl(hash_algorithm); } diff --git a/src/lib/d2srv/nc_trans.cc b/src/lib/d2srv/nc_trans.cc index 4b2baaf2aa..43e71cb098 100644 --- a/src/lib/d2srv/nc_trans.cc +++ b/src/lib/d2srv/nc_trans.cc @@ -8,7 +8,7 @@ #include <d2srv/d2_log.h> #include <d2srv/nc_trans.h> -#include <dns/qid_gen.h> +#include <cryptolink/crypto_rng.h> #include <dns/rdata.h> #include <dns/rdataclass.h> #include <hooks/hooks.h> @@ -352,7 +352,7 @@ NameChangeTransaction::prepNewRequest(DdnsDomainPtr domain) { D2UpdateMessagePtr request(new D2UpdateMessage(D2UpdateMessage:: OUTBOUND)); // Set the query id - request->setId(dns::QidGenerator::getInstance().generateQid()); + request->setId(cryptolink::generateQid()); // Construct the Zone Section. dns::Name zone_name(domain->getName()); request->setZone(zone_name, dns::RRClass::IN()); diff --git a/src/lib/dns/.gitignore b/src/lib/dns/.gitignore index fa14fe18b4..e69de29bb2 100644 --- a/src/lib/dns/.gitignore +++ b/src/lib/dns/.gitignore @@ -1,2 +0,0 @@ -/gen-rdatacode.py -/s-rdatacode diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index feac9e3c22..9dd841065a 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -7,45 +7,7 @@ AM_CPPFLAGS += $(BOOST_INCLUDES) AM_CXXFLAGS = $(KEA_CXXFLAGS) CLEANFILES = *.gcno *.gcda -CLEANFILES += s-rdatacode -# These two are created with rrtype/class.h, so not explicitly listed in -# BUILT_SOURCES. -CLEANFILES += python/rrtype_constants_inc.cc -CLEANFILES += python/rrclass_constants_inc.cc -DISTCLEANFILES = gen-rdatacode.py - -EXTRA_DIST = rrclass-placeholder.h -EXTRA_DIST += rrparamregistry-placeholder.cc -EXTRA_DIST += rrtype-placeholder.h - -# TODO: double-check that this is the only way -# NOTE: when an rdata file is added, please also add to this list: -EXTRA_DIST += rdata/any_255/tsig_250.cc -EXTRA_DIST += rdata/any_255/tsig_250.h -EXTRA_DIST += rdata/generic/detail/lexer_util.h -EXTRA_DIST += rdata/generic/opt_41.cc -EXTRA_DIST += rdata/generic/opt_41.h -EXTRA_DIST += rdata/generic/ptr_12.cc -EXTRA_DIST += rdata/generic/ptr_12.h -EXTRA_DIST += rdata/generic/rrsig_46.cc -EXTRA_DIST += rdata/generic/rrsig_46.h -EXTRA_DIST += rdata/generic/soa_6.cc -EXTRA_DIST += rdata/generic/soa_6.h -EXTRA_DIST += rdata/generic/tkey_249.cc -EXTRA_DIST += rdata/generic/tkey_249.h -EXTRA_DIST += rdata/in_1/a_1.cc -EXTRA_DIST += rdata/in_1/a_1.h -EXTRA_DIST += rdata/in_1/aaaa_28.cc -EXTRA_DIST += rdata/in_1/aaaa_28.h -EXTRA_DIST += rdata/in_1/dhcid_49.cc -EXTRA_DIST += rdata/in_1/dhcid_49.h -EXTRA_DIST += rdata/template.cc -EXTRA_DIST += rdata/template.h - -noinst_SCRIPTS = gen-rdatacode.py - -# auto-generate by gen-rdatacode.py: BUILT_SOURCES = rrclass.h rrtype.h rrparamregistry.cc BUILT_SOURCES += rdataclass.h rdataclass.cc @@ -55,7 +17,6 @@ libkea_dns___la_LDFLAGS = -no-undefined -version-info 54:0:0 libkea_dns___la_LDFLAGS += $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) libkea_dns___la_SOURCES = -libkea_dns___la_SOURCES += dns_fwd.h libkea_dns___la_SOURCES += edns.h edns.cc libkea_dns___la_SOURCES += exceptions.h exceptions.cc libkea_dns___la_SOURCES += master_lexer_inputsource.h master_lexer_inputsource.cc @@ -75,7 +36,6 @@ libkea_dns___la_SOURCES += rrparamregistry.h libkea_dns___la_SOURCES += rrset.h rrset.cc libkea_dns___la_SOURCES += rrttl.h rrttl.cc libkea_dns___la_SOURCES += rrtype.cc -libkea_dns___la_SOURCES += qid_gen.h qid_gen.cc libkea_dns___la_SOURCES += question.h question.cc libkea_dns___la_SOURCES += serial.h serial.cc libkea_dns___la_SOURCES += tsig.h tsig.cc @@ -84,6 +44,9 @@ libkea_dns___la_SOURCES += tsigkey.h tsigkey.cc libkea_dns___la_SOURCES += tsigrecord.h tsigrecord.cc libkea_dns___la_SOURCES += master_loader_callbacks.h libkea_dns___la_SOURCES += master_loader.h +libkea_dns___la_SOURCES += txt_like.h +libkea_dns___la_SOURCES += char_string.h char_string.cc + libkea_dns___la_CPPFLAGS = $(AM_CPPFLAGS) libkea_dns___la_LIBADD = $(top_builddir)/src/lib/cryptolink/libkea-cryptolink.la @@ -96,24 +59,9 @@ libkea_dns___la_LIBADD += $(CRYPTO_LIBS) libkea_dns___la_SOURCES += rdataclass.h rrclass.h rrtype.h libkea_dns___la_SOURCES += rdataclass.cc rrparamregistry.cc -rrclass.h: rrclass-placeholder.h -rrtype.h: rrtype-placeholder.h -rrparamregistry.cc: rrparamregistry-placeholder.cc - -s-rdatacode: Makefile $(EXTRA_DIST) - $(PYTHON) ./gen-rdatacode.py - touch $@ - -# In ticket #3413 we removed the whole BIND10/Bundy framework. We also want -# to not require Python3, hence instead of generating the code every time, -# we added the generated files to our repo. It is still possible to regenerate -# those files, but that step is no longer required for successful compilation. - -#rrclass.h rrtype.h rrparamregistry.cc rdataclass.h rdataclass.cc: s-rdatacode - libdns___includedir = $(pkgincludedir)/dns libdns___include_HEADERS = \ - dns_fwd.h \ + char_string.h \ edns.h \ exceptions.h \ labelsequence.h \ @@ -126,7 +74,6 @@ libdns___include_HEADERS = \ messagerenderer.h \ name.h \ opcode.h \ - qid_gen.h \ question.h \ rcode.h \ rdata.h \ @@ -140,9 +87,7 @@ libdns___include_HEADERS = \ tsig.h \ tsigerror.h \ tsigkey.h \ - tsigrecord.h + tsigrecord.h \ + txt_like.h # Purposely not installing these headers: # name_internal.h: used only internally, and not actually DNS specific -# rdata/*/detail/*.h: these are internal use only -# rrclass-placeholder.h -# rrtype-placeholder.h diff --git a/src/lib/dns/char_string.cc b/src/lib/dns/char_string.cc new file mode 100644 index 0000000000..c28d2047c5 --- /dev/null +++ b/src/lib/dns/char_string.cc @@ -0,0 +1,265 @@ +// Copyright (C) 2012-2024 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <exceptions/exceptions.h> + +#include <dns/exceptions.h> +#include <dns/rdata.h> +#include <dns/master_lexer.h> +#include <dns/char_string.h> +#include <util/buffer.h> + +#include <boost/lexical_cast.hpp> + +#include <cassert> +#include <cctype> +#include <cstring> +#include <vector> + +#include <stdint.h> + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { + +namespace { +// Convert a DDD form to the corresponding integer +int +decimalToNumber(const char* s, const char* s_end) { + if (s_end - s < 3) { + isc_throw(InvalidRdataText, "Escaped digits too short"); + } + + const std::string num_str(s, s + 3); + try { + const int i = boost::lexical_cast<int>(num_str); + if (i > 255) { + isc_throw(InvalidRdataText, "Escaped digits too large: " + << num_str); + } + return (i); + } catch (const boost::bad_lexical_cast&) { + isc_throw(InvalidRdataText, + "Invalid form for escaped digits: " << num_str); + } +} +} + +void +stringToCharString(const MasterToken::StringRegion& str_region, + CharString& result) { + // make a space for the 1-byte length field; filled in at the end + result.push_back(0); + + bool escape = false; + const char* s = str_region.beg; + const char* const s_end = str_region.beg + str_region.len; + + for (size_t n = str_region.len; n != 0; --n, ++s) { + int c = (*s & 0xff); + if (escape && std::isdigit(c) != 0) { + c = decimalToNumber(s, s_end); + assert(n >= 3); + n -= 2; + s += 2; + } else if (!escape && c == '\\') { + escape = true; + continue; + } + escape = false; + result.push_back(c); + } + if (escape) { // terminated by non-escaped '\' + isc_throw(InvalidRdataText, "character-string ends with '\\'"); + } + if (result.size() > MAX_CHARSTRING_LEN + 1) { // '+ 1' due to the len field + isc_throw(CharStringTooLong, "character-string is too long: " << + (result.size() - 1) << "(+1) characters"); + } + result[0] = result.size() - 1; +} + +void +stringToCharStringData(const MasterToken::StringRegion& str_region, + CharStringData& result) { + bool escape = false; + const char* s = str_region.beg; + const char* const s_end = str_region.beg + str_region.len; + + for (size_t n = str_region.len; n != 0; --n, ++s) { + int c = (*s & 0xff); + if (escape && std::isdigit(c) != 0) { + c = decimalToNumber(s, s_end); + // decimalToNumber() already throws if (s_end - s) is less + // than 3, so the following assertion is unnecessary. But we + // assert it anyway. 'n' is an unsigned type (size_t) and + // can underflow. + assert(n >= 3); + // 'n' and 's' are also updated by 1 in the for statement's + // expression, so we update them by 2 instead of 3 here. + n -= 2; + s += 2; + } else if (!escape && c == '\\') { + escape = true; + continue; + } + escape = false; + result.push_back(c); + } + if (escape) { // terminated by non-escaped '\' + isc_throw(InvalidRdataText, "character-string ends with '\\'"); + } +} + +std::string +charStringToString(const CharString& char_string) { + std::string s; + bool first = true; + for (auto const& it : char_string) { + if (first) { + first = false; + continue; + } + const uint8_t ch = it; + if ((ch < 0x20) || (ch >= 0x7f)) { + // convert to escaped \xxx (decimal) format + s.push_back('\\'); + s.push_back('0' + ((ch / 100) % 10)); + s.push_back('0' + ((ch / 10) % 10)); + s.push_back('0' + (ch % 10)); + continue; + } + if ((ch == '"') || (ch == ';') || (ch == '\\')) { + s.push_back('\\'); + } + s.push_back(ch); + } + + return (s); +} + +std::string +charStringDataToString(const CharStringData& char_string) { + std::string s; + for (auto const& it : char_string) { + const uint8_t ch = it; + if ((ch < 0x20) || (ch >= 0x7f)) { + // convert to escaped \xxx (decimal) format + s.push_back('\\'); + s.push_back('0' + ((ch / 100) % 10)); + s.push_back('0' + ((ch / 10) % 10)); + s.push_back('0' + (ch % 10)); + continue; + } + if ((ch == '"') || (ch == ';') || (ch == '\\')) { + s.push_back('\\'); + } + s.push_back(ch); + } + + return (s); +} + +int compareCharStrings(const detail::CharString& self, + const detail::CharString& other) { + if (self.size() == 0 && other.size() == 0) { + return (0); + } + if (self.size() == 0) { + return (-1); + } + if (other.size() == 0) { + return (1); + } + const size_t self_len = self[0]; + const size_t other_len = other[0]; + const size_t cmp_len = std::min(self_len, other_len); + if (cmp_len == 0) { + if (self_len < other_len) { + return (-1); + } else if (self_len > other_len) { + return (1); + } else { + return (0); + } + } + const int cmp = std::memcmp(&self[1], &other[1], cmp_len); + if (cmp < 0) { + return (-1); + } else if (cmp > 0) { + return (1); + } else if (self_len < other_len) { + return (-1); + } else if (self_len > other_len) { + return (1); + } else { + return (0); + } +} + +int compareCharStringDatas(const detail::CharStringData& self, + const detail::CharStringData& other) { + if (self.size() == 0 && other.size() == 0) { + return (0); + } + if (self.size() == 0) { + return (-1); + } + if (other.size() == 0) { + return (1); + } + const size_t self_len = self.size(); + const size_t other_len = other.size(); + const size_t cmp_len = std::min(self_len, other_len); + const int cmp = std::memcmp(&self[0], &other[0], cmp_len); + if (cmp < 0) { + return (-1); + } else if (cmp > 0) { + return (1); + } else if (self_len < other_len) { + return (-1); + } else if (self_len > other_len) { + return (1); + } else { + return (0); + } +} + +size_t +bufferToCharString(isc::util::InputBuffer& buffer, size_t rdata_len, + CharString& target) { + if (rdata_len < 1 || buffer.getLength() - buffer.getPosition() < 1) { + isc_throw(isc::dns::DNSMessageFORMERR, + "insufficient data to read character-string length"); + } + const uint8_t len = buffer.readUint8(); + if (rdata_len < len + 1) { + isc_throw(isc::dns::DNSMessageFORMERR, + "character string length is too large: " << + static_cast<int>(len)); + } + if (buffer.getLength() - buffer.getPosition() < len) { + isc_throw(isc::dns::DNSMessageFORMERR, + "not enough data in buffer to read character-string of len" + << static_cast<int>(len)); + } + + target.resize(len + 1); + target[0] = len; + buffer.readData(&target[0] + 1, len); + + return (len + 1); +} + +} // end of detail +} // end of generic +} // end of rdata +} // end of dns +} // end of isc diff --git a/src/lib/dns/char_string.h b/src/lib/dns/char_string.h new file mode 100644 index 0000000000..adfcab1db4 --- /dev/null +++ b/src/lib/dns/char_string.h @@ -0,0 +1,136 @@ +// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef DNS_RDATA_CHARSTRING_H +#define DNS_RDATA_CHARSTRING_H 1 + +#include <dns/master_lexer.h> + +#include <string> +#include <vector> +#include <algorithm> +#include <stdint.h> + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { + +/// \brief Type for DNS character string. +/// +/// A character string can contain any unsigned 8-bit value, so this cannot +/// be the bare char basis. +typedef std::vector<uint8_t> CharString; + +/// \brief Type for DNS character string without the length prefix. +typedef std::vector<uint8_t> CharStringData; + +/// \brief Convert a DNS character-string into corresponding binary data. +/// +/// This helper function takes a string object that is expected to be a +/// textual representation of a valid DNS character-string, and dumps +/// the corresponding binary sequence in the given placeholder (passed +/// via the \c result parameter). It handles escape notations of +/// character-strings with a backslash ('\'), and checks the length +/// restriction. +/// +/// \throw CharStringTooLong The resulting binary data are too large for a +/// valid character-string. +/// \throw InvalidRdataText Other syntax errors. +/// +/// \brief str_region A string that represents a character-string. +/// \brief result A placeholder vector where the resulting data are to be +/// stored. Expected to be empty, but it's not checked. +void stringToCharString(const MasterToken::StringRegion& str_region, + CharString& result); + +/// \brief Convert a DNS character-string into corresponding binary data. +/// +/// This method functions similar to \c stringToCharString() except it +/// does not include the 1-octet length prefix in the \c result, and the +/// result is not limited to MAX_CHARSTRING_LEN octets. +/// +/// \throw InvalidRdataText Upon syntax errors. +/// +/// \brief str_region A string that represents a character-string. +/// \brief result A placeholder vector where the resulting data are to be +/// stored. Expected to be empty, but it's not checked. +void stringToCharStringData(const MasterToken::StringRegion& str_region, + CharStringData& result); + +/// \brief Convert a CharString into a textual DNS character-string. +/// +/// This method converts a binary 8-bit representation of a DNS +/// character string into a textual string representation, escaping any +/// special characters in the process. For example, characters like +/// double-quotes, semi-colon and backspace are prefixed with backspace +/// character, and characters not in the printable range of [0x20, 0x7e] +/// (inclusive) are converted to the \xxx 3-digit decimal +/// representation. +/// +/// \param char_string The \c CharString to convert. +/// \return A string representation of \c char_string. +std::string charStringToString(const CharString& char_string); + +/// \brief Convert a CharStringData into a textual DNS character-string. +/// +/// Reverse of \c stringToCharStringData(). See \c stringToCharString() +/// vs. \c stringToCharStringData(). +/// +/// \param char_string The \c CharStringData to convert. +/// \return A string representation of \c char_string. +std::string charStringDataToString(const CharStringData& char_string); + +/// \brief Compare two CharString objects +/// +/// \param self The CharString field to compare +/// \param other The CharString field to compare to +/// +/// \return -1 if \c self would be sorted before \c other +/// 1 if \c self would be sorted after \c other +/// 0 if \c self and \c other are equal +int compareCharStrings(const CharString& self, const CharString& other); + +/// \brief Compare two CharStringData objects +/// +/// \param self The CharStringData field to compare +/// \param other The CharStringData field to compare to +/// +/// \return -1 if \c self would be sorted before \c other +/// 1 if \c self would be sorted after \c other +/// 0 if \c self and \c other are equal +int compareCharStringDatas(const CharStringData& self, + const CharStringData& other); + +/// \brief Convert a buffer containing a character-string to CharString +/// +/// This method reads one character-string from the given buffer (in wire +/// format) and places the result in the given \c CharString object. +/// Since this is expected to be used in message parsing, the exception it +/// raises is of that type. +/// +/// On success, the buffer position is advanced to the end of the char-string, +/// and the number of bytes read is returned. +/// +/// \param buffer The buffer to read from. +/// \param rdata_len The total size of the rr's rdata currently being read +/// (used for integrity checks in the wire data) +/// \param target The \c CharString where the result will be stored. Any +/// existing data in the target will be overwritten. +/// \throw DNSMessageFORMERR If the available data is not enough to read +/// the character-string, or if the character-string length is out of bounds +/// \return The number of bytes read +size_t bufferToCharString(isc::util::InputBuffer& buffer, size_t rdata_len, + CharString& target); + + +} // namespace detail +} // namespace generic +} // namespace rdata +} // namespace dns +} // namespace isc +#endif // DNS_RDATA_CHARSTRING_H diff --git a/src/lib/dns/dns_fwd.h b/src/lib/dns/dns_fwd.h deleted file mode 100644 index 5ba2802a7f..0000000000 --- a/src/lib/dns/dns_fwd.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef DNS_FWD_H -#define DNS_FWD_H - -/// \file dns_fwd.h -/// \brief Forward declarations for definitions of libdns++ -/// -/// This file provides a set of forward declarations for definitions commonly -/// used in libdns++ to help minimize dependency when actual the definition -/// is not necessary. - -namespace isc { -namespace dns { -} // namespace dns -} // namespace isc -#endif // DNS_FWD_H diff --git a/src/lib/dns/edns.cc b/src/lib/dns/edns.cc index 3d9c21ac45..35ad58733f 100644 --- a/src/lib/dns/edns.cc +++ b/src/lib/dns/edns.cc @@ -6,14 +6,7 @@ #include <config.h> -#include <stdint.h> - -#include <cassert> - -#include <boost/lexical_cast.hpp> - #include <exceptions/exceptions.h> - #include <dns/edns.h> #include <dns/exceptions.h> #include <dns/message.h> @@ -25,10 +18,14 @@ #include <dns/rrttl.h> #include <dns/rrtype.h> +#include <stdint.h> +#include <boost/lexical_cast.hpp> + +using namespace isc::util; +using namespace isc::dns::rdata; + using namespace std; using boost::lexical_cast; -using namespace isc::dns::rdata; -using namespace isc::util; namespace isc { namespace dns { @@ -104,7 +101,7 @@ namespace { /// Helper function to define unified implementation for the public versions /// of toWire(). template <typename Output> -int +uint32_t toWireCommon(Output& output, const uint8_t version, const uint16_t udp_size, const bool dnssec_aware, const uint8_t extended_rcode) { @@ -127,7 +124,7 @@ toWireCommon(Output& output, const uint8_t version, } } -unsigned int +uint32_t EDNS::toWire(AbstractMessageRenderer& renderer, const uint8_t extended_rcode) const { // If adding the OPT RR would exceed the size limit, don't do it. @@ -141,7 +138,7 @@ EDNS::toWire(AbstractMessageRenderer& renderer, extended_rcode)); } -unsigned int +uint32_t EDNS::toWire(isc::util::OutputBuffer& buffer, const uint8_t extended_rcode) const { return (toWireCommon(buffer, version_, udp_size_, dnssec_aware_, diff --git a/src/lib/dns/edns.h b/src/lib/dns/edns.h index 2415a52724..362f7bdc75 100644 --- a/src/lib/dns/edns.h +++ b/src/lib/dns/edns.h @@ -304,8 +304,8 @@ public: /// \param extended_rcode Upper 8 bits of extended RCODE to be rendered as /// part of the EDNS OPT RR. /// \return 1 if the OPT RR fits in the message size limit; otherwise 0. - unsigned int toWire(AbstractMessageRenderer& renderer, - const uint8_t extended_rcode) const; + uint32_t toWire(AbstractMessageRenderer& renderer, + const uint8_t extended_rcode) const; /// \brief Render the \c EDNS in the wire format. /// @@ -313,8 +313,8 @@ public: /// except it renders the OPT RR in an \c OutputBuffer and therefore /// does not care about message size limit. /// As a consequence it always returns 1. - unsigned int toWire(isc::util::OutputBuffer& buffer, - const uint8_t extended_rcode) const; + uint32_t toWire(isc::util::OutputBuffer& buffer, + const uint8_t extended_rcode) const; /// \brief Convert the EDNS to a string. /// diff --git a/src/lib/dns/gen-rdatacode.py.in b/src/lib/dns/gen-rdatacode.py.in deleted file mode 100644 index e5284a3459..0000000000 --- a/src/lib/dns/gen-rdatacode.py.in +++ /dev/null @@ -1,382 +0,0 @@ -#!@PYTHON@ - -# Copyright (C) 2010-2021 Internet Systems Consortium, Inc. ("ISC") -# -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -"""\ -This is a supplemental script to (half) auto-generate DNS Rdata related -classes and constants. -""" - -# This script should be used every time existing RR data changes or when new -# RR types are added or existing are removed. Since DHCP uses only four -# (A,AAAA,PTR,DHCID) RR types, its usage is expected to be an uncommon event. -# The only envisaged use case is if/when we decide to trim down libdns++. - -import os -from os.path import getmtime -import re -import sys - -re_typecode = re.compile('([\da-z\-]+)_(\d+)') -classcode2txt = {} -typecode2txt = {} -# For meta types and types well-known but not implemented. This is a dict from -# type code values (as string) to textual mnemonic. -meta_types = { - # Real meta types. We won't have Rdata implement for them, but we need - # RRType constants. - '251': 'ixfr', '252': 'axfr', '255': 'any', - # Obsolete types. We probably won't implement Rdata for them, but it's - # better to have RRType constants. - '3': 'md', '4': 'mf', '7': 'mb', '8': 'mg', '9': 'mr', '30': 'nxt', - '38': 'a6', '254': 'maila', - # Types officially assigned but not yet supported in our implementation. - '10': 'null', '11': 'wks', '19': 'x25', '21': 'rt', '22': 'nsap', - '23': 'nsap-ptr', '24': 'sig', '20': 'isdn', '25': 'key', '26': 'px', - '27': 'gpos', '29': 'loc', '36': 'kx', '37': 'cert', '42': 'apl', - '45': 'ipseckey', '55': 'hip', '103': 'unspec', - '104': 'nid', '105': 'l32', '106': 'l64', '107': 'lp', - '253': 'mailb', '256': 'uri' - } -# Classes that don't have any known types. This is a dict from type code -# values (as string) to textual mnemonic. -meta_classes = {'254': 'none'} -typeandclass = [] -generic_code = 65536 # something larger than any code value -rdata_declarations = '' -class_definitions = '' -classdir_mtime = 0 -rdatadef_mtime = 0 -rdatahdr_mtime = 0 -heading_txt = '''/////////////// -/////////////// -/////////////// THIS FILE IS AUTOMATICALLY GENERATED BY gen-rdatacode.py. -/////////////// DO NOT EDIT! -/////////////// -/////////////// - -''' - -def import_classdef(class_txt, file): - content = '' - rdata_source = open(file, 'r') - for line in rdata_source.readlines(): - if re.match('// BEGIN_ISC_NAMESPACE', line): - content += 'namespace isc {\n' - content += 'namespace dns {\n' - continue - if re.match('// BEGIN_RDATA_NAMESPACE', line): - content += 'namespace rdata {\n' - content += 'namespace ' + class_txt + ' {\n' - continue - if re.match('// END_ISC_NAMESPACE', line): - content += '} // end of namespace "dns"\n' - content += '} // end of namespace "isc"\n' - continue - if re.match('// END_RDATA_NAMESPACE', line): - content += '} // end of namespace "' + class_txt +'"\n' - content += '} // end of namespace "rdata"\n' - continue - content += line - rdata_source.close() - return content - -def import_classheader(class_txt, type_txt, type_code, file): - type_utxt = type_txt.upper() - class_utxt = class_txt.upper() - - # for each CLASS_n/TYPE_m.h - rdata_header = open(file, 'r') - content = '' - guard_macro = class_txt.upper() + '_' + type_txt.upper() - guard_macro += '_' + type_code + '_H' - for line in rdata_header.readlines(): - if re.match('// BEGIN_HEADER_GUARD', line): - content += '#ifndef ' + guard_macro + '\n' - content += '#define ' + guard_macro + ' 1\n' - continue - if re.match('// END_HEADER_GUARD', line): - content += '#endif // ' + guard_macro + '\n' - continue - if re.match('// BEGIN_ISC_NAMESPACE', line): - content += 'namespace isc {\n' - content += 'namespace dns {\n' - continue - if re.match('// BEGIN_RDATA_NAMESPACE', line): - content += 'namespace rdata {\n' - content += 'namespace ' + class_txt + ' {\n' - continue - if re.match('// END_ISC_NAMESPACE', line): - content += '} // end of namespace "dns"\n' - content += '} // end of namespace "isc"\n' - continue - if re.match('// END_RDATA_NAMESPACE', line): - content += '} // end of namespace "' + class_txt +'"\n' - content += '} // end of namespace "rdata"\n' - continue - if re.match('// Local Variables:', line): - break - content += line - if re.match('// BEGIN_COMMON_DECLARATIONS', line): - content += ''' -class AbstractMessageRenderer;\n\n''' - if re.match('\s+// BEGIN_COMMON_MEMBERS$', line): - content += ''' - explicit ''' + type_utxt + '''(const std::string& type_str); - ''' + type_utxt + '''(isc::util::InputBuffer& buffer, size_t rdata_len); - ''' + type_utxt + '''(const ''' + type_utxt + '''& other); - ''' + type_utxt + '''( - MasterLexer& lexer, const Name* name, - MasterLoader::Options options, MasterLoaderCallbacks& callbacks); - virtual std::string toText() const; - virtual void toWire(isc::util::OutputBuffer& buffer) const; - virtual void toWire(AbstractMessageRenderer& renderer) const; - virtual int compare(const Rdata& other) const;\n\n''' - rdata_header.close() - return content - -def import_definitions(classcode2txt, typecode2txt, typeandclass): - global rdata_declarations - global class_definitions - global classdir_mtime - global rdatadef_mtime - global rdatahdr_mtime - - if classdir_mtime < getmtime('@srcdir@/rdata'): - classdir_mtime = getmtime('@srcdir@/rdata') - - # Sort directories before iterating through them so that the directory - # list is processed in the same order on all systems. The resulting - # files should compile regardless of the order in which the components - # are included but... Having a fixed order for the directories should - # eliminate system-dependent problems. (Note that the directory names - # in BIND 10 are ASCII, so the order should be locale-independent.) - dirlist = os.listdir('@srcdir@/rdata') - dirlist.sort() - for dir in dirlist: - classdir = '@srcdir@/rdata' + os.sep + dir - m = re_typecode.match(dir) - if os.path.isdir(classdir) and (m != None or dir == 'generic'): - if dir == 'generic': - class_txt = 'generic' - class_code = generic_code - else: - class_txt = m.group(1) - class_code = m.group(2) - if not class_code in classcode2txt: - classcode2txt[class_code] = class_txt - - # Same considerations as directories regarding sorted order - # also apply to files. - filelist = os.listdir(classdir) - filelist.sort() - for file in filelist: - file = classdir + os.sep + file - m = re_typecode.match(os.path.split(file)[1]) - if m != None: - type_txt = m.group(1) - type_code = m.group(2) - if not type_code in typecode2txt: - typecode2txt[type_code] = type_txt - if re.search('\.cc$', file): - if rdatadef_mtime < getmtime(file): - rdatadef_mtime = getmtime(file) - class_definitions += import_classdef(class_txt, file) - elif re.search('\.h$', file): - if rdatahdr_mtime < getmtime(file): - rdatahdr_mtime = getmtime(file) - rdata_declarations += import_classheader(class_txt, - type_txt, - type_code, - file) - typeandclass.append((type_txt, int(type_code), - (class_txt, class_txt), - int(class_code))) - if class_txt == 'generic': - typeandclass.append((type_txt, int(type_code), - (class_txt, 'in'), 1)) - -def need_generate(file, mtime): - '''Check if we need to generate the specified file. - - To avoid unnecessary compilation, we skip (re)generating the file when - the file already exists and newer than the base file. - ''' - if os.path.exists(file) and getmtime(file) > mtime: - return False - return True - -def generate_rdatadef(file, basemtime): - if not need_generate(file, basemtime): - print('skip generating ' + file); - return - rdata_deffile = open(file, 'w') - rdata_deffile.write(heading_txt) - rdata_deffile.write(class_definitions) - rdata_deffile.close() - -def generate_rdatahdr(file, heading, declarations, basemtime): - if not need_generate(file, basemtime): - print('skip generating ' + file); - return - heading += ''' -#ifndef DNS_RDATACLASS_H -#define DNS_RDATACLASS_H - -#include <dns/master_loader.h> - -namespace isc { -namespace dns { -class Name; -class MasterLexer; -class MasterLoaderCallbacks; -} -} -''' - declarations += ''' -#endif // DNS_RDATACLASS_H -''' - rdata_header = open(file, 'w') - rdata_header.write(heading) - rdata_header.write(declarations) - rdata_header.close() - -def generate_typeclasscode(fileprefix, basemtime, code2txt, type_or_class): - placeholder = '@srcdir@/' + fileprefix + '-placeholder.h' - outputfile = '@builddir@/' + fileprefix + '.h' - py_outputfile = '@builddir@/python/' + fileprefix + '_constants_inc.cc' - upper_key = type_or_class.upper() # TYPE or CLASS - lower_key = 'rr' + type_or_class.lower() # rrtype or rrclass - cap_key = type_or_class # Type or Class - - # We only decide whether to generate files for libdns++ files; Python - # files are generated if and only if libdns++ files are generated. - # In practice it should be sufficient. - if (not need_generate(outputfile, basemtime) and - getmtime(outputfile) > getmtime(placeholder)): - print('skip generating ' + outputfile) - return - - # Create a list of (code, code-text) pairs, where code-text is generally - # upper-cased, with applying special filters when necessary. - def convert(code_txt): - # Workaround by heuristics: there's a "NULL" RR type, but it would - # cause conflict with the C/C++ macro. We use Null as a special case. - if code_txt == 'null': - return 'Null' - # Likewise, convert "nsap-ptr" to "NSAP_PTR" as a dash cannot be part - # of a C/C++ variable. - if code_txt == 'nsap-ptr': - return 'NSAP_PTR' - return code_txt.upper() - codes = [ (code, convert(txt)) for code, txt in code2txt.items() ] - - # Dump source code for libdns++ - with open(placeholder, 'r') as header_temp: - with open(outputfile, 'w') as header_out: - header_out.write(heading_txt) - for line in header_temp: - header_out.write(line) - if re.match('\s+// BEGIN_WELL_KNOWN_' + upper_key + - '_DECLARATIONS$', line): - for code in codes: - header_out.write(' ' * 4 + 'static const RR' + - cap_key + '& ' + code[1] + '();\n') - if re.match('// BEGIN_WELL_KNOWN_' + upper_key + - '_DEFINITIONS$', line): - for code in codes: - header_out.write('''inline const RR''' + cap_key + - '''& -RR''' + cap_key + '''::''' + code[1] + '''() { - static RR''' + cap_key + ''' ''' + lower_key + '''(''' + code[0] + '''); - return (''' + lower_key + '''); -}\n -''') - - # Dump source code snippet for isc.dns Python module - with open(py_outputfile, 'w') as py_out: - py_out.write(" // auto-generated by ../gen-rdatacode.py." - " Don't edit this file.\n") - py_out.write("\n") - for code in codes: - py_out.write('''\ - installClassVariable(''' + lower_key + '''_type, "''' + code[1] + '''", - createRR''' + cap_key + '''Object(RR''' + \ - cap_key + '''::''' + code[1] + '''())); -''') - -def generate_rrparam(fileprefix, basemtime): - placeholder = '@srcdir@/' + fileprefix + '-placeholder.cc' - outputfile = '@builddir@/' + fileprefix + '.cc' - if not need_generate(outputfile, basemtime) and getmtime(outputfile) > getmtime(placeholder): - print('skip generating ' + outputfile) - return - - # sort by class, then by type - typeandclassparams = '' - typeandclass.sort(key = lambda x: (x[3], x[1])) - for param in typeandclass: - # for rrparamregistry.cc - # each param is a tuple of (type_txt, type_code, class_tuple, - # class_code) - (type_txt, type_code, class_tuple, class_code) = param - type_utxt = type_txt.upper() - class_txt = class_tuple[0] - class_utxt = class_tuple[1].upper() - indent = ' ' * 8 - typeandclassparams += indent - - if class_tuple[1] != 'generic': - typeandclassparams += 'add("' + type_utxt + '", ' - typeandclassparams += str(type_code) + ', "' + class_utxt - typeandclassparams += '", ' + str(class_code) - typeandclassparams += ', RdataFactoryPtr(new ' + 'RdataFactory' + '<' - typeandclassparams += class_txt + '::' + type_utxt + '>()));\n' - else: - typeandclassparams += 'add("' + type_utxt + '", ' + str(type_code) - typeandclassparams += ', RdataFactoryPtr(new ' + 'RdataFactory' + '<' - typeandclassparams += class_txt + '::' + type_utxt + '>()));\n' - - typeandclassparams += indent + '// Meta and non-implemented RR types\n' - for type_code, type_txt in meta_types.items(): - typeandclassparams += indent + \ - 'addType("' + type_txt.upper() + '", ' + type_code + ');\n' - - typeandclassparams += indent + '// Meta classes\n' - for cls_code, cls_txt in meta_classes.items(): - typeandclassparams += indent + \ - 'addClass("' + cls_txt.upper() + '", ' + cls_code + ');\n' - - rrparam_temp = open(placeholder, 'r') - rrparam_out = open(outputfile, 'w') - rrparam_out.write(heading_txt) - for line in rrparam_temp.readlines(): - rrparam_out.write(line) - if re.match('\s+// BEGIN_WELL_KNOWN_PARAMS', line): - rrparam_out.write(typeandclassparams) - rrparam_temp.close() - rrparam_out.close() - -if __name__ == "__main__": - try: - import_definitions(classcode2txt, typecode2txt, typeandclass) - generate_rdatadef('@builddir@/rdataclass.cc', rdatadef_mtime) - generate_rdatahdr('@builddir@/rdataclass.h', heading_txt, - rdata_declarations, rdatahdr_mtime) - - # merge auto-generated types/classes with meta maps and generate the - # corresponding code. - generate_typeclasscode('rrtype', rdatahdr_mtime, - dict(typecode2txt, **meta_types), 'Type') - generate_typeclasscode('rrclass', classdir_mtime, - dict(classcode2txt, **meta_classes), 'Class') - - generate_rrparam('rrparamregistry', rdatahdr_mtime) - except: - sys.stderr.write('Code generation failed due to exception: %s\n' % - sys.exc_info()[1]) - exit(1) diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc index f2b52816fe..d855f4d90d 100644 --- a/src/lib/dns/message.cc +++ b/src/lib/dns/message.cc @@ -6,21 +6,7 @@ #include <config.h> -#include <stdint.h> - -#include <algorithm> -#include <cassert> -#include <string> -#include <sstream> -#include <vector> - -#include <boost/lexical_cast.hpp> -#include <boost/shared_ptr.hpp> - #include <exceptions/exceptions.h> - -#include <util/buffer.h> - #include <dns/edns.h> #include <dns/exceptions.h> #include <dns/message.h> @@ -35,12 +21,23 @@ #include <dns/rrttl.h> #include <dns/rrset.h> #include <dns/tsig.h> +#include <util/buffer.h> + +#include <stdint.h> +#include <algorithm> +#include <cassert> +#include <string> +#include <sstream> +#include <vector> +#include <boost/lexical_cast.hpp> +#include <boost/shared_ptr.hpp> -using namespace std; -using boost::lexical_cast; using namespace isc::dns::rdata; using namespace isc::util; +using namespace std; +using boost::lexical_cast; + namespace isc { namespace dns { diff --git a/src/lib/dns/messagerenderer.cc b/src/lib/dns/messagerenderer.cc index fcef95c9aa..bc59e16d63 100644 --- a/src/lib/dns/messagerenderer.cc +++ b/src/lib/dns/messagerenderer.cc @@ -7,23 +7,23 @@ #include <config.h> #include <exceptions/exceptions.h> -#include <util/buffer.h> #include <dns/name.h> #include <dns/name_internal.h> #include <dns/labelsequence.h> #include <dns/messagerenderer.h> +#include <util/buffer.h> #include <boost/array.hpp> #include <boost/static_assert.hpp> - #include <limits> #include <cassert> #include <vector> -using namespace std; using namespace isc::util; using isc::dns::name::internal::maptolower; +using namespace std; + namespace isc { namespace dns { diff --git a/src/lib/dns/messagerenderer.h b/src/lib/dns/messagerenderer.h index d63bbd7ea5..ec47132a4b 100644 --- a/src/lib/dns/messagerenderer.h +++ b/src/lib/dns/messagerenderer.h @@ -13,7 +13,6 @@ #include <boost/noncopyable.hpp> namespace isc { - namespace dns { // forward declarations class Name; diff --git a/src/lib/dns/name.cc b/src/lib/dns/name.cc index addac4fa8e..efdea0e10d 100644 --- a/src/lib/dns/name.cc +++ b/src/lib/dns/name.cc @@ -6,6 +6,12 @@ #include <config.h> +#include <exceptions/isc_assert.h> +#include <dns/name.h> +#include <dns/name_internal.h> +#include <dns/messagerenderer.h> +#include <dns/labelsequence.h> + #include <cctype> #include <iterator> #include <functional> @@ -13,16 +19,11 @@ #include <iostream> #include <algorithm> -#include <exceptions/isc_assert.h> -#include <dns/name.h> -#include <dns/name_internal.h> -#include <dns/messagerenderer.h> -#include <dns/labelsequence.h> - -using namespace std; using namespace isc::util; using namespace isc::dns::name::internal; +using namespace std; + namespace isc { namespace dns { diff --git a/src/lib/dns/qid_gen.cc b/src/lib/dns/qid_gen.cc deleted file mode 100644 index 207eeadb3e..0000000000 --- a/src/lib/dns/qid_gen.cc +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2011-2021 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -// qid_gen defines a generator for query id's -// -// We probably want to merge this with the weighted random in the nsas -// (and other parts where we need randomness, perhaps another thing -// for a general libutil?) - -#include <config.h> - -#include <cryptolink/crypto_rng.h> -#include <dns/qid_gen.h> -#include <cstring> - -namespace isc { -namespace dns { - -QidGenerator qid_generator_instance; - -QidGenerator& -QidGenerator::getInstance() { - return (qid_generator_instance); -} - -QidGenerator::QidGenerator() { -} - -uint16_t -QidGenerator::generateQid() { - uint16_t val; - std::vector<uint8_t> rnd = isc::cryptolink::random(sizeof(uint16_t)); - memmove(&val, &rnd[0], sizeof(uint16_t)); - return (val); -} - -} // namespace dns -} // namespace isc diff --git a/src/lib/dns/qid_gen.h b/src/lib/dns/qid_gen.h deleted file mode 100644 index 732dd9a3bc..0000000000 --- a/src/lib/dns/qid_gen.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (C) 2011-2021 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -// qid_gen defines a generator for query id's -// -// We probably want to merge this with the weighted random in the nsas -// (and other parts where we need randomness, perhaps another thing -// for a general libutil?) - -#ifndef QID_GEN_H -#define QID_GEN_H - -#include <cryptolink/crypto_rng.h> -#include <stdint.h> - -namespace isc { -namespace dns { - -/// This class generates Qids for outgoing queries -/// -/// It is implemented as a singleton; the public way to access it -/// is to call getInstance()->generateQid(). -/// -/// It automatically seeds it with the current time when it is first -/// used. -class QidGenerator { -public: - /// \brief Returns the singleton instance of the QidGenerator - /// - /// Returns a reference to the singleton instance of the generator - static QidGenerator& getInstance(); - - /// \brief Default constructor - /// - /// It is recommended that getInstance is used rather than creating - /// separate instances of this class. - /// - /// The constructor automatically seeds the generator with the - /// current time. - QidGenerator(); - - /// Generate a Qid - /// - /// \return A random Qid - uint16_t generateQid(); -}; - -} // namespace dns -} // namespace isc - -#endif // QID_GEN_H diff --git a/src/lib/dns/question.cc b/src/lib/dns/question.cc index 72499f2355..7659f34ae3 100644 --- a/src/lib/dns/question.cc +++ b/src/lib/dns/question.cc @@ -6,19 +6,20 @@ #include <config.h> -#include <iostream> -#include <string> - -#include <util/buffer.h> #include <dns/messagerenderer.h> #include <dns/name.h> #include <dns/question.h> #include <dns/rrclass.h> #include <dns/rrtype.h> +#include <util/buffer.h> + +#include <iostream> +#include <string> -using namespace std; using namespace isc::util; +using namespace std; + namespace isc { namespace dns { Question::Question(InputBuffer& buffer) : @@ -44,7 +45,7 @@ Question::toText(bool newline) const { return (r); } -unsigned int +uint32_t Question::toWire(OutputBuffer& buffer) const { name_.toWire(buffer); rrtype_.toWire(buffer); @@ -53,7 +54,7 @@ Question::toWire(OutputBuffer& buffer) const { return (1); } -unsigned int +uint32_t Question::toWire(AbstractMessageRenderer& renderer) const { const size_t pos0 = renderer.getLength(); diff --git a/src/lib/dns/question.h b/src/lib/dns/question.h index 055907a315..21a8a9b4e7 100644 --- a/src/lib/dns/question.h +++ b/src/lib/dns/question.h @@ -17,7 +17,6 @@ #include <dns/rrtype.h> namespace isc { - namespace dns { class Question; @@ -214,7 +213,7 @@ public: /// output buffer and name compression information. /// /// \return 1 on success; 0 if it causes truncation - unsigned int toWire(AbstractMessageRenderer& renderer) const; + uint32_t toWire(AbstractMessageRenderer& renderer) const; /// \brief Render the Question in the wire format without name compression. /// @@ -224,7 +223,7 @@ public: /// /// \param buffer An output buffer to store the wire data. /// \return 1 - unsigned int toWire(isc::util::OutputBuffer& buffer) const; + uint32_t toWire(isc::util::OutputBuffer& buffer) const; //@} /// diff --git a/src/lib/dns/rdata.cc b/src/lib/dns/rdata.cc index 25829c8d2e..dd778a9c17 100644 --- a/src/lib/dns/rdata.cc +++ b/src/lib/dns/rdata.cc @@ -7,20 +7,17 @@ #include <config.h> #include <exceptions/exceptions.h> - -#include <util/buffer.h> -#include <util/encode/encode.h> - #include <dns/name.h> #include <dns/messagerenderer.h> #include <dns/master_lexer.h> #include <dns/rdata.h> #include <dns/rrparamregistry.h> #include <dns/rrtype.h> +#include <util/buffer.h> +#include <util/encode/encode.h> #include <boost/lexical_cast.hpp> #include <boost/shared_ptr.hpp> - #include <algorithm> #include <cctype> #include <string> @@ -29,13 +26,13 @@ #include <ios> #include <ostream> #include <vector> - #include <stdint.h> #include <string.h> +using namespace isc::util; + using namespace std; using boost::lexical_cast; -using namespace isc::util; namespace isc { namespace dns { @@ -61,7 +58,7 @@ createRdata(const RRType& rrtype, const RRClass& rrclass, RdataPtr createRdata(const RRType& rrtype, const RRClass& rrclass, - isc::util::InputBuffer& buffer, size_t len) { + InputBuffer& buffer, size_t len) { if (len > MAX_RDLENGTH) { isc_throw(InvalidRdataLength, "RDLENGTH too large"); } @@ -197,7 +194,7 @@ struct GenericImpl { vector<uint8_t> data_; }; -Generic::Generic(isc::util::InputBuffer& buffer, size_t rdata_len) { +Generic::Generic(InputBuffer& buffer, size_t rdata_len) { if (rdata_len > MAX_RDLENGTH) { isc_throw(InvalidRdataLength, "RDLENGTH too large"); } @@ -256,7 +253,7 @@ Generic::constructFromLexer(MasterLexer& lexer) { } try { - isc::util::encode::decodeHex(hex_txt, data); + encode::decodeHex(hex_txt, data); } catch (const isc::BadValue& ex) { isc_throw(InvalidRdataText, "Invalid hex encoding of generic RDATA: " << ex.what()); @@ -351,7 +348,7 @@ Generic::toText() const { } void -Generic::toWire(isc::util::OutputBuffer& buffer) const { +Generic::toWire(OutputBuffer& buffer) const { buffer.writeData(&impl_->data_[0], impl_->data_.size()); } diff --git a/src/lib/dns/rdata/any_255/tsig_250.cc b/src/lib/dns/rdata/any_255/tsig_250.cc deleted file mode 100644 index b5469d2bcd..0000000000 --- a/src/lib/dns/rdata/any_255/tsig_250.cc +++ /dev/null @@ -1,562 +0,0 @@ -// Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <string> -#include <sstream> -#include <vector> - -#include <boost/lexical_cast.hpp> - -#include <util/buffer.h> -#include <util/encode/encode.h> - -#include <dns/messagerenderer.h> -#include <dns/name.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> -#include <dns/rcode.h> -#include <dns/tsigkey.h> -#include <dns/tsigerror.h> -#include <dns/rdata/generic/detail/lexer_util.h> - -using namespace std; -using boost::lexical_cast; -using namespace isc::util; -using namespace isc::util::encode; -using namespace isc::dns; -using isc::dns::rdata::generic::detail::createNameFromLexer; - -// BEGIN_ISC_NAMESPACE -// BEGIN_RDATA_NAMESPACE - -// straightforward representation of TSIG RDATA fields -struct TSIGImpl { - TSIGImpl(const Name& algorithm, uint64_t time_signed, uint16_t fudge, - vector<uint8_t>& mac, uint16_t original_id, uint16_t error, - vector<uint8_t>& other_data) : - algorithm_(algorithm), time_signed_(time_signed), fudge_(fudge), - mac_(mac), original_id_(original_id), error_(error), - other_data_(other_data) { - } - TSIGImpl(const Name& algorithm, uint64_t time_signed, uint16_t fudge, - size_t macsize, const void* mac, uint16_t original_id, - uint16_t error, size_t other_len, const void* other_data) : - algorithm_(algorithm), time_signed_(time_signed), fudge_(fudge), - mac_(static_cast<const uint8_t*>(mac), - static_cast<const uint8_t*>(mac) + macsize), - original_id_(original_id), error_(error), - other_data_(static_cast<const uint8_t*>(other_data), - static_cast<const uint8_t*>(other_data) + other_len) { - } - template <typename Output> - void toWireCommon(Output& output) const; - - const Name algorithm_; - const uint64_t time_signed_; - const uint16_t fudge_; - const vector<uint8_t> mac_; - const uint16_t original_id_; - const uint16_t error_; - const vector<uint8_t> other_data_; -}; - -// helper function for string and lexer constructors -boost::shared_ptr<TSIGImpl> -TSIG::constructFromLexer(MasterLexer& lexer, const Name* origin) { - const Name& algorithm = - createNameFromLexer(lexer, origin ? origin : &Name::ROOT_NAME()); - const Name& canonical_algorithm_name = - (algorithm == TSIGKey::HMACMD5_SHORT_NAME()) ? - TSIGKey::HMACMD5_NAME() : algorithm; - - const string& time_txt = - lexer.getNextToken(MasterToken::STRING).getString(); - uint64_t time_signed; - try { - time_signed = boost::lexical_cast<uint64_t>(time_txt); - } catch (const boost::bad_lexical_cast&) { - isc_throw(InvalidRdataText, "Invalid TSIG Time"); - } - if ((time_signed >> 48) != 0) { - isc_throw(InvalidRdataText, "TSIG Time out of range"); - } - - const uint32_t fudge = lexer.getNextToken(MasterToken::NUMBER).getNumber(); - if (fudge > 0xffff) { - isc_throw(InvalidRdataText, "TSIG Fudge out of range"); - } - const uint32_t macsize = - lexer.getNextToken(MasterToken::NUMBER).getNumber(); - if (macsize > 0xffff) { - isc_throw(InvalidRdataText, "TSIG MAC Size out of range"); - } - - const string& mac_txt = (macsize > 0) ? - lexer.getNextToken(MasterToken::STRING).getString() : ""; - vector<uint8_t> mac; - decodeBase64(mac_txt, mac); - if (mac.size() != macsize) { - isc_throw(InvalidRdataText, "TSIG MAC Size and data are inconsistent"); - } - - const uint32_t orig_id = - lexer.getNextToken(MasterToken::NUMBER).getNumber(); - if (orig_id > 0xffff) { - isc_throw(InvalidRdataText, "TSIG Original ID out of range"); - } - - const string& error_txt = - lexer.getNextToken(MasterToken::STRING).getString(); - uint32_t error = 0; - // XXX: In the initial implementation we hardcode the mnemonics. - // We'll soon generalize this. - if (error_txt == "NOERROR") { - error = Rcode::NOERROR_CODE; - } else if (error_txt == "BADSIG") { - error = TSIGError::BAD_SIG_CODE; - } else if (error_txt == "BADKEY") { - error = TSIGError::BAD_KEY_CODE; - } else if (error_txt == "BADTIME") { - error = TSIGError::BAD_TIME_CODE; - } else if (error_txt == "BADMODE") { - error = TSIGError::BAD_MODE_CODE; - } else if (error_txt == "BADNAME") { - error = TSIGError::BAD_NAME_CODE; - } else if (error_txt == "BADALG") { - error = TSIGError::BAD_ALG_CODE; - } else if (error_txt == "BADTRUNC") { - error = TSIGError::BAD_TRUNC_CODE; - } else { - /// we cast to uint32_t and range-check, because casting directly to - /// uint16_t will convert negative numbers to large positive numbers - try { - error = boost::lexical_cast<uint32_t>(error_txt); - } catch (const boost::bad_lexical_cast&) { - isc_throw(InvalidRdataText, "Invalid TSIG Error"); - } - if (error > 0xffff) { - isc_throw(InvalidRdataText, "TSIG Error out of range"); - } - } - - const uint32_t otherlen = - lexer.getNextToken(MasterToken::NUMBER).getNumber(); - if (otherlen > 0xffff) { - isc_throw(InvalidRdataText, "TSIG Other Len out of range"); - } - const string otherdata_txt = (otherlen > 0) ? - lexer.getNextToken(MasterToken::STRING).getString() : ""; - vector<uint8_t> other_data; - decodeBase64(otherdata_txt, other_data); - if (other_data.size() != otherlen) { - isc_throw(InvalidRdataText, - "TSIG Other Data length does not match Other Len"); - } - // RFC2845 says Other Data is "empty unless Error == BADTIME". - // However, we don't enforce that. - - return (boost::shared_ptr<TSIGImpl>(new TSIGImpl(canonical_algorithm_name, - time_signed, fudge, mac, - orig_id, error, other_data))); -} - -/// \brief Constructor from string. -/// -/// The given string must represent a valid TSIG RDATA. There can be extra -/// space characters at the beginning or end of the text (which are simply -/// ignored), but other extra text, including a new line, will make the -/// construction fail with an exception. -/// -/// \c tsig_str must be formatted as follows: -/// \code <Algorithm Name> <Time Signed> <Fudge> <MAC Size> [<MAC>] -/// <Original ID> <Error> <Other Len> [<Other Data>] -/// \endcode -/// -/// Note that, since the Algorithm Name field is defined to be "in domain name -/// syntax", but it is not actually a domain name, it does not have to be -/// fully qualified. -/// -/// The Error field is an unsigned 16-bit decimal integer or a valid mnemonic -/// as specified in RFC2845. Currently, "NOERROR", "BADSIG", "BADKEY", and -/// "BADTIME" are supported (case sensitive). In future versions other -/// representations that are compatible with the DNS RCODE may be supported. -/// -/// The MAC and Other Data fields are base-64 encoded strings that do not -/// contain space characters. -/// If the MAC Size field is 0, the MAC field must not appear in \c tsig_str. -/// If the Other Len field is 0, the Other Data field must not appear in -/// \c tsig_str. -/// The decoded data of the MAC field is MAC Size bytes of binary stream. -/// The decoded data of the Other Data field is Other Len bytes of binary -/// stream. -/// -/// An example of valid string is: -/// \code "hmac-sha256. 853804800 300 3 AAAA 2845 0 0" \endcode -/// In this example Other Data is missing because Other Len is 0. -/// -/// Note that RFC2845 does not define the standard presentation format -/// of %TSIG RR, so the above syntax is implementation specific. -/// This is, however, compatible with the format acceptable to BIND 9's -/// RDATA parser. -/// -/// \throw Others Exception from the Name constructors. -/// \throw InvalidRdataText if any fields are out of their valid range, -/// or are incorrect. -/// \throw BadValue if MAC or Other Data is not validly encoded in base-64. -/// -/// \param tsig_str A string containing the RDATA to be created -TSIG::TSIG(const std::string& tsig_str) : impl_(NULL) { - // We use unique_ptr here because if there is an exception in this - // constructor, the destructor is not called and there could be a - // leak of the TSIGImpl that constructFromLexer() returns. - boost::shared_ptr<TSIGImpl> impl_ptr; - - try { - std::istringstream ss(tsig_str); - MasterLexer lexer; - lexer.pushSource(ss); - - impl_ptr.reset(constructFromLexer(lexer, NULL)); - - if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { - isc_throw(InvalidRdataText, - "Extra input text for TSIG: " << tsig_str); - } - } catch (const MasterLexer::LexerError& ex) { - isc_throw(InvalidRdataText, - "Failed to construct TSIG from '" << tsig_str << "': " - << ex.what()); - } - - impl_ = impl_ptr; -} - -/// \brief Constructor with a context of MasterLexer. -/// -/// The \c lexer should point to the beginning of valid textual -/// representation of an TSIG RDATA. -/// -/// See \c TSIG::TSIG(const std::string&) for description of the -/// expected RDATA fields. -/// -/// \throw MasterLexer::LexerError General parsing error such as -/// missing field. -/// \throw InvalidRdataText if any fields are out of their valid range, -/// or are incorrect. -/// -/// \param lexer A \c MasterLexer object parsing a master file for the -/// RDATA to be created -TSIG::TSIG(MasterLexer& lexer, const Name* origin, - MasterLoader::Options, MasterLoaderCallbacks&) : - impl_(constructFromLexer(lexer, origin)) { -} - -/// \brief Constructor from wire-format data. -/// -/// When a read operation on \c buffer fails (e.g., due to a corrupted -/// message) a corresponding exception from the \c InputBuffer class will -/// be thrown. -/// If the wire-format data does not begin with a valid domain name, -/// a corresponding exception from the \c Name class will be thrown. -/// In addition, this constructor internally involves resource allocation, -/// and if it fails a corresponding standard exception will be thrown. -/// -/// According to RFC3597, the Algorithm field must be a non compressed form -/// of domain name. But this implementation accepts a %TSIG RR even if that -/// field is compressed. -/// -/// \param buffer A buffer storing the wire format data. -/// \param rdata_len The length of the RDATA in bytes, normally expected -/// to be the value of the RDLENGTH field of the corresponding RR. -/// But this constructor does not use this parameter; if necessary, the caller -/// must check consistency between the length parameter and the actual -/// RDATA length. -TSIG::TSIG(InputBuffer& buffer, size_t) : - impl_(NULL) { - Name algorithm(buffer); - - uint8_t time_signed_buf[6]; - buffer.readData(time_signed_buf, sizeof(time_signed_buf)); - const uint64_t time_signed = - (static_cast<uint64_t>(time_signed_buf[0]) << 40 | - static_cast<uint64_t>(time_signed_buf[1]) << 32 | - static_cast<uint64_t>(time_signed_buf[2]) << 24 | - static_cast<uint64_t>(time_signed_buf[3]) << 16 | - static_cast<uint64_t>(time_signed_buf[4]) << 8 | - static_cast<uint64_t>(time_signed_buf[5])); - - const uint16_t fudge = buffer.readUint16(); - - const uint16_t mac_size = buffer.readUint16(); - vector<uint8_t> mac(mac_size); - if (mac_size > 0) { - buffer.readData(&mac[0], mac_size); - } - - const uint16_t original_id = buffer.readUint16(); - const uint16_t error = buffer.readUint16(); - - const uint16_t other_len = buffer.readUint16(); - vector<uint8_t> other_data(other_len); - if (other_len > 0) { - buffer.readData(&other_data[0], other_len); - } - - const Name& canonical_algorithm_name = - (algorithm == TSIGKey::HMACMD5_SHORT_NAME()) ? - TSIGKey::HMACMD5_NAME() : algorithm; - impl_.reset(new TSIGImpl(canonical_algorithm_name, time_signed, fudge, mac, - original_id, error, other_data)); -} - -TSIG::TSIG(const Name& algorithm, uint64_t time_signed, uint16_t fudge, - uint16_t mac_size, const void* mac, uint16_t original_id, - uint16_t error, uint16_t other_len, const void* other_data) : - impl_(NULL) { - // Time Signed is a 48-bit value. - if ((time_signed >> 48) != 0) { - isc_throw(OutOfRange, "TSIG Time Signed is too large: " << - time_signed); - } - if ((mac_size == 0 && mac != NULL) || (mac_size > 0 && mac == NULL)) { - isc_throw(InvalidParameter, "TSIG MAC size and data inconsistent"); - } - if ((other_len == 0 && other_data != NULL) || - (other_len > 0 && other_data == NULL)) { - isc_throw(InvalidParameter, - "TSIG Other data length and data inconsistent"); - } - const Name& canonical_algorithm_name = - (algorithm == TSIGKey::HMACMD5_SHORT_NAME()) ? - TSIGKey::HMACMD5_NAME() : algorithm; - impl_.reset(new TSIGImpl(canonical_algorithm_name, time_signed, fudge, mac_size, - mac, original_id, error, other_len, other_data)); -} - -/// \brief The copy constructor. -/// -/// It internally allocates a resource, and if it fails a corresponding -/// standard exception will be thrown. -/// This constructor never throws an exception otherwise. -TSIG::TSIG(const TSIG& source) : Rdata(), impl_(new TSIGImpl(*source.impl_)) { -} - -TSIG& -TSIG::operator=(const TSIG& source) { - if (this == &source) { - return (*this); - } - - impl_.reset(new TSIGImpl(*source.impl_)); - - return (*this); -} - -TSIG::~TSIG() { -} - -/// \brief Convert the \c TSIG to a string. -/// -/// The output of this method is formatted as described in the "from string" -/// constructor (\c TSIG(const std::string&))). -/// -/// If internal resource allocation fails, a corresponding -/// standard exception will be thrown. -/// -/// \return A \c string object that represents the \c TSIG object. -std::string -TSIG::toText() const { - string result; - - result += impl_->algorithm_.toText() + " " + - lexical_cast<string>(impl_->time_signed_) + " " + - lexical_cast<string>(impl_->fudge_) + " " + - lexical_cast<string>(impl_->mac_.size()) + " "; - if (!impl_->mac_.empty()) { - result += encodeBase64(impl_->mac_) + " "; - } - result += lexical_cast<string>(impl_->original_id_) + " "; - result += TSIGError(impl_->error_).toText() + " "; - result += lexical_cast<string>(impl_->other_data_.size()); - if (!impl_->other_data_.empty()) { - result += " " + encodeBase64(impl_->other_data_); - } - - return (result); -} - -// Common sequence of toWire() operations used for the two versions of -// toWire(). -template <typename Output> -void -TSIGImpl::toWireCommon(Output& output) const { - output.writeUint16(time_signed_ >> 32); - output.writeUint32(time_signed_ & 0xffffffff); - output.writeUint16(fudge_); - const uint16_t mac_size = mac_.size(); - output.writeUint16(mac_size); - if (mac_size > 0) { - output.writeData(&mac_[0], mac_size); - } - output.writeUint16(original_id_); - output.writeUint16(error_); - const uint16_t other_len = other_data_.size(); - output.writeUint16(other_len); - if (other_len > 0) { - output.writeData(&other_data_[0], other_len); - } -} - -/// \brief Render the \c TSIG in the wire format without name compression. -/// -/// If internal resource allocation fails, a corresponding -/// standard exception will be thrown. -/// This method never throws an exception otherwise. -/// -/// \param buffer An output buffer to store the wire data. -void -TSIG::toWire(OutputBuffer& buffer) const { - impl_->algorithm_.toWire(buffer); - impl_->toWireCommon<OutputBuffer>(buffer); -} - -/// \brief Render the \c TSIG in the wire format with taking into account -/// compression. -/// -/// As specified in RFC3597, the Algorithm field (a domain name) will not -/// be compressed. However, the domain name could be a target of compression -/// of other compressible names (though pretty unlikely), the offset -/// information of the algorithm name may be recorded in \c renderer. -/// -/// If internal resource allocation fails, a corresponding -/// standard exception will be thrown. -/// This method never throws an exception otherwise. -/// -/// \param renderer DNS message rendering context that encapsulates the -/// output buffer and name compression information. -void -TSIG::toWire(AbstractMessageRenderer& renderer) const { - renderer.writeName(impl_->algorithm_, false); - impl_->toWireCommon<AbstractMessageRenderer>(renderer); -} - -// A helper function commonly used for TSIG::compare(). -int -vectorComp(const vector<uint8_t>& v1, const vector<uint8_t>& v2) { - const size_t this_size = v1.size(); - const size_t other_size = v2.size(); - if (this_size != other_size) { - return (this_size < other_size ? -1 : 1); - } - if (this_size > 0) { - return (memcmp(&v1[0], &v2[0], this_size)); - } - return (0); -} - -/// \brief Compare two instances of \c TSIG RDATA. -/// -/// This method compares \c this and the \c other \c TSIG objects -/// in terms of the DNSSEC sorting order as defined in RFC4034, and returns -/// the result as an integer. -/// -/// This method is expected to be used in a polymorphic way, and the -/// parameter to compare against is therefore of the abstract \c Rdata class. -/// However, comparing two \c Rdata objects of different RR types -/// is meaningless, and \c other must point to a \c TSIG object; -/// otherwise, the standard \c bad_cast exception will be thrown. -/// This method never throws an exception otherwise. -/// -/// \param other the right-hand operand to compare against. -/// \return < 0 if \c this would be sorted before \c other. -/// \return 0 if \c this is identical to \c other in terms of sorting order. -/// \return > 0 if \c this would be sorted after \c other. -int -TSIG::compare(const Rdata& other) const { - const TSIG& other_tsig = dynamic_cast<const TSIG&>(other); - - const int ncmp = compareNames(impl_->algorithm_, - other_tsig.impl_->algorithm_); - if (ncmp != 0) { - return (ncmp); - } - - if (impl_->time_signed_ != other_tsig.impl_->time_signed_) { - return (impl_->time_signed_ < other_tsig.impl_->time_signed_ ? -1 : 1); - } - if (impl_->fudge_ != other_tsig.impl_->fudge_) { - return (impl_->fudge_ < other_tsig.impl_->fudge_ ? -1 : 1); - } - const int vcmp = vectorComp(impl_->mac_, other_tsig.impl_->mac_); - if (vcmp != 0) { - return (vcmp); - } - if (impl_->original_id_ != other_tsig.impl_->original_id_) { - return (impl_->original_id_ < other_tsig.impl_->original_id_ ? -1 : 1); - } - if (impl_->error_ != other_tsig.impl_->error_) { - return (impl_->error_ < other_tsig.impl_->error_ ? -1 : 1); - } - return (vectorComp(impl_->other_data_, other_tsig.impl_->other_data_)); -} - -const Name& -TSIG::getAlgorithm() const { - return (impl_->algorithm_); -} - -uint64_t -TSIG::getTimeSigned() const { - return (impl_->time_signed_); -} - -uint16_t -TSIG::getFudge() const { - return (impl_->fudge_); -} - -uint16_t -TSIG::getMACSize() const { - return (impl_->mac_.size()); -} - -const void* -TSIG::getMAC() const { - if (!impl_->mac_.empty()) { - return (&impl_->mac_[0]); - } else { - return (NULL); - } -} - -uint16_t -TSIG::getOriginalID() const { - return (impl_->original_id_); -} - -uint16_t -TSIG::getError() const { - return (impl_->error_); -} - -uint16_t -TSIG::getOtherLen() const { - return (impl_->other_data_.size()); -} - -const void* -TSIG::getOtherData() const { - if (!impl_->other_data_.empty()) { - return (&impl_->other_data_[0]); - } else { - return (NULL); - } -} - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/any_255/tsig_250.h b/src/lib/dns/rdata/any_255/tsig_250.h deleted file mode 100644 index 826cdbca54..0000000000 --- a/src/lib/dns/rdata/any_255/tsig_250.h +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -// BEGIN_HEADER_GUARD - -#include <stdint.h> - -#include <string> - -#include <util/buffer.h> -#include <dns/name.h> -#include <dns/rdata.h> -#include <boost/shared_ptr.hpp> - -// BEGIN_ISC_NAMESPACE - -// BEGIN_COMMON_DECLARATIONS -// END_COMMON_DECLARATIONS - -// BEGIN_RDATA_NAMESPACE - -struct TSIGImpl; - -/// \brief \c rdata::TSIG class represents the TSIG RDATA as defined %in -/// RFC2845. -/// -/// This class implements the basic interfaces inherited from the abstract -/// \c rdata::Rdata class, and provides trivial accessors specific to the -/// TSIG RDATA. -class TSIG : public Rdata { -public: - // BEGIN_COMMON_MEMBERS - // END_COMMON_MEMBERS - - /// \brief Constructor from RDATA field parameters. - /// - /// The parameters are a straightforward mapping of %TSIG RDATA - /// fields as defined %in RFC2845, but there are some implementation - /// specific notes as follows. - /// - /// \c algorithm is a \c Name object that specifies the algorithm. - /// For example, if the algorithm is HMAC-SHA256, \c algorithm would be - /// \c Name("hmac-sha256"). - /// - /// \c time_signed corresponds to the Time Signed field, which is of - /// 48-bit unsigned integer type, and therefore cannot exceed 2^48-1; - /// otherwise, an exception of type \c OutOfRange will be thrown. - /// - /// \c mac_size and \c mac correspond to the MAC Size and MAC fields, - /// respectively. When the MAC field is empty, \c mac must be NULL. - /// \c mac_size and \c mac must be consistent %in that \c mac_size is 0 if - /// and only if \c mac is NULL; otherwise an exception of type - /// InvalidParameter will be thrown. - /// - /// The same restriction applies to \c other_len and \c other_data, - /// which correspond to the Other Len and Other Data fields, respectively. - /// - /// This constructor internally involves resource allocation, and if - /// it fails, a corresponding standard exception will be thrown. - TSIG(const Name& algorithm, uint64_t time_signed, uint16_t fudge, - uint16_t mac_size, const void* mac, uint16_t original_id, - uint16_t error, uint16_t other_len, const void* other_data); - - /// \brief Assignment operator. - /// - /// It internally allocates a resource, and if it fails a corresponding - /// standard exception will be thrown. - /// This operator never throws an exception otherwise. - /// - /// This operator provides the strong exception guarantee: When an - /// exception is thrown the content of the assignment target will be - /// intact. - TSIG& operator=(const TSIG& source); - - /// \brief The destructor. - ~TSIG(); - - /// \brief Return the algorithm name. - /// - /// This method never throws an exception. - const Name& getAlgorithm() const; - - /// \brief Return the value of the Time Signed field. - /// - /// The returned value does not exceed 2^48-1. - /// - /// This method never throws an exception. - uint64_t getTimeSigned() const; - - /// \brief Return the value of the Fudge field. - /// - /// This method never throws an exception. - uint16_t getFudge() const; - - /// \brief Return the value of the MAC Size field. - /// - /// This method never throws an exception. - uint16_t getMACSize() const; - - /// \brief Return the value of the MAC field. - /// - /// If the MAC field is empty, it returns NULL. - /// Otherwise, the memory region beginning at the address returned by - /// this method is valid up to the bytes specified by the return value - /// of \c getMACSize(). - /// The memory region is only valid while the corresponding \c TSIG - /// object is valid. The caller must hold the \c TSIG object while - /// it needs to refer to the region or it must make a local copy of the - /// region. - /// - /// This method never throws an exception. - const void* getMAC() const; - - /// \brief Return the value of the Original ID field. - /// - /// This method never throws an exception. - uint16_t getOriginalID() const; - - /// \brief Return the value of the Error field. - /// - /// This method never throws an exception. - uint16_t getError() const; - - /// \brief Return the value of the Other Len field. - /// - /// This method never throws an exception. - uint16_t getOtherLen() const; - - /// \brief Return the value of the Other Data field. - /// - /// The same note as \c getMAC() applies. - /// - /// This method never throws an exception. - const void* getOtherData() const; -private: - boost::shared_ptr<TSIGImpl> constructFromLexer(MasterLexer& lexer, const Name* origin); - - boost::shared_ptr<TSIGImpl> impl_; -}; - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE -// END_HEADER_GUARD diff --git a/src/lib/dns/rdata/generic/detail/.gitignore b/src/lib/dns/rdata/generic/detail/.gitignore deleted file mode 100644 index c83c0130e8..0000000000 --- a/src/lib/dns/rdata/generic/detail/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/.dirstamp diff --git a/src/lib/dns/rdata/generic/detail/lexer_util.h b/src/lib/dns/rdata/generic/detail/lexer_util.h deleted file mode 100644 index 4e3d120c2a..0000000000 --- a/src/lib/dns/rdata/generic/detail/lexer_util.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (C) 2013-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef DNS_RDATA_LEXER_UTIL_H -#define DNS_RDATA_LEXER_UTIL_H - -#include <dns/name.h> -#include <dns/master_lexer.h> - -/// \file lexer_util.h -/// \brief Utilities for extracting RDATA fields from lexer. -/// -/// This file intends to define convenient small routines that can be -/// commonly used in the RDATA implementation to build RDATA fields from -/// a \c MasterLexer. - -namespace isc { -namespace dns { -namespace rdata { -namespace generic { -namespace detail { - -/// \brief Construct a Name object using a master lexer and optional origin. -/// -/// This is a convenient shortcut of commonly used code pattern that would -/// be used to build RDATA that contain a domain name field. -/// -/// Note that this function throws an exception against invalid input. -/// The (direct or indirect) caller's responsibility needs to expect and -/// handle exceptions appropriately. -/// -/// \throw MasterLexer::LexerError The next token from lexer is not string. -/// \throw Other Exceptions from the \c Name class constructor if the next -/// string token from the lexer does not represent a valid name. -/// -/// \param lexer A \c MasterLexer object. Its next token is expected to be -/// a string that represent a domain name. -/// \param origin If non NULL, specifies the origin of the name to be -/// constructed. -/// -/// \return A new Name object that corresponds to the next string token of -/// the \c lexer. -inline Name -createNameFromLexer(MasterLexer& lexer, const Name* origin) { - const MasterToken::StringRegion& str_region = - lexer.getNextToken(MasterToken::STRING).getStringRegion(); - return (Name(str_region.beg, str_region.len, origin)); -} - -} // namespace detail -} // namespace generic -} // namespace rdata -} // namespace dns -} // namespace isc -#endif // DNS_RDATA_LEXER_UTIL_H diff --git a/src/lib/dns/rdata/generic/opt_41.cc b/src/lib/dns/rdata/generic/opt_41.cc deleted file mode 100644 index c382955904..0000000000 --- a/src/lib/dns/rdata/generic/opt_41.cc +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <util/buffer.h> -#include <dns/messagerenderer.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> - -#include <string> -#include <string.h> - -using namespace std; -using namespace isc::util; - -// BEGIN_ISC_NAMESPACE -// BEGIN_RDATA_NAMESPACE - -/// \brief Constructor. -OPT::PseudoRR::PseudoRR(uint16_t code, - boost::shared_ptr<std::vector<uint8_t> >& data) : - code_(code), - data_(data) { -} - -uint16_t -OPT::PseudoRR::getCode() const { - return (code_); -} - -const uint8_t* -OPT::PseudoRR::getData() const { - return (&(*data_)[0]); -} - -uint16_t -OPT::PseudoRR::getLength() const { - return (data_->size()); -} - -struct OPTImpl { - OPTImpl() : - rdlength_(0) { - } - - uint16_t rdlength_; - std::vector<OPT::PseudoRR> pseudo_rrs_; -}; - -/// \brief Default constructor. -OPT::OPT() : - impl_(new OPTImpl()) { -} - -/// \brief Constructor from string. -/// -/// This constructor cannot be used, and always throws an exception. -/// -/// \throw InvalidRdataText OPT RR cannot be constructed from text. -OPT::OPT(const std::string&) : - impl_(NULL) { - isc_throw(InvalidRdataText, "OPT RR cannot be constructed from text"); -} - -/// \brief Constructor with a context of MasterLexer. -/// -/// This constructor cannot be used, and always throws an exception. -/// -/// \throw InvalidRdataText OPT RR cannot be constructed from text. -OPT::OPT(MasterLexer&, const Name*, - MasterLoader::Options, MasterLoaderCallbacks&) : - impl_(NULL) { - isc_throw(InvalidRdataText, "OPT RR cannot be constructed from text"); -} - -OPT::OPT(InputBuffer& buffer, size_t rdata_len) : - impl_(NULL) { - boost::shared_ptr<OPTImpl> impl_ptr(new OPTImpl()); - - while (true) { - if (rdata_len == 0) { - break; - } - - if (rdata_len < 4) { - isc_throw(InvalidRdataLength, - "Pseudo OPT RR record too short: " - << rdata_len << " bytes"); - } - - const uint16_t option_code = buffer.readUint16(); - const uint16_t option_length = buffer.readUint16(); - rdata_len -= 4; - - if (static_cast<uint16_t>(impl_ptr->rdlength_ + option_length) < - impl_ptr->rdlength_) { - isc_throw(InvalidRdataText, - "Option length " << option_length - << " would overflow OPT RR RDLEN (currently " - << impl_ptr->rdlength_ << ")."); - } - - if (rdata_len < option_length) { - isc_throw(InvalidRdataLength, "Corrupt pseudo OPT RR record"); - } - - boost::shared_ptr<std::vector<uint8_t> > - option_data(new std::vector<uint8_t>(option_length)); - buffer.readData(&(*option_data)[0], option_length); - impl_ptr->pseudo_rrs_.push_back(PseudoRR(option_code, option_data)); - impl_ptr->rdlength_ += option_length; - rdata_len -= option_length; - } - - impl_ = impl_ptr; -} - -OPT::OPT(const OPT& other) : - Rdata(), impl_(new OPTImpl(*other.impl_)) { -} - -OPT& -OPT::operator=(const OPT& source) { - if (this == &source) { - return (*this); - } - - OPTImpl* newimpl = new OPTImpl(*source.impl_); - impl_ = newimpl; - - return (*this); -} - -OPT::~OPT() { -} - -std::string -OPT::toText() const { - isc_throw(isc::InvalidOperation, - "OPT RRs do not have a presentation format"); -} - -void -OPT::toWire(OutputBuffer& buffer) const { - for (auto const& pseudo_rr : impl_->pseudo_rrs_) { - buffer.writeUint16(pseudo_rr.getCode()); - const uint16_t length = pseudo_rr.getLength(); - buffer.writeUint16(length); - if (length > 0) { - buffer.writeData(pseudo_rr.getData(), length); - } - } -} - -void -OPT::toWire(AbstractMessageRenderer& renderer) const { - for (auto const& pseudo_rr : impl_->pseudo_rrs_) { - renderer.writeUint16(pseudo_rr.getCode()); - const uint16_t length = pseudo_rr.getLength(); - renderer.writeUint16(length); - if (length > 0) { - renderer.writeData(pseudo_rr.getData(), length); - } - } -} - -int -OPT::compare(const Rdata&) const { - isc_throw(isc::InvalidOperation, - "It is meaningless to compare a set of OPT pseudo RRs; " - "they have unspecified order"); - return (0); -} - -void -OPT::appendPseudoRR(uint16_t code, const uint8_t* data, uint16_t length) { - // See if it overflows 16-bit length field. We only worry about the - // pseudo-RR length here, not the whole message length (which should - // be checked and enforced elsewhere). - if (static_cast<uint16_t>(impl_->rdlength_ + length) < - impl_->rdlength_) { - isc_throw(isc::InvalidParameter, - "Option length " << length - << " would overflow OPT RR RDLEN (currently " - << impl_->rdlength_ << ")."); - } - - boost::shared_ptr<std::vector<uint8_t> > - option_data(new std::vector<uint8_t>(length)); - if (length != 0) { - std::memcpy(&(*option_data)[0], data, length); - } - impl_->pseudo_rrs_.push_back(PseudoRR(code, option_data)); - impl_->rdlength_ += length; -} - -const std::vector<OPT::PseudoRR>& -OPT::getPseudoRRs() const { - return (impl_->pseudo_rrs_); -} - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/opt_41.h b/src/lib/dns/rdata/generic/opt_41.h deleted file mode 100644 index e2d3ccb516..0000000000 --- a/src/lib/dns/rdata/generic/opt_41.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -// BEGIN_HEADER_GUARD - -#include <string> - -#include <dns/rdata.h> - -#include <boost/shared_ptr.hpp> - -#include <vector> - -// BEGIN_ISC_NAMESPACE - -// BEGIN_COMMON_DECLARATIONS -// END_COMMON_DECLARATIONS - -// BEGIN_RDATA_NAMESPACE - -struct OPTImpl; - -class OPT : public Rdata { -public: - // BEGIN_COMMON_MEMBERS - // END_COMMON_MEMBERS - - // The default constructor makes sense for OPT as it can be empty. - OPT(); - OPT& operator=(const OPT& source); - ~OPT(); - - /// \brief A class representing a pseudo RR (or option) within an - /// OPT RR (see RFC 6891). - class PseudoRR { - public: - /// \brief Constructor. - /// \param code The OPTION-CODE field of the pseudo RR. - /// \param data The OPTION-DATA field of the pseudo - /// RR. OPTION-LENGTH is set to the length of this vector. - PseudoRR(uint16_t code, - boost::shared_ptr<std::vector<uint8_t> >& data); - - /// \brief Return the option code of this pseudo RR. - uint16_t getCode() const; - - /// \brief Return the option data of this pseudo RR. - const uint8_t* getData() const; - - /// \brief Return the length of the option data of this - /// pseudo RR. - uint16_t getLength() const; - - private: - uint16_t code_; - boost::shared_ptr<std::vector<uint8_t> > data_; - }; - - /// \brief Append a pseudo RR (option) in this OPT RR. - /// - /// \param code The OPTION-CODE field of the pseudo RR. - /// \param data The OPTION-DATA field of the pseudo RR. - /// \param length The size of the \c data argument. OPTION-LENGTH is - /// set to this size. - /// \throw isc::InvalidParameter if this pseudo RR would cause - /// the OPT RDATA to overflow its RDLENGTH. - void appendPseudoRR(uint16_t code, const uint8_t* data, uint16_t length); - - /// \brief Return a vector of the pseudo RRs (options) in this - /// OPT RR. - /// - /// Note: The returned reference is only valid during the lifetime - /// of this \c generic::OPT object. It should not be used - /// afterwards. - const std::vector<PseudoRR>& getPseudoRRs() const; - -private: - boost::shared_ptr<OPTImpl> impl_; -}; - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE -// END_HEADER_GUARD - -// Local Variables: -// mode: c++ -// End: diff --git a/src/lib/dns/rdata/generic/ptr_12.cc b/src/lib/dns/rdata/generic/ptr_12.cc deleted file mode 100644 index d33a6d8a45..0000000000 --- a/src/lib/dns/rdata/generic/ptr_12.cc +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <string> - -#include <util/buffer.h> -#include <dns/name.h> -#include <dns/messagerenderer.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> - -#include <dns/rdata/generic/detail/lexer_util.h> - -using namespace std; -using namespace isc::util; -using isc::dns::rdata::generic::detail::createNameFromLexer; - -// BEGIN_ISC_NAMESPACE -// BEGIN_RDATA_NAMESPACE - -/// \brief Constructor from string. -/// -/// The given string must represent a valid PTR RDATA. There can be -/// extra space characters at the beginning or end of the text (which -/// are simply ignored), but other extra text, including a new line, -/// will make the construction fail with an exception. -/// -/// The PTRDNAME must be absolute since there's no parameter that -/// specifies the origin name; if it is not absolute, \c -/// MissingNameOrigin exception will be thrown. These must not be -/// represented as a quoted string. -/// -/// \throw Others Exception from the Name and RRTTL constructors. -/// \throw InvalidRdataText Other general syntax errors. -PTR::PTR(const std::string& type_str) : - // Fill in dummy name and replace them soon below. - ptr_name_(Name::ROOT_NAME()) { - try { - std::istringstream ss(type_str); - MasterLexer lexer; - lexer.pushSource(ss); - - ptr_name_ = createNameFromLexer(lexer, NULL); - - if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { - isc_throw(InvalidRdataText, "extra input text for PTR: " - << type_str); - } - } catch (const MasterLexer::LexerError& ex) { - isc_throw(InvalidRdataText, "Failed to construct PTR from '" << - type_str << "': " << ex.what()); - } -} - -PTR::PTR(InputBuffer& buffer, size_t) : - ptr_name_(buffer) { - // we don't need rdata_len for parsing. if necessary, the caller will - // check consistency. -} - -/// \brief Constructor with a context of MasterLexer. -/// -/// The \c lexer should point to the beginning of valid textual -/// representation of a PTR RDATA. The PTRDNAME field can be -/// non-absolute if \c origin is non-NULL, in which case \c origin is -/// used to make it absolute. It must not be represented as a quoted -/// string. -/// -/// \throw MasterLexer::LexerError General parsing error such as missing field. -/// \throw Other Exceptions from the Name and RRTTL constructors if -/// construction of textual fields as these objects fail. -/// -/// \param lexer A \c MasterLexer object parsing a master file for the -/// RDATA to be created -/// \param origin If non NULL, specifies the origin of PTRDNAME when it -/// is non-absolute. -PTR::PTR(MasterLexer& lexer, const Name* origin, - MasterLoader::Options, MasterLoaderCallbacks&) : - ptr_name_(createNameFromLexer(lexer, origin)) { -} - -PTR::PTR(const PTR& source) : - Rdata(), ptr_name_(source.ptr_name_) { -} - -std::string -PTR::toText() const { - return (ptr_name_.toText()); -} - -void -PTR::toWire(OutputBuffer& buffer) const { - ptr_name_.toWire(buffer); -} - -void -PTR::toWire(AbstractMessageRenderer& renderer) const { - renderer.writeName(ptr_name_); -} - -int -PTR::compare(const Rdata& other) const { - // The compare method normally begins with this dynamic cast. - const PTR& other_ptr = dynamic_cast<const PTR&>(other); - - return (compareNames(ptr_name_, other_ptr.ptr_name_)); -} - -const Name& -PTR::getPTRName() const { - return (ptr_name_); -} - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/ptr_12.h b/src/lib/dns/rdata/generic/ptr_12.h deleted file mode 100644 index 101d60c263..0000000000 --- a/src/lib/dns/rdata/generic/ptr_12.h +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -// BEGIN_HEADER_GUARD - -#include <string> - -#include <dns/name.h> -#include <dns/rdata.h> - -// BEGIN_ISC_NAMESPACE - -// BEGIN_COMMON_DECLARATIONS -// END_COMMON_DECLARATIONS - -// BEGIN_RDATA_NAMESPACE - -class PTR : public Rdata { -public: - // BEGIN_COMMON_MEMBERS - // END_COMMON_MEMBERS - - /// - /// Specialized constructor - /// - explicit PTR(const Name& ptr_name) : ptr_name_(ptr_name) { - } - /// - /// Specialized methods - /// - const Name& getPTRName() const; -private: - Name ptr_name_; -}; - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE -// END_HEADER_GUARD diff --git a/src/lib/dns/rdata/generic/rrsig_46.cc b/src/lib/dns/rdata/generic/rrsig_46.cc deleted file mode 100644 index 94d62f5e16..0000000000 --- a/src/lib/dns/rdata/generic/rrsig_46.cc +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <string> -#include <iomanip> -#include <iostream> -#include <sstream> -#include <vector> - -#include <boost/lexical_cast.hpp> - -#include <util/encode/encode.h> -#include <util/buffer.h> -#include <util/time_utilities.h> -#include <dns/messagerenderer.h> -#include <dns/name.h> -#include <dns/rrtype.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> -#include <dns/rdata/generic/detail/lexer_util.h> - -#include <stdio.h> -#include <time.h> - -using namespace std; -using namespace isc::util; -using namespace isc::util::encode; -using isc::dns::rdata::generic::detail::createNameFromLexer; - -// BEGIN_ISC_NAMESPACE -// BEGIN_RDATA_NAMESPACE - -namespace { -// This is the minimum necessary length of all wire-format RRSIG RDATA: -// - two 8-bit fields (algorithm and labels) -// - two 16-bit fields (covered and tag) -// - three 32-bit fields (original TTL, expire and inception) -const size_t RRSIG_MINIMUM_LEN = 2 * sizeof(uint8_t) + 2 * sizeof(uint16_t) + - 3 * sizeof(uint32_t); -} - -struct RRSIGImpl { - // straightforward representation of RRSIG RDATA fields - RRSIGImpl(const RRType& covered, uint8_t algorithm, uint8_t labels, - uint32_t originalttl, uint32_t timeexpire, - uint32_t timeinception, uint16_t tag, const Name& signer, - const vector<uint8_t>& signature) : - covered_(covered), algorithm_(algorithm), labels_(labels), - originalttl_(originalttl), timeexpire_(timeexpire), - timeinception_(timeinception), tag_(tag), signer_(signer), - signature_(signature) { - } - - const RRType covered_; - uint8_t algorithm_; - uint8_t labels_; - uint32_t originalttl_; - uint32_t timeexpire_; - uint32_t timeinception_; - uint16_t tag_; - const Name signer_; - const vector<uint8_t> signature_; -}; - -// helper function for string and lexer constructors -boost::shared_ptr<RRSIGImpl> -RRSIG::constructFromLexer(MasterLexer& lexer, const Name* origin) { - const RRType covered(lexer.getNextToken(MasterToken::STRING).getString()); - const uint32_t algorithm = - lexer.getNextToken(MasterToken::NUMBER).getNumber(); - if (algorithm > 0xff) { - isc_throw(InvalidRdataText, "RRSIG algorithm out of range"); - } - const uint32_t labels = - lexer.getNextToken(MasterToken::NUMBER).getNumber(); - if (labels > 0xff) { - isc_throw(InvalidRdataText, "RRSIG labels out of range"); - } - const uint32_t originalttl = - lexer.getNextToken(MasterToken::NUMBER).getNumber(); - const uint32_t timeexpire = - timeFromText32(lexer.getNextToken(MasterToken::STRING).getString()); - const uint32_t timeinception = - timeFromText32(lexer.getNextToken(MasterToken::STRING).getString()); - const uint32_t tag = - lexer.getNextToken(MasterToken::NUMBER).getNumber(); - if (tag > 0xffff) { - isc_throw(InvalidRdataText, "RRSIG key tag out of range"); - } - const Name& signer = createNameFromLexer(lexer, origin); - - string signature_txt; - string signature_part; - // Whitespace is allowed within base64 text, so read to the end of input. - while (true) { - const MasterToken& token = - lexer.getNextToken(MasterToken::STRING, true); - if ((token.getType() == MasterToken::END_OF_FILE) || - (token.getType() == MasterToken::END_OF_LINE)) { - break; - } - token.getString(signature_part); - signature_txt.append(signature_part); - } - lexer.ungetToken(); - - vector<uint8_t> signature; - // missing signature is okay - if (signature_txt.size() > 0) { - decodeBase64(signature_txt, signature); - } - - return (boost::shared_ptr<RRSIGImpl>(new RRSIGImpl(covered, algorithm, labels, - originalttl, timeexpire, timeinception, - static_cast<uint16_t>(tag), signer, signature))); -} - -/// \brief Constructor from string. -/// -/// The given string must represent a valid RRSIG RDATA. There can be extra -/// space characters at the beginning or end of the text (which are simply -/// ignored), but other extra text, including a new line, will make the -/// construction fail with an exception. -/// -/// The Signer's Name must be absolute since there's no parameter that -/// specifies the origin name; if this is not absolute, \c MissingNameOrigin -/// exception will be thrown. This must not be represented as a quoted -/// string. -/// -/// See the construction that takes \c MasterLexer for other fields. -/// -/// \throw Others Exception from the Name constructor. -/// \throw InvalidRdataText Other general syntax errors. -RRSIG::RRSIG(const std::string& rrsig_str) : - impl_(NULL) { - // We use unique_ptr here because if there is an exception in this - // constructor, the destructor is not called and there could be a - // leak of the RRSIGImpl that constructFromLexer() returns. - boost::shared_ptr<RRSIGImpl> impl_ptr; - - try { - std::istringstream iss(rrsig_str); - MasterLexer lexer; - lexer.pushSource(iss); - - impl_ptr.reset(constructFromLexer(lexer, NULL)); - - if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { - isc_throw(InvalidRdataText, "extra input text for RRSIG: " - << rrsig_str); - } - } catch (const MasterLexer::LexerError& ex) { - isc_throw(InvalidRdataText, "Failed to construct RRSIG from '" << - rrsig_str << "': " << ex.what()); - } - - impl_ = impl_ptr; -} - -/// \brief Constructor with a context of MasterLexer. -/// -/// The \c lexer should point to the beginning of valid textual representation -/// of an RRSIG RDATA. The Signer's Name fields can be non absolute if \c -/// origin is non NULL, in which case \c origin is used to make it absolute. -/// This must not be represented as a quoted string. -/// -/// The Original TTL field is a valid decimal representation of an unsigned -/// 32-bit integer. Note that alternate textual representations of \c RRTTL, -/// such as "1H" for 3600 seconds, are not allowed here. -/// -/// \throw MasterLexer::LexerError General parsing error such as missing field. -/// \throw Other Exceptions from the Name constructor if -/// construction of textual fields as these objects fail. -/// -/// \param lexer A \c MasterLexer object parsing a master file for the -/// RDATA to be created -/// \param origin If non NULL, specifies the origin of Signer's Name when -/// it is non absolute. -RRSIG::RRSIG(MasterLexer& lexer, const Name* origin, - MasterLoader::Options, MasterLoaderCallbacks&) : - impl_(constructFromLexer(lexer, origin)) { -} - -RRSIG::RRSIG(InputBuffer& buffer, size_t rdata_len) { - size_t pos = buffer.getPosition(); - - if (rdata_len < RRSIG_MINIMUM_LEN) { - isc_throw(InvalidRdataLength, "RRSIG too short"); - } - - RRType covered(buffer); - uint8_t algorithm = buffer.readUint8(); - uint8_t labels = buffer.readUint8(); - uint32_t originalttl = buffer.readUint32(); - uint32_t timeexpire = buffer.readUint32(); - uint32_t timeinception = buffer.readUint32(); - uint16_t tag = buffer.readUint16(); - Name signer(buffer); - - // rdata_len must be sufficiently large to hold non empty signature data. - if (rdata_len <= buffer.getPosition() - pos) { - isc_throw(InvalidRdataLength, "RRSIG too short"); - } - rdata_len -= (buffer.getPosition() - pos); - - vector<uint8_t> signature(rdata_len); - buffer.readData(&signature[0], rdata_len); - - impl_.reset(new RRSIGImpl(covered, algorithm, labels, - originalttl, timeexpire, timeinception, tag, - signer, signature)); -} - -RRSIG::RRSIG(const RRSIG& source) : - Rdata(), impl_(new RRSIGImpl(*source.impl_)) { -} - -RRSIG& -RRSIG::operator=(const RRSIG& source) { - if (this == &source) { - return (*this); - } - - impl_.reset(new RRSIGImpl(*source.impl_)); - - return (*this); -} - -RRSIG::~RRSIG() { -} - -string -RRSIG::toText() const { - return (impl_->covered_.toText() + - " " + boost::lexical_cast<string>(static_cast<int>(impl_->algorithm_)) - + " " + boost::lexical_cast<string>(static_cast<int>(impl_->labels_)) - + " " + boost::lexical_cast<string>(impl_->originalttl_) - + " " + timeToText32(impl_->timeexpire_) - + " " + timeToText32(impl_->timeinception_) - + " " + boost::lexical_cast<string>(impl_->tag_) - + " " + impl_->signer_.toText() - + " " + encodeBase64(impl_->signature_)); -} - -void -RRSIG::toWire(OutputBuffer& buffer) const { - impl_->covered_.toWire(buffer); - buffer.writeUint8(impl_->algorithm_); - buffer.writeUint8(impl_->labels_); - buffer.writeUint32(impl_->originalttl_); - buffer.writeUint32(impl_->timeexpire_); - buffer.writeUint32(impl_->timeinception_); - buffer.writeUint16(impl_->tag_); - impl_->signer_.toWire(buffer); - buffer.writeData(&impl_->signature_[0], impl_->signature_.size()); -} - -void -RRSIG::toWire(AbstractMessageRenderer& renderer) const { - impl_->covered_.toWire(renderer); - renderer.writeUint8(impl_->algorithm_); - renderer.writeUint8(impl_->labels_); - renderer.writeUint32(impl_->originalttl_); - renderer.writeUint32(impl_->timeexpire_); - renderer.writeUint32(impl_->timeinception_); - renderer.writeUint16(impl_->tag_); - renderer.writeName(impl_->signer_, false); - renderer.writeData(&impl_->signature_[0], impl_->signature_.size()); -} - -int -RRSIG::compare(const Rdata& other) const { - const RRSIG& other_rrsig = dynamic_cast<const RRSIG&>(other); - - if (impl_->covered_.getCode() != other_rrsig.impl_->covered_.getCode()) { - return (impl_->covered_.getCode() < - other_rrsig.impl_->covered_.getCode() ? -1 : 1); - } - if (impl_->algorithm_ != other_rrsig.impl_->algorithm_) { - return (impl_->algorithm_ < other_rrsig.impl_->algorithm_ ? -1 : 1); - } - if (impl_->labels_ != other_rrsig.impl_->labels_) { - return (impl_->labels_ < other_rrsig.impl_->labels_ ? -1 : 1); - } - if (impl_->originalttl_ != other_rrsig.impl_->originalttl_) { - return (impl_->originalttl_ < other_rrsig.impl_->originalttl_ ? - -1 : 1); - } - if (impl_->timeexpire_ != other_rrsig.impl_->timeexpire_) { - return (impl_->timeexpire_ < other_rrsig.impl_->timeexpire_ ? - -1 : 1); - } - if (impl_->timeinception_ != other_rrsig.impl_->timeinception_) { - return (impl_->timeinception_ < other_rrsig.impl_->timeinception_ ? - -1 : 1); - } - if (impl_->tag_ != other_rrsig.impl_->tag_) { - return (impl_->tag_ < other_rrsig.impl_->tag_ ? -1 : 1); - } - - int cmp = compareNames(impl_->signer_, other_rrsig.impl_->signer_); - if (cmp != 0) { - return (cmp); - } - - size_t this_len = impl_->signature_.size(); - size_t other_len = other_rrsig.impl_->signature_.size(); - size_t cmplen = min(this_len, other_len); - cmp = memcmp(&impl_->signature_[0], &other_rrsig.impl_->signature_[0], - cmplen); - if (cmp != 0) { - return (cmp); - } else { - return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); - } -} - -const RRType& -RRSIG::typeCovered() const { - return (impl_->covered_); -} - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/rrsig_46.h b/src/lib/dns/rdata/generic/rrsig_46.h deleted file mode 100644 index 8443ad79e8..0000000000 --- a/src/lib/dns/rdata/generic/rrsig_46.h +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <stdint.h> - -#include <string> - -#include <dns/name.h> -#include <dns/rrtype.h> -#include <dns/rdata.h> -#include <boost/shared_ptr.hpp> - -// BEGIN_HEADER_GUARD - -// BEGIN_ISC_NAMESPACE - -// BEGIN_COMMON_DECLARATIONS -// END_COMMON_DECLARATIONS - -// BEGIN_RDATA_NAMESPACE - -struct RRSIGImpl; - -/// \brief \c rdata::RRSIG class represents the RRSIG RDATA as defined %in -/// RFC4034. -/// -/// This class implements the basic interfaces inherited from the abstract -/// \c rdata::Rdata class, and provides trivial accessors specific to the -/// RRSIG RDATA. -class RRSIG : public Rdata { -public: - // BEGIN_COMMON_MEMBERS - // END_COMMON_MEMBERS - RRSIG& operator=(const RRSIG& source); - ~RRSIG(); - - // specialized methods - const RRType& typeCovered() const; -private: - // helper function for string and lexer constructors - boost::shared_ptr<RRSIGImpl> constructFromLexer(MasterLexer& lexer, const Name* origin); - - boost::shared_ptr<RRSIGImpl> impl_; -}; - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE -// END_HEADER_GUARD diff --git a/src/lib/dns/rdata/generic/soa_6.cc b/src/lib/dns/rdata/generic/soa_6.cc deleted file mode 100644 index 945d4b99cd..0000000000 --- a/src/lib/dns/rdata/generic/soa_6.cc +++ /dev/null @@ -1,205 +0,0 @@ -// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <exceptions/exceptions.h> - -#include <util/buffer.h> -#include <dns/name.h> -#include <dns/master_lexer.h> -#include <dns/master_loader.h> -#include <dns/master_loader_callbacks.h> -#include <dns/messagerenderer.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> - -#include <dns/rdata/generic/detail/lexer_util.h> - -#include <boost/static_assert.hpp> -#include <boost/lexical_cast.hpp> - -#include <string> -#include <sstream> - -using namespace std; -using boost::lexical_cast; -using namespace isc::util; -using isc::dns::rdata::generic::detail::createNameFromLexer; - -// BEGIN_ISC_NAMESPACE -// BEGIN_RDATA_NAMESPACE - -SOA::SOA(InputBuffer& buffer, size_t) : - mname_(buffer), rname_(buffer) { - // we don't need rdata_len for parsing. if necessary, the caller will - // check consistency. - buffer.readData(numdata_, sizeof(numdata_)); -} - -namespace { -void -fillParameters(MasterLexer& lexer, uint8_t numdata[20]) { - // Copy serial, refresh, retry, expire, minimum. We accept the extended - // TTL-compatible style for the latter four. - OutputBuffer buffer(20); - buffer.writeUint32(lexer.getNextToken(MasterToken::NUMBER).getNumber()); - for (int i = 0; i < 4; ++i) { - buffer.writeUint32(RRTTL(lexer.getNextToken(MasterToken::STRING). - getString()).getValue()); - } - memcpy(numdata, buffer.getData(), buffer.getLength()); -} -} - -/// \brief Constructor from string. -/// -/// The given string must represent a valid SOA RDATA. There can be extra -/// space characters at the beginning or end of the text (which are simply -/// ignored), but other extra text, including a new line, will make the -/// construction fail with an exception. -/// -/// The MNAME and RNAME must be absolute since there's no parameter that -/// specifies the origin name; if these are not absolute, \c MissingNameOrigin -/// exception will be thrown. These must not be represented as a quoted -/// string. -/// -/// See the construction that takes \c MasterLexer for other fields. -/// -/// \throw Others Exception from the Name and RRTTL constructors. -/// \throw InvalidRdataText Other general syntax errors. -SOA::SOA(const std::string& soastr) : - // Fill in dummy name and replace them soon below. - mname_(Name::ROOT_NAME()), rname_(Name::ROOT_NAME()) { - try { - std::istringstream ss(soastr); - MasterLexer lexer; - lexer.pushSource(ss); - - mname_ = createNameFromLexer(lexer, NULL); - rname_ = createNameFromLexer(lexer, NULL); - fillParameters(lexer, numdata_); - - if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { - isc_throw(InvalidRdataText, "extra input text for SOA: " - << soastr); - } - } catch (const MasterLexer::LexerError& ex) { - isc_throw(InvalidRdataText, "Failed to construct SOA from '" << - soastr << "': " << ex.what()); - } -} - -/// \brief Constructor with a context of MasterLexer. -/// -/// The \c lexer should point to the beginning of valid textual representation -/// of an SOA RDATA. The MNAME and RNAME fields can be non absolute if -/// \c origin is non NULL, in which case \c origin is used to make them -/// absolute. These must not be represented as a quoted string. -/// -/// The REFRESH, RETRY, EXPIRE, and MINIMUM fields can be either a valid -/// decimal representation of an unsigned 32-bit integer or other -/// valid textual representation of \c RRTTL such as "1H" (which means 3600). -/// -/// \throw MasterLexer::LexerError General parsing error such as missing field. -/// \throw Other Exceptions from the Name and RRTTL constructors if -/// construction of textual fields as these objects fail. -/// -/// \param lexer A \c MasterLexer object parsing a master file for the -/// RDATA to be created -/// \param origin If non NULL, specifies the origin of MNAME and RNAME when -/// they are non absolute. -SOA::SOA(MasterLexer& lexer, const Name* origin, - MasterLoader::Options, MasterLoaderCallbacks&) : - mname_(createNameFromLexer(lexer, origin)), - rname_(createNameFromLexer(lexer, origin)) { - fillParameters(lexer, numdata_); -} - -SOA::SOA(const Name& mname, const Name& rname, uint32_t serial, - uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum) : - mname_(mname), rname_(rname) { - OutputBuffer b(20); - b.writeUint32(serial); - b.writeUint32(refresh); - b.writeUint32(retry); - b.writeUint32(expire); - b.writeUint32(minimum); - assert(b.getLength() == sizeof(numdata_)); - memcpy(numdata_, b.getData(), sizeof(numdata_)); -} - -SOA::SOA(const SOA& other) : - Rdata(), mname_(other.mname_), rname_(other.rname_) { - memcpy(numdata_, other.numdata_, sizeof(numdata_)); -} - -void -SOA::toWire(OutputBuffer& buffer) const { - mname_.toWire(buffer); - rname_.toWire(buffer); - buffer.writeData(numdata_, sizeof(numdata_)); -} - -void -SOA::toWire(AbstractMessageRenderer& renderer) const { - renderer.writeName(mname_); - renderer.writeName(rname_); - renderer.writeData(numdata_, sizeof(numdata_)); -} - -Serial -SOA::getSerial() const { - InputBuffer b(numdata_, sizeof(numdata_)); - return (Serial(b.readUint32())); -} - -uint32_t -SOA::getMinimum() const { - // Make sure the buffer access is safe. - BOOST_STATIC_ASSERT(sizeof(numdata_) == - sizeof(uint32_t) * 4 + sizeof(uint32_t)); - - InputBuffer b(&numdata_[sizeof(uint32_t) * 4], sizeof(uint32_t)); - return (b.readUint32()); -} - -string -SOA::toText() const { - InputBuffer b(numdata_, sizeof(numdata_)); - uint32_t serial = b.readUint32(); - uint32_t refresh = b.readUint32(); - uint32_t retry = b.readUint32(); - uint32_t expire = b.readUint32(); - uint32_t minimum = b.readUint32(); - - return (mname_.toText() + " " + rname_.toText() + " " + - lexical_cast<string>(serial) + " " + - lexical_cast<string>(refresh) + " " + - lexical_cast<string>(retry) + " " + - lexical_cast<string>(expire) + " " + - lexical_cast<string>(minimum)); -} - -int -SOA::compare(const Rdata& other) const { - const SOA& other_soa = dynamic_cast<const SOA&>(other); - - int order = compareNames(mname_, other_soa.mname_); - if (order != 0) { - return (order); - } - - order = compareNames(rname_, other_soa.rname_); - if (order != 0) { - return (order); - } - - return (memcmp(numdata_, other_soa.numdata_, sizeof(numdata_))); -} - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/soa_6.h b/src/lib/dns/rdata/generic/soa_6.h deleted file mode 100644 index 509663311f..0000000000 --- a/src/lib/dns/rdata/generic/soa_6.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -// BEGIN_HEADER_GUARD - -#include <string> - -#include <dns/name.h> -#include <dns/rdata.h> -#include <dns/rrttl.h> -#include <dns/serial.h> - -// BEGIN_ISC_NAMESPACE - -// BEGIN_COMMON_DECLARATIONS -// END_COMMON_DECLARATIONS - -// BEGIN_RDATA_NAMESPACE - -class SOA : public Rdata { -public: - // BEGIN_COMMON_MEMBERS - // END_COMMON_MEMBERS - - SOA(const Name& mname, const Name& rname, uint32_t serial, - uint32_t refresh, uint32_t retry, uint32_t expire, - uint32_t minimum); - - /// \brief Returns the serial stored in the SOA. - Serial getSerial() const; - - /// brief Returns the minimum TTL field value of the SOA. - uint32_t getMinimum() const; -private: - /// Note: this is a prototype version; we may reconsider - /// this representation later. - Name mname_; - Name rname_; - /// serial, refresh, retry, expire, minimum, stored in network byte order - uint8_t numdata_[20]; -}; - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE -// END_HEADER_GUARD diff --git a/src/lib/dns/rdata/generic/tkey_249.cc b/src/lib/dns/rdata/generic/tkey_249.cc deleted file mode 100644 index d12e2c6295..0000000000 --- a/src/lib/dns/rdata/generic/tkey_249.cc +++ /dev/null @@ -1,608 +0,0 @@ -// Copyright (C) 2021-2024 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <string> -#include <sstream> -#include <vector> - -#include <boost/lexical_cast.hpp> - -#include <util/buffer.h> -#include <util/encode/encode.h> -#include <util/time_utilities.h> - -#include <dns/tsigerror.h> -#include <dns/messagerenderer.h> -#include <dns/name.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> -#include <dns/rcode.h> -#include <dns/rdata/generic/detail/lexer_util.h> - -using namespace std; -using boost::lexical_cast; -using namespace isc::util; -using namespace isc::util::encode; -using namespace isc::dns; -using isc::dns::rdata::generic::detail::createNameFromLexer; - -// BEGIN_ISC_NAMESPACE -// BEGIN_RDATA_NAMESPACE - -const uint16_t TKEY::GSS_API_MODE = 3; - -// straightforward representation of TKEY RDATA fields -struct TKEYImpl { - /// \brief Constructor from RDATA field parameters. - /// - /// \param algorithm The DNS name of the algorithm e.g. gss-tsig. - /// \param inception The inception time (in seconds since 1970). - /// \param expire The expire time (in seconds since 1970). - /// \param mode The mode e.g. Diffie-Hellman (2) or GSS-API (3). - /// \param error The error code (extended error space shared with TSIG). - /// \param key The key (can be empty). - /// \param other_data The other data (can be and usually is empty). - TKEYImpl(const Name& algorithm, uint32_t inception, uint32_t expire, - uint16_t mode, uint16_t error, vector<uint8_t>& key, - vector<uint8_t>& other_data) : - algorithm_(algorithm), inception_(inception), expire_(expire), - mode_(mode), error_(error), key_(key), other_data_(other_data) { - } - - /// \brief Constructor from RDATA field parameters. - /// - /// \param algorithm The DNS name of the algorithm e.g. gss-tsig. - /// \param inception The inception time (in seconds since 1970). - /// \param expire The expire time (in seconds since 1970). - /// \param mode The mode e.g. Diffie-Hellman (2) or GSS-API (3). - /// \param error The error code (extended error space shared with TSIG). - /// \param key_len The key length (0 means no key). - /// \param key The key (can be 0). - /// \param other_len The other data length (0 means no other data). - /// \param other_data The other data (can be and usually is 0). - TKEYImpl(const Name& algorithm, uint32_t inception, uint32_t expire, - uint16_t mode, uint16_t error, size_t key_len, - const void* key, size_t other_len, const void* other_data) : - algorithm_(algorithm), inception_(inception), expire_(expire), - mode_(mode), error_(error), - key_(key_len > 0 ? - vector<uint8_t>(static_cast<const uint8_t*>(key), - static_cast<const uint8_t*>(key) + key_len) : - vector<uint8_t>(key_len)), - other_data_(other_len > 0 ? - vector<uint8_t>(static_cast<const uint8_t*>(other_data), - static_cast<const uint8_t*>(other_data) + - other_len) : - vector<uint8_t>(other_len)) { - } - - /// \brief Common part of toWire methods. - /// \tparam Output \c OutputBuffer or \c AbstractMessageRenderer. - template <typename Output> - void toWireCommon(Output& output) const; - - /// \brief The DNS name of the algorithm e.g. gss-tsig. - const Name algorithm_; - - /// \brief The inception time (in seconds since 1970). - const uint32_t inception_; - - /// \brief The expire time (in seconds since 1970). - const uint32_t expire_; - - /// \brief The mode e.g. Diffie-Hellman (2) or GSS-API (3). - const uint16_t mode_; - - /// \brief The error code (extended error space shared with TSIG). - const uint16_t error_; - - /// \brief The key (can be empty). - const vector<uint8_t> key_; - - /// \brief The other data (can be and usually is empty). - const vector<uint8_t> other_data_; -}; - -// helper function for string and lexer constructors -boost::shared_ptr<TKEYImpl> -TKEY::constructFromLexer(MasterLexer& lexer, const Name* origin) { - const Name& algorithm = - createNameFromLexer(lexer, origin ? origin : &Name::ROOT_NAME()); - - const uint32_t inception = - timeFromText32(lexer.getNextToken(MasterToken::STRING).getString()); - - const uint32_t expire = - timeFromText32(lexer.getNextToken(MasterToken::STRING).getString()); - - /// The mode is either a mnemonic (only one is defined: GSS-API) or - /// a number. - const string& mode_txt = - lexer.getNextToken(MasterToken::STRING).getString(); - uint32_t mode = 0; - if (mode_txt == "GSS-API") { - mode = GSS_API_MODE; - } else { - /// we cast to uint32_t and range-check, because casting directly to - /// uint16_t will convert negative numbers to large positive numbers - try { - mode = boost::lexical_cast<uint32_t>(mode_txt); - } catch (const boost::bad_lexical_cast&) { - isc_throw(InvalidRdataText, "Invalid TKEY Mode"); - } - if (mode > 0xffff) { - isc_throw(InvalidRdataText, "TKEY Mode out of range"); - } - } - - const string& error_txt = - lexer.getNextToken(MasterToken::STRING).getString(); - uint32_t error = 0; - // XXX: In the initial implementation we hardcode the mnemonics. - // We'll soon generalize this. - if (error_txt == "NOERROR") { - error = Rcode::NOERROR_CODE; - } else if (error_txt == "BADSIG") { - error = TSIGError::BAD_SIG_CODE; - } else if (error_txt == "BADKEY") { - error = TSIGError::BAD_KEY_CODE; - } else if (error_txt == "BADTIME") { - error = TSIGError::BAD_TIME_CODE; - } else if (error_txt == "BADMODE") { - error = TSIGError::BAD_MODE_CODE; - } else if (error_txt == "BADNAME") { - error = TSIGError::BAD_NAME_CODE; - } else if (error_txt == "BADALG") { - error = TSIGError::BAD_ALG_CODE; - } else if (error_txt == "BADTRUNC") { - error = TSIGError::BAD_TRUNC_CODE; - } else { - /// we cast to uint32_t and range-check, because casting directly to - /// uint16_t will convert negative numbers to large positive numbers - try { - error = boost::lexical_cast<uint32_t>(error_txt); - } catch (const boost::bad_lexical_cast&) { - isc_throw(InvalidRdataText, "Invalid TKEY Error"); - } - if (error > 0xffff) { - isc_throw(InvalidRdataText, "TKEY Error out of range"); - } - } - - const uint32_t keylen = - lexer.getNextToken(MasterToken::NUMBER).getNumber(); - if (keylen > 0xffff) { - isc_throw(InvalidRdataText, "TKEY Key Len out of range"); - } - const string keydata_txt = (keylen > 0) ? - lexer.getNextToken(MasterToken::STRING).getString() : ""; - vector<uint8_t> key_data; - decodeBase64(keydata_txt, key_data); - if (key_data.size() != keylen) { - isc_throw(InvalidRdataText, - "TKEY Key Data length does not match Other Len"); - } - - const uint32_t otherlen = - lexer.getNextToken(MasterToken::NUMBER).getNumber(); - if (otherlen > 0xffff) { - isc_throw(InvalidRdataText, "TKEY Other Len out of range"); - } - const string otherdata_txt = (otherlen > 0) ? - lexer.getNextToken(MasterToken::STRING).getString() : ""; - vector<uint8_t> other_data; - decodeBase64(otherdata_txt, other_data); - if (other_data.size() != otherlen) { - isc_throw(InvalidRdataText, - "TKEY Other Data length does not match Other Len"); - } - // RFC2845 says Other Data is "empty unless Error == BADTIME". - // However, we don't enforce that. - - return (new boost::shared_ptr<TKEYImpl>(algorithm, inception, expire, mode, error, - key_data, other_data)); -} - -/// \brief Constructor from string. -/// -/// The given string must represent a valid TKEY RDATA. There can be extra -/// space characters at the beginning or end of the text (which are simply -/// ignored), but other extra text, including a new line, will make the -/// construction fail with an exception. -/// -/// \c tkey_str must be formatted as follows: -/// \code <Algorithm Name> <Inception> <Expire> <Mode> <Error> -/// <Key Len> [<Key Data>] <Other Len> [<Other Data>] -/// \endcode -/// -/// Note that, since the Algorithm Name field is defined to be "in domain name -/// syntax", but it is not actually a domain name, it does not have to be -/// fully qualified. -/// -/// The Mode field is an unsigned 16-bit decimal integer as specified -/// in RFC2930 or a common mnemonic. Currently only "GSS-API" (case sensitive) -/// is supported ("Diffie-Hellman" is not). -/// -/// The Error field is an unsigned 16-bit decimal integer or a valid mnemonic -/// as specified in RFC2845. Currently, "NOERROR", "BADSIG", "BADKEY", -/// "BADTIME", "BADMODE", "BADNAME", and "BADALG" are supported -/// (case sensitive). In future versions other representations that -/// are compatible with the DNS RCODE may be supported. -/// -/// The Key Data and Other Data fields are base-64 encoded strings that do not -/// contain space characters. -/// If the Key Len field is 0, the Key Data field must not appear in -/// \c tkey_str. -/// If the Other Len field is 0, the Other Data field must not appear in -/// \c tkey_str. -/// The decoded data of the Key Data field is Key Len bytes of binary stream. -/// The decoded data of the Other Data field is Other Len bytes of binary -/// stream. -/// -/// An example of valid string is: -/// \code "gss-tsig. 20210501120000 20210501130000 0 3 aabbcc 0" \endcode -/// In this example Other Data is missing because Other Len is 0. -/// -/// Note that RFC2930 does not define the standard presentation format -/// of %TKEY RR, so the above syntax is implementation specific. -/// This is, however, compatible with the format acceptable to BIND 9's -/// RDATA parser. -/// -/// \throw Others Exception from the Name constructors. -/// \throw InvalidRdataText if any fields are out of their valid range, -/// or are incorrect. -/// \throw BadValue if Key Data or Other Data is not validly encoded -/// in base-64. -/// -/// \param tkey_str A string containing the RDATA to be created -TKEY::TKEY(const std::string& tkey_str) : impl_(0) { - // We use unique_ptr here because if there is an exception in this - // constructor, the destructor is not called and there could be a - // leak of the TKEYImpl that constructFromLexer() returns. - boost::shared_ptr<TKEYImpl> impl_ptr; - - try { - std::istringstream ss(tkey_str); - MasterLexer lexer; - lexer.pushSource(ss); - - impl_ptr.reset(constructFromLexer(lexer, 0)); - - if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { - isc_throw(InvalidRdataText, - "Extra input text for TKEY: " << tkey_str); - } - } catch (const MasterLexer::LexerError& ex) { - isc_throw(InvalidRdataText, - "Failed to construct TKEY from '" << tkey_str << "': " - << ex.what()); - } - - impl_ = impl_ptr; -} - -/// \brief Constructor with a context of MasterLexer. -/// -/// The \c lexer should point to the beginning of valid textual -/// representation of an TKEY RDATA. -/// -/// See \c TKEY::TKEY(const std::string&) for description of the -/// expected RDATA fields. -/// -/// \throw MasterLexer::LexerError General parsing error such as -/// missing field. -/// \throw InvalidRdataText if any fields are out of their valid range, -/// or are incorrect. -/// -/// \param lexer A \c MasterLexer object parsing a master file for the -/// RDATA to be created -TKEY::TKEY(MasterLexer& lexer, const Name* origin, - MasterLoader::Options, MasterLoaderCallbacks&) : - impl_(constructFromLexer(lexer, origin)) { -} - -/// \brief Constructor from wire-format data. -/// -/// When a read operation on \c buffer fails (e.g., due to a corrupted -/// message) a corresponding exception from the \c InputBuffer class will -/// be thrown. -/// If the wire-format data does not begin with a valid domain name, -/// a corresponding exception from the \c Name class will be thrown. -/// In addition, this constructor internally involves resource allocation, -/// and if it fails a corresponding standard exception will be thrown. -/// -/// According to RFC3597, the Algorithm field must be a non compressed form -/// of domain name. But this implementation accepts a %TKEY RR even if that -/// field is compressed. -/// -/// \param buffer A buffer storing the wire format data. -/// \param rdata_len The length of the RDATA in bytes, normally expected -/// to be the value of the RDLENGTH field of the corresponding RR. -/// But this constructor does not use this parameter; if necessary, the caller -/// must check consistency between the length parameter and the actual -/// RDATA length. -TKEY::TKEY(InputBuffer& buffer, size_t) : - impl_(0) { - Name algorithm(buffer); - - const uint32_t inception = buffer.readUint32(); - - const uint32_t expire = buffer.readUint32(); - - const uint16_t mode = buffer.readUint16(); - - const uint16_t error = buffer.readUint16(); - - const uint16_t key_len = buffer.readUint16(); - vector<uint8_t> key(key_len); - if (key_len > 0) { - buffer.readData(&key[0], key_len); - } - - const uint16_t other_len = buffer.readUint16(); - vector<uint8_t> other_data(other_len); - if (other_len > 0) { - buffer.readData(&other_data[0], other_len); - } - - impl_.reset(new TKEYImpl(algorithm, inception, expire, mode, error, - key, other_data)); -} - -TKEY::TKEY(const Name& algorithm, uint32_t inception, uint32_t expire, - uint16_t mode, uint16_t error, uint16_t key_len, - const void* key, uint16_t other_len, const void* other_data) : - impl_(0) { - if ((key_len == 0 && key != 0) || (key_len > 0 && key == 0)) { - isc_throw(InvalidParameter, "TKEY Key length and data inconsistent"); - } - if ((other_len == 0 && other_data != 0) || - (other_len > 0 && other_data == 0)) { - isc_throw(InvalidParameter, - "TKEY Other data length and data inconsistent"); - } - impl_.reset(new TKEYImpl(algorithm, inception, expire, mode, error, - key_len, key, other_len, other_data)); -} - -/// \brief The copy constructor. -/// -/// It internally allocates a resource, and if it fails a corresponding -/// standard exception will be thrown. -/// This constructor never throws an exception otherwise. -TKEY::TKEY(const TKEY& source) : Rdata(), impl_(new TKEYImpl(*source.impl_)) { -} - -TKEY& -TKEY::operator=(const TKEY& source) { - if (this == &source) { - return (*this); - } - - impl_.reset(new TKEYImpl(*source.impl_)); - - return (*this); -} - -TKEY::~TKEY() { -} - -/// \brief Convert the \c TKEY to a string. -/// -/// The output of this method is formatted as described in the "from string" -/// constructor (\c TKEY(const std::string&))). -/// -/// If internal resource allocation fails, a corresponding -/// standard exception will be thrown. -/// -/// \return A \c string object that represents the \c TKEY object. -std::string -TKEY::toText() const { - string result; - - result += impl_->algorithm_.toText() + " " + - timeToText32(impl_->inception_) + " " + - timeToText32(impl_->expire_) + " "; - if (impl_->mode_ == GSS_API_MODE) { - result += "GSS-API "; - } else { - result += lexical_cast<string>(impl_->mode_) + " "; - } - result += TSIGError(impl_->error_).toText() + " " + - lexical_cast<string>(impl_->key_.size()) + " "; - if (!impl_->key_.empty()) { - result += encodeBase64(impl_->key_) + " "; - } - result += lexical_cast<string>(impl_->other_data_.size()); - if (!impl_->other_data_.empty()) { - result += " " + encodeBase64(impl_->other_data_); - } - - return (result); -} - -// Common sequence of toWire() operations used for the two versions of -// toWire(). -template <typename Output> -void -TKEYImpl::toWireCommon(Output& output) const { - output.writeUint32(inception_); - output.writeUint32(expire_); - output.writeUint16(mode_); - output.writeUint16(error_); - const uint16_t key_len = key_.size(); - output.writeUint16(key_len); - if (key_len > 0) { - output.writeData(&key_[0], key_len); - } - const uint16_t other_len = other_data_.size(); - output.writeUint16(other_len); - if (other_len > 0) { - output.writeData(&other_data_[0], other_len); - } -} - -/// \brief Render the \c TKEY in the wire format without name compression. -/// -/// If internal resource allocation fails, a corresponding -/// standard exception will be thrown. -/// This method never throws an exception otherwise. -/// -/// \param buffer An output buffer to store the wire data. -void -TKEY::toWire(OutputBuffer& buffer) const { - impl_->algorithm_.toWire(buffer); - impl_->toWireCommon<OutputBuffer>(buffer); -} - -/// \brief Render the \c TKEY in the wire format with taking into account -/// compression. -/// -/// As specified in RFC3597, the Algorithm field (a domain name) will not -/// be compressed. However, the domain name could be a target of compression -/// of other compressible names (though pretty unlikely), the offset -/// information of the algorithm name may be recorded in \c renderer. -/// -/// If internal resource allocation fails, a corresponding -/// standard exception will be thrown. -/// This method never throws an exception otherwise. -/// -/// \param renderer DNS message rendering context that encapsulates the -/// output buffer and name compression information. -void -TKEY::toWire(AbstractMessageRenderer& renderer) const { - renderer.writeName(impl_->algorithm_, false); - impl_->toWireCommon<AbstractMessageRenderer>(renderer); -} - -// A helper function commonly used for TKEY::compare(). -int -vectorComp(const vector<uint8_t>& v1, const vector<uint8_t>& v2) { - const size_t this_size = v1.size(); - const size_t other_size = v2.size(); - if (this_size != other_size) { - return (this_size < other_size ? -1 : 1); - } - if (this_size > 0) { - return (memcmp(&v1[0], &v2[0], this_size)); - } - return (0); -} - -/// \brief Compare two instances of \c TKEY RDATA. -/// -/// This method compares \c this and the \c other \c TKEY objects -/// in terms of the DNSSEC sorting order as defined in RFC4034, and returns -/// the result as an integer. -/// -/// This method is expected to be used in a polymorphic way, and the -/// parameter to compare against is therefore of the abstract \c Rdata class. -/// However, comparing two \c Rdata objects of different RR types -/// is meaningless, and \c other must point to a \c TKEY object; -/// otherwise, the standard \c bad_cast exception will be thrown. -/// This method never throws an exception otherwise. -/// -/// \param other the right-hand operand to compare against. -/// \return < 0 if \c this would be sorted before \c other. -/// \return 0 if \c this is identical to \c other in terms of sorting order. -/// \return > 0 if \c this would be sorted after \c other. -int -TKEY::compare(const Rdata& other) const { - const TKEY& other_tkey = dynamic_cast<const TKEY&>(other); - - const int ncmp = compareNames(impl_->algorithm_, - other_tkey.impl_->algorithm_); - if (ncmp != 0) { - return (ncmp); - } - - if (impl_->inception_ != other_tkey.impl_->inception_) { - return (impl_->inception_ < other_tkey.impl_->inception_ ? -1 : 1); - } - if (impl_->expire_ != other_tkey.impl_->expire_) { - return (impl_->expire_ < other_tkey.impl_->expire_ ? -1 : 1); - } - if (impl_->mode_ != other_tkey.impl_->mode_) { - return (impl_->mode_ < other_tkey.impl_->mode_ ? -1 : 1); - } - if (impl_->error_ != other_tkey.impl_->error_) { - return (impl_->error_ < other_tkey.impl_->error_ ? -1 : 1); - } - - const int vcmp = vectorComp(impl_->key_, other_tkey.impl_->key_); - if (vcmp != 0) { - return (vcmp); - } - return (vectorComp(impl_->other_data_, other_tkey.impl_->other_data_)); -} - -const Name& -TKEY::getAlgorithm() const { - return (impl_->algorithm_); -} - -uint32_t -TKEY::getInception() const { - return (impl_->inception_); -} - -string -TKEY::getInceptionDate() const { - return (timeToText32(impl_->inception_)); -} - -uint32_t -TKEY::getExpire() const { - return (impl_->expire_); -} - -string -TKEY::getExpireDate() const { - return (timeToText32(impl_->expire_)); -} - -uint16_t -TKEY::getMode() const { - return (impl_->mode_); -} - -uint16_t -TKEY::getError() const { - return (impl_->error_); -} - -uint16_t -TKEY::getKeyLen() const { - return (impl_->key_.size()); -} - -const void* -TKEY::getKey() const { - if (!impl_->key_.empty()) { - return (&impl_->key_[0]); - } else { - return (0); - } -} - -uint16_t -TKEY::getOtherLen() const { - return (impl_->other_data_.size()); -} - -const void* -TKEY::getOtherData() const { - if (!impl_->other_data_.empty()) { - return (&impl_->other_data_[0]); - } else { - return (0); - } -} - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/tkey_249.h b/src/lib/dns/rdata/generic/tkey_249.h deleted file mode 100644 index cd7278d374..0000000000 --- a/src/lib/dns/rdata/generic/tkey_249.h +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -// BEGIN_HEADER_GUARD - -#include <stdint.h> - -#include <string> - -#include <dns/name.h> -#include <dns/rdata.h> -#include <boost/shared_ptr.hpp> - -// BEGIN_ISC_NAMESPACE - -// BEGIN_COMMON_DECLARATIONS -// END_COMMON_DECLARATIONS - -// BEGIN_RDATA_NAMESPACE - -struct TKEYImpl; - -/// \brief \c rdata::TKEY class represents the TKEY RDATA as defined %in -/// RFC2930. -/// -/// This class implements the basic interfaces inherited from the abstract -/// \c rdata::Rdata class, and provides trivial accessors specific to the -/// TKEY RDATA. -class TKEY : public Rdata { -public: - // BEGIN_COMMON_MEMBERS - // END_COMMON_MEMBERS - - /// \brief Constructor from RDATA field parameters. - /// - /// The parameters are a straightforward mapping of %TKEY RDATA - /// fields as defined %in RFC2930. - /// - /// This RR is pretty close to the TSIG RR with 32 bit timestamps, - /// or the RRSIG RR with a second "other" data field. - /// - /// This constructor internally involves resource allocation, and if - /// it fails, a corresponding standard exception will be thrown. - /// - /// \param algorithm The DNS name of the algorithm e.g. gss-tsig. - /// \param inception The inception time (in seconds since 1970). - /// \param expire The expire time (in seconds since 1970). - /// \param mode The mode e.g. Diffie-Hellman (2) or GSS-API (3). - /// \param error The error code (extended error space shared with TSIG). - /// \param key_len The key length (0 means no key). - /// \param key The key (can be 0). - /// \param other_len The other data length (0 means no other data). - /// \param other_data The other data (can be and usually is 0). - TKEY(const Name& algorithm, uint32_t inception, uint32_t expire, - uint16_t mode, uint16_t error, uint16_t key_len, - const void* key, uint16_t other_len, const void* other_data); - - /// \brief Assignment operator. - /// - /// It internally allocates a resource, and if it fails a corresponding - /// standard exception will be thrown. - /// This operator never throws an exception otherwise. - /// - /// This operator provides the strong exception guarantee: When an - /// exception is thrown the content of the assignment target will be - /// intact. - TKEY& operator=(const TKEY& source); - - /// \brief The destructor. - ~TKEY(); - - /// \brief Return the algorithm name. - /// - /// This method never throws an exception. - const Name& getAlgorithm() const; - - /// \brief Return the value of the Inception field as a number. - /// - /// This method never throws an exception. - uint32_t getInception() const; - - /// \brief Return the value of the Inception field as a string. - std::string getInceptionDate() const; - - /// \brief Return the value of the Expire field as a number. - /// - /// This method never throws an exception. - uint32_t getExpire() const; - - /// \brief Return the value of the Expire field as a string. - std::string getExpireDate() const; - - /// \brief Return the value of the Mode field. - /// - /// This method never throws an exception. - uint16_t getMode() const; - - /// \brief Return the value of the Error field. - /// - /// This method never throws an exception. - uint16_t getError() const; - - /// \brief Return the value of the Key Len field. - /// - /// This method never throws an exception. - uint16_t getKeyLen() const; - - /// \brief Return the value of the Key field. - /// - /// This method never throws an exception. - const void* getKey() const; - - /// \brief Return the value of the Other Len field. - /// - /// This method never throws an exception. - uint16_t getOtherLen() const; - - /// \brief Return the value of the Other Data field. - /// - /// The same note as \c getMAC() applies. - /// - /// This method never throws an exception. - const void* getOtherData() const; - - /// \brief The GSS_API constant for the Mode field. - static const uint16_t GSS_API_MODE; - -private: - boost::shared_ptr<TKEYImpl> constructFromLexer(MasterLexer& lexer, const Name* origin); - - boost::shared_ptr<TKEYImpl> impl_; -}; - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE -// END_HEADER_GUARD diff --git a/src/lib/dns/rdata/in_1/a_1.cc b/src/lib/dns/rdata/in_1/a_1.cc deleted file mode 100644 index 2a7b3efbb8..0000000000 --- a/src/lib/dns/rdata/in_1/a_1.cc +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <stdint.h> -#include <string.h> - -#include <cerrno> -#include <cstring> -#include <string> - -#include <arpa/inet.h> // XXX: for inet_pton/ntop(), not exist in C++ standards -#include <sys/socket.h> // for AF_INET/AF_INET6 - -#include <exceptions/exceptions.h> - -#include <util/buffer.h> - -#include <dns/exceptions.h> -#include <dns/messagerenderer.h> -#include <dns/master_lexer.h> -#include <dns/master_loader_callbacks.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> - -using namespace std; -using namespace isc::util; - -// BEGIN_ISC_NAMESPACE -// BEGIN_RDATA_NAMESPACE - -namespace { -void -convertToIPv4Addr(const char* src, size_t src_len, uint32_t* dst) { - // This check specifically rejects invalid input that begins with valid - // address text followed by a nul character (and possibly followed by - // further garbage). It cannot be detected by inet_pton(). - // - // Note that this is private subroutine of the in::A constructors, which - // pass std::string.size() or StringRegion::len as src_len, so it should - // be equal to strlen() unless there's an intermediate nul character. - if (src_len != strlen(src)) { - isc_throw(InvalidRdataText, - "Bad IN/A RDATA text: unexpected nul in string: '" - << src << "'"); - } - const int result = inet_pton(AF_INET, src, dst); - if (result == 0) { - isc_throw(InvalidRdataText, "Bad IN/A RDATA text: '" << src << "'"); - } else if (result < 0) { - isc_throw(isc::Unexpected, - "Unexpected failure in parsing IN/A RDATA text: '" - << src << "': " << std::strerror(errno)); - } -} -} - -/// \brief Constructor from string. -/// -/// The given string must be a valid textual representation of an IPv4 -/// address as specified in RFC1035, that is, four decimal numbers separated -/// by dots without any embedded spaces. Note that it excludes abbreviated -/// forms such as "10.1" to mean "10.0.0.1". -/// -/// Internally, this implementation uses the standard inet_pton() library -/// function for the AF_INET family to parse and convert the textual -/// representation. While standard compliant implementations of this function -/// should accept exactly what this constructor expects, specific -/// implementation may behave differently, in which case this constructor -/// will simply accept the result of inet_pton(). In any case, the user of -/// the class shouldn't assume such specific implementation behavior of -/// inet_pton(). -/// -/// No extra character should be contained in \c addrstr other than the -/// textual address. These include spaces and the nul character. -/// -/// \throw InvalidRdata The text extracted by the lexer isn't recognized as -/// a valid IPv4 address. -/// \throw Unexpected Unexpected system error in conversion (this should be -/// very rare). -/// -/// \param addrstr Textual representation of IPv4 address to be used as the -/// RDATA. -A::A(const std::string& addrstr) { - convertToIPv4Addr(addrstr.c_str(), addrstr.size(), &addr_); -} - -/// \brief Constructor with a context of MasterLexer. -/// -/// The \c lexer should point to the beginning of valid textual representation -/// of a class IN A RDATA. -/// -/// The acceptable form of the textual address is generally the same as the -/// string version of the constructor, but this version accepts beginning -/// spaces and trailing spaces or other characters. Trailing non space -/// characters would be considered an invalid form in an RR representation, -/// but handling such errors is not the responsibility of this constructor. -/// It also accepts other unusual syntax that would be considered valid -/// in the context of DNS master file; for example, it accepts an IPv4 -/// address surrounded by parentheses, such as "(192.0.2.1)", although it's -/// very unlikely to be used for this type of RDATA. -/// -/// \throw MasterLexer::LexerError General parsing error such as missing field. -/// \throw InvalidRdata The text extracted by the lexer isn't recognized as -/// a valid IPv4 address. -/// \throw Unexpected Unexpected system error in conversion (this should be -/// very rare). -/// -/// \param lexer A \c MasterLexer object parsing a master file for the -/// RDATA to be created -A::A(MasterLexer& lexer, const Name*, - MasterLoader::Options, MasterLoaderCallbacks&) { - const MasterToken& token = lexer.getNextToken(MasterToken::STRING); - convertToIPv4Addr(token.getStringRegion().beg, token.getStringRegion().len, - &addr_); -} - -A::A(InputBuffer& buffer, size_t rdata_len) { - if (rdata_len != sizeof(addr_)) { - isc_throw(DNSMessageFORMERR, - "IN/A RDATA construction from wire failed: Invalid length: " - << rdata_len); - } - if (buffer.getLength() - buffer.getPosition() < sizeof(addr_)) { - isc_throw(DNSMessageFORMERR, - "IN/A RDATA construction from wire failed: " - "insufficient buffer length: " - << buffer.getLength() - buffer.getPosition()); - } - buffer.readData(&addr_, sizeof(addr_)); -} - -/// \brief Copy constructor. -A::A(const A& other) : Rdata(), addr_(other.addr_) { -} - -void -A::toWire(OutputBuffer& buffer) const { - buffer.writeData(&addr_, sizeof(addr_)); -} - -void -A::toWire(AbstractMessageRenderer& renderer) const { - renderer.writeData(&addr_, sizeof(addr_)); -} - -/// \brief Return a textual form of the underlying IPv4 address of the RDATA. -string -A::toText() const { - char addr_string[sizeof("255.255.255.255")]; - - if (inet_ntop(AF_INET, &addr_, addr_string, sizeof(addr_string)) == NULL) { - isc_throw(Unexpected, - "Failed to convert IN/A RDATA to textual IPv4 address"); - } - - return (addr_string); -} - -/// \brief Compare two in::A RDATAs. -/// -/// In effect, it compares the two RDATA as an unsigned 32-bit integer. -int -A::compare(const Rdata& other) const { - const A& other_a = dynamic_cast<const A&>(other); - return (memcmp(&addr_, &other_a.addr_, sizeof(addr_))); -} -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/in_1/a_1.h b/src/lib/dns/rdata/in_1/a_1.h deleted file mode 100644 index f8061d7d22..0000000000 --- a/src/lib/dns/rdata/in_1/a_1.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -// BEGIN_HEADER_GUARD - -#include <string> - -#include <dns/rdata.h> - -// BEGIN_ISC_NAMESPACE - -// BEGIN_COMMON_DECLARATIONS -// END_COMMON_DECLARATIONS - -// BEGIN_RDATA_NAMESPACE - -class A : public Rdata { -public: - // BEGIN_COMMON_MEMBERS - // END_COMMON_MEMBERS - - //We can use the default destructor. - //virtual ~A() {} - // notyet: - //const struct in_addr& getAddress() const { return (addr_); } -private: - uint32_t addr_; // raw IPv4 address (network byte order) -}; -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE -// END_HEADER_GUARD diff --git a/src/lib/dns/rdata/in_1/aaaa_28.cc b/src/lib/dns/rdata/in_1/aaaa_28.cc deleted file mode 100644 index 51243f48e8..0000000000 --- a/src/lib/dns/rdata/in_1/aaaa_28.cc +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <exceptions/exceptions.h> -#include <util/buffer.h> -#include <dns/exceptions.h> -#include <dns/messagerenderer.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> -#include <dns/master_lexer.h> -#include <dns/master_loader.h> - -#include <stdint.h> -#include <string.h> - -#include <cerrno> -#include <cstring> -#include <string> - -#include <arpa/inet.h> // XXX: for inet_pton/ntop(), not exist in C++ standards -#include <sys/socket.h> // for AF_INET/AF_INET6 - -using namespace std; -using namespace isc::util; - -// BEGIN_ISC_NAMESPACE -// BEGIN_RDATA_NAMESPACE - -namespace { -void -convertToIPv6Addr(const char* src, size_t src_len, void* dst) { - // See a_1.cc for this check. - if (src_len != strlen(src)) { - isc_throw(InvalidRdataText, - "Bad IN/AAAA RDATA text: unexpected nul in string: '" - << src << "'"); - } - const int result = inet_pton(AF_INET6, src, dst); - if (result == 0) { - isc_throw(InvalidRdataText, "Bad IN/AAAA RDATA text: '" << src << "'"); - } else if (result < 0) { - isc_throw(isc::Unexpected, - "Unexpected failure in parsing IN/AAAA RDATA text: '" - << src << "': " << std::strerror(errno)); - } -} -} - -/// \brief Constructor from string. -/// -/// The given string must be a valid textual representation of an IPv6 -/// address as specified in RFC1886. -/// -/// No extra character should be contained in \c addrstr other than the -/// textual address. These include spaces and the nul character. -/// -/// \throw InvalidRdata The text extracted by the lexer isn't recognized as -/// a valid IPv6 address. -/// \throw Unexpected Unexpected system error in conversion (this should be -/// very rare). -/// -/// \param addrstr Textual representation of IPv6 address to be used as the -/// RDATA. -AAAA::AAAA(const std::string& addrstr) { - convertToIPv6Addr(addrstr.c_str(), addrstr.size(), addr_); -} - -/// \brief Constructor with a context of MasterLexer. -/// -/// The \c lexer should point to the beginning of valid textual representation -/// of a class IN AAAA RDATA. -/// -/// The acceptable form of the textual address is generally the same as the -/// string version of the constructor, but this version is slightly more -/// flexible. See the similar constructor of \c in::A class; the same -/// notes apply here. -/// -/// \throw MasterLexer::LexerError General parsing error such as missing field. -/// \throw InvalidRdata The text extracted by the lexer isn't recognized as -/// a valid IPv6 address. -/// \throw Unexpected Unexpected system error in conversion (this should be -/// very rare). -/// -/// \param lexer A \c MasterLexer object parsing a master file for the -/// RDATA to be created -AAAA::AAAA(MasterLexer& lexer, const Name*, - MasterLoader::Options, MasterLoaderCallbacks&) { - const MasterToken& token = lexer.getNextToken(MasterToken::STRING); - convertToIPv6Addr(token.getStringRegion().beg, token.getStringRegion().len, - addr_); -} - -/// \brief Copy constructor. -AAAA::AAAA(InputBuffer& buffer, size_t rdata_len) { - if (rdata_len != sizeof(addr_)) { - isc_throw(DNSMessageFORMERR, - "IN/AAAA RDATA construction from wire failed: " - "Invalid length: " << rdata_len); - } - if (buffer.getLength() - buffer.getPosition() < sizeof(addr_)) { - isc_throw(DNSMessageFORMERR, - "IN/AAAA RDATA construction from wire failed: " - "insufficient buffer length: " - << buffer.getLength() - buffer.getPosition()); - } - buffer.readData(&addr_, sizeof(addr_)); -} - -AAAA::AAAA(const AAAA& other) : Rdata() { - memcpy(addr_, other.addr_, sizeof(addr_)); -} - -/// \brief Return a textual form of the underlying IPv6 address of the RDATA. -void -AAAA::toWire(OutputBuffer& buffer) const { - buffer.writeData(&addr_, sizeof(addr_)); -} - -void -AAAA::toWire(AbstractMessageRenderer& renderer) const { - renderer.writeData(&addr_, sizeof(addr_)); -} - -string -AAAA::toText() const { - char addr_string[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; - - if (inet_ntop(AF_INET6, &addr_, addr_string, sizeof(addr_string)) - == NULL) { - isc_throw(Unexpected, - "Failed to convert IN/AAAA RDATA to textual IPv6 address"); - } - - return (string(addr_string)); -} - -/// \brief Compare two in::AAAA RDATAs. -/// -/// In effect, it compares the two RDATA as an unsigned 128-bit integer. -int -AAAA::compare(const Rdata& other) const { - const AAAA& other_a = dynamic_cast<const AAAA&>(other); - return (memcmp(&addr_, &other_a.addr_, sizeof(addr_))); -} - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/in_1/aaaa_28.h b/src/lib/dns/rdata/in_1/aaaa_28.h deleted file mode 100644 index c8829344b4..0000000000 --- a/src/lib/dns/rdata/in_1/aaaa_28.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -// BEGIN_HEADER_GUARD - -#include <stdint.h> - -#include <string> - -#include <dns/rdata.h> - -// BEGIN_ISC_NAMESPACE - -// BEGIN_COMMON_DECLARATIONS -// END_COMMON_DECLARATIONS - -// BEGIN_RDATA_NAMESPACE - -class AAAA : public Rdata { -public: - // BEGIN_COMMON_MEMBERS - // END_COMMON_MEMBERS - // notyet: - //const struct in6_addr& getAddress() const { return (addr_); } -private: - uint8_t addr_[16]; // raw IPv6 address (network byte order) -}; - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE -// END_HEADER_GUARD diff --git a/src/lib/dns/rdata/in_1/dhcid_49.cc b/src/lib/dns/rdata/in_1/dhcid_49.cc deleted file mode 100644 index 89c3f89360..0000000000 --- a/src/lib/dns/rdata/in_1/dhcid_49.cc +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright (C) 2011-2024 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <stdint.h> -#include <string.h> - -#include <exceptions/exceptions.h> - -#include <util/buffer.h> -#include <util/encode/encode.h> -#include <dns/exceptions.h> -#include <dns/messagerenderer.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> - -using namespace std; -using namespace isc::util; -using namespace isc::util::encode; - -// BEGIN_ISC_NAMESPACE -// BEGIN_RDATA_NAMESPACE - -void -DHCID::constructFromLexer(MasterLexer& lexer) { - string digest_txt = lexer.getNextToken(MasterToken::STRING).getString(); - - // Whitespace is allowed within base64 text, so read to the end of input. - string digest_part; - while (true) { - const MasterToken& token = - lexer.getNextToken(MasterToken::STRING, true); - if ((token.getType() == MasterToken::END_OF_FILE) || - (token.getType() == MasterToken::END_OF_LINE)) { - break; - } - token.getString(digest_part); - digest_txt.append(digest_part); - } - lexer.ungetToken(); - - decodeBase64(digest_txt, digest_); -} - -/// \brief Constructor from string. -/// -/// \param dhcid_str A base-64 representation of the DHCID binary data. -/// -/// \throw InvalidRdataText if the string could not be parsed correctly. -DHCID::DHCID(const std::string& dhcid_str) { - try { - std::istringstream iss(dhcid_str); - MasterLexer lexer; - lexer.pushSource(iss); - - constructFromLexer(lexer); - - if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { - isc_throw(InvalidRdataText, "extra input text for DHCID: " - << dhcid_str); - } - } catch (const MasterLexer::LexerError& ex) { - isc_throw(InvalidRdataText, "Failed to construct DHCID from '" << - dhcid_str << "': " << ex.what()); - } -} - -/// \brief Constructor with a context of MasterLexer. -/// -/// The \c lexer should point to the beginning of valid textual representation -/// of a DHCID RDATA. -/// -/// \throw BadValue if the text is not valid base-64. -/// \throw MasterLexer::LexerError General parsing error such as missing field. -/// -/// \param lexer A \c MasterLexer object parsing a master file for the -/// RDATA to be created -DHCID::DHCID(MasterLexer& lexer, const Name*, - MasterLoader::Options, MasterLoaderCallbacks&) { - constructFromLexer(lexer); -} - -/// \brief Constructor from wire-format data. -/// -/// \param buffer A buffer storing the wire format data. -/// \param rdata_len The length of the RDATA in bytes -DHCID::DHCID(InputBuffer& buffer, size_t rdata_len) { - if (rdata_len == 0) { - isc_throw(InvalidRdataLength, "Missing DHCID rdata"); - } - - digest_.resize(rdata_len); - buffer.readData(&digest_[0], rdata_len); -} - -/// \brief The copy constructor. -/// -/// This trivial copy constructor never throws an exception. -DHCID::DHCID(const DHCID& other) : Rdata(), digest_(other.digest_) { -} - -/// \brief Render the \c DHCID in the wire format. -/// -/// \param buffer An output buffer to store the wire data. -void -DHCID::toWire(OutputBuffer& buffer) const { - buffer.writeData(&digest_[0], digest_.size()); -} - -/// \brief Render the \c DHCID in the wire format into a -/// \c MessageRenderer object. -/// -/// \param renderer DNS message rendering context that encapsulates the -/// output buffer in which the \c DHCID is to be stored. -void -DHCID::toWire(AbstractMessageRenderer& renderer) const { - renderer.writeData(&digest_[0], digest_.size()); -} - -/// \brief Convert the \c DHCID to a string. -/// -/// This method returns a \c std::string object representing the \c DHCID. -/// -/// \return A string representation of \c DHCID. -string -DHCID::toText() const { - return (encodeBase64(digest_)); -} - -/// \brief Compare two instances of \c DHCID RDATA. -/// -/// See documentation in \c Rdata. -int -DHCID::compare(const Rdata& other) const { - const DHCID& other_dhcid = dynamic_cast<const DHCID&>(other); - - size_t this_len = digest_.size(); - size_t other_len = other_dhcid.digest_.size(); - size_t cmplen = min(this_len, other_len); - int cmp = memcmp(&digest_[0], &other_dhcid.digest_[0], cmplen); - if (cmp != 0) { - return (cmp); - } else { - return ((this_len == other_len) ? 0 : (this_len < other_len) ? -1 : 1); - } -} - -/// \brief Accessor method to get the DHCID digest -/// -/// \return A reference to the binary DHCID data -const std::vector<uint8_t>& -DHCID::getDigest() const { - return (digest_); -} - -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/in_1/dhcid_49.h b/src/lib/dns/rdata/in_1/dhcid_49.h deleted file mode 100644 index e5602c1cce..0000000000 --- a/src/lib/dns/rdata/in_1/dhcid_49.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -// BEGIN_HEADER_GUARD - -#include <string> -#include <vector> - -#include <dns/rdata.h> - -// BEGIN_ISC_NAMESPACE - -// BEGIN_COMMON_DECLARATIONS -// END_COMMON_DECLARATIONS - -// BEGIN_RDATA_NAMESPACE - -/// \brief \c rdata::DHCID class represents the DHCID RDATA as defined %in -/// RFC4701. -/// -/// This class implements the basic interfaces inherited from the abstract -/// \c rdata::Rdata class, and provides trivial accessors specific to the -/// DHCID RDATA. -class DHCID : public Rdata { -public: - // BEGIN_COMMON_MEMBERS - // END_COMMON_MEMBERS - - /// \brief Return the digest. - /// - /// This method never throws an exception. - const std::vector<uint8_t>& getDigest() const; - -private: - // helper for string and lexer constructors - void constructFromLexer(MasterLexer& lexer); - - /// \brief Private data representation - /// - /// Opaque data at least 3 octets long as per RFC4701. - /// - std::vector<uint8_t> digest_; -}; -// END_RDATA_NAMESPACE -// END_ISC_NAMESPACE -// END_HEADER_GUARD diff --git a/src/lib/dns/rdata/template.cc b/src/lib/dns/rdata/template.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/rdata/template.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/rdata/template.h b/src/lib/dns/rdata/template.h deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/rdata/template.h +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/rdataclass.cc b/src/lib/dns/rdataclass.cc index 699f6aaaa4..43c26c5936 100644 --- a/src/lib/dns/rdataclass.cc +++ b/src/lib/dns/rdataclass.cc @@ -1,10 +1,3 @@ -/////////////// -/////////////// -/////////////// THIS FILE IS AUTOMATICALLY GENERATED BY gen-rdatacode.py. -/////////////// DO NOT EDIT! -/////////////// -/////////////// - // Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public @@ -13,32 +6,50 @@ #include <config.h> -#include <string> -#include <sstream> -#include <vector> - -#include <boost/lexical_cast.hpp> -#include <boost/shared_ptr.hpp> - -#include <util/buffer.h> -#include <util/encode/encode.h> - +#include <exceptions/exceptions.h> +#include <dns/exceptions.h> +#include <dns/master_lexer.h> +#include <dns/master_loader.h> +#include <dns/master_loader_callbacks.h> #include <dns/messagerenderer.h> #include <dns/name.h> +#include <dns/rcode.h> #include <dns/rdata.h> #include <dns/rdataclass.h> -#include <dns/rcode.h> +#include <dns/rrtype.h> #include <dns/tsigkey.h> #include <dns/tsigerror.h> -#include <dns/rdata/generic/detail/lexer_util.h> +#include <dns/txt_like.h> +#include <util/buffer.h> +#include <util/encode/encode.h> +#include <util/buffer.h> +#include <util/time_utilities.h> + +#include <cerrno> +#include <cstring> +#include <iomanip> +#include <iostream> +#include <string> +#include <sstream> +#include <vector> + +#include <arpa/inet.h> // XXX: for inet_pton/ntop(), not exist in C++ standards +#include <stdio.h> +#include <stdint.h> +#include <sys/socket.h> // for AF_INET/AF_INET6 +#include <time.h> + +#include <boost/lexical_cast.hpp> +#include <boost/shared_ptr.hpp> -using namespace std; -using boost::lexical_cast; using namespace isc::util; using namespace isc::util::encode; using namespace isc::dns; using isc::dns::rdata::generic::detail::createNameFromLexer; +using namespace std; +using boost::lexical_cast; + namespace isc { namespace dns { namespace rdata { @@ -568,32 +579,100 @@ TSIG::getOtherData() const { } } // end of namespace "any" -} // end of namespace "rdata" -} // end of namespace "dns" -} // end of namespace "isc" -// Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. -#include <config.h> +namespace generic { -#include <util/buffer.h> -#include <dns/messagerenderer.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> +/// \brief Constructor from string. +/// +/// The given string must represent a valid NS RDATA. There can be extra +/// space characters at the beginning or end of the text (which are simply +/// ignored), but other extra text, including a new line, will make the +/// construction fail with an exception. +/// +/// The NSDNAME must be absolute since there's no parameter that +/// specifies the origin name; if it is not absolute, \c +/// MissingNameOrigin exception will be thrown. These must not be +/// represented as a quoted string. +/// +/// \throw Others Exception from the Name and RRTTL constructors. +/// \throw InvalidRdataText Other general syntax errors. +NS::NS(const std::string& namestr) : + // Fill in dummy name and replace them soon below. + nsname_(Name::ROOT_NAME()) { + try { + std::istringstream ss(namestr); + MasterLexer lexer; + lexer.pushSource(ss); -#include <string> -#include <string.h> + nsname_ = createNameFromLexer(lexer, NULL); -using namespace std; -using namespace isc::util; + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for NS: " + << namestr); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct NS from '" << + namestr << "': " << ex.what()); + } +} -namespace isc { -namespace dns { -namespace rdata { -namespace generic { +NS::NS(InputBuffer& buffer, size_t) : + nsname_(buffer) { + // we don't need rdata_len for parsing. if necessary, the caller will + // check consistency. +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual +/// representation of an NS RDATA. The NSDNAME field can be +/// non-absolute if \c origin is non-NULL, in which case \c origin is +/// used to make it absolute. It must not be represented as a quoted +/// string. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw Other Exceptions from the Name and RRTTL constructors if +/// construction of textual fields as these objects fail. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +/// \param origin If non NULL, specifies the origin of NSDNAME when it +/// is non-absolute. +NS::NS(MasterLexer& lexer, const Name* origin, + MasterLoader::Options, MasterLoaderCallbacks&) : + nsname_(createNameFromLexer(lexer, origin)) { +} + +NS::NS(const NS& other) : + Rdata(), nsname_(other.nsname_) { +} + +void +NS::toWire(OutputBuffer& buffer) const { + nsname_.toWire(buffer); +} + +void +NS::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeName(nsname_); +} + +string +NS::toText() const { + return (nsname_.toText()); +} + +int +NS::compare(const Rdata& other) const { + const NS& other_ns = dynamic_cast<const NS&>(other); + + return (compareNames(nsname_, other_ns.nsname_)); +} + +const Name& +NS::getNSName() const { + return (nsname_); +} /// \brief Constructor. OPT::PseudoRR::PseudoRR(uint16_t code, @@ -777,38 +856,6 @@ OPT::getPseudoRRs() const { return (impl_->pseudo_rrs_); } -} // end of namespace "generic" -} // end of namespace "rdata" -} // end of namespace "dns" -} // end of namespace "isc" - -// Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <string> - -#include <util/buffer.h> -#include <dns/name.h> -#include <dns/messagerenderer.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> - -#include <dns/rdata/generic/detail/lexer_util.h> - -using namespace std; -using namespace isc::util; -using isc::dns::rdata::generic::detail::createNameFromLexer; - -namespace isc { -namespace dns { -namespace rdata { -namespace generic { - /// \brief Constructor from string. /// /// The given string must represent a valid PTR RDATA. There can be @@ -903,50 +950,6 @@ PTR::getPTRName() const { return (ptr_name_); } -} // end of namespace "generic" -} // end of namespace "rdata" -} // end of namespace "dns" -} // end of namespace "isc" - -// Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <string> -#include <iomanip> -#include <iostream> -#include <sstream> -#include <vector> - -#include <boost/lexical_cast.hpp> - -#include <util/encode/encode.h> -#include <util/buffer.h> -#include <util/time_utilities.h> -#include <dns/messagerenderer.h> -#include <dns/name.h> -#include <dns/rrtype.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> -#include <dns/rdata/generic/detail/lexer_util.h> - -#include <stdio.h> -#include <time.h> - -using namespace std; -using namespace isc::util; -using namespace isc::util::encode; -using isc::dns::rdata::generic::detail::createNameFromLexer; - -namespace isc { -namespace dns { -namespace rdata { -namespace generic { - namespace { // This is the minimum necessary length of all wire-format RRSIG RDATA: // - two 8-bit fields (algorithm and labels) @@ -1237,48 +1240,6 @@ RRSIG::typeCovered() const { return (impl_->covered_); } -} // end of namespace "generic" -} // end of namespace "rdata" -} // end of namespace "dns" -} // end of namespace "isc" - -// Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <exceptions/exceptions.h> - -#include <util/buffer.h> -#include <dns/name.h> -#include <dns/master_lexer.h> -#include <dns/master_loader.h> -#include <dns/master_loader_callbacks.h> -#include <dns/messagerenderer.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> - -#include <dns/rdata/generic/detail/lexer_util.h> - -#include <boost/static_assert.hpp> -#include <boost/lexical_cast.hpp> - -#include <string> -#include <sstream> - -using namespace std; -using boost::lexical_cast; -using namespace isc::util; -using isc::dns::rdata::generic::detail::createNameFromLexer; - -namespace isc { -namespace dns { -namespace rdata { -namespace generic { - SOA::SOA(InputBuffer& buffer, size_t) : mname_(buffer), rname_(buffer) { // we don't need rdata_len for parsing. if necessary, the caller will @@ -1447,49 +1408,6 @@ SOA::compare(const Rdata& other) const { return (memcmp(numdata_, other_soa.numdata_, sizeof(numdata_))); } -} // end of namespace "generic" -} // end of namespace "rdata" -} // end of namespace "dns" -} // end of namespace "isc" - -// Copyright (C) 2021-2024 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <string> -#include <sstream> -#include <vector> - -#include <boost/lexical_cast.hpp> - -#include <util/buffer.h> -#include <util/encode/encode.h> -#include <util/time_utilities.h> - -#include <dns/tsigerror.h> -#include <dns/messagerenderer.h> -#include <dns/name.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> -#include <dns/rcode.h> -#include <dns/rdata/generic/detail/lexer_util.h> - -using namespace std; -using boost::lexical_cast; -using namespace isc::util; -using namespace isc::util::encode; -using namespace isc::dns; -using isc::dns::rdata::generic::detail::createNameFromLexer; - -namespace isc { -namespace dns { -namespace rdata { -namespace generic { - const uint16_t TKEY::GSS_API_MODE = 3; // straightforward representation of TKEY RDATA fields @@ -2061,46 +1979,72 @@ TKEY::getOtherData() const { } } -} // end of namespace "generic" -} // end of namespace "rdata" -} // end of namespace "dns" -} // end of namespace "isc" -// Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. +TXT& +TXT::operator=(const TXT& source) { + if (this == &source) { + return (*this); + } -#include <config.h> + impl_.reset(new TXTImpl(*source.impl_)); -#include <stdint.h> -#include <string.h> + return (*this); +} -#include <cerrno> -#include <cstring> -#include <string> +TXT::~TXT() { +} -#include <arpa/inet.h> // XXX: for inet_pton/ntop(), not exist in C++ standards -#include <sys/socket.h> // for AF_INET/AF_INET6 +TXT::TXT(InputBuffer& buffer, size_t rdata_len) : + impl_(new TXTImpl(buffer, rdata_len)) { +} -#include <exceptions/exceptions.h> +/// \brief Constructor using the master lexer. +/// +/// This implementation only uses the \c lexer parameters; others are +/// ignored. +/// +/// \throw CharStringTooLong the parameter string length exceeds maximum. +/// \throw InvalidRdataText the method cannot process the parameter data +/// +/// \param lexer A \c MasterLexer object parsing a master file for this +/// RDATA. +TXT::TXT(MasterLexer& lexer, const Name*, MasterLoader::Options, + MasterLoaderCallbacks&) : + impl_(new TXTImpl(lexer)) { +} -#include <util/buffer.h> +TXT::TXT(const std::string& txtstr) : + impl_(new TXTImpl(txtstr)) { +} -#include <dns/exceptions.h> -#include <dns/messagerenderer.h> -#include <dns/master_lexer.h> -#include <dns/master_loader_callbacks.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> +TXT::TXT(const TXT& other) : + Rdata(), impl_(new TXTImpl(*other.impl_)) { +} -using namespace std; -using namespace isc::util; +void +TXT::toWire(OutputBuffer& buffer) const { + impl_->toWire(buffer); +} + +void +TXT::toWire(AbstractMessageRenderer& renderer) const { + impl_->toWire(renderer); +} + +string +TXT::toText() const { + return (impl_->toText()); +} + +int +TXT::compare(const Rdata& other) const { + const TXT& other_txt = dynamic_cast<const TXT&>(other); + + return (impl_->compare(*other_txt.impl_)); +} + +} // end of namespace "generic" -namespace isc { -namespace dns { -namespace rdata { namespace in { namespace { @@ -2239,44 +2183,6 @@ A::compare(const Rdata& other) const { const A& other_a = dynamic_cast<const A&>(other); return (memcmp(&addr_, &other_a.addr_, sizeof(addr_))); } -} // end of namespace "in" -} // end of namespace "rdata" -} // end of namespace "dns" -} // end of namespace "isc" -// Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <exceptions/exceptions.h> -#include <util/buffer.h> -#include <dns/exceptions.h> -#include <dns/messagerenderer.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> -#include <dns/master_lexer.h> -#include <dns/master_loader.h> - -#include <stdint.h> -#include <string.h> - -#include <cerrno> -#include <cstring> -#include <string> - -#include <arpa/inet.h> // XXX: for inet_pton/ntop(), not exist in C++ standards -#include <sys/socket.h> // for AF_INET/AF_INET6 - -using namespace std; -using namespace isc::util; - -namespace isc { -namespace dns { -namespace rdata { -namespace in { namespace { void @@ -2395,39 +2301,6 @@ AAAA::compare(const Rdata& other) const { return (memcmp(&addr_, &other_a.addr_, sizeof(addr_))); } -} // end of namespace "in" -} // end of namespace "rdata" -} // end of namespace "dns" -} // end of namespace "isc" -// Copyright (C) 2011-2024 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <stdint.h> -#include <string.h> - -#include <exceptions/exceptions.h> - -#include <util/buffer.h> -#include <util/encode/encode.h> -#include <dns/exceptions.h> -#include <dns/messagerenderer.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> - -using namespace std; -using namespace isc::util; -using namespace isc::util::encode; - -namespace isc { -namespace dns { -namespace rdata { -namespace in { - void DHCID::constructFromLexer(MasterLexer& lexer) { string digest_txt = lexer.getNextToken(MasterToken::STRING).getString(); diff --git a/src/lib/dns/rdataclass.h b/src/lib/dns/rdataclass.h index da60839f0d..25eaab8bc0 100644 --- a/src/lib/dns/rdataclass.h +++ b/src/lib/dns/rdataclass.h @@ -1,49 +1,33 @@ -/////////////// -/////////////// -/////////////// THIS FILE IS AUTOMATICALLY GENERATED BY gen-rdatacode.py. -/////////////// DO NOT EDIT! -/////////////// -/////////////// - -#ifndef DNS_RDATACLASS_H -#define DNS_RDATACLASS_H - -#include <dns/master_loader.h> - -namespace isc { -namespace dns { -class Name; -class MasterLexer; -class MasterLoaderCallbacks; -} -} -// Copyright (C) 2010-2021 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#ifndef ANY_TSIG_250_H -#define ANY_TSIG_250_H - -#include <stdint.h> - -#include <string> +#ifndef DNS_RDATACLASS_H +#define DNS_RDATACLASS_H -#include <util/buffer.h> +#include <dns/master_loader.h> #include <dns/name.h> #include <dns/rdata.h> +#include <dns/rrttl.h> +#include <dns/rrtype.h> +#include <dns/serial.h> +#include <util/buffer.h> + +#include <stdint.h> +#include <string> +#include <vector> #include <boost/shared_ptr.hpp> namespace isc { namespace dns { -// BEGIN_COMMON_DECLARATIONS - +class Name; +class MasterLexer; +class MasterLoaderCallbacks; class AbstractMessageRenderer; -// END_COMMON_DECLARATIONS - namespace rdata { namespace any { @@ -57,8 +41,6 @@ struct TSIGImpl; /// TSIG RDATA. class TSIG : public Rdata { public: - // BEGIN_COMMON_MEMBERS - explicit TSIG(const std::string& type_str); TSIG(isc::util::InputBuffer& buffer, size_t rdata_len); TSIG(const TSIG& other); @@ -70,8 +52,6 @@ public: virtual void toWire(AbstractMessageRenderer& renderer) const; virtual int compare(const Rdata& other) const; - // END_COMMON_MEMBERS - /// \brief Constructor from RDATA field parameters. /// /// The parameters are a straightforward mapping of %TSIG RDATA @@ -179,47 +159,71 @@ private: }; } // end of namespace "any" -} // end of namespace "rdata" -} // end of namespace "dns" -} // end of namespace "isc" -#endif // ANY_TSIG_250_H - -// Copyright (C) 2010-2021 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef GENERIC_OPT_41_H -#define GENERIC_OPT_41_H - -#include <string> -#include <util/buffer.h> -#include <dns/rdata.h> - -#include <boost/shared_ptr.hpp> - -#include <vector> +namespace generic { +namespace detail { -namespace isc { -namespace dns { +/// \brief Construct a Name object using a master lexer and optional origin. +/// +/// This is a convenient shortcut of commonly used code pattern that would +/// be used to build RDATA that contain a domain name field. +/// +/// Note that this function throws an exception against invalid input. +/// The (direct or indirect) caller's responsibility needs to expect and +/// handle exceptions appropriately. +/// +/// \throw MasterLexer::LexerError The next token from lexer is not string. +/// \throw Other Exceptions from the \c Name class constructor if the next +/// string token from the lexer does not represent a valid name. +/// +/// \param lexer A \c MasterLexer object. Its next token is expected to be +/// a string that represent a domain name. +/// \param origin If non NULL, specifies the origin of the name to be +/// constructed. +/// +/// \return A new Name object that corresponds to the next string token of +/// the \c lexer. +inline Name +createNameFromLexer(MasterLexer& lexer, const Name* origin) { + const MasterToken::StringRegion& str_region = + lexer.getNextToken(MasterToken::STRING).getStringRegion(); + return (Name(str_region.beg, str_region.len, origin)); +} -// BEGIN_COMMON_DECLARATIONS +template<class Type, uint16_t typeCode> +class TXTLikeImpl; -class AbstractMessageRenderer; +} // namespace detail -// END_COMMON_DECLARATIONS +class NS : public Rdata { +public: + explicit NS(const std::string& type_str); + NS(isc::util::InputBuffer& buffer, size_t rdata_len); + NS(const NS& other); + NS( + MasterLexer& lexer, const Name* name, + MasterLoader::Options options, MasterLoaderCallbacks& callbacks); + virtual std::string toText() const; + virtual void toWire(isc::util::OutputBuffer& buffer) const; + virtual void toWire(AbstractMessageRenderer& renderer) const; + virtual int compare(const Rdata& other) const; -namespace rdata { -namespace generic { + /// Specialized constructor + /// + explicit NS(const Name& nsname) : nsname_(nsname) { + } + /// + /// Specialized methods + /// + const Name& getNSName() const; +private: + Name nsname_; +}; struct OPTImpl; class OPT : public Rdata { public: - // BEGIN_COMMON_MEMBERS - explicit OPT(const std::string& type_str); OPT(isc::util::InputBuffer& buffer, size_t rdata_len); OPT(const OPT& other); @@ -231,8 +235,6 @@ public: virtual void toWire(AbstractMessageRenderer& renderer) const; virtual int compare(const Rdata& other) const; - // END_COMMON_MEMBERS - // The default constructor makes sense for OPT as it can be empty. OPT(); OPT& operator=(const OPT& source); @@ -286,43 +288,8 @@ private: boost::shared_ptr<OPTImpl> impl_; }; -} // end of namespace "generic" -} // end of namespace "rdata" -} // end of namespace "dns" -} // end of namespace "isc" -#endif // GENERIC_OPT_41_H - -// Copyright (C) 2010-2021 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef GENERIC_PTR_12_H -#define GENERIC_PTR_12_H - -#include <string> - -#include <util/buffer.h> -#include <dns/name.h> -#include <dns/rdata.h> - -namespace isc { -namespace dns { - -// BEGIN_COMMON_DECLARATIONS - -class AbstractMessageRenderer; - -// END_COMMON_DECLARATIONS - -namespace rdata { -namespace generic { - class PTR : public Rdata { public: - // BEGIN_COMMON_MEMBERS - explicit PTR(const std::string& type_str); PTR(isc::util::InputBuffer& buffer, size_t rdata_len); PTR(const PTR& other); @@ -334,8 +301,6 @@ public: virtual void toWire(AbstractMessageRenderer& renderer) const; virtual int compare(const Rdata& other) const; - // END_COMMON_MEMBERS - /// /// Specialized constructor /// @@ -349,42 +314,6 @@ private: Name ptr_name_; }; -} // end of namespace "generic" -} // end of namespace "rdata" -} // end of namespace "dns" -} // end of namespace "isc" -#endif // GENERIC_PTR_12_H - -// Copyright (C) 2010-2021 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <stdint.h> - -#include <string> - -#include <dns/name.h> -#include <dns/rrtype.h> -#include <dns/rdata.h> -#include <boost/shared_ptr.hpp> - -#ifndef GENERIC_RRSIG_46_H -#define GENERIC_RRSIG_46_H - -namespace isc { -namespace dns { - -// BEGIN_COMMON_DECLARATIONS - -class AbstractMessageRenderer; - -// END_COMMON_DECLARATIONS - -namespace rdata { -namespace generic { - struct RRSIGImpl; /// \brief \c rdata::RRSIG class represents the RRSIG RDATA as defined %in @@ -395,8 +324,6 @@ struct RRSIGImpl; /// RRSIG RDATA. class RRSIG : public Rdata { public: - // BEGIN_COMMON_MEMBERS - explicit RRSIG(const std::string& type_str); RRSIG(isc::util::InputBuffer& buffer, size_t rdata_len); RRSIG(const RRSIG& other); @@ -408,7 +335,6 @@ public: virtual void toWire(AbstractMessageRenderer& renderer) const; virtual int compare(const Rdata& other) const; - // END_COMMON_MEMBERS RRSIG& operator=(const RRSIG& source); ~RRSIG(); @@ -421,45 +347,8 @@ private: boost::shared_ptr<RRSIGImpl> impl_; }; -} // end of namespace "generic" -} // end of namespace "rdata" -} // end of namespace "dns" -} // end of namespace "isc" -#endif // GENERIC_RRSIG_46_H - -// Copyright (C) 2010-2021 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef GENERIC_SOA_6_H -#define GENERIC_SOA_6_H - -#include <string> - -#include <dns/name.h> -#include <dns/rdata.h> -#include <dns/rrttl.h> -#include <dns/serial.h> -#include <util/buffer.h> - -namespace isc { -namespace dns { - -// BEGIN_COMMON_DECLARATIONS - -class AbstractMessageRenderer; - -// END_COMMON_DECLARATIONS - -namespace rdata { -namespace generic { - class SOA : public Rdata { public: - // BEGIN_COMMON_MEMBERS - explicit SOA(const std::string& type_str); SOA(isc::util::InputBuffer& buffer, size_t rdata_len); SOA(const SOA& other); @@ -471,8 +360,6 @@ public: virtual void toWire(AbstractMessageRenderer& renderer) const; virtual int compare(const Rdata& other) const; - // END_COMMON_MEMBERS - SOA(const Name& mname, const Name& rname, uint32_t serial, uint32_t refresh, uint32_t retry, uint32_t expire, uint32_t minimum); @@ -491,43 +378,6 @@ private: uint8_t numdata_[20]; }; -} // end of namespace "generic" -} // end of namespace "rdata" -} // end of namespace "dns" -} // end of namespace "isc" -#endif // GENERIC_SOA_6_H -#endif // DNS_RDATACLASS_H - -// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef GENERIC_TKEY_249_H -#define GENERIC_TKEY_249_H - -#include <stdint.h> - -#include <string> - -#include <util/buffer.h> -#include <dns/name.h> -#include <dns/rdata.h> -#include <boost/shared_ptr.hpp> - -namespace isc { -namespace dns { - -// BEGIN_COMMON_DECLARATIONS - -class AbstractMessageRenderer; - -// END_COMMON_DECLARATIONS - -namespace rdata { -namespace generic { - struct TKEYImpl; /// \brief \c rdata::TKEY class represents the TKEY RDATA as defined %in @@ -538,8 +388,6 @@ struct TKEYImpl; /// TKEY RDATA. class TKEY : public Rdata { public: - // BEGIN_COMMON_MEMBERS - explicit TKEY(const std::string& type_str); TKEY(isc::util::InputBuffer& buffer, size_t rdata_len); TKEY(const TKEY& other); @@ -551,8 +399,6 @@ public: virtual void toWire(AbstractMessageRenderer& renderer) const; virtual int compare(const Rdata& other) const; - // END_COMMON_MEMBERS - /// \brief Constructor from RDATA field parameters. /// /// The parameters are a straightforward mapping of %TKEY RDATA @@ -653,42 +499,33 @@ private: boost::shared_ptr<TKEYImpl> impl_; }; -} // end of namespace "generic" -} // end of namespace "rdata" -} // end of namespace "dns" -} // end of namespace "isc" -#endif // GENERIC_TKEY_249_H - -// Copyright (C) 2010-2021 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef IN_A_1_H -#define IN_A_1_H - -#include <string> - -#include <util/buffer.h> -#include <dns/rdata.h> - -namespace isc { -namespace dns { +class TXT : public Rdata { +public: + explicit TXT(const std::string& type_str); + TXT(isc::util::InputBuffer& buffer, size_t rdata_len); + TXT(const TXT& other); + TXT( + MasterLexer& lexer, const Name* name, + MasterLoader::Options options, MasterLoaderCallbacks& callbacks); + virtual std::string toText() const; + virtual void toWire(isc::util::OutputBuffer& buffer) const; + virtual void toWire(AbstractMessageRenderer& renderer) const; + virtual int compare(const Rdata& other) const; -// BEGIN_COMMON_DECLARATIONS + TXT& operator=(const TXT& source); + ~TXT(); -class AbstractMessageRenderer; +private: + typedef isc::dns::rdata::generic::detail::TXTLikeImpl<TXT, 16> TXTImpl; + boost::shared_ptr<TXTImpl> impl_; +}; +} // namespace generic -// END_COMMON_DECLARATIONS -namespace rdata { namespace in { class A : public Rdata { public: - // BEGIN_COMMON_MEMBERS - explicit A(const std::string& type_str); A(isc::util::InputBuffer& buffer, size_t rdata_len); A(const A& other); @@ -699,54 +536,12 @@ public: virtual void toWire(isc::util::OutputBuffer& buffer) const; virtual void toWire(AbstractMessageRenderer& renderer) const; virtual int compare(const Rdata& other) const; - - // END_COMMON_MEMBERS - - //We can use the default destructor. - //virtual ~A() {} - // notyet: - //const struct in_addr& getAddress() const { return (addr_); } private: uint32_t addr_; // raw IPv4 address (network byte order) }; -} // end of namespace "in" -} // end of namespace "rdata" -} // end of namespace "dns" -} // end of namespace "isc" -#endif // IN_A_1_H - -// Copyright (C) 2010-2021 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef IN_AAAA_28_H -#define IN_AAAA_28_H - -#include <stdint.h> - -#include <string> - -#include <util/buffer.h> -#include <dns/rdata.h> - -namespace isc { -namespace dns { - -// BEGIN_COMMON_DECLARATIONS - -class AbstractMessageRenderer; - -// END_COMMON_DECLARATIONS - -namespace rdata { -namespace in { class AAAA : public Rdata { public: - // BEGIN_COMMON_MEMBERS - explicit AAAA(const std::string& type_str); AAAA(isc::util::InputBuffer& buffer, size_t rdata_len); AAAA(const AAAA& other); @@ -757,47 +552,10 @@ public: virtual void toWire(isc::util::OutputBuffer& buffer) const; virtual void toWire(AbstractMessageRenderer& renderer) const; virtual int compare(const Rdata& other) const; - - // END_COMMON_MEMBERS - // notyet: - //const struct in6_addr& getAddress() const { return (addr_); } private: uint8_t addr_[16]; // raw IPv6 address (network byte order) }; -} // end of namespace "in" -} // end of namespace "rdata" -} // end of namespace "dns" -} // end of namespace "isc" -#endif // IN_AAAA_28_H - -// Copyright (C) 2011-2021 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef IN_DHCID_49_H -#define IN_DHCID_49_H - -#include <string> -#include <vector> - -#include <util/buffer.h> -#include <dns/rdata.h> - -namespace isc { -namespace dns { - -// BEGIN_COMMON_DECLARATIONS - -class AbstractMessageRenderer; - -// END_COMMON_DECLARATIONS - -namespace rdata { -namespace in { - /// \brief \c rdata::DHCID class represents the DHCID RDATA as defined %in /// RFC4701. /// @@ -806,8 +564,6 @@ namespace in { /// DHCID RDATA. class DHCID : public Rdata { public: - // BEGIN_COMMON_MEMBERS - explicit DHCID(const std::string& type_str); DHCID(isc::util::InputBuffer& buffer, size_t rdata_len); DHCID(const DHCID& other); @@ -819,8 +575,6 @@ public: virtual void toWire(AbstractMessageRenderer& renderer) const; virtual int compare(const Rdata& other) const; - // END_COMMON_MEMBERS - /// \brief Return the digest. /// /// This method never throws an exception. @@ -836,8 +590,9 @@ private: /// std::vector<uint8_t> digest_; }; + } // end of namespace "in" } // end of namespace "rdata" } // end of namespace "dns" } // end of namespace "isc" -#endif // IN_DHCID_49_H +#endif // DNS_RDATACLASS_H diff --git a/src/lib/dns/rrclass-placeholder.h b/src/lib/dns/rrclass-placeholder.h deleted file mode 100644 index a9c4ebda0b..0000000000 --- a/src/lib/dns/rrclass-placeholder.h +++ /dev/null @@ -1,315 +0,0 @@ -// Copyright (C) 2010-2017 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef RRCLASS_H -#define RRCLASS_H - -#include <util/buffer.h> - -#include <stdint.h> - -#include <string> -#include <ostream> - -#include <dns/exceptions.h> - -#include <boost/optional.hpp> - -// Undefine the macro IN which is defined in some operating systems -// but conflicts the IN RR class. - -#ifdef IN -#undef IN -#endif - -namespace isc { - -namespace dns { - -// forward declarations -class AbstractMessageRenderer; - -/// -/// \brief A standard DNS module exception that is thrown if an RRClass object -/// is being constructed from an unrecognized string. -/// -class InvalidRRClass : public DNSTextError { -public: - InvalidRRClass(const char* file, size_t line, const char* what) : - DNSTextError(file, line, what) {} -}; - -/// -/// \brief A standard DNS module exception that is thrown if an RRClass object -/// is being constructed from a incomplete (too short) wire-format data. -/// -class IncompleteRRClass : public isc::dns::Exception { -public: - IncompleteRRClass(const char* file, size_t line, const char* what) : - isc::dns::Exception(file, line, what) {} -}; - -/// -/// The \c RRClass class encapsulates DNS resource record classes. -/// -/// This class manages the 16-bit integer class codes in quite a straightforward -/// way. The only non trivial task is to handle textual representations of -/// RR classes, such as "IN", "CH", or "CLASS65534". -/// -/// This class consults a helper \c RRParamRegistry class, which is a registry -/// of RR related parameters and has the singleton object. This registry -/// provides a mapping between RR class codes and their "well-known" textual -/// representations. -/// Parameters of RR classes defined by DNS protocol standards are automatically -/// registered at initialization time and are ensured to be always available for -/// applications unless the application explicitly modifies the registry. -/// -/// For convenience, this class defines constant class objects corresponding to -/// standard RR classes. These are generally referred to as the form of -/// <code>RRClass::{class-text}()</code>. -/// For example, \c RRClass::IN() is an \c RRClass object corresponding to the -/// IN class (class code 1). -/// Note that these constants are used through a "proxy" function. -/// This is because they may be used to initialize another non-local (e.g. -/// global or namespace-scope) static object as follows: -/// -/// \code -/// namespace foo { -/// const RRClass default_class = RRClass::IN(); -/// } \endcode -/// -/// In order to ensure that the constant RRClass object has been initialized -/// before the initialization for \c default_class, we need help from -/// the proxy function. -/// -/// Note to developers: same note as \c RRType applies. -class RRClass { -public: - /// - /// \name Constructors and Destructor - /// - //@{ - /// Constructor from an integer class code. - /// - /// This constructor never throws an exception. - /// - /// \param classcode An 16-bit integer code corresponding to the RRClass. - explicit RRClass(uint16_t classcode) : classcode_(classcode) {} - /// - /// A valid string is one of "well-known" textual class representations - /// such as "IN" or "CH", or in the standard format for "unknown" - /// classes as defined in RFC3597, i.e., "CLASSnnnn". - /// - /// More precisely, the "well-known" representations are the ones stored - /// in the \c RRParamRegistry registry (see the class description). - /// - /// As for the format of "CLASSnnnn", "nnnn" must represent a valid 16-bit - /// unsigned integer, which may contain leading 0's as long as it consists - /// of at most 5 characters (inclusive). - /// For example, "CLASS1" and "CLASS0001" are valid and represent the same - /// class, but "CLASS65536" and "CLASS000001" are invalid. - /// A "CLASSnnnn" representation is valid even if the corresponding class - /// code is registered in the \c RRParamRegistry object. For example, both - /// "IN" and "CLASS1" are valid and represent the same class. - /// - /// All of these representations are case insensitive; "IN" and "in", and - /// "CLASS1" and "class1" are all valid and represent the same classes, - /// respectively. - /// - /// If the given string is not recognized as a valid representation of - /// an RR class, an exception of class \c InvalidRRClass will be thrown. - /// - /// \param class_str A string representation of the \c RRClass - explicit RRClass(const std::string& class_str); - /// Constructor from wire-format data. - /// - /// The \c buffer parameter normally stores a complete DNS message - /// containing the RRClass to be constructed. The current read position of - /// the buffer points to the head of the class. - /// - /// If the given data does not large enough to contain a 16-bit integer, - /// an exception of class \c IncompleteRRClass will be thrown. - /// - /// \param buffer A buffer storing the wire format data. - explicit RRClass(isc::util::InputBuffer& buffer); - - /// A separate factory of RRClass from text. - /// - /// This static method is similar to the constructor that takes a - /// string object, but works as a factory and reports parsing - /// failure in the form of the return value. Normally the - /// constructor version should suffice, but in some cases the caller - /// may have to expect mixture of valid and invalid input, and may - /// want to minimize the overhead of possible exception handling. - /// This version is provided for such purpose. - /// - /// For the format of the \c class_str argument, see the - /// <code>RRClass(const std::string&)</code> constructor. - /// - /// If the given text represents a valid RRClass, it returns a - /// pointer to a new \c RRClass object. If the given text does not - /// represent a valid RRClass, it returns \c NULL. - /// - /// One main purpose of this function is to minimize the overhead - /// when the given text does not represent a valid RR class. For - /// this reason this function intentionally omits the capability of - /// delivering a detailed reason for the parse failure, such as in the - /// \c want() string when exception is thrown from the constructor - /// (it will internally require a creation of string object, which - /// is relatively expensive). If such detailed information is - /// necessary, the constructor version should be used to catch the - /// resulting exception. - /// - /// This function never throws the \c InvalidRRClass exception. - /// - /// \param class_str A string representation of the \c RRClass. - /// \return A new RRClass object for the given text or a \c NULL - /// value. - static RRClass* createFromText(const std::string& class_str); - - /// - /// We use the default copy constructor intentionally. - //@} - /// We use the default copy assignment operator intentionally. - /// - - /// - /// \name Converter methods - /// - //@{ - /// \brief Convert the \c RRClass to a string. - /// - /// If a "well known" textual representation for the class code is - /// registered in the RR parameter registry (see the class description), - /// that will be used as the return value of this method. Otherwise, this - /// method creates a new string for an "unknown" class in the format defined - /// in RFC3597, i.e., "CLASSnnnn", and returns it. - /// - /// If resource allocation for the string fails, a corresponding standard - /// exception will be thrown. - /// - /// \return A string representation of the \c RRClass. - const std::string toText() const; - /// \brief Render the \c RRClass in the wire format. - /// - /// This method renders the class code in network byte order via - /// \c renderer, which encapsulates output buffer and other rendering - /// contexts. - /// - /// If resource allocation in rendering process fails, a corresponding - /// standard exception will be thrown. - /// - /// \param renderer DNS message rendering context that encapsulates the - /// output buffer in which the RRClass is to be stored. - void toWire(AbstractMessageRenderer& renderer) const; - /// \brief Render the \c RRClass in the wire format. - /// - /// This method renders the class code in network byte order into the - /// \c buffer. - /// - /// If resource allocation in rendering process fails, a corresponding - /// standard exception will be thrown. - /// - /// \param buffer An output buffer to store the wire data. - void toWire(isc::util::OutputBuffer& buffer) const; - //@} - - /// - /// \name Getter Methods - /// - //@{ - /// \brief Returns the RR class code as a 16-bit unsigned integer. - /// - /// This method never throws an exception. - /// - /// \return An 16-bit integer code corresponding to the RRClass. - uint16_t getCode() const { - return (classcode_); - } - //@} - - /// - /// \name Comparison methods - /// - //@{ - /// \brief Return true iff two RRClasses are equal. - /// - /// Two RRClasses are equal iff their class codes are equal. - /// - /// This method never throws an exception. - /// - /// \param other the \c RRClass object to compare against. - /// \return true if the two RRClasses are equal; otherwise false. - bool equals(const RRClass& other) const { - return (classcode_ == other.classcode_); - } - /// \brief Same as \c equals(). - bool operator==(const RRClass& other) const { - return (equals(other)); - } - - /// \brief Return true iff two RRClasses are not equal. - /// - /// This method never throws an exception. - /// - /// \param other the \c RRClass object to compare against. - /// \return true if the two RRClasses are not equal; otherwise false. - bool nequals(const RRClass& other) const { - return (classcode_ != other.classcode_); - } - /// \brief Same as \c nequals(). - bool operator!=(const RRClass& other) const { - return (nequals(other)); - } - - /// \brief Less-than comparison for RRClass against \c other - /// - /// We define the less-than relationship based on their class codes; - /// one RRClass is less than the other iff the code of the former is less - /// than that of the other as unsigned integers. - /// The relationship is meaningless in terms of DNS protocol; the only - /// reason we define this method is that RRClass objects can be stored in - /// STL containers without requiring user-defined less-than relationship. - /// We therefore don't define other comparison operators. - /// - /// This method never throws an exception. - /// - /// \param other the \c RRClass object to compare against. - /// \return true if \c this RRClass is less than the \c other; otherwise - /// false. - bool operator<(const RRClass& other) const { - return (classcode_ < other.classcode_); - } - - // BEGIN_WELL_KNOWN_CLASS_DECLARATIONS - // END_WELL_KNOWN_CLASS_DECLARATIONS - -private: - uint16_t classcode_; -}; - -// BEGIN_WELL_KNOWN_CLASS_DEFINITIONS -// END_WELL_KNOWN_CLASS_DEFINITIONS - -/// -/// \brief Insert the \c RRClass as a string into stream. -/// -/// This method convert the \c rrclass into a string and inserts it into the -/// output stream \c os. -/// -/// This function overloads the global operator<< to behave as described in -/// ostream::operator<< but applied to \c RRClass objects. -/// -/// \param os A \c std::ostream object on which the insertion operation is -/// performed. -/// \param rrclass The \c RRClass object output by the operation. -/// \return A reference to the same \c std::ostream object referenced by -/// parameter \c os after the insertion operation. -std::ostream& -operator<<(std::ostream& os, const RRClass& rrclass); -} -} -#endif // RRCLASS_H diff --git a/src/lib/dns/rrclass.cc b/src/lib/dns/rrclass.cc index 4617a3e16a..b01f86ede1 100644 --- a/src/lib/dns/rrclass.cc +++ b/src/lib/dns/rrclass.cc @@ -6,21 +6,20 @@ #include <config.h> -#include <stdint.h> - -#include <string> - #include <exceptions/exceptions.h> - -#include <util/buffer.h> #include <dns/messagerenderer.h> #include <dns/rrparamregistry.h> #include <dns/rrclass.h> +#include <util/buffer.h> + +#include <stdint.h> +#include <string> -using namespace std; using namespace isc::dns; using namespace isc::util; +using namespace std; + namespace isc { namespace dns { diff --git a/src/lib/dns/rrclass.h b/src/lib/dns/rrclass.h index b730971454..12d94f2ef3 100644 --- a/src/lib/dns/rrclass.h +++ b/src/lib/dns/rrclass.h @@ -1,11 +1,4 @@ -/////////////// -/////////////// -/////////////// THIS FILE IS AUTOMATICALLY GENERATED BY gen-rdatacode.py. -/////////////// DO NOT EDIT! -/////////////// -/////////////// - -// Copyright (C) 2010-2021 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -295,6 +288,7 @@ public: // BEGIN_WELL_KNOWN_CLASS_DECLARATIONS static const RRClass& ANY(); static const RRClass& IN(); + static const RRClass& CH(); static const RRClass& NONE(); // END_WELL_KNOWN_CLASS_DECLARATIONS @@ -316,6 +310,12 @@ RRClass::IN() { } inline const RRClass& +RRClass::CH() { + static RRClass rrclass(3); + return (rrclass); +} + +inline const RRClass& RRClass::NONE() { static RRClass rrclass(254); return (rrclass); diff --git a/src/lib/dns/rrparamregistry-placeholder.cc b/src/lib/dns/rrparamregistry-placeholder.cc deleted file mode 100644 index da46d12b70..0000000000 --- a/src/lib/dns/rrparamregistry-placeholder.cc +++ /dev/null @@ -1,538 +0,0 @@ -// Copyright (C) 2010-2022 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#include <config.h> - -#include <cassert> -#include <algorithm> -#include <cctype> -#include <functional> -#include <map> -#include <string> -#include <sstream> -#include <utility> - -#include <stdint.h> - -#include <boost/shared_ptr.hpp> - -#include <exceptions/exceptions.h> - -#include <dns/rrparamregistry.h> -#include <dns/rrclass.h> -#include <dns/rrtype.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> - -using namespace std; - -using namespace isc::util; -using namespace isc::dns::rdata; - -namespace isc { -namespace dns { - -namespace { -/// -/// The following function and class are a helper to define case-insensitive -/// equivalence relationship on strings. They are used in the mapping -/// containers below. -/// -bool -CICharLess(char c1, char c2) { - return (tolower(static_cast<unsigned char>(c1)) < - tolower(static_cast<unsigned char>(c2))); -} - -struct CIStringLess { - bool operator()(const string& s1, const string& s2) const - { - return (lexicographical_compare(s1.begin(), s1.end(), - s2.begin(), s2.end(), CICharLess)); - } -}; - -struct RRTypeParam { - RRTypeParam(const string& code_string, uint16_t code) : - code_string_(code_string), code_(code) {} - string code_string_; - uint16_t code_; - - /// magic constants - static const unsigned int MAX_CODE = 0xffff; - static const string& UNKNOWN_PREFIX(); - static size_t UNKNOWN_PREFIXLEN(); - static const string& UNKNOWN_MAX(); - static size_t UNKNOWN_MAXLEN(); -}; - -typedef boost::shared_ptr<RRTypeParam> RRTypeParamPtr; -typedef map<string, RRTypeParamPtr, CIStringLess> StrRRTypeMap; -typedef map<uint16_t, RRTypeParamPtr> CodeRRTypeMap; - -inline const string& -RRTypeParam::UNKNOWN_PREFIX() { - static const string p("TYPE"); - return (p); -} - -inline size_t -RRTypeParam::UNKNOWN_PREFIXLEN() { - static size_t plen = UNKNOWN_PREFIX().size(); - return (plen); -} - -inline const string& -RRTypeParam::UNKNOWN_MAX() { - static const string p("TYPE65535"); - return (p); -} - -inline size_t -RRTypeParam::UNKNOWN_MAXLEN() { - static size_t plen = UNKNOWN_MAX().size(); - return (plen); -} - -struct RRClassParam { - RRClassParam(const string& code_string, uint16_t code) : - code_string_(code_string), code_(code) {} - string code_string_; - uint16_t code_; - - /// magic constants - static const unsigned int MAX_CODE = 0xffff; - static const string& UNKNOWN_PREFIX(); - static size_t UNKNOWN_PREFIXLEN(); - static const string& UNKNOWN_MAX(); - static size_t UNKNOWN_MAXLEN(); -}; - -typedef boost::shared_ptr<RRClassParam> RRClassParamPtr; -typedef map<string, RRClassParamPtr, CIStringLess> StrRRClassMap; -typedef map<uint16_t, RRClassParamPtr> CodeRRClassMap; - -inline const string& -RRClassParam::UNKNOWN_PREFIX() { - static const string p("CLASS"); - return (p); -} - -inline size_t -RRClassParam::UNKNOWN_PREFIXLEN() { - static size_t plen = UNKNOWN_PREFIX().size(); - return (plen); -} - -inline const string& -RRClassParam::UNKNOWN_MAX() { - static const string p("CLASS65535"); - return (p); -} - -inline size_t -RRClassParam::UNKNOWN_MAXLEN() { - static size_t plen = UNKNOWN_MAX().size(); - return (plen); -} -} // end of anonymous namespace - -/// Note: the element ordering in the type/class pair is intentional. -/// The standard library will perform inequality comparison (i.e, '<') -/// in the way that the second elements (RRClass) are compared only when -/// the first elements are equivalent. -/// In practice, when we compare two pairs of RRType and RRClass, RRClass -/// would be the same (and, in particular, be class IN) in the majority of -/// cases. So this comparison ordering should be more efficient in common -/// cases. -typedef pair<RRType, RRClass> RRTypeClass; -typedef map<RRTypeClass, RdataFactoryPtr> RdataFactoryMap; -typedef map<RRType, RdataFactoryPtr> GenericRdataFactoryMap; - -template <typename T> -class RdataFactory : public AbstractRdataFactory { -public: - virtual RdataPtr create(const string& rdata_str) const { - return (RdataPtr(new T(rdata_str))); - } - - virtual RdataPtr create(InputBuffer& buffer, size_t rdata_len) const { - return (RdataPtr(new T(buffer, rdata_len))); - } - - virtual RdataPtr create(const Rdata& source) const { - return (RdataPtr(new T(dynamic_cast<const T&>(source)))); - } - - virtual RdataPtr create(MasterLexer& lexer, const Name* origin, - MasterLoader::Options options, - MasterLoaderCallbacks& callbacks) const { - return (RdataPtr(new T(lexer, origin, options, callbacks))); - } -}; - -/// -/// \brief The \c RRParamRegistryImpl class is the actual implementation of -/// \c RRParamRegistry. -/// -/// The implementation is hidden from applications. We can refer to specific -/// members of this class only within the implementation source file. -/// -struct RRParamRegistryImpl { - /// Mappings from RR type codes to textual representations. - StrRRTypeMap str2typemap; - /// Mappings from textual representations of RR types to integer codes. - CodeRRTypeMap code2typemap; - /// Mappings from RR class codes to textual representations. - StrRRClassMap str2classmap; - /// Mappings from textual representations of RR classes to integer codes. - CodeRRClassMap code2classmap; - RdataFactoryMap rdata_factories; - GenericRdataFactoryMap genericrdata_factories; -}; - -RRParamRegistry::RRParamRegistry() : impl_(new RRParamRegistryImpl()) { // set up parameters for well-known RRs - try { - // BEGIN_WELL_KNOWN_PARAMS - // END_WELL_KNOWN_PARAMS - } catch (...) { - throw; - } -} - -RRParamRegistry::~RRParamRegistry() { -} - -RRParamRegistry& -RRParamRegistry::getRegistry() { - static RRParamRegistry registry; - - return (registry); -} - -void -RRParamRegistry::add(const std::string& typecode_string, uint16_t typecode, - RdataFactoryPtr rdata_factory) -{ - bool type_added = false; - try { - type_added = addType(typecode_string, typecode); - impl_->genericrdata_factories.insert(pair<RRType, RdataFactoryPtr>( - RRType(typecode), - rdata_factory)); - } catch (...) { - if (type_added) { - removeType(typecode); - } - throw; - } -} - -void -RRParamRegistry::add(const std::string& typecode_string, uint16_t typecode, - const std::string& classcode_string, uint16_t classcode, - RdataFactoryPtr rdata_factory) -{ - // Rollback logic on failure is complicated. If adding the new type or - // class fails, we should revert to the original state, cleaning up - // intermediate state. But we need to make sure that we don't remove - // existing data. addType()/addClass() will simply ignore an attempt to - // add the same data, so the cleanup should be performed only when we add - // something new but we fail in other part of the process. - bool type_added = false; - bool class_added = false; - - try { - type_added = addType(typecode_string, typecode); - class_added = addClass(classcode_string, classcode); - impl_->rdata_factories.insert(pair<RRTypeClass, RdataFactoryPtr>( - RRTypeClass(RRType(typecode), - RRClass(classcode)), - rdata_factory)); - } catch (...) { - if (type_added) { - removeType(typecode); - } - if (class_added) { - removeClass(classcode); - } - throw; - } -} - -bool -RRParamRegistry::removeRdataFactory(const RRType& rrtype, - const RRClass& rrclass) { - RdataFactoryMap::iterator found = - impl_->rdata_factories.find(RRTypeClass(rrtype, rrclass)); - if (found != impl_->rdata_factories.end()) { - impl_->rdata_factories.erase(found); - return (true); - } - - return (false); -} - -bool -RRParamRegistry::removeRdataFactory(const RRType& rrtype) { - GenericRdataFactoryMap::iterator found = - impl_->genericrdata_factories.find(rrtype); - if (found != impl_->genericrdata_factories.end()) { - impl_->genericrdata_factories.erase(found); - return (true); - } - - return (false); -} - -namespace { -/// -/// These are helper functions to implement case-insensitive string comparison. -/// This could be simplified using strncasecmp(), but unfortunately it's not -/// included in <cstring>. To be as much as portable within the C++ standard -/// we take the "in house" approach here. -/// -bool CICharEqual(char c1, char c2) { - return (tolower(static_cast<unsigned char>(c1)) == - tolower(static_cast<unsigned char>(c2))); -} - -bool -caseStringEqual(const string& s1, const string& s2, size_t n) { - assert(s1.size() >= n && s2.size() >= n); - - return (mismatch(s1.begin(), s1.begin() + n, s2.begin(), CICharEqual).first - == s1.begin() + n); -} - -/// Code logic for RRTypes and RRClasses is mostly common except (C++) type and -/// member names. So we define type-independent templates to describe the -/// common logic and let concrete classes use it to avoid code duplicates. -/// The following summarize template parameters used in the set of template -/// functions: -/// PT: parameter type, either RRTypeParam or RRClassParam -/// MC: type of mapping class from code: either CodeRRTypeMap or CodeRRClassMap -/// MS: type of mapping class from string: either StrRRTypeMap or StrRRClassMap -/// ET: exception type for error handling: either InvalidRRType or -/// InvalidRRClass -template <typename PT, typename MC, typename MS, typename ET> -inline bool -addParam(const string& code_string, uint16_t code, MC& codemap, MS& stringmap) { - // Duplicate type check - typename MC::const_iterator found = codemap.find(code); - if (found != codemap.end()) { - if (found->second->code_string_ != code_string) { - isc_throw(ET, "Duplicate RR parameter registration"); - } - return (false); - } - - typedef boost::shared_ptr<PT> ParamPtr; - typedef pair<string, ParamPtr> StrParamPair; - typedef pair<uint16_t, ParamPtr> CodeParamPair; - ParamPtr param = ParamPtr(new PT(code_string, code)); - try { - stringmap.insert(StrParamPair(code_string, param)); - codemap.insert(CodeParamPair(code, param)); - } catch (...) { - // Rollback to the previous state: not all of the erase operations will - // find the entry, but we don't care. - stringmap.erase(code_string); - codemap.erase(code); - throw; - } - - return (true); -} - -template <typename MC, typename MS> -inline bool -removeParam(uint16_t code, MC& codemap, MS& stringmap) { - typename MC::iterator found = codemap.find(code); - - if (found != codemap.end()) { - size_t erased = stringmap.erase(found->second->code_string_); - // We must have a corresponding entry of the str2 map exists - assert(erased == 1); - - codemap.erase(found); - - return (true); - } - - return (false); -} - -template <typename PT, typename MS> -inline bool -textToCode(const string& code_str, MS& stringmap, uint16_t& ret_code) { - typename MS::const_iterator found; - - found = stringmap.find(code_str); - if (found != stringmap.end()) { - ret_code = found->second->code_; - return (true); - } - - size_t l = code_str.size(); - if (l > PT::UNKNOWN_PREFIXLEN() && - l <= PT::UNKNOWN_MAXLEN() && - caseStringEqual(code_str, PT::UNKNOWN_PREFIX(), - PT::UNKNOWN_PREFIXLEN())) { - unsigned int code; - istringstream iss(code_str.substr(PT::UNKNOWN_PREFIXLEN(), - l - PT::UNKNOWN_PREFIXLEN())); - iss >> dec >> code; - if (iss.rdstate() == ios::eofbit && code <= PT::MAX_CODE) { - ret_code = code; - return (true); - } - } - - return (false); -} - -template <typename PT, typename MC> -inline string -codeToText(uint16_t code, MC& codemap) { - typename MC::const_iterator found; - - found = codemap.find(code); - if (found != codemap.end()) { - return (found->second->code_string_); - } - - ostringstream ss; - ss << code; - return (PT::UNKNOWN_PREFIX() + ss.str()); -} -} - -bool -RRParamRegistry::addType(const string& type_string, uint16_t code) { - return (addParam<RRTypeParam, CodeRRTypeMap, StrRRTypeMap, RRTypeExists> - (type_string, code, impl_->code2typemap, impl_->str2typemap)); -} - -bool -RRParamRegistry::removeType(uint16_t code) { - return (removeParam<CodeRRTypeMap, StrRRTypeMap>(code, impl_->code2typemap, - impl_->str2typemap)); -} - -bool -RRParamRegistry::textToTypeCode(const string& type_string, - uint16_t& type_code) const { - return (textToCode<RRTypeParam, StrRRTypeMap> - (type_string, impl_->str2typemap, type_code)); -} - -string -RRParamRegistry::codeToTypeText(uint16_t code) const { - return (codeToText<RRTypeParam, CodeRRTypeMap>(code, impl_->code2typemap)); -} - -bool -RRParamRegistry::addClass(const string& class_string, uint16_t code) { - return (addParam<RRClassParam, CodeRRClassMap, StrRRClassMap, RRClassExists> - (class_string, code, impl_->code2classmap, impl_->str2classmap)); -} - -bool -RRParamRegistry::removeClass(uint16_t code) { - return (removeParam<CodeRRClassMap, StrRRClassMap>(code, - impl_->code2classmap, - impl_->str2classmap)); -} - -bool -RRParamRegistry::textToClassCode(const string& class_string, - uint16_t& class_code) const { - return (textToCode<RRClassParam, StrRRClassMap> - (class_string, impl_->str2classmap, class_code)); -} - -string -RRParamRegistry::codeToClassText(uint16_t code) const { - return (codeToText<RRClassParam, CodeRRClassMap>(code, - impl_->code2classmap)); -} - -namespace { -inline const AbstractRdataFactory* -findRdataFactory(RRParamRegistryImpl* reg_impl, - const RRType& rrtype, const RRClass& rrclass) { - RdataFactoryMap::const_iterator found; - found = reg_impl->rdata_factories.find(RRTypeClass(rrtype, rrclass)); - if (found != reg_impl->rdata_factories.end()) { - return (found->second.get()); - } - - GenericRdataFactoryMap::const_iterator genfound = - reg_impl->genericrdata_factories.find(rrtype); - if (genfound != reg_impl->genericrdata_factories.end()) { - return (genfound->second.get()); - } - - return (NULL); -} -} - -RdataPtr -RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass, - const std::string& rdata_string) { - // If the text indicates that it's rdata of an "unknown" type (beginning - // with '\# n'), parse it that way. (TBD) - - const AbstractRdataFactory* factory = - findRdataFactory(impl_, rrtype, rrclass); - if (factory != NULL) { - return (factory->create(rdata_string)); - } - - return (RdataPtr(new generic::Generic(rdata_string))); -} - -RdataPtr -RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass, - InputBuffer& buffer, size_t rdata_len) { - const AbstractRdataFactory* factory = - findRdataFactory(impl_, rrtype, rrclass); - if (factory != NULL) { - return (factory->create(buffer, rdata_len)); - } - - return (RdataPtr(new generic::Generic(buffer, rdata_len))); -} - -RdataPtr -RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass, - const Rdata& source) { - const AbstractRdataFactory* factory = - findRdataFactory(impl_, rrtype, rrclass); - if (factory != NULL) { - return (factory->create(source)); - } - - return (RdataPtr(new rdata::generic::Generic( - dynamic_cast<const generic::Generic&>(source)))); -} - -RdataPtr -RRParamRegistry::createRdata(const RRType& rrtype, const RRClass& rrclass, - MasterLexer& lexer, const Name* name, - MasterLoader::Options options, - MasterLoaderCallbacks& callbacks) { - const AbstractRdataFactory* factory = - findRdataFactory(impl_, rrtype, rrclass); - if (factory != NULL) { - return (factory->create(lexer, name, options, callbacks)); - } - - return (RdataPtr(new generic::Generic(lexer, name, options, callbacks))); -} -} -} diff --git a/src/lib/dns/rrparamregistry.cc b/src/lib/dns/rrparamregistry.cc index 91a0e5420c..612878b522 100644 --- a/src/lib/dns/rrparamregistry.cc +++ b/src/lib/dns/rrparamregistry.cc @@ -1,46 +1,34 @@ -// Copyright (C) 2010-2022 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -/// @file rrparamregistry.cc -/// -/// THIS FILE USED TO BE AUTOMATICALLY GENERATED BY gen-rdatacode.py. -/// Once this library was taken over by Kea project, we do not develop -/// the DNS capabilities anymore, as Kea focuses on DHCP, not DNS. -/// As such, we stopped adding new RR types as those we have are -/// sufficient. Therefore it's unlikely this file will ever be -/// regenerated. - #include <config.h> +#include <exceptions/exceptions.h> +#include <dns/rrparamregistry.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> + #include <cassert> #include <algorithm> #include <cctype> #include <functional> #include <map> +#include <stdint.h> #include <string> #include <sstream> #include <utility> - -#include <stdint.h> - #include <boost/shared_ptr.hpp> -#include <exceptions/exceptions.h> - -#include <dns/rrparamregistry.h> -#include <dns/rrclass.h> -#include <dns/rrtype.h> -#include <dns/rdata.h> -#include <dns/rdataclass.h> - -using namespace std; - using namespace isc::util; using namespace isc::dns::rdata; +using namespace std; + namespace isc { namespace dns { @@ -208,21 +196,26 @@ RRParamRegistry::RRParamRegistry() : impl_(new RRParamRegistryImpl()) { try { // BEGIN_WELL_KNOWN_PARAMS add("A", 1, "IN", 1, RdataFactoryPtr(new RdataFactory<in::A>())); + add("NS", 2, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::NS>())); add("SOA", 6, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::SOA>())); add("PTR", 12, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::PTR>())); + add("TXT", 16, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::TXT>())); add("AAAA", 28, "IN", 1, RdataFactoryPtr(new RdataFactory<in::AAAA>())); add("OPT", 41, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::OPT>())); add("RRSIG", 46, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::RRSIG>())); add("DHCID", 49, "IN", 1, RdataFactoryPtr(new RdataFactory<in::DHCID>())); add("TKEY", 249, "IN", 1, RdataFactoryPtr(new RdataFactory<generic::TKEY>())); add("TSIG", 250, "ANY", 255, RdataFactoryPtr(new RdataFactory<any::TSIG>())); + add("NS", 2, RdataFactoryPtr(new RdataFactory<generic::NS>())); add("SOA", 6, RdataFactoryPtr(new RdataFactory<generic::SOA>())); add("PTR", 12, RdataFactoryPtr(new RdataFactory<generic::PTR>())); + add("TXT", 16, RdataFactoryPtr(new RdataFactory<generic::TXT>())); add("OPT", 41, RdataFactoryPtr(new RdataFactory<generic::OPT>())); add("RRSIG", 46, RdataFactoryPtr(new RdataFactory<generic::RRSIG>())); add("TKEY", 249, RdataFactoryPtr(new RdataFactory<generic::TKEY>())); addType("ANY", 255); // Meta classes + addClass("CH", 3); addClass("NONE", 254); // END_WELL_KNOWN_PARAMS } catch (...) { diff --git a/src/lib/dns/rrparamregistry.h b/src/lib/dns/rrparamregistry.h index 36aa1e6b45..578b5ab3e9 100644 --- a/src/lib/dns/rrparamregistry.h +++ b/src/lib/dns/rrparamregistry.h @@ -94,7 +94,8 @@ public: /// \c Rdata to parse. /// \param rdata_len The length in buffer of the \c Rdata. In bytes. /// \return An \c RdataPtr object pointing to the created \c Rdata object. - virtual RdataPtr create(isc::util::InputBuffer& buffer, size_t rdata_len) const = 0; + virtual RdataPtr create(isc::util::InputBuffer& buffer, + size_t rdata_len) const = 0; /// \brief Create RDATA from another \c Rdata object of the same type. /// diff --git a/src/lib/dns/rrset.cc b/src/lib/dns/rrset.cc index 5458279197..1ab3bde031 100644 --- a/src/lib/dns/rrset.cc +++ b/src/lib/dns/rrset.cc @@ -6,25 +6,25 @@ #include <config.h> -#include <algorithm> -#include <string> -#include <vector> - -#include <boost/shared_ptr.hpp> - -#include <util/buffer.h> #include <dns/messagerenderer.h> #include <dns/name.h> #include <dns/rrclass.h> #include <dns/rrtype.h> #include <dns/rrttl.h> #include <dns/rrset.h> +#include <util/buffer.h> + +#include <algorithm> +#include <string> +#include <vector> +#include <boost/shared_ptr.hpp> -using namespace std; using namespace isc::dns; using namespace isc::util; using namespace isc::dns::rdata; +using namespace std; + namespace isc { namespace dns { void @@ -70,9 +70,9 @@ namespace { // unnamed namespace // FIXME: This method's code should somehow be unified with // BasicRRsetImpl::toWire() below to avoid duplication. template <typename T> -inline unsigned int +inline uint32_t rrsetToWire(const AbstractRRset& rrset, T& output, const size_t limit) { - unsigned int n = 0; + uint32_t n = 0; RdataIteratorPtr it = rrset.getRdataIterator(); if (it->isLast()) { @@ -124,14 +124,14 @@ rrsetToWire(const AbstractRRset& rrset, T& output, const size_t limit) { } // end of unnamed namespace -unsigned int +uint32_t AbstractRRset::toWire(OutputBuffer& buffer) const { return (rrsetToWire<OutputBuffer>(*this, buffer, 0)); } -unsigned int +uint32_t AbstractRRset::toWire(AbstractMessageRenderer& renderer) const { - const unsigned int rrs_written = rrsetToWire<AbstractMessageRenderer>( + const uint32_t rrs_written = rrsetToWire<AbstractMessageRenderer>( *this, renderer, renderer.getLengthLimit()); if (getRdataCount() > rrs_written) { renderer.setTruncated(); @@ -163,7 +163,7 @@ public: const RRType& rrtype, const RRTTL& ttl) : name_(name), rrclass_(rrclass), rrtype_(rrtype), ttl_(ttl) {} - unsigned int toWire(AbstractMessageRenderer& renderer, size_t limit) const; + uint32_t toWire(AbstractMessageRenderer& renderer, size_t limit) const; Name name_; RRClass rrclass_; @@ -177,7 +177,7 @@ public: // FIXME: This method's code should somehow be unified with // rrsetToWire() above to avoid duplication. -unsigned int +uint32_t BasicRRsetImpl::toWire(AbstractMessageRenderer& renderer, size_t limit) const { if (rdatalist_.empty()) { // empty rrsets are only allowed for classes ANY and NONE @@ -197,7 +197,7 @@ BasicRRsetImpl::toWire(AbstractMessageRenderer& renderer, size_t limit) const { return (1); } - unsigned int n = 0; + uint32_t n = 0; // sort the set of Rdata based on rrset-order and sortlist, and possible // other options. Details to be considered. @@ -250,7 +250,7 @@ BasicRRset::addRdata(const std::string& rdata_str) { addRdata(createRdata(getType(), getClass(), rdata_str)); } -unsigned int +uint32_t BasicRRset::getRdataCount() const { return (impl_->rdatalist_.size()); } @@ -329,14 +329,14 @@ BasicRRset::getLength() const { return (length); } -unsigned int +uint32_t BasicRRset::toWire(OutputBuffer& buffer) const { return (AbstractRRset::toWire(buffer)); } -unsigned int +uint32_t BasicRRset::toWire(AbstractMessageRenderer& renderer) const { - const unsigned int rrs_written = impl_->toWire(renderer, + const uint32_t rrs_written = impl_->toWire(renderer, renderer.getLengthLimit()); if (impl_->rdatalist_.size() > rrs_written) { renderer.setTruncated(); @@ -345,14 +345,14 @@ BasicRRset::toWire(AbstractMessageRenderer& renderer) const { } RRset::RRset(const Name& name, const RRClass& rrclass, - const RRType& rrtype, const RRTTL& ttl) : + const RRType& rrtype, const RRTTL& ttl) : BasicRRset(name, rrclass, rrtype, ttl) { } RRset::~RRset() { } -unsigned int +uint32_t RRset::getRRsigDataCount() const { if (rrsig_) { return (rrsig_->getRdataCount()); @@ -376,9 +376,9 @@ RRset::getLength() const { return (length); } -unsigned int +uint32_t RRset::toWire(OutputBuffer& buffer) const { - unsigned int rrs_written = BasicRRset::toWire(buffer); + uint32_t rrs_written = BasicRRset::toWire(buffer); if (getRdataCount() > rrs_written) { return (rrs_written); } @@ -390,9 +390,9 @@ RRset::toWire(OutputBuffer& buffer) const { return (rrs_written); } -unsigned int +uint32_t RRset::toWire(AbstractMessageRenderer& renderer) const { - unsigned int rrs_written = BasicRRset::toWire(renderer); + uint32_t rrs_written = BasicRRset::toWire(renderer); if (getRdataCount() > rrs_written) { return (rrs_written); } diff --git a/src/lib/dns/rrset.h b/src/lib/dns/rrset.h index ece89697ad..26b92eec23 100644 --- a/src/lib/dns/rrset.h +++ b/src/lib/dns/rrset.h @@ -16,12 +16,9 @@ #include <dns/rdata.h> #include <dns/rrtype.h> +#include <util/buffer.h> namespace isc { -namespace util { -class OututBuffer; -} - namespace dns { /// @@ -196,7 +193,7 @@ public: /// this method may return 0. /// /// \return The number of \c Rdata objects contained. - virtual unsigned int getRdataCount() const = 0; + virtual uint32_t getRdataCount() const = 0; /// \brief Get the wire format length of the \c AbstractRRset. /// @@ -316,7 +313,7 @@ public: /// \return The number of RRs rendered. If the truncation is necessary /// this value may be different from the number of RDATA objects contained /// in the RRset. - virtual unsigned int toWire(AbstractMessageRenderer& renderer) const = 0; + virtual uint32_t toWire(AbstractMessageRenderer& renderer) const = 0; /// \brief Render the RRset in the wire format without any compression. /// @@ -324,7 +321,7 @@ public: /// /// \param buffer An output buffer to store the wire data. /// \return The number of RRs rendered. - virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const = 0; + virtual uint32_t toWire(isc::util::OutputBuffer& buffer) const = 0; //@} /// @@ -450,7 +447,7 @@ public: /// method may return 0. /// /// \return The number of \c RRSIG records associated. - virtual unsigned int getRRsigDataCount() const = 0; + virtual uint32_t getRRsigDataCount() const = 0; /// \brief Adds RRSIG RRset RRs to the associated RRSIG RRset /// @@ -664,7 +661,7 @@ public: /// This method never throws an exception. /// /// \return The number of \c Rdata objects contained. - virtual unsigned int getRdataCount() const; + virtual uint32_t getRdataCount() const; /// \brief Get the wire format length of the \c BasicRRset. /// @@ -729,13 +726,13 @@ public: /// /// This method simply uses the default implementation. /// See \c AbstractRRset::toWire(MessageRenderer&)const. - virtual unsigned int toWire(AbstractMessageRenderer& renderer) const; + virtual uint32_t toWire(AbstractMessageRenderer& renderer) const; /// \brief Render the RRset in the wire format without any compression. /// /// This method simply uses the default implementation. /// See \c AbstractRRset::toWire(OutputBuffer&)const. - virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const; + virtual uint32_t toWire(isc::util::OutputBuffer& buffer) const; //@} /// @@ -801,7 +798,7 @@ public: /// /// \return Always returns 0. Associated RRSIG RRsets are not /// supported in this class. - virtual unsigned int getRRsigDataCount() const { + virtual uint32_t getRRsigDataCount() const { return (0); } @@ -862,12 +859,12 @@ public: /// truncation handling. /// /// See \c AbstractRRset::toWire(MessageRenderer&)const. - virtual unsigned int toWire(AbstractMessageRenderer& renderer) const; + virtual uint32_t toWire(AbstractMessageRenderer& renderer) const; /// \brief Render the RRset in the wire format without any compression. /// /// See \c AbstractRRset::toWire(OutputBuffer&)const. - virtual unsigned int toWire(isc::util::OutputBuffer& buffer) const; + virtual uint32_t toWire(isc::util::OutputBuffer& buffer) const; /// \brief Updates the owner name of the \c RRset, including RRSIGs if any virtual void setTTL(const RRTTL& ttl) { @@ -927,7 +924,7 @@ public: /// method may return 0. /// /// \return The number of \c RRSIG records associated. - virtual unsigned int getRRsigDataCount() const; + virtual uint32_t getRRsigDataCount() const; private: RRsetPtr rrsig_; diff --git a/src/lib/dns/rrttl.cc b/src/lib/dns/rrttl.cc index 83877c74ed..8d9a7112d0 100644 --- a/src/lib/dns/rrttl.cc +++ b/src/lib/dns/rrttl.cc @@ -6,18 +6,17 @@ #include <config.h> -#include <stdint.h> - -#include <sstream> -#include <ostream> - -#include <util/buffer.h> #include <dns/messagerenderer.h> #include <dns/rrttl.h> +#include <util/buffer.h> -#include <boost/lexical_cast.hpp> #include <algorithm> #include <cctype> +#include <stdint.h> +#include <sstream> +#include <ostream> + +#include <boost/lexical_cast.hpp> using namespace std; using namespace isc::dns; diff --git a/src/lib/dns/rrtype-placeholder.h b/src/lib/dns/rrtype-placeholder.h deleted file mode 100644 index 4e9c99acce..0000000000 --- a/src/lib/dns/rrtype-placeholder.h +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") -// -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. - -#ifndef RRTYPE_H -#define RRTYPE_H - -#include <stdint.h> - -#include <string> -#include <ostream> - -#include <dns/exceptions.h>' -#include <util/buffer.h> - -// Solaris x86 defines DS in <sys/regset.h>, which gets pulled in by Boost -#if defined(__sun) && defined(DS) -# undef DS -#endif - -namespace isc { -namespace dns { - -// forward declarations -class AbstractMessageRenderer; -/// -/// The \c RRType class encapsulates DNS resource record types. -/// -/// This class manages the 16-bit integer type codes in quite a straightforward -/// way. The only non trivial task is to handle textual representations of -/// RR types, such as "A", "AAAA", or "TYPE65534". -/// -/// This class consults a helper \c RRParamRegistry class, which is a registry -/// of RR related parameters and has the singleton object. This registry -/// provides a mapping between RR type codes and their "well-known" textual -/// representations. -/// Parameters of RR types defined by DNS protocol standards are automatically -/// registered at initialization time and are ensured to be always available for -/// applications unless the application explicitly modifies the registry. -/// -/// For convenience, this class defines constant class objects corresponding to -/// standard RR types. These are generally referred to as the form of -/// <code>RRType::{type-text}()</code>. -/// For example, \c RRType::NS() is an \c RRType object corresponding to the NS -/// resource record (type code 2). -/// Note that these constants are used through a "proxy" function. -/// This is because they may be used to initialize another non-local (e.g. -/// global or namespace-scope) static object as follows: -/// -/// \code -/// namespace foo { -/// const RRType default_type = RRType::A(); -/// } \endcode -/// -/// In order to ensure that the constant RRType object has been initialized -/// before the initialization for \c default_type, we need help from -/// the proxy function. -/// -/// In the current implementation, the initialization of the well-known -/// static objects is not thread safe. The same consideration as the -/// \c RRParamRegistry class applies. We may extend the implementation so -/// that the initialization is ensured to be thread safe in a future version. -/// -/// Note to developers: since it's expected that some of these constant -/// \c RRType objects are frequently used in a performance sensitive path, -/// we define these proxy functions as inline. This makes sense only when -/// the corresponding static objects are defined only once even if they used -/// in different source files. Sufficiently modern compilers should meet -/// this assumption, but if we encounter memory bloat due to this problem with -/// particular compilers we need to revisit the design or think about -/// workaround. -class RRType { -public: - /// - /// \name Constructors and Destructor - /// - //@{ - /// Constructor from an integer type code. - /// - /// This constructor never throws an exception. - /// - /// \param typecode An 16-bit integer code corresponding to the RRType. - explicit RRType(uint16_t typecode) : typecode_(typecode) {} - /// Constructor from a string. - /// - /// A valid string is one of "well-known" textual type representations - /// such as "A", "AAAA", or "NS", or in the standard format for "unknown" - /// RR types as defined in RFC3597, i.e., "TYPEnnnn". - /// - /// More precisely, the "well-known" representations are the ones stored - /// in the \c RRParamRegistry registry (see the class description). - /// - /// As for the format of "TYPEnnnn", "nnnn" must represent a valid 16-bit - /// unsigned integer, which may contain leading 0's as long as it consists - /// of at most 5 characters (inclusive). - /// For example, "TYPE1" and "TYPE001" are valid and represent the same - /// RR type, but "TYPE65536" and "TYPE000001" are invalid. - /// A "TYPEnnnn" representation is valid even if the corresponding type code - /// is registered in the \c RRParamRegistry object. For example, both - /// "A" and "TYPE1" are valid and represent the same RR type. - /// - /// All of these representations are case insensitive; "NS" and "ns", and - /// "TYPE1" and "type1" are all valid and represent the same RR types, - /// respectively. - /// - /// If the given string is not recognized as a valid representation of - /// an RR type, an exception of class \c InvalidRRType will be thrown. - /// - /// \param typestr A string representation of the \c RRType - explicit RRType(const std::string& typestr); - /// Constructor from wire-format data. - /// - /// The \c buffer parameter normally stores a complete DNS message - /// containing the RRType to be constructed. The current read position of - /// the buffer points to the head of the type. - /// - /// If the given data does not large enough to contain a 16-bit integer, - /// an exception of class \c IncompleteRRType will be thrown. - /// - /// \param buffer A buffer storing the wire format data. - explicit RRType(isc::util::InputBuffer& buffer); - /// - /// We use the default copy constructor intentionally. - //@} - /// We use the default copy assignment operator intentionally. - /// - /// - /// \name Getter Methods - /// - //@{ - /// \brief Returns the RR type code as a 16-bit unsigned integer. - /// - /// This method never throws an exception. - /// - /// \return An 16-bit integer code corresponding to the RRType. - uint16_t getCode() const { - return (typecode_); - } - //@} - // BEGIN_WELL_KNOWN_TYPE_DECLARATIONS - // END_WELL_KNOWN_TYPE_DECLARATIONS - -private: - uint16_t typecode_; -}; - -// BEGIN_WELL_KNOWN_TYPE_DEFINITIONS -// END_WELL_KNOWN_TYPE_DEFINITIONS -} -} -#endif // RRTYPE_H diff --git a/src/lib/dns/rrtype.cc b/src/lib/dns/rrtype.cc index 242af518ba..2944cb6bdd 100644 --- a/src/lib/dns/rrtype.cc +++ b/src/lib/dns/rrtype.cc @@ -6,22 +6,21 @@ #include <config.h> -#include <stdint.h> - -#include <string> -#include <ostream> - #include <exceptions/exceptions.h> - -#include <util/buffer.h> #include <dns/messagerenderer.h> #include <dns/rrparamregistry.h> #include <dns/rrtype.h> +#include <util/buffer.h> + +#include <stdint.h> +#include <string> +#include <ostream> -using namespace std; using namespace isc::util; using isc::dns::RRType; +using namespace std; + namespace isc { namespace dns { diff --git a/src/lib/dns/rrtype.h b/src/lib/dns/rrtype.h index 6f2c583323..63d8e5282e 100644 --- a/src/lib/dns/rrtype.h +++ b/src/lib/dns/rrtype.h @@ -1,11 +1,4 @@ -/////////////// -/////////////// -/////////////// THIS FILE IS AUTOMATICALLY GENERATED BY gen-rdatacode.py. -/////////////// DO NOT EDIT! -/////////////// -/////////////// - -// Copyright (C) 2010-2021 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -265,15 +258,17 @@ public: //@} // BEGIN_WELL_KNOWN_TYPE_DECLARATIONS - static const RRType& TSIG(); + static const RRType& A(); + static const RRType& NS(); + static const RRType& SOA(); static const RRType& OPT(); static const RRType& PTR(); - static const RRType& RRSIG(); - static const RRType& SOA(); - static const RRType& TKEY(); - static const RRType& A(); + static const RRType& TXT(); static const RRType& AAAA(); + static const RRType& RRSIG(); static const RRType& DHCID(); + static const RRType& TKEY(); + static const RRType& TSIG(); static const RRType& ANY(); // END_WELL_KNOWN_TYPE_DECLARATIONS @@ -284,14 +279,20 @@ private: // BEGIN_WELL_KNOWN_TYPE_DEFINITIONS inline const RRType& -RRType::TSIG() { - static RRType rrtype(250); +RRType::A() { + static RRType rrtype(1); return (rrtype); } inline const RRType& -RRType::A() { - static RRType rrtype(1); +RRType::NS() { + static RRType rrtype(2); + return (rrtype); +} + +inline const RRType& +RRType::SOA() { + static RRType rrtype(6); return (rrtype); } @@ -308,32 +309,38 @@ RRType::PTR() { } inline const RRType& -RRType::RRSIG() { - static RRType rrtype(46); +RRType::TXT() { + static RRType rrtype(16); return (rrtype); } inline const RRType& -RRType::SOA() { - static RRType rrtype(6); +RRType::AAAA() { + static RRType rrtype(28); return (rrtype); } inline const RRType& -RRType::TKEY() { - static RRType rrtype(249); +RRType::RRSIG() { + static RRType rrtype(46); return (rrtype); } inline const RRType& -RRType::AAAA() { - static RRType rrtype(28); +RRType::DHCID() { + static RRType rrtype(49); return (rrtype); } inline const RRType& -RRType::DHCID() { - static RRType rrtype(49); +RRType::TKEY() { + static RRType rrtype(249); + return (rrtype); +} + +inline const RRType& +RRType::TSIG() { + static RRType rrtype(250); return (rrtype); } diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am index c89c3db48b..113bf4a9e6 100644 --- a/src/lib/dns/tests/Makefile.am +++ b/src/lib/dns/tests/Makefile.am @@ -28,49 +28,28 @@ run_unittests_SOURCES += master_lexer_unittest.cc run_unittests_SOURCES += master_loader_unittest.cc run_unittests_SOURCES += master_lexer_state_unittest.cc run_unittests_SOURCES += name_unittest.cc -run_unittests_SOURCES += nsec3hash_unittest.cc -run_unittests_SOURCES += rrclass_unittest.cc rrtype_unittest.cc run_unittests_SOURCES += rrttl_unittest.cc -run_unittests_SOURCES += rrcollator_unittest.cc +run_unittests_SOURCES += rrclass_unittest.cc +run_unittests_SOURCES += rrtype_unittest.cc run_unittests_SOURCES += opcode_unittest.cc run_unittests_SOURCES += rcode_unittest.cc run_unittests_SOURCES += rdata_unittest.h rdata_unittest.cc -run_unittests_SOURCES += rdatafields_unittest.cc -run_unittests_SOURCES += rdata_pimpl_holder_unittest.cc run_unittests_SOURCES += rdata_char_string_unittest.cc run_unittests_SOURCES += rdata_char_string_data_unittest.cc -run_unittests_SOURCES += rdata_in_a_unittest.cc rdata_in_aaaa_unittest.cc -run_unittests_SOURCES += rdata_ns_unittest.cc rdata_soa_unittest.cc +run_unittests_SOURCES += rdata_in_a_unittest.cc +run_unittests_SOURCES += rdata_in_aaaa_unittest.cc +run_unittests_SOURCES += rdata_ns_unittest.cc +run_unittests_SOURCES += rdata_soa_unittest.cc run_unittests_SOURCES += rdata_txt_like_unittest.cc -run_unittests_SOURCES += rdata_mx_unittest.cc -run_unittests_SOURCES += rdata_sshfp_unittest.cc -run_unittests_SOURCES += rdata_ptr_unittest.cc rdata_cname_unittest.cc -run_unittests_SOURCES += rdata_dname_unittest.cc -run_unittests_SOURCES += rdata_afsdb_unittest.cc +run_unittests_SOURCES += rdata_ptr_unittest.cc run_unittests_SOURCES += rdata_opt_unittest.cc run_unittests_SOURCES += rdata_dhcid_unittest.cc -run_unittests_SOURCES += rdata_dnskey_unittest.cc -run_unittests_SOURCES += rdata_ds_like_unittest.cc -run_unittests_SOURCES += rdata_nsec_unittest.cc -run_unittests_SOURCES += rdata_nsec3_unittest.cc -run_unittests_SOURCES += rdata_nsecbitmap_unittest.cc -run_unittests_SOURCES += rdata_nsec3param_unittest.cc -run_unittests_SOURCES += rdata_nsec3param_like_unittest.cc run_unittests_SOURCES += rdata_rrsig_unittest.cc -run_unittests_SOURCES += rdata_rp_unittest.cc -run_unittests_SOURCES += rdata_srv_unittest.cc -run_unittests_SOURCES += rdata_tlsa_unittest.cc -run_unittests_SOURCES += rdata_minfo_unittest.cc run_unittests_SOURCES += rdata_tsig_unittest.cc -run_unittests_SOURCES += rdata_naptr_unittest.cc -run_unittests_SOURCES += rdata_hinfo_unittest.cc -run_unittests_SOURCES += rdata_caa_unittest.cc run_unittests_SOURCES += rdata_tkey_unittest.cc run_unittests_SOURCES += rrset_unittest.cc -run_unittests_SOURCES += qid_gen_unittest.cc run_unittests_SOURCES += question_unittest.cc run_unittests_SOURCES += rrparamregistry_unittest.cc -run_unittests_SOURCES += masterload_unittest.cc run_unittests_SOURCES += message_unittest.cc run_unittests_SOURCES += serial_unittest.cc run_unittests_SOURCES += tsig_unittest.cc @@ -78,8 +57,6 @@ run_unittests_SOURCES += tsigerror_unittest.cc run_unittests_SOURCES += tsigkey_unittest.cc run_unittests_SOURCES += tsigrecord_unittest.cc run_unittests_SOURCES += master_loader_callbacks_test.cc -run_unittests_SOURCES += rrset_collection_unittest.cc -run_unittests_SOURCES += zone_checker_unittest.cc run_unittests_SOURCES += run_unittests.cc run_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES) run_unittests_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS) diff --git a/src/lib/dns/tests/dns_exceptions_unittest.cc b/src/lib/dns/tests/dns_exceptions_unittest.cc index 8b13789179..a8d25907c7 100644 --- a/src/lib/dns/tests/dns_exceptions_unittest.cc +++ b/src/lib/dns/tests/dns_exceptions_unittest.cc @@ -1 +1,65 @@ +// Copyright (C) 2014-2015,2017 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <dns/exceptions.h> + +#include <gtest/gtest.h> + +namespace { // begin unnamed namespace + +TEST(DNSExceptionsTest, checkExceptionsHierarchy) { + EXPECT_NO_THROW({ + const isc::dns::Exception exception("", 0, ""); + const isc::Exception& exception_cast = + dynamic_cast<const isc::Exception&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::DNSTextError exception("", 0, ""); + const isc::dns::Exception& exception_cast = + dynamic_cast<const isc::dns::Exception&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::NameParserException exception("", 0, ""); + const isc::dns::DNSTextError& exception_cast = + dynamic_cast<const isc::dns::DNSTextError&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::DNSMessageFORMERR exception("", 0, ""); + const isc::dns::DNSProtocolError& exception_cast = + dynamic_cast<const isc::dns::DNSProtocolError&>(exception); + const isc::dns::Exception& exception_cast2 = + dynamic_cast<const isc::dns::Exception&>(exception); + // to avoid compiler warning + exception_cast.getRcode(); + exception_cast.what(); + exception_cast2.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::DNSMessageBADVERS exception("", 0, ""); + const isc::dns::DNSProtocolError& exception_cast = + dynamic_cast<const isc::dns::DNSProtocolError&>(exception); + const isc::dns::Exception& exception_cast2 = + dynamic_cast<const isc::dns::Exception&>(exception); + // to avoid compiler warning + exception_cast.getRcode(); + exception_cast.what(); + exception_cast2.what(); + }); +} + +} // end unnamed namespace diff --git a/src/lib/dns/tests/edns_unittest.cc b/src/lib/dns/tests/edns_unittest.cc index 8b13789179..dfc68cc67e 100644 --- a/src/lib/dns/tests/edns_unittest.cc +++ b/src/lib/dns/tests/edns_unittest.cc @@ -1 +1,258 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <sstream> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/edns.h> +#include <dns/exceptions.h> +#include <dns/message.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rcode.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrttl.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +const uint8_t EDNS::SUPPORTED_VERSION; + +namespace { +class EDNSTest : public ::testing::Test { +protected: + EDNSTest() : rrtype(RRType::OPT()), buffer(NULL, 0), obuffer(0), rcode(0) { + opt_rdata = ConstRdataPtr(new generic::OPT()); + edns_base.setUDPSize(4096); + } + RRType rrtype; + EDNS edns_base; + ConstEDNSPtr edns; + InputBuffer buffer; + OutputBuffer obuffer; + MessageRenderer renderer; + ConstRdataPtr opt_rdata; + Rcode rcode; + vector<unsigned char> wiredata; +}; + +// RRClass of EDNS OPT means UDP buffer size +const RRClass rrclass(4096); +// RRTTL of EDNS OPT encodes extended-rcode, version, and DO bit +const RRTTL rrttl_do_on(0x00008000); // DO=on +const RRTTL rrttl_do_off(0); // DO=off +const RRTTL rrttl_badver(0x00018000); // version=1, DO=on + +TEST_F(EDNSTest, badVerConstruct) { + EXPECT_THROW(EDNS(1), isc::InvalidParameter); +} + +TEST_F(EDNSTest, DNSSECDOBit) { + // tests for EDNS from RR + + // DO bit is on, so DNSSEC should be considered to be supported. + EXPECT_TRUE(EDNS(Name::ROOT_NAME(), rrclass, rrtype, + rrttl_do_on, *opt_rdata).getDNSSECAwareness()); + + // DO bit is off. DNSSEC should be considered to be unsupported. + EXPECT_FALSE(EDNS(Name::ROOT_NAME(), rrclass, rrtype, + rrttl_do_off, *opt_rdata).getDNSSECAwareness()); + + // tests for EDNS constructed by hand + EXPECT_FALSE(edns_base.getDNSSECAwareness()); // false by default + edns_base.setDNSSECAwareness(true); // enable by hand + EXPECT_TRUE(edns_base.getDNSSECAwareness()); + edns_base.setDNSSECAwareness(false); // disable by hand + EXPECT_FALSE(edns_base.getDNSSECAwareness()); +} + +TEST_F(EDNSTest, UDPSize) { + EXPECT_EQ(4096, EDNS(Name::ROOT_NAME(), rrclass, rrtype, rrttl_do_on, + *opt_rdata).getUDPSize()); + + // EDNS UDP size smaller than the traditional max, 512. Unusual, but + // not prohibited. + edns_base.setUDPSize(511); + EXPECT_EQ(511, edns_base.getUDPSize()); + + // Even 0 is okay. + edns_base.setUDPSize(0); + EXPECT_EQ(0, edns_base.getUDPSize()); + + // Possible max value is also okay, although the higher layer app may + // adjust it to a reasonably lower value + edns_base.setUDPSize(65535); + EXPECT_EQ(65535, edns_base.getUDPSize()); +} + +TEST_F(EDNSTest, getVersion) { + // Constructed by hand + EXPECT_EQ(EDNS::SUPPORTED_VERSION, EDNS().getVersion()); + + // From RR + EXPECT_EQ(EDNS::SUPPORTED_VERSION, + EDNS(Name::ROOT_NAME(), rrclass, rrtype, rrttl_do_on, + *opt_rdata).getVersion()); +} + +TEST_F(EDNSTest, BadWireData) { + // Incompatible RR type + EXPECT_THROW(EDNS(Name::ROOT_NAME(), rrclass, RRType::A(), + rrttl_do_on, *opt_rdata), isc::InvalidParameter); + + // OPT RR of a non root name + EXPECT_THROW(EDNS(Name("example.com"), rrclass, rrtype, + rrttl_do_on, *opt_rdata), DNSMessageFORMERR); + + // Unsupported Version + EXPECT_THROW(EDNS(Name::ROOT_NAME(), rrclass, rrtype, + rrttl_badver, *opt_rdata), DNSMessageBADVERS); +} + +TEST_F(EDNSTest, toText) { + // Typical case, disabling DNSSEC + EXPECT_EQ("; EDNS: version: 0, flags:; udp: 4096\n", + EDNS(Name::ROOT_NAME(), rrclass, rrtype, rrttl_do_off, + *opt_rdata).toText()); + + // Typical case, enabling DNSSEC + EXPECT_EQ("; EDNS: version: 0, flags: do; udp: 4096\n", + EDNS(Name::ROOT_NAME(), rrclass, rrtype, rrttl_do_on, + *opt_rdata).toText()); + + // Non-0 extended Rcode: ignored in the toText() output. + EXPECT_EQ("; EDNS: version: 0, flags: do; udp: 4096\n", + EDNS(Name::ROOT_NAME(), rrclass, rrtype, + RRTTL(0x01008000), *opt_rdata).toText()); + + // Unknown flag: ignored in the toText() output. + EXPECT_EQ("; EDNS: version: 0, flags: do; udp: 4096\n", + EDNS(Name::ROOT_NAME(), rrclass, rrtype, + RRTTL(0x00008001), *opt_rdata).toText()); +} + +TEST_F(EDNSTest, toWireRenderer) { + // Typical case, (explicitly) disabling DNSSEC + edns_base.setDNSSECAwareness(false); + EXPECT_EQ(1, edns_base.toWire(renderer, + Rcode::NOERROR().getExtendedCode())); + UnitTestUtil::readWireData("edns_toWire1.wire", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); + + // Typical case, enabling DNSSEC + renderer.clear(); + wiredata.clear(); + edns_base.setDNSSECAwareness(true); + EXPECT_EQ(1, edns_base.toWire(renderer, + Rcode::NOERROR().getExtendedCode())); + UnitTestUtil::readWireData("edns_toWire2.wire", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); + + // Non-0 extended Rcode + renderer.clear(); + wiredata.clear(); + edns_base.setDNSSECAwareness(true); + EXPECT_EQ(1, edns_base.toWire(renderer, + Rcode::BADVERS().getExtendedCode())); + UnitTestUtil::readWireData("edns_toWire3.wire", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); + + // Uncommon UDP buffer size + renderer.clear(); + wiredata.clear(); + edns_base.setDNSSECAwareness(true); + edns_base.setUDPSize(511); + EXPECT_EQ(1, edns_base.toWire(renderer, + Rcode::NOERROR().getExtendedCode())); + UnitTestUtil::readWireData("edns_toWire4.wire", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); + + // From RR with unknown flag. If used for toWire(), the unknown flag + // should disappear. + renderer.clear(); + wiredata.clear(); + EXPECT_EQ(1, EDNS(Name::ROOT_NAME(), rrclass, rrtype, RRTTL(0x00008001), + *opt_rdata).toWire(renderer, + Rcode::NOERROR().getExtendedCode())); + UnitTestUtil::readWireData("edns_toWire2.wire", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); + + // If the available length in the renderer is not sufficient for the OPT + // RR, it shouldn't be inserted. + renderer.clear(); + renderer.setLengthLimit(10); // 10 = minimum length of OPT RR - 1 + edns_base.setDNSSECAwareness(true); + edns_base.setUDPSize(4096); + EXPECT_EQ(0, edns_base.toWire(renderer, + Rcode::NOERROR().getExtendedCode())); + // renderer should be intact + EXPECT_EQ(0, renderer.getLength()); +} + +TEST_F(EDNSTest, toWireBuffer) { + // "to renderer" and "to buffer" should generally produce the same result. + // for simplicity we only check one typical case to confirm that. + EXPECT_EQ(1, edns_base.toWire(obuffer, + Rcode::NOERROR().getExtendedCode())); + UnitTestUtil::readWireData("edns_toWire1.wire", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(EDNSTest, createFromRR) { + uint8_t extended_rcode; + + // normal case + edns = ConstEDNSPtr(createEDNSFromRR(Name::ROOT_NAME(), rrclass, rrtype, + rrttl_do_on, *opt_rdata, + extended_rcode)); + EXPECT_EQ(EDNS::SUPPORTED_VERSION, edns->getVersion()); + EXPECT_TRUE(edns->getDNSSECAwareness()); + EXPECT_EQ(4096, edns->getUDPSize()); + EXPECT_EQ(0, static_cast<int>(extended_rcode)); + + // non-0 extended rcode + extended_rcode = 0; + ConstEDNSPtr(createEDNSFromRR(Name::ROOT_NAME(), rrclass, rrtype, + RRTTL(0x01008000), *opt_rdata, + extended_rcode)); + EXPECT_EQ(1, static_cast<int>(extended_rcode)); + + // creation triggers an exception. extended_rcode must be intact. + extended_rcode = 0; + EXPECT_THROW(createEDNSFromRR(Name::ROOT_NAME(), rrclass, rrtype, + rrttl_badver, *opt_rdata, extended_rcode), + DNSMessageBADVERS); + EXPECT_EQ(0, static_cast<int>(extended_rcode)); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(EDNSTest, LeftShiftOperator) { + ostringstream oss; + oss << edns_base; + EXPECT_EQ(edns_base.toText(), oss.str()); +} +} diff --git a/src/lib/dns/tests/labelsequence_unittest.cc b/src/lib/dns/tests/labelsequence_unittest.cc index 8b13789179..15650fac07 100644 --- a/src/lib/dns/tests/labelsequence_unittest.cc +++ b/src/lib/dns/tests/labelsequence_unittest.cc @@ -1 +1,1243 @@ +// Copyright (C) 2012-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <util/buffer.h> + +#include <dns/labelsequence.h> +#include <dns/name.h> +#include <exceptions/exceptions.h> + +#include <gtest/gtest.h> + +#include <boost/functional/hash.hpp> + +#include <string> +#include <vector> +#include <utility> +#include <set> + +using namespace isc::dns; +using namespace std; + +// XXX: this is defined as class static constants, but some compilers +// seemingly cannot find the symbols when used in the EXPECT_xxx macros. +const size_t LabelSequence::MAX_SERIALIZED_LENGTH; + +namespace { + +// Common check that two labelsequences are equal +void check_equal(const LabelSequence& ls1, const LabelSequence& ls2) { + NameComparisonResult result = ls1.compare(ls2); + EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL, + result.getRelation()) << ls1.toText() << " != " << ls2.toText(); + EXPECT_EQ(0, result.getOrder()) << ls1.toText() << " != " << ls2.toText(); + EXPECT_EQ(ls1.getLabelCount(), result.getCommonLabels()); +} + +// Common check for general comparison of two labelsequences +void check_compare(const LabelSequence& ls1, const LabelSequence& ls2, + isc::dns::NameComparisonResult::NameRelation relation, + size_t common_labels, bool check_order, int order=0) { + NameComparisonResult result = ls1.compare(ls2); + EXPECT_EQ(relation, result.getRelation()); + EXPECT_EQ(common_labels, result.getCommonLabels()); + if (check_order) { + EXPECT_EQ(order, result.getOrder()); + } +} + +class LabelSequenceTest : public ::testing::Test { +public: + LabelSequenceTest() : n1("example.org"), n2("example.com"), + n3("example.org"), n4("foo.bar.test.example"), + n5("example.ORG"), n6("ExAmPlE.org"), + n7("."), n8("foo.example.org.bar"), + n9("\\000xample.org"), + n10("\\000xample.org"), + n11("\\000xample.com"), + n12("\\000xamplE.com"), + n_maxlabel("0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6"), + ls1(n1), ls2(n2), ls3(n3), ls4(n4), ls5(n5), + ls6(n6), ls7(n7), ls8(n8), + ls9(n9), ls10(n10), ls11(n11), ls12(n12) + {}; + // Need to keep names in scope for at least the lifetime of + // the labelsequences + Name n1, n2, n3, n4, n5, n6, n7, n8; + Name n9, n10, n11, n12; + const Name n_maxlabel; + + LabelSequence ls1, ls2, ls3, ls4, ls5, ls6, ls7, ls8; + LabelSequence ls9, ls10, ls11, ls12; +}; + +// Check the assignment operator. +TEST_F(LabelSequenceTest, assign) { + // Create the label sequence with example.org (n1 name). + LabelSequence ls(n1); + EXPECT_TRUE(ls == ls1); + + // Assign the label sequence to example.com (n2 name). + ls = ls2; + EXPECT_FALSE(ls == ls1); + EXPECT_TRUE(ls == ls2); +} + +// Basic equality tests +TEST_F(LabelSequenceTest, equals_sensitive) { + EXPECT_TRUE(ls1.equals(ls1, true)); + EXPECT_FALSE(ls1.equals(ls2, true)); + EXPECT_TRUE(ls1.equals(ls3, true)); + EXPECT_FALSE(ls1.equals(ls4, true)); + EXPECT_FALSE(ls1.equals(ls5, true)); + EXPECT_FALSE(ls1.equals(ls6, true)); + EXPECT_FALSE(ls1.equals(ls7, true)); + EXPECT_FALSE(ls1.equals(ls8, true)); + + EXPECT_FALSE(ls2.equals(ls1, true)); + EXPECT_TRUE(ls2.equals(ls2, true)); + EXPECT_FALSE(ls2.equals(ls3, true)); + EXPECT_FALSE(ls2.equals(ls4, true)); + EXPECT_FALSE(ls2.equals(ls5, true)); + EXPECT_FALSE(ls2.equals(ls6, true)); + EXPECT_FALSE(ls2.equals(ls7, true)); + EXPECT_FALSE(ls2.equals(ls8, true)); + + EXPECT_FALSE(ls4.equals(ls1, true)); + EXPECT_FALSE(ls4.equals(ls2, true)); + EXPECT_FALSE(ls4.equals(ls3, true)); + EXPECT_TRUE(ls4.equals(ls4, true)); + EXPECT_FALSE(ls4.equals(ls5, true)); + EXPECT_FALSE(ls4.equals(ls6, true)); + EXPECT_FALSE(ls4.equals(ls7, true)); + EXPECT_FALSE(ls4.equals(ls8, true)); + + EXPECT_FALSE(ls5.equals(ls1, true)); + EXPECT_FALSE(ls5.equals(ls2, true)); + EXPECT_FALSE(ls5.equals(ls3, true)); + EXPECT_FALSE(ls5.equals(ls4, true)); + EXPECT_TRUE(ls5.equals(ls5, true)); + EXPECT_FALSE(ls5.equals(ls6, true)); + EXPECT_FALSE(ls5.equals(ls7, true)); + EXPECT_FALSE(ls5.equals(ls8, true)); + + EXPECT_TRUE(ls9.equals(ls10, true)); + EXPECT_FALSE(ls9.equals(ls11, true)); + EXPECT_FALSE(ls9.equals(ls12, true)); + EXPECT_FALSE(ls11.equals(ls12, true)); +} + +TEST_F(LabelSequenceTest, equals_insensitive) { + EXPECT_TRUE(ls1.equals(ls1)); + EXPECT_FALSE(ls1.equals(ls2)); + EXPECT_TRUE(ls1.equals(ls3)); + EXPECT_FALSE(ls1.equals(ls4)); + EXPECT_TRUE(ls1.equals(ls5)); + EXPECT_TRUE(ls1.equals(ls6)); + EXPECT_FALSE(ls1.equals(ls7)); + + EXPECT_FALSE(ls2.equals(ls1)); + EXPECT_TRUE(ls2.equals(ls2)); + EXPECT_FALSE(ls2.equals(ls3)); + EXPECT_FALSE(ls2.equals(ls4)); + EXPECT_FALSE(ls2.equals(ls5)); + EXPECT_FALSE(ls2.equals(ls6)); + EXPECT_FALSE(ls2.equals(ls7)); + + EXPECT_TRUE(ls3.equals(ls1)); + EXPECT_FALSE(ls3.equals(ls2)); + EXPECT_TRUE(ls3.equals(ls3)); + EXPECT_FALSE(ls3.equals(ls4)); + EXPECT_TRUE(ls3.equals(ls5)); + EXPECT_TRUE(ls3.equals(ls6)); + EXPECT_FALSE(ls3.equals(ls7)); + + EXPECT_FALSE(ls4.equals(ls1)); + EXPECT_FALSE(ls4.equals(ls2)); + EXPECT_FALSE(ls4.equals(ls3)); + EXPECT_TRUE(ls4.equals(ls4)); + EXPECT_FALSE(ls4.equals(ls5)); + EXPECT_FALSE(ls4.equals(ls6)); + EXPECT_FALSE(ls4.equals(ls7)); + + EXPECT_TRUE(ls5.equals(ls1)); + EXPECT_FALSE(ls5.equals(ls2)); + EXPECT_TRUE(ls5.equals(ls3)); + EXPECT_FALSE(ls5.equals(ls4)); + EXPECT_TRUE(ls5.equals(ls5)); + EXPECT_TRUE(ls5.equals(ls6)); + EXPECT_FALSE(ls5.equals(ls7)); + + EXPECT_TRUE(ls9.equals(ls10)); + EXPECT_FALSE(ls9.equals(ls11)); + EXPECT_FALSE(ls9.equals(ls12)); + EXPECT_TRUE(ls11.equals(ls12)); +} + +// operator==(). This is mostly trivial wrapper, so it should suffice to +// check some basic cases. +TEST_F(LabelSequenceTest, operatorEqual) { + // cppcheck-suppress duplicateExpression + EXPECT_TRUE(ls1 == ls1); // self equivalence + EXPECT_TRUE(ls1 == LabelSequence(n1)); // equivalent two different objects + EXPECT_FALSE(ls1 == ls2); // non equivalent objects + EXPECT_TRUE(ls1 == ls5); // it's always case insensitive +} + +// Compare tests +TEST_F(LabelSequenceTest, compare) { + // "example.org." and "example.org.", case sensitive + NameComparisonResult result = ls1.compare(ls3, true); + EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL, + result.getRelation()); + EXPECT_EQ(0, result.getOrder()); + EXPECT_EQ(3, result.getCommonLabels()); + + // "example.org." and "example.ORG.", case sensitive + result = ls3.compare(ls5, true); + EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR, + result.getRelation()); + EXPECT_LT(0, result.getOrder()); + EXPECT_EQ(1, result.getCommonLabels()); + + // "example.org." and "example.ORG.", case in-sensitive + result = ls3.compare(ls5); + EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL, + result.getRelation()); + EXPECT_EQ(0, result.getOrder()); + EXPECT_EQ(3, result.getCommonLabels()); + + Name na("a.example.org"); + Name nb("b.example.org"); + LabelSequence lsa(na); + LabelSequence lsb(nb); + + // "a.example.org." and "b.example.org.", case in-sensitive + result = lsa.compare(lsb); + EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR, + result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(3, result.getCommonLabels()); + + // "example.org." and "b.example.org.", case in-sensitive + lsa.stripLeft(1); + result = lsa.compare(lsb); + EXPECT_EQ(isc::dns::NameComparisonResult::SUPERDOMAIN, + result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(3, result.getCommonLabels()); + + Name nc("g.f.e.d.c.example.org"); + LabelSequence lsc(nc); + + // "g.f.e.d.c.example.org." and "b.example.org" (not absolute), case + // in-sensitive; the absolute one is always smaller. + lsb.stripRight(1); + result = lsc.compare(lsb); + EXPECT_EQ(isc::dns::NameComparisonResult::NONE, result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(0, result.getCommonLabels()); + + // "g.f.e.d.c.example.org." and "example.org.", case in-sensitive + result = lsc.compare(ls1); + EXPECT_EQ(isc::dns::NameComparisonResult::SUBDOMAIN, + result.getRelation()); + EXPECT_LT(0, result.getOrder()); + EXPECT_EQ(3, result.getCommonLabels()); + + // "e.d.c.example.org." and "example.org.", case in-sensitive + lsc.stripLeft(2); + result = lsc.compare(ls1); + EXPECT_EQ(isc::dns::NameComparisonResult::SUBDOMAIN, + result.getRelation()); + EXPECT_LT(0, result.getOrder()); + EXPECT_EQ(3, result.getCommonLabels()); + + // "example.org." and "example.org.", case in-sensitive + lsc.stripLeft(3); + result = lsc.compare(ls1); + EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL, + result.getRelation()); + EXPECT_EQ(0, result.getOrder()); + EXPECT_EQ(3, result.getCommonLabels()); + + // "." and "example.org.", case in-sensitive + lsc.stripLeft(2); + result = lsc.compare(ls1); + EXPECT_EQ(isc::dns::NameComparisonResult::SUPERDOMAIN, + result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(1, result.getCommonLabels()); + + Name nd("a.b.c.isc.example.org"); + LabelSequence lsd(nd); + Name ne("w.x.y.isc.EXAMPLE.org"); + LabelSequence lse(ne); + + // "a.b.c.isc.example.org." and "w.x.y.isc.EXAMPLE.org.", + // case sensitive + result = lsd.compare(lse, true); + EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR, + result.getRelation()); + EXPECT_LT(0, result.getOrder()); + EXPECT_EQ(2, result.getCommonLabels()); + + // "a.b.c.isc.example.org." and "w.x.y.isc.EXAMPLE.org.", + // case in-sensitive + result = lsd.compare(lse); + EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR, + result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(4, result.getCommonLabels()); + + // "isc.example.org." and "isc.EXAMPLE.org.", case sensitive + lsd.stripLeft(3); + lse.stripLeft(3); + result = lsd.compare(lse, true); + EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR, + result.getRelation()); + EXPECT_LT(0, result.getOrder()); + EXPECT_EQ(2, result.getCommonLabels()); + + // "isc.example.org." and "isc.EXAMPLE.org.", case in-sensitive + result = lsd.compare(lse); + EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL, + result.getRelation()); + EXPECT_EQ(0, result.getOrder()); + EXPECT_EQ(4, result.getCommonLabels()); + + Name nf("a.b.c.isc.example.org"); + LabelSequence lsf(nf); + Name ng("w.x.y.isc.EXAMPLE.org"); + LabelSequence lsg(ng); + + // lsf: "a.b.c.isc.example.org." + // lsg: "w.x.y.isc.EXAMPLE.org" (not absolute), case in-sensitive. + // the absolute one is always smaller. + lsg.stripRight(1); + result = lsg.compare(lsf); // lsg > lsf + EXPECT_EQ(isc::dns::NameComparisonResult::NONE, result.getRelation()); + EXPECT_LT(0, result.getOrder()); + EXPECT_EQ(0, result.getCommonLabels()); + + // "a.b.c.isc.example.org" (not absolute) and + // "w.x.y.isc.EXAMPLE.org" (not absolute), case in-sensitive + lsf.stripRight(1); + result = lsg.compare(lsf); + EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR, + result.getRelation()); + EXPECT_LT(0, result.getOrder()); + EXPECT_EQ(3, result.getCommonLabels()); + + // "a.b.c.isc.example" (not absolute) and + // "w.x.y.isc.EXAMPLE" (not absolute), case in-sensitive + lsf.stripRight(1); + lsg.stripRight(1); + result = lsg.compare(lsf); + EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR, + result.getRelation()); + EXPECT_LT(0, result.getOrder()); + EXPECT_EQ(2, result.getCommonLabels()); + + // lsf: "a.b.c" (not absolute) and + // lsg: "w.x.y" (not absolute), case in-sensitive; a.b.c < w.x.y; + // no common labels. + lsf.stripRight(2); + lsg.stripRight(2); + result = lsf.compare(lsg); + EXPECT_EQ(isc::dns::NameComparisonResult::NONE, result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(0, result.getCommonLabels()); + + // lsf2: a.b.cc (not absolute); a.b.c < a.b.cc, no common labels. + const Name nf2("a.b.cc"); + LabelSequence lsf2(nf2); + lsf2.stripRight(1); + result = lsf.compare(lsf2); + EXPECT_EQ(isc::dns::NameComparisonResult::NONE, result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(0, result.getCommonLabels()); + + Name nh("aexample.org"); + LabelSequence lsh(nh); + Name ni("bexample.org"); + LabelSequence lsi(ni); + + // "aexample.org" (not absolute) and + // "bexample.org" (not absolute), case in-sensitive + lsh.stripRight(1); + lsi.stripRight(1); + result = lsh.compare(lsi); + EXPECT_EQ(isc::dns::NameComparisonResult::COMMONANCESTOR, + result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(1, result.getCommonLabels()); + + // "aexample" (not absolute) and + // "bexample" (not absolute), case in-sensitive; + // aexample < bexample; no common labels. + lsh.stripRight(1); + lsi.stripRight(1); + result = lsh.compare(lsi); + EXPECT_EQ(isc::dns::NameComparisonResult::NONE, result.getRelation()); + EXPECT_GT(0, result.getOrder()); + EXPECT_EQ(0, result.getCommonLabels()); + + Name nj("example.org"); + LabelSequence lsj(nj); + Name nk("example.org"); + LabelSequence lsk(nk); + + // "example.org" (not absolute) and + // "example.org" (not absolute), case in-sensitive + lsj.stripRight(1); + lsk.stripRight(1); + result = lsj.compare(lsk); + EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL, + result.getRelation()); + EXPECT_EQ(0, result.getOrder()); + EXPECT_EQ(2, result.getCommonLabels()); + + // "example" (not absolute) and + // "example" (not absolute), case in-sensitive + lsj.stripRight(1); + lsk.stripRight(1); + result = lsj.compare(lsk); + EXPECT_EQ(isc::dns::NameComparisonResult::EQUAL, + result.getRelation()); + EXPECT_EQ(0, result.getOrder()); + EXPECT_EQ(1, result.getCommonLabels()); +} + +void +getDataCheck(const uint8_t* expected_data, size_t expected_len, + const LabelSequence& ls) +{ + size_t len; + const uint8_t* data = ls.getData(&len); + ASSERT_EQ(expected_len, len) << "Expected data: " << expected_data << + ", label sequence: " << ls; + EXPECT_EQ(expected_len, ls.getDataLength()) << + "Expected data: " << expected_data << + ", label sequence: " << ls; + for (size_t i = 0; i < len; ++i) { + EXPECT_EQ(expected_data[i], data[i]) << + "Difference at pos " << i << ": Expected data: " << expected_data << + ", label sequence: " << ls; + } +} + +// Convenient data converter for expected data. Label data must be of +// uint8_t*, while it's convenient if we can specify some test data in +// plain string (which is of char*). This wrapper converts the latter to +// the former in a safer way. +void +getDataCheck(const char* expected_char_data, size_t expected_len, + const LabelSequence& ls) +{ + const vector<uint8_t> expected_data(expected_char_data, + expected_char_data + expected_len); + getDataCheck(&expected_data[0], expected_len, ls); +} + +TEST_F(LabelSequenceTest, getData) { + getDataCheck("\007example\003org\000", 13, ls1); + getDataCheck("\007example\003com\000", 13, ls2); + getDataCheck("\007example\003org\000", 13, ls3); + getDataCheck("\003foo\003bar\004test\007example\000", 22, ls4); + getDataCheck("\007example\003ORG\000", 13, ls5); + getDataCheck("\007ExAmPlE\003org\000", 13, ls6); + getDataCheck("\000", 1, ls7); +}; + +TEST_F(LabelSequenceTest, stripLeft) { + EXPECT_TRUE(ls1.equals(ls3)); + ls1.stripLeft(0); + getDataCheck("\007example\003org\000", 13, ls1); + EXPECT_TRUE(ls1.equals(ls3)); + ls1.stripLeft(1); + getDataCheck("\003org\000", 5, ls1); + EXPECT_FALSE(ls1.equals(ls3)); + ls1.stripLeft(1); + getDataCheck("\000", 1, ls1); + EXPECT_TRUE(ls1.equals(ls7)); + + ls2.stripLeft(2); + getDataCheck("\000", 1, ls2); + EXPECT_TRUE(ls2.equals(ls7)); +} + +TEST_F(LabelSequenceTest, stripRight) { + EXPECT_TRUE(ls1.equals(ls3)); + ls1.stripRight(1); + getDataCheck("\007example\003org", 12, ls1); + EXPECT_FALSE(ls1.equals(ls3)); + ls1.stripRight(1); + getDataCheck("\007example", 8, ls1); + EXPECT_FALSE(ls1.equals(ls3)); + + ASSERT_FALSE(ls1.equals(ls2)); + ls2.stripRight(2); + getDataCheck("\007example", 8, ls2); + EXPECT_TRUE(ls1.equals(ls2)); +} + +TEST_F(LabelSequenceTest, stripOutOfRange) { + EXPECT_THROW(ls1.stripLeft(100), isc::OutOfRange); + EXPECT_THROW(ls1.stripLeft(5), isc::OutOfRange); + EXPECT_THROW(ls1.stripLeft(4), isc::OutOfRange); + EXPECT_THROW(ls1.stripLeft(3), isc::OutOfRange); + getDataCheck("\007example\003org\000", 13, ls1); + + EXPECT_THROW(ls1.stripRight(100), isc::OutOfRange); + EXPECT_THROW(ls1.stripRight(5), isc::OutOfRange); + EXPECT_THROW(ls1.stripRight(4), isc::OutOfRange); + EXPECT_THROW(ls1.stripRight(3), isc::OutOfRange); + getDataCheck("\007example\003org\000", 13, ls1); +} + +TEST_F(LabelSequenceTest, getLabelCount) { + EXPECT_EQ(3, ls1.getLabelCount()); + ls1.stripLeft(0); + EXPECT_EQ(3, ls1.getLabelCount()); + ls1.stripLeft(1); + EXPECT_EQ(2, ls1.getLabelCount()); + ls1.stripLeft(1); + EXPECT_EQ(1, ls1.getLabelCount()); + + EXPECT_EQ(3, ls2.getLabelCount()); + ls2.stripRight(1); + EXPECT_EQ(2, ls2.getLabelCount()); + ls2.stripRight(1); + EXPECT_EQ(1, ls2.getLabelCount()); + + EXPECT_EQ(3, ls3.getLabelCount()); + ls3.stripRight(2); + EXPECT_EQ(1, ls3.getLabelCount()); + + EXPECT_EQ(5, ls4.getLabelCount()); + ls4.stripRight(3); + EXPECT_EQ(2, ls4.getLabelCount()); + + EXPECT_EQ(3, ls5.getLabelCount()); + ls5.stripLeft(2); + EXPECT_EQ(1, ls5.getLabelCount()); +} + +TEST_F(LabelSequenceTest, comparePart) { + EXPECT_FALSE(ls1.equals(ls8)); + + // strip root label from example.org. + ls1.stripRight(1); + // strip foo from foo.example.org.bar. + ls8.stripLeft(1); + // strip bar. (i.e. bar and root) too + ls8.stripRight(2); + + EXPECT_TRUE(ls1.equals(ls8)); + + // Data comparison + size_t len; + const uint8_t* data = ls1.getData(&len); + getDataCheck(data, len, ls8); +} + +TEST_F(LabelSequenceTest, isAbsolute) { + ASSERT_TRUE(ls1.isAbsolute()); + + ls1.stripLeft(1); + ASSERT_TRUE(ls1.isAbsolute()); + ls1.stripRight(1); + ASSERT_FALSE(ls1.isAbsolute()); + + ASSERT_TRUE(ls2.isAbsolute()); + ls2.stripRight(1); + ASSERT_FALSE(ls2.isAbsolute()); + + ASSERT_TRUE(ls3.isAbsolute()); + ls3.stripLeft(2); + ASSERT_TRUE(ls3.isAbsolute()); +} + +TEST_F(LabelSequenceTest, toText) { + EXPECT_EQ(".", ls7.toText()); + + EXPECT_EQ("example.org.", ls1.toText()); + ls1.stripLeft(1); + EXPECT_EQ("org.", ls1.toText()); + ls1.stripLeft(1); + EXPECT_EQ(".", ls1.toText()); + + EXPECT_EQ("example.com.", ls2.toText()); + ls2.stripRight(1); + EXPECT_EQ("example.com", ls2.toText()); + ls2.stripRight(1); + EXPECT_EQ("example", ls2.toText()); + + EXPECT_EQ("foo.example.org.bar.", ls8.toText()); + ls8.stripRight(2); + EXPECT_EQ("foo.example.org", ls8.toText()); + + EXPECT_EQ(".", ls7.toText()); + EXPECT_THROW(ls7.stripLeft(1), isc::OutOfRange); + + Name n_long1("012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "0123456789012345678901234567890"); + LabelSequence ls_long1(n_long1); + + EXPECT_EQ("012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "0123456789012345678901234567890.", ls_long1.toText()); + ls_long1.stripRight(1); + EXPECT_EQ("012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "012345678901234567890123456789012." + "012345678901234567890123456789" + "0123456789012345678901234567890", ls_long1.toText()); + + LabelSequence ls_long2(n_maxlabel); + + EXPECT_EQ("0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.", ls_long2.toText()); + ls_long2.stripRight(1); + EXPECT_EQ("0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." + "0.1.2.3.4.5.6", ls_long2.toText()); + ls_long2.stripRight(125); + EXPECT_EQ("0.1", ls_long2.toText()); +} + +// The following verifies that toRawText() returns a string +// actual characters in place of escape sequences. We do not +// bother with an exhaustive set of tests here as this is +// not a primary use case. +TEST_F(LabelSequenceTest, toRawText) { + Name n("a bc.$exa(m)ple.@org"); + LabelSequence l(n); + EXPECT_EQ("a bc.$exa(m)ple.@org", l.toRawText(true)); + EXPECT_EQ("a bc.$exa(m)ple.@org.", l.toRawText(false)); + + // toRawText is not supposed to do any sanity checks. + // Let's try with a very weird name. + Name n2("xtra\tchars\n.in.name"); + LabelSequence l2(n2); + EXPECT_EQ("xtra\tchars\n.in.name.", l2.toRawText(false)); +} + +// The following are test data used in the getHash test below. Normally +// we use example/documentation domain names for testing, but in this case +// we'd specifically like to use more realistic data, and are intentionally +// using real-world samples: They are the NS names of root and some top level +// domains as of this test. +const char* const root_servers[] = { + "a.root-servers.net", "b.root-servers.net", "c.root-servers.net", + "d.root-servers.net", "e.root-servers.net", "f.root-servers.net", + "g.root-servers.net", "h.root-servers.net", "i.root-servers.net", + "j.root-servers.net", "k.root-servers.net", "l.root-servers.net", + "m.root-servers.net", NULL +}; + +const char* const jp_servers[] = { + "a.dns.jp", "b.dns.jp", "c.dns.jp", "d.dns.jp", "e.dns.jp", + "f.dns.jp", "g.dns.jp", NULL +}; +const char* const cn_servers[] = { + "a.dns.cn", "b.dns.cn", "c.dns.cn", "d.dns.cn", "e.dns.cn", + "ns.cernet.net", NULL +}; +const char* const ca_servers[] = { + "k.ca-servers.ca", "e.ca-servers.ca", "a.ca-servers.ca", "z.ca-servers.ca", + "tld.isc-sns.net", "c.ca-servers.ca", "j.ca-servers.ca", "l.ca-servers.ca", + "sns-pb.isc.org", "f.ca-servers.ca", NULL +}; + +// A helper function used in the getHash test below. +void +hashDistributionCheck(const char* const* servers) { + const size_t BUCKETS = 64; // constant used in the MessageRenderer + set<Name> names; + vector<size_t> hash_counts(BUCKETS); + + // Store all test names and their super domain names (excluding the + // "root" label) in the set, calculates their hash values, and increments + // the counter for the corresponding hash "bucket". + for (size_t i = 0; servers[i] != NULL; ++i) { + const Name name(servers[i]); + for (size_t l = 0; l < name.getLabelCount() - 1; ++l) { + pair<set<Name>::const_iterator, bool> ret = + names.insert(name.split(l)); + if (ret.second) { + hash_counts[LabelSequence((*ret.first)).getHash(false) % + BUCKETS]++; + } + } + } + + // See how many conflicts we have in the buckets. For the testing purpose + // we expect there's at most 2 conflicts in each set, which is an + // arbitrary choice (it should happen to succeed with the hash function + // and data we are using; if it's not the case, maybe with an update to + // the hash implementation, we should revise the test). + for (size_t i = 0; i < BUCKETS; ++i) { + EXPECT_GE(3, hash_counts[i]); + } +} + +TEST_F(LabelSequenceTest, getHash) { + // Trivial case. The same sequence should have the same hash. + EXPECT_EQ(ls1.getHash(true), ls1.getHash(true)); + + // Check the case-insensitive mode behavior. + EXPECT_EQ(ls1.getHash(false), ls5.getHash(false)); + + // Check that the distribution of hash values is "not too bad" (such as + // everything has the same hash value due to a stupid bug). It's + // difficult to check such things reliably. We do some ad hoc tests here. + hashDistributionCheck(root_servers); + hashDistributionCheck(jp_servers); + hashDistributionCheck(cn_servers); + hashDistributionCheck(ca_servers); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(LabelSequenceTest, LeftShiftOperator) { + ostringstream oss; + oss << ls1; + EXPECT_EQ(ls1.toText(), oss.str()); +} + +TEST_F(LabelSequenceTest, serialize) { + // placeholder for serialized data. We use a sufficiently large space + // for testing the overwrapping cases below. + uint8_t labels_buf[LabelSequence::MAX_SERIALIZED_LENGTH * 3]; + + // vector to store expected and actual data + vector<LabelSequence> actual_labelseqs; + typedef pair<size_t, const uint8_t*> DataPair; + vector<DataPair> expected; + + // An absolute sequence directly constructed from a valid name. + // labels = 3, offset sequence = 0, 8, 12, data = "example.com." + actual_labelseqs.push_back(ls1); + const uint8_t expected_data1[] = { + 3, 0, 8, 12, 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 3, 'o', 'r', 'g', 0 }; + expected.push_back(DataPair(sizeof(expected_data1), expected_data1)); + + // Strip the original one from right. + // labels = 2, offset sequence = 0, 8, data = "example.com" (non absolute) + LabelSequence ls_rstripped = ls1; + ls_rstripped.stripRight(1); + actual_labelseqs.push_back(ls_rstripped); + const uint8_t expected_data2[] = { + 2, 0, 8, 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e', + 3, 'o', 'r', 'g'}; + expected.push_back(DataPair(sizeof(expected_data2), expected_data2)); + + // Strip the original one from left. + // labels = 2, offset sequence = 0, 4, data = "com." + // Note that offsets are adjusted so that they begin with 0. + LabelSequence ls_lstripped = ls1; + ls_lstripped.stripLeft(1); + actual_labelseqs.push_back(ls_lstripped); + const uint8_t expected_data3[] = { 2, 0, 4, 3, 'o', 'r', 'g', 0 }; + expected.push_back(DataPair(sizeof(expected_data3), expected_data3)); + + // Root label. + LabelSequence ls_root(Name::ROOT_NAME()); + actual_labelseqs.push_back(ls_root); + const uint8_t expected_data4[] = { 1, 0, 0 }; + expected.push_back(DataPair(sizeof(expected_data4), expected_data4)); + + // Non absolute single-label. + LabelSequence ls_single = ls_rstripped; + ls_single.stripRight(1); + actual_labelseqs.push_back(ls_single); + const uint8_t expected_data5[] = { + 1, 0, 7, 'e', 'x', 'a', 'm', 'p', 'l', 'e' }; + expected.push_back(DataPair(sizeof(expected_data5), expected_data5)); + + // Labels containing a longest possible label + const Name name_longlabel(std::string(63, 'x')); // 63 'x's + LabelSequence ls_longlabel(name_longlabel); + actual_labelseqs.push_back(ls_longlabel); + vector<uint8_t> expected_data6; + expected_data6.push_back(2); // 2 labels + expected_data6.push_back(0); // 1st offset + expected_data6.push_back(64); // 2nd offset + expected_data6.push_back(63); // 1st label length + expected_data6.insert(expected_data6.end(), 63, 'x'); // 1st label: 63 'x's + expected_data6.push_back(0); // 2nd label: trailing 0 + expected.push_back(DataPair(expected_data6.size(), &expected_data6[0])); + + // Max number of labels and longest possible name + EXPECT_EQ(Name::MAX_WIRE, n_maxlabel.getLength()); + LabelSequence ls_maxlabel(n_maxlabel); + actual_labelseqs.push_back(ls_maxlabel); + vector<uint8_t> expected_data7; + expected_data7.push_back(Name::MAX_LABELS); // number of labels + for (size_t i = 0; i < Name::MAX_LABELS; ++i) { + expected_data7.push_back(i * 2); // each label has length and 1 byte + } + // Copy wire data of the name + isc::util::OutputBuffer ob(0); + n_maxlabel.toWire(ob); + expected_data7.insert(expected_data7.end(), + static_cast<const uint8_t*>(ob.getData()), + static_cast<const uint8_t*>(ob.getData()) + + ob.getLength()); + expected.push_back(DataPair(expected_data7.size(), &expected_data7[0])); + + // For each data set, serialize the labels and compare the data to the + // expected one. + vector<DataPair>::const_iterator it = expected.begin(); + vector<LabelSequence>::const_iterator itl = actual_labelseqs.begin(); + for (; it != expected.end(); ++it, ++itl) { + SCOPED_TRACE(itl->toText()); + + const size_t serialized_len = itl->getSerializedLength(); + + ASSERT_GE(LabelSequence::MAX_SERIALIZED_LENGTH, serialized_len); + itl->serialize(labels_buf, serialized_len); + EXPECT_EQ(it->first, serialized_len); + EXPECT_EQ(0, memcmp(it->second, labels_buf, serialized_len)); + + EXPECT_EQ(NameComparisonResult::EQUAL, + LabelSequence(labels_buf).compare(*itl).getRelation()); + + // Shift the data to the middle of the buffer for overwrap check + uint8_t* const bp = labels_buf; + std::memcpy(bp + serialized_len, bp, serialized_len); + // Memory layout is now as follows: + // <- ser_len -> <- ser_len ------> + // bp bp+ser_len bp+(ser_len*2) + // olen,odata,ndata + + // end of buffer would be the first byte of offsets: invalid. + EXPECT_THROW(LabelSequence(bp + serialized_len). + serialize(bp + 2, serialized_len), + isc::BadValue); + // begin of buffer would be the last byte of ndata: invalid. + EXPECT_THROW(LabelSequence(bp + serialized_len). + serialize(bp + (2 * serialized_len) - 1, serialized_len), + isc::BadValue); + // A boundary safe case: buffer is placed after the sequence data. + // should cause no disruption. + LabelSequence(bp + serialized_len). + serialize(bp + 2 * serialized_len, serialized_len); + // A boundary safe case: buffer is placed before the sequence data + // should cause no disruption. (but the original serialized data will + // be overridden, so it can't be used any more) + LabelSequence(bp + serialized_len). + serialize(bp + 1, serialized_len); + } + + EXPECT_THROW(ls1.serialize(labels_buf, ls1.getSerializedLength() - 1), + isc::BadValue); +} + +#ifdef ENABLE_DEBUG + +// These checks are enabled only in debug mode in the LabelSequence +// class. +TEST_F(LabelSequenceTest, badDeserialize) { + EXPECT_THROW(LabelSequence(NULL), isc::BadValue); + const uint8_t zero_offsets[] = { 0 }; + EXPECT_THROW(LabelSequence ls(zero_offsets), isc::BadValue); + const uint8_t toomany_offsets[] = { Name::MAX_LABELS + 1 }; + EXPECT_THROW(LabelSequence ls(toomany_offsets), isc::BadValue); + + // (second) offset does not match actual label length + const uint8_t offsets_wrongoffset[] = { 2, 0, 64, 1 }; + EXPECT_THROW(LabelSequence ls(offsets_wrongoffset), isc::BadValue); + + // offset matches, but exceeds MAX_LABEL_LEN + const uint8_t offsets_toolonglabel[] = { 2, 0, 64, 64 }; + EXPECT_THROW(LabelSequence ls(offsets_toolonglabel), isc::BadValue); + + // Inconsistent data: an offset is lower than the previous offset + const uint8_t offsets_lower[] = { 3, // # of offsets + 0, 2, 1, // offsets + 1, 'a', 1, 'b', 0}; + EXPECT_THROW(LabelSequence ls(offsets_lower), isc::BadValue); + + // Inconsistent data: an offset is equal to the previous offset + const uint8_t offsets_noincrease[] = { 2, 0, 0, 0, 0 }; + EXPECT_THROW(LabelSequence ls(offsets_noincrease), isc::BadValue); +} + +#endif + +namespace { + +// Helper function; repeatedly calls +// - Initially, all three labelsequences should be the same +// - repeatedly performs: +// - checks all three are equal +// - stripLeft on ls1 +// - checks ls1 and ls2 are different, and ls2 and ls3 are equal +// - stripLeft on ls2 +// - checks ls1 and ls2 are equal, and ls2 and ls3 are different +// - stripLeft on ls3 +// +// (this test makes sure the stripLeft of one has no effect on the other +// two, and that the strip properties hold regardless of how they were +// constructed) +// +void stripLeftCheck(LabelSequence ls1, LabelSequence ls2, LabelSequence ls3) { + ASSERT_LT(1, ls1.getLabelCount()); + while (ls1.getLabelCount() > 1) { + check_equal(ls1, ls2); + check_equal(ls2, ls3); + + ls1.stripLeft(1); + check_compare(ls1, ls2, isc::dns::NameComparisonResult::SUPERDOMAIN, + ls1.getLabelCount(), true, -1); + check_equal(ls2, ls3); + + ls2.stripLeft(1); + check_equal(ls1, ls2); + check_compare(ls2, ls3, isc::dns::NameComparisonResult::SUPERDOMAIN, + ls1.getLabelCount(), true, -1); + + ls3.stripLeft(1); + } +} + +// Similar to stripLeftCheck, but using stripRight() +void stripRightCheck(LabelSequence ls1, LabelSequence ls2, LabelSequence ls3) { + ASSERT_LT(1, ls1.getLabelCount()); + while (ls1.getLabelCount() > 1) { + check_equal(ls1, ls2); + check_equal(ls2, ls3); + + ls1.stripRight(1); + check_compare(ls1, ls2, isc::dns::NameComparisonResult::NONE, 0, + false); + check_equal(ls2, ls3); + + ls2.stripRight(1); + check_equal(ls1, ls2); + check_compare(ls2, ls3, isc::dns::NameComparisonResult::NONE, 0, + false); + + ls3.stripRight(1); + } +} + +} // end anonymous namespace + +class ExtendableLabelSequenceTest : public ::testing::Test { +public: + ExtendableLabelSequenceTest() : bar("bar."), + example_org("example.org"), + foo("foo."), + foo_bar("foo.bar."), + foo_bar_example_org("foo.bar.example.org."), + foo_bar_foo_bar("foo.bar.foo.bar."), + foo_example("foo.example."), + org("org") + { + // explicitly set to non-zero data, to make sure + // we don't try to use data we don't set + memset(buf, 0xff, LabelSequence::MAX_SERIALIZED_LENGTH); + } + + Name bar; + Name example_org; + Name foo; + Name foo_bar; + Name foo_bar_example_org; + Name foo_bar_foo_bar; + Name foo_example; + Name org; + + uint8_t buf[LabelSequence::MAX_SERIALIZED_LENGTH]; +}; + +// Test that 'extendable' labelsequences behave correctly when using +// stripLeft() and stripRight() +TEST_F(ExtendableLabelSequenceTest, extendableLabelSequence) { + LabelSequence ls1(example_org); + LabelSequence ls2(example_org); + + LabelSequence els(ls1, buf); + // ls1 is absolute, so els should be too + EXPECT_TRUE(els.isAbsolute()); + check_equal(ls1, els); + + ASSERT_EQ(ls1.getDataLength(), els.getDataLength()); + stripLeftCheck(ls1, els, ls2); + stripRightCheck(ls1, els, ls2); + + // Creating an extendable labelsequence from a non-absolute + // label sequence should result in a non-absolute label sequence + ls1.stripRight(1); + els = LabelSequence(ls1, buf); + EXPECT_FALSE(els.isAbsolute()); + check_equal(ls1, els); + + // and extending with the root label should make it absolute again + els.extend(LabelSequence(Name(".")), buf); + EXPECT_TRUE(els.isAbsolute()); + check_equal(ls2, els); +} + +// Test that 'extendable' LabelSequences behave correctly when initialized +// with a stripped source LabelSequence +TEST_F(ExtendableLabelSequenceTest, extendableLabelSequenceLeftStrippedSource) { + LabelSequence ls1(foo_bar_example_org); + LabelSequence ls2(foo_bar_example_org); + + while (ls1.getLabelCount() > 2) { + ls1.stripLeft(1); + ls2.stripLeft(1); + + LabelSequence els(ls1, buf); + + ASSERT_EQ(ls1.getDataLength(), els.getDataLength()); + stripLeftCheck(ls1, els, ls2); + stripRightCheck(ls1, els, ls2); + } +} + +TEST_F(ExtendableLabelSequenceTest, extendableLabelSequenceRightStrippedSource) { + LabelSequence ls1(foo_bar_example_org); + LabelSequence ls2(foo_bar_example_org); + + while (ls1.getLabelCount() > 2) { + ls1.stripRight(1); + ls2.stripRight(1); + + LabelSequence els(ls1, buf); + + ASSERT_EQ(ls1.getDataLength(), els.getDataLength()); + stripLeftCheck(ls1, els, ls2); + stripRightCheck(ls1, els, ls2); + } +} + +// Check some basic 'extend' functionality +TEST_F(ExtendableLabelSequenceTest, extend) { + LabelSequence ls1(foo_bar); + LabelSequence ls2(foo); + LabelSequence ls3(bar); + LabelSequence ls4(foo_bar); + + LabelSequence els(ls2, buf); + + check_compare(ls1, els, isc::dns::NameComparisonResult::COMMONANCESTOR, 1, + true, -4); + els.extend(ls3, buf); + EXPECT_TRUE(els.isAbsolute()); + + check_equal(ls1, els); + stripLeftCheck(ls1, els, ls4); + stripRightCheck(ls1, els, ls4); + + // strip, then extend again + els.stripRight(2); // (2, 1 for root label, 1 for last label) + els.extend(ls3, buf); + EXPECT_TRUE(els.isAbsolute()); + check_equal(ls1, els); + + // Extending again should make it different + els.extend(ls3, buf); + EXPECT_TRUE(els.isAbsolute()); + check_compare(ls1, els, isc::dns::NameComparisonResult::COMMONANCESTOR, 2, + true, 4); + + // Extending with a non-absolute name should make it non-absolute as well + ls3.stripRight(1); + els.extend(ls3, buf); + EXPECT_FALSE(els.isAbsolute()); + + Name check_name("foo.bar.bar.bar"); + LabelSequence check_ls(check_name); + check_ls.stripRight(1); + check_equal(check_ls, els); + + // And try extending when both are not absolute + els.stripRight(3); + ls1.stripRight(1); + EXPECT_FALSE(els.isAbsolute()); + els.extend(ls3, buf); + EXPECT_FALSE(els.isAbsolute()); + check_equal(ls1, els); + + // Extending non-absolute with absolute should make it absolute again + EXPECT_FALSE(els.isAbsolute()); + els.extend(LabelSequence(Name("absolute.")), buf); + EXPECT_TRUE(els.isAbsolute()); + check_equal(LabelSequence(Name("foo.bar.absolute")), els); +} + +TEST_F(ExtendableLabelSequenceTest, extendLeftStripped) { + LabelSequence ls1(foo_example); + LabelSequence ls2(example_org); + LabelSequence ls3(org); + + LabelSequence els(ls1, buf); + + els.stripLeft(1); + els.extend(ls3, buf); + EXPECT_TRUE(els.isAbsolute()); + check_equal(ls2, els); +} + +// Check that when extending with itself, it does not cause horrible failures +TEST_F(ExtendableLabelSequenceTest, extendWithItself) { + LabelSequence ls1(foo_bar); + LabelSequence ls2(foo_bar_foo_bar); + + LabelSequence els(ls1, buf); + + els.extend(els, buf); + EXPECT_TRUE(els.isAbsolute()); + check_equal(ls2, els); + + // Also try for non-absolute names + ls2.stripRight(1); + els = LabelSequence(ls1, buf); + els.stripRight(1); + els.extend(els, buf); + EXPECT_FALSE(els.isAbsolute()); + check_equal(ls2, els); + + // Once more, now start out with non-absolute labelsequence + ls1.stripRight(1); + els = LabelSequence(ls1, buf); + els.extend(els, buf); + EXPECT_FALSE(els.isAbsolute()); + check_equal(ls2, els); +} + +// Test that 'extending' with just a root label is a no-op, iff the original +// was already absolute +TEST_F(ExtendableLabelSequenceTest, extendWithRoot) { + LabelSequence ls1(example_org); + + LabelSequence els(LabelSequence(ls1, buf)); + check_equal(ls1, els); + els.extend(LabelSequence(Name(".")), buf); + EXPECT_TRUE(els.isAbsolute()); + check_equal(ls1, els); + + // but not if the original was not absolute (it will be equal to + // the original labelsequence used above, but not the one it was based + // on). + LabelSequence ls2(example_org); + ls2.stripRight(1); + els = LabelSequence(ls2, buf); + EXPECT_FALSE(els.isAbsolute()); + els.extend(LabelSequence(Name(".")), buf); + EXPECT_TRUE(els.isAbsolute()); + check_equal(ls1, els); + check_compare(ls2, els, isc::dns::NameComparisonResult::NONE, 0, true, 3); +} + +// Check possible failure modes of extend() +TEST_F(ExtendableLabelSequenceTest, extendBadData) { + LabelSequence ls1(example_org); + + LabelSequence els(ls1, buf); + + // try use with unrelated labelsequence + EXPECT_THROW(ls1.extend(ls1, buf), isc::BadValue); + + // Create a long name, but so that we can still extend once + Name longlabel("1234567890123456789012345678901234567890" + "12345678901234567890"); + LabelSequence long_ls(longlabel); + els = LabelSequence(long_ls, buf); + els.extend(els, buf); + els.extend(long_ls, buf); + els.extend(long_ls, buf); + ASSERT_EQ(245, els.getDataLength()); + // Extending once more with 10 bytes should still work + els.extend(LabelSequence(Name("123456789")), buf); + EXPECT_TRUE(els.isAbsolute()); + + // Extended label sequence should now look like + const Name full_name( + "123456789012345678901234567890123456789012345678901234567890." + "123456789012345678901234567890123456789012345678901234567890." + "123456789012345678901234567890123456789012345678901234567890." + "123456789012345678901234567890123456789012345678901234567890." + "123456789."); + const LabelSequence full_ls(full_name); + check_equal(full_ls, els); + + // But now, even the shortest extension should fail + EXPECT_THROW(els.extend(LabelSequence(Name("1")), buf), isc::BadValue); + + // Check it hasn't been changed + EXPECT_TRUE(els.isAbsolute()); + check_equal(full_ls, els); + + // Also check that extending past MAX_LABELS is not possible + Name shortname("1."); + LabelSequence short_ls(shortname); + els = LabelSequence(short_ls, buf); + for (size_t i=0; i < 126; ++i) { + els.extend(short_ls, buf); + } + + // Should now look like this + const Name full_name2( + "1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1." + "1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1." + "1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1." + "1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1." + "1.1.1.1.1.1.1."); + const LabelSequence full_ls2(full_name2); + EXPECT_TRUE(els.isAbsolute()); + check_equal(full_ls2, els); + + EXPECT_THROW(els.extend(short_ls, buf), isc::BadValue); + + EXPECT_TRUE(els.isAbsolute()); + check_equal(full_ls2, els); +} + +// Check the static fixed 'wildcard' LabelSequence +TEST(WildCardLabelSequence, wildcard) { + ASSERT_FALSE(LabelSequence::WILDCARD().isAbsolute()); + ASSERT_EQ("*", LabelSequence::WILDCARD().toText()); +} + +} diff --git a/src/lib/dns/tests/master_lexer_inputsource_unittest.cc b/src/lib/dns/tests/master_lexer_inputsource_unittest.cc index 8b13789179..c5f618af06 100644 --- a/src/lib/dns/tests/master_lexer_inputsource_unittest.cc +++ b/src/lib/dns/tests/master_lexer_inputsource_unittest.cc @@ -1 +1,368 @@ +// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <dns/master_lexer_inputsource.h> +#include <dns/master_lexer.h> +#include <exceptions/exceptions.h> + +#include <gtest/gtest.h> + +#include <iostream> +#include <sstream> +#include <string> + +#include <string.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::dns::master_lexer_internal; + +namespace { + +const char* const test_input = + "Line1 to scan.\nLine2 to scan.\nLine3 to scan.\n"; + +class InputSourceTest : public ::testing::Test { +protected: + InputSourceTest() : + str_(test_input), + str_length_(strlen(str_)), + iss_(str_), + source_(iss_) + {} + + const char* str_; + const size_t str_length_; + stringstream iss_; + InputSource source_; +}; + +// Test the default return values set during InputSource construction. +TEST_F(InputSourceTest, defaults) { + EXPECT_EQ(1, source_.getCurrentLine()); + EXPECT_FALSE(source_.atEOF()); +} + +// getName() on file and stream sources +TEST_F(InputSourceTest, getName) { + EXPECT_EQ(0, source_.getName().find("stream-")); + + // Use some file; doesn't really matter what. + InputSource source2(TEST_DATA_SRCDIR "/masterload.txt"); + EXPECT_EQ(TEST_DATA_SRCDIR "/masterload.txt", source2.getName()); +} + +TEST_F(InputSourceTest, nonExistentFile) { + EXPECT_THROW({ + InputSource source(TEST_DATA_SRCDIR "/does-not-exist"); + }, InputSource::OpenError); +} + +// getChar() should return characters from the input stream in +// sequence. ungetChar() should skip backwards. +void +checkGetAndUngetChar(InputSource& source, + const char* str, const size_t str_length) +{ + for (size_t i = 0; i < str_length; ++i) { + EXPECT_EQ(str[i], source.getChar()); + EXPECT_EQ(i + 1, source.getPosition()); + EXPECT_FALSE(source.atEOF()); + } + + // At this point, we still have not reached EOF. + EXPECT_FALSE(source.atEOF()); + + // This should cause EOF to be set. + EXPECT_EQ(InputSource::END_OF_STREAM, source.getChar()); + + // Now, EOF should be set. + EXPECT_TRUE(source.atEOF()); + + // It doesn't increase the position count. + EXPECT_EQ(str_length, source.getPosition()); + EXPECT_EQ(str_length, source.getSize()); // this should be == getSize(). + + // Now, let's go backwards. This should cause the EOF to be set to + // false. + source.ungetChar(); + + // Now, EOF should be false. + EXPECT_FALSE(source.atEOF()); + + // But the position shouldn't change. + EXPECT_EQ(str_length, source.getPosition()); + + // This should cause EOF to be set again. + EXPECT_EQ(InputSource::END_OF_STREAM, source.getChar()); + + // Now, EOF should be set. + EXPECT_TRUE(source.atEOF()); + + // Now, let's go backwards in a loop. Start by skipping the EOF. + source.ungetChar(); + + for (size_t i = 0; i < str_length; ++i) { + const size_t index = str_length - 1 - i; + // Skip one character. + source.ungetChar(); + EXPECT_EQ(str[index], source.getChar()); + EXPECT_EQ(index + 1, source.getPosition()); + // Skip the character we received again. + source.ungetChar(); + } + + // Skipping past the start of buffer should throw. + EXPECT_THROW(source.ungetChar(), InputSource::UngetBeforeBeginning); +} + +TEST_F(InputSourceTest, stream) { + checkGetAndUngetChar(source_, str_, str_length_); +} + +TEST_F(InputSourceTest, file) { + std::ifstream fs(TEST_DATA_SRCDIR "/masterload.txt"); + const std::string str((std::istreambuf_iterator<char>(fs)), + std::istreambuf_iterator<char>()); + fs.close(); + + InputSource source(TEST_DATA_SRCDIR "/masterload.txt"); + checkGetAndUngetChar(source, str.c_str(), str.size()); +} + +// ungetAll() should skip back to the place where the InputSource +// started at construction, or the last saved start of line. +TEST_F(InputSourceTest, ungetAll) { + while (!source_.atEOF()) { + source_.getChar(); + } + + // Now, we are at EOF. + EXPECT_TRUE(source_.atEOF()); + EXPECT_EQ(4, source_.getCurrentLine()); + + source_.ungetAll(); + + // Now we are back to where we started. + EXPECT_EQ(1, source_.getCurrentLine()); + EXPECT_FALSE(source_.atEOF()); + EXPECT_EQ(0, source_.getPosition()); +} + +TEST_F(InputSourceTest, compact) { + // Compact at the start + source_.compact(); + + // Ungetting here must throw. + EXPECT_THROW(source_.ungetChar(), InputSource::UngetBeforeBeginning); + + for (size_t i = 0; i < str_length_; ++i) { + EXPECT_EQ(str_[i], source_.getChar()); + EXPECT_FALSE(source_.atEOF()); + } + + // At this point, we still have not reached EOF. + EXPECT_FALSE(source_.atEOF()); + + // This should cause EOF to be set. + EXPECT_EQ(InputSource::END_OF_STREAM, source_.getChar()); + + // Now, EOF should be set. + EXPECT_TRUE(source_.atEOF()); + EXPECT_EQ(4, source_.getCurrentLine()); + + // Compact again + source_.compact(); + + // We are still at EOF. + EXPECT_TRUE(source_.atEOF()); + EXPECT_EQ(4, source_.getCurrentLine()); + + // compact shouldn't change the position count. + EXPECT_EQ(source_.getSize(), source_.getPosition()); + + // Skip the EOF. + source_.ungetChar(); + + // Ungetting here must throw. + EXPECT_THROW(source_.ungetChar(), InputSource::UngetBeforeBeginning); + + EXPECT_EQ(InputSource::END_OF_STREAM, source_.getChar()); + EXPECT_TRUE(source_.atEOF()); +} + +TEST_F(InputSourceTest, markDuring) { + // First, skip to line 2. + while (!source_.atEOF() && + (source_.getCurrentLine() != 2)) { + source_.getChar(); + } + EXPECT_FALSE(source_.atEOF()); + EXPECT_EQ(2, source_.getCurrentLine()); + + // Now, unget a couple of characters. This should cause the + // buffer_pos_ to be not equal to the size of the buffer. + source_.ungetChar(); + source_.ungetChar(); + + // Now "mark" the source, meaning that we save line number and also + // compact the internal buffer at this stage. + source_.mark(); + + // Ungetting here must throw. + EXPECT_THROW(source_.ungetChar(), InputSource::UngetBeforeBeginning); + + for (size_t i = 13; i < str_length_; ++i) { + EXPECT_EQ(str_[i], source_.getChar()); + EXPECT_FALSE(source_.atEOF()); + } + + // At this point, we still have not reached EOF. + EXPECT_FALSE(source_.atEOF()); + + // This should cause EOF to be set. + EXPECT_EQ(InputSource::END_OF_STREAM, source_.getChar()); + + // Now, EOF should be set. + EXPECT_TRUE(source_.atEOF()); + + // Now, ungetAll() and check where it goes back. + source_.ungetAll(); + + // Ungetting here must throw. + EXPECT_THROW(source_.ungetChar(), InputSource::UngetBeforeBeginning); + + for (size_t i = 13; i < str_length_; ++i) { + EXPECT_EQ(str_[i], source_.getChar()); + EXPECT_FALSE(source_.atEOF()); + } + + // At this point, we still have not reached EOF. + EXPECT_FALSE(source_.atEOF()); + + // This should cause EOF to be set. + EXPECT_EQ(InputSource::END_OF_STREAM, source_.getChar()); + + // Now, EOF should be set. + EXPECT_TRUE(source_.atEOF()); +} + +// Test line counters. +TEST_F(InputSourceTest, lines) { + size_t line = 1; + while (!source_.atEOF()) { + if (source_.getChar() == '\n') { + ++line; + } + EXPECT_EQ(line, source_.getCurrentLine()); + } + + // Now, we are at EOF. + EXPECT_TRUE(source_.atEOF()); + EXPECT_EQ(4, source_.getCurrentLine()); + + // Go backwards 2 characters, skipping the last EOF and '\n'. + source_.ungetChar(); + source_.ungetChar(); + + EXPECT_FALSE(source_.atEOF()); + EXPECT_EQ(3, source_.getCurrentLine()); + + source_.ungetAll(); + + // Now we are back to where we started. + EXPECT_EQ(1, source_.getCurrentLine()); + EXPECT_FALSE(source_.atEOF()); + + // Now check that line numbers are decremented properly (as much as + // possible using the available API). + while (!source_.atEOF()) { + source_.getChar(); + } + line = source_.getCurrentLine(); + + // Now, we are at EOF. + EXPECT_TRUE(source_.atEOF()); + EXPECT_EQ(4, line); + + EXPECT_THROW({ + while (true) { + source_.ungetChar(); + EXPECT_TRUE(((line == source_.getCurrentLine()) || + ((line - 1) == source_.getCurrentLine()))); + line = source_.getCurrentLine(); + } + }, InputSource::UngetBeforeBeginning); + + // Now we are back to where we started. + EXPECT_EQ(1, source_.getCurrentLine()); +} + +// ungetAll() after saveLine() should skip back to the last-saved place. +TEST_F(InputSourceTest, saveLine) { + // First, skip to line 2. + while (!source_.atEOF() && + (source_.getCurrentLine() != 2)) { + source_.getChar(); + } + EXPECT_FALSE(source_.atEOF()); + EXPECT_EQ(2, source_.getCurrentLine()); + + // Now, save the line. + source_.saveLine(); + + // Now, go to EOF + while (!source_.atEOF()) { + source_.getChar(); + } + + // Now, we are at EOF. + EXPECT_TRUE(source_.atEOF()); + EXPECT_EQ(4, source_.getCurrentLine()); + + // Now, ungetAll() and check where it goes back. + source_.ungetAll(); + + // Now we are back to where we last-saved. + EXPECT_EQ(2, source_.getCurrentLine()); + EXPECT_FALSE(source_.atEOF()); +} + +TEST_F(InputSourceTest, getSize) { + // A simple case using string stream + EXPECT_EQ(strlen(test_input), source_.getSize()); + + // Check it works with an empty input + istringstream iss(""); + EXPECT_EQ(0, InputSource(iss).getSize()); + + // Pretend there's an error in seeking in the stream. It will be + // considered a seek specific error, and getSize() returns "unknown". + iss.setstate(std::ios_base::failbit); + EXPECT_EQ(MasterLexer::SOURCE_SIZE_UNKNOWN, InputSource(iss).getSize()); + // The fail bit should have been cleared. + EXPECT_FALSE(iss.fail()); + + // Pretend there's a *critical* error in the stream. The constructor will + // throw in the attempt of getting the input size. + iss.setstate(std::ios_base::badbit); + EXPECT_THROW(InputSource isrc(iss), InputSource::OpenError); + + // Check with input source from file name. We hardcode the file size + // for simplicity. It won't change too often. + EXPECT_EQ(143, InputSource(TEST_DATA_SRCDIR "/masterload.txt").getSize()); +} + +TEST_F(InputSourceTest, getPosition) { + // Initially the position is set to 0. Other cases are tested in tests + // for get and unget. + EXPECT_EQ(0, source_.getPosition()); + EXPECT_EQ(0, InputSource(TEST_DATA_SRCDIR "/masterload.txt").getPosition()); +} + +} // end namespace diff --git a/src/lib/dns/tests/master_lexer_state_unittest.cc b/src/lib/dns/tests/master_lexer_state_unittest.cc index 8b13789179..e810136068 100644 --- a/src/lib/dns/tests/master_lexer_state_unittest.cc +++ b/src/lib/dns/tests/master_lexer_state_unittest.cc @@ -1 +1,607 @@ +// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include <config.h> + +#include <dns/master_lexer.h> +#include <dns/master_lexer_inputsource.h> +#include <dns/master_lexer_state.h> + +#include <gtest/gtest.h> + +#include <sstream> + +using namespace isc::dns; +using namespace master_lexer_internal; + +namespace { +typedef MasterToken Token; // shortcut + +class MasterLexerStateTest : public ::testing::Test { +protected: + MasterLexerStateTest() : common_options(MasterLexer::INITIAL_WS), + s_null(NULL), + s_crlf(State::getInstance(State::CRLF)), + s_string(State::getInstance(State::String)), + s_qstring(State::getInstance(State::QString)), + s_number(State::getInstance(State::Number)), + options(MasterLexer::NONE), + orig_options(options) + {} + + // Specify INITIAL_WS as common initial options. + const MasterLexer::Options common_options; + MasterLexer lexer; + const State* const s_null; + const State& s_crlf; + const State& s_string; + const State& s_qstring; + const State& s_number; + std::stringstream ss; + MasterLexer::Options options, orig_options; +}; + +// Common check for the end-of-file condition. +// Token is set to END_OF_FILE, and the lexer was NOT last eol state. +// Passed state can be any valid one; they are stateless, just providing the +// interface for inspection. +void +eofCheck(const State& state, MasterLexer& lexer) { + EXPECT_EQ(Token::END_OF_FILE, state.getToken(lexer).getType()); + EXPECT_FALSE(state.wasLastEOL(lexer)); +} + +TEST_F(MasterLexerStateTest, startAndEnd) { + // A simple case: the input is empty, so we begin with start and + // are immediately done. + lexer.pushSource(ss); + EXPECT_EQ(s_null, State::start(lexer, common_options)); + eofCheck(s_crlf, lexer); +} + +TEST_F(MasterLexerStateTest, startToEOL) { + ss << "\n"; + lexer.pushSource(ss); + + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_TRUE(s_crlf.wasLastEOL(lexer)); + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + + // The next lexer session will reach EOF. Same eof check should pass. + EXPECT_EQ(s_null, State::start(lexer, common_options)); + eofCheck(s_crlf, lexer); +} + +TEST_F(MasterLexerStateTest, space) { + // repeat '\t\n' twice (see below), then space after EOL + ss << " \t\n\t\n "; + lexer.pushSource(ss); + + // by default space characters and tabs will be ignored. We check this + // twice; at the second iteration, it's a white space at the beginning + // of line, but since we don't specify INITIAL_WS option, it's treated as + // normal space and ignored. + for (size_t i = 0; i < 2; ++i) { + EXPECT_EQ(s_null, State::start(lexer, MasterLexer::NONE)); + EXPECT_TRUE(s_crlf.wasLastEOL(lexer)); + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + } + + // Now we specify the INITIAL_WS option. It will be recognized and the + // corresponding token will be returned. + EXPECT_EQ(s_null, State::start(lexer, MasterLexer::INITIAL_WS)); + EXPECT_FALSE(s_crlf.wasLastEOL(lexer)); + EXPECT_EQ(Token::INITIAL_WS, s_crlf.getToken(lexer).getType()); +} + +TEST_F(MasterLexerStateTest, parentheses) { + ss << "\n(\na\n )\n "; // 1st \n is to check if 'was EOL' is set to false + lexer.pushSource(ss); + + EXPECT_EQ(s_null, State::start(lexer, common_options)); // handle \n + + // Now handle '('. It skips \n and recognize 'a' as string + EXPECT_EQ(0, s_crlf.getParenCount(lexer)); // check pre condition + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + EXPECT_EQ(1, s_crlf.getParenCount(lexer)); // check post condition + EXPECT_FALSE(s_crlf.wasLastEOL(lexer)); + + // skip 'a' + s_string.handle(lexer); + + // Then handle ')'. '\n' before ')' isn't recognized because + // it's canceled due to the '('. Likewise, the space after the '\n' + // shouldn't be recognized but should be just ignored. + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(0, s_crlf.getParenCount(lexer)); + + // Now, temporarily disabled options are restored: Both EOL and the + // initial WS are recognized + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(Token::INITIAL_WS, s_crlf.getToken(lexer).getType()); +} + +TEST_F(MasterLexerStateTest, nestedParentheses) { + // This is an unusual, but allowed (in this implementation) case. + ss << "(a(b)\n c)\n "; + lexer.pushSource(ss); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); // consume '(' + s_string.handle(lexer); // consume 'a' + EXPECT_EQ(&s_string, State::start(lexer, common_options)); // consume '(' + s_string.handle(lexer); // consume 'b' + EXPECT_EQ(2, s_crlf.getParenCount(lexer)); // now the count is 2 + + // Close the inner most parentheses. count will be decreased, but option + // shouldn't be restored yet, so the intermediate EOL or initial WS won't + // be recognized. + EXPECT_EQ(&s_string, State::start(lexer, common_options)); // consume ')' + s_string.handle(lexer); // consume 'c' + EXPECT_EQ(1, s_crlf.getParenCount(lexer)); + + // Close the outermost parentheses. count will be reset to 0, and original + // options are restored. + EXPECT_EQ(s_null, State::start(lexer, common_options)); + + // Now, temporarily disabled options are restored: Both EOL and the + // initial WS are recognized + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(Token::INITIAL_WS, s_crlf.getToken(lexer).getType()); +} + +TEST_F(MasterLexerStateTest, unbalancedParentheses) { + // Only closing paren is provided. We prepend a \n to check if it's + // correctly canceled after detecting the error. + ss << "\n)"; + ss << "(a"; + lexer.pushSource(ss); + + EXPECT_EQ(s_null, State::start(lexer, common_options)); // consume '\n' + EXPECT_TRUE(s_crlf.wasLastEOL(lexer)); // this \n was remembered + + // Now checking ')'. The result should be error, count shouldn't be + // changed. "last EOL" should be canceled. + EXPECT_EQ(0, s_crlf.getParenCount(lexer)); + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(0, s_crlf.getParenCount(lexer)); + ASSERT_EQ(Token::ERROR, s_crlf.getToken(lexer).getType()); + EXPECT_EQ(Token::UNBALANCED_PAREN, s_crlf.getToken(lexer).getErrorCode()); + EXPECT_FALSE(s_crlf.wasLastEOL(lexer)); + + // Reach EOF with a dangling open parenthesis. + EXPECT_EQ(&s_string, State::start(lexer, common_options)); // consume '(' + s_string.handle(lexer); // consume 'a' + EXPECT_EQ(1, s_crlf.getParenCount(lexer)); + EXPECT_EQ(s_null, State::start(lexer, common_options)); // reach EOF + ASSERT_EQ(Token::ERROR, s_crlf.getToken(lexer).getType()); + EXPECT_EQ(Token::UNBALANCED_PAREN, s_crlf.getToken(lexer).getErrorCode()); + EXPECT_EQ(0, s_crlf.getParenCount(lexer)); // should be reset to 0 +} + +TEST_F(MasterLexerStateTest, startToComment) { + // Begin with 'start', detect space, then encounter a comment. Skip + // the rest of the line, and recognize the new line. Note that the + // second ';' is simply ignored. + ss << " ;a;\n"; + ss << ";a;"; // Likewise, but the comment ends with EOF. + lexer.pushSource(ss); + + // Initial whitespace (asked for in common_options) + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(Token::INITIAL_WS, s_crlf.getToken(lexer).getType()); + // Comment ending with EOL + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + + // Comment ending with EOF + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(Token::END_OF_FILE, s_crlf.getToken(lexer).getType()); +} + +TEST_F(MasterLexerStateTest, commentAfterParen) { + // comment after an opening parenthesis. The code that is tested by + // other tests should also ensure that it works correctly, but we + // check it explicitly. + ss << "( ;this is a comment\na)\n"; + lexer.pushSource(ss); + + // consume '(', skip comments, consume 'a', then consume ')' + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); +} + +TEST_F(MasterLexerStateTest, crlf) { + ss << "\r\n"; // case 1 + ss << "\r "; // case 2 + ss << "\r;comment\na"; // case 3 + ss << "\r"; // case 4 + lexer.pushSource(ss); + + // 1. A sequence of \r, \n is recognized as a single 'end-of-line' + EXPECT_EQ(&s_crlf, State::start(lexer, common_options)); // recognize '\r' + s_crlf.handle(lexer); // recognize '\n' + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + EXPECT_TRUE(s_crlf.wasLastEOL(lexer)); + + // 2. Single '\r' (not followed by \n) is recognized as a single + // 'end-of-line'. then there will be "initial WS" + EXPECT_EQ(&s_crlf, State::start(lexer, common_options)); // recognize '\r' + // see ' ', "unget" it + s_crlf.handle(lexer); + EXPECT_EQ(s_null, State::start(lexer, common_options)); // recognize ' ' + EXPECT_EQ(Token::INITIAL_WS, s_crlf.getToken(lexer).getType()); + + // 3. comment between \r and \n + EXPECT_EQ(&s_crlf, State::start(lexer, common_options)); // recognize '\r' + // skip comments, recognize '\n' + s_crlf.handle(lexer); + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // skip 'a' + + // 4. \r then EOF + EXPECT_EQ(&s_crlf, State::start(lexer, common_options)); // recognize '\r' + // see EOF, then "unget" it + s_crlf.handle(lexer); + EXPECT_EQ(s_null, State::start(lexer, common_options)); // recognize EOF + EXPECT_EQ(Token::END_OF_FILE, s_crlf.getToken(lexer).getType()); +} + +// Commonly used check for string related test cases, checking if the given +// token has expected values. +void +stringTokenCheck(const std::string& expected, const MasterToken& token, + bool quoted = false) +{ + EXPECT_EQ(quoted ? Token::QSTRING : Token::STRING, token.getType()); + EXPECT_EQ(expected, token.getString()); + const std::string actual(token.getStringRegion().beg, + token.getStringRegion().beg + + token.getStringRegion().len); + EXPECT_EQ(expected, actual); + + // There should be "hidden" nul-terminator after the string data. + ASSERT_NE(static_cast<const char*>(NULL), token.getStringRegion().beg); + EXPECT_EQ(0, *(token.getStringRegion().beg + token.getStringRegion().len)); +} + +TEST_F(MasterLexerStateTest, string) { + // Check with simple strings followed by separate characters + ss << "followed-by-EOL\n"; + ss << "followed-by-CR\r"; + ss << "followed-by-space "; + ss << "followed-by-tab\t"; + ss << "followed-by-comment;this is comment and ignored\n"; + ss << "followed-by-paren(closing)"; + ss << "followed-by-EOF"; + lexer.pushSource(ss); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see \n + EXPECT_FALSE(s_string.wasLastEOL(lexer)); + stringTokenCheck("followed-by-EOL", s_string.getToken(lexer)); + EXPECT_EQ(s_null, State::start(lexer, common_options)); // skip \n + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see \r + stringTokenCheck("followed-by-CR", s_string.getToken(lexer)); + EXPECT_EQ(&s_crlf, State::start(lexer, common_options)); // handle \r... + s_crlf.handle(lexer); // ...and skip it + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see ' ' + stringTokenCheck("followed-by-space", s_string.getToken(lexer)); + + // skip ' ', then recognize the next string + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see \t + stringTokenCheck("followed-by-tab", s_string.getToken(lexer)); + + // skip \t, then recognize the next string + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see comment + stringTokenCheck("followed-by-comment", s_string.getToken(lexer)); + EXPECT_EQ(s_null, State::start(lexer, common_options)); // skip \n after it + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see '(' + stringTokenCheck("followed-by-paren", s_string.getToken(lexer)); + EXPECT_EQ(&s_string, State::start(lexer, common_options)); // str in () + s_string.handle(lexer); // recognize the str, see ')' + stringTokenCheck("closing", s_string.getToken(lexer)); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see EOF + stringTokenCheck("followed-by-EOF", s_string.getToken(lexer)); +} + +TEST_F(MasterLexerStateTest, stringEscape) { + // some of the separate characters should be considered part of the + // string if escaped. + ss << "escaped\\ space "; + ss << "escaped\\\ttab "; + ss << "escaped\\(paren "; + ss << "escaped\\)close "; + ss << "escaped\\;comment "; + ss << "escaped\\\\ backslash "; // second '\' shouldn't escape ' ' + lexer.pushSource(ss); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("escaped\\ space", s_string.getToken(lexer)); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("escaped\\\ttab", s_string.getToken(lexer)); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("escaped\\(paren", s_string.getToken(lexer)); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("escaped\\)close", s_string.getToken(lexer)); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("escaped\\;comment", s_string.getToken(lexer)); + + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see ' ' in mid + stringTokenCheck("escaped\\\\", s_string.getToken(lexer)); + + // Confirm the word that follows the escaped '\' is correctly recognized. + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("backslash", s_string.getToken(lexer)); +} + +TEST_F(MasterLexerStateTest, quotedString) { + ss << "\"ignore-quotes\"\n"; + ss << "\"quoted string\" "; // space is part of the qstring + ss << "\"\" "; // empty quoted string + // also check other separator characters. note that \r doesn't cause + // UNBALANCED_QUOTES. Not sure if it's intentional, but that's how the + // BIND 9 version works, so we follow it (it should be too minor to matter + // in practice anyway) + ss << "\"quoted()\t\rstring\" "; + ss << "\"escape\\ in quote\" "; + ss << "\"escaped\\\"\" "; + ss << "\"escaped backslash\\\\\" "; + ss << "\"no;comment\""; + lexer.pushSource(ss); + + // by default, '"' is unexpected (when QSTRING is not specified), + // and it returns MasterToken::UNEXPECTED_QUOTES. + EXPECT_EQ(s_null, State::start(lexer, common_options)); + EXPECT_EQ(Token::UNEXPECTED_QUOTES, s_string.getToken(lexer).getErrorCode()); + // Read it as a QSTRING. + s_qstring.handle(lexer); // recognize quoted str, see \n + stringTokenCheck("ignore-quotes", s_qstring.getToken(lexer), true); + EXPECT_EQ(s_null, State::start(lexer, common_options)); // skip \n after it + EXPECT_TRUE(s_string.wasLastEOL(lexer)); + + // If QSTRING is specified in option, '"' is regarded as a beginning of + // a quoted string. + const MasterLexer::Options options = common_options | MasterLexer::QSTRING; + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + EXPECT_FALSE(s_string.wasLastEOL(lexer)); // EOL is canceled due to '"' + s_qstring.handle(lexer); + stringTokenCheck("quoted string", s_string.getToken(lexer), true); + + // Empty string is okay as qstring + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + stringTokenCheck("", s_string.getToken(lexer), true); + + // Also checks other separator characters within a qstring + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + stringTokenCheck("quoted()\t\rstring", s_string.getToken(lexer), true); + + // escape character mostly doesn't have any effect in the qstring + // processing + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + stringTokenCheck("escape\\ in quote", s_string.getToken(lexer), true); + + // The only exception is the quotation mark itself. Note that the escape + // only works on the quotation mark immediately after it. + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + stringTokenCheck("escaped\"", s_string.getToken(lexer), true); + + // quoted '\' then '"'. Unlike the previous case '"' shouldn't be + // escaped. + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + stringTokenCheck("escaped backslash\\\\", s_string.getToken(lexer), true); + + // ';' has no meaning in a quoted string (not indicating a comment) + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + stringTokenCheck("no;comment", s_string.getToken(lexer), true); +} + +TEST_F(MasterLexerStateTest, brokenQuotedString) { + ss << "\"unbalanced-quote\n"; + ss << "\"quoted\\\n\" "; + ss << "\"unclosed quote and EOF"; + lexer.pushSource(ss); + + // EOL is encountered without closing the quote + const MasterLexer::Options options = common_options | MasterLexer::QSTRING; + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + ASSERT_EQ(Token::ERROR, s_qstring.getToken(lexer).getType()); + EXPECT_EQ(Token::UNBALANCED_QUOTES, + s_qstring.getToken(lexer).getErrorCode()); + // We can resume after the error from the '\n' + EXPECT_EQ(s_null, State::start(lexer, options)); + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + + // \n is okay in a quoted string if escaped + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + stringTokenCheck("quoted\\\n", s_string.getToken(lexer), true); + + // EOF is encountered without closing the quote + EXPECT_EQ(&s_qstring, State::start(lexer, options)); + s_qstring.handle(lexer); + ASSERT_EQ(Token::ERROR, s_qstring.getToken(lexer).getType()); + EXPECT_EQ(Token::UNEXPECTED_END, s_qstring.getToken(lexer).getErrorCode()); + // If we continue we'll simply see the EOF + EXPECT_EQ(s_null, State::start(lexer, options)); + EXPECT_EQ(Token::END_OF_FILE, s_crlf.getToken(lexer).getType()); +} + +TEST_F(MasterLexerStateTest, basicNumbers) { + ss << "0 "; + ss << "1 "; + ss << "12345 "; + ss << "4294967295 "; // 2^32-1 + ss << "4294967296 "; // Out of range + ss << "340282366920938463463374607431768211456 "; + // Very much out of range (2^128) + ss << "005 "; // Leading zeroes are ignored + ss << "42;asdf\n"; // Number with comment + ss << "37"; // Simple number again, here to make + // sure none of the above messed up + // the tokenizer + lexer.pushSource(ss); + + // Ask the lexer to recognize numbers as well + const MasterLexer::Options options = common_options | MasterLexer::NUMBER; + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(0, s_number.getToken(lexer).getNumber()); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(1, s_number.getToken(lexer).getNumber()); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(12345, s_number.getToken(lexer).getNumber()); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(4294967295u, s_number.getToken(lexer).getNumber()); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(Token::NUMBER_OUT_OF_RANGE, + s_number.getToken(lexer).getErrorCode()); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(Token::NUMBER_OUT_OF_RANGE, + s_number.getToken(lexer).getErrorCode()); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(5, s_number.getToken(lexer).getNumber()); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(42, s_number.getToken(lexer).getNumber()); + + EXPECT_EQ(s_null, State::start(lexer, options)); + EXPECT_TRUE(s_crlf.wasLastEOL(lexer)); + EXPECT_EQ(Token::END_OF_LINE, s_crlf.getToken(lexer).getType()); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + EXPECT_EQ(37, s_number.getToken(lexer).getNumber()); + + // If we continue we'll simply see the EOF + EXPECT_EQ(s_null, State::start(lexer, options)); + EXPECT_EQ(Token::END_OF_FILE, s_crlf.getToken(lexer).getType()); +} + +// Test tokens that look like (or start out as) numbers, +// but turn out to be strings. Tests include escaped characters. +TEST_F(MasterLexerStateTest, stringNumbers) { + ss << "123 "; // Should be read as a string if the + // NUMBER option is not given + ss << "-1 "; // Negative numbers are interpreted + // as strings (unsigned integers only) + ss << "123abc456 "; // 'Numbers' containing non-digits should + // be interpreted as strings + ss << "123\\456 "; // Numbers containing escaped digits are + // interpreted as strings + ss << "3scaped\\ space "; + ss << "3scaped\\\ttab "; + ss << "3scaped\\(paren "; + ss << "3scaped\\)close "; + ss << "3scaped\\;comment "; + ss << "3scaped\\\\ 8ackslash "; // second '\' shouldn't escape ' ' + + lexer.pushSource(ss); + + // Note that common_options does not include MasterLexer::NUMBER, + // so the token should be recognized as a string + EXPECT_EQ(&s_string, State::start(lexer, common_options)); + s_string.handle(lexer); + stringTokenCheck("123", s_string.getToken(lexer), false); + + // Ask the lexer to recognize numbers as well + const MasterLexer::Options options = common_options | MasterLexer::NUMBER; + + EXPECT_EQ(&s_string, State::start(lexer, options)); + s_string.handle(lexer); + stringTokenCheck("-1", s_string.getToken(lexer), false); + + // Starts out as a number, but ends up being a string + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + stringTokenCheck("123abc456", s_number.getToken(lexer), false); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); + stringTokenCheck("123\\456", s_number.getToken(lexer), false); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("3scaped\\ space", s_number.getToken(lexer)); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("3scaped\\\ttab", s_number.getToken(lexer)); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("3scaped\\(paren", s_number.getToken(lexer)); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("3scaped\\)close", s_number.getToken(lexer)); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("3scaped\\;comment", s_number.getToken(lexer)); + + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); // recognize str, see ' ' in mid + stringTokenCheck("3scaped\\\\", s_number.getToken(lexer)); + + // Confirm the word that follows the escaped '\' is correctly recognized. + EXPECT_EQ(&s_number, State::start(lexer, options)); + s_number.handle(lexer); // recognize str, see ' ' at end + stringTokenCheck("8ackslash", s_number.getToken(lexer)); + + // If we continue we'll simply see the EOF + EXPECT_EQ(s_null, State::start(lexer, options)); + EXPECT_EQ(Token::END_OF_FILE, s_crlf.getToken(lexer).getType()); +} + +} // end anonymous namespace diff --git a/src/lib/dns/tests/master_lexer_token_unittest.cc b/src/lib/dns/tests/master_lexer_token_unittest.cc index 8b13789179..2167a9f5e6 100644 --- a/src/lib/dns/tests/master_lexer_token_unittest.cc +++ b/src/lib/dns/tests/master_lexer_token_unittest.cc @@ -1 +1,162 @@ +// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <exceptions/exceptions.h> + +#include <dns/master_lexer.h> + +#include <gtest/gtest.h> + +#include <string> + +using namespace isc::dns; + +namespace { + +const char TEST_STRING[] = "string token"; +// This excludes the ending \0 character +const size_t TEST_STRING_LEN = sizeof(TEST_STRING) - 1; + +class MasterLexerTokenTest : public ::testing::Test { +protected: + MasterLexerTokenTest() : + token_eof(MasterToken::END_OF_FILE), + token_str(TEST_STRING, TEST_STRING_LEN), + token_num(42), + token_err(MasterToken::UNEXPECTED_END) + {} + + const MasterToken token_eof; // an example of non-value type token + const MasterToken token_str; + const MasterToken token_num; + const MasterToken token_err; +}; + + +TEST_F(MasterLexerTokenTest, strings) { + // basic construction and getter checks + EXPECT_EQ(MasterToken::STRING, token_str.getType()); + EXPECT_EQ(std::string("string token"), token_str.getString()); + std::string strval = "dummy"; // this should be replaced + token_str.getString(strval); + EXPECT_EQ(std::string("string token"), strval); + const MasterToken::StringRegion str_region = + token_str.getStringRegion(); + EXPECT_EQ(TEST_STRING, str_region.beg); + EXPECT_EQ(TEST_STRING_LEN, str_region.len); + + // Even if the stored string contains a nul character (in this case, + // it happens to be at the end of the string, but could be in the middle), + // getString() should return a string object containing the nul. + std::string expected_str("string token"); + expected_str.push_back('\0'); + EXPECT_EQ(expected_str, + MasterToken(TEST_STRING, TEST_STRING_LEN + 1).getString()); + MasterToken(TEST_STRING, TEST_STRING_LEN + 1).getString(strval); + EXPECT_EQ(expected_str, strval); + + // Construct type of qstring + EXPECT_EQ(MasterToken::QSTRING, + MasterToken(TEST_STRING, sizeof(TEST_STRING), true). + getType()); + // if we explicitly set 'quoted' to false, it should be normal string + EXPECT_EQ(MasterToken::STRING, + MasterToken(TEST_STRING, sizeof(TEST_STRING), false). + getType()); + + // getString/StringRegion() aren't allowed for non string(-variant) types + EXPECT_THROW(token_eof.getString(), isc::InvalidOperation); + EXPECT_THROW(token_eof.getString(strval), isc::InvalidOperation); + EXPECT_THROW(token_num.getString(), isc::InvalidOperation); + EXPECT_THROW(token_num.getString(strval), isc::InvalidOperation); + EXPECT_THROW(token_eof.getStringRegion(), isc::InvalidOperation); + EXPECT_THROW(token_num.getStringRegion(), isc::InvalidOperation); +} + +TEST_F(MasterLexerTokenTest, numbers) { + EXPECT_EQ(42, token_num.getNumber()); + EXPECT_EQ(MasterToken::NUMBER, token_num.getType()); + + // It's copyable and assignable. + MasterToken token(token_num); + EXPECT_EQ(42, token.getNumber()); + EXPECT_EQ(MasterToken::NUMBER, token.getType()); + + token = token_num; + EXPECT_EQ(42, token.getNumber()); + EXPECT_EQ(MasterToken::NUMBER, token.getType()); + + // it's okay to replace it with a different type of token + token = token_eof; + EXPECT_EQ(MasterToken::END_OF_FILE, token.getType()); + + // Possible max value + token = MasterToken(0xffffffff); + EXPECT_EQ(4294967295u, token.getNumber()); + + // getNumber() isn't allowed for non number types + EXPECT_THROW(token_eof.getNumber(), isc::InvalidOperation); + EXPECT_THROW(token_str.getNumber(), isc::InvalidOperation); +} + +TEST_F(MasterLexerTokenTest, novalues) { + // Just checking we can construct them and getType() returns correct value. + EXPECT_EQ(MasterToken::END_OF_FILE, token_eof.getType()); + EXPECT_EQ(MasterToken::END_OF_LINE, + MasterToken(MasterToken::END_OF_LINE).getType()); + EXPECT_EQ(MasterToken::INITIAL_WS, + MasterToken(MasterToken::INITIAL_WS).getType()); + + // Special types of tokens cannot have value-based types + EXPECT_THROW(MasterToken t(MasterToken::STRING), isc::InvalidParameter); + EXPECT_THROW(MasterToken t(MasterToken::QSTRING), isc::InvalidParameter); + EXPECT_THROW(MasterToken t(MasterToken::NUMBER), isc::InvalidParameter); + EXPECT_THROW(MasterToken t(MasterToken::ERROR), isc::InvalidParameter); +} + +TEST_F(MasterLexerTokenTest, errors) { + EXPECT_EQ(MasterToken::ERROR, token_err.getType()); + EXPECT_EQ(MasterToken::UNEXPECTED_END, token_err.getErrorCode()); + EXPECT_EQ("unexpected end of input", token_err.getErrorText()); + EXPECT_EQ("lexer not started", MasterToken(MasterToken::NOT_STARTED). + getErrorText()); + EXPECT_EQ("unbalanced parentheses", + MasterToken(MasterToken::UNBALANCED_PAREN). + getErrorText()); + EXPECT_EQ("unbalanced quotes", MasterToken(MasterToken::UNBALANCED_QUOTES). + getErrorText()); + EXPECT_EQ("no token produced", MasterToken(MasterToken::NO_TOKEN_PRODUCED). + getErrorText()); + EXPECT_EQ("number out of range", + MasterToken(MasterToken::NUMBER_OUT_OF_RANGE). + getErrorText()); + EXPECT_EQ("not a valid number", + MasterToken(MasterToken::BAD_NUMBER).getErrorText()); + EXPECT_EQ("unexpected quotes", + MasterToken(MasterToken::UNEXPECTED_QUOTES).getErrorText()); + + // getErrorCode/Text() isn't allowed for non number types + EXPECT_THROW(token_num.getErrorCode(), isc::InvalidOperation); + EXPECT_THROW(token_num.getErrorText(), isc::InvalidOperation); + + // Only the pre-defined error code is accepted. Hardcoding '8' (max code + // + 1) is intentional; it'd be actually better if we notice it when we + // update the enum list (which shouldn't happen too often). + // + // Note: if you fix this testcase, you probably want to update the + // getErrorText() tests above too. + EXPECT_THROW(MasterToken(MasterToken::ErrorCode(8)), + isc::InvalidParameter); + + // Check the coexistence of "from number" and "from error-code" + // constructors won't cause confusion. + EXPECT_EQ(MasterToken::NUMBER, + MasterToken(static_cast<uint32_t>(MasterToken::NOT_STARTED)). + getType()); +} +} diff --git a/src/lib/dns/tests/master_lexer_unittest.cc b/src/lib/dns/tests/master_lexer_unittest.cc index 8b13789179..7bebb48bbe 100644 --- a/src/lib/dns/tests/master_lexer_unittest.cc +++ b/src/lib/dns/tests/master_lexer_unittest.cc @@ -1 +1,521 @@ +// Copyright (C) 2012-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <exceptions/exceptions.h> + +#include <dns/master_lexer.h> +#include <dns/master_lexer_state.h> + +#include <gtest/gtest.h> + +#include <boost/lexical_cast.hpp> +#include <boost/scoped_ptr.hpp> + +#include <string> +#include <sstream> + +using namespace isc::dns; +using std::string; +using std::stringstream; +using boost::lexical_cast; +using boost::scoped_ptr; +using master_lexer_internal::State; + +namespace { + +class MasterLexerTest : public ::testing::Test { +protected: + MasterLexerTest() : + expected_stream_name("stream-" + lexical_cast<string>(&ss)) + {} + + MasterLexer lexer; + stringstream ss; + const string expected_stream_name; +}; + +// Commonly used check case where the input sources stack is empty. +void +checkEmptySource(const MasterLexer& lexer) { + EXPECT_TRUE(lexer.getSourceName().empty()); + EXPECT_EQ(0, lexer.getSourceLine()); + EXPECT_EQ(0, lexer.getPosition()); +} + +TEST_F(MasterLexerTest, preOpen) { + // Initially sources stack is empty. + checkEmptySource(lexer); +} + +TEST_F(MasterLexerTest, pushStream) { + EXPECT_EQ(0, lexer.getSourceCount()); + ss << "test"; + lexer.pushSource(ss); + EXPECT_EQ(expected_stream_name, lexer.getSourceName()); + EXPECT_EQ(1, lexer.getSourceCount()); + EXPECT_EQ(4, lexer.getTotalSourceSize()); // 4 = len("test") + + // From the point of view of this test, we only have to check (though + // indirectly) getSourceLine calls InputSource::getCurrentLine. It should + // return 1 initially. + EXPECT_EQ(1, lexer.getSourceLine()); + + // By popping it the stack will be empty again. + lexer.popSource(); + EXPECT_EQ(0, lexer.getSourceCount()); + checkEmptySource(lexer); + EXPECT_EQ(4, lexer.getTotalSourceSize()); // this shouldn't change +} + +TEST_F(MasterLexerTest, pushStreamFail) { + // Pretend a "bad" thing happened in the stream. This will make the + // initialization throw an exception. + ss << "test"; + ss.setstate(std::ios_base::badbit); + + EXPECT_THROW(lexer.pushSource(ss), isc::Unexpected); +} + +TEST_F(MasterLexerTest, pushFile) { + // We use zone file (-like) data, but in this test that actually doesn't + // matter. + EXPECT_EQ(0, lexer.getSourceCount()); + EXPECT_TRUE(lexer.pushSource(TEST_DATA_SRCDIR "/masterload.txt")); + EXPECT_EQ(1, lexer.getSourceCount()); + EXPECT_EQ(TEST_DATA_SRCDIR "/masterload.txt", lexer.getSourceName()); + EXPECT_EQ(1, lexer.getSourceLine()); + + // 143 = size of the test zone file. hardcode it assuming it won't change + // too often. + EXPECT_EQ(143, lexer.getTotalSourceSize()); + + lexer.popSource(); + checkEmptySource(lexer); + EXPECT_EQ(0, lexer.getSourceCount()); + EXPECT_EQ(143, lexer.getTotalSourceSize()); // this shouldn't change + + // If we give a non NULL string pointer, its content will be intact + // if pushSource succeeds. + std::string error_txt = "dummy"; + EXPECT_TRUE(lexer.pushSource(TEST_DATA_SRCDIR "/masterload.txt", + &error_txt)); + EXPECT_EQ("dummy", error_txt); +} + +TEST_F(MasterLexerTest, pushBadFileName) { + EXPECT_THROW(lexer.pushSource(NULL), isc::InvalidParameter); +} + +TEST_F(MasterLexerTest, pushFileFail) { + // The file to be pushed doesn't exist. pushSource() fails and + // some non empty error string should be set. + std::string error_txt; + EXPECT_TRUE(error_txt.empty()); + EXPECT_FALSE(lexer.pushSource("no-such-file", &error_txt)); + EXPECT_FALSE(error_txt.empty()); + + // It's safe to pass NULL error_txt (either explicitly or implicitly as + // the default) + EXPECT_FALSE(lexer.pushSource("no-such-file", NULL)); + EXPECT_FALSE(lexer.pushSource("no-such-file")); +} + +TEST_F(MasterLexerTest, nestedPush) { + const string test_txt = "test"; + ss << test_txt; + lexer.pushSource(ss); + + EXPECT_EQ(test_txt.size(), lexer.getTotalSourceSize()); + EXPECT_EQ(0, lexer.getPosition()); + + EXPECT_EQ(expected_stream_name, lexer.getSourceName()); + + // Read the string; getPosition() should reflect that. + EXPECT_EQ(MasterToken::STRING, lexer.getNextToken().getType()); + EXPECT_EQ(test_txt.size(), lexer.getPosition()); + + // We can push another source without popping the previous one. + lexer.pushSource(TEST_DATA_SRCDIR "/masterload.txt"); + EXPECT_EQ(TEST_DATA_SRCDIR "/masterload.txt", lexer.getSourceName()); + EXPECT_EQ(143 + test_txt.size(), + lexer.getTotalSourceSize()); // see above for magic nums + + // the next token should be the EOL (skipping a comment line), its + // position in the file is 35 (hardcoded). + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + EXPECT_EQ(test_txt.size() + 35, lexer.getPosition()); + + // popSource() works on the "topmost" (last-pushed) source + lexer.popSource(); + EXPECT_EQ(expected_stream_name, lexer.getSourceName()); + + // pop shouldn't change the total size and the current position + EXPECT_EQ(143 + test_txt.size(), lexer.getTotalSourceSize()); + EXPECT_EQ(test_txt.size() + 35, lexer.getPosition()); + + lexer.popSource(); + EXPECT_TRUE(lexer.getSourceName().empty()); + + // size and position still shouldn't change + EXPECT_EQ(143 + test_txt.size(), lexer.getTotalSourceSize()); + EXPECT_EQ(test_txt.size() + 35, lexer.getPosition()); +} + +TEST_F(MasterLexerTest, unknownSourceSize) { + // Similar to the previous case, but the size of the second source + // will be considered "unknown" (by emulating an error). + ss << "test"; + lexer.pushSource(ss); + EXPECT_EQ(4, lexer.getTotalSourceSize()); + + stringstream ss2; + ss2.setstate(std::ios_base::failbit); // this will make the size unknown + lexer.pushSource(ss2); + // Then the total size is also unknown. + EXPECT_EQ(MasterLexer::SOURCE_SIZE_UNKNOWN, lexer.getTotalSourceSize()); + + // Even if we pop that source, the size is still unknown. + lexer.popSource(); + EXPECT_EQ(MasterLexer::SOURCE_SIZE_UNKNOWN, lexer.getTotalSourceSize()); +} + +TEST_F(MasterLexerTest, invalidPop) { + // popSource() cannot be called if the sources stack is empty. + EXPECT_THROW(lexer.popSource(), isc::InvalidOperation); +} + +// Test it is not possible to get token when no source is available. +TEST_F(MasterLexerTest, noSource) { + EXPECT_THROW(lexer.getNextToken(), isc::InvalidOperation); +} + +// Test getting some tokens. It also check basic behavior of getPosition(). +TEST_F(MasterLexerTest, getNextToken) { + ss << "\n \n\"STRING\"\n"; + lexer.pushSource(ss); + + // First, the newline should get out. + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + EXPECT_EQ(1, lexer.getPosition()); + // Then the whitespace, if we specify the option. + EXPECT_EQ(MasterToken::INITIAL_WS, + lexer.getNextToken(MasterLexer::INITIAL_WS).getType()); + EXPECT_EQ(2, lexer.getPosition()); + // The newline + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + EXPECT_EQ(5, lexer.getPosition()); // 1st \n + 3 spaces, then 2nd \n + // The (quoted) string + EXPECT_EQ(MasterToken::QSTRING, + lexer.getNextToken(MasterLexer::QSTRING).getType()); + EXPECT_EQ(5 + 8, lexer.getPosition()); // 8 = len("STRING') + quotes + + // And the end of line and file + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + EXPECT_EQ(5 + 8 + 1, lexer.getPosition()); // previous + 3rd \n + EXPECT_EQ(MasterToken::END_OF_FILE, lexer.getNextToken().getType()); + EXPECT_EQ(5 + 8 + 1, lexer.getPosition()); // position doesn't change +} + +// Test we correctly find end of file. +TEST_F(MasterLexerTest, eof) { + // Let the ss empty. + lexer.pushSource(ss); + + // The first one is found to be EOF + EXPECT_EQ(MasterToken::END_OF_FILE, lexer.getNextToken().getType()); + // And it stays on EOF for any following attempts + EXPECT_EQ(MasterToken::END_OF_FILE, lexer.getNextToken().getType()); + // And we can step back one token, but that is the EOF too. + lexer.ungetToken(); + EXPECT_EQ(MasterToken::END_OF_FILE, lexer.getNextToken().getType()); +} + +// Check we properly return error when there's an opened parentheses and no +// closing one +TEST_F(MasterLexerTest, getUnbalancedParen) { + ss << "(string"; + lexer.pushSource(ss); + + // The string gets out first + EXPECT_EQ(MasterToken::STRING, lexer.getNextToken().getType()); + // Then an unbalanced parenthesis + EXPECT_EQ(MasterToken::UNBALANCED_PAREN, + lexer.getNextToken().getErrorCode()); + // And then EOF + EXPECT_EQ(MasterToken::END_OF_FILE, lexer.getNextToken().getType()); +} + +// Check we properly return error when there's an opened quoted string and no +// closing one +TEST_F(MasterLexerTest, getUnbalancedString) { + ss << "\"string"; + lexer.pushSource(ss); + + // Then an unbalanced qstring (reported as an unexpected end) + EXPECT_EQ(MasterToken::UNEXPECTED_END, + lexer.getNextToken(MasterLexer::QSTRING).getErrorCode()); + // And then EOF + EXPECT_EQ(MasterToken::END_OF_FILE, lexer.getNextToken().getType()); +} + +// Test ungetting tokens works. Also check getPosition() is adjusted +TEST_F(MasterLexerTest, ungetToken) { + ss << "\n (\"string\"\n) more"; + lexer.pushSource(ss); + + // Try getting the newline + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + EXPECT_EQ(1, lexer.getPosition()); + // Return it and get again + lexer.ungetToken(); + EXPECT_EQ(0, lexer.getPosition()); + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + EXPECT_EQ(1, lexer.getPosition()); + // Get the string and return it back + EXPECT_EQ(MasterToken::QSTRING, + lexer.getNextToken(MasterLexer::QSTRING).getType()); + EXPECT_EQ(string("\n (\"string\"").size(), lexer.getPosition()); + lexer.ungetToken(); + EXPECT_EQ(1, lexer.getPosition()); // back to just after 1st \n + // But if we change the options, it honors them + EXPECT_EQ(MasterToken::INITIAL_WS, + lexer.getNextToken(MasterLexer::QSTRING | + MasterLexer::INITIAL_WS).getType()); + // Get to the "more" string + EXPECT_EQ(MasterToken::QSTRING, + lexer.getNextToken(MasterLexer::QSTRING).getType()); + EXPECT_EQ(MasterToken::STRING, + lexer.getNextToken(MasterLexer::QSTRING).getType()); + // Return it back. It should get inside the parentheses. + // Upon next attempt to get it again, the newline inside the parentheses + // should be still ignored. + lexer.ungetToken(); + EXPECT_EQ(MasterToken::STRING, + lexer.getNextToken(MasterLexer::QSTRING).getType()); +} + +// Check ungetting token without overriding the start method. We also +// check it works well with changing options between the calls. +TEST_F(MasterLexerTest, ungetRealOptions) { + ss << " \n"; + lexer.pushSource(ss); + + // If we call it the usual way, it skips up to the newline and returns + // it + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + + // Now we return it. If we call it again, but with different options, + // we get the initial whitespace. + lexer.ungetToken(); + EXPECT_EQ(MasterToken::INITIAL_WS, + lexer.getNextToken(MasterLexer::INITIAL_WS).getType()); +} + +// Check the initial whitespace is found even in the first line of included +// file. It also confirms getPosition() works for multiple sources, each +// of which is partially parsed. +TEST_F(MasterLexerTest, includeAndInitialWS) { + ss << " \n"; + lexer.pushSource(ss); + + stringstream ss2; + ss2 << " \n"; + + EXPECT_EQ(MasterToken::INITIAL_WS, + lexer.getNextToken(MasterLexer::INITIAL_WS).getType()); + EXPECT_EQ(1, lexer.getPosition()); + lexer.pushSource(ss2); + EXPECT_EQ(MasterToken::INITIAL_WS, + lexer.getNextToken(MasterLexer::INITIAL_WS).getType()); + EXPECT_EQ(2, lexer.getPosition()); // should be sum of pushed positions. +} + +// Test only one token can be ungotten +TEST_F(MasterLexerTest, ungetTwice) { + ss << "\n"; + lexer.pushSource(ss); + + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + // Unget the token. It can be done once + lexer.ungetToken(); + // But not twice + EXPECT_THROW(lexer.ungetToken(), isc::InvalidOperation); +} + +// Test we can't unget a token before we get one +TEST_F(MasterLexerTest, ungetBeforeGet) { + lexer.pushSource(ss); // Just to eliminate the missing source problem + EXPECT_THROW(lexer.ungetToken(), isc::InvalidOperation); +} + +// Test we can't unget a token after a source switch, even when we got +// something before. +TEST_F(MasterLexerTest, ungetAfterSwitch) { + ss << "\n\n"; + lexer.pushSource(ss); + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + // Switch the source + std::stringstream ss2; + ss2 << "\n\n"; + lexer.pushSource(ss2); + EXPECT_THROW(lexer.ungetToken(), isc::InvalidOperation); + // We can get from the new source + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); + // And when we drop the current source, we can't unget again + lexer.popSource(); + EXPECT_THROW(lexer.ungetToken(), isc::InvalidOperation); +} + +// Common checks for the case when getNextToken() should result in LexerError +void +lexerErrorCheck(MasterLexer& lexer, MasterToken::Type expect, + MasterToken::ErrorCode expected_error) +{ + bool thrown = false; + try { + lexer.getNextToken(expect); + } catch (const MasterLexer::LexerError& error) { + EXPECT_EQ(expected_error, error.token_.getErrorCode()); + thrown = true; + } + EXPECT_TRUE(thrown); +} + +// Common checks regarding expected/unexpected end-of-line +// +// The 'lexer' should be at a position before two consecutive '\n's. +// The first one will be recognized, and the second one will be considered an +// unexpected token. Then this helper consumes the second '\n', so the caller +// can continue the test after these '\n's. +void +eolCheck(MasterLexer& lexer, MasterToken::Type expect) { + // If EOL is found and eol_ok is true, we get it. + EXPECT_EQ(MasterToken::END_OF_LINE, + lexer.getNextToken(expect, true).getType()); + // We'll see the second '\n'; by default it will fail. + EXPECT_THROW(lexer.getNextToken(expect), MasterLexer::LexerError); + // Same if eol_ok is explicitly set to false. This also checks the + // offending '\n' was "ungotten". + EXPECT_THROW(lexer.getNextToken(expect, false), MasterLexer::LexerError); + + // And also check the error token set in the exception object. + lexerErrorCheck(lexer, expect, MasterToken::UNEXPECTED_END); + + // Then skip the 2nd '\n' + EXPECT_EQ(MasterToken::END_OF_LINE, lexer.getNextToken().getType()); +} + +// Common checks regarding expected/unexpected end-of-file +// +// The 'lexer' should be at a position just before an end-of-file. +void +eofCheck(MasterLexer& lexer, MasterToken::Type expect) { + EXPECT_EQ(MasterToken::END_OF_FILE, + lexer.getNextToken(expect, true).getType()); + EXPECT_THROW(lexer.getNextToken(expect), MasterLexer::LexerError); + EXPECT_THROW(lexer.getNextToken(expect, false), MasterLexer::LexerError); +} + +TEST_F(MasterLexerTest, getNextTokenString) { + ss << "normal-string\n"; + ss << "\n"; + ss << "another-string"; + lexer.pushSource(ss); + + // Normal successful case: Expecting a string and get one. + EXPECT_EQ("normal-string", + lexer.getNextToken(MasterToken::STRING).getString()); + eolCheck(lexer, MasterToken::STRING); + + // Same set of tests but for end-of-file + EXPECT_EQ("another-string", + lexer.getNextToken(MasterToken::STRING, true).getString()); + eofCheck(lexer, MasterToken::STRING); +} + +TEST_F(MasterLexerTest, getNextTokenQString) { + ss << "\"quoted-string\"\n"; + ss << "\n"; + ss << "normal-string"; + lexer.pushSource(ss); + + // Expecting a quoted string and get one. + EXPECT_EQ("quoted-string", + lexer.getNextToken(MasterToken::QSTRING).getString()); + eolCheck(lexer, MasterToken::QSTRING); + + // Expecting a quoted string but see a normal string. It's okay. + EXPECT_EQ("normal-string", + lexer.getNextToken(MasterToken::QSTRING).getString()); + eofCheck(lexer, MasterToken::QSTRING); +} + +TEST_F(MasterLexerTest, getNextTokenNumber) { + ss << "3600\n"; + ss << "\n"; + ss << "4294967296 "; // =2^32, out of range + ss << "not-a-number "; + ss << "123abc "; // starting with digits, but resulting in a string + ss << "86400"; + lexer.pushSource(ss); + + // Expecting a number string and get one. + EXPECT_EQ(3600, + lexer.getNextToken(MasterToken::NUMBER).getNumber()); + eolCheck(lexer, MasterToken::NUMBER); + + // Expecting a number, but it's too big for uint32. + lexerErrorCheck(lexer, MasterToken::NUMBER, + MasterToken::NUMBER_OUT_OF_RANGE); + // The token should have been "ungotten". Re-read and skip it. + EXPECT_EQ(MasterToken::STRING, lexer.getNextToken().getType()); + + // Expecting a number, but see a string. + lexerErrorCheck(lexer, MasterToken::NUMBER, MasterToken::BAD_NUMBER); + // The unexpected string should have been "ungotten". Re-read and skip it. + EXPECT_EQ(MasterToken::STRING, lexer.getNextToken().getType()); + + // Expecting a number, but see a string. + lexerErrorCheck(lexer, MasterToken::NUMBER, MasterToken::BAD_NUMBER); + // The unexpected string should have been "ungotten". Re-read and skip it. + EXPECT_EQ(MasterToken::STRING, lexer.getNextToken().getType()); + + // Unless we specify NUMBER, decimal number string should be recognized + // as a string. + EXPECT_EQ("86400", + lexer.getNextToken(MasterToken::STRING).getString()); + eofCheck(lexer, MasterToken::NUMBER); +} + +TEST_F(MasterLexerTest, getNextTokenErrors) { + // Check miscellaneous error cases + + ss << ") "; // unbalanced parenthesis + ss << "string-after-error "; + lexer.pushSource(ss); + + // Only string/qstring/number can be "expected". + EXPECT_THROW(lexer.getNextToken(MasterToken::END_OF_LINE), + isc::InvalidParameter); + EXPECT_THROW(lexer.getNextToken(MasterToken::END_OF_FILE), + isc::InvalidParameter); + EXPECT_THROW(lexer.getNextToken(MasterToken::INITIAL_WS), + isc::InvalidParameter); + EXPECT_THROW(lexer.getNextToken(MasterToken::ERROR), + isc::InvalidParameter); + + // If it encounters a syntax error, it results in LexerError exception. + lexerErrorCheck(lexer, MasterToken::STRING, MasterToken::UNBALANCED_PAREN); + + // Unlike the NUMBER_OUT_OF_RANGE case, the error part has been skipped + // within getNextToken(). We should be able to get the next token. + EXPECT_EQ("string-after-error", + lexer.getNextToken(MasterToken::STRING).getString()); +} + +} diff --git a/src/lib/dns/tests/master_loader_callbacks_test.cc b/src/lib/dns/tests/master_loader_callbacks_test.cc index 8b13789179..9d238023dd 100644 --- a/src/lib/dns/tests/master_loader_callbacks_test.cc +++ b/src/lib/dns/tests/master_loader_callbacks_test.cc @@ -1 +1,79 @@ +// Copyright (C) 2012-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <dns/master_loader_callbacks.h> +#include <dns/rrset.h> +#include <dns/name.h> +#include <dns/rrttl.h> +#include <dns/rrclass.h> + +#include <exceptions/exceptions.h> + +#include <gtest/gtest.h> +#include <functional> + +namespace { + +using std::string; +using namespace isc::dns; +namespace ph = std::placeholders; + +class MasterLoaderCallbacksTest : public ::testing::Test { +protected: + MasterLoaderCallbacksTest() : + last_was_error_(false), // Not needed, but then cppcheck complains + issue_called_(false), + rrset_(new RRset(Name("example.org"), RRClass::IN(), RRType::A(), + RRTTL(3600))), + error_(std::bind(&MasterLoaderCallbacksTest::checkCallback, this, + true, ph::_1, ph::_2, ph::_3)), + warning_(std::bind(&MasterLoaderCallbacksTest::checkCallback, this, + false, ph::_1, ph::_2, ph::_3)), + callbacks_(error_, warning_) + {} + + void checkCallback(bool error, const string& source, size_t line, + const string& reason) + { + issue_called_ = true; + last_was_error_ = error; + EXPECT_EQ("source", source); + EXPECT_EQ(1, line); + EXPECT_EQ("reason", reason); + } + bool last_was_error_; + bool issue_called_; + const RRsetPtr rrset_; + const MasterLoaderCallbacks::IssueCallback error_, warning_; + MasterLoaderCallbacks callbacks_; +}; + +// Check the constructor rejects empty callbacks, but accepts non-empty ones +TEST_F(MasterLoaderCallbacksTest, constructor) { + EXPECT_THROW(MasterLoaderCallbacks(MasterLoaderCallbacks::IssueCallback(), + warning_), isc::InvalidParameter); + EXPECT_THROW(MasterLoaderCallbacks(error_, + MasterLoaderCallbacks::IssueCallback()), + isc::InvalidParameter); + EXPECT_NO_THROW(MasterLoaderCallbacks(error_, warning_)); +} + +// Call the issue callbacks +TEST_F(MasterLoaderCallbacksTest, issueCall) { + callbacks_.error("source", 1, "reason"); + EXPECT_TRUE(last_was_error_); + EXPECT_TRUE(issue_called_); + + issue_called_ = false; + + callbacks_.warning("source", 1, "reason"); + EXPECT_FALSE(last_was_error_); + EXPECT_TRUE(issue_called_); +} + +} diff --git a/src/lib/dns/tests/master_loader_unittest.cc b/src/lib/dns/tests/master_loader_unittest.cc index 8b13789179..b9830072f6 100644 --- a/src/lib/dns/tests/master_loader_unittest.cc +++ b/src/lib/dns/tests/master_loader_unittest.cc @@ -1 +1,1426 @@ +// Copyright (C) 2012-2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <dns/master_loader_callbacks.h> +#include <dns/master_loader.h> +#include <dns/rrtype.h> +#include <dns/rrset.h> +#include <dns/rrclass.h> +#include <dns/rrttl.h> +#include <dns/name.h> +#include <dns/rdata.h> + +#include <gtest/gtest.h> + +#include <boost/lexical_cast.hpp> +#include <boost/scoped_ptr.hpp> + +#include <functional> +#include <string> +#include <vector> +#include <list> +#include <sstream> + +using namespace isc::dns; +using std::vector; +using std::string; +using std::list; +using std::stringstream; +using std::endl; +using boost::lexical_cast; +namespace ph = std::placeholders; + +namespace { +class MasterLoaderTest : public ::testing::Test { +public: + MasterLoaderTest() : + callbacks_(std::bind(&MasterLoaderTest::callback, this, + &errors_, ph::_1, ph::_2, ph::_3), + std::bind(&MasterLoaderTest::callback, this, + &warnings_, ph::_1, ph::_2, ph::_3)) { + } + + void TearDown() { + // Check there are no more RRs we didn't expect + EXPECT_TRUE(rrsets_.empty()); + } + + /// Concatenate file, line, and reason, and add it to either errors + /// or warnings + void callback(vector<string>* target, const std::string& file, size_t line, + const std::string& reason) { + std::stringstream ss; + ss << reason << " [" << file << ":" << line << "]"; + target->push_back(ss.str()); + } + + void addRRset(const Name& name, const RRClass& rrclass, + const RRType& rrtype, const RRTTL& rrttl, + const rdata::RdataPtr& data) { + const RRsetPtr rrset(new BasicRRset(name, rrclass, rrtype, rrttl)); + rrset->addRdata(data); + rrsets_.push_back(rrset); + } + + void setLoader(const char* file, const Name& origin, + const RRClass& rrclass, const MasterLoader::Options options) { + loader_.reset(new MasterLoader(file, origin, rrclass, callbacks_, + std::bind(&MasterLoaderTest::addRRset, + this, ph::_1, ph::_2, ph::_3, + ph::_4, ph::_5), + options)); + } + + void setLoader(std::istream& stream, const Name& origin, + const RRClass& rrclass, const MasterLoader::Options options) { + loader_.reset(new MasterLoader(stream, origin, rrclass, callbacks_, + std::bind(&MasterLoaderTest::addRRset, + this, ph::_1, ph::_2, ph::_3, + ph::_4, ph::_5), + options)); + } + + static string prepareZone(const string& line, bool include_last) { + string result; + result += "example.org. 3600 IN SOA ns1.example.org. " + "admin.example.org. 1234 3600 1800 2419200 7200\n"; + result += line; + if (include_last) { + result += "\n"; + result += "correct 3600 IN A 192.0.2.2\n"; + } + return (result); + } + + void clear() { + warnings_.clear(); + errors_.clear(); + rrsets_.clear(); + } + + // Check the next RR in the ones produced by the loader + // Other than passed arguments are checked to be the default for the tests + void checkRR(const string& name, const RRType& type, const string& data, + const RRTTL& rrttl = RRTTL(3600)) { + ASSERT_FALSE(rrsets_.empty()); + RRsetPtr current = rrsets_.front(); + rrsets_.pop_front(); + + EXPECT_EQ(Name(name), current->getName()); + EXPECT_EQ(type, current->getType()); + EXPECT_EQ(RRClass::IN(), current->getClass()); + EXPECT_EQ(rrttl, current->getTTL()); + ASSERT_EQ(1, current->getRdataCount()); + EXPECT_EQ(0, isc::dns::rdata::createRdata(type, RRClass::IN(), data)-> + compare(current->getRdataIterator()->getCurrent())) + << data << " vs. " + << current->getRdataIterator()->getCurrent().toText(); + } + + void checkBasicRRs() { + checkRR("example.org", RRType::SOA(), + "ns1.example.org. admin.example.org. " + "1234 3600 1800 2419200 7200"); + checkRR("example.org", RRType::NS(), "ns1.example.org."); + checkRR("www.example.org", RRType::A(), "192.0.2.1"); + checkRR("www.example.org", RRType::AAAA(), "2001:db8::1"); + } + + void checkARR(const string& name) { + checkRR(name, RRType::A(), "192.0.2.1"); + } + + MasterLoaderCallbacks callbacks_; + boost::scoped_ptr<MasterLoader> loader_; + vector<string> errors_; + vector<string> warnings_; + list<RRsetPtr> rrsets_; +}; + +// Test simple loading. The zone file contains no tricky things, and nothing is +// omitted. No RRset contains more than one RR Also no errors or warnings. +TEST_F(MasterLoaderTest, basicLoad) { + setLoader(TEST_DATA_SRCDIR "/example.org", Name("example.org."), + RRClass::IN(), MasterLoader::MANY_ERRORS); + + EXPECT_FALSE(loader_->loadedSuccessfully()); + + // The following three should be set to 0 initially in case the loader + // is constructed from a file name. + EXPECT_EQ(0, loader_->getSize()); + EXPECT_EQ(0, loader_->getPosition()); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + + EXPECT_TRUE(errors_.empty()); + EXPECT_TRUE(warnings_.empty()); + + // Hardcode expected values taken from the test data file, assuming it + // won't change too often. + EXPECT_EQ(550, loader_->getSize()); + EXPECT_EQ(550, loader_->getPosition()); + + checkBasicRRs(); +} + +// Test the $INCLUDE directive +TEST_F(MasterLoaderTest, include) { + // Test various cases of include + const char* includes[] = { + "$include", + "$INCLUDE", + "$Include", + "$InCluDe", + "\"$INCLUDE\"", + NULL + }; + for (const char** include = includes; *include != NULL; ++include) { + SCOPED_TRACE(*include); + + clear(); + // Prepare input source that has the include and some more data + // below (to see it returns back to the original source). + const string include_str = string(*include) + " " + + TEST_DATA_SRCDIR + "/example.org\nwww 3600 IN AAAA 2001:db8::1\n"; + stringstream ss(include_str); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + EXPECT_TRUE(warnings_.empty()); + + checkBasicRRs(); + checkRR("www.example.org", RRType::AAAA(), "2001:db8::1"); + } +} + +TEST_F(MasterLoaderTest, includeAndIncremental) { + // Check getSize() and getPosition() are adjusted before and after + // $INCLUDE. + const string first_rr = "before.example.org. 0 A 192.0.2.1\n"; + const string include_str = "$INCLUDE " TEST_DATA_SRCDIR "/example.org"; + const string zone_data = first_rr + include_str + "\n" + + "www 3600 IN AAAA 2001:db8::1\n"; + stringstream ss(zone_data); + setLoader(ss, Name("example.org."), RRClass::IN(), MasterLoader::DEFAULT); + + // On construction, getSize() returns the size of the data (exclude the + // the file to be included); position is set to 0. + EXPECT_EQ(zone_data.size(), loader_->getSize()); + EXPECT_EQ(0, loader_->getPosition()); + + // Read the first RR. getSize() doesn't change; position should be + // at the end of the first line. + loader_->loadIncremental(1); + EXPECT_EQ(zone_data.size(), loader_->getSize()); + EXPECT_EQ(first_rr.size(), loader_->getPosition()); + + // Read next 4. It includes $INCLUDE processing. Magic number of 550 + // is the size of the test zone file (see above); 507 is the position in + // the file at the end of 4th RR (due to extra comments it's smaller than + // the file size). + loader_->loadIncremental(4); + EXPECT_EQ(zone_data.size() + 550, loader_->getSize()); + EXPECT_EQ(first_rr.size() + include_str.size() + 507, + loader_->getPosition()); + + // Read the last one. At this point getSize and getPosition return + // the same value, indicating progress of 100%. + loader_->loadIncremental(1); + EXPECT_EQ(zone_data.size() + 550, loader_->getSize()); + EXPECT_EQ(zone_data.size() + 550, loader_->getPosition()); + + // we were not interested in checking RRs in this test. clear them to + // not confuse TearDown(). + rrsets_.clear(); +} + +// A commonly used helper to check callback message. +void +checkCallbackMessage(const string& actual_msg, const string& expected_msg, + size_t expected_line) { + // The actual message should begin with the expected message. + EXPECT_EQ(0, actual_msg.find(expected_msg)) << "actual message: " << + actual_msg << " expected: " << + expected_msg; + + // and it should end with "...:<line_num>]" + const string line_desc = ":" + lexical_cast<string>(expected_line) + "]"; + EXPECT_EQ(actual_msg.size() - line_desc.size(), + actual_msg.find(line_desc)) << "Expected on line " << + expected_line; +} + +TEST_F(MasterLoaderTest, origin) { + // Various forms of the directive + const char* origins[] = { + "$origin", + "$ORIGIN", + "$Origin", + "$OrigiN", + "\"$ORIGIN\"", + NULL + }; + for (const char** origin = origins; *origin != NULL; ++origin) { + SCOPED_TRACE(*origin); + + clear(); + const string directive = *origin; + const string input = + "@ 1H IN A 192.0.2.1\n" + + directive + " sub.example.org.\n" + "\"www\" 1H IN A 192.0.2.1\n" + + // Relative name in the origin + directive + " relative\n" + "@ 1H IN A 192.0.2.1\n" + // Origin is _not_ used here (absolute name) + "noorigin.example.org. 60M IN A 192.0.2.1\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + // There's a relative origin in it, we warn about that. + EXPECT_EQ(1, warnings_.size()); + checkCallbackMessage(warnings_.at(0), + "The new origin is relative, did you really mean " + "relative.sub.example.org.?", 4); + + checkARR("example.org"); + checkARR("www.sub.example.org"); + checkARR("relative.sub.example.org"); + checkARR("noorigin.example.org"); + } +} + +TEST_F(MasterLoaderTest, generate) { + // Various forms of the directive + const char* generates[] = { + "$generate", + "$GENERATE", + "$Generate", + "$GeneratE", + "\"$GENERATE\"", + NULL + }; + for (const char** generate = generates; *generate != NULL; ++generate) { + SCOPED_TRACE(*generate); + + clear(); + const string directive = *generate; + const string input = + "$ORIGIN example.org.\n" + "before.example.org. 3600 IN A 192.0.2.0\n" + + directive + " 3-5 host$ A 192.0.2.$\n" + + "after.example.org. 3600 IN A 192.0.2.255\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + // The "before" and "after" scaffolding below checks that no + // extra records are added by $GENERATE outside the requested + // range. + checkRR("before.example.org", RRType::A(), "192.0.2.0"); + checkRR("host3.example.org", RRType::A(), "192.0.2.3"); + checkRR("host4.example.org", RRType::A(), "192.0.2.4"); + checkRR("host5.example.org", RRType::A(), "192.0.2.5"); + checkRR("after.example.org", RRType::A(), "192.0.2.255"); + } +} + +TEST_F(MasterLoaderTest, generateRelativeLHS) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 1-2 @ 3600 NS ns$.example.org.\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("example.org", RRType::NS(), "ns1.example.org."); + checkRR("example.org", RRType::NS(), "ns2.example.org."); +} + +TEST_F(MasterLoaderTest, generateInFront) { + // $ is in the front + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 9-10 $host 3600 TXT \"$ pomegranate\"\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("9host.example.org", RRType::TXT(), "9 pomegranate"); + checkRR("10host.example.org", RRType::TXT(), "10 pomegranate"); +} + +TEST_F(MasterLoaderTest, generateInMiddle) { + // $ is in the middle + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 9-10 num$-host 3600 TXT \"This is $ pomegranate\"\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("num9-host.example.org", RRType::TXT(), "This is 9 pomegranate"); + checkRR("num10-host.example.org", RRType::TXT(), "This is 10 pomegranate"); +} + +TEST_F(MasterLoaderTest, generateAtEnd) { + // $ is at the end + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 9-10 num$-host 3600 TXT Pomegranate$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("num9-host.example.org", RRType::TXT(), "Pomegranate9"); + checkRR("num10-host.example.org", RRType::TXT(), "Pomegranate10"); +} + +TEST_F(MasterLoaderTest, generateWithDoublePlaceholder) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 9-10 host$ 3600 TXT \"This is $$ pomegranate\"\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("host9.example.org", RRType::TXT(), "This is $ pomegranate"); + checkRR("host10.example.org", RRType::TXT(), "This is $ pomegranate"); +} + +TEST_F(MasterLoaderTest, generateWithEscape) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 9-10 host$ 3600 TXT \"This is \\$\\pomegranate\"\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("host9.example.org", RRType::TXT(), "This is \\$\\pomegranate"); + checkRR("host10.example.org", RRType::TXT(), "This is \\$\\pomegranate"); +} + +TEST_F(MasterLoaderTest, generateWithParams) { + const string input = + "$ORIGIN example.org.\n" + "$TTL 3600\n" + "$GENERATE 2-3 host$ A 192.0.2.$\n" + "$GENERATE 5-6 host$ 3600 A 192.0.2.$\n" + "$GENERATE 8-9 host$ IN A 192.0.2.$\n" + "$GENERATE 11-12 host$ IN 3600 A 192.0.2.$\n" + "$GENERATE 14-15 host$ 3600 IN A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("host2.example.org", RRType::A(), "192.0.2.2"); + checkRR("host3.example.org", RRType::A(), "192.0.2.3"); + + checkRR("host5.example.org", RRType::A(), "192.0.2.5"); + checkRR("host6.example.org", RRType::A(), "192.0.2.6"); + + checkRR("host8.example.org", RRType::A(), "192.0.2.8"); + checkRR("host9.example.org", RRType::A(), "192.0.2.9"); + + checkRR("host11.example.org", RRType::A(), "192.0.2.11"); + checkRR("host12.example.org", RRType::A(), "192.0.2.12"); + + checkRR("host14.example.org", RRType::A(), "192.0.2.14"); + checkRR("host15.example.org", RRType::A(), "192.0.2.15"); +} + +TEST_F(MasterLoaderTest, generateWithStep) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 2-9/2 host$ 3600 A 192.0.2.$\n" + "$GENERATE 12-21/3 host$ 3600 A 192.0.2.$\n" + "$GENERATE 30-31/1 host$ 3600 A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("host2.example.org", RRType::A(), "192.0.2.2"); + checkRR("host4.example.org", RRType::A(), "192.0.2.4"); + checkRR("host6.example.org", RRType::A(), "192.0.2.6"); + checkRR("host8.example.org", RRType::A(), "192.0.2.8"); + + checkRR("host12.example.org", RRType::A(), "192.0.2.12"); + checkRR("host15.example.org", RRType::A(), "192.0.2.15"); + checkRR("host18.example.org", RRType::A(), "192.0.2.18"); + checkRR("host21.example.org", RRType::A(), "192.0.2.21"); + + checkRR("host30.example.org", RRType::A(), "192.0.2.30"); + checkRR("host31.example.org", RRType::A(), "192.0.2.31"); +} + +TEST_F(MasterLoaderTest, generateWithModifiers) { + const string input = + "$ORIGIN example.org.\n" + "$TTL 3600\n" + + // Use a positive delta of 1 in the LHS and a negative delta of + // -1 in the RHS + "$GENERATE 2-9/2 host${1} A 192.0.2.${-1}\n" + + "$GENERATE 10-12 host${0,4} A 192.0.2.$\n" + "$GENERATE 14-15 host${0,4,d} A 192.0.2.$\n" + + // Names are case-insensitive, so we use TXT's RDATA to check + // case with hex representation. + "$GENERATE 30-31 host$ TXT \"Value ${0,4,x}\"\n" + "$GENERATE 42-43 host$ TXT \"Value ${0,4,X}\"\n" + + // Octal does not use any alphabets + "$GENERATE 45-46 host${0,4,o} A 192.0.2.$\n" + + // Here, the LHS has a trailing dot (which would result in an + // out-of-zone name), but that should be handled as a relative + // name. + "$GENERATE 90-92 ${0,8,n} A 192.0.2.$\n" + + // Here, the LHS has no trailing dot, and results in the same + // number of labels as width=8 above. + "$GENERATE 94-96 ${0,7,n} A 192.0.2.$\n" + + // Names are case-insensitive, so we use TXT's RDATA to check + // case with nibble representation. + "$GENERATE 106-107 host$ TXT \"Value ${0,9,n}\"\n" + "$GENERATE 109-110 host$ TXT \"Value ${0,9,N}\"\n" + + // Junk type will not parse and 'd' is assumed. No error is + // generated (this is to match BIND 9 behavior). + "$GENERATE 200-201 host${0,4,j} A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + + checkRR("host3.example.org", RRType::A(), "192.0.2.1"); + checkRR("host5.example.org", RRType::A(), "192.0.2.3"); + checkRR("host7.example.org", RRType::A(), "192.0.2.5"); + checkRR("host9.example.org", RRType::A(), "192.0.2.7"); + + checkRR("host0010.example.org", RRType::A(), "192.0.2.10"); + checkRR("host0011.example.org", RRType::A(), "192.0.2.11"); + checkRR("host0012.example.org", RRType::A(), "192.0.2.12"); + + checkRR("host0014.example.org", RRType::A(), "192.0.2.14"); + checkRR("host0015.example.org", RRType::A(), "192.0.2.15"); + + checkRR("host30.example.org", RRType::TXT(), "Value 001e"); + checkRR("host31.example.org", RRType::TXT(), "Value 001f"); + + checkRR("host42.example.org", RRType::TXT(), "Value 002A"); + checkRR("host43.example.org", RRType::TXT(), "Value 002B"); + + checkRR("host0055.example.org", RRType::A(), "192.0.2.45"); + checkRR("host0056.example.org", RRType::A(), "192.0.2.46"); + + checkRR("a.5.0.0.example.org", RRType::A(), "192.0.2.90"); + checkRR("b.5.0.0.example.org", RRType::A(), "192.0.2.91"); + checkRR("c.5.0.0.example.org", RRType::A(), "192.0.2.92"); + + checkRR("e.5.0.0.example.org", RRType::A(), "192.0.2.94"); + checkRR("f.5.0.0.example.org", RRType::A(), "192.0.2.95"); + checkRR("0.6.0.0.example.org", RRType::A(), "192.0.2.96"); + + checkRR("host106.example.org", RRType::TXT(), "Value a.6.0.0.0"); + checkRR("host107.example.org", RRType::TXT(), "Value b.6.0.0.0"); + checkRR("host109.example.org", RRType::TXT(), "Value D.6.0.0.0"); + checkRR("host110.example.org", RRType::TXT(), "Value E.6.0.0.0"); + + checkRR("host0200.example.org", RRType::A(), "192.0.2.200"); + checkRR("host0201.example.org", RRType::A(), "192.0.2.201"); +} + +TEST_F(MasterLoaderTest, generateWithNoModifiers) { + const string input = + "$ORIGIN example.org.\n" + "$TTL 3600\n" + "$GENERATE 10-12 host${} A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + ASSERT_EQ(2, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "Invalid $GENERATE format modifiers", 3); + checkCallbackMessage(errors_.at(1), + "$GENERATE error", 3); +} + +TEST_F(MasterLoaderTest, generateWithBadModifiers) { + const string input = + "$ORIGIN example.org.\n" + "$TTL 3600\n" + "$GENERATE 10-12 host${GARBAGE} A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + ASSERT_EQ(2, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "Invalid $GENERATE format modifiers", 3); + checkCallbackMessage(errors_.at(1), + "$GENERATE error", 3); +} + +TEST_F(MasterLoaderTest, generateMissingRange) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "unexpected end of input", 2); +} + +TEST_F(MasterLoaderTest, generateMissingLHS) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 2-4\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "unexpected end of input", 2); +} + +TEST_F(MasterLoaderTest, generateMissingType) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 2-4 host$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "unexpected end of input", 2); +} + +TEST_F(MasterLoaderTest, generateMissingRHS) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 2-4 host$ A\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "unexpected end of input", 2); +} + +TEST_F(MasterLoaderTest, generateWithBadRangeSyntax) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE ABCD host$ 3600 A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "$GENERATE: invalid range: ABCD", 2); +} + +TEST_F(MasterLoaderTest, generateWithInvalidRange) { + // start > stop + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 2-1 host$ 3600 A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "$GENERATE: invalid range: 2-1", 2); +} + +TEST_F(MasterLoaderTest, generateWithInvalidClass) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 1-2 host$ 3600 CH A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "Class mismatch: CH vs. IN", 2); +} + +TEST_F(MasterLoaderTest, generateWithNoAvailableTTL) { + const string input = + "$ORIGIN example.org.\n" + "$GENERATE 1-2 host$ A 192.0.2.$\n"; + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken GENERATE + EXPECT_TRUE(warnings_.empty()); + + checkCallbackMessage(errors_.at(0), + "no TTL specified; load rejected", 2); +} + +// Test the source is correctly popped even after error +TEST_F(MasterLoaderTest, popAfterError) { + const string include_str = "$include " TEST_DATA_SRCDIR + "/broken.zone\nwww 3600 IN AAAA 2001:db8::1\n"; + stringstream ss(include_str); + // We perform the test with MANY_ERRORS, we want to see what happens + // after the error. + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); // For the broken RR + EXPECT_EQ(1, warnings_.size()); // For missing EOLN + + // The included file doesn't contain anything usable, but the + // line after the include should be there. + checkRR("www.example.org", RRType::AAAA(), "2001:db8::1"); +} + +// Check it works the same when created based on a stream, not filename +TEST_F(MasterLoaderTest, streamConstructor) { + const string zone_data(prepareZone("", true)); + stringstream zone_stream(zone_data); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + EXPECT_FALSE(loader_->loadedSuccessfully()); + + // Unlike the basicLoad test, if we construct the loader from a stream + // getSize() returns the data size in the stream immediately after the + // construction. + EXPECT_EQ(zone_data.size(), loader_->getSize()); + EXPECT_EQ(0, loader_->getPosition()); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + + EXPECT_TRUE(errors_.empty()); + EXPECT_TRUE(warnings_.empty()); + checkRR("example.org", RRType::SOA(), "ns1.example.org. " + "admin.example.org. 1234 3600 1800 2419200 7200"); + checkRR("correct.example.org", RRType::A(), "192.0.2.2"); + + // On completion of the load, both getSize() and getPosition() return the + // size of the data. + EXPECT_EQ(zone_data.size(), loader_->getSize()); + EXPECT_EQ(zone_data.size(), loader_->getPosition()); +} + +// Try loading data incrementally. +TEST_F(MasterLoaderTest, incrementalLoad) { + setLoader(TEST_DATA_SRCDIR "/example.org", Name("example.org."), + RRClass::IN(), MasterLoader::MANY_ERRORS); + + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_FALSE(loader_->loadIncremental(2)); + EXPECT_FALSE(loader_->loadedSuccessfully()); + + EXPECT_TRUE(errors_.empty()); + EXPECT_TRUE(warnings_.empty()); + + checkRR("example.org", RRType::SOA(), + "ns1.example.org. admin.example.org. " + "1234 3600 1800 2419200 7200"); + checkRR("example.org", RRType::NS(), "ns1.example.org."); + + // The third one is not loaded yet + EXPECT_TRUE(rrsets_.empty()); + + // Load the rest. + EXPECT_TRUE(loader_->loadIncremental(20)); + EXPECT_TRUE(loader_->loadedSuccessfully()); + + EXPECT_TRUE(errors_.empty()); + EXPECT_TRUE(warnings_.empty()); + + checkRR("www.example.org", RRType::A(), "192.0.2.1"); + checkRR("www.example.org", RRType::AAAA(), "2001:db8::1"); +} + +// Try loading from file that doesn't exist. There should be single error +// saying so. +TEST_F(MasterLoaderTest, invalidFile) { + setLoader("This file doesn't exist at all", + Name("example.org."), RRClass::IN(), MasterLoader::MANY_ERRORS); + + // Nothing yet. The loader is dormant until invoked. + // Is it really what we want? + EXPECT_TRUE(errors_.empty()); + + loader_->load(); + + EXPECT_TRUE(warnings_.empty()); + EXPECT_TRUE(rrsets_.empty()); + ASSERT_EQ(1, errors_.size()); + EXPECT_EQ(0, errors_[0].find("Error opening the input source file: ")) << + "Different error: " << errors_[0]; +} + +struct ErrorCase { + const char* const line; // The broken line in master file + const char* const reason; // If non NULL, the reason string + const char* const problem; // Description of the problem for SCOPED_TRACE +} const error_cases[] = { + { "www... 3600 IN A 192.0.2.1", NULL, "Invalid name" }, + { "www FORTNIGHT IN A 192.0.2.1", NULL, "Invalid TTL" }, + { "www 3600 XX A 192.0.2.1", NULL, "Invalid class" }, + { "www 3600 IN A bad_ip", NULL, "Invalid Rdata" }, + + // Parameter ordering errors + { "www IN A 3600 192.168.2.7", + "createRdata from text failed: Bad IN/A RDATA text: '3600'", + "Incorrect order of class, TTL and type" }, + { "www A IN 3600 192.168.2.8", + "createRdata from text failed: Bad IN/A RDATA text: 'IN'", + "Incorrect order of class, TTL and type" }, + { "www 3600 A IN 192.168.2.7", + "createRdata from text failed: Bad IN/A RDATA text: 'IN'", + "Incorrect order of class, TTL and type" }, + { "www A 3600 IN 192.168.2.8", + "createRdata from text failed: Bad IN/A RDATA text: '3600'", + "Incorrect order of class, TTL and type" }, + + // Missing type and Rdata + { "www", "unexpected end of input", "Missing type and Rdata" }, + { "www 3600", "unexpected end of input", "Missing type and Rdata" }, + { "www IN", "unexpected end of input", "Missing type and Rdata" }, + { "www 3600 IN", "unexpected end of input", "Missing type and Rdata" }, + { "www IN 3600", "unexpected end of input", "Missing type and Rdata" }, + + // Missing Rdata + { "www A", + "createRdata from text failed: unexpected end of input", + "Missing Rdata" }, + { "www 3600 A", + "createRdata from text failed: unexpected end of input", + "Missing Rdata" }, + { "www IN A", + "createRdata from text failed: unexpected end of input", + "Missing Rdata" }, + { "www 3600 IN A", + "createRdata from text failed: unexpected end of input", + "Missing Rdata" }, + { "www IN 3600 A", + "createRdata from text failed: unexpected end of input", + "Missing Rdata" }, + + { "www 3600 IN", NULL, "Unexpected EOLN" }, + { "www 3600 CH TXT nothing", "Class mismatch: CH vs. IN", + "Class mismatch" }, + { "www \"3600\" IN A 192.0.2.1", NULL, "Quoted TTL" }, + { "www 3600 \"IN\" A 192.0.2.1", NULL, "Quoted class" }, + { "www 3600 IN \"A\" 192.0.2.1", NULL, "Quoted type" }, + { "unbalanced)paren 3600 IN A 192.0.2.1", NULL, "Token error 1" }, + { "www 3600 unbalanced)paren A 192.0.2.1", NULL, + "Token error 2" }, + // Check the unknown directive. The rest looks like ordinary RR, + // so we see the $ is actually special. + { "$UNKNOWN 3600 IN A 192.0.2.1", NULL, "Unknown $ directive" }, + { "$INCLUD " TEST_DATA_SRCDIR "/example.org", "Unknown directive 'INCLUD'", + "Include too short" }, + { "$INCLUDES " TEST_DATA_SRCDIR "/example.org", + "Unknown directive 'INCLUDES'", "Include too long" }, + { "$INCLUDE", "unexpected end of input", "Missing include path" }, + // The following two error messages are system dependent, omitting + { "$INCLUDE /file/not/found", NULL, "Include file not found" }, + { "$INCLUDE /file/not/found example.org. and here goes bunch of garbage", + NULL, "Include file not found and garbage at the end of line" }, + { "$ORIGIN", "unexpected end of input", "Missing origin name" }, + { "$ORIGIN invalid...name", "duplicate period in invalid...name", + "Invalid name for origin" }, + { "$ORIGIN )brokentoken", "unbalanced parentheses", + "Broken token in origin" }, + { "$ORIGIN example.org. garbage", "Extra tokens at the end of line", + "Garbage after origin" }, + { "$ORIGI name.", "Unknown directive 'ORIGI'", "$ORIGIN too short" }, + { "$ORIGINAL name.", "Unknown directive 'ORIGINAL'", "$ORIGIN too long" }, + { "$TTL 100 extra-garbage", "Extra tokens at the end of line", + "$TTL with extra token" }, + { "$TTL", "unexpected end of input", "missing TTL" }, + { "$TTL No-ttl", "Unknown unit used: N in: No-ttl", "bad TTL" }, + { "$TTL \"100\"", "unexpected quotes", "bad TTL, quoted" }, + { "$TT 100", "Unknown directive 'TT'", "bad directive, too short" }, + { "$TTLLIKE 100", "Unknown directive 'TTLLIKE'", "bad directive, extra" }, + { NULL, NULL, NULL } +}; + +// Test a broken zone is handled properly. We test several problems, +// both in strict and lenient mode. +TEST_F(MasterLoaderTest, brokenZone) { + for (const ErrorCase* ec = error_cases; ec->line != NULL; ++ec) { + SCOPED_TRACE(ec->problem); + const string zone(prepareZone(ec->line, true)); + + { + SCOPED_TRACE("Strict mode"); + clear(); + stringstream zone_stream(zone); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::DEFAULT); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_THROW(loader_->load(), MasterLoaderError); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); + if (ec->reason != NULL) { + checkCallbackMessage(errors_.at(0), ec->reason, 2); + } + EXPECT_TRUE(warnings_.empty()); + + checkRR("example.org", RRType::SOA(), "ns1.example.org. " + "admin.example.org. 1234 3600 1800 2419200 7200"); + // In the strict mode, it is aborted. The last RR is not + // even attempted. + EXPECT_TRUE(rrsets_.empty()); + } + + { + SCOPED_TRACE("Lenient mode"); + clear(); + stringstream zone_stream(zone); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_NO_THROW(loader_->load()); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); + EXPECT_TRUE(warnings_.empty()); + checkRR("example.org", RRType::SOA(), "ns1.example.org. " + "admin.example.org. 1234 3600 1800 2419200 7200"); + // This one is below the error one. + checkRR("correct.example.org", RRType::A(), "192.0.2.2"); + EXPECT_TRUE(rrsets_.empty()); + } + + { + SCOPED_TRACE("Error at EOF"); + // This case is interesting only in the lenient mode. + clear(); + const string zoneEOF(prepareZone(ec->line, false)); + stringstream zone_stream(zoneEOF); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_NO_THROW(loader_->load()); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()) << errors_[0] << "\n" << errors_[1]; + // The unexpected EOF warning + EXPECT_EQ(1, warnings_.size()); + checkRR("example.org", RRType::SOA(), "ns1.example.org. " + "admin.example.org. 1234 3600 1800 2419200 7200"); + EXPECT_TRUE(rrsets_.empty()); + } + } +} + +// Check that a garbage after the include generates an error, but not fatal +// one (in lenient mode) and we can recover. +TEST_F(MasterLoaderTest, includeWithGarbage) { + // Include an origin (example.org) because we expect it to be handled + // soon and we don't want it to break here. + const string include_str("$INCLUDE " TEST_DATA_SRCDIR + "/example.org example.org. bunch of other stuff\n" + "www 3600 IN AAAA 2001:db8::1\n"); + stringstream zone_stream(include_str); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + EXPECT_NO_THROW(loader_->load()); + EXPECT_FALSE(loader_->loadedSuccessfully()); + ASSERT_EQ(1, errors_.size()); + checkCallbackMessage(errors_.at(0), "Extra tokens at the end of line", 1); + // It says something about extra tokens at the end + EXPECT_NE(string::npos, errors_[0].find("Extra")); + EXPECT_TRUE(warnings_.empty()); + checkBasicRRs(); + checkRR("www.example.org", RRType::AAAA(), "2001:db8::1"); +} + +// Check we error about garbage at the end of $ORIGIN line (but the line +// works). +TEST_F(MasterLoaderTest, originWithGarbage) { + const string origin_str = "$ORIGIN www.example.org. More garbage here\n" + "@ 1H IN A 192.0.2.1\n"; + stringstream ss(origin_str); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + EXPECT_NO_THROW(loader_->load()); + EXPECT_FALSE(loader_->loadedSuccessfully()); + ASSERT_EQ(1, errors_.size()); + checkCallbackMessage(errors_.at(0), "Extra tokens at the end of line", 1); + EXPECT_TRUE(warnings_.empty()); + checkARR("www.example.org"); +} + +// Test we can pass both file to include and the origin to switch +TEST_F(MasterLoaderTest, includeAndOrigin) { + // First, switch origin to something else, so we can check it is + // switched back. + const string include_string = "$ORIGIN www.example.org.\n" + "@ 1H IN A 192.0.2.1\n" + // Then include the file with data and switch origin back + "$INCLUDE " TEST_DATA_SRCDIR "/example.org example.org.\n" + // Another RR to see we fall back to the previous origin. + "www 1H IN A 192.0.2.1\n"; + stringstream ss(include_string); + setLoader(ss, Name("example.org"), RRClass::IN(), + MasterLoader::MANY_ERRORS); + // Successfully load the data + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + EXPECT_TRUE(warnings_.empty()); + // And check it's the correct data + checkARR("www.example.org"); + checkBasicRRs(); + checkARR("www.www.example.org"); +} + +// Like above, but the origin after include is bogus. The whole line should +// be rejected. +TEST_F(MasterLoaderTest, includeAndBadOrigin) { + const string include_string = + "$INCLUDE " TEST_DATA_SRCDIR "/example.org example..org.\n" + // Another RR to see the switch survives after we exit include + "www 1H IN A 192.0.2.1\n"; + stringstream ss(include_string); + setLoader(ss, Name("example.org"), RRClass::IN(), + MasterLoader::MANY_ERRORS); + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); + checkCallbackMessage(errors_.at(0), "duplicate period in example..org.", + 1); + EXPECT_TRUE(warnings_.empty()); + // And check it's the correct data + checkARR("www.example.org"); +} + +// Check the origin doesn't get outside of the included file. +TEST_F(MasterLoaderTest, includeOriginRestore) { + const string include_string = + "$INCLUDE " TEST_DATA_SRCDIR "/origincheck.txt\n" + "@ 1H IN A 192.0.2.1\n"; + stringstream ss(include_string); + setLoader(ss, Name("example.org"), RRClass::IN(), + MasterLoader::MANY_ERRORS); + // Successfully load the data + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + EXPECT_TRUE(warnings_.empty()); + // And check it's the correct data + checkARR("www.example.org"); + checkARR("example.org"); +} + +// Check we restore the last name for initial whitespace when returning from +// include. But we do produce a warning if there's one just ofter the include. +TEST_F(MasterLoaderTest, includeAndInitialWS) { + const string include_string = "xyz 1H IN A 192.0.2.1\n" + "$INCLUDE " TEST_DATA_SRCDIR "/example.org\n" + " 1H IN A 192.0.2.1\n"; + stringstream ss(include_string); + setLoader(ss, Name("example.org"), RRClass::IN(), + MasterLoader::MANY_ERRORS); + // Successfully load the data + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + EXPECT_EQ(1, warnings_.size()); + checkCallbackMessage(warnings_.at(0), + "Owner name omitted around $INCLUDE, the result might " + "not be as expected", 3); + checkARR("xyz.example.org"); + checkBasicRRs(); + checkARR("xyz.example.org"); +} + +// Test for "$TTL" +TEST_F(MasterLoaderTest, ttlDirective) { + stringstream zone_stream; + + // Set the default TTL with $TTL followed by an RR omitting the TTL + zone_stream << "$TTL 1800\nexample.org. IN A 192.0.2.1\n"; + // $TTL can be quoted. Also testing the case of $TTL being changed. + zone_stream << "\"$TTL\" 100\na.example.org. IN A 192.0.2.2\n"; + // Extended TTL form is accepted. + zone_stream << "$TTL 1H\nb.example.org. IN A 192.0.2.3\n"; + // Matching is case insensitive. + zone_stream << "$tTl 360\nc.example.org. IN A 192.0.2.4\n"; + // Maximum allowable TTL + zone_stream << "$TTL 2147483647\nd.example.org. IN A 192.0.2.5\n"; + + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::DEFAULT); + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + checkRR("example.org", RRType::A(), "192.0.2.1", RRTTL(1800)); + checkRR("a.example.org", RRType::A(), "192.0.2.2", RRTTL(100)); + checkRR("b.example.org", RRType::A(), "192.0.2.3", RRTTL(3600)); + checkRR("c.example.org", RRType::A(), "192.0.2.4", RRTTL(360)); + checkRR("d.example.org", RRType::A(), "192.0.2.5", RRTTL(2147483647)); +} + +TEST_F(MasterLoaderTest, ttlFromSOA) { + // No $TTL, and the SOA doesn't have an explicit TTL field. Its minimum + // TTL field will be used as the RR's TTL, and it'll be used as the + // default TTL for others. + stringstream zone_stream("example.org. IN SOA . . 0 0 0 0 1800\n" + "a.example.org. IN A 192.0.2.1\n"); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::DEFAULT); + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + checkRR("example.org", RRType::SOA(), ". . 0 0 0 0 1800", RRTTL(1800)); + checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(1800)); + + // The use of SOA minimum TTL should have caused a warning. + EXPECT_EQ(1, warnings_.size()); + checkCallbackMessage(warnings_.at(0), + "no TTL specified; using SOA MINTTL instead", 1); +} + +TEST_F(MasterLoaderTest, ttlFromPrevious) { + // No available default TTL. 2nd and 3rd RR will use the TTL of the + // 1st RR. This will result in a warning, but only for the first time. + stringstream zone_stream("a.example.org. 1800 IN A 192.0.2.1\n" + "b.example.org. IN A 192.0.2.2\n" + "c.example.org. IN A 192.0.2.3\n"); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::DEFAULT); + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(1800)); + checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(1800)); + checkRR("c.example.org", RRType::A(), "192.0.2.3", RRTTL(1800)); + + EXPECT_EQ(1, warnings_.size()); + checkCallbackMessage(warnings_.at(0), "using RFC1035 TTL semantics", 2); +} + +TEST_F(MasterLoaderTest, RRParamsOrdering) { + // We test the order and existence of TTL, class and type. See + // MasterLoader::MasterLoaderImpl::parseRRParams() for ordering. + + stringstream zone_stream; + // <TTL> <class> <type> <RDATA> + zone_stream << "a.example.org. 1800 IN A 192.0.2.1\n"; + // <type> <RDATA> + zone_stream << "b.example.org. A 192.0.2.2\n"; + // <class> <TTL> <type> <RDATA> + zone_stream << "c.example.org. IN 3600 A 192.0.2.3\n"; + // <TTL> <type> <RDATA> + zone_stream << "d.example.org. 7200 A 192.0.2.4\n"; + // <class> <type> <RDATA> + zone_stream << "e.example.org. IN A 192.0.2.5\n"; + + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::DEFAULT); + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(1800)); + checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(1800)); + checkRR("c.example.org", RRType::A(), "192.0.2.3", RRTTL(3600)); + checkRR("d.example.org", RRType::A(), "192.0.2.4", RRTTL(7200)); + checkRR("e.example.org", RRType::A(), "192.0.2.5", RRTTL(7200)); + + EXPECT_EQ(1, warnings_.size()); + checkCallbackMessage(warnings_.at(0), "using RFC1035 TTL semantics", 2); +} + +TEST_F(MasterLoaderTest, ttlFromPreviousSOA) { + // Mixture of the previous two cases: SOA has explicit TTL, followed by + // an RR without an explicit TTL. In this case the minimum TTL won't be + // recognized as the "default TTL". + stringstream zone_stream("example.org. 100 IN SOA . . 0 0 0 0 1800\n" + "a.example.org. IN A 192.0.2.1\n"); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::DEFAULT); + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + + checkRR("example.org", RRType::SOA(), ". . 0 0 0 0 1800", RRTTL(100)); + checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(100)); + + EXPECT_EQ(1, warnings_.size()); + checkCallbackMessage(warnings_.at(0), "using RFC1035 TTL semantics", 2); +} + +TEST_F(MasterLoaderTest, ttlUnknown) { + // No available TTL is known for the first RR. + stringstream zone_stream("a.example.org. IN A 192.0.2.1\n"); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::DEFAULT); + EXPECT_THROW(loader_->load(), MasterLoaderError); +} + +TEST_F(MasterLoaderTest, ttlUnknownAndContinue) { + stringstream zone_stream("a.example.org. IN A 192.0.2.1\n" + "b.example.org. 1800 IN A 192.0.2.2\n"); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(1800)); + + EXPECT_TRUE(warnings_.empty()); + EXPECT_EQ(1, errors_.size()); + checkCallbackMessage(errors_.at(0), "no TTL specified; load rejected", 1); +} + +TEST_F(MasterLoaderTest, ttlUnknownAndEOF) { + // Similar to the previous case, but the input will be abruptly terminated + // after the offending RR. This will cause an additional warning. + stringstream zone_stream("a.example.org. IN A 192.0.2.1"); + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_TRUE(rrsets_.empty()); + + EXPECT_EQ(1, errors_.size()); + checkCallbackMessage(errors_.at(0), "no TTL specified; load rejected", 1); + + // RDATA implementation can complain about it, too. To be independent of + // its details, we focus on the very last warning. + EXPECT_FALSE(warnings_.empty()); + checkCallbackMessage(*warnings_.rbegin(), "File does not end with newline", + 1); +} + +TEST_F(MasterLoaderTest, ttlOverflow) { + stringstream zone_stream; + zone_stream << "example.org. IN SOA . . 0 0 0 0 2147483648\n"; + zone_stream << "$TTL 3600\n"; // reset to an in-range value + zone_stream << "$TTL 2147483649\n"; + zone_stream << "a.example.org. IN A 192.0.2.1\n"; + zone_stream << "$TTL 3600\n"; // reset to an in-range value + zone_stream << "b.example.org. 2147483650 IN A 192.0.2.2\n"; + setLoader(zone_stream, Name("example.org."), RRClass::IN(), + MasterLoader::DEFAULT); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_EQ(3, rrsets_.size()); + + checkRR("example.org", RRType::SOA(), ". . 0 0 0 0 2147483648", RRTTL(0)); + checkRR("a.example.org", RRType::A(), "192.0.2.1", RRTTL(0)); + checkRR("b.example.org", RRType::A(), "192.0.2.2", RRTTL(0)); + + EXPECT_EQ(4, warnings_.size()); + checkCallbackMessage(warnings_.at(1), + "TTL 2147483648 > MAXTTL, setting to 0 per RFC2181", + 1); + checkCallbackMessage(warnings_.at(2), + "TTL 2147483649 > MAXTTL, setting to 0 per RFC2181", + 3); + checkCallbackMessage(warnings_.at(3), + "TTL 2147483650 > MAXTTL, setting to 0 per RFC2181", + 6); +} + +// Test the constructor rejects empty add callback. +TEST_F(MasterLoaderTest, emptyCallback) { + EXPECT_THROW(MasterLoader(TEST_DATA_SRCDIR "/example.org", + Name("example.org"), RRClass::IN(), callbacks_, + AddRRCallback()), isc::InvalidParameter); + // And the same with the second constructor + stringstream ss(""); + EXPECT_THROW(MasterLoader(ss, Name("example.org"), RRClass::IN(), + callbacks_, AddRRCallback()), + isc::InvalidParameter); +} + +// Check it throws when we try to load after loading was complete. +TEST_F(MasterLoaderTest, loadTwice) { + setLoader(TEST_DATA_SRCDIR "/example.org", Name("example.org."), + RRClass::IN(), MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_THROW(loader_->load(), isc::InvalidOperation); + // Don't check them, they are not interesting, so suppress the error + // at TearDown + rrsets_.clear(); +} + +// Load 0 items should be rejected +TEST_F(MasterLoaderTest, loadZero) { + setLoader(TEST_DATA_SRCDIR "/example.org", Name("example.org."), + RRClass::IN(), MasterLoader::MANY_ERRORS); + EXPECT_THROW(loader_->loadIncremental(0), isc::InvalidParameter); +} + +// Test there's a warning when the file terminates without end of +// line. +TEST_F(MasterLoaderTest, noEOLN) { + // No \n at the end + const string input("example.org. 3600 IN SOA ns1.example.org. " + "admin.example.org. 1234 3600 1800 2419200 7200"); + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + // There should be one warning about the EOLN + EXPECT_EQ(1, warnings_.size()); + checkRR("example.org", RRType::SOA(), "ns1.example.org. " + "admin.example.org. 1234 3600 1800 2419200 7200"); +} + +// Test it rejects when we don't have the previous name to use in place of +// initial whitespace +TEST_F(MasterLoaderTest, noPreviousName) { + const string input(" 1H IN A 192.0.2.1\n"); + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + loader_->load(); + EXPECT_FALSE(loader_->loadedSuccessfully()); + EXPECT_EQ(1, errors_.size()); + checkCallbackMessage(errors_.at(0), "No previous name to use in place of " + "initial whitespace", 1); + EXPECT_TRUE(warnings_.empty()); +} + +// Check we warn if the first RR in an included file has omitted name +TEST_F(MasterLoaderTest, previousInInclude) { + const string input("www 1H IN A 192.0.2.1\n" + "$INCLUDE " TEST_DATA_SRCDIR "/omitcheck.txt\n"); + stringstream ss(input); + setLoader(ss, Name("example.org"), RRClass::IN(), + MasterLoader::MANY_ERRORS); + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + // There should be one warning about the EOLN + EXPECT_EQ(1, warnings_.size()); + checkCallbackMessage(warnings_.at(0), "Owner name omitted around " + "$INCLUDE, the result might not be as expected", 1); + checkARR("www.example.org"); + checkARR("www.example.org"); +} + +TEST_F(MasterLoaderTest, numericOwnerName) { + const string input("$ORIGIN example.org.\n" + "1 3600 IN A 192.0.2.1\n"); + stringstream ss(input); + setLoader(ss, Name("example.org."), RRClass::IN(), + MasterLoader::MANY_ERRORS); + + loader_->load(); + EXPECT_TRUE(loader_->loadedSuccessfully()); + EXPECT_TRUE(errors_.empty()); + EXPECT_TRUE(warnings_.empty()); + + checkRR("1.example.org", RRType::A(), "192.0.2.1"); +} + +} diff --git a/src/lib/dns/tests/masterload_unittest.cc b/src/lib/dns/tests/masterload_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/masterload_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/message_unittest.cc b/src/lib/dns/tests/message_unittest.cc index 8b13789179..850d018158 100644 --- a/src/lib/dns/tests/message_unittest.cc +++ b/src/lib/dns/tests/message_unittest.cc @@ -1 +1,1157 @@ +// Copyright (C) 2010-2024 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <fstream> + +#include <boost/scoped_ptr.hpp> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/time_utilities.h> + +#include <util/unittests/testdata.h> +#include <util/unittests/textdata.h> + +#include <dns/edns.h> +#include <dns/exceptions.h> +#include <dns/message.h> +#include <dns/messagerenderer.h> +#include <dns/question.h> +#include <dns/opcode.h> +#include <dns/rcode.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrttl.h> +#include <dns/rrtype.h> +#include <dns/tsig.h> +#include <dns/tsigkey.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +// +// Note: we need more tests, including: +// parsing malformed headers +// more complete tests about parsing/rendering header flags, opcode, rcode, etc. +// tests for adding RRsets +// tests for RRset/Question iterators +// But, we'll ship with the current set of tests for now, partly because many +// of the above are covered as part of other tests, and partly due to time +// limitation. We also expect to revisit the fundamental design of the Message +// class, at which point we'll also revise the tests including more cases. +// + +const uint16_t Message::DEFAULT_MAX_UDPSIZE; + +namespace isc { +namespace util { +namespace detail { +extern int64_t (*gettimeFunction)(); +} +} +} + +// XXX: this is defined as class static constants, but some compilers +// seemingly cannot find the symbol when used in the EXPECT_xxx macros. +const uint16_t TSIGContext::DEFAULT_FUDGE; + +namespace { +class MessageTest : public ::testing::Test { +protected: + MessageTest() : test_name("test.example.com"), obuffer(0), + message_parse(Message::PARSE), + message_render(Message::RENDER), + bogus_section(static_cast<Message::Section>( + Message::SECTION_ADDITIONAL + 1)), + tsig_ctx(TSIGKey("www.example.com:" + "SFuWd/q99SzF8Yzd1QbB9g==")) { + rrset_a = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::A(), RRTTL(3600))); + rrset_a->addRdata(in::A("192.0.2.1")); + rrset_a->addRdata(in::A("192.0.2.2")); + + rrset_aaaa = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::AAAA(), RRTTL(3600))); + rrset_aaaa->addRdata(in::AAAA("2001:db8::1234")); + + rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::RRSIG(), RRTTL(3600))); + rrset_rrsig->addRdata(generic::RRSIG("AAAA 5 3 7200 20100322084538 " + "20100220084538 1 example.com. " + "FAKEFAKEFAKEFAKE")); + rrset_aaaa->addRRsig(rrset_rrsig); + } + + static Question factoryFromFile(const char* datafile); + const Name test_name; + OutputBuffer obuffer; + MessageRenderer renderer; + Message message_parse; + Message message_render; + const Message::Section bogus_section; + RRsetPtr rrset_a; // A RRset with two RDATAs + RRsetPtr rrset_aaaa; // AAAA RRset with one RDATA with RRSIG + RRsetPtr rrset_rrsig; // RRSIG for the AAAA RRset + TSIGContext tsig_ctx; + vector<unsigned char> received_data; + vector<unsigned char> expected_data; + + void factoryFromFile(Message& message, const char* datafile, + Message::ParseOptions options = + Message::PARSE_DEFAULT); +}; + +void +MessageTest::factoryFromFile(Message& message, const char* datafile, + Message::ParseOptions options) +{ + received_data.clear(); + UnitTestUtil::readWireData(datafile, received_data); + + InputBuffer buffer(&received_data[0], received_data.size()); + message.fromWire(buffer, options); +} + +TEST_F(MessageTest, headerFlag) { + // by default no flag is set + EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_QR)); + EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_AA)); + EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_TC)); + EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_RD)); + EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_RA)); + EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_AD)); + EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_CD)); + + // set operation: by default it will be on + message_render.setHeaderFlag(Message::HEADERFLAG_QR); + EXPECT_TRUE(message_render.getHeaderFlag(Message::HEADERFLAG_QR)); + + // it can be set to on explicitly, too + message_render.setHeaderFlag(Message::HEADERFLAG_AA, true); + EXPECT_TRUE(message_render.getHeaderFlag(Message::HEADERFLAG_AA)); + + // the bit can also be cleared + message_render.setHeaderFlag(Message::HEADERFLAG_AA, false); + EXPECT_FALSE(message_render.getHeaderFlag(Message::HEADERFLAG_AA)); + + // Invalid flag values + EXPECT_THROW(message_render.setHeaderFlag( + static_cast<Message::HeaderFlag>(0)), InvalidParameter); + EXPECT_THROW(message_render.setHeaderFlag( + static_cast<Message::HeaderFlag>(0x7000)), + InvalidParameter); + EXPECT_THROW(message_render.setHeaderFlag( + static_cast<Message::HeaderFlag>(0x0800)), + InvalidParameter); + EXPECT_THROW(message_render.setHeaderFlag( + static_cast<Message::HeaderFlag>(0x0040)), + InvalidParameter); + EXPECT_THROW(message_render.setHeaderFlag( + static_cast<Message::HeaderFlag>(0x10000)), + InvalidParameter); + EXPECT_THROW(message_render.setHeaderFlag( + static_cast<Message::HeaderFlag>(0x80000000)), + InvalidParameter); + + // set operation isn't allowed in the parse mode. + EXPECT_THROW(message_parse.setHeaderFlag(Message::HEADERFLAG_QR), + InvalidMessageOperation); +} +TEST_F(MessageTest, getEDNS) { + EXPECT_FALSE(message_parse.getEDNS()); // by default EDNS isn't set + + factoryFromFile(message_parse, "message_fromWire10.wire"); + EXPECT_TRUE(message_parse.getEDNS()); + EXPECT_EQ(0, message_parse.getEDNS()->getVersion()); + EXPECT_EQ(4096, message_parse.getEDNS()->getUDPSize()); + EXPECT_TRUE(message_parse.getEDNS()->getDNSSECAwareness()); +} + +TEST_F(MessageTest, setEDNS) { + // setEDNS() isn't allowed in the parse mode + EXPECT_THROW(message_parse.setEDNS(EDNSPtr(new EDNS())), + InvalidMessageOperation); + + EDNSPtr edns = EDNSPtr(new EDNS()); + message_render.setEDNS(edns); + EXPECT_EQ(edns, message_render.getEDNS()); +} + +TEST_F(MessageTest, fromWireWithTSIG) { + // Initially there should be no TSIG + EXPECT_EQ(static_cast<void*>(NULL), message_parse.getTSIGRecord()); + + // getTSIGRecord() is only valid in the parse mode. + EXPECT_THROW(message_render.getTSIGRecord(), InvalidMessageOperation); + + factoryFromFile(message_parse, "message_toWire2.wire"); + const uint8_t expected_mac[] = { + 0x22, 0x70, 0x26, 0xad, 0x29, 0x7b, 0xee, 0xe7, + 0x21, 0xce, 0x6c, 0x6f, 0xff, 0x1e, 0x9e, 0xf3 + }; + const TSIGRecord* tsig_rr = message_parse.getTSIGRecord(); + ASSERT_NE(static_cast<void*>(NULL), tsig_rr); + EXPECT_EQ(Name("www.example.com"), tsig_rr->getName()); + EXPECT_EQ(85, tsig_rr->getLength()); // see TSIGRecordTest.getLength + EXPECT_EQ(TSIGKey::HMACMD5_NAME(), tsig_rr->getRdata().getAlgorithm()); + EXPECT_EQ(0x4da8877a, tsig_rr->getRdata().getTimeSigned()); + EXPECT_EQ(TSIGContext::DEFAULT_FUDGE, tsig_rr->getRdata().getFudge()); + matchWireData(expected_mac, sizeof(expected_mac), + tsig_rr->getRdata().getMAC(), + tsig_rr->getRdata().getMACSize()); + EXPECT_EQ(0, tsig_rr->getRdata().getError()); + EXPECT_EQ(0, tsig_rr->getRdata().getOtherLen()); + EXPECT_EQ(static_cast<void*>(NULL), tsig_rr->getRdata().getOtherData()); + + // If we clear the message for reuse, the recorded TSIG will be cleared. + message_parse.clear(Message::PARSE); + EXPECT_EQ(static_cast<void*>(NULL), message_parse.getTSIGRecord()); +} + +TEST_F(MessageTest, fromWireWithTSIGCompressed) { + // Mostly same as fromWireWithTSIG, but the TSIG owner name is compressed. + factoryFromFile(message_parse, "message_fromWire12.wire"); + const TSIGRecord* tsig_rr = message_parse.getTSIGRecord(); + ASSERT_NE(static_cast<void*>(NULL), tsig_rr); + EXPECT_EQ(Name("www.example.com"), tsig_rr->getName()); + // len(www.example.com) = 17, but when fully compressed, the length is + // 2 bytes. So the length of the record should be 15 bytes shorter. + EXPECT_EQ(70, tsig_rr->getLength()); +} + +TEST_F(MessageTest, fromWireWithBadTSIG) { + // Multiple TSIG RRs + EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire13.wire"), + DNSMessageFORMERR); + message_parse.clear(Message::PARSE); + + // TSIG in the answer section (must be in additional) + EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire14.wire"), + DNSMessageFORMERR); + message_parse.clear(Message::PARSE); + + // TSIG is not the last record. + EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire15.wire"), + DNSMessageFORMERR); + message_parse.clear(Message::PARSE); + + // Unexpected RR Class (this will fail in constructing TSIGRecord) + EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire16.wire"), + DNSMessageFORMERR); +} + +TEST_F(MessageTest, getRRCount) { + // by default all counters should be 0 + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_QUESTION)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ANSWER)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL)); + + message_render.addQuestion(Question(Name("test.example.com"), + RRClass::IN(), RRType::A())); + EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION)); + + // rrset_a contains two RRs + message_render.addRRset(Message::SECTION_ANSWER, rrset_a); + EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER)); + + // parse a message containing a Question and EDNS OPT RR. + // OPT shouldn't be counted as normal RR, so result of getRRCount + // shouldn't change. + factoryFromFile(message_parse, "message_fromWire11.wire"); + EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL)); + + // out-of-band section ID + EXPECT_THROW(message_parse.getRRCount(bogus_section), OutOfRange); +} + +TEST_F(MessageTest, addRRset) { + // initially, we have 0 + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ANSWER)); + + // add two A RRs (unsigned) + message_render.addRRset(Message::SECTION_ANSWER, rrset_a); + EXPECT_EQ(rrset_a, + *message_render.beginSection(Message::SECTION_ANSWER)); + EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER)); + + message_render.clear(Message::RENDER); + + // add one AAAA RR (signed) + message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa); + EXPECT_EQ(rrset_aaaa, + *message_render.beginSection(Message::SECTION_ANSWER)); + EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER)); +} + +TEST_F(MessageTest, badAddRRset) { + // addRRset() isn't allowed in the parse mode. + EXPECT_THROW(message_parse.addRRset(Message::SECTION_ANSWER, + rrset_a), InvalidMessageOperation); + // out-of-band section ID + EXPECT_THROW(message_render.addRRset(bogus_section, rrset_a), OutOfRange); + + // NULL RRset + EXPECT_THROW(message_render.addRRset(Message::SECTION_ANSWER, RRsetPtr()), + InvalidParameter); +} + +TEST_F(MessageTest, hasRRset) { + message_render.addRRset(Message::SECTION_ANSWER, rrset_a); + EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::A())); + // section doesn't match + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name, + RRClass::IN(), RRType::A())); + // name doesn't match + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, + Name("nomatch.example"), + RRClass::IN(), RRType::A())); + // RR class doesn't match + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::CH(), RRType::A())); + // RR type doesn't match + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::AAAA())); + + // out-of-band section ID + EXPECT_THROW(message_render.hasRRset(bogus_section, test_name, + RRClass::IN(), RRType::A()), + OutOfRange); + + // Repeat the checks having created an RRset of the appropriate type. + + RRsetPtr rrs1(new RRset(test_name, RRClass::IN(), RRType::A(), RRTTL(60))); + EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, rrs1)); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ADDITIONAL, rrs1)); + + RRsetPtr rrs2(new RRset(Name("nomatch.example"), RRClass::IN(), RRType::A(), + RRTTL(5))); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, rrs2)); + + RRsetPtr rrs3(new RRset(test_name, RRClass::CH(), RRType::A(), RRTTL(60))); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, rrs3)); + + RRsetPtr rrs4(new RRset(test_name, RRClass::IN(), RRType::AAAA(), RRTTL(5))); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, rrs4)); + + RRsetPtr rrs5(new RRset(test_name, RRClass::IN(), RRType::AAAA(), RRTTL(5))); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, rrs4)); + + EXPECT_THROW(message_render.hasRRset(bogus_section, rrs1), OutOfRange); +} + +TEST_F(MessageTest, removeRRset) { + message_render.addRRset(Message::SECTION_ANSWER, rrset_a); + message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa); + EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::A())); + EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::AAAA())); + EXPECT_EQ(4, message_render.getRRCount(Message::SECTION_ANSWER)); + + // Locate the AAAA RRset and remove it and any associated RRSIGs + RRsetIterator i = message_render.beginSection(Message::SECTION_ANSWER); + if ((*i)->getType() == RRType::A()) { + ++i; + } + EXPECT_EQ(RRType::AAAA(), (*i)->getType()); + message_render.removeRRset(Message::SECTION_ANSWER, i); + + EXPECT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::A())); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::AAAA())); + EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER)); +} + +TEST_F(MessageTest, clearQuestionSection) { + QuestionPtr q(new Question(Name("www.example.com"), RRClass::IN(), + RRType::A())); + message_render.addQuestion(q); + ASSERT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION)); + + message_render.clearSection(Message::SECTION_QUESTION); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_QUESTION)); + EXPECT_TRUE(message_render.beginQuestion() == + message_render.endQuestion()); +} + + +TEST_F(MessageTest, clearAnswerSection) { + // Add two RRsets, check they are present, clear the section, + // check if they are gone. + message_render.addRRset(Message::SECTION_ANSWER, rrset_a); + message_render.addRRset(Message::SECTION_ANSWER, rrset_aaaa); + ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::A())); + ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::AAAA())); + ASSERT_EQ(4, message_render.getRRCount(Message::SECTION_ANSWER)); + + message_render.clearSection(Message::SECTION_ANSWER); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::A())); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::AAAA())); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ANSWER)); +} + +TEST_F(MessageTest, clearAuthoritySection) { + // Add two RRsets, check they are present, clear the section, + // check if they are gone. + message_render.addRRset(Message::SECTION_AUTHORITY, rrset_a); + message_render.addRRset(Message::SECTION_AUTHORITY, rrset_aaaa); + ASSERT_TRUE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name, + RRClass::IN(), RRType::A())); + ASSERT_TRUE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name, + RRClass::IN(), RRType::AAAA())); + ASSERT_EQ(4, message_render.getRRCount(Message::SECTION_AUTHORITY)); + + message_render.clearSection(Message::SECTION_AUTHORITY); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name, + RRClass::IN(), RRType::A())); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_AUTHORITY, test_name, + RRClass::IN(), RRType::AAAA())); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY)); +} + +TEST_F(MessageTest, clearAdditionalSection) { + // Add two RRsets, check they are present, clear the section, + // check if they are gone. + message_render.addRRset(Message::SECTION_ADDITIONAL, rrset_a); + message_render.addRRset(Message::SECTION_ADDITIONAL, rrset_aaaa); + ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name, + RRClass::IN(), RRType::A())); + ASSERT_TRUE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name, + RRClass::IN(), RRType::AAAA())); + ASSERT_EQ(4, message_render.getRRCount(Message::SECTION_ADDITIONAL)); + + message_render.clearSection(Message::SECTION_ADDITIONAL); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name, + RRClass::IN(), RRType::A())); + EXPECT_FALSE(message_render.hasRRset(Message::SECTION_ADDITIONAL, test_name, + RRClass::IN(), RRType::AAAA())); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL)); +} + +TEST_F(MessageTest, badClearSection) { + // attempt of clearing a message in the parse mode. + EXPECT_THROW(message_parse.clearSection(Message::SECTION_QUESTION), + InvalidMessageOperation); + // attempt of clearing out-of-range section + EXPECT_THROW(message_render.clearSection(bogus_section), OutOfRange); +} + +TEST_F(MessageTest, badBeginSection) { + // valid cases are tested via other tests + EXPECT_THROW(message_render.beginSection(Message::SECTION_QUESTION), + InvalidMessageSection); + EXPECT_THROW(message_render.beginSection(bogus_section), OutOfRange); +} + +TEST_F(MessageTest, badEndSection) { + // valid cases are tested via other tests + EXPECT_THROW(message_render.endSection(Message::SECTION_QUESTION), + InvalidMessageSection); + EXPECT_THROW(message_render.endSection(bogus_section), OutOfRange); +} + +TEST_F(MessageTest, appendSection) { + Message target(Message::RENDER); + + // Section check + EXPECT_THROW(target.appendSection(bogus_section, message_render), + OutOfRange); + + // Make sure nothing is copied if there is nothing to copy + target.appendSection(Message::SECTION_QUESTION, message_render); + EXPECT_EQ(0, target.getRRCount(Message::SECTION_QUESTION)); + target.appendSection(Message::SECTION_ANSWER, message_render); + EXPECT_EQ(0, target.getRRCount(Message::SECTION_ANSWER)); + target.appendSection(Message::SECTION_AUTHORITY, message_render); + EXPECT_EQ(0, target.getRRCount(Message::SECTION_AUTHORITY)); + target.appendSection(Message::SECTION_ADDITIONAL, message_render); + EXPECT_EQ(0, target.getRRCount(Message::SECTION_ADDITIONAL)); + + // Now add some data, copy again, and see if it got added + message_render.addQuestion(Question(Name("test.example.com"), + RRClass::IN(), RRType::A())); + message_render.addRRset(Message::SECTION_ANSWER, rrset_a); + message_render.addRRset(Message::SECTION_AUTHORITY, rrset_a); + message_render.addRRset(Message::SECTION_ADDITIONAL, rrset_a); + message_render.addRRset(Message::SECTION_ADDITIONAL, rrset_aaaa); + + target.appendSection(Message::SECTION_QUESTION, message_render); + EXPECT_EQ(1, target.getRRCount(Message::SECTION_QUESTION)); + + target.appendSection(Message::SECTION_ANSWER, message_render); + EXPECT_EQ(2, target.getRRCount(Message::SECTION_ANSWER)); + EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::A())); + + target.appendSection(Message::SECTION_AUTHORITY, message_render); + EXPECT_EQ(2, target.getRRCount(Message::SECTION_AUTHORITY)); + EXPECT_TRUE(target.hasRRset(Message::SECTION_AUTHORITY, test_name, + RRClass::IN(), RRType::A())); + + target.appendSection(Message::SECTION_ADDITIONAL, message_render); + EXPECT_EQ(4, target.getRRCount(Message::SECTION_ADDITIONAL)); + EXPECT_TRUE(target.hasRRset(Message::SECTION_ADDITIONAL, test_name, + RRClass::IN(), RRType::A())); + EXPECT_TRUE(target.hasRRset(Message::SECTION_ADDITIONAL, test_name, + RRClass::IN(), RRType::AAAA())); + + // One more test, test to see if the section gets added, not replaced + Message source2(Message::RENDER); + source2.addRRset(Message::SECTION_ANSWER, rrset_aaaa); + target.appendSection(Message::SECTION_ANSWER, source2); + EXPECT_EQ(4, target.getRRCount(Message::SECTION_ANSWER)); + EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::A())); + EXPECT_TRUE(target.hasRRset(Message::SECTION_ANSWER, test_name, + RRClass::IN(), RRType::AAAA())); +} + +TEST_F(MessageTest, parseHeader) { + received_data.clear(); + UnitTestUtil::readWireData("message_fromWire1", received_data); + + // parseHeader() isn't allowed in the render mode. + InputBuffer buffer(&received_data[0], received_data.size()); + EXPECT_THROW(message_render.parseHeader(buffer), InvalidMessageOperation); + + message_parse.parseHeader(buffer); + EXPECT_EQ(0x1035, message_parse.getQid()); + EXPECT_EQ(Opcode::QUERY(), message_parse.getOpcode()); + EXPECT_EQ(Rcode::NOERROR(), message_parse.getRcode()); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_QR)); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_AA)); + EXPECT_FALSE(message_parse.getHeaderFlag(Message::HEADERFLAG_TC)); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_RD)); + EXPECT_FALSE(message_parse.getHeaderFlag(Message::HEADERFLAG_RA)); + EXPECT_FALSE(message_parse.getHeaderFlag(Message::HEADERFLAG_AD)); + EXPECT_FALSE(message_parse.getHeaderFlag(Message::HEADERFLAG_CD)); + EXPECT_EQ(1, message_parse.getRRCount(Message::SECTION_QUESTION)); + EXPECT_EQ(2, message_parse.getRRCount(Message::SECTION_ANSWER)); + EXPECT_EQ(0, message_parse.getRRCount(Message::SECTION_AUTHORITY)); + EXPECT_EQ(0, message_parse.getRRCount(Message::SECTION_ADDITIONAL)); + + // Only the header part should have been examined. + EXPECT_EQ(12, buffer.getPosition()); // 12 = size of the header section + EXPECT_TRUE(message_parse.beginQuestion() == message_parse.endQuestion()); + EXPECT_TRUE(message_parse.beginSection(Message::SECTION_ANSWER) == + message_parse.endSection(Message::SECTION_ANSWER)); + EXPECT_TRUE(message_parse.beginSection(Message::SECTION_AUTHORITY) == + message_parse.endSection(Message::SECTION_AUTHORITY)); + EXPECT_TRUE(message_parse.beginSection(Message::SECTION_ADDITIONAL) == + message_parse.endSection(Message::SECTION_ADDITIONAL)); +} + +void +checkMessageFromWire(const Message& message_parse, + const Name& test_name) +{ + EXPECT_EQ(0x1035, message_parse.getQid()); + EXPECT_EQ(Opcode::QUERY(), message_parse.getOpcode()); + EXPECT_EQ(Rcode::NOERROR(), message_parse.getRcode()); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_QR)); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_RD)); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_AA)); + + QuestionPtr q = *message_parse.beginQuestion(); + EXPECT_EQ(test_name, q->getName()); + EXPECT_EQ(RRType::A(), q->getType()); + EXPECT_EQ(RRClass::IN(), q->getClass()); + EXPECT_EQ(1, message_parse.getRRCount(Message::SECTION_QUESTION)); + EXPECT_EQ(2, message_parse.getRRCount(Message::SECTION_ANSWER)); + EXPECT_EQ(0, message_parse.getRRCount(Message::SECTION_AUTHORITY)); + EXPECT_EQ(0, message_parse.getRRCount(Message::SECTION_ADDITIONAL)); + + RRsetPtr rrset = *message_parse.beginSection(Message::SECTION_ANSWER); + EXPECT_EQ(test_name, rrset->getName()); + EXPECT_EQ(RRType::A(), rrset->getType()); + EXPECT_EQ(RRClass::IN(), rrset->getClass()); + // TTL should be 3600, even though that of the 2nd RR is 7200 + EXPECT_EQ(RRTTL(3600), rrset->getTTL()); + RdataIteratorPtr it = rrset->getRdataIterator(); + EXPECT_EQ("192.0.2.1", it->getCurrent().toText()); + it->next(); + EXPECT_EQ("192.0.2.2", it->getCurrent().toText()); + it->next(); + EXPECT_TRUE(it->isLast()); +} + + +TEST_F(MessageTest, fromWire) { + // fromWire() isn't allowed in the render mode. + EXPECT_THROW(factoryFromFile(message_render, "message_fromWire1"), + InvalidMessageOperation); + + factoryFromFile(message_parse, "message_fromWire1"); + checkMessageFromWire(message_parse, test_name); +} + +TEST_F(MessageTest, fromWireMultiple) { + // Parse from wire multiple times. + factoryFromFile(message_parse, "message_fromWire1"); + factoryFromFile(message_parse, "message_fromWire1"); + factoryFromFile(message_parse, "message_fromWire1"); + factoryFromFile(message_parse, "message_fromWire1"); + checkMessageFromWire(message_parse, test_name); + + // Calling parseHeader() directly before fromWire() should not cause + // any problems. + received_data.clear(); + UnitTestUtil::readWireData("message_fromWire1", received_data); + + InputBuffer buffer(&received_data[0], received_data.size()); + message_parse.parseHeader(buffer); + message_parse.fromWire(buffer); + message_parse.parseHeader(buffer); + message_parse.fromWire(buffer); + checkMessageFromWire(message_parse, test_name); +} + +TEST_F(MessageTest, fromWireShortBuffer) { + // We trim a valid message (ending with an SOA RR) for one byte. + // fromWire() should throw an exception while parsing the trimmed RR. + UnitTestUtil::readWireData("message_fromWire22.wire", received_data); + InputBuffer buffer(&received_data[0], received_data.size() - 1); + EXPECT_THROW(message_parse.fromWire(buffer), InvalidBufferPosition); +} + +TEST_F(MessageTest, fromWireCombineRRs) { + // This message contains 3 RRs in the answer section in the order of + // A, AAAA, A types. fromWire() should combine the two A RRs into a + // single RRset by default. + factoryFromFile(message_parse, "message_fromWire19.wire"); + + RRsetIterator it = message_parse.beginSection(Message::SECTION_ANSWER); + RRsetIterator it_end = message_parse.endSection(Message::SECTION_ANSWER); + ASSERT_TRUE(it != it_end); + EXPECT_EQ(RRType::A(), (*it)->getType()); + EXPECT_EQ(2, (*it)->getRdataCount()); + + ++it; + ASSERT_TRUE(it != it_end); + EXPECT_EQ(RRType::AAAA(), (*it)->getType()); + EXPECT_EQ(1, (*it)->getRdataCount()); +} + +// A helper function for a test pattern commonly used in several tests below. +void +preserveRRCheck(const Message& message, Message::Section section) { + RRsetIterator it = message.beginSection(section); + RRsetIterator it_end = message.endSection(section); + ASSERT_TRUE(it != it_end); + EXPECT_EQ(RRType::A(), (*it)->getType()); + EXPECT_EQ(1, (*it)->getRdataCount()); + EXPECT_EQ("192.0.2.1", (*it)->getRdataIterator()->getCurrent().toText()); + + ++it; + ASSERT_TRUE(it != it_end); + EXPECT_EQ(RRType::AAAA(), (*it)->getType()); + EXPECT_EQ(1, (*it)->getRdataCount()); + EXPECT_EQ("2001:db8::1", (*it)->getRdataIterator()->getCurrent().toText()); + + ++it; + ASSERT_TRUE(it != it_end); + EXPECT_EQ(RRType::A(), (*it)->getType()); + EXPECT_EQ(1, (*it)->getRdataCount()); + EXPECT_EQ("192.0.2.2", (*it)->getRdataIterator()->getCurrent().toText()); +} + +TEST_F(MessageTest, fromWirePreserveAnswer) { + // Using the same data as the previous test, but specify the PRESERVE_ORDER + // option. The received order of RRs should be preserved, and each RR + // should be stored in a single RRset. + factoryFromFile(message_parse, "message_fromWire19.wire", + Message::PRESERVE_ORDER); + { + SCOPED_TRACE("preserve answer RRs"); + preserveRRCheck(message_parse, Message::SECTION_ANSWER); + } +} + +TEST_F(MessageTest, fromWirePreserveAuthority) { + // Same for the previous test, but for the authority section. + factoryFromFile(message_parse, "message_fromWire20.wire", + Message::PRESERVE_ORDER); + { + SCOPED_TRACE("preserve authority RRs"); + preserveRRCheck(message_parse, Message::SECTION_AUTHORITY); + } +} + +TEST_F(MessageTest, fromWirePreserveAdditional) { + // Same for the previous test, but for the additional section. + factoryFromFile(message_parse, "message_fromWire21.wire", + Message::PRESERVE_ORDER); + { + SCOPED_TRACE("preserve additional RRs"); + preserveRRCheck(message_parse, Message::SECTION_ADDITIONAL); + } +} + +TEST_F(MessageTest, EDNS0ExtRcode) { + // Extended Rcode = BADVERS + factoryFromFile(message_parse, "message_fromWire10.wire"); + EXPECT_EQ(Rcode::BADVERS(), message_parse.getRcode()); + + // Maximum extended Rcode + message_parse.clear(Message::PARSE); + factoryFromFile(message_parse, "message_fromWire11.wire"); + EXPECT_EQ(0xfff, message_parse.getRcode().getCode()); +} + +TEST_F(MessageTest, BadEDNS0) { + // OPT RR in the answer section + EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire4"), + DNSMessageFORMERR); + // multiple OPT RRs (in the additional section) + message_parse.clear(Message::PARSE); + EXPECT_THROW(factoryFromFile(message_parse, "message_fromWire5"), + DNSMessageFORMERR); +} + +TEST_F(MessageTest, toWire) { + message_render.setQid(0x1035); + message_render.setOpcode(Opcode::QUERY()); + message_render.setRcode(Rcode::NOERROR()); + message_render.setHeaderFlag(Message::HEADERFLAG_QR, true); + message_render.setHeaderFlag(Message::HEADERFLAG_RD, true); + message_render.setHeaderFlag(Message::HEADERFLAG_AA, true); + message_render.addQuestion(Question(Name("test.example.com"), RRClass::IN(), + RRType::A())); + message_render.addRRset(Message::SECTION_ANSWER, rrset_a); + + EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION)); + EXPECT_EQ(2, message_render.getRRCount(Message::SECTION_ANSWER)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL)); + + message_render.toWire(renderer); + vector<unsigned char> data; + UnitTestUtil::readWireData("message_toWire1", data); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageTest, toWireSigned) { + message_render.setQid(0x75c1); + message_render.setOpcode(Opcode::QUERY()); + message_render.setRcode(Rcode::NOERROR()); + message_render.setHeaderFlag(Message::HEADERFLAG_QR, true); + message_render.setHeaderFlag(Message::HEADERFLAG_RD, true); + message_render.setHeaderFlag(Message::HEADERFLAG_AA, true); + message_render.addQuestion(Question(Name("test.example.com"), RRClass::IN(), + RRType::A())); + + rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::RRSIG(), RRTTL(3600))); + // one signature algorithm (5 = RSA/SHA-1) + rrset_rrsig->addRdata(generic::RRSIG("A 5 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + // another signature algorithm (3 = DSA/SHA-1) + rrset_rrsig->addRdata(generic::RRSIG("A 3 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + rrset_a->addRRsig(rrset_rrsig); + EXPECT_EQ(2, rrset_a->getRRsigDataCount()); + + message_render.addRRset(Message::SECTION_ANSWER, rrset_a); + + EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION)); + EXPECT_EQ(4, message_render.getRRCount(Message::SECTION_ANSWER)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL)); + + message_render.toWire(renderer); + vector<unsigned char> data; + UnitTestUtil::readWireData("message_toWire6", data); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageTest, toWireSignedAndTruncated) { + message_render.setQid(0x75c1); + message_render.setOpcode(Opcode::QUERY()); + message_render.setRcode(Rcode::NOERROR()); + message_render.setHeaderFlag(Message::HEADERFLAG_QR, true); + message_render.setHeaderFlag(Message::HEADERFLAG_RD, true); + message_render.setHeaderFlag(Message::HEADERFLAG_AA, true); + message_render.addQuestion(Question(Name("test.example.com"), RRClass::IN(), + RRType::TXT())); + + RRsetPtr rrset_txt = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::TXT(), RRTTL(3600))); + rrset_txt->addRdata(generic::TXT(string(255, 'a'))); + rrset_txt->addRdata(generic::TXT(string(255, 'b'))); + rrset_txt->addRdata(generic::TXT(string(255, 'c'))); + rrset_txt->addRdata(generic::TXT(string(255, 'd'))); + rrset_txt->addRdata(generic::TXT(string(255, 'e'))); + rrset_txt->addRdata(generic::TXT(string(255, 'f'))); + rrset_txt->addRdata(generic::TXT(string(255, 'g'))); + rrset_txt->addRdata(generic::TXT(string(255, 'h'))); + + rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::RRSIG(), RRTTL(3600))); + // one signature algorithm (5 = RSA/SHA-1) + rrset_rrsig->addRdata(generic::RRSIG("TXT 5 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + rrset_txt->addRRsig(rrset_rrsig); + EXPECT_EQ(1, rrset_txt->getRRsigDataCount()); + + message_render.addRRset(Message::SECTION_ANSWER, rrset_txt); + + EXPECT_EQ(1, message_render.getRRCount(Message::SECTION_QUESTION)); + EXPECT_EQ(9, message_render.getRRCount(Message::SECTION_ANSWER)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_AUTHORITY)); + EXPECT_EQ(0, message_render.getRRCount(Message::SECTION_ADDITIONAL)); + + message_render.toWire(renderer); + vector<unsigned char> data; + UnitTestUtil::readWireData("message_toWire7", data); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageTest, toWireInParseMode) { + // toWire() isn't allowed in the parse mode. + EXPECT_THROW(message_parse.toWire(renderer), InvalidMessageOperation); +} + +// See dnssectime_unittest.cc +template <int64_t NOW> +int64_t +testGetTime() { + return (NOW); +} + +// bit-wise constant flags to configure DNS header flags for test +// messages. +const unsigned int QR_FLAG = 0x1; +const unsigned int AA_FLAG = 0x2; +const unsigned int RD_FLAG = 0x4; + +void +commonTSIGToWireCheck(Message& message, MessageRenderer& renderer, + TSIGContext& tsig_ctx, const char* const expected_file, + unsigned int message_flags = RD_FLAG, + RRType qtype = RRType::A(), + const vector<const char*>* answer_data = NULL) { + message.setOpcode(Opcode::QUERY()); + message.setRcode(Rcode::NOERROR()); + if ((message_flags & QR_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_QR); + } + if ((message_flags & AA_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_AA); + } + if ((message_flags & RD_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_RD); + } + message.addQuestion(Question(Name("www.example.com"), RRClass::IN(), + qtype)); + + if (answer_data != NULL) { + RRsetPtr ans_rrset(new RRset(Name("www.example.com"), RRClass::IN(), + qtype, RRTTL(86400))); + for (auto const& it : *answer_data) { + ans_rrset->addRdata(createRdata(qtype, RRClass::IN(), it)); + } + message.addRRset(Message::SECTION_ANSWER, ans_rrset); + } + + message.toWire(renderer, &tsig_ctx); + vector<unsigned char> expected_data; + UnitTestUtil::readWireData(expected_file, expected_data); + matchWireData(&expected_data[0], expected_data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageTest, toWireWithTSIG) { + // Rendering a message with TSIG. Various special cases specific to + // TSIG are tested in the tsig tests. We only check the message contains + // a TSIG at the end and the ARCOUNT of the header is updated. + + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + message_render.setQid(0x2d65); + + { + SCOPED_TRACE("Message sign with TSIG"); + commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire2.wire"); + } +} + +TEST_F(MessageTest, toWireWithEDNSAndTSIG) { + // Similar to the previous test, but with an EDNS before TSIG. + // The wire data check will confirm the ordering. + isc::util::detail::gettimeFunction = testGetTime<0x4db60d1f>; + + message_render.setQid(0x6cd); + + EDNSPtr edns(new EDNS()); + edns->setUDPSize(4096); + message_render.setEDNS(edns); + + { + SCOPED_TRACE("Message sign with TSIG and EDNS"); + commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire3.wire"); + } +} + +// Some of the following tests involve truncation. We use the query name +// "www.example.com" and some TXT question/answers. The length of the +// header and question will be 33 bytes. If we also try to include a +// TSIG of the same key name (not compressed) with HMAC-MD5, the TSIG RR +// will be 85 bytes. + +// A long TXT RDATA. With a fully compressed owner name, the corresponding +// RR will be 268 bytes. +const char* const long_txt1 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde"; + +// With a fully compressed owner name, the corresponding RR will be 212 bytes. +// It should result in truncation even without TSIG (33 + 268 + 212 = 513) +const char* const long_txt2 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456"; + +// With a fully compressed owner name, the corresponding RR will be 127 bytes. +// So, it can fit in the standard 512 bytes with txt1 and without TSIG, but +// adding a TSIG would result in truncation (33 + 268 + 127 + 85 = 513) +const char* const long_txt3 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01"; + +// This is 1 byte shorter than txt3, which will result in a possible longest +// message containing answer RRs and TSIG. +const char* const long_txt4 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0"; + +// Example output generated by +// "dig -y www.example.com:SFuWd/q99SzF8Yzd1QbB9g== www.example.com txt +// QID: 0x22c2 +// Time Signed: 0x00004e179212 +TEST_F(MessageTest, toWireTSIGTruncation) { + isc::util::detail::gettimeFunction = testGetTime<0x4e179212>; + + // Verify a validly signed query so that we can use the TSIG context + + factoryFromFile(message_parse, "message_fromWire17.wire"); + EXPECT_EQ(TSIGError::NOERROR(), + tsig_ctx.verify(message_parse.getTSIGRecord(), + &received_data[0], received_data.size())); + + message_render.setQid(0x22c2); + vector<const char*> answer_data; + answer_data.push_back(long_txt1); + answer_data.push_back(long_txt2); + { + SCOPED_TRACE("Message sign with TSIG and TC bit on"); + commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire4.wire", + QR_FLAG|AA_FLAG|RD_FLAG, + RRType::TXT(), &answer_data); + } +} + +TEST_F(MessageTest, toWireTSIGTruncation2) { + // Similar to the previous test, but without TSIG it wouldn't cause + // truncation. + isc::util::detail::gettimeFunction = testGetTime<0x4e179212>; + factoryFromFile(message_parse, "message_fromWire17.wire"); + EXPECT_EQ(TSIGError::NOERROR(), + tsig_ctx.verify(message_parse.getTSIGRecord(), + &received_data[0], received_data.size())); + + message_render.setQid(0x22c2); + vector<const char*> answer_data; + answer_data.push_back(long_txt1); + answer_data.push_back(long_txt3); + { + SCOPED_TRACE("Message sign with TSIG and TC bit on (2)"); + commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire4.wire", + QR_FLAG|AA_FLAG|RD_FLAG, + RRType::TXT(), &answer_data); + } +} + +TEST_F(MessageTest, toWireTSIGTruncation3) { + // Similar to previous ones, but truncation occurs due to too many + // Questions (very unusual, but not necessarily illegal). + + // We are going to create a message starting with a standard + // header (12 bytes) and multiple questions in the Question + // section of the same owner name (changing the RRType, just so + // that it would be the form that would be accepted by the BIND 9 + // parser). The first Question is 21 bytes in length, and the subsequent + // ones are 6 bytes. We'll also use a TSIG whose size is 85 bytes. + // Up to 66 questions can fit in the standard 512-byte buffer + // (12 + 21 + 6 * 65 + 85 = 508). If we try to add one more it would + // result in truncation. + message_render.setOpcode(Opcode::QUERY()); + message_render.setRcode(Rcode::NOERROR()); + for (int i = 1; i <= 67; ++i) { + message_render.addQuestion(Question(Name("www.example.com"), + RRClass::IN(), RRType(i))); + } + message_render.toWire(renderer, &tsig_ctx); + + // Check the rendered data by parsing it. We only check it has the + // TC bit on, has the correct number of questions, and has a TSIG RR. + // Checking the signature wouldn't be necessary for this rare case + // scenario. + InputBuffer buffer(renderer.getData(), renderer.getLength()); + message_parse.fromWire(buffer); + EXPECT_TRUE(message_parse.getHeaderFlag(Message::HEADERFLAG_TC)); + // Note that the number of questions are 66, not 67 as we tried to add. + EXPECT_EQ(66, message_parse.getRRCount(Message::SECTION_QUESTION)); + EXPECT_TRUE(message_parse.getTSIGRecord() != NULL); +} + +TEST_F(MessageTest, toWireTSIGNoTruncation) { + // A boundary case that shouldn't cause truncation: the resulting + // response message with a TSIG will be 512 bytes long. + isc::util::detail::gettimeFunction = testGetTime<0x4e17b38d>; + factoryFromFile(message_parse, "message_fromWire18.wire"); + EXPECT_EQ(TSIGError::NOERROR(), + tsig_ctx.verify(message_parse.getTSIGRecord(), + &received_data[0], received_data.size())); + + message_render.setQid(0xd6e2); + vector<const char*> answer_data; + answer_data.push_back(long_txt1); + answer_data.push_back(long_txt4); + { + SCOPED_TRACE("Message sign with TSIG, no truncation"); + commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire5.wire", + QR_FLAG|AA_FLAG|RD_FLAG, + RRType::TXT(), &answer_data); + } +} + +// This is a buggy renderer for testing. It behaves like the straightforward +// MessageRenderer, but once it has some data, its setLengthLimit() ignores +// the given parameter and resets the limit to the current length, making +// subsequent insertion result in truncation, which would make TSIG RR +// rendering fail unexpectedly in the test that follows. +class BadRenderer : public MessageRenderer { +public: + virtual void setLengthLimit(size_t len) { + if (getLength() > 0) { + MessageRenderer::setLengthLimit(getLength()); + } else { + MessageRenderer::setLengthLimit(len); + } + } +}; + +TEST_F(MessageTest, toWireTSIGLengthErrors) { + // specify an unusual short limit that wouldn't be able to hold + // the TSIG. + renderer.setLengthLimit(tsig_ctx.getTSIGLength() - 1); + // Use commonTSIGToWireCheck() only to call toWire() with otherwise valid + // conditions. The checks inside it don't matter because we expect an + // exception before any of the checks. + EXPECT_THROW(commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire2.wire"), + InvalidParameter); + + // This one is large enough for TSIG, but the remaining limit isn't + // even enough for the Header section. + renderer.clear(); + message_render.clear(Message::RENDER); + renderer.setLengthLimit(tsig_ctx.getTSIGLength() + 1); + EXPECT_THROW(commonTSIGToWireCheck(message_render, renderer, tsig_ctx, + "message_toWire2.wire"), + InvalidParameter); + + // Trying to render a message with TSIG using a buggy renderer. + BadRenderer bad_renderer; + bad_renderer.setLengthLimit(512); + message_render.clear(Message::RENDER); + EXPECT_THROW(commonTSIGToWireCheck(message_render, bad_renderer, tsig_ctx, + "message_toWire2.wire"), + Unexpected); +} + +TEST_F(MessageTest, toWireWithoutOpcode) { + message_render.setRcode(Rcode::NOERROR()); + EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation); +} + +TEST_F(MessageTest, toWireWithoutRcode) { + message_render.setOpcode(Opcode::QUERY()); + EXPECT_THROW(message_render.toWire(renderer), InvalidMessageOperation); +} + +TEST_F(MessageTest, toText) { + // Check toText() output for a typical DNS response with records in + // all sections + + factoryFromFile(message_parse, "message_toText1.wire"); + { + SCOPED_TRACE("Message toText test (basic case)"); + ifstream ifs; + unittests::openTestData("message_toText1.txt", ifs); + unittests::matchTextData(ifs, message_parse.toText()); + } + + // Another example with EDNS. The expected data was slightly modified + // from the dig output (other than replacing tabs with a space): adding + // a newline after the "OPT PSEUDOSECTION". This is an intentional change + // in our version for better readability. + message_parse.clear(Message::PARSE); + factoryFromFile(message_parse, "message_toText2.wire"); + { + SCOPED_TRACE("Message toText test with EDNS"); + ifstream ifs; + unittests::openTestData("message_toText2.txt", ifs); + unittests::matchTextData(ifs, message_parse.toText()); + } + + // Another example with TSIG. The expected data was slightly modified + // from the dig output (other than replacing tabs with a space): removing + // a redundant white space at the end of TSIG RDATA. We'd rather consider + // it a dig's defect than a feature. + message_parse.clear(Message::PARSE); + factoryFromFile(message_parse, "message_toText3.wire"); + { + SCOPED_TRACE("Message toText test with TSIG"); + ifstream ifs; + unittests::openTestData("message_toText3.txt", ifs); + unittests::matchTextData(ifs, message_parse.toText()); + } +} + +TEST_F(MessageTest, toTextWithoutOpcode) { + message_render.setRcode(Rcode::NOERROR()); + EXPECT_THROW(message_render.toText(), InvalidMessageOperation); +} + +TEST_F(MessageTest, toTextWithoutRcode) { + message_render.setOpcode(Opcode::QUERY()); + EXPECT_THROW(message_render.toText(), InvalidMessageOperation); +} +} diff --git a/src/lib/dns/tests/messagerenderer_unittest.cc b/src/lib/dns/tests/messagerenderer_unittest.cc index 8b13789179..c3a53eb6ca 100644 --- a/src/lib/dns/tests/messagerenderer_unittest.cc +++ b/src/lib/dns/tests/messagerenderer_unittest.cc @@ -1 +1,292 @@ +// Copyright (C) 2009-2017 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <exceptions/exceptions.h> +#include <util/buffer.h> +#include <dns/name.h> +#include <dns/labelsequence.h> +#include <dns/messagerenderer.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +#include <gtest/gtest.h> + +#include <boost/lexical_cast.hpp> + +#include <string> +#include <vector> + +using isc::UnitTestUtil; +using isc::dns::Name; +using isc::dns::LabelSequence; +using isc::dns::MessageRenderer; +using isc::util::OutputBuffer; +using boost::lexical_cast; +using isc::util::unittests::matchWireData; + +namespace { +class MessageRendererTest : public ::testing::Test { +protected: + MessageRendererTest() : expected_size(0) { + data16 = (2 << 8) | 3; + data32 = (4 << 24) | (5 << 16) | (6 << 8) | 7; + } + size_t expected_size; + uint16_t data16; + uint32_t data32; + MessageRenderer renderer; + std::vector<unsigned char> data; + static const uint8_t testdata[5]; +}; + +const uint8_t MessageRendererTest::testdata[5] = {1, 2, 3, 4, 5}; + +// The test cases are borrowed from those for the OutputBuffer class. +TEST_F(MessageRendererTest, writeInteger) { + renderer.writeUint16(data16); + expected_size += sizeof(data16); + + matchWireData(&testdata[1], sizeof(data16), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, writeName) { + UnitTestUtil::readWireData("name_toWire1", data); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("b.example.com.")); + renderer.writeName(Name("a.example.org.")); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, writeNameInLargeBuffer) { + size_t offset = 0x3fff; + renderer.skip(offset); + + UnitTestUtil::readWireData("name_toWire2", data); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("b.example.com.")); + matchWireData(&data[0], data.size(), + static_cast<const uint8_t*>(renderer.getData()) + offset, + renderer.getLength() - offset); +} + +TEST_F(MessageRendererTest, writeNameWithUncompressed) { + UnitTestUtil::readWireData("name_toWire3", data); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("b.example.com."), false); + renderer.writeName(Name("b.example.com.")); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, writeNamePointerChain) { + UnitTestUtil::readWireData("name_toWire4", data); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("b.example.com.")); + renderer.writeName(Name("b.example.com.")); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, compressMode) { + // By default the render performs case insensitive compression. + EXPECT_EQ(MessageRenderer::CASE_INSENSITIVE, renderer.getCompressMode()); + + // The mode can be explicitly changed. + renderer.setCompressMode(MessageRenderer::CASE_SENSITIVE); + EXPECT_EQ(MessageRenderer::CASE_SENSITIVE, renderer.getCompressMode()); + renderer.setCompressMode(MessageRenderer::CASE_INSENSITIVE); + EXPECT_EQ(MessageRenderer::CASE_INSENSITIVE, renderer.getCompressMode()); + + // The clear() method resets the mode to the default. + renderer.setCompressMode(MessageRenderer::CASE_SENSITIVE); + renderer.clear(); + EXPECT_EQ(MessageRenderer::CASE_INSENSITIVE, renderer.getCompressMode()); +} + +TEST_F(MessageRendererTest, writeNameCaseCompress) { + // By default MessageRenderer performs case insensitive compression. + + UnitTestUtil::readWireData("name_toWire1", data); + renderer.writeName(Name("a.example.com.")); + // this should match the first name in terms of compression: + renderer.writeName(Name("b.exAmple.CoM.")); + renderer.writeName(Name("a.example.org.")); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, writeNameCaseSensitiveCompress) { + // name compression in case sensitive manner. See the data file + // description for details. + renderer.setCompressMode(MessageRenderer::CASE_SENSITIVE); + UnitTestUtil::readWireData("name_toWire5.wire", data); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("b.eXample.com.")); + renderer.writeName(Name("c.eXample.com.")); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, writeNameMixedCaseCompress) { + renderer.setCompressMode(MessageRenderer::CASE_SENSITIVE); + UnitTestUtil::readWireData("name_toWire6.wire", data); + renderer.writeName(Name("a.example.com.")); + renderer.writeName(Name("b.eXample.com.")); + + // Change the compression mode in the middle of rendering. This is not + // allowed in this implementation. + EXPECT_THROW(renderer.setCompressMode(MessageRenderer::CASE_INSENSITIVE), + isc::InvalidParameter); + + // Once the renderer is cleared, it's okay again. + renderer.clear(); + EXPECT_NO_THROW(renderer.setCompressMode( + MessageRenderer::CASE_INSENSITIVE)); +} + +TEST_F(MessageRendererTest, writeRootName) { + // root name is special: it never causes compression or can (reasonably) + // be a compression pointer. So it makes sense to check this case + // explicitly. + Name example_name = Name("www.example.com"); + + OutputBuffer expected(0); + expected.writeUint8(0); // root name + example_name.toWire(expected); + + renderer.writeName(Name(".")); + renderer.writeName(example_name); + matchWireData(static_cast<const uint8_t*>(expected.getData()), + expected.getLength(), + static_cast<const uint8_t*>(renderer.getData()), + renderer.getLength()); +} + +TEST_F(MessageRendererTest, writeNameLabelSequence1) { + UnitTestUtil::readWireData("name_toWire7", data); + + Name n1("a.example.com"); + LabelSequence ls1(n1); + + // a.example.com. + renderer.writeName(ls1); + + ls1.stripLeft(1); + + // example.com. + renderer.writeName(ls1); + + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, writeNameLabelSequence2) { + UnitTestUtil::readWireData("name_toWire8", data); + + Name n1("a.example.com"); + LabelSequence ls1(n1); + + ls1.stripRight(1); + + // a.example.com (without root .) + renderer.writeName(ls1); + + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, writeNameLabelSequence3) { + UnitTestUtil::readWireData("name_toWire9", data); + + Name n1("a.example.com"); + LabelSequence ls1(n1); + + // a.example.com. + renderer.writeName(ls1); + + ls1.stripRight(1); + + // a.example.com (without root .) + renderer.writeName(ls1); + + ls1.stripRight(1); + + // a.example + renderer.writeName(ls1); + + ls1.stripLeft(1); + + // example + renderer.writeName(ls1); + + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(MessageRendererTest, setBuffer) { + OutputBuffer new_buffer(0); + renderer.setBuffer(&new_buffer); + EXPECT_EQ(0, new_buffer.getLength()); // the buffer should be still empty + renderer.writeUint32(42); + EXPECT_EQ(sizeof(uint32_t), new_buffer.getLength()); + EXPECT_EQ(sizeof(uint32_t), renderer.getLength()); + + // Change some other internal state for the reset test below. + EXPECT_EQ(512, renderer.getLengthLimit()); + renderer.setLengthLimit(4096); + EXPECT_EQ(4096, renderer.getLengthLimit()); + + // Reset the buffer to the default again. Other internal states and + // resources should be cleared. The used buffer should be intact. + renderer.setBuffer(NULL); + EXPECT_EQ(sizeof(uint32_t), new_buffer.getLength()); + EXPECT_EQ(0, renderer.getLength()); + EXPECT_EQ(512, renderer.getLengthLimit()); +} + +TEST_F(MessageRendererTest, setBufferErrors) { + OutputBuffer new_buffer(0); + + // Buffer cannot be reset when the renderer is in use. + renderer.writeUint32(10); + EXPECT_THROW(renderer.setBuffer(&new_buffer), isc::InvalidParameter); + + renderer.clear(); + renderer.setBuffer(&new_buffer); + renderer.writeUint32(10); + EXPECT_THROW(renderer.setBuffer(&new_buffer), isc::InvalidParameter); + + // Resetting the buffer isn't allowed for the default buffer. + renderer.setBuffer(NULL); + EXPECT_THROW(renderer.setBuffer(NULL), isc::InvalidParameter); + + // It's okay to reset a temporary buffer without using it. + renderer.setBuffer(&new_buffer); + EXPECT_NO_THROW(renderer.setBuffer(NULL)); +} + +TEST_F(MessageRendererTest, manyRRs) { + // Render a large number of names, and the confirm the resulting wire + // data store the expected names in the correct order (1000 is an + // arbitrary choice). + for (size_t i = 0; i < 1000; ++i) { + renderer.writeName(Name(lexical_cast<std::string>(i) + ".example")); + } + isc::util::InputBuffer b(renderer.getData(), renderer.getLength()); + for (size_t i = 0; i < 1000; ++i) { + EXPECT_EQ(Name(lexical_cast<std::string>(i) + ".example"), Name(b)); + } + // This will trigger trimming excessive hash items. It shouldn't cause + // any disruption. + EXPECT_NO_THROW(renderer.clear()); +} +} diff --git a/src/lib/dns/tests/name_unittest.cc b/src/lib/dns/tests/name_unittest.cc index 8b13789179..636a403f38 100644 --- a/src/lib/dns/tests/name_unittest.cc +++ b/src/lib/dns/tests/name_unittest.cc @@ -1 +1,794 @@ +// Copyright (C) 2009-2024 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <vector> +#include <string> +#include <sstream> +#include <iomanip> +#include <limits> +#include <stdexcept> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/name.h> +#include <dns/messagerenderer.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +#include <gtest/gtest.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using isc::util::unittests::matchWireData; + +// +// XXX: these are defined as class static constants, but some compilers +// seemingly cannot find the symbols when used in the EXPECT_xxx macros. +// +const size_t Name::MAX_WIRE; +const size_t Name::MAX_LABELS; + +// This is a name of maximum allowed number of labels +const char* max_labels_str = "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 40 + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 80 + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 120 + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 160 + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 200 + "0.1.2.3.4.5.6.7.8.9.0.1.2.3.4.5.6.7.8.9." // 240 + "0.1.2.3.4.5.6"; +// This is a name of maximum allowed length +const char* max_len_str = "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123"; + +namespace { +class NameTest : public ::testing::Test { +protected: + NameTest() : example_name("www.example.com"), + example_name_upper("WWW.EXAMPLE.COM"), + small_name("aaa.example.com"), + large_name("zzz.example.com"), + origin_name("example.com."), + origin_name_upper("EXAMPLE.COM"), + buffer_actual(0), buffer_expected(0) + {} + + const Name example_name; + Name example_name_upper; // this will be modified and cannot be const + const Name small_name; + const Name large_name; + const Name origin_name; + const Name origin_name_upper; + OutputBuffer buffer_actual, buffer_expected; + + // + // helper methods + // + static Name nameFactoryFromWire(const char* datafile, size_t position, + bool downcase = false); + // construct a name including all non-upper-case-alphabet characters. + static Name nameFactoryLowerCase(); + void compareInWireFormat(const Name& name_actual, + const Name& name_expected); +}; + +const Name downcased_global("\\255.EXAMPLE.COM", true); + +Name +NameTest::nameFactoryFromWire(const char* datafile, size_t position, + bool downcase) +{ + vector<unsigned char> data; + UnitTestUtil::readWireData(datafile, data); + + InputBuffer buffer(&data[0], data.size()); + buffer.setPosition(position); + + return (Name(buffer, downcase)); +} + +Name +NameTest::nameFactoryLowerCase() { + string lowercase_namestr; + lowercase_namestr.reserve(Name::MAX_WIRE); + + unsigned int ch = 0; + unsigned int labelcount = 0; + do { + if (ch < 'A' || ch > 'Z') { + ostringstream ss; + ss.setf(ios_base::right, ios_base::adjustfield); + ss.width(3); + ss << setfill('0') << ch; + lowercase_namestr += '\\' + ss.str(); + + if (++labelcount == Name::MAX_LABELLEN) { + lowercase_namestr.push_back('.'); + labelcount = 0; + } + } + } while (++ch <= Name::MAX_WIRE); + + return (Name(lowercase_namestr)); +} + +void +NameTest::compareInWireFormat(const Name& name_actual, + const Name& name_expected) +{ + buffer_actual.clear(); + buffer_expected.clear(); + + name_actual.toWire(buffer_actual); + name_expected.toWire(buffer_expected); + + matchWireData(buffer_expected.getData(), buffer_expected.getLength(), + buffer_actual.getData(), buffer_actual.getLength()); +} + +TEST_F(NameTest, nonlocalObject) { + // A previous version of code relied on a non local static object for + // name construction, so a non local static Name object defined outside + // the name module might not be initialized correctly. This test detects + // that kind of bug. + EXPECT_EQ("\\255.example.com.", downcased_global.toText()); +} + +template <typename ExceptionType> +void +checkBadTextName(const string& txt) { + // Check it results in the specified type of exception as well as + // NameParserException. + EXPECT_THROW(Name(txt, false), ExceptionType); + EXPECT_THROW(Name(txt, false), NameParserException); + // The same is thrown when constructing by the master-file constructor + EXPECT_THROW(Name(txt.c_str(), txt.length(), &Name::ROOT_NAME()), + ExceptionType); + EXPECT_THROW(Name(txt.c_str(), txt.length(), &Name::ROOT_NAME()), + NameParserException); +} + +TEST_F(NameTest, checkExceptionsHierarchy) { + EXPECT_NO_THROW({ + const isc::dns::EmptyLabel exception("", 0, ""); + const isc::dns::NameParserException& exception_cast = + dynamic_cast<const isc::dns::NameParserException&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::TooLongName exception("", 0, ""); + const isc::dns::NameParserException& exception_cast = + dynamic_cast<const isc::dns::NameParserException&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::TooLongLabel exception("", 0, ""); + const isc::dns::NameParserException& exception_cast = + dynamic_cast<const isc::dns::NameParserException&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::BadLabelType exception("", 0, ""); + const isc::dns::NameParserException& exception_cast = + dynamic_cast<const isc::dns::NameParserException&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::BadEscape exception("", 0, ""); + const isc::dns::NameParserException& exception_cast = + dynamic_cast<const isc::dns::NameParserException&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::IncompleteName exception("", 0, ""); + const isc::dns::NameParserException& exception_cast = + dynamic_cast<const isc::dns::NameParserException&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); + + EXPECT_NO_THROW({ + const isc::dns::MissingNameOrigin exception("", 0, ""); + const isc::dns::NameParserException& exception_cast = + dynamic_cast<const isc::dns::NameParserException&>(exception); + // to avoid compiler warning + exception_cast.what(); + }); +} + +TEST_F(NameTest, fromText) { + vector<string> strnames; + strnames.push_back("www.example.com"); + strnames.push_back("www.example.com."); // with a trailing dot + strnames.push_back("wWw.exAmpLe.com"); // mixed cases + strnames.push_back("\\wWw.exAmpLe.com"); // escape with a backslash + // decimal representation for "WWW" + strnames.push_back("\\087\\087\\087.example.com"); + + for (auto const& it : strnames) { + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name, Name(it)); + } + + // root names + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, Name("@"), Name(".")); + + // downcase + EXPECT_EQ(Name("Www.eXample.coM", true).toText(), example_name.toText()); + + // + // Tests for bogus names. These should trigger exceptions. + // + // empty label cannot be followed by another label + checkBadTextName<EmptyLabel>(".a"); + // duplicate period + checkBadTextName<EmptyLabel>("a.."); + // label length must be < 64 + checkBadTextName<TooLongLabel>("012345678901234567890123456789" + "012345678901234567890123456789" + "0123"); + // now-unsupported bitstring labels + checkBadTextName<BadLabelType>("\\[b11010000011101]"); + // label length must be < 64 + checkBadTextName<TooLongLabel>("012345678901234567890123456789" + "012345678901234567890123456789" + "012\\x"); + // but okay as long as resulting len < 64 even if the original string is + // "too long" + EXPECT_NO_THROW(Name("012345678901234567890123456789" + "012345678901234567890123456789" + "01\\x")); + // incomplete \DDD pattern (exactly 3 D's must appear) + checkBadTextName<BadEscape>("\\12abc"); + // \DDD must not exceed 255 + checkBadTextName<BadEscape>("\\256"); + // Same tests for \111 as for \\x above + checkBadTextName<TooLongLabel>("012345678901234567890123456789" + "012345678901234567890123456789" + "012\\111"); + EXPECT_NO_THROW(Name("012345678901234567890123456789" + "012345678901234567890123456789" + "01\\111")); + // A domain name must be 255 octets or less + checkBadTextName<TooLongName>("123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789." + "123456789.1234"); + // This is a possible longest name and should be accepted + EXPECT_NO_THROW(Name(string(max_len_str))); + // \DDD must consist of 3 digits. + checkBadTextName<IncompleteName>("\\12"); + + // a name with the max number of labels. should be constructed without + // an error, and its length should be the max value. + Name maxlabels = Name(string(max_labels_str)); + EXPECT_EQ(Name::MAX_LABELS, maxlabels.getLabelCount()); +} + +// The following test uses a name data that was produced by +// fuzz testing and causes an unexpected condition in stringParser. +// Formerly this condition was trapped by an assert, but for +// robustness it has been replaced by a throw. +TEST_F(NameTest, unexpectedParseError) { + std::vector<uint8_t> badname { + 0xff,0xff,0x7f,0x00,0x00,0x00,0x7f,0x00,0x00,0x00,0x00, + 0x00,0x00,0x04,0x63,0x82,0x53,0x63,0x35,0x01,0x01,0x3d,0x07,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x19,0x0c,0x4e,0x01,0x00,0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x35,0x01,0x05,0x3a,0x04,0x00,0x00,0x07,0x08,0x3b,0x04,0x00, + 0x00,0x2e,0x3b,0x04,0x00,0x19,0x2e,0x00,0x00,0x00,0x0a,0x00,0x12,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0b,0x82,0x01,0xfc,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x35,0x01,0x05,0x3a,0x04,0x00,0x00,0x07,0x08,0x3b,0x04, + 0x00,0x00,0x2e,0x3b,0x04,0x00,0x19,0x2e,0x56,0x00,0x00,0x0a,0x00,0x12,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x0b,0x82,0x01,0xfc,0x42,0x00,0x00,0x00,0x00,0x19,0x0c, + 0x4e,0x01,0x05,0x3a,0x04,0xde,0x00,0x07,0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x35,0x01,0x05,0x3a,0x07,0x08,0x3b,0x04,0x00,0x00,0x2e,0x3b,0x04, + 0x00,0x19,0x2e,0x56,0x40,0x00,0x00,0x00,0x00,0x00,0x0a,0x00,0x12,0x00,0x00,0x00, + 0x00,0x00,0x19,0x00,0x0b,0x82,0x01,0xfc,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0xfc,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x35,0x01,0x05,0xff,0xff,0x05,0x00,0x07,0x08,0x3b,0x04, + 0x00,0x00,0x2e,0x3b + }; + + std::string badnamestr(badname.begin(), badname.end()); + EXPECT_THROW(Name(badnamestr, false), Unexpected); +} + +// on the rest while we prepare it. +// Check the @ syntax is accepted and it just copies the origin. +TEST_F(NameTest, copyOrigin) { + EXPECT_EQ(origin_name, Name("@", 1, &origin_name)); + // The downcase works on the origin too. But only when we provide it. + EXPECT_EQ(origin_name, Name("@", 1, &origin_name_upper, true)); + EXPECT_EQ(origin_name_upper, Name("@", 1, &origin_name_upper, true)); + // If we don't provide the origin, it throws + EXPECT_THROW(Name("@", 1, NULL), MissingNameOrigin); +} + +// Test the master-file constructor does not append the origin when the +// provided name is absolute +TEST_F(NameTest, dontAppendOrigin) { + EXPECT_EQ(example_name, Name("www.example.com.", 16, &origin_name)); + // The downcase works (only if provided, though) + EXPECT_EQ(example_name, Name("WWW.EXAMPLE.COM.", 16, &origin_name, true)); + EXPECT_EQ(example_name_upper, Name("WWW.EXAMPLE.COM.", 16, &origin_name)); + // And it does not require the origin to be provided + EXPECT_NO_THROW(Name("www.example.com.", 16, NULL)); +} + +// Test the master-file constructor properly appends the origin when +// the provided name is relative. +TEST_F(NameTest, appendOrigin) { + EXPECT_EQ(example_name, Name("www", 3, &origin_name)); + // Check the downcase works (if provided) + EXPECT_EQ(example_name, Name("WWW", 3, &origin_name, true)); + EXPECT_EQ(example_name, Name("WWW", 3, &origin_name_upper, true)); + EXPECT_EQ(example_name_upper, Name("WWW", 3, &origin_name_upper)); + // Check we can prepend more than one label + EXPECT_EQ(Name("a.b.c.d.example.com."), Name("a.b.c.d", 7, &origin_name)); + // When the name is relative, we throw. + EXPECT_THROW(Name("www", 3, NULL), MissingNameOrigin); +} + +// When we don't provide the data, it throws +TEST_F(NameTest, noDataProvided) { + EXPECT_THROW(Name(NULL, 10, NULL), isc::InvalidParameter); + EXPECT_THROW(Name(NULL, 10, &origin_name), isc::InvalidParameter); + EXPECT_THROW(Name("www", 0, NULL), isc::InvalidParameter); + EXPECT_THROW(Name("www", 0, &origin_name), isc::InvalidParameter); +} + +// When we combine the first part and the origin together, the resulting name +// is too long. It should throw. Other test checks this is valid when alone +// (without the origin appended). +TEST_F(NameTest, combinedTooLong) { + EXPECT_THROW(Name(max_len_str, strlen(max_len_str), &origin_name), + TooLongName); + EXPECT_THROW(Name(max_labels_str, strlen(max_labels_str), &origin_name), + TooLongName); + // Appending the root should be OK + EXPECT_NO_THROW(Name(max_len_str, strlen(max_len_str), + &Name::ROOT_NAME())); + EXPECT_NO_THROW(Name(max_labels_str, strlen(max_labels_str), + &Name::ROOT_NAME())); +} + +// Test the handling of @ in the name. If it is alone, it is the origin (when +// it exists) or the root. If it is somewhere else, it has no special meaning. +TEST_F(NameTest, atSign) { + // If it is alone, it is the origin + EXPECT_EQ(origin_name, Name("@", 1, &origin_name)); + EXPECT_THROW(Name("@", 1, NULL), MissingNameOrigin); + EXPECT_EQ(Name::ROOT_NAME(), Name("@")); + + // It is not alone. It is taken verbatim. We check the name converted + // back to the textual form, since checking it against other name object + // may be wrong -- if we create it wrong the same way as the tested + // object. + EXPECT_EQ("\\@.", Name("@.").toText()); + EXPECT_EQ("\\@.", Name("@.", 2, NULL).toText()); + EXPECT_EQ("\\@something.", Name("@something").toText()); + EXPECT_EQ("something\\@.", Name("something@").toText()); + EXPECT_EQ("\\@x.example.com.", Name("@x", 2, &origin_name).toText()); + EXPECT_EQ("x\\@.example.com.", Name("x@", 2, &origin_name).toText()); + + // An escaped at-sign isn't active + EXPECT_EQ("\\@.", Name("\\@").toText()); + EXPECT_EQ("\\@.example.com.", Name("\\@", 2, &origin_name).toText()); +} + +TEST_F(NameTest, fromWire) { + // + // test cases derived from BIND9 tests. + // + // normal case with a compression pointer + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, + nameFactoryFromWire("name_fromWire1", 25), + Name("vix.com")); + // bogus label character (looks like a local compression pointer) + EXPECT_THROW(nameFactoryFromWire("name_fromWire2", 25), DNSMessageFORMERR); + // a bad compression pointer (too big) + EXPECT_THROW(nameFactoryFromWire("name_fromWire3_1", 25), + DNSMessageFORMERR); + // forward reference + EXPECT_THROW(nameFactoryFromWire("name_fromWire3_2", 25), + DNSMessageFORMERR); + // invalid name length + EXPECT_THROW(nameFactoryFromWire("name_fromWire4", 550), DNSMessageFORMERR); + + // skip test for from Wire5. It's for disabling decompression, but our + // implementation always allows it. + + // bad pointer (too big) + EXPECT_THROW(nameFactoryFromWire("name_fromWire6", 25), DNSMessageFORMERR); + // input ends unexpectedly + EXPECT_THROW(nameFactoryFromWire("name_fromWire7", 25), DNSMessageFORMERR); + // many hops of compression but valid. should succeed. + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, + nameFactoryFromWire("name_fromWire8", 383), + Name("vix.com")); + + // + // Additional test cases + // + + // large names, a long but valid one, and invalid (too long) one. + EXPECT_EQ(Name::MAX_WIRE, + nameFactoryFromWire("name_fromWire9", 0).getLength()); + EXPECT_THROW(nameFactoryFromWire("name_fromWire10", 0).getLength(), + DNSMessageFORMERR); + + // A name with possible maximum number of labels; awkward but valid + EXPECT_EQ(nameFactoryFromWire("name_fromWire11", 0).getLabelCount(), + Name::MAX_LABELS); + + // Wire format including an invalid label length + EXPECT_THROW(nameFactoryFromWire("name_fromWire12", 0), DNSMessageFORMERR); + + // converting upper-case letters to down-case + EXPECT_EQ("vix.com.", + nameFactoryFromWire("name_fromWire1", 25, true).toText()); + EXPECT_EQ(3, nameFactoryFromWire("name_fromWire1", 25).getLabelCount()); +} + +TEST_F(NameTest, copyConstruct) { + Name copy(example_name); + EXPECT_EQ(copy, example_name); + + // Check the copied data is valid even after the original is deleted + Name* copy2 = new Name(example_name); + Name copy3(*copy2); + delete copy2; + EXPECT_EQ(copy3, example_name); +} + +TEST_F(NameTest, assignment) { + Name copy("."); + copy = example_name; + EXPECT_EQ(copy, example_name); + + // Check if the copied data is valid even after the original is deleted + Name* copy2 = new Name(example_name); + Name copy3("."); + copy3 = *copy2; + delete copy2; + EXPECT_EQ(copy3, example_name); + + // Self assignment + copy = *© + EXPECT_EQ(example_name, copy); +} + +TEST_F(NameTest, toText) { + // tests derived from BIND9 + EXPECT_EQ("a.b.c.d", Name("a.b.c.d").toText(true)); + EXPECT_EQ("a.\\\\[[.c.d", Name("a.\\\\[\\[.c.d").toText(true)); + EXPECT_EQ("a.b.C.d.", Name("a.b.C.d").toText(false)); + EXPECT_EQ("a.b.", Name("a.b.").toText(false)); + + // test omit_final_dot. It's false by default. + EXPECT_EQ("a.b.c.d", Name("a.b.c.d.").toText(true)); + EXPECT_EQ(Name("a.b.").toText(false), Name("a.b.").toText()); + + // the root name is a special case: omit_final_dot will be ignored. + EXPECT_EQ(".", Name(".").toText(true)); + + // test all printable characters to see whether special characters are + // escaped while the others are intact. note that the conversion is + // implementation specific; for example, it's not invalid to escape a + // "normal" character such as 'a' with regard to the standard. + string all_printable("!\\\"#\\$%&'\\(\\)*+,-\\./0123456789:\\;<=>?\\@" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "[\\\\]^_.`abcdefghijklmnopqrstuvwxyz{|}~."); + EXPECT_EQ(all_printable, + nameFactoryFromWire("name_fromWire13", 0).toText()); + + string all_nonprintable( + "\\000\\001\\002\\003\\004\\005\\006\\007\\008\\009" + "\\010\\011\\012\\013\\014\\015\\016\\017\\018\\019" + "\\020\\021\\022\\023\\024\\025\\026\\027\\028\\029" + "\\030\\031\\032\\127\\128\\129" + "\\130\\131\\132\\133\\134\\135\\136\\137\\138\\139" + "\\140\\141\\142\\143\\144\\145\\146\\147\\148\\149" + "\\150\\151\\152\\153\\154\\155\\156." + "\\157\\158\\159" + "\\160\\161\\162\\163\\164\\165\\166\\167\\168\\169" + "\\170\\171\\172\\173\\174\\175\\176\\177\\178\\179" + "\\180\\181\\182\\183\\184\\185\\186\\187\\188\\189" + "\\190\\191\\192\\193\\194\\195\\196\\197\\198\\199" + "\\200\\201\\202\\203\\204\\205\\206\\207\\208\\209" + "\\210\\211\\212\\213\\214\\215\\216\\217\\218\\219." + "\\220\\221\\222\\223\\224\\225\\226\\227\\228\\229" + "\\230\\231\\232\\233\\234\\235\\236\\237\\238\\239" + "\\240\\241\\242\\243\\244\\245\\246\\247\\248\\249" + "\\250\\251\\252\\253\\254\\255."); + EXPECT_EQ(all_nonprintable, + nameFactoryFromWire("name_fromWire14", 0).toText()); +} + +TEST_F(NameTest, toWireBuffer) { + vector<unsigned char> data; + OutputBuffer buffer(0); + + UnitTestUtil::readWireData(string("01610376697803636f6d00"), data); + Name("a.vix.com.").toWire(buffer); + matchWireData(&data[0], data.size(), + buffer.getData(), buffer.getLength()); +} + +// +// We test various corner cases in Renderer tests, but add this test case +// to fill the code coverage gap. +// +TEST_F(NameTest, toWireRenderer) { + vector<unsigned char> data; + MessageRenderer renderer; + + UnitTestUtil::readWireData(string("01610376697803636f6d00"), data); + Name("a.vix.com.").toWire(renderer); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +// +// Helper class to hold comparison test parameters. +// +struct CompareParameters { + CompareParameters(const Name& n1, const Name& n2, + NameComparisonResult::NameRelation r, int o, + unsigned int l) : + name1(n1), name2(n2), reln(r), order(o), labels(l) {} + static int normalizeOrder(int o) + { + if (o > 0) { + return (1); + } else if (o < 0) { + return (-1); + } + return (0); + } + Name name1; + Name name2; + NameComparisonResult::NameRelation reln; + int order; + unsigned int labels; +}; + +TEST_F(NameTest, compare) { + vector<CompareParameters> params; + params.push_back(CompareParameters(Name("c.d"), Name("a.b.c.d"), + NameComparisonResult::SUPERDOMAIN, + -1, 3)); + params.push_back(CompareParameters(Name("a.b.c.d"), Name("c.d"), + NameComparisonResult::SUBDOMAIN, 1, 3)); + params.push_back(CompareParameters(Name("a.b.c.d"), Name("c.d.e.f"), + NameComparisonResult::COMMONANCESTOR, + -1, 1)); + params.push_back(CompareParameters(Name("a.b.c.d"), Name("f.g.c.d"), + NameComparisonResult::COMMONANCESTOR, + -1, 3)); + params.push_back(CompareParameters(Name("a.b.c.d"), Name("A.b.C.d."), + NameComparisonResult::EQUAL, + 0, 5)); + + for (auto const& it : params) { + NameComparisonResult result = it.name1.compare(it.name2); + EXPECT_EQ(it.reln, result.getRelation()); + EXPECT_EQ(it.order, CompareParameters::normalizeOrder(result.getOrder())); + EXPECT_EQ(it.labels, result.getCommonLabels()); + } +} + +TEST_F(NameTest, equal) { + EXPECT_TRUE(example_name == Name("WWW.EXAMPLE.COM.")); + EXPECT_TRUE(example_name.equals(Name("WWW.EXAMPLE.COM."))); + EXPECT_TRUE(example_name != Name("www.example.org.")); + EXPECT_TRUE(example_name.nequals(Name("www.example.org."))); + // lengths don't match + EXPECT_TRUE(example_name != Name("www2.example.com.")); + EXPECT_TRUE(example_name.nequals(Name("www2.example.com."))); + // lengths are equal, but # of labels don't match (first test checks the + // prerequisite). + EXPECT_EQ(example_name.getLength(), Name("www\\.example.com.").getLength()); + EXPECT_TRUE(example_name != Name("www\\.example.com.")); + EXPECT_TRUE(example_name.nequals(Name("www\\.example.com."))); +} + +TEST_F(NameTest, isWildcard) { + EXPECT_FALSE(example_name.isWildcard()); + EXPECT_TRUE(Name("*.a.example.com").isWildcard()); + EXPECT_FALSE(Name("a.*.example.com").isWildcard()); +} + +TEST_F(NameTest, concatenate) { + NameComparisonResult result = + Name("aaa.www.example.com.").compare(Name("aaa").concatenate(example_name)); + EXPECT_EQ(NameComparisonResult::EQUAL, result.getRelation()); + + result = example_name.compare(Name(".").concatenate(example_name)); + EXPECT_EQ(NameComparisonResult::EQUAL, result.getRelation()); + + result = example_name.compare(example_name.concatenate(Name("."))); + EXPECT_EQ(NameComparisonResult::EQUAL, result.getRelation()); + + // concatenating two valid names would result in too long a name. + Name n1("123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789."); + Name n2("123456789.123456789.123456789.123456789.123456789." + "123456789.123456789.123456789.123456789.123456789." + "1234."); + EXPECT_THROW(n1.concatenate(n2), TooLongName); +} + +TEST_F(NameTest, reverse) { + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.reverse(), + Name("com.example.www.")); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, Name(".").reverse(), + Name(".")); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, + Name("a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s").reverse(), + Name("s.r.q.p.o.n.m.l.k.j.i.h.g.f.e.d.c.b.a")); +} + +TEST_F(NameTest, split) { + // normal cases with or without explicitly specifying the trailing dot. + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(1, 2), + Name("example.com.")); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(1, 3), + Name("example.com.")); + // edge cases: only the first or last label. + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(0, 1), + Name("www.")); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(3, 1), + Name(".")); + // invalid range: an exception should be thrown. + EXPECT_THROW(example_name.split(1, 0), OutOfRange); + EXPECT_THROW(example_name.split(2, 3), OutOfRange); + + // invalid range: the following parameters would cause overflow, + // bypassing naive validation. + EXPECT_THROW(example_name.split(1, numeric_limits<unsigned int>::max()), + OutOfRange); +} + +TEST_F(NameTest, split_for_suffix) { + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(1), + Name("example.com")); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(0), + example_name); + EXPECT_PRED_FORMAT2(UnitTestUtil::matchName, example_name.split(3), + Name(".")); + + // Invalid case: the level must be less than the original label count. + EXPECT_THROW(example_name.split(4), OutOfRange); +} + +TEST_F(NameTest, downcase) { + // usual case: all-upper case name to all-lower case + compareInWireFormat(example_name_upper.downcase(), example_name); + // confirm that non upper-case characters are intact + compareInWireFormat(nameFactoryLowerCase().downcase(), + nameFactoryLowerCase()); + // confirm the calling object is actually modified + example_name_upper.downcase(); + compareInWireFormat(example_name_upper, example_name); +} + +TEST_F(NameTest, at) { + // Confirm at() produces the exact sequence of wire-format name data + vector<uint8_t> data; + + for (size_t i = 0; i < example_name.getLength(); i++) { + data.push_back(example_name.at(i)); + } + + example_name.toWire(buffer_expected); + matchWireData(&data[0], data.size(), + buffer_expected.getData(), buffer_expected.getLength()); + + // Out-of-range access: should trigger an exception. + EXPECT_THROW(example_name.at(example_name.getLength()), OutOfRange); +} + +// +// The following set of tests confirm the result of <=, <, >=, > +// The test logic is simple, and all tests are just straightforward variations +// of the first one. +// +TEST_F(NameTest, leq) { + // small <= large is true + EXPECT_TRUE(small_name.leq(large_name)); + EXPECT_TRUE(small_name <= large_name); + + // small <= small is true + EXPECT_TRUE(small_name.leq(small_name)); + EXPECT_LE(small_name, small_name); + + // large <= small is false + EXPECT_FALSE(large_name.leq(small_name)); + EXPECT_FALSE(large_name <= small_name); +} + +TEST_F(NameTest, geq) { + EXPECT_TRUE(large_name.geq(small_name)); + EXPECT_TRUE(large_name >= small_name); + + EXPECT_TRUE(large_name.geq(large_name)); + EXPECT_GE(large_name, large_name); + + EXPECT_FALSE(small_name.geq(large_name)); + EXPECT_FALSE(small_name >= large_name); +} + +TEST_F(NameTest, lthan) { + EXPECT_TRUE(small_name.lthan(large_name)); + EXPECT_TRUE(small_name < large_name); + + EXPECT_FALSE(small_name.lthan(small_name)); + // cppcheck-suppress duplicateExpression + EXPECT_FALSE(small_name < small_name); + + EXPECT_FALSE(large_name.lthan(small_name)); + EXPECT_FALSE(large_name < small_name); +} + +TEST_F(NameTest, gthan) { + EXPECT_TRUE(large_name.gthan(small_name)); + EXPECT_TRUE(large_name > small_name); + + EXPECT_FALSE(large_name.gthan(large_name)); + // cppcheck-suppress duplicateExpression + EXPECT_FALSE(large_name > large_name); + + EXPECT_FALSE(small_name.gthan(large_name)); + EXPECT_FALSE(small_name > large_name); +} + +TEST_F(NameTest, constants) { + EXPECT_EQ(Name("."), Name::ROOT_NAME()); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(NameTest, LeftShiftOperator) { + ostringstream oss; + oss << example_name; + EXPECT_EQ(example_name.toText(), oss.str()); +} + +// The following verifies that toRawText() returns a string +// actual characters in place of escape sequences. We do not +// bother with an exhaustive set of tests here as this is +// not a primary use case. +TEST_F(NameTest, toRawText) { + Name n("a bc.$exa(m)ple.@org"); + EXPECT_EQ("a bc.$exa(m)ple.@org", n.toRawText(true)); + EXPECT_EQ("a bc.$exa(m)ple.@org.", n.toRawText(false)); + // Verify default value of omit parameter is false. + EXPECT_EQ("a bc.$exa(m)ple.@org.", n.toRawText()); +} + +} diff --git a/src/lib/dns/tests/nsec3hash_unittest.cc b/src/lib/dns/tests/nsec3hash_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/nsec3hash_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/opcode_unittest.cc b/src/lib/dns/tests/opcode_unittest.cc index 8b13789179..9bc60b5b7e 100644 --- a/src/lib/dns/tests/opcode_unittest.cc +++ b/src/lib/dns/tests/opcode_unittest.cc @@ -1 +1,100 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <vector> +#include <sstream> + +#include <exceptions/exceptions.h> + +#include <dns/opcode.h> + +#include <gtest/gtest.h> + +using namespace std; +using namespace isc::dns; + +namespace { +TEST(OpcodeTest, construct) { + // This test also tests getCode() + EXPECT_EQ(0, Opcode(0).getCode()); + EXPECT_EQ(15, Opcode(Opcode::RESERVED15_CODE).getCode()); + + EXPECT_THROW(Opcode(16), isc::OutOfRange); +} + +TEST(OpcodeTest, constants) { + // We'll only test arbitrarily chosen subsets of the codes. + // This class is quite simple, so it should be suffice. + + EXPECT_EQ(Opcode::QUERY_CODE, Opcode(0).getCode()); + EXPECT_EQ(Opcode::IQUERY_CODE, Opcode(1).getCode()); + EXPECT_EQ(Opcode::NOTIFY_CODE, Opcode(4).getCode()); + EXPECT_EQ(Opcode::UPDATE_CODE, Opcode(5).getCode()); + EXPECT_EQ(Opcode::RESERVED15_CODE, Opcode(15).getCode()); + + EXPECT_EQ(Opcode::QUERY_CODE, Opcode::QUERY().getCode()); + EXPECT_EQ(Opcode::IQUERY_CODE, Opcode::IQUERY().getCode()); + EXPECT_EQ(Opcode::NOTIFY_CODE, Opcode::NOTIFY().getCode()); + EXPECT_EQ(Opcode::UPDATE_CODE, Opcode::UPDATE().getCode()); + EXPECT_EQ(Opcode::RESERVED15_CODE, Opcode::RESERVED15().getCode()); +} + +TEST(OpcodeTest, equal) { + EXPECT_TRUE(Opcode::QUERY() == Opcode(Opcode::QUERY_CODE)); + EXPECT_TRUE(Opcode::QUERY().equals(Opcode(Opcode::QUERY_CODE))); + EXPECT_TRUE(Opcode::IQUERY() == Opcode(Opcode::IQUERY_CODE)); + EXPECT_TRUE(Opcode::IQUERY().equals(Opcode(Opcode::IQUERY_CODE))); + EXPECT_TRUE(Opcode::NOTIFY() == Opcode(Opcode::NOTIFY_CODE)); + EXPECT_TRUE(Opcode::NOTIFY().equals(Opcode(Opcode::NOTIFY_CODE))); + EXPECT_TRUE(Opcode::UPDATE() == Opcode(Opcode::UPDATE_CODE)); + EXPECT_TRUE(Opcode::UPDATE().equals(Opcode(Opcode::UPDATE_CODE))); + EXPECT_TRUE(Opcode::RESERVED15() == Opcode(Opcode::RESERVED15())); + EXPECT_TRUE(Opcode::RESERVED15().equals(Opcode(Opcode::RESERVED15()))); +} + +TEST(OpcodeTest, nequal) { + EXPECT_TRUE(Opcode::QUERY() != Opcode::IQUERY()); + EXPECT_TRUE(Opcode::QUERY().nequals(Opcode::IQUERY())); + EXPECT_TRUE(Opcode::NOTIFY() != Opcode(1)); + EXPECT_TRUE(Opcode::NOTIFY().nequals(Opcode(1))); + EXPECT_TRUE(Opcode(10) != Opcode(11)); + EXPECT_TRUE(Opcode(10).nequals(Opcode(11))); +} + +TEST(OpcodeTest, toText) { + vector<const char*> expects; + expects.resize(Opcode::RESERVED15_CODE + 1); + expects[Opcode::QUERY_CODE] = "QUERY"; + expects[Opcode::IQUERY_CODE] = "IQUERY"; + expects[Opcode::STATUS_CODE] = "STATUS"; + expects[Opcode::RESERVED3_CODE] = "RESERVED3"; + expects[Opcode::NOTIFY_CODE] = "NOTIFY"; + expects[Opcode::UPDATE_CODE] = "UPDATE"; + expects[Opcode::RESERVED6_CODE] = "RESERVED6"; + expects[Opcode::RESERVED7_CODE] = "RESERVED7"; + expects[Opcode::RESERVED8_CODE] = "RESERVED8"; + expects[Opcode::RESERVED9_CODE] = "RESERVED9"; + expects[Opcode::RESERVED10_CODE] = "RESERVED10"; + expects[Opcode::RESERVED11_CODE] = "RESERVED11"; + expects[Opcode::RESERVED12_CODE] = "RESERVED12"; + expects[Opcode::RESERVED13_CODE] = "RESERVED13"; + expects[Opcode::RESERVED14_CODE] = "RESERVED14"; + expects[Opcode::RESERVED15_CODE] = "RESERVED15"; + + for (unsigned int i = 0; i <= Opcode::RESERVED15_CODE; ++i) { + EXPECT_EQ(expects.at(i), Opcode(i).toText()); + } +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST(OpcodeTest, LeftShiftOperator) { + ostringstream oss; + oss << Opcode::NOTIFY(); + EXPECT_EQ(Opcode::NOTIFY().toText(), oss.str()); +} +} diff --git a/src/lib/dns/tests/qid_gen_unittest.cc b/src/lib/dns/tests/qid_gen_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/qid_gen_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/question_unittest.cc b/src/lib/dns/tests/question_unittest.cc index 8b13789179..d096c6b18d 100644 --- a/src/lib/dns/tests/question_unittest.cc +++ b/src/lib/dns/tests/question_unittest.cc @@ -1 +1,198 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <vector> +#include <sstream> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/question.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class QuestionTest : public ::testing::Test { +protected: + QuestionTest() : obuffer(0), + example_name1(Name("foo.example.com")), + example_name2(Name("bar.example.com")), + test_question1(example_name1, RRClass::IN(), + RRType::NS()), + test_question2(example_name2, RRClass::CH(), + RRType::A()) { + } + OutputBuffer obuffer; + MessageRenderer renderer; + Name example_name1; + Name example_name2; + Question test_question1; + Question test_question2; + vector<unsigned char> wiredata; +}; + +Question +questionFromWire(const char* datafile, size_t position = 0) { + vector<unsigned char> data; + UnitTestUtil::readWireData(datafile, data); + + InputBuffer buffer(&data[0], data.size()); + buffer.setPosition(position); + + return (Question(buffer)); +} + +TEST_F(QuestionTest, fromWire) { + Question q = questionFromWire("question_fromWire"); + + EXPECT_EQ(example_name1, q.getName()); + EXPECT_EQ(RRClass::IN(), q.getClass()); + EXPECT_EQ(RRType::NS(), q.getType()); + + // owner name of the second Question is compressed. It's uncommon + // (to have multiple questions), but isn't prohibited by the protocol. + q = questionFromWire("question_fromWire", 21); + EXPECT_EQ(example_name2, q.getName()); + EXPECT_EQ(RRClass::CH(), q.getClass()); + EXPECT_EQ(RRType::A(), q.getType()); + + // Pathological cases: Corresponding exceptions will be thrown from + // the underlying parser. + EXPECT_THROW(questionFromWire("question_fromWire", 31), DNSMessageFORMERR); + EXPECT_THROW(questionFromWire("question_fromWire", 36), IncompleteRRClass); +} + +TEST_F(QuestionTest, toText) { + EXPECT_EQ("foo.example.com. IN NS", test_question1.toText()); + EXPECT_EQ("bar.example.com. CH A", test_question2.toText()); + + EXPECT_EQ("foo.example.com. IN NS", test_question1.toText(false)); + EXPECT_EQ("bar.example.com. CH A", test_question2.toText(false)); + + EXPECT_EQ("foo.example.com. IN NS\n", test_question1.toText(true)); + EXPECT_EQ("bar.example.com. CH A\n", test_question2.toText(true)); +} + +TEST_F(QuestionTest, toWireBuffer) { + test_question1.toWire(obuffer); + test_question2.toWire(obuffer); + UnitTestUtil::readWireData("question_toWire1", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(QuestionTest, toWireRenderer) { + test_question1.toWire(renderer); + test_question2.toWire(renderer); + UnitTestUtil::readWireData("question_toWire2", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(QuestionTest, toWireTruncated) { + // If the available length in the renderer is too small, it would require + // truncation. This won't happen in normal cases, but protocol wise it + // could still happen if and when we support some (possibly future) opcode + // that allows multiple questions. + + // Set the length limit to the qname length so that the whole question + // would request truncated + renderer.setLengthLimit(example_name1.getLength()); + + EXPECT_FALSE(renderer.isTruncated()); // check pre-render condition + EXPECT_EQ(0, test_question1.toWire(renderer)); + EXPECT_TRUE(renderer.isTruncated()); + EXPECT_EQ(0, renderer.getLength()); // renderer shouldn't have any data +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(QuestionTest, LeftShiftOperator) { + ostringstream oss; + oss << test_question1; + EXPECT_EQ(test_question1.toText(), oss.str()); +} + +TEST_F(QuestionTest, comparison) { + const Name a("a"); + const Name b("b"); + const RRClass in(RRClass::IN()); + const RRClass ch(RRClass::CH()); + const RRType ns(RRType::NS()); + const RRType aaaa(RRType::AAAA()); + + EXPECT_TRUE(Question(a, in, ns) < Question(a, in, aaaa)); + EXPECT_FALSE(Question(a, in, aaaa) < Question(a, in, ns)); + + EXPECT_TRUE(Question(a, in, ns) < Question(a, ch, ns)); + EXPECT_FALSE(Question(a, ch, ns) < Question(a, in, ns)); + + EXPECT_TRUE(Question(a, in, ns) < Question(a, ch, aaaa)); + EXPECT_FALSE(Question(a, ch, aaaa) < Question(a, in, ns)); + + EXPECT_TRUE(Question(a, in, ns) < Question(b, in, ns)); + EXPECT_FALSE(Question(a, in, ns) < Question(a, in, ns)); + + EXPECT_TRUE(Question(a, in, ns) < Question(b, ch, ns)); + EXPECT_FALSE(Question(b, ch, ns) < Question(a, in, ns)); + + EXPECT_TRUE(Question(a, in, ns) < Question(b, ch, aaaa)); + EXPECT_FALSE(Question(b, ch, aaaa) < Question(a, in, ns)); + + EXPECT_FALSE(Question(a, in, ns) < Question(a, in, ns)); + EXPECT_FALSE(Question(a, ch, ns) < Question(a, ch, ns)); + EXPECT_FALSE(Question(b, in, ns) < Question(b, in, ns)); + EXPECT_FALSE(Question(b, in, aaaa) < Question(b, in, aaaa)); + + // Identical questions are equal + + EXPECT_TRUE(Question(a, in, ns) == Question(a, in, ns)); + EXPECT_FALSE(Question(a, in, ns) != Question(a, in, ns)); + + // Components differing by one component are unequal... + + EXPECT_FALSE(Question(b, in, ns) == Question(a, in, ns)); + EXPECT_TRUE(Question(b, in, ns) != Question(a, in, ns)); + + EXPECT_FALSE(Question(a, ch, ns) == Question(a, in, ns)); + EXPECT_TRUE(Question(a, ch, ns) != Question(a, in, ns)); + + EXPECT_FALSE(Question(a, in, aaaa) == Question(a, in, ns)); + EXPECT_TRUE(Question(a, in, aaaa) != Question(a, in, ns)); + + // ... as are those differing by two components + + EXPECT_FALSE(Question(b, ch, ns) == Question(a, in, ns)); + EXPECT_TRUE(Question(b, ch, ns) != Question(a, in, ns)); + + EXPECT_FALSE(Question(b, in, aaaa) == Question(a, in, ns)); + EXPECT_TRUE(Question(b, in, aaaa) != Question(a, in, ns)); + + EXPECT_FALSE(Question(a, ch, aaaa) == Question(a, in, ns)); + EXPECT_TRUE(Question(a, ch, aaaa) != Question(a, in, ns)); + + // ... and question differing by all three + + EXPECT_FALSE(Question(b, ch, aaaa) == Question(a, in, ns)); + EXPECT_TRUE(Question(b, ch, aaaa) != Question(a, in, ns)); +} + +} diff --git a/src/lib/dns/tests/rcode_unittest.cc b/src/lib/dns/tests/rcode_unittest.cc index 8b13789179..220248f062 100644 --- a/src/lib/dns/tests/rcode_unittest.cc +++ b/src/lib/dns/tests/rcode_unittest.cc @@ -1 +1,126 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <vector> +#include <sstream> + +#include <exceptions/exceptions.h> + +#include <dns/rcode.h> + +#include <gtest/gtest.h> + +using namespace std; +using namespace isc::dns; + +namespace { +TEST(RcodeTest, constructFromCode) { + // Normal cases. This test also tests getCode() + EXPECT_EQ(0, Rcode(0).getCode()); + EXPECT_EQ(0xfff, Rcode(0xfff).getCode()); // possible max code + + // should fail on attempt of construction with an out of range code + EXPECT_THROW(Rcode(0x1000), isc::OutOfRange); + EXPECT_THROW(Rcode(0xffff), isc::OutOfRange); +} + +TEST(RcodeTest, constructFromCodePair) { + EXPECT_EQ(3, Rcode(Rcode::NXDOMAIN_CODE, 0).getCode()); + EXPECT_EQ(Rcode::BADVERS_CODE, Rcode(0, 1).getCode()); + EXPECT_EQ(0xfff, Rcode(0xf, 0xff).getCode()); + EXPECT_THROW(Rcode(0x10, 0xff), isc::OutOfRange); +} + +TEST(RcodeTest, getExtendedCode) { + EXPECT_EQ(0, Rcode::NOERROR().getExtendedCode()); + EXPECT_EQ(0, Rcode::YXRRSET().getExtendedCode()); + EXPECT_EQ(1, Rcode::BADVERS().getExtendedCode()); + EXPECT_EQ(0xab, Rcode(0xabf).getExtendedCode()); + EXPECT_EQ(0xff, Rcode(0xfff).getExtendedCode()); +} + +TEST(RcodeTest, constants) { + // We'll only test arbitrarily chosen subsets of the codes. + // This class is quite simple, so it should be suffice. + + EXPECT_EQ(Rcode::NOERROR_CODE, Rcode(0).getCode()); + EXPECT_EQ(Rcode::FORMERR_CODE, Rcode(1).getCode()); + EXPECT_EQ(Rcode::NOTIMP_CODE, Rcode(4).getCode()); + EXPECT_EQ(Rcode::REFUSED_CODE, Rcode(5).getCode()); + EXPECT_EQ(Rcode::RESERVED15_CODE, Rcode(15).getCode()); + EXPECT_EQ(Rcode::BADVERS_CODE, Rcode(16).getCode()); + + EXPECT_EQ(Rcode::NOERROR_CODE, Rcode::NOERROR().getCode()); + EXPECT_EQ(Rcode::FORMERR_CODE, Rcode::FORMERR().getCode()); + EXPECT_EQ(Rcode::NOTIMP_CODE, Rcode::NOTIMP().getCode()); + EXPECT_EQ(Rcode::REFUSED_CODE, Rcode::REFUSED().getCode()); + EXPECT_EQ(Rcode::RESERVED15_CODE, Rcode::RESERVED15().getCode()); + EXPECT_EQ(Rcode::BADVERS_CODE, Rcode::BADVERS().getCode()); +} + +TEST(RcodeTest, equal) { + EXPECT_TRUE(Rcode::NOERROR() == Rcode(Rcode::NOERROR_CODE)); + EXPECT_TRUE(Rcode::NOERROR().equals(Rcode(Rcode::NOERROR_CODE))); + EXPECT_TRUE(Rcode::FORMERR() == Rcode(Rcode::FORMERR_CODE)); + EXPECT_TRUE(Rcode::FORMERR().equals(Rcode(Rcode::FORMERR_CODE))); + EXPECT_TRUE(Rcode::NOTIMP() == Rcode(Rcode::NOTIMP_CODE)); + EXPECT_TRUE(Rcode::NOTIMP().equals(Rcode(Rcode::NOTIMP_CODE))); + EXPECT_TRUE(Rcode::REFUSED() == Rcode(Rcode::REFUSED_CODE)); + EXPECT_TRUE(Rcode::REFUSED().equals(Rcode(Rcode::REFUSED_CODE))); + EXPECT_TRUE(Rcode::RESERVED15() == Rcode(Rcode::RESERVED15())); + EXPECT_TRUE(Rcode::RESERVED15().equals(Rcode(Rcode::RESERVED15()))); + EXPECT_TRUE(Rcode::BADVERS() == Rcode(Rcode::BADVERS_CODE)); + EXPECT_TRUE(Rcode::BADVERS().equals(Rcode(Rcode::BADVERS_CODE))); +} + +TEST(RcodeTest, nequal) { + EXPECT_TRUE(Rcode::NOERROR() != Rcode::FORMERR()); + EXPECT_TRUE(Rcode::NOERROR().nequals(Rcode::FORMERR())); + EXPECT_TRUE(Rcode::NOTIMP() != Rcode(1)); + EXPECT_TRUE(Rcode::NOTIMP().nequals(Rcode(1))); + EXPECT_TRUE(Rcode(10) != Rcode(11)); + EXPECT_TRUE(Rcode(10).nequals(Rcode(11))); +} + +TEST(RcodeTest, toText) { + vector<const char*> expects; + expects.resize(Rcode::BADVERS_CODE + 1); + expects[Rcode::NOERROR_CODE] = "NOERROR"; + expects[Rcode::FORMERR_CODE] = "FORMERR"; + expects[Rcode::SERVFAIL_CODE] = "SERVFAIL"; + expects[Rcode::NXDOMAIN_CODE] = "NXDOMAIN"; + expects[Rcode::NOTIMP_CODE] = "NOTIMP"; + expects[Rcode::REFUSED_CODE] = "REFUSED"; + expects[Rcode::YXDOMAIN_CODE] = "YXDOMAIN"; + expects[Rcode::YXRRSET_CODE] = "YXRRSET"; + expects[Rcode::NXRRSET_CODE] = "NXRRSET"; + expects[Rcode::NOTAUTH_CODE] = "NOTAUTH"; + expects[Rcode::NOTZONE_CODE] = "NOTZONE"; + expects[Rcode::RESERVED11_CODE] = "RESERVED11"; + expects[Rcode::RESERVED12_CODE] = "RESERVED12"; + expects[Rcode::RESERVED13_CODE] = "RESERVED13"; + expects[Rcode::RESERVED14_CODE] = "RESERVED14"; + expects[Rcode::RESERVED15_CODE] = "RESERVED15"; + expects[Rcode::BADVERS_CODE] = "BADVERS"; + + for (unsigned int i = 0; i <= Rcode::BADVERS_CODE; ++i) { + EXPECT_EQ(expects.at(i), Rcode(i).toText()); + } + + // Non well-known Rcodes + EXPECT_EQ("17", Rcode(Rcode::BADVERS().getCode() + 1).toText()); + EXPECT_EQ("4095", Rcode(Rcode(0xfff)).toText()); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST(RcodeTest, LeftShiftOperator) { + ostringstream oss; + oss << Rcode::SERVFAIL(); + EXPECT_EQ(Rcode::SERVFAIL().toText(), oss.str()); +} +} diff --git a/src/lib/dns/tests/rdata_afsdb_unittest.cc b/src/lib/dns/tests/rdata_afsdb_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_afsdb_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_caa_unittest.cc b/src/lib/dns/tests/rdata_caa_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_caa_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_char_string_data_unittest.cc b/src/lib/dns/tests/rdata_char_string_data_unittest.cc index 8b13789179..d4da8420a2 100644 --- a/src/lib/dns/tests/rdata_char_string_data_unittest.cc +++ b/src/lib/dns/tests/rdata_char_string_data_unittest.cc @@ -1 +1,181 @@ +// Copyright (C) 2014-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <util/unittests/wiredata.h> + +#include <dns/exceptions.h> +#include <dns/rdata.h> +#include <dns/char_string.h> +#include <util/buffer.h> + +#include <gtest/gtest.h> + +#include <string> +#include <vector> + +using namespace isc::dns; +using namespace isc::dns::rdata; +using isc::dns::rdata::generic::detail::CharStringData; +using isc::dns::rdata::generic::detail::stringToCharStringData; +using isc::dns::rdata::generic::detail::charStringDataToString; +using isc::dns::rdata::generic::detail::compareCharStringDatas; +using isc::util::unittests::matchWireData; + +namespace { +const uint8_t test_charstr[] = { + 'T', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g' +}; + +class CharStringDataTest : public ::testing::Test { +protected: + CharStringDataTest() : + // char-string representation for test data using two types of escape + // ('r' = 114) + test_str("Test\\ St\\114ing") + { + str_region.beg = &test_str[0]; + str_region.len = test_str.size(); + } + CharStringData chstr; // place holder + const std::string test_str; + MasterToken::StringRegion str_region; +}; + +MasterToken::StringRegion +createStringRegion(const std::string& str) { + MasterToken::StringRegion region; + region.beg = &str[0]; // note std ensures this works even if str is empty + region.len = str.size(); + return (region); +} + +TEST_F(CharStringDataTest, normalConversion) { + uint8_t tmp[3]; // placeholder for expected sequence + + stringToCharStringData(str_region, chstr); + matchWireData(test_charstr, sizeof(test_charstr), &chstr[0], chstr.size()); + + // Empty string + chstr.clear(); + stringToCharStringData(createStringRegion(""), chstr); + EXPECT_TRUE(chstr.empty()); + + // Possible largest char string + chstr.clear(); + std::string long_str(255, 'x'); + stringToCharStringData(createStringRegion(long_str), chstr); + std::vector<uint8_t> expected; + expected.insert(expected.end(), long_str.begin(), long_str.end()); + matchWireData(&expected[0], expected.size(), &chstr[0], chstr.size()); + + // Escaped '\' + chstr.clear(); + tmp[0] = '\\'; + stringToCharStringData(createStringRegion("\\\\"), chstr); + matchWireData(tmp, 1, &chstr[0], chstr.size()); + + // Boundary values for \DDD + chstr.clear(); + tmp[0] = 0; + stringToCharStringData(createStringRegion("\\000"), chstr); + matchWireData(tmp, 1, &chstr[0], chstr.size()); + + chstr.clear(); + stringToCharStringData(createStringRegion("\\255"), chstr); + tmp[0] = 255; + matchWireData(tmp, 1, &chstr[0], chstr.size()); + + // Another digit follows DDD; it shouldn't cause confusion + chstr.clear(); + stringToCharStringData(createStringRegion("\\2550"), chstr); + tmp[1] = '0'; + matchWireData(tmp, 2, &chstr[0], chstr.size()); +} + +TEST_F(CharStringDataTest, badConversion) { + // input string ending with (non escaped) '\' + chstr.clear(); + EXPECT_THROW(stringToCharStringData(createStringRegion("foo\\"), chstr), + InvalidRdataText); +} + +TEST_F(CharStringDataTest, badDDD) { + // Check various type of bad form of \DDD + + // Not a number + EXPECT_THROW(stringToCharStringData(createStringRegion("\\1a2"), chstr), + InvalidRdataText); + EXPECT_THROW(stringToCharStringData(createStringRegion("\\12a"), chstr), + InvalidRdataText); + + // Not in the range of uint8_t + EXPECT_THROW(stringToCharStringData(createStringRegion("\\256"), chstr), + InvalidRdataText); + + // Short buffer + EXPECT_THROW(stringToCharStringData(createStringRegion("\\42"), chstr), + InvalidRdataText); +} + +const struct TestData { + const char *data; + const char *expected; +} conversion_data[] = { + {"Test\"Test", "Test\\\"Test"}, + {"Test;Test", "Test\\;Test"}, + {"Test\\Test", "Test\\\\Test"}, + {"Test\x1fTest", "Test\\031Test"}, + {"Test ~ Test", "Test ~ Test"}, + {"Test\x7fTest", "Test\\127Test"}, + {NULL, NULL} +}; + +TEST_F(CharStringDataTest, charStringDataToString) { + for (const TestData* cur = conversion_data; cur->data != NULL; ++cur) { + uint8_t idata[32]; + size_t length = std::strlen(cur->data); + ASSERT_LT(length, sizeof(idata)); + std::memcpy(idata, cur->data, length); + const CharStringData test_data(idata, idata + length); + EXPECT_EQ(cur->expected, charStringDataToString(test_data)); + } +} + +TEST_F(CharStringDataTest, compareCharStringData) { + CharStringData charstr; + CharStringData charstr2; + CharStringData charstr_small1; + CharStringData charstr_small2; + CharStringData charstr_large1; + CharStringData charstr_large2; + CharStringData charstr_empty; + + stringToCharStringData(createStringRegion("test string"), charstr); + stringToCharStringData(createStringRegion("test string"), charstr2); + stringToCharStringData(createStringRegion("test strin"), charstr_small1); + stringToCharStringData(createStringRegion("test strina"), charstr_small2); + stringToCharStringData(createStringRegion("test stringa"), charstr_large1); + stringToCharStringData(createStringRegion("test strinz"), charstr_large2); + + EXPECT_EQ(0, compareCharStringDatas(charstr, charstr2)); + EXPECT_EQ(0, compareCharStringDatas(charstr2, charstr)); + EXPECT_EQ(1, compareCharStringDatas(charstr, charstr_small1)); + EXPECT_EQ(1, compareCharStringDatas(charstr, charstr_small2)); + EXPECT_EQ(-1, compareCharStringDatas(charstr, charstr_large1)); + EXPECT_EQ(-1, compareCharStringDatas(charstr, charstr_large2)); + EXPECT_EQ(-1, compareCharStringDatas(charstr_small1, charstr)); + EXPECT_EQ(-1, compareCharStringDatas(charstr_small2, charstr)); + EXPECT_EQ(1, compareCharStringDatas(charstr_large1, charstr)); + EXPECT_EQ(1, compareCharStringDatas(charstr_large2, charstr)); + + EXPECT_EQ(-1, compareCharStringDatas(charstr_empty, charstr)); + EXPECT_EQ(1, compareCharStringDatas(charstr, charstr_empty)); + EXPECT_EQ(0, compareCharStringDatas(charstr_empty, charstr_empty)); +} + +} // unnamed namespace diff --git a/src/lib/dns/tests/rdata_char_string_unittest.cc b/src/lib/dns/tests/rdata_char_string_unittest.cc index 8b13789179..607e6afe6b 100644 --- a/src/lib/dns/tests/rdata_char_string_unittest.cc +++ b/src/lib/dns/tests/rdata_char_string_unittest.cc @@ -1 +1,246 @@ +// Copyright (C) 2012-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <util/unittests/wiredata.h> + +#include <dns/exceptions.h> +#include <dns/rdata.h> +#include <dns/char_string.h> +#include <util/buffer.h> + +#include <gtest/gtest.h> + +#include <string> +#include <vector> + +using namespace isc::dns; +using namespace isc::dns::rdata; +using isc::dns::rdata::generic::detail::CharString; +using isc::dns::rdata::generic::detail::bufferToCharString; +using isc::dns::rdata::generic::detail::stringToCharString; +using isc::dns::rdata::generic::detail::charStringToString; +using isc::dns::rdata::generic::detail::compareCharStrings; +using isc::util::unittests::matchWireData; + +namespace { +const uint8_t test_charstr[] = { + sizeof("Test String") - 1, + 'T', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g' +}; + +class CharStringTest : public ::testing::Test { +protected: + CharStringTest() : + // char-string representation for test data using two types of escape + // ('r' = 114) + test_str("Test\\ St\\114ing") + { + str_region.beg = &test_str[0]; + str_region.len = test_str.size(); + } + CharString chstr; // place holder + const std::string test_str; + MasterToken::StringRegion str_region; +}; + +MasterToken::StringRegion +createStringRegion(const std::string& str) { + MasterToken::StringRegion region; + region.beg = &str[0]; // note std ensures this works even if str is empty + region.len = str.size(); + return (region); +} + +TEST_F(CharStringTest, normalConversion) { + uint8_t tmp[3]; // placeholder for expected sequence + + stringToCharString(str_region, chstr); + matchWireData(test_charstr, sizeof(test_charstr), &chstr[0], chstr.size()); + + // Empty string + chstr.clear(); + stringToCharString(createStringRegion(""), chstr); + tmp[0] = 0; + matchWireData(tmp, 1, &chstr[0], chstr.size()); + + // Possible largest char string + chstr.clear(); + std::string long_str(255, 'x'); + stringToCharString(createStringRegion(long_str), chstr); + std::vector<uint8_t> expected; + expected.push_back(255); // len of char string + expected.insert(expected.end(), long_str.begin(), long_str.end()); + matchWireData(&expected[0], expected.size(), &chstr[0], chstr.size()); + + // Same data as the previous case, but the original string is longer than + // the max; this shouldn't be rejected + chstr.clear(); + long_str.at(254) = '\\'; // replace the last 'x' with '\' + long_str.append("120"); // 'x' = 120 + stringToCharString(createStringRegion(long_str), chstr); + matchWireData(&expected[0], expected.size(), &chstr[0], chstr.size()); + + // Escaped '\' + chstr.clear(); + tmp[0] = 1; + tmp[1] = '\\'; + stringToCharString(createStringRegion("\\\\"), chstr); + matchWireData(tmp, 2, &chstr[0], chstr.size()); + + // Boundary values for \DDD + chstr.clear(); + tmp[0] = 1; + tmp[1] = 0; + stringToCharString(createStringRegion("\\000"), chstr); + matchWireData(tmp, 2, &chstr[0], chstr.size()); + + chstr.clear(); + stringToCharString(createStringRegion("\\255"), chstr); + tmp[0] = 1; + tmp[1] = 255; + matchWireData(tmp, 2, &chstr[0], chstr.size()); + + // Another digit follows DDD; it shouldn't cause confusion + chstr.clear(); + stringToCharString(createStringRegion("\\2550"), chstr); + tmp[0] = 2; // string len is now 2 + tmp[2] = '0'; + matchWireData(tmp, 3, &chstr[0], chstr.size()); +} + +TEST_F(CharStringTest, badConversion) { + // string cannot exceed 255 bytes + EXPECT_THROW(stringToCharString(createStringRegion(std::string(256, 'a')), + chstr), + CharStringTooLong); + + // input string ending with (non escaped) '\' + chstr.clear(); + EXPECT_THROW(stringToCharString(createStringRegion("foo\\"), chstr), + InvalidRdataText); +} + +TEST_F(CharStringTest, badDDD) { + // Check various type of bad form of \DDD + + // Not a number + EXPECT_THROW(stringToCharString(createStringRegion("\\1a2"), chstr), + InvalidRdataText); + EXPECT_THROW(stringToCharString(createStringRegion("\\12a"), chstr), + InvalidRdataText); + + // Not in the range of uint8_t + EXPECT_THROW(stringToCharString(createStringRegion("\\256"), chstr), + InvalidRdataText); + + // Short buffer + EXPECT_THROW(stringToCharString(createStringRegion("\\42"), chstr), + InvalidRdataText); +} + +const struct TestData { + const char *data; + const char *expected; +} conversion_data[] = { + {"Test\"Test", "Test\\\"Test"}, + {"Test;Test", "Test\\;Test"}, + {"Test\\Test", "Test\\\\Test"}, + {"Test\x1fTest", "Test\\031Test"}, + {"Test ~ Test", "Test ~ Test"}, + {"Test\x7fTest", "Test\\127Test"}, + {NULL, NULL} +}; + +TEST_F(CharStringTest, charStringToString) { + for (const TestData* cur = conversion_data; cur->data != NULL; ++cur) { + uint8_t idata[32]; + size_t length = std::strlen(cur->data); + // length (1 byte) + string (length bytes) + assert(sizeof(idata) > length); + idata[0] = static_cast<uint8_t>(length); + std::memcpy(idata + 1, cur->data, length); + const CharString test_data(idata, idata + length + 1); + EXPECT_EQ(cur->expected, charStringToString(test_data)); + } +} + +TEST_F(CharStringTest, bufferToCharString) { + const size_t chstr_size = sizeof(test_charstr); + isc::util::InputBuffer buf(test_charstr, chstr_size); + size_t read = bufferToCharString(buf, chstr_size, chstr); + + EXPECT_EQ(chstr_size, read); + EXPECT_EQ("Test String", charStringToString(chstr)); +} + +TEST_F(CharStringTest, bufferToCharString_bad) { + const size_t chstr_size = sizeof(test_charstr); + isc::util::InputBuffer buf(test_charstr, chstr_size); + // Set valid data in both so we can make sure the charstr is not + // modified + bufferToCharString(buf, chstr_size, chstr); + ASSERT_EQ("Test String", charStringToString(chstr)); + + // Should be at end of buffer now, so it should fail + EXPECT_THROW(bufferToCharString(buf, chstr_size - 1, chstr), + DNSMessageFORMERR); + EXPECT_EQ("Test String", charStringToString(chstr)); + + // reset and try to read with too low rdata_len + buf.setPosition(0); + EXPECT_THROW(bufferToCharString(buf, chstr_size - 1, chstr), + DNSMessageFORMERR); + EXPECT_EQ("Test String", charStringToString(chstr)); + + // set internal charstring len too high + const uint8_t test_charstr_err[] = { + sizeof("Test String") + 1, + 'T', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g' + }; + buf = isc::util::InputBuffer(test_charstr_err, sizeof(test_charstr_err)); + EXPECT_THROW(bufferToCharString(buf, chstr_size, chstr), + DNSMessageFORMERR); + EXPECT_EQ("Test String", charStringToString(chstr)); + +} + + + +TEST_F(CharStringTest, compareCharString) { + CharString charstr; + CharString charstr2; + CharString charstr_small1; + CharString charstr_small2; + CharString charstr_large1; + CharString charstr_large2; + CharString charstr_empty; + + stringToCharString(createStringRegion("test string"), charstr); + stringToCharString(createStringRegion("test string"), charstr2); + stringToCharString(createStringRegion("test strin"), charstr_small1); + stringToCharString(createStringRegion("test strina"), charstr_small2); + stringToCharString(createStringRegion("test stringa"), charstr_large1); + stringToCharString(createStringRegion("test strinz"), charstr_large2); + + EXPECT_EQ(0, compareCharStrings(charstr, charstr2)); + EXPECT_EQ(0, compareCharStrings(charstr2, charstr)); + EXPECT_EQ(1, compareCharStrings(charstr, charstr_small1)); + EXPECT_EQ(1, compareCharStrings(charstr, charstr_small2)); + EXPECT_EQ(-1, compareCharStrings(charstr, charstr_large1)); + EXPECT_EQ(-1, compareCharStrings(charstr, charstr_large2)); + EXPECT_EQ(-1, compareCharStrings(charstr_small1, charstr)); + EXPECT_EQ(-1, compareCharStrings(charstr_small2, charstr)); + EXPECT_EQ(1, compareCharStrings(charstr_large1, charstr)); + EXPECT_EQ(1, compareCharStrings(charstr_large2, charstr)); + + EXPECT_EQ(-1, compareCharStrings(charstr_empty, charstr)); + EXPECT_EQ(1, compareCharStrings(charstr, charstr_empty)); + EXPECT_EQ(0, compareCharStrings(charstr_empty, charstr_empty)); +} + +} // unnamed namespace diff --git a/src/lib/dns/tests/rdata_cname_unittest.cc b/src/lib/dns/tests/rdata_cname_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_cname_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_dhcid_unittest.cc b/src/lib/dns/tests/rdata_dhcid_unittest.cc index 8b13789179..c2cedee57c 100644 --- a/src/lib/dns/tests/rdata_dhcid_unittest.cc +++ b/src/lib/dns/tests/rdata_dhcid_unittest.cc @@ -1 +1,165 @@ +// Copyright (C) 2011-2024 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/rdataclass.h> +#include <util/encode/encode.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { + +class Rdata_DHCID_Test : public RdataTest { +protected: + Rdata_DHCID_Test() : + dhcid_txt("0LIg0LvQtdGB0YMg0YDQvtC00LjQu9Cw0YHRjCDRkdC70L7Rh9C60LA="), + rdata_dhcid(dhcid_txt) + {} + + void checkFromText_None(const string& rdata_str) { + checkFromText<in::DHCID, isc::Exception, isc::Exception>( + rdata_str, rdata_dhcid, false, false); + } + + void checkFromText_BadValue(const string& rdata_str) { + checkFromText<in::DHCID, BadValue, BadValue>( + rdata_str, rdata_dhcid, true, true); + } + + void checkFromText_LexerError(const string& rdata_str) { + checkFromText + <in::DHCID, InvalidRdataText, MasterLexer::LexerError>( + rdata_str, rdata_dhcid, true, true); + } + + void checkFromText_BadString(const string& rdata_str) { + checkFromText + <in::DHCID, InvalidRdataText, isc::Exception>( + rdata_str, rdata_dhcid, true, false); + } + + const string dhcid_txt; + const in::DHCID rdata_dhcid; +}; + +TEST_F(Rdata_DHCID_Test, fromText) { + EXPECT_EQ(dhcid_txt, rdata_dhcid.toText()); + + // Space in digest data is OK + checkFromText_None( + "0LIg0LvQtdGB0YMg 0YDQvtC00LjQu9Cw 0YHRjCDRkdC70L7R h9C60LA="); + + // Multi-line digest data is OK, if enclosed in parentheses + checkFromText_None( + "( 0LIg0LvQtdGB0YMg0YDQvtC00LjQu9Cw\n0YHRjCDRkdC70L7R h9C60LA= )"); + + // Trailing garbage. This should cause only the string constructor + // to fail, but the lexer constructor must be able to continue + // parsing from it. + checkFromText_BadString( + "0LIg0LvQtdGB0YMg0YDQvtC00LjQu9Cw0YHRjCDRkdC70L7Rh9C60LA=" + " ; comment\n" + "AAIBY2/AuCccgoJbsaxcQc9TUapptP69lOjxfNuVAA2kjEA="); +} + +TEST_F(Rdata_DHCID_Test, badText) { + // missing digest data + checkFromText_LexerError(""); + + // invalid base64 + checkFromText_BadValue("EEeeeeeeEEEeeeeeeGaaahAAAAAAAAHHHHHHHHHHH!="); + + // unterminated multi-line base64 + checkFromText_LexerError( + "( 0LIg0LvQtdGB0YMg0YDQvtC00LjQu9Cw\n0YHRjCDRkdC70L7R h9C60LA="); +} + +TEST_F(Rdata_DHCID_Test, copy) { + const in::DHCID rdata_dhcid2(rdata_dhcid); + EXPECT_EQ(0, rdata_dhcid.compare(rdata_dhcid2)); +} + +TEST_F(Rdata_DHCID_Test, createFromWire) { + EXPECT_EQ(0, rdata_dhcid.compare( + *rdataFactoryFromFile(RRType("DHCID"), RRClass("IN"), + "rdata_dhcid_fromWire"))); + + InputBuffer buffer(NULL, 0); + EXPECT_THROW(in::DHCID(buffer, 0), InvalidRdataLength); + + // TBD: more tests +} + +TEST_F(Rdata_DHCID_Test, createFromLexer) { + EXPECT_EQ(0, rdata_dhcid.compare( + *test::createRdataUsingLexer(RRType::DHCID(), RRClass::IN(), + dhcid_txt))); +} + +TEST_F(Rdata_DHCID_Test, toWireRenderer) { + rdata_dhcid.toWire(renderer); + + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_dhcid_toWire", data); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_DHCID_Test, toWireBuffer) { + rdata_dhcid.toWire(obuffer); + + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_dhcid_toWire", data); + matchWireData(&data[0], data.size(), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_DHCID_Test, toText) { + EXPECT_EQ(dhcid_txt, rdata_dhcid.toText()); +} + +TEST_F(Rdata_DHCID_Test, getDHCIDDigest) { + const string dhcid_txt1(encodeBase64(rdata_dhcid.getDigest())); + + EXPECT_EQ(dhcid_txt, dhcid_txt1); +} + +TEST_F(Rdata_DHCID_Test, compare) { + // trivial case: self equivalence + // cppcheck-suppress uselessCallsCompare + EXPECT_EQ(0, rdata_dhcid.compare(rdata_dhcid)); + + in::DHCID rdata_dhcid1("0YLQvtC/0L7Qu9GPINC00LLQsCDRgNGD0LHQu9GP"); + in::DHCID rdata_dhcid2("0YLQvtC/0L7Qu9GPINGC0YDQuCDRgNGD0LHQu9GP"); + in::DHCID rdata_dhcid3("0YLQvtC/0L7Qu9GPINGH0LXRgtGL0YDQtSDRgNGD0LHQu9GP"); + + EXPECT_LT(rdata_dhcid1.compare(rdata_dhcid2), 0); + EXPECT_GT(rdata_dhcid2.compare(rdata_dhcid1), 0); + + EXPECT_LT(rdata_dhcid2.compare(rdata_dhcid3), 0); + EXPECT_GT(rdata_dhcid3.compare(rdata_dhcid2), 0); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_dhcid.compare(*rdata_nomatch), bad_cast); +} +} diff --git a/src/lib/dns/tests/rdata_dname_unittest.cc b/src/lib/dns/tests/rdata_dname_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_dname_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_dnskey_unittest.cc b/src/lib/dns/tests/rdata_dnskey_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_dnskey_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_ds_like_unittest.cc b/src/lib/dns/tests/rdata_ds_like_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_ds_like_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_hinfo_unittest.cc b/src/lib/dns/tests/rdata_hinfo_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_hinfo_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_in_a_unittest.cc b/src/lib/dns/tests/rdata_in_a_unittest.cc index 8b13789179..809cfb5bd4 100644 --- a/src/lib/dns/tests/rdata_in_a_unittest.cc +++ b/src/lib/dns/tests/rdata_in_a_unittest.cc @@ -1 +1,159 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <dns/rdataclass.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/master_lexer.h> +#include <dns/master_loader.h> +#include <dns/rdata.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +#include <sstream> + +#include <arpa/inet.h> +#include <sys/socket.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_IN_A_Test : public RdataTest { +protected: + Rdata_IN_A_Test() : rdata_in_a("192.0.2.1") {} + + void checkFromTextIN_A(const std::string& rdata_txt, + bool throw_str_version = true, + bool throw_lexer_version = true) { + checkFromText<in::A, InvalidRdataText, InvalidRdataText>( + rdata_txt, rdata_in_a, throw_str_version, throw_lexer_version); + } + + const in::A rdata_in_a; +}; + +const uint8_t wiredata_in_a[] = { 192, 0, 2, 1 }; + +TEST_F(Rdata_IN_A_Test, createFromText) { + // Normal case: no exception for either case, so the exception type + // doesn't matter. + checkFromText<in::A, isc::Exception, isc::Exception>("192.0.2.1", + rdata_in_a, false, + false); + + // should reject an abbreviated form of IPv4 address + checkFromTextIN_A("10.1"); + // or an IPv6 address + checkFromTextIN_A("2001:db8::1234"); + // or any meaningless text as an IP address + checkFromTextIN_A("xxx"); + + // NetBSD's inet_pton accepts trailing space after an IPv4 address, which + // would confuse some of the tests below. We check the case differently + // in these cases depending on the strictness of inet_pton (most + // implementations seem to be stricter). + uint8_t v4addr_buf[4]; + const bool reject_extra_space = + inet_pton(AF_INET, "192.0.2.1 ", v4addr_buf) == 0; + + // trailing white space: only string version throws + checkFromTextIN_A("192.0.2.1 ", reject_extra_space, false); + // same for beginning white space. + checkFromTextIN_A(" 192.0.2.1", true, false); + // same for trailing non-space garbage (note that lexer version still + // ignore it; it's expected to be detected at a higher layer). + checkFromTextIN_A("192.0.2.1 xxx", reject_extra_space, false); + + // nul character after a valid textual representation. + string nul_after_addr = "192.0.2.1"; + nul_after_addr.push_back(0); + checkFromTextIN_A(nul_after_addr, true, true); + + // a valid address surrounded by parentheses; only okay with lexer + checkFromTextIN_A("(192.0.2.1)", true, false); + + // input that would cause lexer-specific error; it's bad text as an + // address so should result in the string version, too. + checkFromText<in::A, InvalidRdataText, MasterLexer::LexerError>( + ")192.0.2.1", rdata_in_a); +} + +TEST_F(Rdata_IN_A_Test, createFromWire) { + // Valid data + EXPECT_EQ(0, rdata_in_a.compare( + *rdataFactoryFromFile(RRType::A(), RRClass::IN(), + "rdata_in_a_fromWire"))); + // RDLENGTH is too short + EXPECT_THROW(rdataFactoryFromFile(RRType::A(), RRClass::IN(), + "rdata_in_a_fromWire", 6), + DNSMessageFORMERR); + // RDLENGTH is too long + EXPECT_THROW(rdataFactoryFromFile(RRType::A(), RRClass::IN(), + "rdata_in_a_fromWire", 12), + DNSMessageFORMERR); + // buffer too short. + EXPECT_THROW(rdataFactoryFromFile(RRType::A(), RRClass::IN(), + "rdata_in_a_fromWire", 19), + DNSMessageFORMERR); +} + +TEST_F(Rdata_IN_A_Test, toWireBuffer) { + rdata_in_a.toWire(obuffer); + matchWireData(wiredata_in_a, sizeof (wiredata_in_a), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_IN_A_Test, toWireRenderer) { + rdata_in_a.toWire(renderer); + matchWireData(wiredata_in_a, sizeof (wiredata_in_a), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_IN_A_Test, toText) { + EXPECT_EQ("192.0.2.1", rdata_in_a.toText()); + + // this shouldn't make the code crash + const string longaddr("255.255.255.255"); + EXPECT_EQ(longaddr, in::A(longaddr).toText()); +} + +TEST_F(Rdata_IN_A_Test, compare) { + const in::A small1("1.1.1.1"); + const in::A small2("1.2.3.4"); + const in::A large1("255.255.255.255"); + const in::A large2("4.3.2.1"); + + // trivial case: self equivalence + // cppcheck-suppress uselessCallsCompare + EXPECT_EQ(0, small1.compare(small1)); + + // confirm these are compared as unsigned values + EXPECT_GT(0, small1.compare(large1)); + EXPECT_LT(0, large1.compare(small1)); + + // confirm these are compared in network byte order + EXPECT_GT(0, small2.compare(large2)); + EXPECT_LT(0, large2.compare(small2)); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_in_a.compare(*RdataTest::rdata_nomatch), bad_cast); +} +} diff --git a/src/lib/dns/tests/rdata_in_aaaa_unittest.cc b/src/lib/dns/tests/rdata_in_aaaa_unittest.cc index 8b13789179..cc7b07d8c6 100644 --- a/src/lib/dns/tests/rdata_in_aaaa_unittest.cc +++ b/src/lib/dns/tests/rdata_in_aaaa_unittest.cc @@ -1 +1,151 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_IN_AAAA_Test : public RdataTest { +protected: + Rdata_IN_AAAA_Test() : rdata_in_aaaa("2001:db8::1234") {} + + // Common check to see the result of in::A Rdata construction either from + // std::string or with MasterLexer object. If it's expected to succeed + // the result should be identical to the commonly used test data + // (rdata_in_a); otherwise it should result in the exception specified as + // the template parameter. + void checkFromTextIN_AAAA(const string& in_aaaa_txt, + bool throw_str_version = true, + bool throw_lexer_version = true) + { + checkFromText<in::AAAA, InvalidRdataText, InvalidRdataText>( + in_aaaa_txt, rdata_in_aaaa, throw_str_version, + throw_lexer_version); + } + + const in::AAAA rdata_in_aaaa; +}; + +const uint8_t wiredata_in_aaaa[] = { + 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x12, 0x34 }; + +TEST_F(Rdata_IN_AAAA_Test, createFromText) { + // Normal case: no exception for either case, so the exception type + // doesn't matter. + checkFromText<in::AAAA, isc::Exception, isc::Exception>( + "2001:db8::1234", rdata_in_aaaa, false, false); + + // should reject an IP4 address. + checkFromTextIN_AAAA("192.0.2.1"); + // or any meaningless text as an IPv6 address + checkFromTextIN_AAAA("xxx"); + + // trailing white space: only string version throws + checkFromTextIN_AAAA("2001:db8::1234 ", true, false); + // same for beginning white space. + checkFromTextIN_AAAA(" 2001:db8::1234", true, false); + // same for trailing non-space garbage (note that lexer version still + // ignore it; it's expected to be detected at a higher layer). + checkFromTextIN_AAAA("2001:db8::1234 xxx", true, false); + + // nul character after a valid textual representation. + string nul_after_addr = "2001:db8::1234"; + nul_after_addr.push_back(0); + checkFromTextIN_AAAA(nul_after_addr, true, true); + + // a valid address surrounded by parentheses; only okay with lexer + checkFromTextIN_AAAA("(2001:db8::1234)", true, false); + + // input that would cause lexer-specific error; it's bad text as an + // address so should result in the string version, too. + checkFromText<in::AAAA, InvalidRdataText, MasterLexer::LexerError>( + ")2001:db8::1234", rdata_in_aaaa); +} + +TEST_F(Rdata_IN_AAAA_Test, createFromWire) { + // Valid data + EXPECT_EQ(0, rdata_in_aaaa.compare( + *rdataFactoryFromFile(RRType::AAAA(), RRClass::IN(), + "rdata_in_aaaa_fromWire"))); + // RDLENGTH is too short + EXPECT_THROW(rdataFactoryFromFile(RRType::AAAA(), RRClass::IN(), + "rdata_in_aaaa_fromWire", 18), + DNSMessageFORMERR); + // RDLENGTH is too long + EXPECT_THROW(rdataFactoryFromFile(RRType::AAAA(), RRClass::IN(), + "rdata_in_aaaa_fromWire", 36), + DNSMessageFORMERR); + // buffer too short. + EXPECT_THROW(rdataFactoryFromFile(RRType::AAAA(), RRClass::IN(), + "rdata_in_aaaa_fromWire", 55), + DNSMessageFORMERR); +} + +TEST_F(Rdata_IN_AAAA_Test, createFromLexer) { + EXPECT_EQ(0, rdata_in_aaaa.compare( + *test::createRdataUsingLexer(RRType::AAAA(), RRClass::IN(), + "2001:db8::1234"))); +} + +TEST_F(Rdata_IN_AAAA_Test, toWireBuffer) { + rdata_in_aaaa.toWire(obuffer); + matchWireData(wiredata_in_aaaa, sizeof (wiredata_in_aaaa), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_IN_AAAA_Test, toWireRenderer) { + rdata_in_aaaa.toWire(renderer); + matchWireData(wiredata_in_aaaa, sizeof (wiredata_in_aaaa), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_IN_AAAA_Test, toText) { + EXPECT_EQ("2001:db8::1234", rdata_in_aaaa.toText()); +} + +TEST_F(Rdata_IN_AAAA_Test, compare) { + in::AAAA small1("::1"); + in::AAAA small2("1:2:3:4:5:6:7:8"); + in::AAAA large1("ffff::"); + in::AAAA large2("8:7:6:5:4:3:2:1"); + + // trivial case: self equivalence + // cppcheck-suppress uselessCallsCompare + EXPECT_EQ(0, small1.compare(small1)); + + // confirm these are compared as unsigned values + EXPECT_GT(0, small1.compare(large1)); + EXPECT_LT(0, large1.compare(small1)); + + // confirm these are compared in network byte order + EXPECT_GT(0, small2.compare(large2)); + EXPECT_LT(0, large2.compare(small2)); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_in_aaaa.compare(*RdataTest::rdata_nomatch), bad_cast); +} +} diff --git a/src/lib/dns/tests/rdata_minfo_unittest.cc b/src/lib/dns/tests/rdata_minfo_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_minfo_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_mx_unittest.cc b/src/lib/dns/tests/rdata_mx_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_mx_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_naptr_unittest.cc b/src/lib/dns/tests/rdata_naptr_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_naptr_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_ns_unittest.cc b/src/lib/dns/tests/rdata_ns_unittest.cc index 8b13789179..80123ae937 100644 --- a/src/lib/dns/tests/rdata_ns_unittest.cc +++ b/src/lib/dns/tests/rdata_ns_unittest.cc @@ -1 +1,145 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> +#include <util/buffer.h> + +#include <gtest/gtest.h> + +using namespace isc::dns; +using namespace isc::dns::rdata; +using namespace isc::util; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +using namespace std; + +namespace { +class Rdata_NS_Test : public RdataTest { +public: + Rdata_NS_Test() : + rdata_ns("ns.example.com."), + rdata_ns2("ns2.example.com.") { + } + + const generic::NS rdata_ns; + const generic::NS rdata_ns2; +}; + +const uint8_t wiredata_ns[] = { + 0x02, 0x6e, 0x73, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00 }; +const uint8_t wiredata_ns2[] = { + // first name: ns.example.com. + 0x02, 0x6e, 0x73, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00, + // second name: ns2.example.com. all labels except the first should be + // compressed. + 0x03, 0x6e, 0x73, 0x32, 0xc0, 0x03 }; + +TEST_F(Rdata_NS_Test, createFromText) { + EXPECT_EQ(0, rdata_ns.compare(generic::NS("ns.example.com."))); + // explicitly add a trailing dot. should be the same RDATA. + EXPECT_EQ(0, rdata_ns.compare(generic::NS("ns.example.com."))); + // should be case sensitive. + EXPECT_EQ(0, rdata_ns.compare(generic::NS("NS.EXAMPLE.COM."))); + // RDATA of a class-independent type should be recognized for any + // "unknown" class. + EXPECT_EQ(0, rdata_ns.compare(*createRdata(RRType("NS"), RRClass(65000), + "ns.example.com."))); +} + +TEST_F(Rdata_NS_Test, badText) { + // Extra input at end of line + EXPECT_THROW(generic::NS("ns.example.com. extra."), InvalidRdataText); +} + +TEST_F(Rdata_NS_Test, createFromWire) { + EXPECT_EQ(0, rdata_ns.compare( + *rdataFactoryFromFile(RRType("NS"), RRClass("IN"), + "rdata_ns_fromWire"))); + // RDLENGTH is too short + EXPECT_THROW(rdataFactoryFromFile(RRType("NS"), RRClass("IN"), + "rdata_ns_fromWire", 18), + InvalidRdataLength); + // RDLENGTH is too long + EXPECT_THROW(rdataFactoryFromFile(RRType("NS"), RRClass("IN"), + "rdata_ns_fromWire", 36), + InvalidRdataLength); + // incomplete name. the error should be detected in the name constructor + EXPECT_THROW(rdataFactoryFromFile(RRType("NS"), RRClass("IN"), + "rdata_ns_fromWire", 71), + DNSMessageFORMERR); + + EXPECT_EQ(0, generic::NS("ns2.example.com.").compare( + *rdataFactoryFromFile(RRType("NS"), RRClass("IN"), + "rdata_ns_fromWire", 55))); + EXPECT_THROW(*rdataFactoryFromFile(RRType("NS"), RRClass("IN"), + "rdata_ns_fromWire", 63), + InvalidRdataLength); +} + +TEST_F(Rdata_NS_Test, createFromLexer) { + EXPECT_EQ(0, rdata_ns.compare( + *test::createRdataUsingLexer(RRType::NS(), RRClass::IN(), + "ns.example.com."))); + + // test::createRdataUsingLexer() constructs relative to + // "example.org." origin. + EXPECT_EQ(0, generic::NS("ns8.example.org.").compare( + *test::createRdataUsingLexer(RRType::NS(), RRClass::IN(), + "ns8"))); + + // Exceptions cause NULL to be returned. + EXPECT_FALSE(test::createRdataUsingLexer(RRType::NS(), RRClass::IN(), + "")); + + // Extra input at end of line + EXPECT_FALSE(test::createRdataUsingLexer(RRType::NS(), RRClass::IN(), + "ns.example.com. extra.")); +} + +TEST_F(Rdata_NS_Test, toWireBuffer) { + rdata_ns.toWire(obuffer); + matchWireData(wiredata_ns, sizeof(wiredata_ns), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_NS_Test, toWireRenderer) { + rdata_ns.toWire(renderer); + matchWireData(wiredata_ns, sizeof(wiredata_ns), + renderer.getData(), renderer.getLength()); + + rdata_ns2.toWire(renderer); + matchWireData(wiredata_ns2, sizeof(wiredata_ns2), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_NS_Test, toText) { + EXPECT_EQ("ns.example.com.", rdata_ns.toText()); +} + +TEST_F(Rdata_NS_Test, compare) { + generic::NS small("a.example."); + generic::NS large("example."); + EXPECT_TRUE(Name("a.example") > Name("example")); + EXPECT_GT(0, small.compare(large)); +} + +TEST_F(Rdata_NS_Test, getNSName) { + EXPECT_EQ(Name("ns.example.com."), rdata_ns.getNSName()); +} +} diff --git a/src/lib/dns/tests/rdata_nsec3_unittest.cc b/src/lib/dns/tests/rdata_nsec3_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_nsec3_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc b/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_nsec3param_like_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_nsec3param_unittest.cc b/src/lib/dns/tests/rdata_nsec3param_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_nsec3param_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_nsec_unittest.cc b/src/lib/dns/tests/rdata_nsec_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_nsec_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc b/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_nsecbitmap_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_opt_unittest.cc b/src/lib/dns/tests/rdata_opt_unittest.cc index 8b13789179..6be2d08a39 100644 --- a/src/lib/dns/tests/rdata_opt_unittest.cc +++ b/src/lib/dns/tests/rdata_opt_unittest.cc @@ -1 +1,198 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_OPT_Test : public RdataTest { + // there's nothing to specialize +}; + +const uint8_t rdata_opt_wiredata[] = { + // Option code + 0x00, 0x2a, + // Option length + 0x00, 0x03, + // Option data + 0x00, 0x01, 0x02 +}; + +TEST_F(Rdata_OPT_Test, createFromText) { + // OPT RR cannot be created from text. + EXPECT_THROW(generic::OPT("this does not matter"), InvalidRdataText); +} + +TEST_F(Rdata_OPT_Test, createFromWire) { + // Valid cases: in the simple implementation with no supported options, + // we can only check these don't throw. + EXPECT_NO_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass("CLASS4096"), + "rdata_opt_fromWire1")); + EXPECT_NO_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass::ANY(), + "rdata_opt_fromWire1", 2)); + + // Short RDLEN. This throws InvalidRdataLength even if subsequent + // pseudo RRs cause RDLEN size to be exhausted. + EXPECT_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass::IN(), + "rdata_opt_fromWire2"), + InvalidRdataLength); + EXPECT_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass::IN(), + "rdata_opt_fromWire3"), + InvalidRdataLength); + // Option lengths can add up and overflow RDLEN. Unlikely when + // parsed from wire data, but we'll check for it anyway. + EXPECT_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass::IN(), + "rdata_opt_fromWire4"), + InvalidRdataText); + + // short buffer case. + EXPECT_THROW(rdataFactoryFromFile(RRType::OPT(), RRClass::IN(), + "rdata_opt_fromWire1", 11), + InvalidBufferPosition); +} + +TEST_F(Rdata_OPT_Test, createFromLexer) { + // OPT RR cannot be created from text. Exceptions cause NULL to be + // returned. + EXPECT_FALSE(test::createRdataUsingLexer(RRType::OPT(), RRClass::IN(), + "this does not matter")); +} + +TEST_F(Rdata_OPT_Test, toWireBuffer) { + const generic::OPT rdata_opt = + dynamic_cast<const generic::OPT&> + (*rdataFactoryFromFile(RRType("OPT"), RRClass("IN"), + "rdata_opt_fromWire1", 2)); + + obuffer.clear(); + rdata_opt.toWire(obuffer); + + matchWireData(rdata_opt_wiredata, sizeof(rdata_opt_wiredata), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_OPT_Test, toWireRenderer) { + const generic::OPT rdata_opt = + dynamic_cast<const generic::OPT&> + (*rdataFactoryFromFile(RRType("OPT"), RRClass("IN"), + "rdata_opt_fromWire1", 2)); + + renderer.clear(); + rdata_opt.toWire(renderer); + + matchWireData(rdata_opt_wiredata, sizeof(rdata_opt_wiredata), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_OPT_Test, toText) { + // empty OPT + const generic::OPT rdata_opt; + + EXPECT_THROW(rdata_opt.toText(), + isc::InvalidOperation); +} + +TEST_F(Rdata_OPT_Test, compare) { + // empty OPT + const generic::OPT rdata_opt; + + EXPECT_THROW(rdata_opt.compare( + *rdataFactoryFromFile(RRType::OPT(), RRClass::ANY(), + "rdata_opt_fromWire1", 2)), + isc::InvalidOperation); + + // comparison attempt between incompatible RR types also results in + // isc::InvalidOperation. + EXPECT_THROW(rdata_opt.compare(*RdataTest::rdata_nomatch), + isc::InvalidOperation); +} + +TEST_F(Rdata_OPT_Test, appendPseudoRR) { + generic::OPT rdata_opt; + + // Append empty option data + rdata_opt.appendPseudoRR(0x0042, NULL, 0); + + // Append simple option data + const uint8_t option_data[] = {'H', 'e', 'l', 'l', 'o'}; + rdata_opt.appendPseudoRR(0x0043, option_data, sizeof(option_data)); + + // Duplicate option codes are okay. + rdata_opt.appendPseudoRR(0x0042, option_data, sizeof(option_data)); + + // When option length may overflow RDLEN, append should throw. + const std::vector<uint8_t> buffer((1 << 16) - 1); + EXPECT_THROW(rdata_opt.appendPseudoRR(0x0044, &buffer[0], buffer.size()), + isc::InvalidParameter); + + const uint8_t rdata_opt_wiredata2[] = { + // OPTION #1 + // ` Option code + 0x00, 0x42, + // ` Option length + 0x00, 0x00, + + // OPTION #2 + // ` Option code + 0x00, 0x43, + // ` Option length + 0x00, 0x05, + // ` Option data + 'H', 'e', 'l', 'l', 'o', + + // OPTION #3 + // ` Option code + 0x00, 0x42, + // ` Option length + 0x00, 0x05, + // ` Option data + 'H', 'e', 'l', 'l', 'o' + }; + + obuffer.clear(); + rdata_opt.toWire(obuffer); + + matchWireData(rdata_opt_wiredata2, sizeof(rdata_opt_wiredata2), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_OPT_Test, getPseudoRRs) { + const generic::OPT rdf = + dynamic_cast<const generic::OPT&> + (*rdataFactoryFromFile(RRType("OPT"), RRClass("IN"), + "rdata_opt_fromWire1", 2)); + + const std::vector<generic::OPT::PseudoRR>& rrs = rdf.getPseudoRRs(); + ASSERT_FALSE(rrs.empty()); + EXPECT_EQ(1, rrs.size()); + EXPECT_EQ(0x2a, rrs.at(0).getCode()); + EXPECT_EQ(3, rrs.at(0).getLength()); + + const uint8_t expected_data[] = {0x00, 0x01, 0x02}; + const uint8_t* actual_data = rrs.at(0).getData(); + EXPECT_EQ(0, std::memcmp(expected_data, actual_data, + sizeof(expected_data))); +} +} diff --git a/src/lib/dns/tests/rdata_pimpl_holder_unittest.cc b/src/lib/dns/tests/rdata_pimpl_holder_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_pimpl_holder_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_ptr_unittest.cc b/src/lib/dns/tests/rdata_ptr_unittest.cc index 8b13789179..8275ea5fb4 100644 --- a/src/lib/dns/tests/rdata_ptr_unittest.cc +++ b/src/lib/dns/tests/rdata_ptr_unittest.cc @@ -1 +1,145 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace isc::dns; +using namespace isc::dns::rdata; +using namespace isc::util; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +using namespace std; +// +// This test currently simply copies the NS RDATA tests. +// + +namespace { +class Rdata_PTR_Test : public RdataTest { +public: + Rdata_PTR_Test() : + rdata_ptr("ns.example.com."), + rdata_ptr2("ns2.example.com.") { + } + + const generic::PTR rdata_ptr; + const generic::PTR rdata_ptr2; +}; + +const uint8_t wiredata_ptr[] = { + 0x02, 0x6e, 0x73, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00 }; +const uint8_t wiredata_ptr2[] = { + // first name: ns.example.com. + 0x02, 0x6e, 0x73, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x03, + 0x63, 0x6f, 0x6d, 0x00, + // second name: ns2.example.com. all labels except the first should be + // compressed. + 0x03, 0x6e, 0x73, 0x32, 0xc0, 0x03 }; + +TEST_F(Rdata_PTR_Test, createFromText) { + EXPECT_EQ(0, rdata_ptr.compare(generic::PTR("ns.example.com."))); + // explicitly add a trailing dot. should be the same RDATA. + EXPECT_EQ(0, rdata_ptr.compare(generic::PTR("ns.example.com."))); + // should be case sensitive. + EXPECT_EQ(0, rdata_ptr.compare(generic::PTR("NS.EXAMPLE.COM."))); + // RDATA of a class-independent type should be recognized for any + // "unknown" class. + EXPECT_EQ(0, rdata_ptr.compare(*createRdata(RRType("PTR"), RRClass(65000), + "ns.example.com."))); +} + +TEST_F(Rdata_PTR_Test, badText) { + // Extra text at end of line + EXPECT_THROW(generic::PTR("foo.example.com. extra."), InvalidRdataText); +} + +TEST_F(Rdata_PTR_Test, createFromWire) { + EXPECT_EQ(0, rdata_ptr.compare( + *rdataFactoryFromFile(RRType("PTR"), RRClass("IN"), + "rdata_ns_fromWire"))); + // RDLENGTH is too short + EXPECT_THROW(rdataFactoryFromFile(RRType("PTR"), RRClass("IN"), + "rdata_ns_fromWire", 18), + InvalidRdataLength); + // RDLENGTH is too long + EXPECT_THROW(rdataFactoryFromFile(RRType("PTR"), RRClass("IN"), + "rdata_ns_fromWire", 36), + InvalidRdataLength); + // incomplete name. the error should be detected in the name constructor + EXPECT_THROW(rdataFactoryFromFile(RRType("PTR"), RRClass("IN"), + "rdata_ns_fromWire", 71), + DNSMessageFORMERR); + + EXPECT_EQ(0, generic::PTR("ns2.example.com.").compare( + *rdataFactoryFromFile(RRType("PTR"), RRClass("IN"), + "rdata_ns_fromWire", 55))); + EXPECT_THROW(*rdataFactoryFromFile(RRType("PTR"), RRClass("IN"), + "rdata_ns_fromWire", 63), + InvalidRdataLength); +} + +TEST_F(Rdata_PTR_Test, createFromLexer) { + EXPECT_EQ(0, rdata_ptr.compare( + *test::createRdataUsingLexer(RRType::PTR(), RRClass::IN(), + "ns.example.com."))); + + // test::createRdataUsingLexer() constructs relative to + // "example.org." origin. + EXPECT_EQ(0, generic::PTR("foo0.example.org.").compare( + *test::createRdataUsingLexer(RRType::PTR(), RRClass::IN(), + "foo0"))); + + // Extra text at end of line + EXPECT_FALSE(test::createRdataUsingLexer(RRType::PTR(), RRClass::IN(), + "foo.example.com. extra.")); +} + +TEST_F(Rdata_PTR_Test, toWireBuffer) { + rdata_ptr.toWire(obuffer); + matchWireData(wiredata_ptr, sizeof(wiredata_ptr), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_PTR_Test, toWireRenderer) { + rdata_ptr.toWire(renderer); + matchWireData(wiredata_ptr, sizeof(wiredata_ptr), + renderer.getData(), renderer.getLength()); + + rdata_ptr2.toWire(renderer); + matchWireData(wiredata_ptr2, sizeof(wiredata_ptr2), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_PTR_Test, toText) { + EXPECT_EQ("ns.example.com.", rdata_ptr.toText()); +} + +TEST_F(Rdata_PTR_Test, compare) { + generic::PTR small("a.example."); + generic::PTR large("example."); + EXPECT_TRUE(Name("a.example") > Name("example")); + EXPECT_GT(0, small.compare(large)); +} + +TEST_F(Rdata_PTR_Test, getPTRName) { + EXPECT_EQ(Name("ns.example.com"), rdata_ptr.getPTRName()); +} +} diff --git a/src/lib/dns/tests/rdata_rp_unittest.cc b/src/lib/dns/tests/rdata_rp_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_rp_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_rrsig_unittest.cc b/src/lib/dns/tests/rdata_rrsig_unittest.cc index 8b13789179..b198d15c62 100644 --- a/src/lib/dns/tests/rdata_rrsig_unittest.cc +++ b/src/lib/dns/tests/rdata_rrsig_unittest.cc @@ -1 +1,369 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/time_utilities.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> +#include <dns/tests/rdata_unittest.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { + +const uint8_t wiredata_rrsig[] = { + // type covered = A + 0x00, 0x01, + // algorithm = 5 + 0x05, + // labels = 4 + 0x04, + // original TTL = 43200 (0x0000a8c0) + 0x00, 0x00, 0xa8, 0xc0, + // signature expiration = 1266961577 (0x4b844ca9) + 0x4b, 0x84, 0x4c, 0xa9, + // signature inception = 1266875177 (0x4b82fb29) + 0x4b, 0x82, 0xfb, 0x29, + // key tag = 8496 (0x2130) + 0x21, 0x30, + // signer's name (isc.org.) + // 3 i s c 3 o r g 0 + 0x03, 0x69, 0x73, 0x63, 0x03, 0x6f, 0x72, 0x67, 0x00, + // signature data follows + 0x7a, 0xfc, 0x61, 0x94, 0x6c, + 0x75, 0xde, 0x6a, 0x4a, 0x2d, 0x59, 0x0a, 0xb2, + 0x3a, 0x46, 0xcf, 0x27, 0x12, 0xe6, 0xdc, 0x2d, + 0x22, 0x8c, 0x4e, 0x9a, 0x53, 0x75, 0xe3, 0x0f, + 0x6d, 0xe4, 0x08, 0x33, 0x18, 0x19, 0xb3, 0x76, + 0x21, 0x9d, 0x2c, 0x8a, 0xc5, 0x69, 0xba, 0xab, + 0xef, 0x66, 0x9f, 0xda, 0xb5, 0x2a, 0xf9, 0x40, + 0xc1, 0x28, 0xc5, 0x97, 0xba, 0x3c, 0x19, 0x4d, + 0x95, 0x13, 0xc2, 0xcd, 0xf6, 0xb1, 0x59, 0x5d, + 0x0c, 0xf9, 0x3f, 0x35, 0xbb, 0x9a, 0x70, 0x93, + 0x36, 0xe5, 0xf4, 0x17, 0x7e, 0xfe, 0x66, 0x3b, + 0x70, 0x1f, 0xed, 0x33, 0xa8, 0xa3, 0x0d, 0xc0, + 0x8c, 0xc6, 0x95, 0x1b, 0xd8, 0x9c, 0x8c, 0x25, + 0xb4, 0x57, 0x9e, 0x56, 0x71, 0x64, 0x14, 0x7f, + 0x8f, 0x6d, 0xfa, 0xc5, 0xca, 0x3f, 0x36, 0xe2, + 0xa4, 0xdf, 0x60, 0xfa, 0xcd, 0x59, 0x3e, 0x22, + 0x32, 0xa1, 0xf7 +}; + +class Rdata_RRSIG_Test : public RdataTest { +protected: + Rdata_RRSIG_Test() : + rrsig_txt("A 5 4 43200 20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="), + rdata_rrsig(rrsig_txt) + {} + + void checkFromText_None(const string& rdata_str) { + checkFromText<generic::RRSIG, isc::Exception, isc::Exception>( + rdata_str, rdata_rrsig, false, false); + } + + void checkFromText_InvalidText(const string& rdata_str) { + checkFromText<generic::RRSIG, InvalidRdataText, InvalidRdataText>( + rdata_str, rdata_rrsig, true, true); + } + + void checkFromText_InvalidType(const string& rdata_str) { + checkFromText<generic::RRSIG, InvalidRRType, InvalidRRType>( + rdata_str, rdata_rrsig, true, true); + } + + void checkFromText_InvalidTime(const string& rdata_str) { + checkFromText<generic::RRSIG, InvalidTime, InvalidTime>( + rdata_str, rdata_rrsig, true, true); + } + + void checkFromText_BadValue(const string& rdata_str) { + checkFromText<generic::RRSIG, BadValue, BadValue>( + rdata_str, rdata_rrsig, true, true); + } + + void checkFromText_LexerError(const string& rdata_str) { + checkFromText + <generic::RRSIG, InvalidRdataText, MasterLexer::LexerError>( + rdata_str, rdata_rrsig, true, true); + } + + void checkFromText_MissingOrigin(const string& rdata_str) { + checkFromText + <generic::RRSIG, MissingNameOrigin, MissingNameOrigin>( + rdata_str, rdata_rrsig, true, true); + } + + void checkFromText_BadString(const string& rdata_str) { + checkFromText + <generic::RRSIG, InvalidRdataText, isc::Exception>( + rdata_str, rdata_rrsig, true, false); + } + + const string rrsig_txt; + const generic::RRSIG rdata_rrsig; +}; + +TEST_F(Rdata_RRSIG_Test, fromText) { + EXPECT_EQ(rrsig_txt, rdata_rrsig.toText()); + EXPECT_EQ(isc::dns::RRType::A(), rdata_rrsig.typeCovered()); + + // Missing signature is OK + EXPECT_NO_THROW(const generic::RRSIG sig( + "A 5 4 43200 20100223214617 20100222214617 8496 isc.org.")); + + // Space in signature data is OK + checkFromText_None( + "A 5 4 43200 20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz " + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/ " + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU " + "f49t+sXKPzbipN9g+s1ZPiIyofc="); + + // Multi-line signature data is OK, if enclosed in parentheses + checkFromText_None( + "A 5 4 43200 20100223214617 20100222214617 8496 isc.org. " + "( evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz\n" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/\n" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU\n" + "f49t+sXKPzbipN9g+s1ZPiIyofc= )"); + + // Trailing garbage. This should cause only the string constructor + // to fail, but the lexer constructor must be able to continue + // parsing from it. + checkFromText_BadString( + "A 5 4 43200 20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc= ; comment\n" + "A 5 4 43200 20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, badText_missingFields) { + checkFromText_LexerError("A"); + checkFromText_LexerError("A 5"); + checkFromText_LexerError("A 5 4"); + checkFromText_LexerError("A 5 4 43200"); + checkFromText_LexerError("A 5 4 43200 20100223214617"); + checkFromText_LexerError("A 5 4 43200 20100223214617 20100222214617"); + checkFromText_LexerError("A 5 4 43200 20100223214617 20100222214617 " + "8496"); +} + +TEST_F(Rdata_RRSIG_Test, badText_coveredType) { + checkFromText_InvalidType("SPORK"); +} + +TEST_F(Rdata_RRSIG_Test, badText_algorithm) { + checkFromText_InvalidText( + "A 555 4 43200 " + "20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); + checkFromText_LexerError( + "A FIVE 4 43200 " + "20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, badText_labels) { + checkFromText_InvalidText( + "A 5 4444 43200 " + "20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); + checkFromText_LexerError( + "A 5 FOUR 43200 " + "20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, badText_ttl) { + checkFromText_LexerError( + "A 5 4 999999999999 " + "20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); + checkFromText_LexerError( + "A 5 4 TTL " + "20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); + + // alternate form of TTL is not okay + checkFromText_LexerError( + "A 5 4 12H 20100223214617 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz " + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/ " + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU " + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, badText_expiration) { + checkFromText_InvalidTime( + "A 5 4 43200 " + "201002232 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); + checkFromText_InvalidTime( + "A 5 4 43200 " + "EXPIRATION 20100222214617 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, badText_inception) { + checkFromText_InvalidTime( + "A 5 4 43200 " + "20100223214617 20100227 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); + checkFromText_InvalidTime( + "A 5 4 43200 " + "20100223214617 INCEPTION 8496 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, badText_keytag) { + checkFromText_InvalidText( + "A 5 4 43200 " + "20100223214617 20100222214617 999999 isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); + checkFromText_LexerError( + "A 5 4 43200 " + "20100223214617 20100222214617 TAG isc.org. " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, badText_signer) { + checkFromText_MissingOrigin( + "A 5 4 43200 " + "20100223214617 20100222214617 8496 isc.org " + "evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, badText_signature) { + checkFromText_BadValue( + "A 5 4 43200 " + "20100223214617 20100222214617 8496 isc.org. " + "EEeeeeeeEEEeeeeeeGaaahAAAAAAAAHHHHHHHHHHH!="); + + // no space between the tag and signer + checkFromText_LexerError( + "A 5 4 43200 20100223214617 20100222214617 " + "8496isc.org. ofc="); + + // unterminated multi-line base64 + checkFromText_LexerError( + "A 5 4 43200 20100223214617 20100222214617 8496 isc.org. " + "( evxhlGx13mpKLVkKsjpGzycS5twtIoxOmlN14w9t5AgzGBmz\n" + "diGdLIrFabqr72af2rUq+UDBKMWXujwZTZUTws32sVldDPk/\n" + "NbuacJM25fQXfv5mO3Af7TOoow3AjMaVG9icjCW0V55WcWQU\n" + "f49t+sXKPzbipN9g+s1ZPiIyofc="); +} + +TEST_F(Rdata_RRSIG_Test, createFromLexer) { + EXPECT_EQ(0, rdata_rrsig.compare( + *test::createRdataUsingLexer(RRType::RRSIG(), RRClass::IN(), + rrsig_txt))); + + // Exceptions cause NULL to be returned. + EXPECT_FALSE(test::createRdataUsingLexer(RRType::RRSIG(), RRClass::IN(), + "INVALIDINPUT")); +} + +TEST_F(Rdata_RRSIG_Test, toWireRenderer) { + rdata_rrsig.toWire(renderer); + + matchWireData(wiredata_rrsig, sizeof(wiredata_rrsig), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_RRSIG_Test, toWireBuffer) { + rdata_rrsig.toWire(obuffer); + + matchWireData(wiredata_rrsig, sizeof(wiredata_rrsig), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_RRSIG_Test, createFromWire) { + const string rrsig_txt2( + "A 5 2 43200 20100327070149 20100225070149 2658 isc.org. " + "HkJk/xZTvzePU8NENl/ley8bbUumhk1hXciyqhLnz1VQFzkDooej6neX" + "ZgWZzQKeTKPOYWrnYtdZW4PnPQFeUl3orgLev7F8J6FZlDn0y/J/ThR5" + "m36Mo2/Gdxjj8lJ/IjPVkdpKyBpcnYND8KEIma5MyNCNeyO1UkfPQZGHNSQ="); + EXPECT_EQ(rrsig_txt2, + rdataFactoryFromFile(RRType("RRSIG"), RRClass("IN"), + "rdata_rrsig_fromWire1")->toText()); + const generic::RRSIG rdata_rrsig2(rrsig_txt2); + EXPECT_EQ(0, rdata_rrsig2.compare( + *rdataFactoryFromFile(RRType("RRSIG"), RRClass("IN"), + "rdata_rrsig_fromWire1"))); + + // RDLEN is too short + EXPECT_THROW(rdataFactoryFromFile(RRType::RRSIG(), RRClass::IN(), + "rdata_rrsig_fromWire2.wire"), + InvalidRdataLength); +} +} diff --git a/src/lib/dns/tests/rdata_soa_unittest.cc b/src/lib/dns/tests/rdata_soa_unittest.cc index 8b13789179..21f4dc4854 100644 --- a/src/lib/dns/tests/rdata_soa_unittest.cc +++ b/src/lib/dns/tests/rdata_soa_unittest.cc @@ -1 +1,249 @@ +// Copyright (C) 2010-2017 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class Rdata_SOA_Test : public RdataTest { +protected: + Rdata_SOA_Test() : + rdata_soa(Name("ns.example.com"), + Name("root.example.com"), + 2010012601, 3600, 300, 3600000, 1200) + {} + + template <typename ExForString, typename ExForLexer> + void checkFromTextSOA(const string& soa_txt, const Name* origin = NULL, + bool throw_str_version = true, + bool throw_lexer_version = true) + { + checkFromText<generic::SOA, ExForString, ExForLexer>( + soa_txt, rdata_soa, throw_str_version, throw_lexer_version, + origin); + } + + const generic::SOA rdata_soa; +}; + +TEST_F(Rdata_SOA_Test, createFromText) { + // Below we specify isc::Exception as a dummy value for the exception type + // in case it's not expected to throw an exception; the type isn't used + // in the check code. + + // A simple case. + checkFromTextSOA<isc::Exception, isc::Exception>( + "ns.example.com. root.example.com. 2010012601 3600 300 3600000 1200", + NULL, false, false); + + // Beginning and trailing space are ignored. + checkFromTextSOA<isc::Exception, isc::Exception>( + " ns.example.com. root.example.com. " + "2010012601 3600 300 3600000 1200 ", NULL, false, false); + + // using extended TTL-like form for some parameters. + checkFromTextSOA<isc::Exception, isc::Exception>( + "ns.example.com. root.example.com. 2010012601 1H 5M 1000H 20M", + NULL, false, false); + + // multi-line. + checkFromTextSOA<isc::Exception, isc::Exception>( + "ns.example.com. (root.example.com.\n" + "2010012601 1H 5M 1000H) 20M", NULL, false, false); + + // relative names for MNAME and RNAME with a separate origin (lexer + // version only) + const Name origin("example.com"); + checkFromTextSOA<MissingNameOrigin, isc::Exception>( + "ns root 2010012601 1H 5M 1000H 20M", &origin, true, false); + + // with the '@' notation with a separate origin (lexer version only; + // string version would throw) + const Name full_mname("ns.example.com"); + checkFromTextSOA<MissingNameOrigin, isc::Exception>( + "@ root.example.com. 2010012601 1H 5M 1000H 20M", &full_mname, true, + false); + + // bad MNAME/RNAMEs + checkFromTextSOA<EmptyLabel, EmptyLabel>( + "bad..example. . 2010012601 1H 5M 1000H 20M"); + checkFromTextSOA<EmptyLabel, EmptyLabel>( + ". bad..example. 2010012601 1H 5M 1000H 20M"); + + // Names shouldn't be quoted. + checkFromTextSOA<InvalidRdataText, MasterLexer::LexerError>( + "\".\" . 0 0 0 0 0"); + checkFromTextSOA<InvalidRdataText, MasterLexer::LexerError>( + ". \".\" 0 0 0 0 0"); + + // Missing MAME or RNAME: for the string version, the serial would be + // tried as RNAME and result in "not absolute". For the lexer version, + // it reaches the end-of-line, missing min TTL. + checkFromTextSOA<MissingNameOrigin, MasterLexer::LexerError>( + ". 2010012601 0 0 0 0", &Name::ROOT_NAME()); + + // bad serial. the string version converts lexer error to + // InvalidRdataText. + checkFromTextSOA<InvalidRdataText, MasterLexer::LexerError>( + ". . bad 0 0 0 0"); + + // bad serial; exceeding the uint32_t range (4294967296 = 2^32) + checkFromTextSOA<InvalidRdataText, MasterLexer::LexerError>( + ". . 4294967296 0 0 0 0"); + + // Bad format for other numeric parameters. These will be tried as a TTL, + // and result in an exception there. + checkFromTextSOA<InvalidRRTTL, InvalidRRTTL>( + ". . 2010012601 bad 0 0 0"); + checkFromTextSOA<InvalidRRTTL, InvalidRRTTL>( + ". . 2010012601 4294967296 0 0 0"); + checkFromTextSOA<InvalidRRTTL, InvalidRRTTL>( + ". . 2010012601 0 bad 0 0"); + checkFromTextSOA<InvalidRRTTL, InvalidRRTTL>( + ". . 2010012601 0 4294967296 0 0"); + checkFromTextSOA<InvalidRRTTL, InvalidRRTTL>( + ". . 2010012601 0 0 bad 0"); + checkFromTextSOA<InvalidRRTTL, InvalidRRTTL>( + ". . 2010012601 0 0 4294967296 0"); + checkFromTextSOA<InvalidRRTTL, InvalidRRTTL>( + ". . 2010012601 0 0 0 bad"); + checkFromTextSOA<InvalidRRTTL, InvalidRRTTL>( + ". . 2010012601 0 0 0 4294967296"); + + // No space between RNAME and serial. This case is the same as missing + // M/RNAME. + checkFromTextSOA<MissingNameOrigin, MasterLexer::LexerError>( + ". example.0 0 0 0 0", &Name::ROOT_NAME()); + + // Extra parameter. string version immediately detects the error. + // lexer version defers the check to the upper layer (we pass origin + // to skip the check with the string version). + checkFromTextSOA<InvalidRdataText, isc::Exception>( + "ns.example.com. root.example.com. 2010012601 1H 5M 1000H 20M " + "extra", &origin, true, false); + + // Likewise. Redundant newline is also considered an error. The lexer + // version accepts trailing newline, but not the beginning one (where + // the lexer expects a string excluding newline and EOF). + checkFromTextSOA<InvalidRdataText, isc::Exception>( + "ns.example.com. root.example.com. 2010012601 1H 5M 1000H 20M\n", + NULL, true, false); + checkFromTextSOA<InvalidRdataText, MasterLexer::LexerError>( + "\nns.example.com. root.example.com. 2010012601 1H 5M 1000H 20M", + NULL, true, true); +} + +TEST_F(Rdata_SOA_Test, createFromWire) { + EXPECT_EQ(0, rdata_soa.compare( + *rdataFactoryFromFile(RRType("SOA"), RRClass("IN"), + "rdata_soa_fromWire"))); + // TBD: more tests +} + +TEST_F(Rdata_SOA_Test, createFromLexer) { + EXPECT_EQ(0, rdata_soa.compare( + *test::createRdataUsingLexer(RRType::SOA(), RRClass::IN(), + "ns.example.com. root.example.com. " + "2010012601 3600 300 3600000 1200"))); +} + +TEST_F(Rdata_SOA_Test, toWireRenderer) { + renderer.skip(2); + rdata_soa.toWire(renderer); + + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_soa_fromWire", data); + matchWireData(&data[2], data.size() - 2, + static_cast<const uint8_t *>(renderer.getData()) + 2, + renderer.getLength() - 2); +} + +TEST_F(Rdata_SOA_Test, toWireBuffer) { + obuffer.skip(2); + rdata_soa.toWire(obuffer); + vector<unsigned char> data; + UnitTestUtil::readWireData("rdata_soa_toWireUncompressed.wire", data); + matchWireData(&data[2], data.size() - 2, + static_cast<const uint8_t *>(obuffer.getData()) + 2, + obuffer.getLength() - 2); +} + +TEST_F(Rdata_SOA_Test, toText) { + EXPECT_EQ("ns.example.com. root.example.com. " + "2010012601 3600 300 3600000 1200", rdata_soa.toText()); +} + +TEST_F(Rdata_SOA_Test, getSerial) { + EXPECT_EQ(2010012601, rdata_soa.getSerial().getValue()); +} + +TEST_F(Rdata_SOA_Test, getMinimum) { + EXPECT_EQ(1200, rdata_soa.getMinimum()); + + // Also check with a very large number (with the MSB being 1). + EXPECT_EQ(2154848336u, generic::SOA(Name("ns.example.com"), + Name("root.example.com"), + 0, 0, 0, 0, 0x80706050).getMinimum()); +} + +void +compareCheck(const generic::SOA& small, const generic::SOA& large) { + EXPECT_GT(0, small.compare(large)); + EXPECT_LT(0, large.compare(small)); +} + +TEST_F(Rdata_SOA_Test, compare) { + // Check simple equivalence + EXPECT_EQ(0, rdata_soa.compare(generic::SOA( + "ns.example.com. root.example.com. " + "2010012601 3600 300 3600000 1200"))); + // Check name comparison is case insensitive + EXPECT_EQ(0, rdata_soa.compare(generic::SOA( + "NS.example.com. root.EXAMPLE.com. " + "2010012601 3600 300 3600000 1200"))); + + // Check names are compared in the RDATA comparison semantics (different + // from DNSSEC ordering for owner names) + compareCheck(generic::SOA("a.example. . 0 0 0 0 0"), + generic::SOA("example. . 0 0 0 0 0")); + compareCheck(generic::SOA(". a.example. 0 0 0 0 0"), + generic::SOA(". example. 0 0 0 0 0")); + + // Compare other numeric fields: 1076895760 = 0x40302010, + // 270544960 = 0x10203040. These are chosen to make sure that machine + // endianness doesn't confuse the comparison results. + compareCheck(generic::SOA(". . 270544960 0 0 0 0"), + generic::SOA(". . 1076895760 0 0 0 0")); + compareCheck(generic::SOA(". . 0 270544960 0 0 0"), + generic::SOA(". . 0 1076895760 0 0 0")); + compareCheck(generic::SOA(". . 0 0 270544960 0 0"), + generic::SOA(". . 0 0 1076895760 0 0")); + compareCheck(generic::SOA(". . 0 0 0 270544960 0"), + generic::SOA(". . 0 0 0 1076895760 0")); + compareCheck(generic::SOA(". . 0 0 0 0 270544960"), + generic::SOA(". . 0 0 0 0 1076895760")); +} + +} diff --git a/src/lib/dns/tests/rdata_srv_unittest.cc b/src/lib/dns/tests/rdata_srv_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_srv_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_sshfp_unittest.cc b/src/lib/dns/tests/rdata_sshfp_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_sshfp_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_tkey_unittest.cc b/src/lib/dns/tests/rdata_tkey_unittest.cc index 8b13789179..8592a27a01 100644 --- a/src/lib/dns/tests/rdata_tkey_unittest.cc +++ b/src/lib/dns/tests/rdata_tkey_unittest.cc @@ -1 +1,450 @@ +// Copyright (C) 2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <string> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/time_utilities.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/tsigerror.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { + +class Rdata_TKEY_Test : public RdataTest { +protected: + Rdata_TKEY_Test() : + // no Key or Other Data + valid_text1("gss-tsig. 20210501120000 20210501130000 GSS-API NOERROR 0 0"), + // Key but no Other Data + valid_text2("GSS-TSIG. 20210501120000 20210501130000 GSS-API BADSIG " + "12 FAKEFAKEFAKEFAKE 0"), + // Key and Other Data + valid_text3("gss.tsig. 20210501120000 20210501130000 GSS-API BADSIG " + "12 FAKEFAKEFAKEFAKE 6 FAKEFAKE"), + // Key and Other Data (with Error that doesn't expect Other Data) + valid_text4("gss.tsig. 20210501120000 20210501130000 3 BADSIG 12 " + "FAKEFAKEFAKEFAKE 6 FAKEFAKE"), + // numeric error code + valid_text5("GSS-TSIG. 20210501120000 20210501130000 GSS-API 2845 12 " + "FAKEFAKEFAKEFAKE 0"), + // GSS-API mode + valid_text6("gss-tsig. 20210501120000 20210501130000 GSS-API 0 12 " + "FAKEFAKEFAKEFAKE 0"), + rdata_tkey(valid_text1) + {} + + void checkFromText_None(const string& rdata_str) { + checkFromText<generic::TKEY, isc::Exception, isc::Exception>( + rdata_str, rdata_tkey, false, false); + } + + void checkFromText_InvalidTime(const string& rdata_str) { + checkFromText<generic::TKEY, InvalidTime, InvalidTime>( + rdata_str, rdata_tkey, true, true); + } + + void checkFromText_InvalidText(const string& rdata_str) { + checkFromText<generic::TKEY, InvalidRdataText, InvalidRdataText>( + rdata_str, rdata_tkey, true, true); + } + + void checkFromText_BadValue(const string& rdata_str) { + checkFromText<generic::TKEY, BadValue, BadValue>( + rdata_str, rdata_tkey, true, true); + } + + void checkFromText_LexerError(const string& rdata_str) { + checkFromText + <generic::TKEY, InvalidRdataText, MasterLexer::LexerError>( + rdata_str, rdata_tkey, true, true); + } + + void checkFromText_TooLongLabel(const string& rdata_str) { + checkFromText<generic::TKEY, TooLongLabel, TooLongLabel>( + rdata_str, rdata_tkey, true, true); + } + + void checkFromText_EmptyLabel(const string& rdata_str) { + checkFromText<generic::TKEY, EmptyLabel, EmptyLabel>( + rdata_str, rdata_tkey, true, true); + } + + void checkFromText_BadString(const string& rdata_str) { + checkFromText + <generic::TKEY, InvalidRdataText, isc::Exception>( + rdata_str, rdata_tkey, true, false); + } + + template <typename Output> + void toWireCommonChecks(Output& output) const; + + const string valid_text1; + const string valid_text2; + const string valid_text3; + const string valid_text4; + const string valid_text5; + const string valid_text6; + vector<uint8_t> expect_data; + const generic::TKEY rdata_tkey; // commonly used test RDATA +}; + +TEST_F(Rdata_TKEY_Test, fromText) { + // normal case. it also tests getter methods. + EXPECT_EQ(Name("gss-tsig"), rdata_tkey.getAlgorithm()); + EXPECT_EQ(1619870400, rdata_tkey.getInception()); + EXPECT_EQ("20210501120000", rdata_tkey.getInceptionDate()); + EXPECT_EQ(1619874000, rdata_tkey.getExpire()); + EXPECT_EQ("20210501130000", rdata_tkey.getExpireDate()); + EXPECT_EQ(3, rdata_tkey.getMode()); + EXPECT_EQ(0, rdata_tkey.getError()); + EXPECT_EQ(0, rdata_tkey.getKeyLen()); + EXPECT_EQ(static_cast<void*>(0), rdata_tkey.getKey()); + EXPECT_EQ(0, rdata_tkey.getOtherLen()); + EXPECT_EQ(static_cast<void*>(0), rdata_tkey.getOtherData()); + + generic::TKEY tkey2(valid_text2); + EXPECT_EQ(12, tkey2.getKeyLen()); + EXPECT_EQ(TSIGError::BAD_SIG_CODE, tkey2.getError()); + + generic::TKEY tkey3(valid_text3); + EXPECT_EQ(6, tkey3.getOtherLen()); + + // The other data is unusual, but we don't reject it. + EXPECT_NO_THROW(generic::TKEY tkey4(valid_text4)); + + // numeric representation of TKEY error + generic::TKEY tkey5(valid_text5); + EXPECT_EQ(2845, tkey5.getError()); + + // symbolic representation of TKEY mode + generic::TKEY tkey6(valid_text6); + EXPECT_EQ(generic::TKEY::GSS_API_MODE, tkey6.getMode()); + + // not fully qualified algorithm name + generic::TKEY tkey1("gss-tsig 20210501120000 20210501130000 3 0 0 0"); + EXPECT_EQ(0, tkey1.compare(rdata_tkey)); + + // multi-line rdata + checkFromText_None("gss-tsig. ( 20210501120000 20210501130000 GSS-API \n" + "NOERROR 0 0 )"); +}; + +TEST_F(Rdata_TKEY_Test, badText) { + // too many fields + checkFromText_BadString(valid_text1 + " 0 0"); + // not enough fields + checkFromText_LexerError("foo 20210501120000 20210501130000 0 BADKEY"); + // bad domain name + checkFromText_TooLongLabel( + "0123456789012345678901234567890123456789012345678901234567890123" + " 20210501120000 20210501130000 0 0 0 0"); + checkFromText_EmptyLabel("foo..bar 20210501120000 20210501130000 0 0 0 0"); + // invalid inception (no digit) + checkFromText_InvalidTime("foo TIME 20210501130000 0 0 0 0"); + // invalid inception (bad format) + checkFromText_InvalidTime("foo 0 20210501130000 0 0 0 0"); + // invalid expire (no digit) + checkFromText_InvalidTime("foo 20210501120000 TIME 0 0 0 0"); + // invalid expire (bad format) + checkFromText_InvalidTime("foo 20210501120000 0 0 0 0 0"); + // Unknown mode + checkFromText_InvalidText("foo 20210501120000 20210501130000 TEST 0 0 0"); + // Numeric mode is is too large + checkFromText_InvalidText("foo 20210501120000 20210501130000 65536 0 0 0"); + // Numeric mode is negative + checkFromText_InvalidText("foo 20210501120000 20210501130000 -1 0 0 0 0"); + // Unknown error code + checkFromText_InvalidText("foo 20210501120000 20210501130000 0 TEST 0 0"); + // Numeric error code is too large + checkFromText_InvalidText("foo 20210501120000 20210501130000 0 65536 0 0"); + // Numeric error code is negative + checkFromText_InvalidText("foo 20210501120000 20210501130000 0 -1 0 0"); + // Key len is too large + checkFromText_InvalidText("foo 20210501120000 20210501130000 0 0 65536 0"); + // invalid Key len (negative) + checkFromText_LexerError("foo 20210501120000 20210501130000 0 0 -1 0"); + // invalid Key len (not a number) + checkFromText_LexerError("foo 20210501120000 20210501130000 0 0 MACSIZE 0"); + // Key len and Key mismatch + checkFromText_InvalidText("foo 20210501120000 20210501130000 0 0 9 FAKE 0"); + // Key is bad base64 + checkFromText_BadValue("foo 20210501120000 20210501130000 0 0 3 FAK= 0"); + // Other len is too large + checkFromText_InvalidText("foo 20210501120000 20210501130000 0 0 0 65536 FAKE"); + // Other len is negative + checkFromText_LexerError("foo 20210501120000 20210501130000 0 0 0 -1 FAKE"); + // invalid Other len + checkFromText_LexerError("foo 20210501120000 20210501130000 0 0 0 LEN FAKE"); + // Other len and data mismatch + checkFromText_InvalidText("foo 20210501120000 20210501130000 0 0 0 9 FAKE"); +} + +void +fromWireCommonChecks(const generic::TKEY& tkey) { + EXPECT_EQ(Name("gss-tsig"), tkey.getAlgorithm()); + EXPECT_EQ(1619870400, tkey.getInception()); + EXPECT_EQ("20210501120000", tkey.getInceptionDate()); + EXPECT_EQ(1619874000, tkey.getExpire()); + EXPECT_EQ("20210501130000", tkey.getExpireDate()); + EXPECT_EQ(3, tkey.getMode()); + EXPECT_EQ(0, tkey.getError()); + + vector<uint8_t> expect_key(32, 'x'); + matchWireData(&expect_key[0], expect_key.size(), + tkey.getKey(), tkey.getKeyLen()); + + EXPECT_EQ(0, tkey.getOtherLen()); + EXPECT_EQ(static_cast<const void*>(0), tkey.getOtherData()); +} + +TEST_F(Rdata_TKEY_Test, createFromWire) { + RdataPtr rdata(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire1.wire")); + fromWireCommonChecks(dynamic_cast<generic::TKEY&>(*rdata)); +} + +TEST_F(Rdata_TKEY_Test, createFromWireWithOtherData) { + RdataPtr rdata(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire2.wire")); + const generic::TKEY& tkey(dynamic_cast<generic::TKEY&>(*rdata)); + + vector<uint8_t> expect_key(32, 'x'); + matchWireData(&expect_key[0], expect_key.size(), + tkey.getKey(), tkey.getKeyLen()); + + vector<uint8_t> expect_data = { 'a', 'b', 'c', 'd', '0', '1', '2', '3' }; + matchWireData(&expect_data[0], expect_data.size(), + tkey.getOtherData(), tkey.getOtherLen()); +} + +TEST_F(Rdata_TKEY_Test, createFromWireWithoutKey) { + RdataPtr rdata(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire3.wire")); + const generic::TKEY& tkey(dynamic_cast<generic::TKEY&>(*rdata)); + EXPECT_EQ(0, tkey.getKeyLen()); + EXPECT_EQ(static_cast<const void*>(0), tkey.getKey()); + + vector<uint8_t> expect_data = { 'a', 'b', 'c', 'd', '0', '1', '2', '3' }; + matchWireData(&expect_data[0], expect_data.size(), + tkey.getOtherData(), tkey.getOtherLen()); +} + +TEST_F(Rdata_TKEY_Test, createFromWireWithCompression) { + RdataPtr rdata(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire4.wire", + // we need to skip the dummy name: + Name("gss-tsig").getLength())); + fromWireCommonChecks(dynamic_cast<generic::TKEY&>(*rdata)); +} + +TEST_F(Rdata_TKEY_Test, badFromWire) { + // RDLENGTH is too short: + EXPECT_THROW(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire5.wire"), + InvalidRdataLength); + // RDLENGTH is too long: + EXPECT_THROW(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire6.wire"), + InvalidRdataLength); + // Algorithm name is broken: + EXPECT_THROW(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire7.wire"), + DNSMessageFORMERR); + // Key length is bogus: + EXPECT_THROW(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire8.wire"), + InvalidBufferPosition); + // Other-data length is bogus: + EXPECT_THROW(rdataFactoryFromFile(RRType::TKEY(), RRClass::ANY(), + "rdata_tkey_fromWire9.wire"), + InvalidBufferPosition); +} + +TEST_F(Rdata_TKEY_Test, copyConstruct) { + const generic::TKEY copy(rdata_tkey); + EXPECT_EQ(0, copy.compare(rdata_tkey)); + + // Check the copied data is valid even after the original is deleted + generic::TKEY* copy2 = new generic::TKEY(rdata_tkey); + generic::TKEY copy3(*copy2); + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_tkey)); +} + +TEST_F(Rdata_TKEY_Test, createFromParams) { + EXPECT_EQ(0, rdata_tkey.compare(generic::TKEY(Name("gss-tsig"), + 1619870400, + 1619874000, + 3, 0, 0, 0, 0, 0))); + + const uint8_t fake_data[] = { 0x14, 0x02, 0x84, 0x14, 0x02, 0x84, + 0x14, 0x02, 0x84, 0x14, 0x02, 0x84 }; + EXPECT_EQ(0, generic::TKEY(valid_text2).compare( + generic::TKEY(Name("GSS-TSIG"), 1619870400, 1619874000, + 3, 16, 12, fake_data, 0, 0))); + + const uint8_t fake_data2[] = { 0x14, 0x02, 0x84, 0x14, 0x02, 0x84 }; + EXPECT_EQ(0, generic::TKEY(valid_text3).compare( + generic::TKEY(Name("gss.tsig"), 1619870400, 1619874000, + 3, 16, 12, fake_data, 6, fake_data2))); + + EXPECT_THROW(generic::TKEY(Name("gss-tsig"), 0, 0, 0, 0, 0, fake_data, 0, 0), + isc::InvalidParameter); + EXPECT_THROW(generic::TKEY(Name("gss-tsig"), 0, 0, 0, 0, 12, 0, 0, 0), + isc::InvalidParameter); + EXPECT_THROW(generic::TKEY(Name("gss-tsig"), 0, 0, 0, 0, 0, 0, 0, fake_data), + isc::InvalidParameter); + EXPECT_THROW(generic::TKEY(Name("fake_data"), 0, 0, 0, 0, 0, 0, 6, 0), + isc::InvalidParameter); +} + +TEST_F(Rdata_TKEY_Test, assignment) { + generic::TKEY copy(valid_text2); + copy = rdata_tkey; + EXPECT_EQ(0, copy.compare(rdata_tkey)); + + // Check if the copied data is valid even after the original is deleted + generic::TKEY* copy2 = new generic::TKEY(rdata_tkey); + generic::TKEY copy3(valid_text2); + copy3 = *copy2; + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_tkey)); + + // Self assignment + copy = *© + EXPECT_EQ(0, copy.compare(rdata_tkey)); +} + +template <typename Output> +void +Rdata_TKEY_Test::toWireCommonChecks(Output& output) const { + vector<uint8_t> expect_data; + + output.clear(); + expect_data.clear(); + rdata_tkey.toWire(output); + // read the expected wire format data and trim the RDLEN part. + UnitTestUtil::readWireData("rdata_tkey_toWire1.wire", expect_data); + expect_data.erase(expect_data.begin(), expect_data.begin() + 2); + matchWireData(&expect_data[0], expect_data.size(), + output.getData(), output.getLength()); + + expect_data.clear(); + output.clear(); + generic::TKEY(valid_text2).toWire(output); + UnitTestUtil::readWireData("rdata_tkey_toWire2.wire", expect_data); + expect_data.erase(expect_data.begin(), expect_data.begin() + 2); + matchWireData(&expect_data[0], expect_data.size(), + output.getData(), output.getLength()); + + expect_data.clear(); + output.clear(); + generic::TKEY(valid_text3).toWire(output); + UnitTestUtil::readWireData("rdata_tkey_toWire3.wire", expect_data); + expect_data.erase(expect_data.begin(), expect_data.begin() + 2); + matchWireData(&expect_data[0], expect_data.size(), + output.getData(), output.getLength()); +} + +TEST_F(Rdata_TKEY_Test, toWireBuffer) { + toWireCommonChecks<OutputBuffer>(obuffer); +} + +TEST_F(Rdata_TKEY_Test, toWireRenderer) { + toWireCommonChecks<MessageRenderer>(renderer); + + // check algorithm name won't compressed when it would otherwise. + expect_data.clear(); + renderer.clear(); + renderer.writeName(Name("gss-tsig")); + renderer.writeUint16(26); // RDLEN + rdata_tkey.toWire(renderer); + UnitTestUtil::readWireData("rdata_tkey_toWire4.wire", expect_data); + matchWireData(&expect_data[0], expect_data.size(), + renderer.getData(), renderer.getLength()); + + // check algorithm can be used as a compression target. + expect_data.clear(); + renderer.clear(); + renderer.writeUint16(26); + rdata_tkey.toWire(renderer); + renderer.writeName(Name("gss-tsig")); + UnitTestUtil::readWireData("rdata_tkey_toWire5.wire", expect_data); + matchWireData(&expect_data[0], expect_data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_TKEY_Test, toText) { + EXPECT_EQ(valid_text1, rdata_tkey.toText()); + EXPECT_EQ(valid_text2, generic::TKEY(valid_text2).toText()); + EXPECT_EQ(valid_text3, generic::TKEY(valid_text3).toText()); + EXPECT_EQ(valid_text5, generic::TKEY(valid_text5).toText()); +} + +TEST_F(Rdata_TKEY_Test, compare) { + // test RDATAs, sorted in the ascending order. + // "AAAA" encoded in BASE64 corresponds to 0x000000, so it should be the + // smallest data of the same length. + vector<generic::TKEY> compare_set; + compare_set.push_back(generic::TKEY("a.example 20210501120000 " + "20210501130000 3 0 0 0")); + compare_set.push_back(generic::TKEY("example 20210501120000 " + "20210501130000 3 0 0 0")); + compare_set.push_back(generic::TKEY("example 20210501120001 " + "20210501130000 3 0 0 0")); + compare_set.push_back(generic::TKEY("example 20210501120001 " + "20210501130001 3 0 0 0")); + compare_set.push_back(generic::TKEY("example 20210501120001 " + "20210501130001 4 0 0 0")); + compare_set.push_back(generic::TKEY("example 20210501120001 " + "20210501130001 4 1 0 0")); + compare_set.push_back(generic::TKEY("example 20210501120001 " + "20210501130001 4 1 3 AAAA 0")); + compare_set.push_back(generic::TKEY("example 20210501120001 " + "20210501130001 4 1 3 FAKE 0")); + compare_set.push_back(generic::TKEY("example 20210501120001 " + "20210501130001 4 1 3 FAKE 3 AAAA")); + compare_set.push_back(generic::TKEY("example 20210501120001 " + "20210501130001 4 1 3 FAKE 3 FAKE")); + + EXPECT_EQ(0, + compare_set[0].compare(generic::TKEY("A.EXAMPLE 20210501120000 " + "20210501130000 3 0 0 0"))); + + vector<generic::TKEY>::const_iterator it; + vector<generic::TKEY>::const_iterator it_end = compare_set.end(); + for (it = compare_set.begin(); it != it_end - 1; ++it) { + EXPECT_GT(0, (*it).compare(*(it + 1))); + EXPECT_LT(0, (*(it + 1)).compare(*it)); + } + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_tkey.compare(*RdataTest::rdata_nomatch), bad_cast); +} +} diff --git a/src/lib/dns/tests/rdata_tlsa_unittest.cc b/src/lib/dns/tests/rdata_tlsa_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdata_tlsa_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rdata_tsig_unittest.cc b/src/lib/dns/tests/rdata_tsig_unittest.cc index 8b13789179..9d3bd89662 100644 --- a/src/lib/dns/tests/rdata_tsig_unittest.cc +++ b/src/lib/dns/tests/rdata_tsig_unittest.cc @@ -1 +1,423 @@ +// Copyright (C) 2010-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <string> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/tsigerror.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using namespace isc::dns::rdata::any; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { + +class Rdata_TSIG_Test : public RdataTest { +protected: + Rdata_TSIG_Test() : + // no MAC or Other Data + valid_text1("hmac-md5.sig-alg.reg.int. 1286779327 300 " + "0 16020 BADKEY 0"), + // MAC but no Other Data + valid_text2("hmac-sha256. 1286779327 300 12 " + "FAKEFAKEFAKEFAKE 16020 BADSIG 0"), + // MAC and Other Data + valid_text3("hmac-sha1. 1286779327 300 12 " + "FAKEFAKEFAKEFAKE 16020 BADTIME 6 FAKEFAKE"), + // MAC and Other Data (with Error that doesn't expect Other Data) + valid_text4("hmac-sha1. 1286779327 300 12 " + "FAKEFAKEFAKEFAKE 16020 BADSIG 6 FAKEFAKE"), + // numeric error code + valid_text5("hmac-sha256. 1286779327 300 12 " + "FAKEFAKEFAKEFAKE 16020 2845 0"), + rdata_tsig(valid_text1) + {} + + void checkFromText_None(const string& rdata_str) { + checkFromText<TSIG, isc::Exception, isc::Exception>( + rdata_str, rdata_tsig, false, false); + } + + void checkFromText_InvalidText(const string& rdata_str) { + checkFromText<TSIG, InvalidRdataText, InvalidRdataText>( + rdata_str, rdata_tsig, true, true); + } + + void checkFromText_BadValue(const string& rdata_str) { + checkFromText<TSIG, BadValue, BadValue>( + rdata_str, rdata_tsig, true, true); + } + + void checkFromText_LexerError(const string& rdata_str) { + checkFromText + <TSIG, InvalidRdataText, MasterLexer::LexerError>( + rdata_str, rdata_tsig, true, true); + } + + void checkFromText_TooLongLabel(const string& rdata_str) { + checkFromText<TSIG, TooLongLabel, TooLongLabel>( + rdata_str, rdata_tsig, true, true); + } + + void checkFromText_EmptyLabel(const string& rdata_str) { + checkFromText<TSIG, EmptyLabel, EmptyLabel>( + rdata_str, rdata_tsig, true, true); + } + + void checkFromText_BadString(const string& rdata_str) { + checkFromText + <TSIG, InvalidRdataText, isc::Exception>( + rdata_str, rdata_tsig, true, false); + } + + template <typename Output> + void toWireCommonChecks(Output& output) const; + + const string valid_text1; + const string valid_text2; + const string valid_text3; + const string valid_text4; + const string valid_text5; + vector<uint8_t> expect_data; + const TSIG rdata_tsig; // commonly used test RDATA +}; + +TEST_F(Rdata_TSIG_Test, fromText) { + // normal case. it also tests getter methods. + EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), rdata_tsig.getAlgorithm()); + EXPECT_EQ(1286779327, rdata_tsig.getTimeSigned()); + EXPECT_EQ(300, rdata_tsig.getFudge()); + EXPECT_EQ(0, rdata_tsig.getMACSize()); + EXPECT_EQ(static_cast<void*>(NULL), rdata_tsig.getMAC()); + EXPECT_EQ(16020, rdata_tsig.getOriginalID()); + EXPECT_EQ(TSIGError::BAD_KEY_CODE, rdata_tsig.getError()); + EXPECT_EQ(0, rdata_tsig.getOtherLen()); + EXPECT_EQ(static_cast<void*>(NULL), rdata_tsig.getOtherData()); + + TSIG tsig2(valid_text2); + EXPECT_EQ(12, tsig2.getMACSize()); + EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig2.getError()); + + TSIG tsig3(valid_text3); + EXPECT_EQ(6, tsig3.getOtherLen()); + + // The other data is unusual, but we don't reject it. + EXPECT_NO_THROW(TSIG tsig4(valid_text4)); + + // numeric representation of TSIG error + TSIG tsig5(valid_text5); + EXPECT_EQ(2845, tsig5.getError()); + + // not fully qualified algorithm name + TSIG tsig1("hmac-md5.sig-alg.reg.int 1286779327 300 0 16020 BADKEY 0"); + EXPECT_EQ(0, tsig1.compare(rdata_tsig)); + + // multi-line rdata + checkFromText_None("hmac-md5.sig-alg.reg.int. ( 1286779327 300 \n" + "0 16020 BADKEY 0 )"); + + // short-form HMAC-MD5 name + const TSIG tsig6("hmac-md5. 1286779327 300 0 16020 BADKEY 0"); + EXPECT_EQ(0, tsig6.compare(rdata_tsig)); +}; + +TEST_F(Rdata_TSIG_Test, badText) { + // too many fields + checkFromText_BadString(valid_text1 + " 0 0"); + // not enough fields + checkFromText_LexerError("foo 0 0 0 0 BADKEY"); + // bad domain name + checkFromText_TooLongLabel( + "0123456789012345678901234567890123456789012345678901234567890123" + " 0 0 0 0 BADKEY 0"); + checkFromText_EmptyLabel("foo..bar 0 0 0 0 BADKEY"); + // time is too large (2814...6 is 2^48) + checkFromText_InvalidText("foo 281474976710656 0 0 0 BADKEY 0"); + // invalid time (negative) + checkFromText_InvalidText("foo -1 0 0 0 BADKEY 0"); + // invalid time (not a number) + checkFromText_InvalidText("foo TIME 0 0 0 BADKEY 0"); + // fudge is too large + checkFromText_InvalidText("foo 0 65536 0 0 BADKEY 0"); + // invalid fudge (negative) + checkFromText_LexerError("foo 0 -1 0 0 BADKEY 0"); + // invalid fudge (not a number) + checkFromText_LexerError("foo 0 FUDGE 0 0 BADKEY 0"); + // MAC size is too large + checkFromText_InvalidText("foo 0 0 65536 0 BADKEY 0"); + // invalid MAC size (negative) + checkFromText_LexerError("foo 0 0 -1 0 BADKEY 0"); + // invalid MAC size (not a number) + checkFromText_LexerError("foo 0 0 MACSIZE 0 BADKEY 0"); + // MAC size and MAC mismatch + checkFromText_InvalidText("foo 0 0 9 FAKE 0 BADKEY 0"); + // MAC is bad base64 + checkFromText_BadValue("foo 0 0 3 FAK= 0 BADKEY 0"); + // Unknown error code + checkFromText_InvalidText("foo 0 0 0 0 TEST 0"); + // Numeric error code is too large + checkFromText_InvalidText("foo 0 0 0 0 65536 0"); + // Numeric error code is negative + checkFromText_InvalidText("foo 0 0 0 0 -1 0"); + // Other len is too large + checkFromText_InvalidText("foo 0 0 0 0 NOERROR 65536 FAKE"); + // Other len is negative + checkFromText_LexerError("foo 0 0 0 0 NOERROR -1 FAKE"); + // invalid Other len + checkFromText_LexerError("foo 0 0 0 0 NOERROR LEN FAKE"); + // Other len and data mismatch + checkFromText_InvalidText("foo 0 0 0 0 NOERROR 9 FAKE"); +} + +void +fromWireCommonChecks(const TSIG& tsig) { + EXPECT_EQ(Name("hmac-sha256"), tsig.getAlgorithm()); + EXPECT_EQ(1286978795, tsig.getTimeSigned()); + EXPECT_EQ(300, tsig.getFudge()); + + vector<uint8_t> expect_mac(32, 'x'); + matchWireData(&expect_mac[0], expect_mac.size(), + tsig.getMAC(), tsig.getMACSize()); + + EXPECT_EQ(2845, tsig.getOriginalID()); + + EXPECT_EQ(0, tsig.getOtherLen()); + EXPECT_EQ(static_cast<const void*>(NULL), tsig.getOtherData()); +} + +TEST_F(Rdata_TSIG_Test, createFromWire) { + RdataPtr rdata(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire1.wire")); + fromWireCommonChecks(dynamic_cast<TSIG&>(*rdata)); +} + +TEST_F(Rdata_TSIG_Test, createFromWireWithOtherData) { + RdataPtr rdata(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire2.wire")); + const TSIG& tsig(dynamic_cast<TSIG&>(*rdata)); + + EXPECT_EQ(18, tsig.getError()); + const uint64_t otherdata = 1286978795 + 300 + 1; // time-signed + fudge + 1 + expect_data.resize(6); + expect_data[0] = (otherdata >> 40); + expect_data[1] = ((otherdata >> 32) & 0xff); + expect_data[2] = ((otherdata >> 24) & 0xff); + expect_data[3] = ((otherdata >> 16) & 0xff); + expect_data[4] = ((otherdata >> 8) & 0xff); + expect_data[5] = (otherdata & 0xff); + matchWireData(&expect_data[0], expect_data.size(), + tsig.getOtherData(), tsig.getOtherLen()); +} + +TEST_F(Rdata_TSIG_Test, createFromWireWithoutMAC) { + RdataPtr rdata(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire3.wire")); + const TSIG& tsig(dynamic_cast<TSIG&>(*rdata)); + EXPECT_EQ(16, tsig.getError()); + EXPECT_EQ(0, tsig.getMACSize()); + EXPECT_EQ(static_cast<const void*>(NULL), tsig.getMAC()); +} + +TEST_F(Rdata_TSIG_Test, createFromWireWithCompression) { + RdataPtr rdata(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire4.wire", + // we need to skip the dummy name: + Name("hmac-sha256").getLength())); + fromWireCommonChecks(dynamic_cast<TSIG&>(*rdata)); +} + +TEST_F(Rdata_TSIG_Test, badFromWire) { + // RDLENGTH is too short: + EXPECT_THROW(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire5.wire"), + InvalidRdataLength); + // RDLENGTH is too long: + EXPECT_THROW(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire6.wire"), + InvalidRdataLength); + // Algorithm name is broken: + EXPECT_THROW(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire7.wire"), + DNSMessageFORMERR); + // MAC size is bogus: + EXPECT_THROW(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire8.wire"), + InvalidBufferPosition); + // Other-data length is bogus: + EXPECT_THROW(rdataFactoryFromFile(RRType::TSIG(), RRClass::ANY(), + "rdata_tsig_fromWire9.wire"), + InvalidBufferPosition); +} + +TEST_F(Rdata_TSIG_Test, copyConstruct) { + const TSIG copy(rdata_tsig); + EXPECT_EQ(0, copy.compare(rdata_tsig)); + + // Check the copied data is valid even after the original is deleted + TSIG* copy2 = new TSIG(rdata_tsig); + TSIG copy3(*copy2); + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_tsig)); +} + +TEST_F(Rdata_TSIG_Test, createFromParams) { + EXPECT_EQ(0, rdata_tsig.compare(TSIG(Name("hmac-md5.sig-alg.reg.int"), + 1286779327, 300, 0, NULL, 16020, 17, 0, NULL))); + + const uint8_t fake_data[] = { 0x14, 0x02, 0x84, 0x14, 0x02, 0x84, + 0x14, 0x02, 0x84, 0x14, 0x02, 0x84 }; + EXPECT_EQ(0, TSIG(valid_text2).compare(TSIG(Name("hmac-sha256"), 1286779327, 300, 12, + fake_data, 16020, 16, 0, NULL))); + + const uint8_t fake_data2[] = { 0x14, 0x02, 0x84, 0x14, 0x02, 0x84 }; + EXPECT_EQ(0, TSIG(valid_text3).compare(TSIG(Name("hmac-sha1"), 1286779327, 300, 12, + fake_data, 16020, 18, 6, fake_data2))); + + EXPECT_THROW(TSIG(Name("hmac-sha256"), 1ULL << 48, 300, 12, fake_data, 16020, 18, 6, fake_data2), + isc::OutOfRange); + EXPECT_THROW(TSIG(Name("hmac-sha256"), 0, 300, 0, fake_data, 16020, 18, 0, NULL), + isc::InvalidParameter); + EXPECT_THROW(TSIG(Name("hmac-sha256"), 0, 300, 12, NULL, 16020, 18, 0, NULL), + isc::InvalidParameter); + EXPECT_THROW(TSIG(Name("hmac-sha256"), 0, 300, 0, NULL, 16020, 18, 0, fake_data), + isc::InvalidParameter); + EXPECT_THROW(TSIG(Name("hmac-sha256"), 0, 300, 0, NULL, 16020, 18, 6, NULL), + isc::InvalidParameter); +} + +TEST_F(Rdata_TSIG_Test, assignment) { + TSIG copy(valid_text2); + copy = rdata_tsig; + EXPECT_EQ(0, copy.compare(rdata_tsig)); + + // Check if the copied data is valid even after the original is deleted + TSIG* copy2 = new TSIG(rdata_tsig); + TSIG copy3(valid_text2); + copy3 = *copy2; + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_tsig)); + + // Self assignment + copy = *© + EXPECT_EQ(0, copy.compare(rdata_tsig)); +} + +template <typename Output> +void +Rdata_TSIG_Test::toWireCommonChecks(Output& output) const { + vector<uint8_t> expect_data; + + output.clear(); + expect_data.clear(); + rdata_tsig.toWire(output); + // read the expected wire format data and trim the RDLEN part. + UnitTestUtil::readWireData("rdata_tsig_toWire1.wire", expect_data); + expect_data.erase(expect_data.begin(), expect_data.begin() + 2); + matchWireData(&expect_data[0], expect_data.size(), + output.getData(), output.getLength()); + + expect_data.clear(); + output.clear(); + TSIG(valid_text2).toWire(output); + UnitTestUtil::readWireData("rdata_tsig_toWire2.wire", expect_data); + expect_data.erase(expect_data.begin(), expect_data.begin() + 2); + matchWireData(&expect_data[0], expect_data.size(), + output.getData(), output.getLength()); + + expect_data.clear(); + output.clear(); + TSIG(valid_text3).toWire(output); + UnitTestUtil::readWireData("rdata_tsig_toWire3.wire", expect_data); + expect_data.erase(expect_data.begin(), expect_data.begin() + 2); + matchWireData(&expect_data[0], expect_data.size(), + output.getData(), output.getLength()); +} + +TEST_F(Rdata_TSIG_Test, toWireBuffer) { + toWireCommonChecks<OutputBuffer>(obuffer); +} + +TEST_F(Rdata_TSIG_Test, toWireRenderer) { + toWireCommonChecks<MessageRenderer>(renderer); + + // check algorithm name won't compressed when it would otherwise. + expect_data.clear(); + renderer.clear(); + renderer.writeName(Name("hmac-md5.sig-alg.reg.int")); + renderer.writeUint16(42); // RDLEN + rdata_tsig.toWire(renderer); + UnitTestUtil::readWireData("rdata_tsig_toWire4.wire", expect_data); + matchWireData(&expect_data[0], expect_data.size(), + renderer.getData(), renderer.getLength()); + + // check algorithm can be used as a compression target. + expect_data.clear(); + renderer.clear(); + renderer.writeUint16(42); + rdata_tsig.toWire(renderer); + renderer.writeName(Name("hmac-md5.sig-alg.reg.int")); + UnitTestUtil::readWireData("rdata_tsig_toWire5.wire", expect_data); + matchWireData(&expect_data[0], expect_data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_TSIG_Test, toText) { + EXPECT_EQ(valid_text1, rdata_tsig.toText()); + EXPECT_EQ(valid_text2, TSIG(valid_text2).toText()); + EXPECT_EQ(valid_text3, TSIG(valid_text3).toText()); + EXPECT_EQ(valid_text5, TSIG(valid_text5).toText()); +} + +TEST_F(Rdata_TSIG_Test, compare) { + // test RDATAs, sorted in the ascending order. + // "AAAA" encoded in BASE64 corresponds to 0x000000, so it should be the + // smallest data of the same length. + vector<TSIG> compare_set; + compare_set.push_back(TSIG("a.example 0 300 0 16020 0 0")); + compare_set.push_back(TSIG("example 0 300 0 16020 0 0")); + compare_set.push_back(TSIG("example 1 300 0 16020 0 0")); + compare_set.push_back(TSIG("example 1 600 0 16020 0 0")); + compare_set.push_back(TSIG("example 1 600 3 AAAA 16020 0 0")); + compare_set.push_back(TSIG("example 1 600 3 FAKE 16020 0 0")); + compare_set.push_back(TSIG("example 1 600 3 FAKE 16021 0 0")); + compare_set.push_back(TSIG("example 1 600 3 FAKE 16021 1 0")); + compare_set.push_back(TSIG("example 1 600 3 FAKE 16021 1 3 AAAA")); + compare_set.push_back(TSIG("example 1 600 3 FAKE 16021 1 3 FAKE")); + + EXPECT_EQ(0, compare_set[0].compare(TSIG("A.EXAMPLE 0 300 0 16020 0 0"))); + + vector<TSIG>::const_iterator it; + vector<TSIG>::const_iterator it_end = compare_set.end(); + for (it = compare_set.begin(); it != it_end - 1; ++it) { + EXPECT_GT(0, (*it).compare(*(it + 1))); + EXPECT_LT(0, (*(it + 1)).compare(*it)); + } + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(rdata_tsig.compare(*RdataTest::rdata_nomatch), bad_cast); +} +} diff --git a/src/lib/dns/tests/rdata_txt_like_unittest.cc b/src/lib/dns/tests/rdata_txt_like_unittest.cc index 8b13789179..dcb5ac168b 100644 --- a/src/lib/dns/tests/rdata_txt_like_unittest.cc +++ b/src/lib/dns/tests/rdata_txt_like_unittest.cc @@ -1 +1,394 @@ +// Copyright (C) 2011-2024 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// This is the common code for TXT tests. + +#include <config.h> + +#include <util/buffer.h> +#include <dns/exceptions.h> +#include <dns/rdataclass.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> + +#include <util/unittests/wiredata.h> + +#include <gtest/gtest.h> + +#include <string> +#include <sstream> +#include <vector> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { + +template<class T> +class RRTYPE : public RRType { +public: + RRTYPE(); +}; + +template<> RRTYPE<generic::TXT>::RRTYPE() : RRType(RRType::TXT()) {} + +const uint8_t wiredata_txt_like[] = { + sizeof("Test-String") - 1, + 'T', 'e', 's', 't', '-', 'S', 't', 'r', 'i', 'n', 'g' +}; + +const uint8_t wiredata_nulltxt[] = { 0 }; + +template<class TXT_LIKE> +class Rdata_TXT_LIKE_Test : public RdataTest { +protected: + Rdata_TXT_LIKE_Test() : + wiredata_longesttxt(256, 'a'), + rdata_txt_like("Test-String"), + rdata_txt_like_empty("\"\""), + rdata_txt_like_quoted("\"Test-String\"") + { + wiredata_longesttxt[0] = 255; // adjust length + } + +protected: + vector<uint8_t> wiredata_longesttxt; + const TXT_LIKE rdata_txt_like; + const TXT_LIKE rdata_txt_like_empty; + const TXT_LIKE rdata_txt_like_quoted; +}; + +// The list of types we want to test. +typedef testing::Types<generic::TXT> Implementations; + +#ifdef TYPED_TEST_SUITE +TYPED_TEST_SUITE(Rdata_TXT_LIKE_Test, Implementations); +#else +TYPED_TEST_CASE(Rdata_TXT_LIKE_Test, Implementations); +#endif + +TYPED_TEST(Rdata_TXT_LIKE_Test, createFromText) { + // Below we check the behavior for the "from text" constructors, both + // from std::string and with MasterLexer. The underlying implementation + // is the same, so both should work exactly same, but we confirm both + // cases. + + const std::string multi_line = "(\n \"Test-String\" )"; + const std::string escaped_txt = "Test\\045Strin\\g"; + + // test input for the lexer version + std::stringstream ss; + ss << "Test-String\n"; + ss << "\"Test-String\"\n"; // explicitly surrounded by '"'s + ss << multi_line << "\n"; // multi-line text with () + ss << escaped_txt << "\n"; // using the two types of escape with '\' + ss << "\"\"\n"; // empty string (note: still valid char-str) + ss << string(255, 'a') << "\n"; // Longest possible character-string. + ss << string(256, 'a') << "\n"; // char-string too long + ss << "\"Test-String\\\"\n"; // unbalanced quote + ss << "\"Test-String\\\"\"\n"; + this->lexer.pushSource(ss); + + // commonly used Rdata to compare below, created from wire + ConstRdataPtr const rdata = + this->rdataFactoryFromFile(RRTYPE<TypeParam>(), + RRClass("IN"), "rdata_txt_fromWire1"); + + // normal case is covered in toWireBuffer. First check the std::string + // case, then with MasterLexer. For the latter, we need to read and skip + // '\n'. These apply to most of the other cases below. + EXPECT_EQ(0, this->rdata_txt_like.compare(*rdata)); + EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, + this->loader_cb).compare(*rdata)); + EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType()); + + // surrounding double-quotes shouldn't change the result. + EXPECT_EQ(0, this->rdata_txt_like_quoted.compare(*rdata)); + EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, + this->loader_cb).compare(*rdata)); + EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType()); + + // multi-line input with () + EXPECT_EQ(0, TypeParam(multi_line).compare(*rdata)); + EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, + this->loader_cb).compare(*rdata)); + EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType()); + + // for the same data using escape + EXPECT_EQ(0, TypeParam(escaped_txt).compare(*rdata)); + EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, + this->loader_cb).compare(*rdata)); + EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType()); + + // Null character-string. + this->obuffer.clear(); + TypeParam(string("\"\"")).toWire(this->obuffer); + matchWireData(wiredata_nulltxt, sizeof(wiredata_nulltxt), + this->obuffer.getData(), this->obuffer.getLength()); + + this->obuffer.clear(); + TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, this->loader_cb). + toWire(this->obuffer); + matchWireData(wiredata_nulltxt, sizeof(wiredata_nulltxt), + this->obuffer.getData(), this->obuffer.getLength()); + + EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType()); + + // Longest possible character-string. + this->obuffer.clear(); + TypeParam(string(255, 'a')).toWire(this->obuffer); + matchWireData(&this->wiredata_longesttxt[0], + this->wiredata_longesttxt.size(), + this->obuffer.getData(), this->obuffer.getLength()); + + this->obuffer.clear(); + TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, this->loader_cb). + toWire(this->obuffer); + matchWireData(&this->wiredata_longesttxt[0], + this->wiredata_longesttxt.size(), + this->obuffer.getData(), this->obuffer.getLength()); + + EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType()); + + // Too long text for a valid character-string. + EXPECT_THROW(TypeParam(string(256, 'a')), CharStringTooLong); + EXPECT_THROW(TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, + this->loader_cb), CharStringTooLong); + EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType()); + + // The escape character makes the double quote a part of character-string, + // so this is invalid input and should be rejected. + EXPECT_THROW(TypeParam("\"Test-String\\\""), InvalidRdataText); + EXPECT_THROW(TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, + this->loader_cb), MasterLexer::LexerError); + EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType()); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, createMultiStringsFromText) { + // Tests for "from text" variants construction with various forms of + // multi character-strings. + + std::vector<std::string > texts; + texts.push_back("\"Test-String\" \"Test-String\""); // most common form + texts.push_back("\"Test-String\"\"Test-String\""); // no space between'em + texts.push_back("\"Test-String\" Test-String"); // no '"' for one + texts.push_back("\"Test-String\"Test-String"); // and no space either + texts.push_back("Test-String \"Test-String\""); // no '"' for the other + texts.push_back("Test-String\"Test-String\""); // and no space either + + std::stringstream ss; + for (auto const& it : texts) { + ss << it << "\n"; + } + this->lexer.pushSource(ss); + + // The corresponding Rdata built from wire to compare in the checks below. + ConstRdataPtr const rdata = + this->rdataFactoryFromFile(RRTYPE<TypeParam>(), + RRClass("IN"), "rdata_txt_fromWire3.wire"); + + // Confirm we can construct the Rdata from the test text, both from + // std::string and with lexer, and that matches the from-wire data. + for (auto const& it : texts) { + SCOPED_TRACE(it); + EXPECT_EQ(0, TypeParam(it).compare(*rdata)); + + EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, + this->loader_cb).compare(*rdata)); + EXPECT_EQ(MasterToken::END_OF_LINE, + this->lexer.getNextToken().getType()); + } +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, createFromTextExtra) { + // This is for the std::string version only: the input must end with EOF; + // an extra new-line will result in an exception. + EXPECT_THROW(TypeParam("\"Test-String\"\n"), InvalidRdataText); + // Same if there's a space before '\n' + EXPECT_THROW(TypeParam("\"Test-String\" \n"), InvalidRdataText); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, fromTextEmpty) { + // If the input text doesn't contain any character-string, it should be + // rejected + EXPECT_THROW(TypeParam(""), InvalidRdataText); + EXPECT_THROW(TypeParam(" "), InvalidRdataText); // even with a space + EXPECT_THROW(TypeParam("(\n)"), InvalidRdataText); // or multi-line with () +} + +void +makeLargest(vector<uint8_t>& data) { + uint8_t ch = 0; + + // create 255 sets of character-strings, each of which has the longest + // length (255bytes string + 1-byte length field) + for (int i = 0; i < 255; ++i, ++ch) { + data.push_back(255); + data.insert(data.end(), 255, ch); + } + // the last character-string should be 255 bytes (including the one-byte + // length field) in length so that the total length should be in the range + // of 16-bit integers. + data.push_back(254); + data.insert(data.end(), 254, ch); + + assert(data.size() == 65535); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, createFromWire) { + EXPECT_EQ(0, this->rdata_txt_like.compare( + *this->rdataFactoryFromFile(RRTYPE<TypeParam>(), + RRClass("IN"), + "rdata_txt_fromWire1"))); + + // Empty character string + EXPECT_EQ(0, this->rdata_txt_like_empty.compare( + *this->rdataFactoryFromFile(RRTYPE<TypeParam>(), + RRClass("IN"), + "rdata_txt_fromWire2.wire"))); + + // Multiple character strings + this->obuffer.clear(); + this->rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"), + "rdata_txt_fromWire3.wire")->toWire(this->obuffer); + // the result should be 'wiredata_txt' repeated twice + vector<uint8_t> expected_data(wiredata_txt_like, wiredata_txt_like + + sizeof(wiredata_txt_like)); + expected_data.insert(expected_data.end(), wiredata_txt_like, + wiredata_txt_like + sizeof(wiredata_txt_like)); + matchWireData(&expected_data[0], expected_data.size(), + this->obuffer.getData(), this->obuffer.getLength()); + + // Largest length of data. There's nothing special, but should be + // constructed safely, and the content should be identical to the original + // data. + vector<uint8_t> largest_txt_like_data; + makeLargest(largest_txt_like_data); + InputBuffer ibuffer(&largest_txt_like_data[0], + largest_txt_like_data.size()); + TypeParam largest_txt_like(ibuffer, largest_txt_like_data.size()); + this->obuffer.clear(); + largest_txt_like.toWire(this->obuffer); + matchWireData(&largest_txt_like_data[0], largest_txt_like_data.size(), + this->obuffer.getData(), this->obuffer.getLength()); + + // rdlen parameter is out of range. This is a rare event because we'd + // normally call the constructor via a polymorphic wrapper, where the + // length is validated. But this should be checked explicitly. + InputBuffer ibuffer2(&largest_txt_like_data[0], + largest_txt_like_data.size()); + EXPECT_THROW(TypeParam(ibuffer2, 65536), InvalidRdataLength); + + // RDATA is empty, which is invalid for TXT_LIKE. + EXPECT_THROW(this->rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"), + "rdata_txt_fromWire4.wire"), + DNSMessageFORMERR); + + // character-string length is too large, which could cause overrun. + EXPECT_THROW(this->rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"), + "rdata_txt_fromWire5.wire"), + DNSMessageFORMERR); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, createFromLexer) { + EXPECT_EQ(0, this->rdata_txt_like.compare( + *test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(), + "Test-String"))); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, toWireBuffer) { + this->rdata_txt_like.toWire(this->obuffer); + matchWireData(wiredata_txt_like, sizeof(wiredata_txt_like), + this->obuffer.getData(), this->obuffer.getLength()); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, toWireRenderer) { + this->rdata_txt_like.toWire(this->renderer); + matchWireData(wiredata_txt_like, sizeof(wiredata_txt_like), + this->renderer.getData(), this->renderer.getLength()); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, toText) { + EXPECT_EQ("\"Test-String\"", this->rdata_txt_like.toText()); + EXPECT_EQ("\"\"", this->rdata_txt_like_empty.toText()); + EXPECT_EQ("\"Test-String\"", this->rdata_txt_like_quoted.toText()); + + // Check escape behavior + const TypeParam double_quotes("Test-String\"Test-String\""); + EXPECT_EQ("\"Test-String\" \"Test-String\"", double_quotes.toText()); + const TypeParam semicolon("Test-String\\;Test-String"); + EXPECT_EQ("\"Test-String\\;Test-String\"", semicolon.toText()); + const TypeParam backslash("Test-String\\\\Test-String"); + EXPECT_EQ("\"Test-String\\\\Test-String\"", backslash.toText()); + const TypeParam before_x20("Test-String\\031Test-String"); + EXPECT_EQ("\"Test-String\\031Test-String\"", before_x20.toText()); + const TypeParam from_x20_to_x7e("\"Test-String ~ Test-String\""); + EXPECT_EQ("\"Test-String ~ Test-String\"", from_x20_to_x7e.toText()); + const TypeParam from_x20_to_x7e_2("Test-String\\032\\126\\032Test-String"); + EXPECT_EQ("\"Test-String ~ Test-String\"", from_x20_to_x7e_2.toText()); + const TypeParam after_x7e("Test-String\\127Test-String"); + EXPECT_EQ("\"Test-String\\127Test-String\"", after_x7e.toText()); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, assignment) { + TypeParam rdata1("assignment1"); + TypeParam rdata2("assignment2"); + rdata1 = rdata2; + EXPECT_EQ(0, rdata2.compare(rdata1)); + + // Check if the copied data is valid even after the original is deleted + TypeParam* rdata3 = new TypeParam(rdata1); + TypeParam rdata4("assignment3"); + rdata4 = *rdata3; + delete rdata3; + EXPECT_EQ(0, rdata4.compare(rdata1)); + + // Self assignment + rdata2 = *&rdata2; + EXPECT_EQ(0, rdata2.compare(rdata1)); +} + +TYPED_TEST(Rdata_TXT_LIKE_Test, compare) { + string const txt1("aaaaaaaa"); + string const txt2("aaaaaaaaaa"); + string const txt3("bbbbbbbb"); + string const txt4(129, 'a'); + string const txt5(128, 'b'); + + EXPECT_EQ(TypeParam(txt1).compare(TypeParam(txt1)), 0); + + EXPECT_LT(TypeParam("\"\"").compare(TypeParam(txt1)), 0); + EXPECT_GT(TypeParam(txt1).compare(TypeParam("\"\"")), 0); + + EXPECT_LT(TypeParam(txt1).compare(TypeParam(txt2)), 0); + EXPECT_GT(TypeParam(txt2).compare(TypeParam(txt1)), 0); + + EXPECT_LT(TypeParam(txt1).compare(TypeParam(txt3)), 0); + EXPECT_GT(TypeParam(txt3).compare(TypeParam(txt1)), 0); + + // we're comparing the data raw, starting at the length octet, so a shorter + // string sorts before a longer one no matter the lexicopraphical order + EXPECT_LT(TypeParam(txt3).compare(TypeParam(txt2)), 0); + EXPECT_GT(TypeParam(txt2).compare(TypeParam(txt3)), 0); + + // to make sure the length octet compares unsigned + EXPECT_LT(TypeParam(txt1).compare(TypeParam(txt4)), 0); + EXPECT_GT(TypeParam(txt4).compare(TypeParam(txt1)), 0); + + EXPECT_LT(TypeParam(txt5).compare(TypeParam(txt4)), 0); + EXPECT_GT(TypeParam(txt4).compare(TypeParam(txt5)), 0); + + // comparison attempt between incompatible RR types should be rejected + EXPECT_THROW(TypeParam(txt1).compare(*this->rdata_nomatch), + bad_cast); +} + +} diff --git a/src/lib/dns/tests/rdata_unittest.cc b/src/lib/dns/tests/rdata_unittest.cc index 8b13789179..4bfa38c81b 100644 --- a/src/lib/dns/tests/rdata_unittest.cc +++ b/src/lib/dns/tests/rdata_unittest.cc @@ -1 +1,471 @@ +// Copyright (C) 2010-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <functional> +#include <iomanip> +#include <vector> +#include <string> +#include <sstream> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> + +#include <gtest/gtest.h> + +#include <dns/tests/unittest_util.h> +#include <dns/tests/rdata_unittest.h> + +#include <util/unittests/wiredata.h> + +#include <boost/lexical_cast.hpp> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; +namespace ph = std::placeholders; + +namespace isc { +namespace dns { +namespace rdata { + +namespace { +void +nullCallback(const std::string&, size_t, const std::string&) { +} +} + +RdataTest::RdataTest() : + obuffer(0), rdata_nomatch(createRdata(RRType(0), RRClass(1), "\\# 0")), + loader_cb(MasterLoaderCallbacks(nullCallback, nullCallback)) { +} + +RdataPtr +RdataTest::rdataFactoryFromFile(const RRType& rrtype, const RRClass& rrclass, + const char* datafile, size_t position) { + std::vector<unsigned char> data; + UnitTestUtil::readWireData(datafile, data); + + InputBuffer buffer(&data[0], data.size()); + buffer.setPosition(position); + + uint16_t rdlen = buffer.readUint16(); + return (createRdata(rrtype, rrclass, buffer, rdlen)); +} + +namespace test { + +RdataPtr +createRdataUsingLexer(const RRType& rrtype, const RRClass& rrclass, + const std::string& str) { + std::stringstream ss(str); + MasterLexer lexer; + lexer.pushSource(ss); + + MasterLoaderCallbacks callbacks(nullCallback, nullCallback); + const Name origin("example.org."); + + return (createRdata(rrtype, rrclass, lexer, &origin, + MasterLoader::MANY_ERRORS, callbacks)); +} + +} // end of namespace isc::dns::rdata::test + +// A mock class to check parameters passed via loader callbacks. Its callback +// records the passed parameters, allowing the test to check them later via +// the check() method. +class CreateRdataCallback { +public: + enum CallbackType { NONE, ERROR, WARN }; + CreateRdataCallback() : type_(NONE), line_(0) {} + void callback(CallbackType type, const string& source, size_t line, + const string& reason_txt) { + type_ = type; + source_ = source; + line_ = line; + reason_txt_ = reason_txt; + } + + void clear() { + type_ = NONE; + source_.clear(); + line_ = 0; + reason_txt_.clear(); + } + + // Return if callback is called since the previous call to clear(). + bool isCalled() const { return (type_ != NONE); } + + void check(const string& expected_srcname, size_t expected_line, + CallbackType expected_type, const string& expected_reason) + const + { + EXPECT_EQ(expected_srcname, source_); + EXPECT_EQ(expected_line, line_); + EXPECT_EQ(expected_type, type_); + EXPECT_EQ(expected_reason, reason_txt_); + } + +private: + CallbackType type_; + string source_; + size_t line_; + string reason_txt_; +}; + +// Test class/type-independent behavior of createRdata(). +TEST_F(RdataTest, createRdataWithLexer) { + const in::AAAA aaaa_rdata("2001:db8::1"); + + stringstream ss; + const string src_name = "stream-" + boost::lexical_cast<string>(&ss); + ss << aaaa_rdata.toText() << "\n"; // valid case + ss << aaaa_rdata.toText() << "; comment, should be ignored\n"; + ss << aaaa_rdata.toText() << " extra-token\n"; // extra token + ss << aaaa_rdata.toText() << " extra token\n"; // 2 extra tokens + ss << ")\n"; // causing lexer error in parsing the RDATA text + ss << "192.0.2.1\n"; // semantics error: IPv4 address is given for AAAA + ss << aaaa_rdata.toText(); // valid, but end with EOF, not EOL + lexer.pushSource(ss); + + CreateRdataCallback callback; + MasterLoaderCallbacks callbacks( + std::bind(&CreateRdataCallback::callback, &callback, + CreateRdataCallback::ERROR, ph::_1, ph::_2, ph::_3), + std::bind(&CreateRdataCallback::callback, &callback, + CreateRdataCallback::WARN, ph::_1, ph::_2, ph::_3)); + + size_t line = 0; + + // Valid case. + ++line; + ConstRdataPtr rdata = createRdata(RRType::AAAA(), RRClass::IN(), lexer, + NULL, MasterLoader::MANY_ERRORS, + callbacks); + EXPECT_EQ(0, aaaa_rdata.compare(*rdata)); + EXPECT_FALSE(callback.isCalled()); + + // Similar to the previous case, but RDATA is followed by a comment. + // It should cause any confusion. + ++line; + callback.clear(); + rdata = createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL, + MasterLoader::MANY_ERRORS, callbacks); + EXPECT_EQ(0, aaaa_rdata.compare(*rdata)); + EXPECT_FALSE(callback.isCalled()); + + // Broken RDATA text: extra token. createRdata() returns NULL, error + // callback is called. + ++line; + callback.clear(); + EXPECT_FALSE(createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL, + MasterLoader::MANY_ERRORS, callbacks)); + callback.check(src_name, line, CreateRdataCallback::ERROR, + "createRdata from text failed near 'extra-token': " + "extra input text"); + + // Similar to the previous case, but only the first extra token triggers + // callback. + ++line; + callback.clear(); + EXPECT_FALSE(createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL, + MasterLoader::MANY_ERRORS, callbacks)); + callback.check(src_name, line, CreateRdataCallback::ERROR, + "createRdata from text failed near 'extra': " + "extra input text"); + + // Lexer error will happen, corresponding error callback will be triggered. + ++line; + callback.clear(); + EXPECT_FALSE(createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL, + MasterLoader::MANY_ERRORS, callbacks)); + callback.check(src_name, line, CreateRdataCallback::ERROR, + "createRdata from text failed: unbalanced parentheses"); + + // Semantics level error will happen, corresponding error callback will be + // triggered. + ++line; + callback.clear(); + EXPECT_FALSE(createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL, + MasterLoader::MANY_ERRORS, callbacks)); + callback.check(src_name, line, CreateRdataCallback::ERROR, + "createRdata from text failed: Bad IN/AAAA RDATA text: " + "'192.0.2.1'"); + + // Input is valid and parse will succeed, but with a warning that the + // file is not ended with a newline. + ++line; + callback.clear(); + rdata = createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL, + MasterLoader::MANY_ERRORS, callbacks); + EXPECT_EQ(0, aaaa_rdata.compare(*rdata)); + callback.check(src_name, line, CreateRdataCallback::WARN, + "file does not end with newline"); +} + +TEST_F(RdataTest, getLength) { + const in::AAAA aaaa_rdata("2001:db8::1"); + EXPECT_EQ(16, aaaa_rdata.getLength()); + + const generic::TXT txt_rdata("Hello World"); + EXPECT_EQ(12, txt_rdata.getLength()); +} + +} +} +} + +namespace { + +// Wire-format data correspond to rdata_unknown. Note that it doesn't +// include RDLENGTH. +const uint8_t wiredata_unknown[] = { 0xa1, 0xb2, 0xc3, 0x0d }; + +class Rdata_Unknown_Test : public RdataTest { +public: + Rdata_Unknown_Test() : + // "Unknown" RR Type used for the test cases below. If/when we + // use this type number as a "well-known" (probably + // experimental) type, we'll need to renumber it. + unknown_rrtype(RRType(65000)), + rdata_unknowntxt("\\# 4 a1b2c30d"), + rdata_unknown(rdata_unknowntxt) + {} +protected: + static string getLongestRdataTxt(); + static void getLongestRdataWire(vector<uint8_t>& v); + + const RRType unknown_rrtype; + const std::string rdata_unknowntxt; + const generic::Generic rdata_unknown; +}; + +string +Rdata_Unknown_Test::getLongestRdataTxt() { + ostringstream oss; + + oss << "\\# " << MAX_RDLENGTH << " "; + oss.fill('0'); + oss << right << hex; + for (int i = 0; i < MAX_RDLENGTH; i++) { + oss << setw(2) << (i & 0xff); + } + + return (oss.str()); +} + +void +Rdata_Unknown_Test::getLongestRdataWire(vector<uint8_t>& v) { + unsigned char ch = 0; + for (int i = 0; i < MAX_RDLENGTH; ++i, ++ch) { + v.push_back(ch); + } +} + +TEST_F(Rdata_Unknown_Test, createFromText) { + // valid construction. This also tests a normal case of "FromWire". + EXPECT_EQ(0, generic::Generic("\\# 4 a1b2c30d").compare( + *rdataFactoryFromFile(unknown_rrtype, RRClass::IN(), + "rdata_unknown_fromWire"))); + // upper case hexadecimal digits should also be okay. + EXPECT_EQ(0, generic::Generic("\\# 4 A1B2C30D").compare( + *rdataFactoryFromFile(unknown_rrtype, RRClass::IN(), + "rdata_unknown_fromWire"))); + // 0-length RDATA should be accepted + EXPECT_EQ(0, generic::Generic("\\# 0").compare( + *rdataFactoryFromFile(unknown_rrtype, RRClass::IN(), + "rdata_unknown_fromWire", 6))); + // hex encoding can be space-separated + EXPECT_EQ(0, generic::Generic("\\# 4 a1 b2c30d").compare(rdata_unknown)); + EXPECT_EQ(0, generic::Generic("\\# 4 a1b2 c30d").compare(rdata_unknown)); + EXPECT_EQ(0, generic::Generic("\\# 4 a1 b2 c3 0d").compare(rdata_unknown)); + EXPECT_EQ(0, generic::Generic("\\# 4 a1\tb2c3 0d").compare(rdata_unknown)); + + // Max-length RDATA + vector<uint8_t> v; + getLongestRdataWire(v); + InputBuffer ibuffer(&v[0], v.size()); + EXPECT_EQ(0, generic::Generic(getLongestRdataTxt()).compare( + generic::Generic(ibuffer, v.size()))); + + // the length field must match the encoding data length. + EXPECT_THROW(generic::Generic("\\# 4 1080c0ff00"), InvalidRdataLength); + EXPECT_THROW(generic::Generic("\\# 5 1080c0ff"), InvalidRdataLength); + // RDATA encoding part must consist of an even number of hex digits. + EXPECT_THROW(generic::Generic("\\# 1 1"), InvalidRdataText); + EXPECT_THROW(generic::Generic("\\# 1 ax"), InvalidRdataText); + // the length should be 16-bit unsigned integer + EXPECT_THROW(generic::Generic("\\# 65536 a1b2c30d"), InvalidRdataLength); + EXPECT_THROW(generic::Generic("\\# -1 a1b2c30d"), InvalidRdataLength); + EXPECT_THROW(generic::Generic("\\# 1.1 a1"), InvalidRdataLength); + EXPECT_THROW(generic::Generic("\\# 0a 00010203040506070809"), + InvalidRdataLength); + // should reject if the special token is missing. + EXPECT_THROW(generic::Generic("4 a1b2c30d"), InvalidRdataText); + // the special token, the RDLENGTH and the data must be space separated. + EXPECT_THROW(generic::Generic("\\#0"), InvalidRdataText); + EXPECT_THROW(generic::Generic("\\# 1ff"), InvalidRdataLength); +} + +TEST_F(Rdata_Unknown_Test, createFromWire) { + // normal case (including 0-length data) is covered in createFromText. + + // buffer too short. the error should be detected in buffer read + EXPECT_THROW(rdataFactoryFromFile(unknown_rrtype, RRClass::IN(), + "rdata_unknown_fromWire", 8), + InvalidBufferPosition); + + // too large data + vector<uint8_t> v; + getLongestRdataWire(v); + v.push_back(0); // making it too long + InputBuffer ibuffer(&v[0], v.size()); + EXPECT_THROW(generic::Generic(ibuffer, v.size()), InvalidRdataLength); +} + +// The following 3 sets of tests check the behavior of createRdata() variants +// with the "unknown" RRtype. The result should be RRclass independent. +TEST_F(Rdata_Unknown_Test, createRdataFromString) { + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass::IN(), + rdata_unknowntxt))); + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass::CH(), + rdata_unknowntxt))); + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass("CLASS65000"), + rdata_unknowntxt))); +} + +TEST_F(Rdata_Unknown_Test, createRdataFromWire) { + InputBuffer ibuffer(wiredata_unknown, sizeof(wiredata_unknown)); + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass::IN(), + ibuffer, sizeof(wiredata_unknown)))); + + InputBuffer ibuffer2(wiredata_unknown, sizeof(wiredata_unknown)); + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass::CH(), + ibuffer2, sizeof(wiredata_unknown)))); + + InputBuffer ibuffer3(wiredata_unknown, sizeof(wiredata_unknown)); + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass(65000), + ibuffer3, sizeof(wiredata_unknown)))); +} + +TEST_F(Rdata_Unknown_Test, createRdataByCopy) { + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass::IN(), rdata_unknown))); + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass::CH(), rdata_unknown))); + EXPECT_EQ(0, rdata_unknown.compare( + *createRdata(unknown_rrtype, RRClass(65000), + rdata_unknown))); +} + +TEST_F(Rdata_Unknown_Test, copyConstruct) { + generic::Generic copy(rdata_unknown); + EXPECT_EQ(0, copy.compare(rdata_unknown)); + + // Check the copied data is valid even after the original is deleted + generic::Generic* copy2 = new generic::Generic(rdata_unknown); + generic::Generic copy3(*copy2); + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_unknown)); +} + +TEST_F(Rdata_Unknown_Test, assignment) { + generic::Generic copy("\\# 1 10"); + copy = rdata_unknown; + EXPECT_EQ(0, copy.compare(rdata_unknown)); + + // Check if the copied data is valid even after the original is deleted + generic::Generic* copy2 = new generic::Generic(rdata_unknown); + generic::Generic copy3("\\# 1 10"); + copy3 = *copy2; + delete copy2; + EXPECT_EQ(0, copy3.compare(rdata_unknown)); + + // Self assignment + copy = *© + EXPECT_EQ(0, copy.compare(rdata_unknown)); +} + +TEST_F(Rdata_Unknown_Test, toText) { + EXPECT_EQ(rdata_unknowntxt, rdata_unknown.toText()); + EXPECT_EQ(getLongestRdataTxt(), + generic::Generic(getLongestRdataTxt()).toText()); +} + +TEST_F(Rdata_Unknown_Test, toWireBuffer) { + rdata_unknown.toWire(obuffer); + matchWireData(wiredata_unknown, sizeof(wiredata_unknown), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(Rdata_Unknown_Test, toWireRenderer) { + rdata_unknown.toWire(renderer); + matchWireData(wiredata_unknown, sizeof(wiredata_unknown), + renderer.getData(), renderer.getLength()); +} + +TEST_F(Rdata_Unknown_Test, compare) { + // comparison as left-justified unsigned octet sequences: + // cppcheck-suppress uselessCallsCompare + EXPECT_EQ(0, rdata_unknown.compare(rdata_unknown)); + + generic::Generic rdata_unknown_small("\\# 4 00b2c3ff"); + EXPECT_GT(0, rdata_unknown_small.compare(rdata_unknown)); + EXPECT_LT(0, rdata_unknown.compare(rdata_unknown_small)); + + generic::Generic rdata_unknown_large("\\# 4 ffb2c300"); + EXPECT_LT(0, rdata_unknown_large.compare(rdata_unknown)); + EXPECT_GT(0, rdata_unknown.compare(rdata_unknown_large)); + + // the absence of an octet sorts before a zero octet. + generic::Generic rdata_unknown_short("\\# 3 a1b2c3"); + EXPECT_GT(0, rdata_unknown_short.compare(rdata_unknown)); + EXPECT_LT(0, rdata_unknown.compare(rdata_unknown_short)); +} + +TEST_F(Rdata_Unknown_Test, LeftShiftOperator) { + ostringstream oss; + oss << rdata_unknown; + EXPECT_EQ(rdata_unknown.toText(), oss.str()); +} + +// +// Tests for global utility functions +// +TEST_F(RdataTest, compareNames) { + Name small("a.example"); + Name large("example"); + + // Check the case where the order is different from the owner name + // comparison: + EXPECT_TRUE(small > large); + EXPECT_EQ(-1, compareNames(small, large)); + EXPECT_EQ(1, compareNames(large, small)); + + // Check case insensitive comparison: + Name small_upper("A.EXAMPLE"); + EXPECT_EQ(0, compareNames(small, small_upper)); + + // the absence of an octet sorts before a zero octet. + Name large2("a.example2"); + EXPECT_EQ(-1, compareNames(small, large2)); + EXPECT_EQ(1, compareNames(large2, small)); +} +} diff --git a/src/lib/dns/tests/rdata_unittest.h b/src/lib/dns/tests/rdata_unittest.h index 8b13789179..c412bbbc06 100644 --- a/src/lib/dns/tests/rdata_unittest.h +++ b/src/lib/dns/tests/rdata_unittest.h @@ -1 +1,87 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#ifndef RDATA_UNITTEST_H +#define RDATA_UNITTEST_H + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/rdata.h> +#include <dns/master_lexer.h> + +#include <gtest/gtest.h> + +#include <string> +#include <sstream> + +namespace isc { +namespace dns { +namespace rdata { +class RdataTest : public ::testing::Test { +protected: + RdataTest(); + static RdataPtr rdataFactoryFromFile(const RRType& rrtype, + const RRClass& rrclass, + const char* datafile, + size_t position = 0); + + // Common check to see the result of Rdata construction of given type + // (template parameter RdataType) either from std::string or with + // MasterLexer object. If it's expected to succeed the result should be + // identical to the commonly used test data (rdata_expected); otherwise it + // should result in the exception specified as the template parameter: + // ExForString for the string version, and ExForLexer for the lexer + // version. throw_str_version and throw_lexer_version are set to true + // iff the string/lexer version is expected to throw, respectively. + // Parameter origin can be set to non NULL for the origin parameter of + // the lexer version of Rdata constructor. + template <typename RdataType, typename ExForString, typename ExForLexer> + void checkFromText(const std::string& rdata_txt, + const RdataType& rdata_expected, + bool throw_str_version = true, + bool throw_lexer_version = true, + const Name* origin = NULL) { + SCOPED_TRACE(rdata_txt); + + if (throw_str_version) { + EXPECT_THROW(RdataType rdata(rdata_txt), ExForString); + } else { + EXPECT_EQ(0, RdataType(rdata_txt).compare(rdata_expected)); + } + + std::stringstream ss(rdata_txt); + MasterLexer lexer; + lexer.pushSource(ss); + if (throw_lexer_version) { + EXPECT_THROW(RdataType rdata(lexer, origin, MasterLoader::DEFAULT, + loader_cb), ExForLexer); + } else { + EXPECT_EQ(0, RdataType(lexer, origin, MasterLoader::DEFAULT, + loader_cb).compare(rdata_expected)); + } + } + + isc::util::OutputBuffer obuffer; + MessageRenderer renderer; + /// This is an RDATA object of some "unknown" RR type so that it can be + /// used to test the compare() method against a well-known RR type. + RdataPtr rdata_nomatch; + MasterLexer lexer; + MasterLoaderCallbacks loader_cb; +}; + +namespace test { +RdataPtr +createRdataUsingLexer(const RRType& rrtype, const RRClass& rrclass, + const std::string& str); +} + +} +} +} +#endif // RDATA_UNITTEST_H diff --git a/src/lib/dns/tests/rdatafields_unittest.cc b/src/lib/dns/tests/rdatafields_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rdatafields_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rrclass_unittest.cc b/src/lib/dns/tests/rrclass_unittest.cc index 8b13789179..54e616cd77 100644 --- a/src/lib/dns/tests/rrclass_unittest.cc +++ b/src/lib/dns/tests/rrclass_unittest.cc @@ -1 +1,174 @@ +// Copyright (C) 2010-2017 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <gtest/gtest.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rrclass.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +#include <boost/scoped_ptr.hpp> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using boost::scoped_ptr; +using isc::util::unittests::matchWireData; + +namespace { +class RRClassTest : public ::testing::Test { +protected: + RRClassTest() : obuffer(0) {} + + OutputBuffer obuffer; + MessageRenderer renderer; + + static RRClass rrclassFactoryFromWire(const char* datafile); + static const RRClass rrclass_1, rrclass_0x80, rrclass_0x800, + rrclass_0x8000, rrclass_max; + static const uint8_t wiredata[]; +}; + +const RRClass RRClassTest::rrclass_1(1); +const RRClass RRClassTest::rrclass_0x80(0x80); +const RRClass RRClassTest::rrclass_0x800(0x800); +const RRClass RRClassTest::rrclass_0x8000(0x8000); +const RRClass RRClassTest::rrclass_max(0xffff); +// This is wire-format data for the above sample RRClass rendered in the +// appearing order. +const uint8_t RRClassTest::wiredata[] = { 0x00, 0x01, 0x00, 0x80, 0x08, + 0x00, 0x80, 0x00, 0xff, 0xff }; + +RRClass +RRClassTest::rrclassFactoryFromWire(const char* datafile) { + std::vector<unsigned char> data; + UnitTestUtil::readWireData(datafile, data); + + InputBuffer buffer(&data[0], data.size()); + + return (RRClass(buffer)); +} + +TEST_F(RRClassTest, fromTextConstructor) { + EXPECT_EQ("IN", RRClass("IN").toText()); + EXPECT_EQ("CH", RRClass("CH").toText()); + + EXPECT_EQ("CLASS65535", RRClass("CLASS65535").toText()); + + // some uncommon cases: see the corresponding RRType tests. + EXPECT_EQ(53, RRClass("CLASS00053").getCode()); + EXPECT_THROW(RRClass("CLASS000053"), InvalidRRClass); + + // bogus CLASSnnn representations: should trigger an exception + EXPECT_THROW(RRClass("CLASS"), InvalidRRClass); + EXPECT_THROW(RRClass("CLASS-1"), InvalidRRClass); + EXPECT_THROW(RRClass("CLASSxxx"), InvalidRRClass); + EXPECT_THROW(RRClass("CLASS65536"), InvalidRRClass); + EXPECT_THROW(RRClass("CLASS6500x"), InvalidRRClass); + EXPECT_THROW(RRClass("CLASS65000 "), InvalidRRClass); +} + +TEST_F(RRClassTest, fromWire) { + EXPECT_EQ(0x1234, + rrclassFactoryFromWire("rrcode16_fromWire1").getCode()); + EXPECT_THROW(rrclassFactoryFromWire("rrcode16_fromWire2"), + IncompleteRRClass); +} + +TEST_F(RRClassTest, caseConstruct) { + EXPECT_EQ("IN", RRClass("in").toText()); + EXPECT_EQ("CH", RRClass("ch").toText()); + EXPECT_EQ("CLASS65535", RRClass("class65535").toText()); +} + +TEST_F(RRClassTest, toText) { + EXPECT_EQ("IN", RRClass(1).toText()); + EXPECT_EQ("CLASS65000", RRClass(65000).toText()); +} + +TEST_F(RRClassTest, createFromText) { + scoped_ptr<RRClass> chclass(RRClass::createFromText("CH")); + EXPECT_TRUE(chclass); + EXPECT_EQ("CH", chclass->toText()); + + scoped_ptr<RRClass> zzclass(RRClass::createFromText("ZZ")); + EXPECT_FALSE(zzclass); +} + +TEST_F(RRClassTest, toWireBuffer) { + rrclass_1.toWire(obuffer); + rrclass_0x80.toWire(obuffer); + rrclass_0x800.toWire(obuffer); + rrclass_0x8000.toWire(obuffer); + rrclass_max.toWire(obuffer); + + matchWireData(wiredata, sizeof (wiredata), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(RRClassTest, toWireRenderer) { + rrclass_1.toWire(renderer); + rrclass_0x80.toWire(renderer); + rrclass_0x800.toWire(renderer); + rrclass_0x8000.toWire(renderer); + rrclass_max.toWire(renderer); + + matchWireData(wiredata, sizeof (wiredata), + renderer.getData(), renderer.getLength()); +} + +TEST_F(RRClassTest, wellKnownClass) { + EXPECT_EQ(1, RRClass::IN().getCode()); + EXPECT_EQ("IN", RRClass::IN().toText()); +} + +TEST_F(RRClassTest, compare) { + EXPECT_TRUE(RRClass(1) == RRClass("IN")); + EXPECT_TRUE(RRClass(1).equals(RRClass("IN"))); + EXPECT_TRUE(RRClass(0).nequals(RRClass("IN"))); + + EXPECT_TRUE(RRClass("IN") < RRClass("CH")); + EXPECT_TRUE(RRClass(100) < RRClass(65535)); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(RRClassTest, LeftShiftOperator) { + ostringstream oss; + oss << RRClass::IN(); + EXPECT_EQ(RRClass::IN().toText(), oss.str()); +} + +// Below, we'll check definitions for all well-known RR classes; whether they +// are defined and have the correct parameter values. Test data are generated +// from the list available at: +// http://www.iana.org/assignments/dns-parameters/dns-parameters.xml +struct ClassParam { + const char* const txt; // "IN", "CH", etc + const uint16_t code; // 1, 3, + const RRClass& (*obj)(); // RRClass::IN(), etc +} known_classes[] = { + {"IN", 1, RRClass::IN}, {"CH", 3, RRClass::CH}, + {"NONE", 254, RRClass::NONE}, {"ANY", 255, RRClass::ANY}, + {NULL, 0, NULL} +}; + +TEST(RRClassConstTest, wellKnowns) { + for (int i = 0; known_classes[i].txt; ++i) { + SCOPED_TRACE("Checking well known RRClass: " + + string(known_classes[i].txt)); + EXPECT_EQ(known_classes[i].code, + RRClass(known_classes[i].txt).getCode()); + EXPECT_EQ(known_classes[i].code, + (*known_classes[i].obj)().getCode()); + } +} +} diff --git a/src/lib/dns/tests/rrcollator_unittest.cc b/src/lib/dns/tests/rrcollator_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rrcollator_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rrparamregistry_unittest.cc b/src/lib/dns/tests/rrparamregistry_unittest.cc index b928cc603f..a5e26656a2 100644 --- a/src/lib/dns/tests/rrparamregistry_unittest.cc +++ b/src/lib/dns/tests/rrparamregistry_unittest.cc @@ -1,3 +1,9 @@ +// Copyright (C) 2010-2020 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + #include <config.h> #include <string> @@ -18,10 +24,14 @@ using namespace std; using namespace isc::dns; -using namespace isc::util; using namespace isc::dns::rdata; +using namespace isc::util; namespace { +void +nullCallback(const std::string&, size_t, const std::string&) { +} + class RRParamRegistryTest : public ::testing::Test { protected: RRParamRegistryTest() @@ -155,7 +165,7 @@ createRdataHelper(const std::string& str) { MasterLexer lexer; lexer.pushSource(ss); - MasterLoaderCallbacks callbacks(MasterLoaderCallbacks::getNullCallbacks()); + MasterLoaderCallbacks callbacks(nullCallback, nullCallback); const Name origin("example.org."); return (rdf->create(lexer, &origin, diff --git a/src/lib/dns/tests/rrset_collection_unittest.cc b/src/lib/dns/tests/rrset_collection_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/rrset_collection_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tests/rrset_unittest.cc b/src/lib/dns/tests/rrset_unittest.cc index 8b13789179..447fed5085 100644 --- a/src/lib/dns/tests/rrset_unittest.cc +++ b/src/lib/dns/tests/rrset_unittest.cc @@ -1 +1,441 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/rrttl.h> +#include <dns/rrset.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +#include <gtest/gtest.h> + +#include <stdexcept> +#include <sstream> + +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class RRsetTest : public ::testing::Test { +protected: + RRsetTest() : buffer(0), + test_name("test.example.com"), + test_domain("example.com"), + test_nsname("ns.example.com"), + rrset_a(test_name, RRClass::IN(), RRType::A(), RRTTL(3600)), + rrset_a_empty(test_name, RRClass::IN(), RRType::A(), + RRTTL(3600)), + rrset_any_a_empty(test_name, RRClass::ANY(), RRType::A(), + RRTTL(3600)), + rrset_none_a_empty(test_name, RRClass::NONE(), RRType::A(), + RRTTL(3600)), + rrset_ns(test_domain, RRClass::IN(), RRType::NS(), + RRTTL(86400)), + rrset_ch_txt(test_domain, RRClass::CH(), RRType::TXT(), + RRTTL(0)) { + rrset_a.addRdata(in::A("192.0.2.1")); + rrset_a.addRdata(in::A("192.0.2.2")); + } + + OutputBuffer buffer; + MessageRenderer renderer; + Name test_name; + Name test_domain; + Name test_nsname; + RRset rrset_a; + RRset rrset_a_empty; + RRset rrset_any_a_empty; + RRset rrset_none_a_empty; + RRset rrset_ns; + RRset rrset_ch_txt; + std::vector<unsigned char> wiredata; + + // max number of Rdata objects added to a test RRset object. + // this is an arbitrary chosen limit, but should be sufficiently large + // in practice and reasonable even as an extreme test case. + static const int MAX_RDATA_COUNT = 100; +}; + +TEST_F(RRsetTest, getRdataCount) { + for (int i = 0; i < MAX_RDATA_COUNT; ++i) { + EXPECT_EQ(i, rrset_a_empty.getRdataCount()); + rrset_a_empty.addRdata(in::A("192.0.2.1")); + } +} + +TEST_F(RRsetTest, getName) { + EXPECT_EQ(test_name, rrset_a.getName()); + EXPECT_EQ(test_domain, rrset_ns.getName()); +} + +TEST_F(RRsetTest, getClass) { + EXPECT_EQ(RRClass("IN"), rrset_a.getClass()); + EXPECT_EQ(RRClass("CH"), rrset_ch_txt.getClass()); +} + +TEST_F(RRsetTest, getType) { + EXPECT_EQ(RRType("A"), rrset_a.getType()); + EXPECT_EQ(RRType("NS"), rrset_ns.getType()); + EXPECT_EQ(RRType("TXT"), rrset_ch_txt.getType()); +} + +TEST_F(RRsetTest, getTTL) { + EXPECT_EQ(RRTTL(3600), rrset_a.getTTL()); + EXPECT_EQ(RRTTL(86400), rrset_ns.getTTL()); + EXPECT_EQ(RRTTL(0), rrset_ch_txt.getTTL()); +} + +TEST_F(RRsetTest, setTTL) { + rrset_a.setTTL(RRTTL(86400)); + EXPECT_EQ(RRTTL(86400), rrset_a.getTTL()); + rrset_a.setTTL(RRTTL(0)); + EXPECT_EQ(RRTTL(0), rrset_a.getTTL()); +} + +TEST_F(RRsetTest, isSameKind) { + RRset rrset_w(test_name, RRClass::IN(), RRType::A(), RRTTL(3600)); + RRset rrset_x(test_name, RRClass::IN(), RRType::A(), RRTTL(3600)); + RRset rrset_y(test_name, RRClass::IN(), RRType::NS(), RRTTL(3600)); + RRset rrset_z(test_name, RRClass::CH(), RRType::A(), RRTTL(3600)); + RRset rrset_p(test_nsname, RRClass::IN(), RRType::A(), RRTTL(3600)); + + EXPECT_TRUE(rrset_w.isSameKind(rrset_w)); + EXPECT_TRUE(rrset_w.isSameKind(rrset_x)); + EXPECT_FALSE(rrset_w.isSameKind(rrset_y)); + EXPECT_FALSE(rrset_w.isSameKind(rrset_z)); + EXPECT_FALSE(rrset_w.isSameKind(rrset_p)); +} + +void +addRdataTestCommon(const RRset& rrset) { + ASSERT_EQ(2, rrset.getRdataCount()); + + RdataIteratorPtr it = rrset.getRdataIterator(); // cursor is set to the 1st + EXPECT_FALSE(it->isLast()); + EXPECT_EQ(0, it->getCurrent().compare(in::A("192.0.2.1"))); + it->next(); + EXPECT_FALSE(it->isLast()); + EXPECT_EQ(0, it->getCurrent().compare(in::A("192.0.2.2"))); + it->next(); + EXPECT_TRUE(it->isLast()); +} + +TEST_F(RRsetTest, addRdata) { + addRdataTestCommon(rrset_a); + + // Reference version of addRdata() doesn't allow to add a different + // type of Rdata. + EXPECT_THROW(rrset_a.addRdata(generic::NS(test_nsname)), std::bad_cast); +} + +TEST_F(RRsetTest, addRdataPtr) { + rrset_a_empty.addRdata(createRdata(rrset_a_empty.getType(), + rrset_a_empty.getClass(), + "192.0.2.1")); + rrset_a_empty.addRdata(createRdata(rrset_a_empty.getType(), + rrset_a_empty.getClass(), + "192.0.2.2")); + addRdataTestCommon(rrset_a_empty); +} + +TEST_F(RRsetTest, addRdataPtrMismatched) { + // Pointer version of addRdata() doesn't type check and does allow to + //add a different type of Rdata as a result. + + // Type mismatch + rrset_a_empty.addRdata(createRdata(RRType::NS(), RRClass::IN(), + "ns.example.com.")); + EXPECT_EQ(1, rrset_a_empty.getRdataCount()); + + // Class mismatch + rrset_ch_txt.addRdata(createRdata(RRType::TXT(), RRClass::IN(), + "Test String")); + EXPECT_EQ(1, rrset_ch_txt.getRdataCount()); +} + +TEST_F(RRsetTest, addRdataString) { + rrset_a_empty.addRdata("192.0.2.1"); + rrset_a_empty.addRdata("192.0.2.2"); + + addRdataTestCommon(rrset_a_empty); + + // String version of addRdata() will throw for bad RDATA for + // RRType::A(). + EXPECT_THROW(rrset_a_empty.addRdata("ns.example.com."), InvalidRdataText); + addRdataTestCommon(rrset_a_empty); +} + +TEST_F(RRsetTest, iterator) { + // Iterator for an empty RRset. + RdataIteratorPtr it = rrset_a_empty.getRdataIterator(); + EXPECT_TRUE(it->isLast()); + + // Normal case (already tested, but do it again just in case) + rrset_a_empty.addRdata(in::A("192.0.2.1")); + rrset_a_empty.addRdata(in::A("192.0.2.2")); + addRdataTestCommon(rrset_a_empty); + + // Rewind test: should be repeat the iteration by calling first(). + for (int i = 0; i < 2; ++i) { + it = rrset_a_empty.getRdataIterator(); + it->first(); + EXPECT_FALSE(it->isLast()); + it->next(); + EXPECT_FALSE(it->isLast()); + it->next(); + EXPECT_TRUE(it->isLast()); + } +} + +TEST_F(RRsetTest, toText) { + EXPECT_EQ("test.example.com. 3600 IN A 192.0.2.1\n" + "test.example.com. 3600 IN A 192.0.2.2\n", + rrset_a.toText()); + + // toText() cannot be performed for an empty RRset + EXPECT_THROW(rrset_a_empty.toText(), EmptyRRset); + + // Unless it is type ANY or NONE + EXPECT_EQ("test.example.com. 3600 ANY A\n", + rrset_any_a_empty.toText()); + EXPECT_EQ("test.example.com. 3600 NONE A\n", + rrset_none_a_empty.toText()); +} + +TEST_F(RRsetTest, getLength) { + // Empty RRset should throw + EXPECT_THROW(rrset_a_empty.getLength(), EmptyRRset); + + // Unless it is type ANY or NONE: + // test.example.com = 1 + 4 + 1 + 7 + 1 + 3 + 1 = 18 octets + // TYPE field = 2 octets + // CLASS field = 2 octets + // TTL field = 4 octets + // RDLENGTH field = 2 octets + // Total = 18 + 2 + 2 + 4 + 2 = 28 octets + EXPECT_EQ(28, rrset_any_a_empty.getLength()); + EXPECT_EQ(28, rrset_none_a_empty.getLength()); + + // RRset with single RDATA + // 28 (above) + 4 octets (A RDATA) = 32 octets + rrset_a_empty.addRdata(in::A("192.0.2.1")); + EXPECT_EQ(32, rrset_a_empty.getLength()); + + // 2 A RRs + rrset_a_empty.addRdata(in::A("192.0.2.2")); + EXPECT_EQ(32 + 32, rrset_a_empty.getLength()); +} + +TEST_F(RRsetTest, toWireBuffer) { + rrset_a.toWire(buffer); + + UnitTestUtil::readWireData("rrset_toWire1", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + buffer.getData(), buffer.getLength()); + + // toWire() cannot be performed for an empty RRset except when + // class=ANY or class=NONE. + buffer.clear(); + EXPECT_THROW(rrset_a_empty.toWire(buffer), EmptyRRset); + + // When class=ANY or class=NONE, toWire() can also be performed for + // an empty RRset. + buffer.clear(); + rrset_any_a_empty.toWire(buffer); + wiredata.clear(); + UnitTestUtil::readWireData("rrset_toWire3", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + buffer.getData(), buffer.getLength()); + + buffer.clear(); + rrset_none_a_empty.toWire(buffer); + wiredata.clear(); + UnitTestUtil::readWireData("rrset_toWire4", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + buffer.getData(), buffer.getLength()); +} + +TEST_F(RRsetTest, toWireRenderer) { + rrset_ns.addRdata(generic::NS(test_nsname)); + + rrset_a.toWire(renderer); + rrset_ns.toWire(renderer); + + UnitTestUtil::readWireData("rrset_toWire2", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); + + // toWire() cannot be performed for an empty RRset except when + // class=ANY or class=NONE. + renderer.clear(); + EXPECT_THROW(rrset_a_empty.toWire(renderer), EmptyRRset); + + // When class=ANY or class=NONE, toWire() can also be performed for + // an empty RRset. + renderer.clear(); + rrset_any_a_empty.toWire(renderer); + wiredata.clear(); + UnitTestUtil::readWireData("rrset_toWire3", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); + + renderer.clear(); + rrset_none_a_empty.toWire(renderer); + wiredata.clear(); + UnitTestUtil::readWireData("rrset_toWire4", wiredata); + matchWireData(&wiredata[0], wiredata.size(), + renderer.getData(), renderer.getLength()); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(RRsetTest, LeftShiftOperator) { + ostringstream oss; + oss << rrset_a; + EXPECT_EQ(rrset_a.toText(), oss.str()); +} + +class RRsetRRSIGTest : public ::testing::Test { +protected: + RRsetRRSIGTest() : test_name("test.example.com") + { + rrset_a = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::A(), RRTTL(3600))); + rrset_a->addRdata(in::A("192.0.2.1")); + rrset_a->addRdata(in::A("192.0.2.2")); + + rrset_aaaa = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::AAAA(), RRTTL(3600))); + rrset_aaaa->addRdata(in::AAAA("2001:db8::1234")); + + rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::RRSIG(), RRTTL(3600))); + rrset_rrsig->addRdata(generic::RRSIG("AAAA 5 3 7200 20100322084538 " + "20100220084538 1 example.com. " + "FAKEFAKEFAKEFAKE")); + rrset_aaaa->addRRsig(rrset_rrsig); + } + + const Name test_name; + RRsetPtr rrset_a; // A RRset with two RDATAs + RRsetPtr rrset_aaaa; // AAAA RRset with one RDATA with RRSIG + RRsetPtr rrset_rrsig; // RRSIG for the AAAA RRset +}; + +TEST_F(RRsetRRSIGTest, getRRsig) { + RRsetPtr sp = rrset_a->getRRsig(); + EXPECT_EQ(static_cast<void*>(NULL), sp.get()); + + sp = rrset_aaaa->getRRsig(); + EXPECT_NE(static_cast<void*>(NULL), sp.get()); +} + +TEST_F(RRsetRRSIGTest, addRRsig) { + RRsetPtr sp = rrset_a->getRRsig(); + EXPECT_EQ(static_cast<void*>(NULL), sp.get()); + + rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::RRSIG(), RRTTL(3600))); + // one signature algorithm (5 = RSA/SHA-1) + rrset_rrsig->addRdata(generic::RRSIG("A 5 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + // another signature algorithm (3 = DSA/SHA-1) + rrset_rrsig->addRdata(generic::RRSIG("A 3 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + rrset_a->addRRsig(rrset_rrsig); + + sp = rrset_a->getRRsig(); + EXPECT_NE(static_cast<void*>(NULL), sp.get()); + EXPECT_EQ(2, sp->getRdataCount()); + + // add to existing RRSIG + rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::RRSIG(), RRTTL(3600))); + // another signature algorithm (4 = ECC) + rrset_rrsig->addRdata(generic::RRSIG("A 4 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + rrset_a->addRRsig(rrset_rrsig); + EXPECT_EQ(3, sp->getRdataCount()); +} + +TEST_F(RRsetRRSIGTest, getRRsigDataCount) { + EXPECT_EQ(1, rrset_aaaa->getRRsigDataCount()); + EXPECT_EQ(0, rrset_a->getRRsigDataCount()); + + rrset_rrsig = RRsetPtr(new RRset(test_name, RRClass::IN(), + RRType::RRSIG(), RRTTL(3600))); + // one signature algorithm (5 = RSA/SHA-1) + rrset_rrsig->addRdata(generic::RRSIG("A 5 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + // another signature algorithm (3 = DSA/SHA-1) + rrset_rrsig->addRdata(generic::RRSIG("A 3 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + rrset_a->addRRsig(rrset_rrsig); + EXPECT_EQ(2, rrset_a->getRRsigDataCount()); + + rrset_a->removeRRsig(); + EXPECT_EQ(0, rrset_a->getRRsigDataCount()); +} + +TEST_F(RRsetRRSIGTest, toText) { + // toText() should also return the associated RRSIG. + EXPECT_EQ("test.example.com. 3600 IN AAAA 2001:db8::1234\n" + "test.example.com. 3600 IN RRSIG AAAA 5 3 7200 " + "20100322084538 20100220084538 1 example.com. FAKEFAKEFAKEFAKE\n", + rrset_aaaa->toText()); +} + +TEST_F(RRsetRRSIGTest, getLength) { + // A RR + // test.example.com = 1 + 4 + 1 + 7 + 1 + 3 + 1 = 18 octets + // TYPE field = 2 octets + // CLASS field = 2 octets + // TTL field = 4 octets + // RDLENGTH field = 2 octets + // A RDATA = 4 octets + // Total = 18 + 2 + 2 + 4 + 2 + 4 = 32 octets + + // 2 A RRs + EXPECT_EQ(32 + 32, rrset_a->getLength()); + + // RRSIG + // test.example.com = 1 + 4 + 1 + 7 + 1 + 3 + 1 = 18 octets + // TYPE field = 2 octets + // CLASS field = 2 octets + // TTL field = 4 octets + // RDLENGTH field = 2 octets + // RRSIG RDATA = 40 octets + // Total = 18 + 2 + 2 + 4 + 2 + 40 = 68 octets + RRsetPtr my_rrsig(new RRset(test_name, RRClass::IN(), + RRType::RRSIG(), RRTTL(3600))); + my_rrsig->addRdata(generic::RRSIG("A 4 3 3600 " + "20000101000000 20000201000000 " + "12345 example.com. FAKEFAKEFAKE")); + EXPECT_EQ(68, my_rrsig->getLength()); + + // RRset with attached RRSIG + rrset_a->addRRsig(my_rrsig); + + EXPECT_EQ(32 + 32 + 68, rrset_a->getLength()); +} +} diff --git a/src/lib/dns/tests/rrttl_unittest.cc b/src/lib/dns/tests/rrttl_unittest.cc index 8b13789179..3cada14cbc 100644 --- a/src/lib/dns/tests/rrttl_unittest.cc +++ b/src/lib/dns/tests/rrttl_unittest.cc @@ -1 +1,279 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <gtest/gtest.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rrttl.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +#include <boost/scoped_ptr.hpp> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using boost::scoped_ptr; +using isc::util::unittests::matchWireData; + +namespace { +class RRTTLTest : public ::testing::Test { +protected: + RRTTLTest() : obuffer(0) {} + + OutputBuffer obuffer; + MessageRenderer renderer; + + static RRTTL rrttlFactoryFromWire(const char* datafile); + static const RRTTL ttl_0, ttl_1h, ttl_1d, ttl_32bit, ttl_max; + static const RRTTL ttl_small, ttl_large; + static const uint8_t wiredata[20]; +}; + +const RRTTL RRTTLTest::ttl_0(0); +const RRTTL RRTTLTest::ttl_1h(3600); +const RRTTL RRTTLTest::ttl_1d(86400); +const RRTTL RRTTLTest::ttl_32bit(0x12345678); +const RRTTL RRTTLTest::ttl_max(0xffffffff); + +const RRTTL RRTTLTest::ttl_small(1); +const RRTTL RRTTLTest::ttl_large(0x80000001); +// This is wire-format data for the above sample RRTTLs rendered in the +// appearing order. +const uint8_t RRTTLTest::wiredata[20] = { 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0e, 0x10, + 0x00, 0x01, 0x51, 0x80, + 0x12, 0x34, 0x56, 0x78, + 0xff, 0xff, 0xff, 0xff }; + +RRTTL +RRTTLTest::rrttlFactoryFromWire(const char* datafile) { + std::vector<unsigned char> data; + UnitTestUtil::readWireData(datafile, data); + + InputBuffer buffer(&data[0], data.size()); + + return (RRTTL(buffer)); +} + +TEST_F(RRTTLTest, getValue) { + EXPECT_EQ(0, ttl_0.getValue()); + EXPECT_EQ(3600, ttl_1h.getValue()); + EXPECT_EQ(86400, ttl_1d.getValue()); + EXPECT_EQ(0x12345678, ttl_32bit.getValue()); + EXPECT_EQ(0xffffffff, ttl_max.getValue()); +} + +TEST_F(RRTTLTest, copyConstruct) { + const RRTTL ttl1(3600); + const RRTTL ttl2(ttl1); + EXPECT_EQ(ttl1.getValue(), ttl2.getValue()); +} + +TEST_F(RRTTLTest, fromText) { + // Border cases + EXPECT_EQ(0, RRTTL("0").getValue()); + EXPECT_EQ(4294967295U, RRTTL("4294967295").getValue()); + + // Invalid cases + EXPECT_THROW(RRTTL("0xdeadbeef"), InvalidRRTTL); // must be decimal + EXPECT_THROW(RRTTL("-1"), InvalidRRTTL); // must be positive + EXPECT_THROW(RRTTL("1.1"), InvalidRRTTL); // must be integer + EXPECT_THROW(RRTTL("4294967296"), InvalidRRTTL); // must be 32-bit +} + +TEST_F(RRTTLTest, createFromText) { + // It returns an actual RRTTL iff the given text is recognized as a + // valid RR TTL. + scoped_ptr<RRTTL> good_ttl(RRTTL::createFromText("3600")); + EXPECT_TRUE(good_ttl); + EXPECT_EQ(RRTTL(3600), *good_ttl); + + scoped_ptr<RRTTL> bad_ttl(RRTTL::createFromText("bad")); + EXPECT_FALSE(bad_ttl); +} + +void +checkUnit(unsigned multiply, char suffix) { + SCOPED_TRACE(string("Unit check with suffix ") + suffix); + const uint32_t value = 10 * multiply; + const string num = "10"; + // Check both lower and upper version of the suffix + EXPECT_EQ(value, + RRTTL(num + static_cast<char>(tolower(suffix))).getValue()); + EXPECT_EQ(value, + RRTTL(num + static_cast<char>(toupper(suffix))).getValue()); +} + +// Check parsing the unit form (1D, etc) +TEST_F(RRTTLTest, fromTextUnit) { + // Check each of the units separately + checkUnit(1, 'S'); + checkUnit(60, 'M'); + checkUnit(60 * 60, 'H'); + checkUnit(24 * 60 * 60, 'D'); + checkUnit(7 * 24 * 60 * 60, 'W'); + + // Some border cases (with units) + EXPECT_EQ(4294967295U, RRTTL("4294967295S").getValue()); + EXPECT_EQ(0, RRTTL("0W0D0H0M0S").getValue()); + EXPECT_EQ(4294967295U, RRTTL("1193046H1695S").getValue()); + // Leading zeroes are accepted + EXPECT_EQ(4294967295U, RRTTL("0000000000000004294967295S").getValue()); + + // Now some compound ones. We allow any order (it would be much work to + // check the order anyway). + EXPECT_EQ(60 * 60 + 3, RRTTL("1H3S").getValue()); + + // Awkward, but allowed case - the same unit used twice. + EXPECT_EQ(20 * 3600, RRTTL("12H8H").getValue()); + + // Negative number in part of the expression, but the total is positive. + // Rejected. + EXPECT_THROW(RRTTL("-1S1H"), InvalidRRTTL); + + // Some things out of range in the ttl, but it wraps to number in range + // in int64_t. Should still not get fooled and reject it. + + // First part out of range + EXPECT_THROW(RRTTL("9223372036854775807S9223372036854775807S2S"), + InvalidRRTTL); + // Second part out of range, but it immediately wraps (2S+2^64-2S) + EXPECT_THROW(RRTTL("2S18446744073709551614S"), InvalidRRTTL); + // The whole thing wraps right away (2^64S) + EXPECT_THROW(RRTTL("18446744073709551616S"), InvalidRRTTL); + // Second part out of range, and will become negative with the unit, + EXPECT_THROW(RRTTL("256S307445734561825856M"), InvalidRRTTL); + + // Missing before unit. + EXPECT_THROW(RRTTL("W5H"), InvalidRRTTL); + EXPECT_THROW(RRTTL("5hW"), InvalidRRTTL); + + // Empty string is not allowed + EXPECT_THROW(RRTTL(""), InvalidRRTTL); + // Missing the last unit is not allowed + EXPECT_THROW(RRTTL("3D5"), InvalidRRTTL); + + // There are some wrong units + EXPECT_THROW(RRTTL("13X"), InvalidRRTTL); + EXPECT_THROW(RRTTL("3D5F"), InvalidRRTTL); +} + +TEST_F(RRTTLTest, fromWire) { + EXPECT_EQ(0x12345678, + rrttlFactoryFromWire("rrcode32_fromWire1").getValue()); + EXPECT_THROW(rrttlFactoryFromWire("rrcode32_fromWire2"), + IncompleteRRTTL); +} + +TEST_F(RRTTLTest, toText) { + EXPECT_EQ("0", ttl_0.toText()); + EXPECT_EQ("3600", ttl_1h.toText()); + EXPECT_EQ("86400", ttl_1d.toText()); + EXPECT_EQ("305419896", ttl_32bit.toText()); + EXPECT_EQ("4294967295", ttl_max.toText()); +} + +TEST_F(RRTTLTest, toWireBuffer) { + ttl_0.toWire(obuffer); + ttl_1h.toWire(obuffer); + ttl_1d.toWire(obuffer); + ttl_32bit.toWire(obuffer); + ttl_max.toWire(obuffer); + + matchWireData(wiredata, sizeof(wiredata), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(RRTTLTest, toWireRenderer) { + ttl_0.toWire(renderer); + ttl_1h.toWire(renderer); + ttl_1d.toWire(renderer); + ttl_32bit.toWire(renderer); + ttl_max.toWire(renderer); + + matchWireData(wiredata, sizeof(wiredata), + renderer.getData(), renderer.getLength()); +} + +TEST_F(RRTTLTest, equal) { + EXPECT_TRUE(RRTTL("3600") == ttl_1h); + EXPECT_TRUE(RRTTL("86400").equals(ttl_1d)); + + EXPECT_TRUE(ttl_1d != ttl_1h); + EXPECT_TRUE(ttl_1d.nequals(ttl_max)); +} + +// +// The following set of tests confirm the result of <=, <, >=, > +// The test logic is simple, and all tests are just straightforward variations +// of the first one. +// +TEST_F(RRTTLTest, leq) { + // small <= large is true + EXPECT_TRUE(ttl_small.leq(ttl_large)); + EXPECT_TRUE(ttl_small <= ttl_large); + + // small <= small is true + EXPECT_TRUE(ttl_small.leq(ttl_small)); + EXPECT_LE(ttl_small, ttl_small); + + // large <= small is false + EXPECT_FALSE(ttl_large.leq(ttl_small)); + EXPECT_FALSE(ttl_large <= ttl_small); +} + +TEST_F(RRTTLTest, geq) { + EXPECT_TRUE(ttl_large.geq(ttl_small)); + EXPECT_TRUE(ttl_large >= ttl_small); + + EXPECT_TRUE(ttl_large.geq(ttl_large)); + EXPECT_GE(ttl_large, ttl_large); + + EXPECT_FALSE(ttl_small.geq(ttl_large)); + EXPECT_FALSE(ttl_small >= ttl_large); +} + +TEST_F(RRTTLTest, lthan) { + EXPECT_TRUE(ttl_small.lthan(ttl_large)); + EXPECT_TRUE(ttl_small < ttl_large); + + EXPECT_FALSE(ttl_small.lthan(ttl_small)); + // cppcheck-suppress duplicateExpression + EXPECT_FALSE(ttl_small < ttl_small); + + EXPECT_FALSE(ttl_large.lthan(ttl_small)); + EXPECT_FALSE(ttl_large < ttl_small); +} + +TEST_F(RRTTLTest, gthan) { + EXPECT_TRUE(ttl_large.gthan(ttl_small)); + EXPECT_TRUE(ttl_large > ttl_small); + + EXPECT_FALSE(ttl_large.gthan(ttl_large)); + // cppcheck-suppress duplicateExpression + EXPECT_FALSE(ttl_large > ttl_large); + + EXPECT_FALSE(ttl_small.gthan(ttl_large)); + EXPECT_FALSE(ttl_small > ttl_large); +} + +TEST_F(RRTTLTest, maxTTL) { + EXPECT_EQ((1u << 31) - 1, RRTTL::MAX_TTL().getValue()); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(RRTTLTest, LeftShiftOperator) { + ostringstream oss; + oss << ttl_1h; + EXPECT_EQ(ttl_1h.toText(), oss.str()); +} +} diff --git a/src/lib/dns/tests/rrtype_unittest.cc b/src/lib/dns/tests/rrtype_unittest.cc index 8b13789179..97975409b8 100644 --- a/src/lib/dns/tests/rrtype_unittest.cc +++ b/src/lib/dns/tests/rrtype_unittest.cc @@ -1 +1,170 @@ +// Copyright (C) 2010-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <gtest/gtest.h> + +#include <util/buffer.h> +#include <dns/messagerenderer.h> +#include <dns/rrtype.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using isc::util::unittests::matchWireData; + +namespace { +class RRTypeTest : public ::testing::Test { +protected: + RRTypeTest() : obuffer(0) {} + + OutputBuffer obuffer; + MessageRenderer renderer; + + static RRType rrtypeFactoryFromWire(const char* datafile); + static const RRType rrtype_1, rrtype_0x80, rrtype_0x800, rrtype_0x8000, + rrtype_max; + static const uint8_t wiredata[]; +}; + +const RRType RRTypeTest::rrtype_1(1); +const RRType RRTypeTest::rrtype_0x80(0x80); +const RRType RRTypeTest::rrtype_0x800(0x800); +const RRType RRTypeTest::rrtype_0x8000(0x8000); +const RRType RRTypeTest::rrtype_max(0xffff); +// This is wire-format data for the above sample RRTypes rendered in the +// appearing order. +const uint8_t RRTypeTest::wiredata[] = { 0x00, 0x01, 0x00, 0x80, 0x08, + 0x00, 0x80, 0x00, 0xff, 0xff }; + +RRType +RRTypeTest::rrtypeFactoryFromWire(const char* datafile) { + std::vector<unsigned char> data; + UnitTestUtil::readWireData(datafile, data); + + InputBuffer buffer(&data[0], data.size()); + + return (RRType(buffer)); +} + +TEST_F(RRTypeTest, fromText) { + EXPECT_EQ("A", RRType("A").toText()); + EXPECT_EQ("NS", RRType("NS").toText()); + + EXPECT_EQ("TYPE65535", RRType("TYPE65535").toText()); + + // something unusual, but existing implementations accept this form, + // so do we. + EXPECT_EQ(53, RRType("TYPE00053").getCode()); + // again, unusual, and the majority of other implementations reject it. + // In any case, there should be no reasonable reason to accept such a + // ridiculously long input. + EXPECT_THROW(RRType("TYPE000053"), InvalidRRType); + + // bogus TYPEnnn representations: should trigger an exception + EXPECT_THROW(RRType("TYPE"), InvalidRRType); + EXPECT_THROW(RRType("TYPE-1"), InvalidRRType); + EXPECT_THROW(RRType("TYPExxx"), InvalidRRType); + EXPECT_THROW(RRType("TYPE65536"), InvalidRRType); + EXPECT_THROW(RRType("TYPE6500x"), InvalidRRType); + EXPECT_THROW(RRType("TYPE65000 "), InvalidRRType); +} + +TEST_F(RRTypeTest, fromWire) { + EXPECT_EQ(0x1234, + rrtypeFactoryFromWire("rrcode16_fromWire1").getCode()); + EXPECT_THROW(rrtypeFactoryFromWire("rrcode16_fromWire2"), IncompleteRRType); +} + +// from string, lower case +TEST_F(RRTypeTest, caseConstruct) { + EXPECT_EQ("A", RRType("a").toText()); + EXPECT_EQ("NS", RRType("ns").toText()); + EXPECT_EQ("TYPE65535", RRType("type65535").toText()); +} + +TEST_F(RRTypeTest, toText) { + EXPECT_EQ("A", RRType(1).toText()); + EXPECT_EQ("TYPE65000", RRType(65000).toText()); +} + +TEST_F(RRTypeTest, toWireBuffer) { + rrtype_1.toWire(obuffer); + rrtype_0x80.toWire(obuffer); + rrtype_0x800.toWire(obuffer); + rrtype_0x8000.toWire(obuffer); + rrtype_max.toWire(obuffer); + + matchWireData(wiredata, sizeof(wiredata), + obuffer.getData(), obuffer.getLength()); +} + +TEST_F(RRTypeTest, toWireRenderer) { + rrtype_1.toWire(renderer); + rrtype_0x80.toWire(renderer); + rrtype_0x800.toWire(renderer); + rrtype_0x8000.toWire(renderer); + rrtype_max.toWire(renderer); + + matchWireData(wiredata, sizeof(wiredata), + renderer.getData(), renderer.getLength()); +} + +TEST_F(RRTypeTest, wellKnownTypes) { + EXPECT_EQ(1, RRType::A().getCode()); + EXPECT_EQ("A", RRType::A().toText()); +} + +TEST_F(RRTypeTest, compare) { + EXPECT_TRUE(RRType(1) == RRType("A")); + EXPECT_TRUE(RRType(1).equals(RRType("A"))); + EXPECT_TRUE(RRType(0) != RRType("A")); + EXPECT_TRUE(RRType(0).nequals(RRType("A"))); + + EXPECT_TRUE(RRType("A") < RRType("NS")); + EXPECT_TRUE(RRType(100) < RRType(65535)); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(RRTypeTest, LeftShiftOperator) { + ostringstream oss; + oss << RRType::A(); + EXPECT_EQ(RRType::A().toText(), oss.str()); +} + +// Below, we'll check definitions for all well-known RR types; whether they +// are defined and have the correct parameter values. Test data are generated +// from the list available at: +// http://www.iana.org/assignments/dns-parameters/dns-parameters.xml +struct TypeParam { + const char* const txt; // "A", "AAAA", "NS", etc + const uint16_t code; // 1, 28, 2, etc + const RRType& (*obj)(); // RRType::A(), etc +} known_types[] = { + {"A", 1, RRType::A}, {"NS", 2, RRType::NS}, + {"SOA", 6, RRType::SOA}, {"PTR", 12, RRType::PTR}, + {"TXT", 16, RRType::TXT}, {"AAAA", 28, RRType::AAAA}, + {"OPT", 41, RRType::OPT}, {"RRSIG", 46, RRType::RRSIG}, + {"DHCID", 49, RRType::DHCID}, {"TKEY", 249, RRType::TKEY}, + {"TSIG", 250, RRType::TSIG}, {"ANY", 255, RRType::ANY}, + {NULL, 0, NULL} +}; + +TEST(RRTypeConstTest, wellKnowns) { + for (int i = 0; known_types[i].txt; ++i) { + SCOPED_TRACE("Checking well known RRType: " + + string(known_types[i].txt)); + EXPECT_EQ(known_types[i].code, RRType(known_types[i].txt).getCode()); + EXPECT_EQ(known_types[i].code, + (*known_types[i].obj)().getCode()); + } +} +} diff --git a/src/lib/dns/tests/serial_unittest.cc b/src/lib/dns/tests/serial_unittest.cc index 8b13789179..305b0b0d79 100644 --- a/src/lib/dns/tests/serial_unittest.cc +++ b/src/lib/dns/tests/serial_unittest.cc @@ -1 +1,173 @@ +// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <gtest/gtest.h> + +#include <dns/serial.h> + +using namespace isc::dns; + +class SerialTest : public ::testing::Test { +public: + SerialTest() : one(1), one_2(1), two(2), + date_zero(1980120100), date_one(1980120101), + min(0), max(4294967295u), + number_low(12345), + number_medium(2000000000), + number_high(4000000000u) + {} + Serial one, one_2, two, date_zero, date_one, min, max, number_low, number_medium, number_high; +}; + +// +// Basic tests +// + +TEST_F(SerialTest, get_value) { + EXPECT_EQ(1, one.getValue()); + EXPECT_NE(2, one.getValue()); + EXPECT_EQ(2, two.getValue()); + EXPECT_EQ(1980120100, date_zero.getValue()); + EXPECT_EQ(1980120101, date_one.getValue()); + EXPECT_EQ(0, min.getValue()); + EXPECT_EQ(4294967295u, max.getValue()); + EXPECT_EQ(12345, number_low.getValue()); + EXPECT_EQ(2000000000, number_medium.getValue()); + EXPECT_EQ(4000000000u, number_high.getValue()); +} + +TEST_F(SerialTest, equals) { + EXPECT_EQ(one, one); + EXPECT_EQ(one, one_2); + EXPECT_NE(one, two); + EXPECT_NE(two, one); + EXPECT_EQ(Serial(12345), number_low); + EXPECT_NE(Serial(12346), number_low); +} + +TEST_F(SerialTest, comparison) { + // These should be true/false even without serial arithmetic + EXPECT_LE(one, one); + EXPECT_LE(one, one_2); + EXPECT_LT(one, two); + EXPECT_LE(one, two); + EXPECT_GE(two, two); + EXPECT_GT(two, one); + EXPECT_GE(two, one); + EXPECT_LT(one, number_low); + EXPECT_LT(number_low, number_medium); + EXPECT_LT(number_medium, number_high); + + // now let's try some that 'wrap', as it were + EXPECT_GT(min, max); + EXPECT_LT(max, min); + EXPECT_LT(number_high, number_low); +} + +// +// RFC 1982 Section 3.1 +// +TEST_F(SerialTest, addition) { + EXPECT_EQ(two, one + one); + EXPECT_EQ(two, one + one_2); + EXPECT_EQ(max, max + min); + EXPECT_EQ(min, max + one); + EXPECT_EQ(one, max + two); + EXPECT_EQ(one, max + one + one); + + EXPECT_EQ(one + 100, max + 102); + EXPECT_EQ(min + 2147483645, max + 2147483646); + EXPECT_EQ(min + 2147483646, max + MAX_SERIAL_INCREMENT); +} + +// +// RFC 1982 Section 3.2 has been checked by the basic tests above +// + +// +// RFC 1982 Section 4.1 +// + +// Helper function for addition_always_larger test, add some numbers +// and check that the result is always larger than the original +void do_addition_larger_test(const Serial& number) { + EXPECT_GE(number + 0, number); + EXPECT_EQ(number + 0, number); + EXPECT_GT(number + 1, number); + EXPECT_GT(number + 2, number); + EXPECT_GT(number + 100, number); + EXPECT_GT(number + 1111111, number); + EXPECT_GT(number + 2147483646, number); + EXPECT_GT(number + MAX_SERIAL_INCREMENT, number); + // Try MAX_SERIAL_INCREMENT as a hardcoded number as well + EXPECT_GT(number + 2147483647, number); +} + +TEST_F(SerialTest, addition_always_larger) { + do_addition_larger_test(one); + do_addition_larger_test(two); + do_addition_larger_test(date_zero); + do_addition_larger_test(date_one); + do_addition_larger_test(min); + do_addition_larger_test(max); + do_addition_larger_test(number_low); + do_addition_larger_test(number_medium); + do_addition_larger_test(number_high); +} + +// +// RFC 1982 Section 4.2 +// + +// Helper function to do the second addition +void +do_two_additions_test_second(const Serial &original, + const Serial &number) +{ + EXPECT_NE(original, number); + EXPECT_NE(original, number + 0); + EXPECT_NE(original, number + 1); + EXPECT_NE(original, number + 2); + EXPECT_NE(original, number + 100); + EXPECT_NE(original, number + 1111111); + EXPECT_NE(original, number + 2147483646); + EXPECT_NE(original, number + MAX_SERIAL_INCREMENT); + EXPECT_NE(original, number + 2147483647); +} + +void do_two_additions_test_first(const Serial &number) { + do_two_additions_test_second(number, number + 1); + do_two_additions_test_second(number, number + 2); + do_two_additions_test_second(number, number + 100); + do_two_additions_test_second(number, number + 1111111); + do_two_additions_test_second(number, number + 2147483646); + do_two_additions_test_second(number, number + MAX_SERIAL_INCREMENT); + do_two_additions_test_second(number, number + 2147483647); +} + +TEST_F(SerialTest, two_additions_never_equal) { + do_two_additions_test_first(one); + do_two_additions_test_first(two); + do_two_additions_test_first(date_zero); + do_two_additions_test_first(date_one); + do_two_additions_test_first(min); + do_two_additions_test_first(max); + do_two_additions_test_first(number_low); + do_two_additions_test_first(number_medium); + do_two_additions_test_first(number_high); +} + +// +// RFC 1982 Section 4.3 and 4.4 have nothing to test +// + +// +// Tests from RFC 1982 examples +// +TEST(SerialTextRFCExamples, rfc_example_tests) { +} diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am index e5c8081c07..66dc8b5900 100644 --- a/src/lib/dns/tests/testdata/Makefile.am +++ b/src/lib/dns/tests/testdata/Makefile.am @@ -30,60 +30,14 @@ EXTRA_DIST += name_toWire1 name_toWire2 name_toWire3 name_toWire4 EXTRA_DIST += name_toWire5.spec name_toWire6.spec EXTRA_DIST += name_toWire7 name_toWire8 name_toWire9 EXTRA_DIST += question_fromWire question_toWire1 question_toWire2 -EXTRA_DIST += rdatafields1.spec rdatafields2.spec rdatafields3.spec -EXTRA_DIST += rdatafields4.spec rdatafields5.spec rdatafields6.spec -EXTRA_DIST += rdata_cname_fromWire rdata_dname_fromWire -EXTRA_DIST += rdata_dnskey_fromWire.spec rdata_dnskey_empty_keydata_fromWire.spec EXTRA_DIST += rdata_dhcid_fromWire rdata_dhcid_toWire -EXTRA_DIST += rdata_ds_fromWire rdata_in_a_fromWire rdata_in_aaaa_fromWire -EXTRA_DIST += rdata_mx_fromWire rdata_mx_toWire1 rdata_mx_toWire2 -EXTRA_DIST += rdata_ns_fromWire -EXTRA_DIST += rdata_nsec_fromWire1 rdata_nsec_fromWire2 rdata_nsec_fromWire3 -EXTRA_DIST += rdata_nsec_fromWire4.spec rdata_nsec_fromWire5.spec -EXTRA_DIST += rdata_nsec_fromWire6.spec rdata_nsec_fromWire7.spec -EXTRA_DIST += rdata_nsec_fromWire8.spec rdata_nsec_fromWire9.spec -EXTRA_DIST += rdata_nsec_fromWire10.spec -EXTRA_DIST += rdata_nsec_fromWire16.spec -EXTRA_DIST += rdata_nsec3param_fromWire1 -EXTRA_DIST += rdata_nsec3param_fromWire2.spec -EXTRA_DIST += rdata_nsec3param_fromWire11.spec -EXTRA_DIST += rdata_nsec3param_fromWire13.spec -EXTRA_DIST += rdata_nsec3_fromWire1 rdata_nsec3_fromWire1.spec -EXTRA_DIST += rdata_nsec3_fromWire2.spec rdata_nsec3_fromWire3 -EXTRA_DIST += rdata_nsec3_fromWire4.spec rdata_nsec3_fromWire5.spec -EXTRA_DIST += rdata_nsec3_fromWire6.spec rdata_nsec3_fromWire7.spec -EXTRA_DIST += rdata_nsec3_fromWire8.spec rdata_nsec3_fromWire9.spec -EXTRA_DIST += rdata_nsec3_fromWire10.spec rdata_nsec3_fromWire11.spec -EXTRA_DIST += rdata_nsec3_fromWire12.spec rdata_nsec3_fromWire13.spec -EXTRA_DIST += rdata_nsec3_fromWire14.spec rdata_nsec3_fromWire15.spec -EXTRA_DIST += rdata_nsec3_fromWire16.spec rdata_nsec3_fromWire17.spec +EXTRA_DIST += rdata_ptr_fromWire +EXTRA_DIST += rdata_in_a_fromWire rdata_in_aaaa_fromWire EXTRA_DIST += rdata_opt_fromWire1 rdata_opt_fromWire2 EXTRA_DIST += rdata_opt_fromWire3 rdata_opt_fromWire4 EXTRA_DIST += rdata_rrsig_fromWire1 EXTRA_DIST += rdata_rrsig_fromWire2.spec -EXTRA_DIST += rdata_rp_fromWire1.spec rdata_rp_fromWire2.spec -EXTRA_DIST += rdata_rp_fromWire3.spec rdata_rp_fromWire4.spec -EXTRA_DIST += rdata_rp_fromWire5.spec rdata_rp_fromWire6.spec -EXTRA_DIST += rdata_rp_toWire1.spec rdata_rp_toWire2.spec -EXTRA_DIST += rdata_sshfp_fromWire rdata_sshfp_fromWire2 -EXTRA_DIST += rdata_sshfp_fromWire1.spec rdata_sshfp_fromWire2.spec -EXTRA_DIST += rdata_sshfp_fromWire3.spec rdata_sshfp_fromWire4.spec -EXTRA_DIST += rdata_sshfp_fromWire5.spec rdata_sshfp_fromWire6.spec -EXTRA_DIST += rdata_sshfp_fromWire7.spec rdata_sshfp_fromWire8.spec -EXTRA_DIST += rdata_sshfp_fromWire9 rdata_sshfp_fromWire10 -EXTRA_DIST += rdata_sshfp_fromWire11 rdata_sshfp_fromWire12 -EXTRA_DIST += rdata_afsdb_fromWire1.spec rdata_afsdb_fromWire2.spec -EXTRA_DIST += rdata_afsdb_fromWire3.spec rdata_afsdb_fromWire4.spec -EXTRA_DIST += rdata_afsdb_fromWire5.spec -EXTRA_DIST += rdata_afsdb_toWire1.spec rdata_afsdb_toWire2.spec EXTRA_DIST += rdata_soa_fromWire rdata_soa_toWireUncompressed.spec -EXTRA_DIST += rdata_srv_fromWire -EXTRA_DIST += rdata_minfo_fromWire1.spec rdata_minfo_fromWire2.spec -EXTRA_DIST += rdata_minfo_fromWire3.spec rdata_minfo_fromWire4.spec -EXTRA_DIST += rdata_minfo_fromWire5.spec rdata_minfo_fromWire6.spec -EXTRA_DIST += rdata_minfo_toWire1.spec rdata_minfo_toWire2.spec -EXTRA_DIST += rdata_minfo_toWireUncompressed1.spec -EXTRA_DIST += rdata_minfo_toWireUncompressed2.spec EXTRA_DIST += rdata_txt_fromWire1 rdata_txt_fromWire2.spec EXTRA_DIST += rdata_txt_fromWire3.spec rdata_txt_fromWire4.spec EXTRA_DIST += rdata_txt_fromWire5.spec rdata_unknown_fromWire @@ -99,12 +53,6 @@ EXTRA_DIST += rdata_tkey_fromWire9.spec EXTRA_DIST += rdata_tkey_toWire1.spec rdata_tkey_toWire2.spec EXTRA_DIST += rdata_tkey_toWire3.spec rdata_tkey_toWire4.spec EXTRA_DIST += rdata_tkey_toWire5.spec -EXTRA_DIST += rdata_tlsa_fromWire rdata_tlsa_fromWire2 -EXTRA_DIST += rdata_tlsa_fromWire3.spec rdata_tlsa_fromWire4.spec -EXTRA_DIST += rdata_tlsa_fromWire5.spec rdata_tlsa_fromWire6.spec -EXTRA_DIST += rdata_tlsa_fromWire7.spec rdata_tlsa_fromWire8.spec -EXTRA_DIST += rdata_tlsa_fromWire9 rdata_tlsa_fromWire10 -EXTRA_DIST += rdata_tlsa_fromWire11 rdata_tlsa_fromWire12 EXTRA_DIST += rdata_tsig_fromWire1.spec rdata_tsig_fromWire2.spec EXTRA_DIST += rdata_tsig_fromWire3.spec rdata_tsig_fromWire4.spec EXTRA_DIST += rdata_tsig_fromWire5.spec rdata_tsig_fromWire6.spec @@ -113,9 +61,6 @@ EXTRA_DIST += rdata_tsig_fromWire9.spec EXTRA_DIST += rdata_tsig_toWire1.spec rdata_tsig_toWire2.spec EXTRA_DIST += rdata_tsig_toWire3.spec rdata_tsig_toWire4.spec EXTRA_DIST += rdata_tsig_toWire5.spec -EXTRA_DIST += rdata_caa_fromWire1.spec rdata_caa_fromWire2.spec -EXTRA_DIST += rdata_caa_fromWire3.spec rdata_caa_fromWire4.spec -EXTRA_DIST += rdata_caa_fromWire5 rdata_caa_fromWire6 EXTRA_DIST += tsigrecord_toWire1.spec tsigrecord_toWire2.spec EXTRA_DIST += tsig_verify1.spec tsig_verify2.spec tsig_verify3.spec EXTRA_DIST += tsig_verify4.spec tsig_verify5.spec tsig_verify6.spec @@ -142,51 +87,11 @@ EXTRA_DIST += message_toText1.txt message_toText1.wire EXTRA_DIST += message_toText2.txt message_toText2.wire EXTRA_DIST += message_toText3.txt message_toText3.wire EXTRA_DIST += name_toWire5.wire name_toWire6.wire -EXTRA_DIST += rdatafields1.wire rdatafields2.wire rdatafields3.wire -EXTRA_DIST += rdatafields4.wire rdatafields5.wire rdatafields6.wire -EXTRA_DIST += rdata_dnskey_fromWire.wire rdata_dnskey_empty_keydata_fromWire.wire -EXTRA_DIST += rdata_nsec_fromWire4.wire rdata_nsec_fromWire5.wire -EXTRA_DIST += rdata_nsec_fromWire6.wire rdata_nsec_fromWire7.wire -EXTRA_DIST += rdata_nsec_fromWire8.wire rdata_nsec_fromWire9.wire -EXTRA_DIST += rdata_nsec_fromWire10.wire -EXTRA_DIST += rdata_nsec_fromWire16.wire -EXTRA_DIST += rdata_nsec3param_fromWire2.wire -EXTRA_DIST += rdata_nsec3param_fromWire11.wire -EXTRA_DIST += rdata_nsec3param_fromWire13.wire -EXTRA_DIST += rdata_nsec3_fromWire2.wire rdata_nsec3_fromWire3 -EXTRA_DIST += rdata_nsec3_fromWire4.wire rdata_nsec3_fromWire5.wire -EXTRA_DIST += rdata_nsec3_fromWire6.wire rdata_nsec3_fromWire7.wire -EXTRA_DIST += rdata_nsec3_fromWire8.wire rdata_nsec3_fromWire9.wire -EXTRA_DIST += rdata_nsec3_fromWire10.wire rdata_nsec3_fromWire11.wire -EXTRA_DIST += rdata_nsec3_fromWire12.wire rdata_nsec3_fromWire13.wire -EXTRA_DIST += rdata_nsec3_fromWire14.wire rdata_nsec3_fromWire15.wire -EXTRA_DIST += rdata_nsec3_fromWire16.wire rdata_nsec3_fromWire17.wire EXTRA_DIST += rdata_rrsig_fromWire2.wire -EXTRA_DIST += rdata_rp_fromWire1.wire rdata_rp_fromWire2.wire -EXTRA_DIST += rdata_rp_fromWire3.wire rdata_rp_fromWire4.wire -EXTRA_DIST += rdata_rp_fromWire5.wire rdata_rp_fromWire6.wire -EXTRA_DIST += rdata_rp_toWire1.wire rdata_rp_toWire2.wire -EXTRA_DIST += rdata_sshfp_fromWire1.wire rdata_sshfp_fromWire2.wire -EXTRA_DIST += rdata_sshfp_fromWire3.wire rdata_sshfp_fromWire4.wire -EXTRA_DIST += rdata_sshfp_fromWire5.wire rdata_sshfp_fromWire6.wire -EXTRA_DIST += rdata_sshfp_fromWire7.wire rdata_sshfp_fromWire8.wire -EXTRA_DIST += rdata_afsdb_fromWire1.wire rdata_afsdb_fromWire2.wire -EXTRA_DIST += rdata_afsdb_fromWire3.wire rdata_afsdb_fromWire4.wire -EXTRA_DIST += rdata_afsdb_fromWire5.wire -EXTRA_DIST += rdata_afsdb_toWire1.wire rdata_afsdb_toWire2.wire EXTRA_DIST += rdata_soa_fromWire rdata_soa_toWireUncompressed.wire -EXTRA_DIST += rdata_minfo_fromWire1.wire rdata_minfo_fromWire2.wire -EXTRA_DIST += rdata_minfo_fromWire3.wire rdata_minfo_fromWire4.wire -EXTRA_DIST += rdata_minfo_fromWire5.wire rdata_minfo_fromWire6.wire -EXTRA_DIST += rdata_minfo_toWire1.wire rdata_minfo_toWire2.wire -EXTRA_DIST += rdata_minfo_toWireUncompressed1.wire -EXTRA_DIST += rdata_minfo_toWireUncompressed2.wire EXTRA_DIST += rdata_txt_fromWire1 rdata_txt_fromWire2.wire EXTRA_DIST += rdata_txt_fromWire3.wire rdata_txt_fromWire4.wire EXTRA_DIST += rdata_txt_fromWire5.wire rdata_unknown_fromWire -EXTRA_DIST += rdata_tlsa_fromWire3.wire rdata_tlsa_fromWire4.wire -EXTRA_DIST += rdata_tlsa_fromWire5.wire rdata_tlsa_fromWire6.wire -EXTRA_DIST += rdata_tlsa_fromWire7.wire rdata_tlsa_fromWire8.wire EXTRA_DIST += rdata_tsig_fromWire1.wire rdata_tsig_fromWire2.wire EXTRA_DIST += rdata_tsig_fromWire3.wire rdata_tsig_fromWire4.wire EXTRA_DIST += rdata_tsig_fromWire5.wire rdata_tsig_fromWire6.wire @@ -195,8 +100,6 @@ EXTRA_DIST += rdata_tsig_fromWire9.wire EXTRA_DIST += rdata_tsig_toWire1.wire rdata_tsig_toWire2.wire EXTRA_DIST += rdata_tsig_toWire3.wire rdata_tsig_toWire4.wire EXTRA_DIST += rdata_tsig_toWire5.wire -EXTRA_DIST += rdata_caa_fromWire1.wire rdata_caa_fromWire2.wire -EXTRA_DIST += rdata_caa_fromWire3.wire rdata_caa_fromWire4.wire EXTRA_DIST += rdata_tkey_fromWire1.wire rdata_tkey_fromWire2.wire EXTRA_DIST += rdata_tkey_fromWire3.wire rdata_tkey_fromWire4.wire EXTRA_DIST += rdata_tkey_fromWire5.wire rdata_tkey_fromWire6.wire diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.spec deleted file mode 100644 index f831313827..0000000000 --- a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.spec +++ /dev/null @@ -1,3 +0,0 @@ -[custom] -sections: afsdb -[afsdb] diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.wire b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.wire deleted file mode 100644 index b32776b37b..0000000000 --- a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire1.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_afsdb_fromWire1.spec -### - -# AFSDB RDATA, RDLEN=21 -0015 -# SUBTYPE=1 SERVER=afsdb.example.com -0001 056166736462076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.spec deleted file mode 100644 index f33e768589..0000000000 --- a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.spec +++ /dev/null @@ -1,6 +0,0 @@ -[custom] -sections: name:afsdb -[name] -name: example.com -[afsdb] -server: afsdb.ptr=0 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.wire deleted file mode 100644 index fdc53c526f..0000000000 --- a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire2.wire +++ /dev/null @@ -1,11 +0,0 @@ -### -### This data file was auto-generated from rdata_afsdb_fromWire2.spec -### - -# DNS Name: example.com -076578616d706c6503636f6d00 - -# AFSDB RDATA, RDLEN=10 -000a -# SUBTYPE=1 SERVER=afsdb.ptr=0 -0001 056166736462c000 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.spec deleted file mode 100644 index 993032f605..0000000000 --- a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.spec +++ /dev/null @@ -1,4 +0,0 @@ -[custom] -sections: afsdb -[afsdb] -rdlen: 3 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.wire b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.wire deleted file mode 100644 index 7ea43429a0..0000000000 --- a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire3.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_afsdb_fromWire3.spec -### - -# AFSDB RDATA, RDLEN=3 -0003 -# SUBTYPE=1 SERVER=afsdb.example.com -0001 056166736462076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.spec deleted file mode 100644 index 37abf134c5..0000000000 --- a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.spec +++ /dev/null @@ -1,4 +0,0 @@ -[custom] -sections: afsdb -[afsdb] -rdlen: 80 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.wire deleted file mode 100644 index 79ff6c222f..0000000000 --- a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire4.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_afsdb_fromWire4.spec -### - -# AFSDB RDATA, RDLEN=80 -0050 -# SUBTYPE=1 SERVER=afsdb.example.com -0001 056166736462076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.spec deleted file mode 100644 index 0ea79dd173..0000000000 --- a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.spec +++ /dev/null @@ -1,4 +0,0 @@ -[custom] -sections: afsdb -[afsdb] -server: "01234567890123456789012345678901234567890123456789012345678901234" diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.wire b/src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.wire deleted file mode 100644 index 13eb016d8d..0000000000 --- a/src/lib/dns/tests/testdata/rdata_afsdb_fromWire5.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_afsdb_fromWire5.spec -### - -# AFSDB RDATA, RDLEN=71 -0047 -# SUBTYPE=1 SERVER="01234567890123456789012345678901234567890123456789012345678901234" -0001 432230313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233342200 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_toWire1.spec b/src/lib/dns/tests/testdata/rdata_afsdb_toWire1.spec deleted file mode 100644 index 19464589e1..0000000000 --- a/src/lib/dns/tests/testdata/rdata_afsdb_toWire1.spec +++ /dev/null @@ -1,4 +0,0 @@ -[custom] -sections: afsdb -[afsdb] -rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_toWire1.wire b/src/lib/dns/tests/testdata/rdata_afsdb_toWire1.wire deleted file mode 100644 index 7746fa6e1d..0000000000 --- a/src/lib/dns/tests/testdata/rdata_afsdb_toWire1.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_afsdb_toWire1.spec -### - -# AFSDB RDATA - -# SUBTYPE=1 SERVER=afsdb.example.com -0001 056166736462076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_toWire2.spec b/src/lib/dns/tests/testdata/rdata_afsdb_toWire2.spec deleted file mode 100644 index c80011a488..0000000000 --- a/src/lib/dns/tests/testdata/rdata_afsdb_toWire2.spec +++ /dev/null @@ -1,8 +0,0 @@ -[custom] -sections: name:afsdb -[name] -name: example.com. -[afsdb] -subtype: 0 -server: root.example.com -rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_afsdb_toWire2.wire b/src/lib/dns/tests/testdata/rdata_afsdb_toWire2.wire deleted file mode 100644 index 7f01512a60..0000000000 --- a/src/lib/dns/tests/testdata/rdata_afsdb_toWire2.wire +++ /dev/null @@ -1,11 +0,0 @@ -### -### This data file was auto-generated from rdata_afsdb_toWire2.spec -### - -# DNS Name: example.com. -076578616d706c6503636f6d00 - -# AFSDB RDATA - -# SUBTYPE=0 SERVER=root.example.com -0000 04726f6f74076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_caa_fromWire1.spec deleted file mode 100644 index d21987c7eb..0000000000 --- a/src/lib/dns/tests/testdata/rdata_caa_fromWire1.spec +++ /dev/null @@ -1,6 +0,0 @@ -# -# The simplest form of CAA: all default parameters -# -[custom] -sections: caa -[caa] diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire1.wire b/src/lib/dns/tests/testdata/rdata_caa_fromWire1.wire deleted file mode 100644 index 780bd8dc25..0000000000 --- a/src/lib/dns/tests/testdata/rdata_caa_fromWire1.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_caa_fromWire1.spec -### - -# CAA RDATA, RDLEN=21 -0015 -# FLAGS=0 TAG=issue VALUE=ca.example.net -00 05 697373756563612e6578616d706c652e6e6574 diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_caa_fromWire2.spec deleted file mode 100644 index 867e43d048..0000000000 --- a/src/lib/dns/tests/testdata/rdata_caa_fromWire2.spec +++ /dev/null @@ -1,7 +0,0 @@ -# -# Mixed case CAA tag field. -# -[custom] -sections: caa -[caa] -tag: 'ISSue' diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_caa_fromWire2.wire deleted file mode 100644 index 09ca24d972..0000000000 --- a/src/lib/dns/tests/testdata/rdata_caa_fromWire2.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_caa_fromWire2.spec -### - -# CAA RDATA, RDLEN=21 -0015 -# FLAGS=0 TAG=ISSue VALUE=ca.example.net -00 05 495353756563612e6578616d706c652e6e6574 diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_caa_fromWire3.spec deleted file mode 100644 index 9297151df0..0000000000 --- a/src/lib/dns/tests/testdata/rdata_caa_fromWire3.spec +++ /dev/null @@ -1,7 +0,0 @@ -# -# Missing CAA value field. -# -[custom] -sections: caa -[caa] -value: '' diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire3.wire b/src/lib/dns/tests/testdata/rdata_caa_fromWire3.wire deleted file mode 100644 index 16b5c43432..0000000000 --- a/src/lib/dns/tests/testdata/rdata_caa_fromWire3.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_caa_fromWire3.spec -### - -# CAA RDATA, RDLEN=7 -0007 -# FLAGS=0 TAG=issue VALUE= -00 05 6973737565 diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_caa_fromWire4.spec deleted file mode 100644 index 53e16b12a3..0000000000 --- a/src/lib/dns/tests/testdata/rdata_caa_fromWire4.spec +++ /dev/null @@ -1,7 +0,0 @@ -# -# Missing CAA value field. -# -[custom] -sections: caa -[caa] -tag: '' diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_caa_fromWire4.wire deleted file mode 100644 index 3225e60b09..0000000000 --- a/src/lib/dns/tests/testdata/rdata_caa_fromWire4.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_caa_fromWire4.spec -### - -# CAA RDATA, RDLEN=16 -0010 -# FLAGS=0 TAG= VALUE=ca.example.net -00 00 63612e6578616d706c652e6e6574 diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire5 b/src/lib/dns/tests/testdata/rdata_caa_fromWire5 deleted file mode 100644 index 123011fdb3..0000000000 --- a/src/lib/dns/tests/testdata/rdata_caa_fromWire5 +++ /dev/null @@ -1,6 +0,0 @@ -# Test where CAA value field is shorter than the RDATA length - -# CAA RDATA, RDLEN=32 -0020 -# FLAGS=0 TAG=c VALUE=ca.example.net -00 01 63 63612e6578616d706c652e6e6574 diff --git a/src/lib/dns/tests/testdata/rdata_caa_fromWire6 b/src/lib/dns/tests/testdata/rdata_caa_fromWire6 deleted file mode 100644 index 1a35a1aef7..0000000000 --- a/src/lib/dns/tests/testdata/rdata_caa_fromWire6 +++ /dev/null @@ -1,4 +0,0 @@ -# Test where RDATA is completely missing - -# CAA RDATA, RDLEN=32 -0020 diff --git a/src/lib/dns/tests/testdata/rdata_cname_fromWire b/src/lib/dns/tests/testdata/rdata_cname_fromWire deleted file mode 100644 index 57eae13b62..0000000000 --- a/src/lib/dns/tests/testdata/rdata_cname_fromWire +++ /dev/null @@ -1,44 +0,0 @@ -# -# various kinds of CNAME RDATA stored in an input buffer -# -# Valid non-compressed RDATA for cn.example.com. -# RDLENGTH=16 bytes -# 0 1 - 00 10 -# 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7(bytes) -#(2) c n (7) e x a m p l e (3) c o m . - 02 63 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 -# -# short length -# 8 9 - 00 0f -#20 1 2 3 4 5 6 7 8 9 30 1 2 3 4 5 - 02 63 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 -# -# length too long -# 6 7 - 00 11 -# -# 8 9 40 1 2 3 4 5 6 7 8 9 50 1 2 3 4 - 02 63 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 -# -# Valid compressed CNAME name: 'cn2' + pointer -# 5 6 - 00 06 -# 7 8 9 60 1 2 -#(3) c n 2 ptr=5 - 03 63 6e 32 c0 05 -# -# Valid compressed CNAME name but RDLENGTH is incorrect: it must be the length -# of the sequence from the head to the pointer, not the decompressed name -# length. -# 3 4 - 00 11 -# 5 6 7 8 9 70 - 03 63 6e 32 c0 05 -# incomplete name (no trailing dot). this can be tested only at the end of -# the buffer. -# 1 2 - 00 0f -# 3 4 5 6 7 8 9 80 1 2 3 4 5 6 7 - 02 63 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d diff --git a/src/lib/dns/tests/testdata/rdata_dname_fromWire b/src/lib/dns/tests/testdata/rdata_dname_fromWire deleted file mode 100644 index 1c899bf19d..0000000000 --- a/src/lib/dns/tests/testdata/rdata_dname_fromWire +++ /dev/null @@ -1,44 +0,0 @@ -# -# various kinds of DNAME RDATA stored in an input buffer -# -# Valid non-compressed RDATA for dn.example.com. -# RDLENGTH=16 bytes -# 0 1 - 00 10 -# 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7(bytes) -#(2) d n (7) e x a m p l e (3) c o m . - 02 64 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 -# -# short length -# 8 9 - 00 0f -#20 1 2 3 4 5 6 7 8 9 30 1 2 3 4 5 - 02 64 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 -# -# length too long -# 6 7 - 00 11 -# -# 8 9 40 1 2 3 4 5 6 7 8 9 50 1 2 3 4 - 02 64 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 00 -# -# Valid compressed DNAME name: 'dn2' + pointer -# 5 6 - 00 06 -# 7 8 9 60 1 2 -#(3) d n 2 ptr=5 - 03 64 6e 32 c0 05 -# -# Valid compressed DNAME name but RDLENGTH is incorrect: it must be the length -# of the sequence from the head to the pointer, not the decompressed name -# length. -# 3 4 - 00 11 -# 5 6 7 8 9 70 - 03 64 6e 32 c0 05 -# incomplete name (no trailing dot). this can be tested only at the end of -# the buffer. -# 1 2 - 00 0f -# 3 4 5 6 7 8 9 80 1 2 3 4 5 6 7 - 02 64 6e 07 65 78 61 6d 70 6c 65 03 63 6f 6d diff --git a/src/lib/dns/tests/testdata/rdata_dnskey_empty_keydata_fromWire.spec b/src/lib/dns/tests/testdata/rdata_dnskey_empty_keydata_fromWire.spec deleted file mode 100644 index b65271d48b..0000000000 --- a/src/lib/dns/tests/testdata/rdata_dnskey_empty_keydata_fromWire.spec +++ /dev/null @@ -1,7 +0,0 @@ -# DNSKEY test data with empty digest - -[custom] -sections: dnskey - -[dnskey] -digest: diff --git a/src/lib/dns/tests/testdata/rdata_dnskey_empty_keydata_fromWire.wire b/src/lib/dns/tests/testdata/rdata_dnskey_empty_keydata_fromWire.wire deleted file mode 100644 index 35388b193a..0000000000 --- a/src/lib/dns/tests/testdata/rdata_dnskey_empty_keydata_fromWire.wire +++ /dev/null @@ -1,14 +0,0 @@ -### -### This data file was auto-generated from rdata_dnskey_empty_keydata_fromWire.spec -### - -# DNSKEY RDATA, RDLEN=4 -0004 -# FLAGS=257 -0101 -# PROTOCOL=3 -03 -# ALGORITHM=5 -05 -# DIGEST= - diff --git a/src/lib/dns/tests/testdata/rdata_dnskey_fromWire.spec b/src/lib/dns/tests/testdata/rdata_dnskey_fromWire.spec deleted file mode 100644 index 87e66db087..0000000000 --- a/src/lib/dns/tests/testdata/rdata_dnskey_fromWire.spec +++ /dev/null @@ -1,7 +0,0 @@ -# DNSKEY test data - -[custom] -sections: dnskey - -[dnskey] -digest: BEAAAAOhHQDBrhQbtphgq2wQUpEQ5t4DtUHxoMVFu2hWLDMvoOMRXjGrhhCeFvAZih7yJHf8ZGfW6hd38hXG/xylYCO6Krpbdojwx8YMXLA5/kA+u50WIL8ZR1R6KTbsYVMf/Qx5RiNbPClw+vT+U8eXEJmO20jIS1ULgqy347cBB1zMnnz/4LJpA0da9CbKj3A254T515sNIMcwsB8/2+2E63/zZrQzBkj0BrN/9Bexjpiks3jRhZatEsXn3dTy47R09Uix5WcJt+xzqZ7+ysyLKOOedS39Z7SDmsn2eA0FKtQpwA6LXeG2w+jxmw3oA8lVUgEf/rzeC/bByBNsO70aEFTd diff --git a/src/lib/dns/tests/testdata/rdata_dnskey_fromWire.wire b/src/lib/dns/tests/testdata/rdata_dnskey_fromWire.wire deleted file mode 100644 index 0a2e41fadc..0000000000 --- a/src/lib/dns/tests/testdata/rdata_dnskey_fromWire.wire +++ /dev/null @@ -1,14 +0,0 @@ -### -### This data file was auto-generated from rdata_dnskey_fromWire.spec -### - -# DNSKEY RDATA, RDLEN=265 -0109 -# FLAGS=257 -0101 -# PROTOCOL=3 -03 -# ALGORITHM=5 -05 -# DIGEST=BEAAAAOhHQDBrhQbtphgq2wQUpEQ5t4DtUHxoMVFu2hWLDMvoOMRXjGrhhCeFvAZih7yJHf8ZGfW6hd38hXG/xylYCO6Krpbdojwx8YMXLA5/kA+u50WIL8ZR1R6KTbsYVMf/Qx5RiNbPClw+vT+U8eXEJmO20jIS1ULgqy347cBB1zMnnz/4LJpA0da9CbKj3A254T515sNIMcwsB8/2+2E63/zZrQzBkj0BrN/9Bexjpiks3jRhZatEsXn3dTy47R09Uix5WcJt+xzqZ7+ysyLKOOedS39Z7SDmsn2eA0FKtQpwA6LXeG2w+jxmw3oA8lVUgEf/rzeC/bByBNsO70aEFTd -0440000003a11d00c1ae141bb69860ab6c10529110e6de03b541f1a0c545bb68562c332fa0e3115e31ab86109e16f0198a1ef22477fc6467d6ea1777f215c6ff1ca56023ba2aba5b7688f0c7c60c5cb039fe403ebb9d1620bf1947547a2936ec61531ffd0c7946235b3c2970faf4fe53c79710998edb48c84b550b82acb7e3b701075ccc9e7cffe0b26903475af426ca8f7036e784f9d79b0d20c730b01f3fdbed84eb7ff366b4330648f406b37ff417b18e98a4b378d18596ad12c5e7ddd4f2e3b474f548b1e56709b7ec73a99efecacc8b28e39e752dfd67b4839ac9f6780d052ad429c00e8b5de1b6c3e8f19b0de803c95552011ffebcde0bf6c1c8136c3bbd1a1054dd diff --git a/src/lib/dns/tests/testdata/rdata_ds_fromWire b/src/lib/dns/tests/testdata/rdata_ds_fromWire deleted file mode 100644 index 81c412ca4e..0000000000 --- a/src/lib/dns/tests/testdata/rdata_ds_fromWire +++ /dev/null @@ -1,6 +0,0 @@ -# RDLENGTH 36 bytes -00 24 -# DS record, keyid 12892 -32 5c 05 02 f1 e1 84 c0 e1 d6 15 d2 0e b3 c2 23 -ac ed 3b 03 c7 73 dd 95 2d 5f 0e b5 c7 77 58 6d -e1 8d a6 b5 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire1.spec deleted file mode 100644 index 2c43db0727..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_fromWire1.spec +++ /dev/null @@ -1,3 +0,0 @@ -[custom] -sections: minfo -[minfo] diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire1.wire b/src/lib/dns/tests/testdata/rdata_minfo_fromWire1.wire deleted file mode 100644 index 3483e9b1ea..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_fromWire1.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_minfo_fromWire1.spec -### - -# MINFO RDATA, RDLEN=44 -002c -# RMAILBOX=rmailbox.example.com EMAILBOX=emailbox.example.com -08726d61696c626f78076578616d706c6503636f6d00 08656d61696c626f78076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire2.spec deleted file mode 100644 index d781cac71d..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_fromWire2.spec +++ /dev/null @@ -1,7 +0,0 @@ -[custom] -sections: name:minfo -[name] -name: a.example.com. -[minfo] -rmailbox: rmailbox.ptr=02 -emailbox: emailbox.ptr=02 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_minfo_fromWire2.wire deleted file mode 100644 index d79e936335..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_fromWire2.wire +++ /dev/null @@ -1,11 +0,0 @@ -### -### This data file was auto-generated from rdata_minfo_fromWire2.spec -### - -# DNS Name: a.example.com. -0161076578616d706c6503636f6d00 - -# MINFO RDATA, RDLEN=22 -0016 -# RMAILBOX=rmailbox.ptr=02 EMAILBOX=emailbox.ptr=02 -08726d61696c626f78c002 08656d61696c626f78c002 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire3.spec deleted file mode 100644 index a1d4b769d9..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_fromWire3.spec +++ /dev/null @@ -1,6 +0,0 @@ -[custom] -sections: minfo -# rdlength too short -[minfo] -emailbox: emailbox.ptr=11 -rdlen: 3 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire3.wire b/src/lib/dns/tests/testdata/rdata_minfo_fromWire3.wire deleted file mode 100644 index 55df1a2de4..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_fromWire3.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_minfo_fromWire3.spec -### - -# MINFO RDATA, RDLEN=3 -0003 -# RMAILBOX=rmailbox.example.com EMAILBOX=emailbox.ptr=11 -08726d61696c626f78076578616d706c6503636f6d00 08656d61696c626f78c00b diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire4.spec deleted file mode 100644 index 269a6ce7e2..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_fromWire4.spec +++ /dev/null @@ -1,6 +0,0 @@ -[custom] -sections: minfo -# rdlength too long -[minfo] -emailbox: emailbox.ptr=11 -rdlen: 80 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_minfo_fromWire4.wire deleted file mode 100644 index 746f55abe2..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_fromWire4.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_minfo_fromWire4.spec -### - -# MINFO RDATA, RDLEN=80 -0050 -# RMAILBOX=rmailbox.example.com EMAILBOX=emailbox.ptr=11 -08726d61696c626f78076578616d706c6503636f6d00 08656d61696c626f78c00b diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire5.spec deleted file mode 100644 index 3a888e3c20..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_fromWire5.spec +++ /dev/null @@ -1,5 +0,0 @@ -[custom] -sections: minfo -# bogus rmailbox name -[minfo] -rmailbox: "01234567890123456789012345678901234567890123456789012345678901234" diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire5.wire b/src/lib/dns/tests/testdata/rdata_minfo_fromWire5.wire deleted file mode 100644 index dacd9a026a..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_fromWire5.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_minfo_fromWire5.spec -### - -# MINFO RDATA, RDLEN=91 -005b -# RMAILBOX="01234567890123456789012345678901234567890123456789012345678901234" EMAILBOX=emailbox.example.com -432230313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233342200 08656d61696c626f78076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_minfo_fromWire6.spec deleted file mode 100644 index c75ed8e214..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_fromWire6.spec +++ /dev/null @@ -1,5 +0,0 @@ -[custom] -sections: minfo -# bogus emailbox name -[minfo] -emailbox: "01234567890123456789012345678901234567890123456789012345678901234" diff --git a/src/lib/dns/tests/testdata/rdata_minfo_fromWire6.wire b/src/lib/dns/tests/testdata/rdata_minfo_fromWire6.wire deleted file mode 100644 index 4199fee44e..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_fromWire6.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_minfo_fromWire6.spec -### - -# MINFO RDATA, RDLEN=91 -005b -# RMAILBOX=rmailbox.example.com EMAILBOX="01234567890123456789012345678901234567890123456789012345678901234" -08726d61696c626f78076578616d706c6503636f6d00 432230313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233342200 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWire1.spec b/src/lib/dns/tests/testdata/rdata_minfo_toWire1.spec deleted file mode 100644 index 7b340a3904..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_toWire1.spec +++ /dev/null @@ -1,5 +0,0 @@ -[custom] -sections: minfo -[minfo] -emailbox: emailbox.ptr=09 -rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWire1.wire b/src/lib/dns/tests/testdata/rdata_minfo_toWire1.wire deleted file mode 100644 index 8ac4afe295..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_toWire1.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_minfo_toWire1.spec -### - -# MINFO RDATA - -# RMAILBOX=rmailbox.example.com EMAILBOX=emailbox.ptr=09 -08726d61696c626f78076578616d706c6503636f6d00 08656d61696c626f78c009 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWire2.spec b/src/lib/dns/tests/testdata/rdata_minfo_toWire2.spec deleted file mode 100644 index 132f11839f..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_toWire2.spec +++ /dev/null @@ -1,6 +0,0 @@ -[custom] -sections: minfo -[minfo] -rmailbox: root.example.com. -emailbox: emailbox.ptr=05 -rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWire2.wire b/src/lib/dns/tests/testdata/rdata_minfo_toWire2.wire deleted file mode 100644 index 7fb847cc91..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_toWire2.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_minfo_toWire2.spec -### - -# MINFO RDATA - -# RMAILBOX=root.example.com. EMAILBOX=emailbox.ptr=05 -04726f6f74076578616d706c6503636f6d00 08656d61696c626f78c005 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed1.spec b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed1.spec deleted file mode 100644 index d99a3813ca..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed1.spec +++ /dev/null @@ -1,7 +0,0 @@ -# -# A simplest form of MINFO: all default parameters -# -[custom] -sections: minfo -[minfo] -rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed1.wire b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed1.wire deleted file mode 100644 index 6de233e633..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed1.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_minfo_toWireUncompressed1.spec -### - -# MINFO RDATA - -# RMAILBOX=rmailbox.example.com EMAILBOX=emailbox.example.com -08726d61696c626f78076578616d706c6503636f6d00 08656d61696c626f78076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.spec b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.spec deleted file mode 100644 index 0f78fcc63b..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.spec +++ /dev/null @@ -1,8 +0,0 @@ -# -# A simplest form of MINFO: custom rmailbox and default emailbox -# -[custom] -sections: minfo -[minfo] -rmailbox: root.example.com. -rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.wire b/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.wire deleted file mode 100644 index 51e9e354c3..0000000000 --- a/src/lib/dns/tests/testdata/rdata_minfo_toWireUncompressed2.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_minfo_toWireUncompressed2.spec -### - -# MINFO RDATA - -# RMAILBOX=root.example.com. EMAILBOX=emailbox.example.com -04726f6f74076578616d706c6503636f6d00 08656d61696c626f78076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_mx_fromWire b/src/lib/dns/tests/testdata/rdata_mx_fromWire deleted file mode 100644 index 8e09154e53..0000000000 --- a/src/lib/dns/tests/testdata/rdata_mx_fromWire +++ /dev/null @@ -1,15 +0,0 @@ -# -# various kinds of MX RDATA stored in an input buffer -# -# Valid RDATA for "10 mail.example.com" -# -# RDLENGTH=18 bytes -# 0 1 - 00 12 -# 2 3 -# PREFERENCE: 10 - 00 0a -# EXCHANGE: non compressed -# 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 (bytes) -#(2) m x (7) e x a m p l e (3) c o m . - 02 6d 78 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 diff --git a/src/lib/dns/tests/testdata/rdata_mx_toWire1 b/src/lib/dns/tests/testdata/rdata_mx_toWire1 deleted file mode 100644 index 3049373246..0000000000 --- a/src/lib/dns/tests/testdata/rdata_mx_toWire1 +++ /dev/null @@ -1,12 +0,0 @@ -# -# compressed MX RDATA stored in an output buffer -# -# sentinel name: example.com. -# 0 1 2 3 4 5 6 7 8 9 10 1 2 (bytes) -#(7) e x a m p l e (3) c o m . - 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 -# PREFERENCE: 10 - 00 0a -# EXCHANGE: compressed -#(4) m x ptr=0 - 02 6d 78 c0 00 diff --git a/src/lib/dns/tests/testdata/rdata_mx_toWire2 b/src/lib/dns/tests/testdata/rdata_mx_toWire2 deleted file mode 100644 index ebd2f27cfc..0000000000 --- a/src/lib/dns/tests/testdata/rdata_mx_toWire2 +++ /dev/null @@ -1,12 +0,0 @@ -# -# compressed MX RDATA stored in an output buffer -# -# sentinel name: example.com. -# 0 1 2 3 4 5 6 7 8 9 10 1 2 (bytes) -#(7) e x a m p l e (3) c o m . - 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 -# PREFERENCE: 10 - 00 0a -# EXCHANGE: not compressed -#(4) m x ptr=0 - 02 6d 78 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire1 b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire1 deleted file mode 100644 index 1dd5f52929..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire1 +++ /dev/null @@ -1,7 +0,0 @@ -# RDLENGTH, 39 bytes -00 27 -# NSEC3 record: -# 1 1 1 D399EAAB H9RSFB7FPF2L8HG35CMPC765TDK23RP6 NS SOA RRSIG DNSKEY NSEC3PARAM -01 01 00 01 04 d3 99 ea ab 14 8a 77 c7 ac ef cb -c5 54 46 03 2b 2d 96 1c c5 eb 68 21 ef 26 00 07 -22 00 00 00 00 02 90 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire1.spec deleted file mode 100644 index 39a78d75f1..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire1.spec +++ /dev/null @@ -1,7 +0,0 @@ -# -# A malformed NSEC3 RDATA: bit map length is too large, causing overflow -# - -[custom] -sections: nsec3 -[nsec3] diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.spec deleted file mode 100644 index 30417f5e15..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.spec +++ /dev/null @@ -1,8 +0,0 @@ -# -# An invalid NSEC3 RDATA: a bitmap block containing empty bytes -# - -[custom] -sections: nsec3 -[nsec3] -bitmap: '01000000' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.wire deleted file mode 100644 index 99c5afd5ae..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire10.wire +++ /dev/null @@ -1,14 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3_fromWire10.spec -### - -# NSEC3 RDATA, RDLEN=37 -0025 -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=5, Salt='sssss' -05 7373737373 -# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' -14 6868686868686868686868686868686868686868 -# Bitmap: Block=0, Length=4 -00 04 01000000 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.spec deleted file mode 100644 index 80ec59f0c4..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.spec +++ /dev/null @@ -1,8 +0,0 @@ -# -# An invalid NSEC3 RDATA: Saltlen is too large -# - -[custom] -sections: nsec3 -[nsec3] -rdlen: 7 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.wire deleted file mode 100644 index 48dc589f35..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire11.wire +++ /dev/null @@ -1,14 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3_fromWire11.spec -### - -# NSEC3 RDATA, RDLEN=7 -0007 -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=5, Salt='sssss' -05 7373737373 -# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' -14 6868686868686868686868686868686868686868 -# Bitmap: Block=0, Length=6 -00 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.spec deleted file mode 100644 index 1e01655d31..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.spec +++ /dev/null @@ -1,9 +0,0 @@ -# -# An invalid NSEC3 RDATA: Hash length is too large -# - -[custom] -sections: nsec3 -[nsec3] -# only contains the first byte of hash -rdlen: 12 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.wire deleted file mode 100644 index e880d553b7..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire12.wire +++ /dev/null @@ -1,14 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3_fromWire12.spec -### - -# NSEC3 RDATA, RDLEN=12 -000c -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=5, Salt='sssss' -05 7373737373 -# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' -14 6868686868686868686868686868686868686868 -# Bitmap: Block=0, Length=6 -00 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.spec deleted file mode 100644 index fcc9d535dd..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.spec +++ /dev/null @@ -1,9 +0,0 @@ -# -# A valid (but unusual) NSEC3 RDATA: salt is empty. -# - -[custom] -sections: nsec3 -[nsec3] -saltlen: 0 -salt: '' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.wire deleted file mode 100644 index 86a06c5d98..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire13.wire +++ /dev/null @@ -1,14 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3_fromWire13.spec -### - -# NSEC3 RDATA, RDLEN=34 -0022 -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=0, Salt='' -00 -# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' -14 6868686868686868686868686868686868686868 -# Bitmap: Block=0, Length=6 -00 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.spec deleted file mode 100644 index a0550d5e6f..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.spec +++ /dev/null @@ -1,9 +0,0 @@ -# -# An invalid NSEC3 RDATA: empty hash -# - -[custom] -sections: nsec3 -[nsec3] -hashlen: 0 -hash: '' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.wire deleted file mode 100644 index 9d76b5ab15..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire14.wire +++ /dev/null @@ -1,14 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3_fromWire14.spec -### - -# NSEC3 RDATA, RDLEN=19 -0013 -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=5, Salt='sssss' -05 7373737373 -# Hash Len=0, Hash='' -00 -# Bitmap: Block=0, Length=6 -00 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.spec deleted file mode 100644 index 4993e03f67..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.spec +++ /dev/null @@ -1,10 +0,0 @@ -# -# NSEC3 RDATA with empty type bitmap. It's okay. -# The test data includes bytes for a bitmap field, but RDLEN indicates -# it's not part of the RDATA and so it will be ignored. -# - -[custom] -sections: nsec3 -[nsec3] -rdlen: 31 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.wire deleted file mode 100644 index bd636a7e93..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire15.wire +++ /dev/null @@ -1,14 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3_fromWire15.spec -### - -# NSEC3 RDATA, RDLEN=31 -001f -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=5, Salt='sssss' -05 7373737373 -# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' -14 6868686868686868686868686868686868686868 -# Bitmap: Block=0, Length=6 -00 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.spec deleted file mode 100644 index dac14eaa21..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.spec +++ /dev/null @@ -1,8 +0,0 @@ -# -# NSEC3 RDATA with an empty bitmap -# - -[custom] -sections: nsec3 -[nsec3] -nbitmap: 0 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.wire deleted file mode 100644 index bab957ead6..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire16.wire +++ /dev/null @@ -1,12 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3_fromWire16.spec -### - -# NSEC3 RDATA, RDLEN=31 -001f -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=5, Salt='sssss' -05 7373737373 -# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' -14 6868686868686868686868686868686868686868 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.spec deleted file mode 100644 index 42533491bf..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.spec +++ /dev/null @@ -1,8 +0,0 @@ -# -# An invalid NSEC3 RDATA: RDLEN is too short to include the hash len field. -# - -[custom] -sections: nsec3 -[nsec3] -rdlen: 10 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.wire deleted file mode 100644 index f7972a0ace..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire17.wire +++ /dev/null @@ -1,14 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3_fromWire17.spec -### - -# NSEC3 RDATA, RDLEN=10 -000a -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=5, Salt='sssss' -05 7373737373 -# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' -14 6868686868686868686868686868686868686868 -# Bitmap: Block=0, Length=6 -00 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.spec deleted file mode 100644 index f35de8399f..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.spec +++ /dev/null @@ -1,9 +0,0 @@ -# -# A malformed NSEC3 RDATA: RDLEN indicates it doesn't even contain the fixed -# 5 octets -# - -[custom] -sections: nsec3 -[nsec3] -rdlen: 4 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.wire deleted file mode 100644 index ec14a5840e..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire2.wire +++ /dev/null @@ -1,14 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3_fromWire2.spec -### - -# NSEC3 RDATA, RDLEN=4 -0004 -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=5, Salt='sssss' -05 7373737373 -# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' -14 6868686868686868686868686868686868686868 -# Bitmap: Block=0, Length=6 -00 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire3 b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire3 deleted file mode 100644 index a0c8f599c0..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire3 +++ /dev/null @@ -1,6 +0,0 @@ -# RDLENGTH, 39 bytes -00 27 -# NSEC3 record with a broken type bitmap -01 01 00 01 04 d3 99 ea ab 14 8a 77 c7 ac ef cb -c5 54 46 03 2b 2d 96 1c c5 eb 68 21 ef 26 00 ff -22 00 00 00 00 02 90 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.spec deleted file mode 100644 index 06d6eb4d2b..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.spec +++ /dev/null @@ -1,9 +0,0 @@ -# -# A malformed NSEC3 RDATA: bit map length is too large, causing overflow -# - -[custom] -sections: nsec3 -[nsec3] -maplen: 31 -bitmap: '01' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.wire deleted file mode 100644 index 527860c35b..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire4.wire +++ /dev/null @@ -1,14 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3_fromWire4.spec -### - -# NSEC3 RDATA, RDLEN=34 -0022 -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=5, Salt='sssss' -05 7373737373 -# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' -14 6868686868686868686868686868686868686868 -# Bitmap: Block=0, Length=31 -00 1f 01 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire5.spec deleted file mode 100644 index 2d5713cee9..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire5.spec +++ /dev/null @@ -1,13 +0,0 @@ -# -# A malformed NSEC3 RDATA: incomplete bit map field -# - -[custom] -sections: nsec3 -[nsec3] -# only containing the block field of the bitmap -rdlen: 32 -#dummy data -maplen: 31 -#dummy data -bitmap: '00' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire5.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire5.wire deleted file mode 100644 index 97b798beca..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire5.wire +++ /dev/null @@ -1,14 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3_fromWire5.spec -### - -# NSEC3 RDATA, RDLEN=32 -0020 -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=5, Salt='sssss' -05 7373737373 -# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' -14 6868686868686868686868686868686868686868 -# Bitmap: Block=0, Length=31 -00 1f 00 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.spec deleted file mode 100644 index 36e9e59b4c..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.spec +++ /dev/null @@ -1,11 +0,0 @@ -# -# A malformed NSEC3 RDATA: bit map length being 0 -# - -[custom] -sections: nsec3 -[nsec3] -rdlen: 33 -maplen: 0 -# dummy data: -bitmap: '01' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.wire deleted file mode 100644 index 4cf1189ea2..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire6.wire +++ /dev/null @@ -1,14 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3_fromWire6.spec -### - -# NSEC3 RDATA, RDLEN=33 -0021 -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=5, Salt='sssss' -05 7373737373 -# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' -14 6868686868686868686868686868686868686868 -# Bitmap: Block=0, Length=0 -00 00 01 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire7.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire7.spec deleted file mode 100644 index 338c0c98de..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire7.spec +++ /dev/null @@ -1,9 +0,0 @@ -# -# NSEC3 RDATA with a longest bitmap field (32 bitmap bytes) -# - -[custom] -sections: nsec3 -[nsec3] -maplen: 32 -bitmap: '0101010101010101010101010101010101010101010101010101010101010101' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire7.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire7.wire deleted file mode 100644 index 1fddd5866d..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire7.wire +++ /dev/null @@ -1,14 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3_fromWire7.spec -### - -# NSEC3 RDATA, RDLEN=65 -0041 -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=5, Salt='sssss' -05 7373737373 -# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' -14 6868686868686868686868686868686868686868 -# Bitmap: Block=0, Length=32 -00 20 0101010101010101010101010101010101010101010101010101010101010101 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire8.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire8.spec deleted file mode 100644 index 041714ecb8..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire8.spec +++ /dev/null @@ -1,9 +0,0 @@ -# -# An invalid NSEC3 RDATA with an oversized bitmap field (33 bitmap bytes) -# - -[custom] -sections: nsec3 -[nsec3] -maplen: 33 -bitmap: '010101010101010101010101010101010101010101010101010101010101010101' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire8.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire8.wire deleted file mode 100644 index 956909470b..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire8.wire +++ /dev/null @@ -1,14 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3_fromWire8.spec -### - -# NSEC3 RDATA, RDLEN=66 -0042 -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=5, Salt='sssss' -05 7373737373 -# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' -14 6868686868686868686868686868686868686868 -# Bitmap: Block=0, Length=33 -00 21 010101010101010101010101010101010101010101010101010101010101010101 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.spec b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.spec deleted file mode 100644 index b04c84f7fc..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.spec +++ /dev/null @@ -1,10 +0,0 @@ -# -# An invalid NSEC3 RDATA: disordered bitmap blocks -# - -[custom] -sections: nsec3 -[nsec3] -nbitmap: 2 -block0: 2 -block1: 1 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.wire b/src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.wire deleted file mode 100644 index 9c0de10c7e..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3_fromWire9.wire +++ /dev/null @@ -1,16 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3_fromWire9.spec -### - -# NSEC3 RDATA, RDLEN=47 -002f -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=5, Salt='sssss' -05 7373737373 -# Hash Len=20, Hash='hhhhhhhhhhhhhhhhhhhh' -14 6868686868686868686868686868686868686868 -# Bitmap: Block=2, Length=6 -02 06 040000000003 -# Bitmap: Block=1, Length=6 -01 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire1 b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire1 deleted file mode 100644 index 1b8697ff83..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire1 +++ /dev/null @@ -1,5 +0,0 @@ -# RDLENGTH, 9 bytes -00 09 -# NSEC3PARAM record -# 1 1 1 D399EAAB -01 01 00 01 04 d3 99 ea ab diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.spec b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.spec deleted file mode 100644 index 41e17847f1..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.spec +++ /dev/null @@ -1,8 +0,0 @@ -# -# An invalid NSEC3PARAM RDATA: Saltlen is too large -# - -[custom] -sections: nsec3param -[nsec3param] -rdlen: 7 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.wire b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.wire deleted file mode 100644 index e9ffb4620e..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire11.wire +++ /dev/null @@ -1,10 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3param_fromWire11.spec -### - -# NSEC3PARAM RDATA, RDLEN=7 -0007 -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=5, Salt='sssss' -05 7373737373 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.spec b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.spec deleted file mode 100644 index 311b2dd898..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.spec +++ /dev/null @@ -1,9 +0,0 @@ -# -# A valid (but unusual) NSEC3PARAM RDATA: salt is empty. -# - -[custom] -sections: nsec3param -[nsec3param] -saltlen: 0 -salt: '' diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.wire b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.wire deleted file mode 100644 index 511da87f5c..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire13.wire +++ /dev/null @@ -1,10 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3param_fromWire13.spec -### - -# NSEC3PARAM RDATA, RDLEN=5 -0005 -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=0, Salt='' -00 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.spec deleted file mode 100644 index e04b818e98..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.spec +++ /dev/null @@ -1,9 +0,0 @@ -# -# A malformed NSEC3PARAM RDATA: RDLEN indicates it doesn't even contain the -# fixed 5 octets -# - -[custom] -sections: nsec3param -[nsec3param] -rdlen: 4 diff --git a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.wire deleted file mode 100644 index adc5ea9f64..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec3param_fromWire2.wire +++ /dev/null @@ -1,10 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec3param_fromWire2.spec -### - -# NSEC3PARAM RDATA, RDLEN=4 -0004 -# Hash Alg=SHA1(1), Opt-Out=0, Other Flags=0, Iterations=1 -01 00 0001 -# Salt Len=5, Salt='sssss' -05 7373737373 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire1 b/src/lib/dns/tests/testdata/rdata_nsec_fromWire1 deleted file mode 100644 index 9c343941ff..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire1 +++ /dev/null @@ -1,6 +0,0 @@ -# RDLENGTH, 22 bytes -00 16 -# NSEC record -# www2.isc.org. CNAME RRSIG NSEC -04 77 77 77 32 03 69 73 63 03 6f 72 67 00 00 06 -04 00 00 00 00 03 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire10.spec b/src/lib/dns/tests/testdata/rdata_nsec_fromWire10.spec deleted file mode 100644 index 39d19ae15c..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire10.spec +++ /dev/null @@ -1,8 +0,0 @@ -# -# An invalid NSEC RDATA: a bitmap block containing empty bytes -# - -[custom] -sections: nsec -[nsec] -bitmap: '01000000' diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire10.wire b/src/lib/dns/tests/testdata/rdata_nsec_fromWire10.wire deleted file mode 100644 index 1145dbd586..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire10.wire +++ /dev/null @@ -1,10 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec_fromWire10.spec -### - -# NSEC RDATA, RDLEN=24 -0018 -# Next Name=next.example.com (18 bytes) -046e657874076578616d706c6503636f6d00 -# Bitmap: Block=0, Length=4 -00 04 01000000 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire16.spec b/src/lib/dns/tests/testdata/rdata_nsec_fromWire16.spec deleted file mode 100644 index d7faeedd8a..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire16.spec +++ /dev/null @@ -1,8 +0,0 @@ -# -# An invalid NSEC RDATA: with an empty bitmap -# - -[custom] -sections: nsec -[nsec] -nbitmap: 0 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire16.wire b/src/lib/dns/tests/testdata/rdata_nsec_fromWire16.wire deleted file mode 100644 index 943601aacb..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire16.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec_fromWire16.spec -### - -# NSEC RDATA, RDLEN=18 -0012 -# Next Name=next.example.com (18 bytes) -046e657874076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire2 b/src/lib/dns/tests/testdata/rdata_nsec_fromWire2 deleted file mode 100644 index e9b41e284a..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire2 +++ /dev/null @@ -1,10 +0,0 @@ -# -# NSEC RDATA with a bogus RDLEN (too short) -# - -# RDLENGTH, 13 bytes (should be 22) -00 0d -# NSEC record -# www2.isc.org. CNAME RRSIG NSEC -04 77 77 77 32 03 69 73 63 03 6f 72 67 00 00 06 -04 00 00 00 00 03 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire3 b/src/lib/dns/tests/testdata/rdata_nsec_fromWire3 deleted file mode 100644 index 67e6b3f112..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire3 +++ /dev/null @@ -1,10 +0,0 @@ -# -# NSEC RDATA with an invalid type bitmap -# - -# RDLENGTH, 22 bytes -00 16 -# NSEC record -# www2.isc.org. followed by a broken bitmap -04 77 77 77 32 03 69 73 63 03 6f 72 67 00 00 ff -00 00 00 00 00 00 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_nsec_fromWire4.spec deleted file mode 100644 index a5744c194f..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire4.spec +++ /dev/null @@ -1,9 +0,0 @@ -# -# A malformed NSEC RDATA: bit map length is too large, causing overflow -# - -[custom] -sections: nsec -[nsec] -maplen: 31 -bitmap: '01' diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_nsec_fromWire4.wire deleted file mode 100644 index 1c4d4a427f..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire4.wire +++ /dev/null @@ -1,10 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec_fromWire4.spec -### - -# NSEC RDATA, RDLEN=21 -0015 -# Next Name=next.example.com (18 bytes) -046e657874076578616d706c6503636f6d00 -# Bitmap: Block=0, Length=31 -00 1f 01 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_nsec_fromWire5.spec deleted file mode 100644 index 795d1e061b..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire5.spec +++ /dev/null @@ -1,13 +0,0 @@ -# -# A malformed NSEC RDATA: incomplete bit map field -# - -[custom] -sections: nsec -[nsec] -# only containing the block field of the bitmap -rdlen: 19 -#dummy data -maplen: 31 -#dummy data -bitmap: '00' diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire5.wire b/src/lib/dns/tests/testdata/rdata_nsec_fromWire5.wire deleted file mode 100644 index 104a2b9b98..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire5.wire +++ /dev/null @@ -1,10 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec_fromWire5.spec -### - -# NSEC RDATA, RDLEN=19 -0013 -# Next Name=next.example.com (18 bytes) -046e657874076578616d706c6503636f6d00 -# Bitmap: Block=0, Length=31 -00 1f 00 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_nsec_fromWire6.spec deleted file mode 100644 index cb864abcf1..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire6.spec +++ /dev/null @@ -1,11 +0,0 @@ -# -# A malformed NSEC RDATA: bit map length being 0 -# - -[custom] -sections: nsec -[nsec] -rdlen: 20 -maplen: 0 -# dummy data: -bitmap: '01' diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire6.wire b/src/lib/dns/tests/testdata/rdata_nsec_fromWire6.wire deleted file mode 100644 index 3108ed7f01..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire6.wire +++ /dev/null @@ -1,10 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec_fromWire6.spec -### - -# NSEC RDATA, RDLEN=20 -0014 -# Next Name=next.example.com (18 bytes) -046e657874076578616d706c6503636f6d00 -# Bitmap: Block=0, Length=0 -00 00 01 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire7.spec b/src/lib/dns/tests/testdata/rdata_nsec_fromWire7.spec deleted file mode 100644 index 46b521b525..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire7.spec +++ /dev/null @@ -1,9 +0,0 @@ -# -# NSEC RDATA with a longest bitmap field (32 bitmap bytes) -# - -[custom] -sections: nsec -[nsec] -maplen: 32 -bitmap: '0101010101010101010101010101010101010101010101010101010101010101' diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire7.wire b/src/lib/dns/tests/testdata/rdata_nsec_fromWire7.wire deleted file mode 100644 index e265a3b7e3..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire7.wire +++ /dev/null @@ -1,10 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec_fromWire7.spec -### - -# NSEC RDATA, RDLEN=52 -0034 -# Next Name=next.example.com (18 bytes) -046e657874076578616d706c6503636f6d00 -# Bitmap: Block=0, Length=32 -00 20 0101010101010101010101010101010101010101010101010101010101010101 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire8.spec b/src/lib/dns/tests/testdata/rdata_nsec_fromWire8.spec deleted file mode 100644 index 3f957a3ce1..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire8.spec +++ /dev/null @@ -1,9 +0,0 @@ -# -# An invalid NSEC RDATA with an oversized bitmap field (33 bitmap bytes) -# - -[custom] -sections: nsec -[nsec] -maplen: 33 -bitmap: '010101010101010101010101010101010101010101010101010101010101010101' diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire8.wire b/src/lib/dns/tests/testdata/rdata_nsec_fromWire8.wire deleted file mode 100644 index 066cd70c26..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire8.wire +++ /dev/null @@ -1,10 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec_fromWire8.spec -### - -# NSEC RDATA, RDLEN=53 -0035 -# Next Name=next.example.com (18 bytes) -046e657874076578616d706c6503636f6d00 -# Bitmap: Block=0, Length=33 -00 21 010101010101010101010101010101010101010101010101010101010101010101 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire9.spec b/src/lib/dns/tests/testdata/rdata_nsec_fromWire9.spec deleted file mode 100644 index a3bd0c90ec..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire9.spec +++ /dev/null @@ -1,10 +0,0 @@ -# -# An invalid NSEC RDATA: disordered bitmap blocks -# - -[custom] -sections: nsec -[nsec] -nbitmap: 2 -block0: 2 -block1: 1 diff --git a/src/lib/dns/tests/testdata/rdata_nsec_fromWire9.wire b/src/lib/dns/tests/testdata/rdata_nsec_fromWire9.wire deleted file mode 100644 index 53ad6cc3f4..0000000000 --- a/src/lib/dns/tests/testdata/rdata_nsec_fromWire9.wire +++ /dev/null @@ -1,12 +0,0 @@ -### -### This data file was auto-generated from rdata_nsec_fromWire9.spec -### - -# NSEC RDATA, RDLEN=34 -0022 -# Next Name=next.example.com (18 bytes) -046e657874076578616d706c6503636f6d00 -# Bitmap: Block=2, Length=6 -02 06 040000000003 -# Bitmap: Block=1, Length=6 -01 06 040000000003 diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire1.spec deleted file mode 100644 index edb9f345be..0000000000 --- a/src/lib/dns/tests/testdata/rdata_rp_fromWire1.spec +++ /dev/null @@ -1,6 +0,0 @@ -# -# A simplest form of RP: all default parameters -# -[custom] -sections: rp -[rp] diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire1.wire b/src/lib/dns/tests/testdata/rdata_rp_fromWire1.wire deleted file mode 100644 index aa939861af..0000000000 --- a/src/lib/dns/tests/testdata/rdata_rp_fromWire1.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_rp_fromWire1.spec -### - -# RP RDATA, RDLEN=39 -0027 -# MAILBOX=root.example.com TEXT=rp-text.example.com -04726f6f74076578616d706c6503636f6d00 0772702d74657874076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire2.spec deleted file mode 100644 index 57adb5a0a0..0000000000 --- a/src/lib/dns/tests/testdata/rdata_rp_fromWire2.spec +++ /dev/null @@ -1,12 +0,0 @@ -# -# A simplest form of RP: names are compressed. -# -[custom] -sections: name/1:name/2:rp -[name/1] -name: a.example.com -[name/2] -name: b.example.net -[rp] -mailbox: root.ptr=2 -text: rp-text.ptr=17 diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_rp_fromWire2.wire deleted file mode 100644 index 507bb1a9b5..0000000000 --- a/src/lib/dns/tests/testdata/rdata_rp_fromWire2.wire +++ /dev/null @@ -1,14 +0,0 @@ -### -### This data file was auto-generated from rdata_rp_fromWire2.spec -### - -# DNS Name: a.example.com -0161076578616d706c6503636f6d00 - -# DNS Name: b.example.net -0162076578616d706c65036e657400 - -# RP RDATA, RDLEN=17 -0011 -# MAILBOX=root.ptr=2 TEXT=rp-text.ptr=17 -04726f6f74c002 0772702d74657874c011 diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire3.spec deleted file mode 100644 index a238b7e2c5..0000000000 --- a/src/lib/dns/tests/testdata/rdata_rp_fromWire3.spec +++ /dev/null @@ -1,7 +0,0 @@ -# -# RP-like RDATA but RDLEN is too short. -# -[custom] -sections: rp -[rp] -rdlen: 38 diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire3.wire b/src/lib/dns/tests/testdata/rdata_rp_fromWire3.wire deleted file mode 100644 index c2a7086093..0000000000 --- a/src/lib/dns/tests/testdata/rdata_rp_fromWire3.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_rp_fromWire3.spec -### - -# RP RDATA, RDLEN=38 -0026 -# MAILBOX=root.example.com TEXT=rp-text.example.com -04726f6f74076578616d706c6503636f6d00 0772702d74657874076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire4.spec deleted file mode 100644 index 6f3abd1a3a..0000000000 --- a/src/lib/dns/tests/testdata/rdata_rp_fromWire4.spec +++ /dev/null @@ -1,7 +0,0 @@ -# -# RP-like RDATA but RDLEN is too long. -# -[custom] -sections: rp -[rp] -rdlen: 40 diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_rp_fromWire4.wire deleted file mode 100644 index 56c643e531..0000000000 --- a/src/lib/dns/tests/testdata/rdata_rp_fromWire4.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_rp_fromWire4.spec -### - -# RP RDATA, RDLEN=40 -0028 -# MAILBOX=root.example.com TEXT=rp-text.example.com -04726f6f74076578616d706c6503636f6d00 0772702d74657874076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire5.spec deleted file mode 100644 index b8d5e29cc0..0000000000 --- a/src/lib/dns/tests/testdata/rdata_rp_fromWire5.spec +++ /dev/null @@ -1,7 +0,0 @@ -# -# RP-like RDATA but mailbox name is broken. -# -[custom] -sections: rp -[rp] -mailbox: "01234567890123456789012345678901234567890123456789012345678901234" diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire5.wire b/src/lib/dns/tests/testdata/rdata_rp_fromWire5.wire deleted file mode 100644 index 1127047130..0000000000 --- a/src/lib/dns/tests/testdata/rdata_rp_fromWire5.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_rp_fromWire5.spec -### - -# RP RDATA, RDLEN=90 -005a -# MAILBOX="01234567890123456789012345678901234567890123456789012345678901234" TEXT=rp-text.example.com -432230313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233342200 0772702d74657874076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_rp_fromWire6.spec deleted file mode 100644 index e9e79f30f7..0000000000 --- a/src/lib/dns/tests/testdata/rdata_rp_fromWire6.spec +++ /dev/null @@ -1,7 +0,0 @@ -# -# RP-like RDATA but text name is broken. -# -[custom] -sections: rp -[rp] -text: "01234567890123456789012345678901234567890123456789012345678901234" diff --git a/src/lib/dns/tests/testdata/rdata_rp_fromWire6.wire b/src/lib/dns/tests/testdata/rdata_rp_fromWire6.wire deleted file mode 100644 index 49aedc6593..0000000000 --- a/src/lib/dns/tests/testdata/rdata_rp_fromWire6.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_rp_fromWire6.spec -### - -# RP RDATA, RDLEN=87 -0057 -# MAILBOX=root.example.com TEXT="01234567890123456789012345678901234567890123456789012345678901234" -04726f6f74076578616d706c6503636f6d00 432230313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334353637383930313233342200 diff --git a/src/lib/dns/tests/testdata/rdata_rp_toWire1.spec b/src/lib/dns/tests/testdata/rdata_rp_toWire1.spec deleted file mode 100644 index 948bd1ad5c..0000000000 --- a/src/lib/dns/tests/testdata/rdata_rp_toWire1.spec +++ /dev/null @@ -1,8 +0,0 @@ -# -# A simplest form of RP for toWire test: all default parameters except rdlen, -# which is to be omitted. -# -[custom] -sections: rp -[rp] -rdlen: -1 diff --git a/src/lib/dns/tests/testdata/rdata_rp_toWire1.wire b/src/lib/dns/tests/testdata/rdata_rp_toWire1.wire deleted file mode 100644 index dcadb154b9..0000000000 --- a/src/lib/dns/tests/testdata/rdata_rp_toWire1.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_rp_toWire1.spec -### - -# RP RDATA - -# MAILBOX=root.example.com TEXT=rp-text.example.com -04726f6f74076578616d706c6503636f6d00 0772702d74657874076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdata_rp_toWire2.spec b/src/lib/dns/tests/testdata/rdata_rp_toWire2.spec deleted file mode 100644 index 09a7ddc255..0000000000 --- a/src/lib/dns/tests/testdata/rdata_rp_toWire2.spec +++ /dev/null @@ -1,14 +0,0 @@ -# -# A simple form of RP: names could be compressed (but MUST NOT). -# rdlen is omitted for the "to wire" test. -# -[custom] -sections: name/1:name/2:rp -[name/1] -name: a.example.com -[name/2] -name: b.example.net -[rp] -rdlen: -1 -mailbox: root.example.com -text: rp-text.example.net diff --git a/src/lib/dns/tests/testdata/rdata_rp_toWire2.wire b/src/lib/dns/tests/testdata/rdata_rp_toWire2.wire deleted file mode 100644 index 38057eff68..0000000000 --- a/src/lib/dns/tests/testdata/rdata_rp_toWire2.wire +++ /dev/null @@ -1,14 +0,0 @@ -### -### This data file was auto-generated from rdata_rp_toWire2.spec -### - -# DNS Name: a.example.com -0161076578616d706c6503636f6d00 - -# DNS Name: b.example.net -0162076578616d706c65036e657400 - -# RP RDATA - -# MAILBOX=root.example.com TEXT=rp-text.example.net -04726f6f74076578616d706c6503636f6d00 0772702d74657874076578616d706c65036e657400 diff --git a/src/lib/dns/tests/testdata/rdata_srv_fromWire b/src/lib/dns/tests/testdata/rdata_srv_fromWire deleted file mode 100644 index 0f1e4ec75a..0000000000 --- a/src/lib/dns/tests/testdata/rdata_srv_fromWire +++ /dev/null @@ -1,36 +0,0 @@ -# -# various kinds of SRV RDATA stored in an input buffer -# -# RDLENGTH=21 bytes -# 0 1 - 00 15 -# 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 20 1 2(bytes) - 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 -# -# short length -# 3 4 - 00 12 -# 5 6 7 8 9 30 1 2 3 4 5 6 7 8 9 40 1 2 3 4 5 - 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 -# -# length too long -# 6 7 - 00 19 -# -# 8 9 50 1 2 3 4 5 6 7 8 9 60 1 2 3 4 5 6 7 8 - 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63 6f 6d 00 -# -# -# incomplete target name -# 9 70 - 00 12 -# 1 2 3 4 5 6 7 8 9 80 1 2 3 4 5 6 7 8 - 00 01 00 05 05 dc 01 61 07 65 78 61 6d 70 6c 65 03 63 -# -# -# Valid compressed target name: 'a' + pointer -# 9 90 - 00 0a -# -# 1 2 3 4 5 6 7 8 9 100 - 00 01 00 05 05 dc 01 61 c0 0a diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire deleted file mode 100644 index added405ca..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire +++ /dev/null @@ -1,4 +0,0 @@ -# SSHFP RDATA, RDLEN=22 -0016 -# ALGORITHM=2 FINGERPRINT_TYPE=1 FINGERPRINT=123456789abcdef67890123456789abcdef67890 -02 01 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.spec deleted file mode 100644 index e28a62fb0f..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.spec +++ /dev/null @@ -1,6 +0,0 @@ -# -# A simplest form of SSHFP: all default parameters -# -[custom] -sections: sshfp -[sshfp] diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.wire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.wire deleted file mode 100644 index c7059e1452..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_sshfp_fromWire1.spec -### - -# SSHFP RDATA, RDLEN=22 -0016 -# ALGORITHM=2 FINGERPRINT_TYPE=1 FINGERPRINT=123456789abcdef67890123456789abcdef67890 -02 01 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire10 b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire10 deleted file mode 100644 index 220e570563..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire10 +++ /dev/null @@ -1,6 +0,0 @@ -# Test where fingerprint is missing - -# SSHFP RDATA, RDLEN=32 -0020 -# ALGORITHM=2 FINGERPRINT_TYPE=1 FINGERPRINT=(none) -02 01 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire11 b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire11 deleted file mode 100644 index a2f86368e1..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire11 +++ /dev/null @@ -1,4 +0,0 @@ -# Test where RDATA is completely missing - -# SSHFP RDATA, RDLEN=32 -0020 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire12 b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire12 deleted file mode 100644 index eabd06b97f..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire12 +++ /dev/null @@ -1,4 +0,0 @@ -# SSHFP RDATA, RDLEN=02 -0002 -# ALGORITHM=4 FINGERPRINT_TYPE=9 -04 09 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2 b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2 deleted file mode 100644 index a695548fbb..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2 +++ /dev/null @@ -1,4 +0,0 @@ -# SSHFP RDATA, RDLEN=22 -0016 -# ALGORITHM=2 FINGERPRINT_TYPE=1 FINGERPRINT=123456789ABCDEF67890123456789abcdef67890 -02 01 123456789ABCDEF67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec deleted file mode 100644 index 59a336e592..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec +++ /dev/null @@ -1,7 +0,0 @@ -# -# SSHFP RDATA -# -[custom] -sections: sshfp -[sshfp] -fingerprint: 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.wire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.wire deleted file mode 100644 index e492316027..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_sshfp_fromWire2.spec -### - -# SSHFP RDATA, RDLEN=22 -0016 -# ALGORITHM=2 FINGERPRINT_TYPE=1 FINGERPRINT=123456789abcdef67890123456789abcdef67890 -02 01 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire3.spec deleted file mode 100644 index d111afde9a..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire3.spec +++ /dev/null @@ -1,8 +0,0 @@ -# -# SSHFP RDATA -# -[custom] -sections: sshfp -[sshfp] -algorithm: 1 -fingerprint_type: 1 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire3.wire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire3.wire deleted file mode 100644 index daa102f483..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire3.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_sshfp_fromWire3.spec -### - -# SSHFP RDATA, RDLEN=22 -0016 -# ALGORITHM=1 FINGERPRINT_TYPE=1 FINGERPRINT=123456789abcdef67890123456789abcdef67890 -01 01 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire4.spec deleted file mode 100644 index b9b2658b1a..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire4.spec +++ /dev/null @@ -1,8 +0,0 @@ -# -# SSHFP RDATA -# -[custom] -sections: sshfp -[sshfp] -algorithm: 255 -fingerprint_type: 1 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire4.wire deleted file mode 100644 index f05faad9d4..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire4.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_sshfp_fromWire4.spec -### - -# SSHFP RDATA, RDLEN=22 -0016 -# ALGORITHM=255 FINGERPRINT_TYPE=1 FINGERPRINT=123456789abcdef67890123456789abcdef67890 -ff 01 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire5.spec deleted file mode 100644 index b3a19facf7..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire5.spec +++ /dev/null @@ -1,8 +0,0 @@ -# -# SSHFP RDATA -# -[custom] -sections: sshfp -[sshfp] -algorithm: 0 -fingerprint_type: 1 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire5.wire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire5.wire deleted file mode 100644 index ddc8edb57e..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire5.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_sshfp_fromWire5.spec -### - -# SSHFP RDATA, RDLEN=22 -0016 -# ALGORITHM=0 FINGERPRINT_TYPE=1 FINGERPRINT=123456789abcdef67890123456789abcdef67890 -00 01 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire6.spec deleted file mode 100644 index 437e282c6c..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire6.spec +++ /dev/null @@ -1,8 +0,0 @@ -# -# SSHFP RDATA -# -[custom] -sections: sshfp -[sshfp] -algorithm: 5 -fingerprint_type: 0 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire6.wire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire6.wire deleted file mode 100644 index d4e591e4b7..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire6.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_sshfp_fromWire6.spec -### - -# SSHFP RDATA, RDLEN=22 -0016 -# ALGORITHM=5 FINGERPRINT_TYPE=0 FINGERPRINT=123456789abcdef67890123456789abcdef67890 -05 00 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire7.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire7.spec deleted file mode 100644 index 8e21d11026..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire7.spec +++ /dev/null @@ -1,8 +0,0 @@ -# -# SSHFP RDATA -# -[custom] -sections: sshfp -[sshfp] -algorithm: 255 -fingerprint_type: 255 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire7.wire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire7.wire deleted file mode 100644 index c8b9d8b2e6..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire7.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_sshfp_fromWire7.spec -### - -# SSHFP RDATA, RDLEN=22 -0016 -# ALGORITHM=255 FINGERPRINT_TYPE=255 FINGERPRINT=123456789abcdef67890123456789abcdef67890 -ff ff 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire8.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire8.spec deleted file mode 100644 index 98aa00f97b..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire8.spec +++ /dev/null @@ -1,9 +0,0 @@ -# -# SSHFP RDATA -# -[custom] -sections: sshfp -[sshfp] -fingerprint: 082359342fd9 -algorithm: 255 -fingerprint_type: 255 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire8.wire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire8.wire deleted file mode 100644 index 32cede87e5..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire8.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_sshfp_fromWire8.spec -### - -# SSHFP RDATA, RDLEN=8 -0008 -# ALGORITHM=255 FINGERPRINT_TYPE=255 FINGERPRINT=082359342fd9 -ff ff 082359342fd9 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire9 b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire9 deleted file mode 100644 index 05fc80684b..0000000000 --- a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire9 +++ /dev/null @@ -1,6 +0,0 @@ -# Test where fingerprint length is smaller than what RDATA len indicates - -# SSHFP RDATA, RDLEN=32 -0020 -# ALGORITHM=2 FINGERPRINT_TYPE=1 FINGERPRINT=123456789abcdef67890123456789abcdef67890 -02 01 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire deleted file mode 100644 index 38e279ceca..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire +++ /dev/null @@ -1,4 +0,0 @@ -# TLSA RDATA, RDLEN=35 -0023 -# CERTIFICATE_USAGE=0 SELECTOR=0 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=... -00 00 01 d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire10 b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire10 deleted file mode 100644 index 67cecb15de..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire10 +++ /dev/null @@ -1,6 +0,0 @@ -# Test where certificate association data is missing. - -# TLSA RDATA, RDLEN=35 -0023 -# CERTIFICATE_USAGE=0 SELECTOR=0 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=(missing) -00 00 01 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire11 b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire11 deleted file mode 100644 index 4b8ec930e2..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire11 +++ /dev/null @@ -1,4 +0,0 @@ -# Test where RDATA is completely missing - -# TLSA RDATA, RDLEN=35 -0023 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire12 b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire12 deleted file mode 100644 index 71c7b9ceb5..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire12 +++ /dev/null @@ -1,4 +0,0 @@ -# TLSA RDATA, RDLEN=3 -0003 -# CERTIFICATE_USAGE=0 SELECTOR=0 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=(none) -03 01 02 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire2 b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire2 deleted file mode 100644 index 36ce27806f..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire2 +++ /dev/null @@ -1,4 +0,0 @@ -# TLSA RDATA, RDLEN=35 -0023 -# CERTIFICATE_USAGE=0 SELECTOR=0 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=... -00 00 01 d2abde240d7cd3ee6b4b28c54df034b97983A1D16E8A410E4561CB106618E971 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire3.spec b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire3.spec deleted file mode 100644 index 39c8057d6a..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire3.spec +++ /dev/null @@ -1,7 +0,0 @@ -# -# TLSA RDATA -# -[custom] -sections: tlsa -[tlsa] -certificate_usage: 0 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire3.wire b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire3.wire deleted file mode 100644 index 6a8b552af2..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire3.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_tlsa_fromWire3.spec -### - -# TLSA RDATA, RDLEN=34 -0022 -# CERTIFICATE_USAGE=0 SELECTOR=0 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 -00 00 01 d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire4.spec deleted file mode 100644 index d97ae6a9e8..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire4.spec +++ /dev/null @@ -1,7 +0,0 @@ -# -# TLSA RDATA -# -[custom] -sections: tlsa -[tlsa] -certificate_usage: 255 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire4.wire b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire4.wire deleted file mode 100644 index b5a39c85be..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire4.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_tlsa_fromWire4.spec -### - -# TLSA RDATA, RDLEN=34 -0022 -# CERTIFICATE_USAGE=255 SELECTOR=0 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 -ff 00 01 d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire5.spec deleted file mode 100644 index cc3e296b84..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire5.spec +++ /dev/null @@ -1,7 +0,0 @@ -# -# TLSA RDATA -# -[custom] -sections: tlsa -[tlsa] -selector: 255 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire5.wire b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire5.wire deleted file mode 100644 index b79f97d93c..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire5.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_tlsa_fromWire5.spec -### - -# TLSA RDATA, RDLEN=34 -0022 -# CERTIFICATE_USAGE=0 SELECTOR=255 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 -00 ff 01 d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire6.spec deleted file mode 100644 index eed0ab90a6..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire6.spec +++ /dev/null @@ -1,7 +0,0 @@ -# -# TLSA RDATA -# -[custom] -sections: tlsa -[tlsa] -matching_type: 255 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire6.wire b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire6.wire deleted file mode 100644 index 02afe27fd1..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire6.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_tlsa_fromWire6.spec -### - -# TLSA RDATA, RDLEN=34 -0022 -# CERTIFICATE_USAGE=0 SELECTOR=0 MATCHING_TYPE=255 CERTIFICATE_ASSOCIATION_DATA=d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 -00 00 ff d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire7.spec b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire7.spec deleted file mode 100644 index 576df1ef36..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire7.spec +++ /dev/null @@ -1,9 +0,0 @@ -# -# TLSA RDATA -# -[custom] -sections: tlsa -[tlsa] -certificate_usage: 3 -selector: 1 -matching_type: 2 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire7.wire b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire7.wire deleted file mode 100644 index e5c23a4e1f..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire7.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_tlsa_fromWire7.spec -### - -# TLSA RDATA, RDLEN=34 -0022 -# CERTIFICATE_USAGE=3 SELECTOR=1 MATCHING_TYPE=2 CERTIFICATE_ASSOCIATION_DATA=d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 -03 01 02 d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire8.spec b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire8.spec deleted file mode 100644 index ef5c108dff..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire8.spec +++ /dev/null @@ -1,7 +0,0 @@ -# -# TLSA RDATA -# -[custom] -sections: tlsa -[tlsa] -certificate_association_data: '0123' diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire8.wire b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire8.wire deleted file mode 100644 index dc29a0bee5..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire8.wire +++ /dev/null @@ -1,8 +0,0 @@ -### -### This data file was auto-generated from rdata_tlsa_fromWire8.spec -### - -# TLSA RDATA, RDLEN=4 -0004 -# CERTIFICATE_USAGE=0 SELECTOR=0 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=0123 -00 00 01 0123 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire9 b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire9 deleted file mode 100644 index fc4560af51..0000000000 --- a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire9 +++ /dev/null @@ -1,7 +0,0 @@ -# Test where certificate association data length is smaller than what -# RDATA length indicates. - -# TLSA RDATA, RDLEN=64 -0040 -# CERTIFICATE_USAGE=0 SELECTOR=0 MATCHING_TYPE=1 CERTIFICATE_ASSOCIATION_DATA=(32 bytes) -00 00 01 d2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971 diff --git a/src/lib/dns/tests/testdata/rdatafields1.spec b/src/lib/dns/tests/testdata/rdatafields1.spec deleted file mode 100644 index 6e105fbe6c..0000000000 --- a/src/lib/dns/tests/testdata/rdatafields1.spec +++ /dev/null @@ -1,10 +0,0 @@ -# -# A sequence of names that could be compressed (but not compressed) -# - -[custom] -sections: name/1:name/2 -[name/1] -name: www.example.com -[name/2] -name: example.com diff --git a/src/lib/dns/tests/testdata/rdatafields1.wire b/src/lib/dns/tests/testdata/rdatafields1.wire deleted file mode 100644 index 42028c167d..0000000000 --- a/src/lib/dns/tests/testdata/rdatafields1.wire +++ /dev/null @@ -1,9 +0,0 @@ -### -### This data file was auto-generated from rdatafields1.spec -### - -# DNS Name: www.example.com -03777777076578616d706c6503636f6d00 - -# DNS Name: example.com -076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdatafields2.spec b/src/lib/dns/tests/testdata/rdatafields2.spec deleted file mode 100644 index 920dc956e6..0000000000 --- a/src/lib/dns/tests/testdata/rdatafields2.spec +++ /dev/null @@ -1,11 +0,0 @@ -# -# A sequence of names that can be compressed. -# - -[custom] -sections: name/1:name/2 -[name/1] -name: www.example.com -[name/2] -name: '' -pointer: 4 diff --git a/src/lib/dns/tests/testdata/rdatafields2.wire b/src/lib/dns/tests/testdata/rdatafields2.wire deleted file mode 100644 index cbb0ce0685..0000000000 --- a/src/lib/dns/tests/testdata/rdatafields2.wire +++ /dev/null @@ -1,9 +0,0 @@ -### -### This data file was auto-generated from rdatafields2.spec -### - -# DNS Name: www.example.com -03777777076578616d706c6503636f6d00 - -# DNS Name: + compression pointer: 4 -c004 diff --git a/src/lib/dns/tests/testdata/rdatafields3.spec b/src/lib/dns/tests/testdata/rdatafields3.spec deleted file mode 100644 index b37fca3483..0000000000 --- a/src/lib/dns/tests/testdata/rdatafields3.spec +++ /dev/null @@ -1,11 +0,0 @@ -# -# TXT RDATA with multiple character-strings. -# - -[custom] -sections: txt -[txt] -nstring: 3 -string0: 'first string' -string1: 'second string' -string2: 'last string' diff --git a/src/lib/dns/tests/testdata/rdatafields3.wire b/src/lib/dns/tests/testdata/rdatafields3.wire deleted file mode 100644 index 3ff32f1692..0000000000 --- a/src/lib/dns/tests/testdata/rdatafields3.wire +++ /dev/null @@ -1,12 +0,0 @@ -### -### This data file was auto-generated from rdatafields3.spec -### - -# TXT RDATA, RDLEN=39 -0027 -# String Len=12, String="first string" -0c 666972737420737472696e67 -# String Len=13, String="second string" -0d 7365636f6e6420737472696e67 -# String Len=11, String="last string" -0b 6c61737420737472696e67 diff --git a/src/lib/dns/tests/testdata/rdatafields4.spec b/src/lib/dns/tests/testdata/rdatafields4.spec deleted file mode 100644 index 24b59aaeb3..0000000000 --- a/src/lib/dns/tests/testdata/rdatafields4.spec +++ /dev/null @@ -1,7 +0,0 @@ -# -# Simple form of RRSIG (all fields use the default of generator script) -# - -[custom] -sections: rrsig -[rrsig] diff --git a/src/lib/dns/tests/testdata/rdatafields4.wire b/src/lib/dns/tests/testdata/rdatafields4.wire deleted file mode 100644 index e5744266c9..0000000000 --- a/src/lib/dns/tests/testdata/rdatafields4.wire +++ /dev/null @@ -1,12 +0,0 @@ -### -### This data file was auto-generated from rdatafields4.spec -### - -# RRSIG RDATA, RDLEN=46 -002e -# Covered=A(1) Algorithm=RSASHA1(5) Labels=2 OrigTTL=3600 -0001 05 02 00000e10 -# Expiration=1264935600, Inception=1262343600 -4b6562b0 4b3dd5b0 -# Tag=4149 Signer=example.com and Signature -1035 076578616d706c6503636f6d00 123456789abcdef123456789abcdef diff --git a/src/lib/dns/tests/testdata/rdatafields5.spec b/src/lib/dns/tests/testdata/rdatafields5.spec deleted file mode 100644 index 2c7828250e..0000000000 --- a/src/lib/dns/tests/testdata/rdatafields5.spec +++ /dev/null @@ -1,12 +0,0 @@ -# -# Names and RDATA (RRSIG) with an incompressible name. All names are -# rendered without compression. -# - -[custom] -sections: name/1:rrsig:name/2 -[name/1] -name: com -[rrsig] -[name/2] -name: www.example.com diff --git a/src/lib/dns/tests/testdata/rdatafields5.wire b/src/lib/dns/tests/testdata/rdatafields5.wire deleted file mode 100644 index d0cebc97d5..0000000000 --- a/src/lib/dns/tests/testdata/rdatafields5.wire +++ /dev/null @@ -1,18 +0,0 @@ -### -### This data file was auto-generated from rdatafields5.spec -### - -# DNS Name: com -03636f6d00 - -# RRSIG RDATA, RDLEN=46 -002e -# Covered=A(1) Algorithm=RSASHA1(5) Labels=2 OrigTTL=3600 -0001 05 02 00000e10 -# Expiration=1264935600, Inception=1262343600 -4b6562b0 4b3dd5b0 -# Tag=4149 Signer=example.com and Signature -1035 076578616d706c6503636f6d00 123456789abcdef123456789abcdef - -# DNS Name: www.example.com -03777777076578616d706c6503636f6d00 diff --git a/src/lib/dns/tests/testdata/rdatafields6.spec b/src/lib/dns/tests/testdata/rdatafields6.spec deleted file mode 100644 index f9f0da18f7..0000000000 --- a/src/lib/dns/tests/testdata/rdatafields6.spec +++ /dev/null @@ -1,13 +0,0 @@ -# -# Names and RDATA (RRSIG) with an incompressible name. The name in RRSIG -# isn't compressed, but it's used as the compression target. -# - -[custom] -sections: name/1:rrsig:name/2 -[name/1] -name: com -[rrsig] -[name/2] -name: www -pointer: 25 diff --git a/src/lib/dns/tests/testdata/rdatafields6.wire b/src/lib/dns/tests/testdata/rdatafields6.wire deleted file mode 100644 index 0e8b3d9001..0000000000 --- a/src/lib/dns/tests/testdata/rdatafields6.wire +++ /dev/null @@ -1,18 +0,0 @@ -### -### This data file was auto-generated from rdatafields6.spec -### - -# DNS Name: com -03636f6d00 - -# RRSIG RDATA, RDLEN=46 -002e -# Covered=A(1) Algorithm=RSASHA1(5) Labels=2 OrigTTL=3600 -0001 05 02 00000e10 -# Expiration=1264935600, Inception=1262343600 -4b6562b0 4b3dd5b0 -# Tag=4149 Signer=example.com and Signature -1035 076578616d706c6503636f6d00 123456789abcdef123456789abcdef - -# DNS Name: www + compression pointer: 25 -03777777c019 diff --git a/src/lib/dns/tests/tsig_unittest.cc b/src/lib/dns/tests/tsig_unittest.cc index 8b13789179..ca2a28c68e 100644 --- a/src/lib/dns/tests/tsig_unittest.cc +++ b/src/lib/dns/tests/tsig_unittest.cc @@ -1 +1,1181 @@ +// Copyright (C) 2011-2024 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <time.h> +#include <string> +#include <stdexcept> +#include <vector> + +#include <boost/scoped_ptr.hpp> + +#include <gtest/gtest.h> + +#include <exceptions/exceptions.h> + +#include <util/buffer.h> +#include <util/encode/encode.h> +#include <util/unittests/newhook.h> +#include <util/time_utilities.h> + +#include <dns/message.h> +#include <dns/messagerenderer.h> +#include <dns/question.h> +#include <dns/opcode.h> +#include <dns/rcode.h> +#include <dns/rrclass.h> +#include <dns/rrtype.h> +#include <dns/tsig.h> +#include <dns/tsigkey.h> +#include <dns/tsigrecord.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::util::encode; +using namespace isc::dns::rdata; +using namespace isc::dns::rdata::any; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +// @note: blocks and SCOPED_TRACE can make buggy cppchecks raise +// a spurious syntax error... + +// See dnssectime.cc +namespace isc { +namespace util { +namespace detail { +extern int64_t (*gettimeFunction)(); +} +} +} + +namespace { +// See dnssectime_unittest.cc +template <int64_t NOW> +int64_t +testGetTime() { + return (NOW); +} + +// Thin wrapper around TSIGContext to allow access to the +// update method. +class TestTSIGContext : public TSIGContext { +public: + TestTSIGContext(const TSIGKey& key) : + TSIGContext(key) { + } + TestTSIGContext(const Name& key_name, const Name& algorithm_name, + const TSIGKeyRing& keyring) : + TSIGContext(key_name, algorithm_name, keyring) { + } + void update(const void* const data, size_t len) { + TSIGContext::update(data, len); + } +}; + +class TSIGTest : public ::testing::Test { +protected: + TSIGTest() : + tsig_ctx(NULL), qid(0x2d65), test_name("www.example.com"), + badkey_name("badkey.example.com"), test_class(RRClass::IN()), + test_ttl(86400), message(Message::RENDER), + dummy_data(1024, 0xdd), // should be sufficiently large for all tests + dummy_record(badkey_name, TSIG(TSIGKey::HMACMD5_NAME(), 0x4da8877a, + TSIGContext::DEFAULT_FUDGE, 0, NULL, qid, 0, 0, NULL)) { + // Make sure we use the system time by default so that we won't be + // confused due to other tests that tweak the time. + isc::util::detail::gettimeFunction = NULL; + + decodeBase64("SFuWd/q99SzF8Yzd1QbB9g==", secret); + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACMD5_NAME(), + &secret[0], + secret.size()))); + tsig_verify_ctx.reset(new TSIGContext(TSIGKey(test_name, + TSIGKey::HMACMD5_NAME(), + &secret[0], + secret.size()))); + } + ~TSIGTest() { + isc::util::detail::gettimeFunction = NULL; + } + + // Many of the tests below create some DNS message and sign it under + // some specific TSIG context. This helper method unifies the common + // logic with slightly different parameters. + ConstTSIGRecordPtr createMessageAndSign(uint16_t qid, const Name& qname, + TSIGContext* ctx, + unsigned int message_flags = + RD_FLAG, + RRType qtype = RRType::A(), + const char* answer_data = NULL, + const RRType* answer_type = NULL, + bool add_question = true, + Rcode rcode = Rcode::NOERROR()); + + void createMessageFromFile(const char* datafile); + + // bit-wise constant flags to configure DNS header flags for test + // messages. + static const unsigned int QR_FLAG = 0x1; + static const unsigned int AA_FLAG = 0x2; + static const unsigned int RD_FLAG = 0x4; + + boost::scoped_ptr<TestTSIGContext> tsig_ctx; + boost::scoped_ptr<TSIGContext> tsig_verify_ctx; + TSIGKeyRing keyring; + const uint16_t qid; + const Name test_name; + const Name badkey_name; + const RRClass test_class; + const RRTTL test_ttl; + Message message; + MessageRenderer renderer; + vector<uint8_t> secret; + vector<uint8_t> dummy_data; + const TSIGRecord dummy_record; + vector<uint8_t> received_data; +}; + +ConstTSIGRecordPtr +TSIGTest::createMessageAndSign(uint16_t id, const Name& qname, + TSIGContext* ctx, unsigned int message_flags, + RRType qtype, const char* answer_data, + const RRType* answer_type, bool add_question, + Rcode rcode) { + message.clear(Message::RENDER); + message.setQid(id); + message.setOpcode(Opcode::QUERY()); + message.setRcode(rcode); + if ((message_flags & QR_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_QR); + } + if ((message_flags & AA_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_AA); + } + if ((message_flags & RD_FLAG) != 0) { + message.setHeaderFlag(Message::HEADERFLAG_RD); + } + if (add_question) { + message.addQuestion(Question(qname, test_class, qtype)); + } + if (answer_data != NULL) { + if (answer_type == NULL) { + answer_type = &qtype; + } + RRsetPtr answer_rrset(new RRset(qname, test_class, *answer_type, + test_ttl)); + answer_rrset->addRdata(createRdata(*answer_type, test_class, + answer_data)); + message.addRRset(Message::SECTION_ANSWER, answer_rrset); + } + renderer.clear(); + + TSIGContext::State expected_new_state = + (ctx->getState() == TSIGContext::INIT) ? + TSIGContext::SENT_REQUEST : TSIGContext::SENT_RESPONSE; + + message.toWire(renderer, ctx); + + message.clear(Message::PARSE); + InputBuffer buffer(renderer.getData(), renderer.getLength()); + message.fromWire(buffer); + + EXPECT_EQ(expected_new_state, ctx->getState()); + + return (ConstTSIGRecordPtr(new TSIGRecord(*message.getTSIGRecord()))); +} + +void +TSIGTest::createMessageFromFile(const char* datafile) { + message.clear(Message::PARSE); + received_data.clear(); + UnitTestUtil::readWireData(datafile, received_data); + InputBuffer buffer(&received_data[0], received_data.size()); + message.fromWire(buffer); +} + +void +commonSignChecks(ConstTSIGRecordPtr tsig, uint16_t expected_qid, + uint64_t expected_timesigned, + const uint8_t* expected_mac, size_t expected_maclen, + uint16_t expected_error = 0, + uint16_t expected_otherlen = 0, + const uint8_t* expected_otherdata = NULL, + const Name& expected_algorithm = TSIGKey::HMACMD5_NAME()) { + ASSERT_TRUE(tsig != NULL); + const TSIG& tsig_rdata = tsig->getRdata(); + + EXPECT_EQ(expected_algorithm, tsig_rdata.getAlgorithm()); + EXPECT_EQ(expected_timesigned, tsig_rdata.getTimeSigned()); + EXPECT_EQ(300, tsig_rdata.getFudge()); + EXPECT_EQ(expected_maclen, tsig_rdata.getMACSize()); + matchWireData(expected_mac, expected_maclen, + tsig_rdata.getMAC(), tsig_rdata.getMACSize()); + + EXPECT_EQ(expected_qid, tsig_rdata.getOriginalID()); + EXPECT_EQ(expected_error, tsig_rdata.getError()); + EXPECT_EQ(expected_otherlen, tsig_rdata.getOtherLen()); + matchWireData(expected_otherdata, expected_otherlen, + tsig_rdata.getOtherData(), tsig_rdata.getOtherLen()); +} + +void +commonVerifyChecks(TSIGContext& ctx, const TSIGRecord* record, + const void* data, size_t data_len, TSIGError expected_error, + TSIGContext::State expected_new_state = + TSIGContext::VERIFIED_RESPONSE, + bool last_should_throw = false) { + EXPECT_EQ(expected_error, ctx.verify(record, data, data_len)); + EXPECT_EQ(expected_error, ctx.getError()); + EXPECT_EQ(expected_new_state, ctx.getState()); + if (last_should_throw) { + EXPECT_THROW(ctx.lastHadSignature(), TSIGContextError); + } else { + EXPECT_EQ(record != NULL, ctx.lastHadSignature()); + } +} + +TEST_F(TSIGTest, initialState) { + // Until signing or verifying, the state should be INIT + EXPECT_EQ(TSIGContext::INIT, tsig_ctx->getState()); + + // And there should be no error code. + EXPECT_EQ(TSIGError(Rcode::NOERROR()), tsig_ctx->getError()); + + // Nothing verified yet + EXPECT_THROW(tsig_ctx->lastHadSignature(), TSIGContextError); +} + +TEST_F(TSIGTest, constructFromKeyRing) { + // Construct a TSIG context with an empty key ring. Key shouldn't be + // found, and the BAD_KEY error should be recorded. + TSIGContext ctx1(test_name, TSIGKey::HMACMD5_NAME(), keyring); + EXPECT_EQ(TSIGContext::INIT, ctx1.getState()); + EXPECT_EQ(TSIGError::BAD_KEY(), ctx1.getError()); + + // Add a matching key (we don't use the secret so leave it empty), and + // construct it again. This time it should be constructed with a valid + // key. + keyring.add(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(), NULL, 0)); + TSIGContext ctx2(test_name, TSIGKey::HMACMD5_NAME(), keyring); + EXPECT_EQ(TSIGContext::INIT, ctx2.getState()); + EXPECT_EQ(TSIGError::NOERROR(), ctx2.getError()); + + // Similar to the first case except that the key ring isn't empty but + // it doesn't contain a matching key. + TSIGContext ctx3(test_name, TSIGKey::HMACSHA1_NAME(), keyring); + EXPECT_EQ(TSIGContext::INIT, ctx3.getState()); + EXPECT_EQ(TSIGError::BAD_KEY(), ctx3.getError()); + + TSIGContext ctx4(Name("different-key.example"), TSIGKey::HMACMD5_NAME(), + keyring); + EXPECT_EQ(TSIGContext::INIT, ctx4.getState()); + EXPECT_EQ(TSIGError::BAD_KEY(), ctx4.getError()); + + // "Unknown" algorithm name will result in BADKEY, too. + TSIGContext ctx5(test_name, Name("unknown.algorithm"), keyring); + EXPECT_EQ(TSIGContext::INIT, ctx5.getState()); + EXPECT_EQ(TSIGError::BAD_KEY(), ctx5.getError()); +} + +// Example output generated by +// "dig -y www.example.com:SFuWd/q99SzF8Yzd1QbB9g== www.example.com +// QID: 0x2d65 +// Time Signed: 0x00004da8877a +// MAC: 227026ad297beee721ce6c6fff1e9ef3 +const uint8_t common_expected_mac[] = { + 0x22, 0x70, 0x26, 0xad, 0x29, 0x7b, 0xee, 0xe7, + 0x21, 0xce, 0x6c, 0x6f, 0xff, 0x1e, 0x9e, 0xf3 +}; +TEST_F(TSIGTest, sign) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + { + SCOPED_TRACE("Sign test for query"); + commonSignChecks(createMessageAndSign(qid, test_name, tsig_ctx.get()), + qid, 0x4da8877a, common_expected_mac, + sizeof(common_expected_mac)); + } +} + +// Same test as sign, but specifying the key name with upper-case (i.e. +// non canonical) characters. The digest must be the same. It should actually +// be ensured at the level of TSIGKey, but we confirm that at this level, too. +TEST_F(TSIGTest, signUsingUpperCasedKeyName) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + TSIGContext cap_ctx(TSIGKey(Name("WWW.EXAMPLE.COM"), + TSIGKey::HMACMD5_NAME(), + &secret[0], secret.size())); + + { + SCOPED_TRACE("Sign test for query using non canonical key name"); + commonSignChecks(createMessageAndSign(qid, test_name, &cap_ctx), qid, + 0x4da8877a, common_expected_mac, + sizeof(common_expected_mac)); + } +} + +// Same as the previous test, but for the algorithm name. +TEST_F(TSIGTest, signUsingUpperCasedAlgorithmName) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + TSIGContext cap_ctx(TSIGKey(test_name, + Name("HMAC-md5.SIG-alg.REG.int"), + &secret[0], secret.size())); + + { + SCOPED_TRACE("Sign test for query using non canonical algorithm name"); + commonSignChecks(createMessageAndSign(qid, test_name, &cap_ctx), qid, + 0x4da8877a, common_expected_mac, + sizeof(common_expected_mac)); + } +} + +TEST_F(TSIGTest, signAtActualTime) { + // Sign the message using the actual time, and check the accuracy of it. + // We cannot reasonably predict the expected MAC, so don't bother to + // check it. + const uint64_t now = static_cast<uint64_t>(time(NULL)); + + { + SCOPED_TRACE("Sign test for query at actual time"); + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_ctx.get()); + const TSIG& tsig_rdata = tsig->getRdata(); + + // Check the resulted time signed is in the range of [now, now + 5] + // (5 is an arbitrary choice). Note that due to the order of the call + // to time() and sign(), time signed must not be smaller than the + // current time. + EXPECT_LE(now, tsig_rdata.getTimeSigned()); + EXPECT_GE(now + 5, tsig_rdata.getTimeSigned()); + } +} + +TEST_F(TSIGTest, signBadData) { + // some specific bad data should be rejected proactively. + const unsigned char dummy_data = 0; + EXPECT_THROW(tsig_ctx->sign(0, NULL, 10), InvalidParameter); + EXPECT_THROW(tsig_ctx->sign(0, &dummy_data, 0), InvalidParameter); +} + +TEST_F(TSIGTest, verifyBadData) { + // the data must at least hold the DNS message header and the specified + // TSIG. + EXPECT_THROW(tsig_ctx->verify(&dummy_record, &dummy_data[0], + 12 + dummy_record.getLength() - 1), + InvalidParameter); + + // Still nothing verified + EXPECT_THROW(tsig_ctx->lastHadSignature(), TSIGContextError); + + // And the data must not be NULL. + EXPECT_THROW(tsig_ctx->verify(&dummy_record, NULL, + 12 + dummy_record.getLength()), + InvalidParameter); + + // Still nothing verified + EXPECT_THROW(tsig_ctx->lastHadSignature(), TSIGContextError); + +} + +#ifdef ENABLE_CUSTOM_OPERATOR_NEW +// We enable this test only when we enable custom new/delete at build time +// We could enable/disable the test runtime using the gtest filter, but +// we'd basically like to minimize the number of disabled tests (they +// should generally be considered tests that temporarily fail and should +// be fixed). +TEST_F(TSIGTest, signExceptionSafety) { + // Check sign() provides the strong exception guarantee for the simpler + // case (with a key error and empty MAC). The general case is more + // complicated and involves more memory allocation, so the test result + // won't be reliable. + + commonVerifyChecks(*tsig_verify_ctx, &dummy_record, &dummy_data[0], + dummy_data.size(), TSIGError::BAD_KEY(), + TSIGContext::RECEIVED_REQUEST); + + try { + int dummydata; + isc::util::unittests::force_throw_on_new = true; + isc::util::unittests::throw_size_on_new = sizeof(TSIGRecord); + tsig_verify_ctx->sign(0, &dummydata, sizeof(dummydata)); + isc::util::unittests::force_throw_on_new = false; + ASSERT_FALSE(true) << "Expected throw on new, but it didn't happen"; + } catch (const std::bad_alloc&) { + isc::util::unittests::force_throw_on_new = false; + + // sign() threw, so the state should still be RECEIVED_REQUEST + EXPECT_EQ(TSIGContext::RECEIVED_REQUEST, tsig_verify_ctx->getState()); + } + isc::util::unittests::force_throw_on_new = false; +} +#endif // ENABLE_CUSTOM_OPERATOR_NEW + +// Same test as "sign" but use a different algorithm just to confirm we don't +// naively hardcode constants specific to a particular algorithm. +// Test data generated by +// "dig -y hmac-sha1:www.example.com:MA+QDhXbyqUak+qnMFyTyEirzng= www.example.com" +// QID: 0x0967, RDflag +// Current Time: 00004da8be86 +// Time Signed: 00004dae7d5f +// HMAC Size: 20 +// HMAC: 415340c7daf824ed684ee586f7b5a67a2febc0d3 +TEST_F(TSIGTest, signUsingHMACSHA1) { + isc::util::detail::gettimeFunction = testGetTime<0x4dae7d5f>; + + secret.clear(); + decodeBase64("MA+QDhXbyqUak+qnMFyTyEirzng=", secret); + TSIGContext sha1_ctx(TSIGKey(test_name, TSIGKey::HMACSHA1_NAME(), + &secret[0], secret.size())); + + const uint16_t sha1_qid = 0x0967; + const uint8_t expected_mac[] = { + 0x41, 0x53, 0x40, 0xc7, 0xda, 0xf8, 0x24, 0xed, 0x68, 0x4e, + 0xe5, 0x86, 0xf7, 0xb5, 0xa6, 0x7a, 0x2f, 0xeb, 0xc0, 0xd3 + }; + { + SCOPED_TRACE("Sign test using HMAC-SHA1"); + commonSignChecks(createMessageAndSign(sha1_qid, test_name, &sha1_ctx), + sha1_qid, 0x4dae7d5f, expected_mac, + sizeof(expected_mac), 0, 0, NULL, + TSIGKey::HMACSHA1_NAME()); + } +} + +TEST_F(TSIGTest, signUsingHMACSHA224) { + isc::util::detail::gettimeFunction = testGetTime<0x4dae7d5f>; + + secret.clear(); + decodeBase64("MA+QDhXbyqUak+qnMFyTyEirzng=", secret); + TSIGContext sha1_ctx(TSIGKey(test_name, TSIGKey::HMACSHA224_NAME(), + &secret[0], secret.size())); + + const uint16_t sha1_qid = 0x0967; + const uint8_t expected_mac[] = { + 0x3b, 0x93, 0xd3, 0xc5, 0xf9, 0x64, 0xb9, 0xc5, 0x00, 0x35, + 0x02, 0x69, 0x9f, 0xfc, 0x44, 0xd6, 0xe2, 0x66, 0xf4, 0x08, + 0xef, 0x33, 0xa2, 0xda, 0xa1, 0x48, 0x71, 0xd3 + }; + { + SCOPED_TRACE("Sign test using HMAC-SHA224"); + commonSignChecks(createMessageAndSign(sha1_qid, test_name, &sha1_ctx), + sha1_qid, 0x4dae7d5f, expected_mac, + sizeof(expected_mac), 0, 0, NULL, + TSIGKey::HMACSHA224_NAME()); + } +} + +// The first part of this test checks verifying the signed query used for +// the "sign" test. +// The second part of this test generates a signed response to the signed +// query as follows: +// Answer: www.example.com. 86400 IN A 192.0.2.1 +// MAC: 8fcda66a7cd1a3b9948eb1869d384a9f +TEST_F(TSIGTest, verifyThenSignResponse) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + // This test data for the message test has the same wire format data + // as the message used in the "sign" test. + createMessageFromFile("message_toWire2.wire"); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), TSIGContext::RECEIVED_REQUEST); + } + + // Transform the original message to a response, then sign the response + // with the context of "verified state". + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_verify_ctx.get(), + QR_FLAG|AA_FLAG|RD_FLAG, + RRType::A(), "192.0.2.1"); + const uint8_t expected_mac[] = { + 0x8f, 0xcd, 0xa6, 0x6a, 0x7c, 0xd1, 0xa3, 0xb9, + 0x94, 0x8e, 0xb1, 0x86, 0x9d, 0x38, 0x4a, 0x9f + }; + { + SCOPED_TRACE("Sign test for response"); + commonSignChecks(tsig, qid, 0x4da8877a, expected_mac, + sizeof(expected_mac)); + } +} + +TEST_F(TSIGTest, verifyUpperCaseNames) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + // This test data for the message test has the same wire format data + // as the message used in the "sign" test. + createMessageFromFile("tsig_verify9.wire"); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), TSIGContext::RECEIVED_REQUEST); + } +} + +TEST_F(TSIGTest, verifyForwardedMessage) { + // Similar to the first part of the previous test, but this test emulates + // the "forward" case, where the ID of the Header and the original ID in + // TSIG is different. + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + createMessageFromFile("tsig_verify6.wire"); + { + SCOPED_TRACE("Verify test for forwarded request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), TSIGContext::RECEIVED_REQUEST); + } +} + +// Example of signing multiple messages in a single TCP stream, +// taken from data using BIND 9's "one-answer" transfer-format. +// Request: +// QID: 0x3410, flags (none) +// Question: example.com/IN/AXFR +// Time Signed: 0x4da8e951 +// MAC: 35b2fd08268781634400c7c8a5533b13 +// First message: +// QID: 0x3410, flags QR, AA +// Question: example.com/IN/AXFR +// Answer: example.com. 86400 IN SOA ns.example.com. root.example.com. ( +// 2011041503 7200 3600 2592000 1200) +// MAC: bdd612cd2c7f9e0648bd6dc23713e83c +// Second message: +// Answer: example.com. 86400 IN NS ns.example.com. +// MAC: 102458f7f62ddd7d638d746034130968 +TEST_F(TSIGTest, signContinuation) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8e951>; + + const uint16_t axfr_qid = 0x3410; + const Name zone_name("example.com"); + + // Create and sign the AXFR request + ConstTSIGRecordPtr tsig = createMessageAndSign(axfr_qid, zone_name, + tsig_ctx.get(), 0, + RRType(252)); + // Then verify it (the wire format test data should contain the same + // message data, and verification should succeed). + received_data.clear(); + UnitTestUtil::readWireData("tsig_verify1.wire", received_data); + { + SCOPED_TRACE("Verify AXFR query"); + commonVerifyChecks(*tsig_verify_ctx, tsig.get(), &received_data[0], + received_data.size(), TSIGError::NOERROR(), + TSIGContext::RECEIVED_REQUEST); + } + + // Create and sign the first response message + tsig = createMessageAndSign(axfr_qid, zone_name, tsig_verify_ctx.get(), + AA_FLAG|QR_FLAG, RRType(252), + "ns.example.com. root.example.com. " + "2011041503 7200 3600 2592000 1200", + &RRType::SOA()); + + // Then verify it at the requester side. + received_data.clear(); + UnitTestUtil::readWireData("tsig_verify2.wire", received_data); + { + SCOPED_TRACE("Verify first AXFR response"); + commonVerifyChecks(*tsig_ctx, tsig.get(), &received_data[0], + received_data.size(), TSIGError::NOERROR()); + } + + // Create and sign the second response message + const uint8_t expected_mac[] = { + 0x10, 0x24, 0x58, 0xf7, 0xf6, 0x2d, 0xdd, 0x7d, + 0x63, 0x8d, 0x74, 0x60, 0x34, 0x13, 0x09, 0x68 + }; + { + SCOPED_TRACE("Sign test for continued response in TCP stream"); + tsig = createMessageAndSign(axfr_qid, zone_name, tsig_verify_ctx.get(), + AA_FLAG|QR_FLAG, RRType(252), + "ns.example.com.", &RRType::NS(), false); + commonSignChecks(tsig, axfr_qid, 0x4da8e951, expected_mac, + sizeof(expected_mac)); + } + + // Then verify it at the requester side. + received_data.clear(); + UnitTestUtil::readWireData("tsig_verify3.wire", received_data); + { + SCOPED_TRACE("Verify second AXFR response"); + commonVerifyChecks(*tsig_ctx, tsig.get(), &received_data[0], + received_data.size(), TSIGError::NOERROR()); + } +} + +// BADTIME example, taken from data using specially hacked BIND 9's nsupdate +// Query: +// QID: 0x1830, RD flag +// Current Time: 00004da8be86 +// Time Signed: 00004da8b9d6 +// Question: www.example.com/IN/SOA +//(mac) 8406 7d50 b8e7 d054 3d50 5bd9 de2a bb68 +// Response: +// QRbit, RCODE=9(NOTAUTH) +// Time Signed: 00004da8b9d6 (the one in the query) +// MAC: d4b043f6f44495ec8a01260e39159d76 +// Error: 0x12 (BADTIME), Other Len: 6 +// Other data: 00004da8be86 +TEST_F(TSIGTest, badtimeResponse) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6>; + + const uint16_t test_qid = 0x7fc4; + ConstTSIGRecordPtr tsig = createMessageAndSign(test_qid, test_name, + tsig_ctx.get(), 0, + RRType::SOA()); + + // "advance the clock" and try validating, which should fail due to BADTIME + isc::util::detail::gettimeFunction = testGetTime<0x4da8be86>; + { + SCOPED_TRACE("Verify resulting in BADTIME due to expired SIG"); + commonVerifyChecks(*tsig_verify_ctx, tsig.get(), &dummy_data[0], + dummy_data.size(), TSIGError::BAD_TIME(), + TSIGContext::RECEIVED_REQUEST); + } + + // make and sign a response in the context of TSIG error. + tsig = createMessageAndSign(test_qid, test_name, tsig_verify_ctx.get(), + QR_FLAG, RRType::SOA(), NULL, NULL, + true, Rcode::NOTAUTH()); + const uint8_t expected_otherdata[] = { 0, 0, 0x4d, 0xa8, 0xbe, 0x86 }; + const uint8_t expected_mac[] = { + 0xd4, 0xb0, 0x43, 0xf6, 0xf4, 0x44, 0x95, 0xec, + 0x8a, 0x01, 0x26, 0x0e, 0x39, 0x15, 0x9d, 0x76 + }; + { + SCOPED_TRACE("Sign test for response with BADTIME"); + commonSignChecks(tsig, message.getQid(), 0x4da8b9d6, + expected_mac, sizeof(expected_mac), + 18, // error: BADTIME + sizeof(expected_otherdata), + expected_otherdata); + } +} + +TEST_F(TSIGTest, badtimeResponse2) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6>; + + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_ctx.get(), 0, + RRType::SOA()); + + // "rewind the clock" and try validating, which should fail due to BADTIME + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 - 600>; + { + SCOPED_TRACE("Verify resulting in BADTIME due to too future SIG"); + commonVerifyChecks(*tsig_verify_ctx, tsig.get(), &dummy_data[0], + dummy_data.size(), TSIGError::BAD_TIME(), + TSIGContext::RECEIVED_REQUEST); + } +} + +TEST_F(TSIGTest, badtimeBoundaries) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6>; + + // Test various boundary conditions. We intentionally use the magic + // number of 300 instead of the constant variable for testing. + // In the okay cases, signature is not correct, but it's sufficient to + // check the error code isn't BADTIME for the purpose of this test. + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_ctx.get(), 0, + RRType::SOA()); + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 + 301>; + EXPECT_EQ(TSIGError::BAD_TIME(), + tsig_verify_ctx->verify(tsig.get(), &dummy_data[0], + dummy_data.size())); + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 + 300>; + EXPECT_NE(TSIGError::BAD_TIME(), + tsig_verify_ctx->verify(tsig.get(), &dummy_data[0], + dummy_data.size())); + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 - 301>; + EXPECT_EQ(TSIGError::BAD_TIME(), + tsig_verify_ctx->verify(tsig.get(), &dummy_data[0], + dummy_data.size())); + isc::util::detail::gettimeFunction = testGetTime<0x4da8b9d6 - 300>; + EXPECT_NE(TSIGError::BAD_TIME(), + tsig_verify_ctx->verify(tsig.get(), &dummy_data[0], + dummy_data.size())); +} + +TEST_F(TSIGTest, badtimeOverflow) { + isc::util::detail::gettimeFunction = testGetTime<200>; + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_ctx.get(), 0, + RRType::SOA()); + + // This should be in the okay range, but since "200 - fudge" overflows + // and we compare them as 64-bit unsigned integers, it results in a false + // positive (we intentionally accept that). + isc::util::detail::gettimeFunction = testGetTime<100>; + EXPECT_EQ(TSIGError::BAD_TIME(), + tsig_verify_ctx->verify(tsig.get(), &dummy_data[0], + dummy_data.size())); +} + +TEST_F(TSIGTest, badsigResponse) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + // Try to sign a simple message with bogus secret. It should fail + // with BADSIG. + createMessageFromFile("message_toWire2.wire"); + TSIGContext bad_ctx(TSIGKey(test_name, TSIGKey::HMACMD5_NAME(), + &dummy_data[0], dummy_data.size())); + { + SCOPED_TRACE("Verify resulting in BADSIG"); + commonVerifyChecks(bad_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_SIG(), TSIGContext::RECEIVED_REQUEST); + } + + // Sign the same message (which doesn't matter for this test) with the + // context of "checked state". + { + SCOPED_TRACE("Sign test for response with BADSIG error"); + commonSignChecks(createMessageAndSign(qid, test_name, &bad_ctx), + message.getQid(), 0x4da8877a, NULL, 0, + 16); // 16: BADSIG + } +} + +TEST_F(TSIGTest, badkeyResponse) { + // A similar test as badsigResponse but for BADKEY + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + tsig_ctx.reset(new TestTSIGContext(badkey_name, TSIGKey::HMACMD5_NAME(), + keyring)); + { + SCOPED_TRACE("Verify resulting in BADKEY"); + commonVerifyChecks(*tsig_ctx, &dummy_record, &dummy_data[0], + dummy_data.size(), TSIGError::BAD_KEY(), + TSIGContext::RECEIVED_REQUEST); + } + + { + SCOPED_TRACE("Sign test for response with BADKEY error"); + ConstTSIGRecordPtr sig = createMessageAndSign(qid, test_name, + tsig_ctx.get()); + EXPECT_EQ(badkey_name, sig->getName()); + commonSignChecks(sig, qid, 0x4da8877a, NULL, 0, 17); // 17: BADKEY + } +} + +TEST_F(TSIGTest, badkeyForResponse) { + // "BADKEY" case for a response to a signed message + createMessageAndSign(qid, test_name, tsig_ctx.get()); + { + SCOPED_TRACE("Verify a response resulting in BADKEY"); + commonVerifyChecks(*tsig_ctx, &dummy_record, &dummy_data[0], + dummy_data.size(), TSIGError::BAD_KEY(), + TSIGContext::SENT_REQUEST); + } + + // A similar case with a different algorithm + const TSIGRecord dummy_record2(test_name, TSIG(TSIGKey::HMACSHA1_NAME(), + 0x4da8877a, + TSIGContext::DEFAULT_FUDGE, + 0, NULL, qid, 0, 0, NULL)); + { + SCOPED_TRACE("Verify a response resulting in BADKEY due to bad alg"); + commonVerifyChecks(*tsig_ctx, &dummy_record2, &dummy_data[0], + dummy_data.size(), TSIGError::BAD_KEY(), + TSIGContext::SENT_REQUEST); + } +} + +TEST_F(TSIGTest, badsigThenValidate) { + // According to RFC2845 4.6, if TSIG verification fails the client + // should discard that message and wait for another signed response. + // This test emulates that situation. + + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + createMessageAndSign(qid, test_name, tsig_ctx.get()); + + createMessageFromFile("tsig_verify4.wire"); + { + SCOPED_TRACE("Verify a response that should fail due to BADSIG"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_SIG(), TSIGContext::SENT_REQUEST); + } + + createMessageFromFile("tsig_verify5.wire"); + { + SCOPED_TRACE("Verify a response after a BADSIG failure"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), + TSIGContext::VERIFIED_RESPONSE); + } +} + +TEST_F(TSIGTest, nosigThenValidate) { + // Similar to the previous test, but the first response doesn't contain + // TSIG. + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + createMessageAndSign(qid, test_name, tsig_ctx.get()); + + { + SCOPED_TRACE("Verify a response without TSIG that should exist"); + commonVerifyChecks(*tsig_ctx, NULL, &dummy_data[0], + dummy_data.size(), TSIGError::FORMERR(), + TSIGContext::SENT_REQUEST, true); + } + + createMessageFromFile("tsig_verify5.wire"); + { + SCOPED_TRACE("Verify a response after a FORMERR failure"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), + TSIGContext::VERIFIED_RESPONSE); + } +} + +TEST_F(TSIGTest, badtimeThenValidate) { + // Similar to the previous test, but the first response results in BADTIME. + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + ConstTSIGRecordPtr tsig = createMessageAndSign(qid, test_name, + tsig_ctx.get()); + + // "advance the clock" and try validating, which should fail due to BADTIME + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a + 600>; + { + SCOPED_TRACE("Verify resulting in BADTIME due to expired SIG"); + commonVerifyChecks(*tsig_ctx, tsig.get(), &dummy_data[0], + dummy_data.size(), TSIGError::BAD_TIME(), + TSIGContext::SENT_REQUEST); + } + + // revert the clock again. + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + createMessageFromFile("tsig_verify5.wire"); + { + SCOPED_TRACE("Verify a response after a BADTIME failure"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), + TSIGContext::VERIFIED_RESPONSE); + } +} + +TEST_F(TSIGTest, emptyMAC) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + // We don't allow empty MAC unless the TSIG error is BADSIG or BADKEY. + createMessageFromFile("tsig_verify7.wire"); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_SIG(), TSIGContext::RECEIVED_REQUEST); + } + + // If the empty MAC comes with a BADKEY error, the error is passed + // transparently. + createMessageFromFile("tsig_verify8.wire"); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_KEY(), TSIGContext::RECEIVED_REQUEST); + } +} + +TEST_F(TSIGTest, verifyAfterSendResponse) { + // Once the context is used for sending a signed response, it shouldn't + // be used for further verification. + + // The following are essentially the same as what verifyThenSignResponse + // does with simplification. + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + createMessageFromFile("message_toWire2.wire"); + tsig_verify_ctx->verify(message.getTSIGRecord(), &received_data[0], + received_data.size()); + EXPECT_EQ(TSIGContext::RECEIVED_REQUEST, tsig_verify_ctx->getState()); + createMessageAndSign(qid, test_name, tsig_verify_ctx.get(), + QR_FLAG|AA_FLAG|RD_FLAG, RRType::A(), "192.0.2.1"); + EXPECT_EQ(TSIGContext::SENT_RESPONSE, tsig_verify_ctx->getState()); + + // Now trying further verification. + createMessageFromFile("message_toWire2.wire"); + EXPECT_THROW(tsig_verify_ctx->verify(message.getTSIGRecord(), + &received_data[0], + received_data.size()), + TSIGContextError); +} + +TEST_F(TSIGTest, signAfterVerified) { + // Likewise, once the context verifies a response, it shouldn't for + // signing any more. + + // The following are borrowed from badsigThenValidate (without the + // intermediate failure) + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + createMessageAndSign(qid, test_name, tsig_ctx.get()); + createMessageFromFile("tsig_verify5.wire"); + tsig_ctx->verify(message.getTSIGRecord(), &received_data[0], + received_data.size()); + EXPECT_EQ(TSIGContext::VERIFIED_RESPONSE, tsig_ctx->getState()); + + // Now trying further signing. + EXPECT_THROW(createMessageAndSign(qid, test_name, tsig_ctx.get()), + TSIGContextError); +} + +TEST_F(TSIGTest, tooShortMAC) { + // Too short MAC should be rejected. + + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + createMessageFromFile("tsig_verify10.wire"); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(*tsig_verify_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::FORMERR(), TSIGContext::RECEIVED_REQUEST); + } +} + +TEST_F(TSIGTest, truncatedMAC) { + // Check truncated MAC support with HMAC-SHA512-256 + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + secret.clear(); + decodeBase64("jI/Pa4qRu96t76Pns5Z/Ndxbn3QCkwcxLOgt9vgvnJw5wqTRvNyk3FtD6yIMd1dWVlqZ+Y4fe6Uasc0ckctEmg==", secret); + TSIGContext sha_ctx(TSIGKey(test_name, TSIGKey::HMACSHA512_NAME(), + &secret[0], secret.size(), 256)); + + createMessageFromFile("tsig_verify11.wire"); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(sha_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::NOERROR(), TSIGContext::RECEIVED_REQUEST); + } + + // Try with HMAC-SHA512-264 (should fail) + TSIGContext bad_sha_ctx(TSIGKey(test_name, TSIGKey::HMACSHA512_NAME(), + &secret[0], secret.size(), 264)); + { + SCOPED_TRACE("Verify test for request"); + commonVerifyChecks(bad_sha_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_TRUNC(), TSIGContext::RECEIVED_REQUEST); + } +} + +TEST_F(TSIGTest, getTSIGLength) { + // Check for the most common case with various algorithms + // See the comment in TSIGContext::getTSIGLength() for calculation and + // parameter notation. + // The key name (www.example.com) is the same for most cases, where n1=17 + + // hmac-md5.sig-alg.reg.int.: n2=26, x=16 + EXPECT_EQ(85, tsig_ctx->getTSIGLength()); + + // hmac-md5-80: n2=26, x=10 + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACMD5_NAME(), + &dummy_data[0], 10, 80))); + EXPECT_EQ(79, tsig_ctx->getTSIGLength()); + + // hmac-sha1: n2=11, x=20 + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACSHA1_NAME(), + &dummy_data[0], 20))); + EXPECT_EQ(74, tsig_ctx->getTSIGLength()); + + // hmac-sha256: n2=13, x=32 + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACSHA256_NAME(), + &dummy_data[0], 32))); + EXPECT_EQ(88, tsig_ctx->getTSIGLength()); + + // hmac-sha224: n2=13, x=28 + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACSHA224_NAME(), + &dummy_data[0], 28))); + EXPECT_EQ(84, tsig_ctx->getTSIGLength()); + + // hmac-sha384: n2=13, x=48 + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACSHA384_NAME(), + &dummy_data[0], 48))); + EXPECT_EQ(104, tsig_ctx->getTSIGLength()); + + // hmac-sha512: n2=13, x=64 + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACSHA512_NAME(), + &dummy_data[0], 64))); + EXPECT_EQ(120, tsig_ctx->getTSIGLength()); + + // hmac-sha512-256: n2=13, x=32 + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACSHA512_NAME(), + &dummy_data[0], 32, 256))); + EXPECT_EQ(88, tsig_ctx->getTSIGLength()); + + // bad key case: n1=len(badkey.example.com)=20, n2=26, x=0 + tsig_ctx.reset(new TestTSIGContext(badkey_name, TSIGKey::HMACMD5_NAME(), + keyring)); + EXPECT_EQ(72, tsig_ctx->getTSIGLength()); + + // bad sig case: n1=17, n2=26, x=0 + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + createMessageFromFile("message_toWire2.wire"); + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACMD5_NAME(), + &dummy_data[0], + dummy_data.size()))); + { + SCOPED_TRACE("Verify resulting in BADSIG"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_SIG(), TSIGContext::RECEIVED_REQUEST); + } + EXPECT_EQ(69, tsig_ctx->getTSIGLength()); + + // bad time case: n1=17, n2=26, x=16, y=6 + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a - 1000>; + tsig_ctx.reset(new TestTSIGContext(TSIGKey(test_name, + TSIGKey::HMACMD5_NAME(), + &dummy_data[0], + dummy_data.size()))); + { + SCOPED_TRACE("Verify resulting in BADTIME"); + commonVerifyChecks(*tsig_ctx, message.getTSIGRecord(), + &received_data[0], received_data.size(), + TSIGError::BAD_TIME(), + TSIGContext::RECEIVED_REQUEST); + } + EXPECT_EQ(91, tsig_ctx->getTSIGLength()); +} + +// Verify a stream of multiple messages. Some of them have a signature omitted. +// +// We have two contexts, one that signs, another that verifies. +TEST_F(TSIGTest, verifyMulti) { + isc::util::detail::gettimeFunction = testGetTime<0x4da8877a>; + + // First, send query from the verify one to the normal one, so + // we initialize something like AXFR + { + SCOPED_TRACE("Query"); + ConstTSIGRecordPtr tsig = createMessageAndSign(1234, test_name, + tsig_verify_ctx.get()); + commonVerifyChecks(*tsig_ctx, tsig.get(), + renderer.getData(), renderer.getLength(), + TSIGError(Rcode::NOERROR()), + TSIGContext::RECEIVED_REQUEST); + } + + { + SCOPED_TRACE("First message"); + ConstTSIGRecordPtr tsig = createMessageAndSign(1234, test_name, + tsig_ctx.get()); + commonVerifyChecks(*tsig_verify_ctx, tsig.get(), + renderer.getData(), renderer.getLength(), + TSIGError(Rcode::NOERROR()), + TSIGContext::VERIFIED_RESPONSE); + EXPECT_TRUE(tsig_verify_ctx->lastHadSignature()); + } + + { + SCOPED_TRACE("Second message"); + ConstTSIGRecordPtr tsig = createMessageAndSign(1234, test_name, + tsig_ctx.get()); + commonVerifyChecks(*tsig_verify_ctx, tsig.get(), + renderer.getData(), renderer.getLength(), + TSIGError(Rcode::NOERROR()), + TSIGContext::VERIFIED_RESPONSE); + EXPECT_TRUE(tsig_verify_ctx->lastHadSignature()); + } + + { + SCOPED_TRACE("Third message. Unsigned."); + // Another message does not carry the TSIG on it. But it should + // be OK, it's in the middle of stream. + message.clear(Message::RENDER); + message.setQid(1234); + message.setOpcode(Opcode::QUERY()); + message.setRcode(Rcode::NOERROR()); + RRsetPtr answer_rrset(new RRset(test_name, test_class, RRType::A(), + test_ttl)); + answer_rrset->addRdata(createRdata(RRType::A(), test_class, + "192.0.2.1")); + message.addRRset(Message::SECTION_ANSWER, answer_rrset); + message.toWire(renderer); + // Update the internal state. We abuse the knowledge of + // internals here a little bit to generate correct test data + tsig_ctx->update(renderer.getData(), renderer.getLength()); + + commonVerifyChecks(*tsig_verify_ctx, NULL, + renderer.getData(), renderer.getLength(), + TSIGError(Rcode::NOERROR()), + TSIGContext::VERIFIED_RESPONSE); + + EXPECT_FALSE(tsig_verify_ctx->lastHadSignature()); + } + + { + SCOPED_TRACE("Fourth message. Signed again."); + ConstTSIGRecordPtr tsig = createMessageAndSign(1234, test_name, + tsig_ctx.get()); + commonVerifyChecks(*tsig_verify_ctx, tsig.get(), + renderer.getData(), renderer.getLength(), + TSIGError(Rcode::NOERROR()), + TSIGContext::VERIFIED_RESPONSE); + EXPECT_TRUE(tsig_verify_ctx->lastHadSignature()); + } + + { + SCOPED_TRACE("Filling in bunch of unsigned messages"); + for (size_t i = 0; i < 100; ++i) { + SCOPED_TRACE(i); + // Another message does not carry the TSIG on it. But it should + // be OK, it's in the middle of stream. + message.clear(Message::RENDER); + message.setQid(1234); + message.setOpcode(Opcode::QUERY()); + message.setRcode(Rcode::NOERROR()); + RRsetPtr answer_rrset(new RRset(test_name, test_class, RRType::A(), + test_ttl)); + answer_rrset->addRdata(createRdata(RRType::A(), test_class, + "192.0.2.1")); + message.addRRset(Message::SECTION_ANSWER, answer_rrset); + message.toWire(renderer); + // Update the internal state. We abuse the knowledge of + // internals here a little bit to generate correct test data + tsig_ctx->update(renderer.getData(), renderer.getLength()); + + // 99 unsigned messages is OK. But the 100th must be signed, according + // to the RFC2845, section 4.4 + commonVerifyChecks(*tsig_verify_ctx, NULL, + renderer.getData(), renderer.getLength(), + i == 99 ? TSIGError::FORMERR() : + TSIGError(Rcode::NOERROR()), + TSIGContext::VERIFIED_RESPONSE); + + EXPECT_FALSE(tsig_verify_ctx->lastHadSignature()); + } + } +} + +} // end namespace diff --git a/src/lib/dns/tests/tsigerror_unittest.cc b/src/lib/dns/tests/tsigerror_unittest.cc index 8b13789179..e50f0763d3 100644 --- a/src/lib/dns/tests/tsigerror_unittest.cc +++ b/src/lib/dns/tests/tsigerror_unittest.cc @@ -1 +1,126 @@ +// Copyright (C) 2011-2015 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <string> +#include <ostream> + +#include <gtest/gtest.h> + +#include <exceptions/exceptions.h> + +#include <dns/rcode.h> +#include <dns/tsigerror.h> + +using namespace std; +using namespace isc; +using namespace isc::dns; + +namespace { +TEST(TSIGErrorTest, constructFromErrorCode) { + // These are pretty trivial, and also test getCode(); + EXPECT_EQ(0, TSIGError(0).getCode()); + EXPECT_EQ(18, TSIGError(18).getCode()); + EXPECT_EQ(65535, TSIGError(65535).getCode()); +} + +TEST(TSIGErrorTest, constructFromRcode) { + // We use RCODE for code values from 0-15. + EXPECT_EQ(0, TSIGError(Rcode::NOERROR()).getCode()); + EXPECT_EQ(15, TSIGError(Rcode(15)).getCode()); + + // From error code 16 TSIG errors define a separate space, so passing + // corresponding RCODE for such code values should be prohibited. + EXPECT_THROW(TSIGError(Rcode(16)).getCode(), OutOfRange); +} + +TEST(TSIGErrorTest, constants) { + // We'll only test arbitrarily chosen subsets of the codes. + // This class is quite simple, so it should be suffice. + + EXPECT_EQ(TSIGError::BAD_SIG_CODE, TSIGError(16).getCode()); + EXPECT_EQ(TSIGError::BAD_KEY_CODE, TSIGError(17).getCode()); + EXPECT_EQ(TSIGError::BAD_TIME_CODE, TSIGError(18).getCode()); + EXPECT_EQ(TSIGError::BAD_MODE_CODE, TSIGError(19).getCode()); + EXPECT_EQ(TSIGError::BAD_NAME_CODE, TSIGError(20).getCode()); + EXPECT_EQ(TSIGError::BAD_ALG_CODE, TSIGError(21).getCode()); + EXPECT_EQ(TSIGError::BAD_TRUNC_CODE, TSIGError(22).getCode()); + + EXPECT_EQ(0, TSIGError::NOERROR().getCode()); + EXPECT_EQ(9, TSIGError::NOTAUTH().getCode()); + EXPECT_EQ(14, TSIGError::RESERVED14().getCode()); + EXPECT_EQ(TSIGError::BAD_SIG_CODE, TSIGError::BAD_SIG().getCode()); + EXPECT_EQ(TSIGError::BAD_KEY_CODE, TSIGError::BAD_KEY().getCode()); + EXPECT_EQ(TSIGError::BAD_TIME_CODE, TSIGError::BAD_TIME().getCode()); + EXPECT_EQ(TSIGError::BAD_MODE_CODE, TSIGError::BAD_MODE().getCode()); + EXPECT_EQ(TSIGError::BAD_NAME_CODE, TSIGError::BAD_NAME().getCode()); + EXPECT_EQ(TSIGError::BAD_ALG_CODE, TSIGError::BAD_ALG().getCode()); + EXPECT_EQ(TSIGError::BAD_TRUNC_CODE, TSIGError::BAD_TRUNC().getCode()); +} + +TEST(TSIGErrorTest, equal) { + EXPECT_TRUE(TSIGError::NOERROR() == TSIGError(Rcode::NOERROR())); + EXPECT_TRUE(TSIGError(Rcode::NOERROR()) == TSIGError::NOERROR()); + EXPECT_TRUE(TSIGError::NOERROR().equals(TSIGError(Rcode::NOERROR()))); + EXPECT_TRUE(TSIGError::NOERROR().equals(TSIGError(Rcode::NOERROR()))); + + EXPECT_TRUE(TSIGError::BAD_SIG() == TSIGError(16)); + EXPECT_TRUE(TSIGError(16) == TSIGError::BAD_SIG()); + EXPECT_TRUE(TSIGError::BAD_SIG().equals(TSIGError(16))); + EXPECT_TRUE(TSIGError(16).equals(TSIGError::BAD_SIG())); +} + +TEST(TSIGErrorTest, nequal) { + EXPECT_TRUE(TSIGError::BAD_KEY() != TSIGError(Rcode::NOERROR())); + EXPECT_TRUE(TSIGError(Rcode::NOERROR()) != TSIGError::BAD_KEY()); + EXPECT_TRUE(TSIGError::BAD_KEY().nequals(TSIGError(Rcode::NOERROR()))); + EXPECT_TRUE(TSIGError(Rcode::NOERROR()).nequals(TSIGError::BAD_KEY())); +} + +TEST(TSIGErrorTest, toText) { + // TSIGError derived from the standard Rcode + EXPECT_EQ("NOERROR", TSIGError(Rcode::NOERROR()).toText()); + + // Well known TSIG errors + EXPECT_EQ("BADSIG", TSIGError::BAD_SIG().toText()); + EXPECT_EQ("BADKEY", TSIGError::BAD_KEY().toText()); + EXPECT_EQ("BADTIME", TSIGError::BAD_TIME().toText()); + EXPECT_EQ("BADMODE", TSIGError::BAD_MODE().toText()); + EXPECT_EQ("BADNAME", TSIGError::BAD_NAME().toText()); + EXPECT_EQ("BADALG", TSIGError::BAD_ALG().toText()); + EXPECT_EQ("BADTRUNC", TSIGError::BAD_TRUNC().toText()); + + // Unknown (or not yet supported) codes. Simply converted as numeric. + EXPECT_EQ("23", TSIGError(23).toText()); + EXPECT_EQ("65535", TSIGError(65535).toText()); +} + +TEST(TSIGErrorTest, toRcode) { + // TSIGError derived from the standard Rcode + EXPECT_EQ(Rcode::NOERROR(), TSIGError(Rcode::NOERROR()).toRcode()); + + // Well known TSIG errors + EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_SIG().toRcode()); + EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_KEY().toRcode()); + EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_TIME().toRcode()); + EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_MODE().toRcode()); + EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_NAME().toRcode()); + EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_ALG().toRcode()); + EXPECT_EQ(Rcode::NOTAUTH(), TSIGError::BAD_TRUNC().toRcode()); + + // Unknown (or not yet supported) codes are treated as SERVFAIL. + EXPECT_EQ(Rcode::SERVFAIL(), TSIGError(23).toRcode()); + EXPECT_EQ(Rcode::SERVFAIL(), TSIGError(65535).toRcode()); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST(TSIGErrorTest, LeftShiftOperator) { + ostringstream oss; + oss << TSIGError::BAD_KEY(); + EXPECT_EQ(TSIGError::BAD_KEY().toText(), oss.str()); +} +} // end namespace diff --git a/src/lib/dns/tests/tsigkey_unittest.cc b/src/lib/dns/tests/tsigkey_unittest.cc index 8b13789179..90c59ac112 100644 --- a/src/lib/dns/tests/tsigkey_unittest.cc +++ b/src/lib/dns/tests/tsigkey_unittest.cc @@ -1 +1,350 @@ +// Copyright (C) 2021-2019 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <string> + +#include <gtest/gtest.h> + +#include <exceptions/exceptions.h> + +#include <cryptolink/cryptolink.h> + +#include <dns/tsigkey.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::dns; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class TSIGKeyTest : public ::testing::Test { +protected: + TSIGKeyTest() : secret("someRandomData"), key_name("example.com") {} + string secret; + const Name key_name; +}; + +TEST_F(TSIGKeyTest, algorithmNames) { + EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), TSIGKey::HMACMD5_NAME()); + EXPECT_EQ(Name("hmac-md5"), TSIGKey::HMACMD5_SHORT_NAME()); + EXPECT_EQ(Name("hmac-sha1"), TSIGKey::HMACSHA1_NAME()); + EXPECT_EQ(Name("hmac-sha256"), TSIGKey::HMACSHA256_NAME()); + EXPECT_EQ(Name("hmac-sha224"), TSIGKey::HMACSHA224_NAME()); + EXPECT_EQ(Name("hmac-sha384"), TSIGKey::HMACSHA384_NAME()); + EXPECT_EQ(Name("hmac-sha512"), TSIGKey::HMACSHA512_NAME()); + EXPECT_EQ(Name("gss-tsig"), TSIGKey::GSSTSIG_NAME()); + + // Also check conversion to cryptolink definitions + EXPECT_EQ(isc::cryptolink::MD5, TSIGKey(key_name, TSIGKey::HMACMD5_NAME(), + NULL, 0).getAlgorithm()); + EXPECT_EQ(isc::cryptolink::MD5, + TSIGKey(key_name, TSIGKey::HMACMD5_SHORT_NAME(), + NULL, 0).getAlgorithm()); + EXPECT_EQ(isc::cryptolink::SHA1, TSIGKey(key_name, TSIGKey::HMACSHA1_NAME(), + NULL, 0).getAlgorithm()); + EXPECT_EQ(isc::cryptolink::SHA256, TSIGKey(key_name, + TSIGKey::HMACSHA256_NAME(), + NULL, 0).getAlgorithm()); + EXPECT_EQ(isc::cryptolink::SHA224, TSIGKey(key_name, + TSIGKey::HMACSHA224_NAME(), + NULL, 0).getAlgorithm()); + EXPECT_EQ(isc::cryptolink::SHA384, TSIGKey(key_name, + TSIGKey::HMACSHA384_NAME(), + NULL, 0).getAlgorithm()); + EXPECT_EQ(isc::cryptolink::SHA512, TSIGKey(key_name, + TSIGKey::HMACSHA512_NAME(), + NULL, 0).getAlgorithm()); +} + +TEST_F(TSIGKeyTest, construct) { + TSIGKey key(key_name, TSIGKey::HMACMD5_NAME(), + secret.c_str(), secret.size()); + EXPECT_EQ(key_name, key.getKeyName()); + EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), key.getAlgorithmName()); + matchWireData(secret.c_str(), secret.size(), + key.getSecret(), key.getSecretLength()); + + TSIGKey key_short_md5(key_name, TSIGKey::HMACMD5_SHORT_NAME(), + secret.c_str(), secret.size()); + EXPECT_EQ(key_name, key_short_md5.getKeyName()); + EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), + key_short_md5.getAlgorithmName()); + matchWireData(secret.c_str(), secret.size(), + key_short_md5.getSecret(), key_short_md5.getSecretLength()); + + // "unknown" algorithm is only accepted with empty secret. + EXPECT_THROW(TSIGKey(key_name, Name("unknown-alg"), + secret.c_str(), secret.size()), + isc::InvalidParameter); + TSIGKey key2(key_name, Name("unknown-alg"), NULL, 0); + EXPECT_EQ(key_name, key2.getKeyName()); + EXPECT_EQ(Name("unknown-alg"), key2.getAlgorithmName()); + + // The algorithm name should be converted to the canonical form. + EXPECT_EQ("hmac-sha1.", + TSIGKey(key_name, Name("HMAC-sha1"), + secret.c_str(), + secret.size()).getAlgorithmName().toText()); + + // Same for key name + EXPECT_EQ("example.com.", + TSIGKey(Name("EXAMPLE.CoM."), TSIGKey::HMACSHA256_NAME(), + secret.c_str(), + secret.size()).getKeyName().toText()); + + // Check digestbits + EXPECT_EQ(key.getDigestbits(), 0); + TSIGKey key_trunc(key_name, TSIGKey::HMACMD5_NAME(), + secret.c_str(), secret.size(), 120); + EXPECT_EQ(key_trunc.getDigestbits(), 120); + + // Invalid combinations of secret and secret_len: + EXPECT_THROW(TSIGKey(key_name, TSIGKey::HMACSHA1_NAME(), secret.c_str(), 0), + isc::InvalidParameter); + EXPECT_THROW(TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), NULL, 16), + isc::InvalidParameter); + + // Empty secret + TSIGKey keye = TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), NULL, 0); + EXPECT_EQ(keye.getSecretLength(), 0); + EXPECT_EQ(keye.getSecret(), (const void*)0); +} + +void +compareTSIGKeys(const TSIGKey& expect, const TSIGKey& actual) { + EXPECT_EQ(expect.getKeyName(), actual.getKeyName()); + EXPECT_EQ(expect.getAlgorithmName(), actual.getAlgorithmName()); + EXPECT_EQ(expect.getDigestbits(), actual.getDigestbits()); + matchWireData(expect.getSecret(), expect.getSecretLength(), + actual.getSecret(), actual.getSecretLength()); +} + +TEST_F(TSIGKeyTest, copyConstruct) { + const TSIGKey original(key_name, TSIGKey::HMACSHA256_NAME(), + secret.c_str(), secret.size(), 128); + const TSIGKey copy(original); + compareTSIGKeys(original, copy); + + // Check the copied data is valid even after the original is deleted + TSIGKey* copy2 = new TSIGKey(original); + TSIGKey copy3(*copy2); + delete copy2; + compareTSIGKeys(original, copy3); +} + +TEST_F(TSIGKeyTest, assignment) { + const TSIGKey original(key_name, TSIGKey::HMACSHA256_NAME(), + secret.c_str(), secret.size(), 200); + TSIGKey copy = original; + compareTSIGKeys(original, copy); + + // Check if the copied data is valid even after the original is deleted + TSIGKey* copy2 = new TSIGKey(original); + TSIGKey copy3(original); + copy3 = *copy2; + delete copy2; + compareTSIGKeys(original, copy3); + + // Self assignment + copy = *© + compareTSIGKeys(original, copy); +} + +class TSIGKeyRingTest : public ::testing::Test { +protected: + TSIGKeyRingTest() : + key_name("example.com"), + md5_name("hmac-md5.sig-alg.reg.int"), + sha1_name("hmac-sha1"), + sha256_name("hmac-sha256"), + secretstring("anotherRandomData"), + secret(secretstring.c_str()), + secret_len(secretstring.size()) + {} + TSIGKeyRing keyring; + const Name key_name; + const Name md5_name; + const Name sha1_name; + const Name sha256_name; +private: + const string secretstring; +protected: + const char* secret; + size_t secret_len; +}; + +TEST_F(TSIGKeyRingTest, init) { + EXPECT_EQ(0, keyring.size()); +} + +TEST_F(TSIGKeyRingTest, add) { + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( + TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), + secret, secret_len))); + EXPECT_EQ(1, keyring.size()); + EXPECT_EQ(TSIGKeyRing::EXIST, keyring.add( + TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), + secret, secret_len))); + // keys are identified by their names, the same name of key with a + // different algorithm would be considered a duplicate. + EXPECT_EQ(TSIGKeyRing::EXIST, keyring.add( + TSIGKey(Name("example.com"), TSIGKey::HMACSHA1_NAME(), + secret, secret_len))); + // names are compared in a case insensitive manner. + EXPECT_EQ(TSIGKeyRing::EXIST, keyring.add( + TSIGKey(Name("EXAMPLE.COM"), TSIGKey::HMACSHA1_NAME(), + secret, secret_len))); + EXPECT_EQ(1, keyring.size()); +} + +TEST_F(TSIGKeyRingTest, addMore) { + // essentially the same test, but try adding more than 1 + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( + TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), + secret, secret_len))); + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( + TSIGKey(Name("another.example"), TSIGKey::HMACMD5_NAME(), + secret, secret_len))); + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( + TSIGKey(Name("more.example"), TSIGKey::HMACSHA1_NAME(), + secret, secret_len))); + EXPECT_EQ(3, keyring.size()); +} + +TEST_F(TSIGKeyRingTest, remove) { + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( + TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), + secret, secret_len))); + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.remove(key_name)); + EXPECT_EQ(TSIGKeyRing::NOTFOUND, keyring.remove(key_name)); +} + +TEST_F(TSIGKeyRingTest, removeFromSome) { + // essentially the same test, but try removing from a larger set + + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( + TSIGKey(key_name, TSIGKey::HMACSHA256_NAME(), + secret, secret_len))); + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( + TSIGKey(Name("another.example"), TSIGKey::HMACMD5_NAME(), + secret, secret_len))); + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add( + TSIGKey(Name("more.example"), TSIGKey::HMACSHA1_NAME(), + secret, secret_len))); + + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.remove(Name("another.example"))); + EXPECT_EQ(TSIGKeyRing::NOTFOUND, keyring.remove(Name("noexist.example"))); + EXPECT_EQ(2, keyring.size()); +} + +TEST_F(TSIGKeyRingTest, find) { + // If the keyring is empty the search should fail. + EXPECT_EQ(TSIGKeyRing::NOTFOUND, keyring.find(key_name, md5_name).code); + EXPECT_EQ(static_cast<const TSIGKey*>(NULL), + keyring.find(key_name, md5_name).key); + + // Add a key and try to find it. Should succeed. + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(TSIGKey(key_name, sha256_name, + secret, secret_len))); + const TSIGKeyRing::FindResult result1(keyring.find(key_name, sha256_name)); + EXPECT_EQ(TSIGKeyRing::SUCCESS, result1.code); + EXPECT_EQ(key_name, result1.key->getKeyName()); + EXPECT_EQ(TSIGKey::HMACSHA256_NAME(), result1.key->getAlgorithmName()); + matchWireData(secret, secret_len, + result1.key->getSecret(), result1.key->getSecretLength()); + + // If either key name or algorithm doesn't match, search should fail. + const TSIGKeyRing::FindResult result2 = + keyring.find(Name("different-key.example"), sha256_name); + EXPECT_EQ(TSIGKeyRing::NOTFOUND, result2.code); + EXPECT_EQ(static_cast<const TSIGKey*>(NULL), result2.key); + + const TSIGKeyRing::FindResult result3 = keyring.find(key_name, md5_name); + EXPECT_EQ(TSIGKeyRing::NOTFOUND, result3.code); + EXPECT_EQ(static_cast<const TSIGKey*>(NULL), result3.key); + + // But with just the name it should work + const TSIGKeyRing::FindResult result4(keyring.find(key_name)); + EXPECT_EQ(TSIGKeyRing::SUCCESS, result4.code); + EXPECT_EQ(key_name, result4.key->getKeyName()); + EXPECT_EQ(TSIGKey::HMACSHA256_NAME(), result4.key->getAlgorithmName()); + matchWireData(secret, secret_len, + result4.key->getSecret(), result4.key->getSecretLength()); +} + +TEST_F(TSIGKeyRingTest, findFromSome) { + // essentially the same test, but search a larger set + + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(TSIGKey(key_name, sha256_name, + secret, secret_len))); + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(TSIGKey(Name("another.example"), + md5_name, + secret, secret_len))); + EXPECT_EQ(TSIGKeyRing::SUCCESS, keyring.add(TSIGKey(Name("more.example"), + sha1_name, + secret, secret_len))); + + const TSIGKeyRing::FindResult result( + keyring.find(Name("another.example"), md5_name)); + EXPECT_EQ(TSIGKeyRing::SUCCESS, result.code); + EXPECT_EQ(Name("another.example"), result.key->getKeyName()); + EXPECT_EQ(TSIGKey::HMACMD5_NAME(), result.key->getAlgorithmName()); + + EXPECT_EQ(TSIGKeyRing::NOTFOUND, + keyring.find(Name("noexist.example"), sha1_name).code); + EXPECT_EQ(static_cast<const TSIGKey*>(NULL), + keyring.find(Name("noexist.example"), sha256_name).key); + + EXPECT_EQ(TSIGKeyRing::NOTFOUND, + keyring.find(Name("another.example"), sha1_name).code); + EXPECT_EQ(static_cast<const TSIGKey*>(NULL), + keyring.find(Name("another.example"), sha256_name).key); +} + +TEST(TSIGStringTest, TSIGKeyFromToString) { + TSIGKey k1 = TSIGKey("test.example:MSG6Ng==:hmac-md5.sig-alg.reg.int"); + TSIGKey k2 = TSIGKey("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int."); + TSIGKey k3 = TSIGKey("test.example:MSG6Ng=="); + TSIGKey k4 = TSIGKey("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.:120"); + TSIGKey k5 = TSIGKey(Name("test.example."), Name("hmac-sha1."), NULL, 0); + // "Unknown" key with empty secret is okay + TSIGKey k6 = TSIGKey("test.example.::unknown"); + + EXPECT_EQ("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.", + k1.toText()); + EXPECT_EQ("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.", + k2.toText()); + EXPECT_EQ("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.", + k3.toText()); + EXPECT_EQ("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.:120", + k4.toText()); + EXPECT_EQ(120, k4.getDigestbits()); + EXPECT_EQ("test.example.::hmac-sha1.", k5.toText()); + EXPECT_EQ(Name("test.example."), k6.getKeyName()); + EXPECT_EQ(Name("unknown"), k6.getAlgorithmName()); + + EXPECT_THROW(TSIGKey(""), isc::InvalidParameter); + EXPECT_THROW(TSIGKey(":"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("::"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("..:aa:"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example:xxxx:"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example.::"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example.:"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example.:MSG6Ng==:"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example.:MSG6Ng==:unknown"), isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.:"), + isc::InvalidParameter); + EXPECT_THROW(TSIGKey("test.example.:MSG6Ng==:hmac-md5.sig-alg.reg.int.:xxx"), + isc::InvalidParameter); +} + + +} // end namespace diff --git a/src/lib/dns/tests/tsigrecord_unittest.cc b/src/lib/dns/tests/tsigrecord_unittest.cc index 8b13789179..624f03b85e 100644 --- a/src/lib/dns/tests/tsigrecord_unittest.cc +++ b/src/lib/dns/tests/tsigrecord_unittest.cc @@ -1 +1,158 @@ +// Copyright (C) 2011-2023 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +#include <config.h> + +#include <vector> +#include <sstream> + +#include <gtest/gtest.h> + +#include <util/buffer.h> + +#include <dns/exceptions.h> +#include <dns/messagerenderer.h> +#include <dns/name.h> +#include <dns/rdata.h> +#include <dns/rdataclass.h> +#include <dns/tsig.h> +#include <dns/tsigkey.h> +#include <dns/tsigrecord.h> + +#include <dns/tests/unittest_util.h> +#include <util/unittests/wiredata.h> + +using namespace std; +using namespace isc::util; +using namespace isc::dns; +using namespace isc::dns::rdata; +using namespace isc::dns::rdata::any; +using isc::UnitTestUtil; +using isc::util::unittests::matchWireData; + +namespace { +class TSIGRecordTest : public ::testing::Test { +protected: + TSIGRecordTest() : + test_name("www.example.com"), test_mac(16, 0xda), + test_rdata(TSIG(TSIGKey::HMACMD5_NAME(), 0x4da8877a, TSIGContext::DEFAULT_FUDGE, + test_mac.size(), &test_mac[0], 0x2d65, 0, 0, NULL)), + test_record(test_name, test_rdata), + buffer(0) { + } + + const Name test_name; + + vector<unsigned char> test_mac; + + const TSIG test_rdata; + + const TSIGRecord test_record; + + OutputBuffer buffer; + + MessageRenderer renderer; + + vector<unsigned char> data; +}; + +TEST_F(TSIGRecordTest, getName) { + EXPECT_EQ(test_name, test_record.getName()); +} + +TEST_F(TSIGRecordTest, getLength) { + // 85 = 17 + 26 + 16 + 26 + // len(www.example.com) = 17 + // len(hmac-md5.sig-alg.reg.int) = 26 + // len(MAC) = 16 + // the rest are fixed length fields (26 in total) + EXPECT_EQ(85, test_record.getLength()); +} + +TEST_F(TSIGRecordTest, fromParams) { + // Construct the same TSIG RR as test_record from parameters. + // See the getLength test for the magic number of 85 (although it + // actually doesn't matter) + const TSIGRecord record(test_name, TSIGRecord::getClass(), + TSIGRecord::getTTL(), test_rdata, 85); + // Perform straight sanity checks + EXPECT_EQ(test_name, record.getName()); + EXPECT_EQ(85, record.getLength()); + EXPECT_EQ(0, test_rdata.compare(record.getRdata())); + + // The constructor doesn't check the length... + EXPECT_NO_THROW(TSIGRecord(test_name, TSIGRecord::getClass(), + TSIGRecord::getTTL(), test_rdata, 82)); + // ...even for impossibly small values... + EXPECT_NO_THROW(TSIGRecord(test_name, TSIGRecord::getClass(), + TSIGRecord::getTTL(), test_rdata, 1)); + // ...or too large values. + EXPECT_NO_THROW(TSIGRecord(test_name, TSIGRecord::getClass(), + TSIGRecord::getTTL(), test_rdata, 65536)); + + // RDATA must indeed be TSIG + EXPECT_THROW(TSIGRecord(test_name, TSIGRecord::getClass(), + TSIGRecord::getTTL(), in::A("192.0.2.1"), 85), + DNSMessageFORMERR); + + // Unexpected class + EXPECT_THROW(TSIGRecord(test_name, RRClass::IN(), TSIGRecord::getTTL(), + test_rdata, 85), DNSMessageFORMERR); + + // Unexpected TTL + EXPECT_THROW(TSIGRecord(test_name, TSIGRecord::getClass(), + RRTTL(3600), test_rdata, 85), DNSMessageFORMERR); +} + +TEST_F(TSIGRecordTest, recordToWire) { + UnitTestUtil::readWireData("tsigrecord_toWire1.wire", data); + EXPECT_EQ(1, test_record.toWire(renderer)); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); + + // Same test for a dumb buffer + buffer.clear(); + EXPECT_EQ(1, test_record.toWire(buffer)); + matchWireData(&data[0], data.size(), + buffer.getData(), buffer.getLength()); +} + +TEST_F(TSIGRecordTest, recordToOLongToWire) { + // By setting the limit to "record length - 1", it will fail, and the + // renderer will be marked as "truncated". + renderer.setLengthLimit(test_record.getLength() - 1); + EXPECT_FALSE(renderer.isTruncated()); // not marked before render attempt + EXPECT_EQ(0, test_record.toWire(renderer)); + EXPECT_TRUE(renderer.isTruncated()); +} + +TEST_F(TSIGRecordTest, recordToWireAfterNames) { + // A similar test but the TSIG RR follows some domain names that could + // cause name compression inside TSIG. Our implementation shouldn't + // compress either owner (key) name or the algorithm name. This test + // confirms that. + + UnitTestUtil::readWireData("tsigrecord_toWire2.wire", data); + renderer.writeName(TSIGKey::HMACMD5_NAME()); + renderer.writeName(Name("foo.example.com")); + EXPECT_EQ(1, test_record.toWire(renderer)); + matchWireData(&data[0], data.size(), + renderer.getData(), renderer.getLength()); +} + +TEST_F(TSIGRecordTest, toText) { + EXPECT_EQ("www.example.com. 0 ANY TSIG hmac-md5.sig-alg.reg.int. " + "1302890362 300 16 2tra2tra2tra2tra2tra2g== 11621 NOERROR 0\n", + test_record.toText()); +} + +// test operator<<. We simply confirm it appends the result of toText(). +TEST_F(TSIGRecordTest, LeftShiftOperator) { + ostringstream oss; + oss << test_record; + EXPECT_EQ(test_record.toText(), oss.str()); +} +} // end namespace diff --git a/src/lib/dns/tests/unittest_util.cc b/src/lib/dns/tests/unittest_util.cc index a86203d3dc..5e0c620527 100644 --- a/src/lib/dns/tests/unittest_util.cc +++ b/src/lib/dns/tests/unittest_util.cc @@ -21,7 +21,7 @@ #include <dns/tests/unittest_util.h> using namespace std; -//using namespace isc::dns; +using namespace isc::dns; using isc::UnitTestUtil; @@ -121,3 +121,20 @@ UnitTestUtil::readWireData(const string& datastr, } } while (!iss.eof()); } + +::testing::AssertionResult +UnitTestUtil::matchName(const char*, const char*, + const isc::dns::Name& name1, + const isc::dns::Name& name2) { + ::testing::Message msg; + + NameComparisonResult cmpresult = name1.compare(name2); + if (cmpresult.getOrder() != 0 || + cmpresult.getRelation() != NameComparisonResult::EQUAL) { + msg << "Two names are expected to be equal but not:\n" + << " One: " << name1 << "\n" + << "Other: " << name2 << "\n"; + return (::testing::AssertionFailure(msg)); + } + return (::testing::AssertionSuccess()); +} diff --git a/src/lib/dns/tests/unittest_util.h b/src/lib/dns/tests/unittest_util.h index f1af813de0..9026119727 100644 --- a/src/lib/dns/tests/unittest_util.h +++ b/src/lib/dns/tests/unittest_util.h @@ -37,6 +37,18 @@ public: static void readWireData(const std::string& datastr, std::vector<unsigned char>& data); + /// + /// Compare two names. + /// + /// This check method uses \c Name::compare() for comparison, which performs + /// deeper checks including the equality of offsets, and should be better + /// than EXPECT_EQ, which uses operator==. Like the \c matchWireData() + /// method, the usage is a bit awkward; the caller should use + /// \c EXPECT_PRED_FORMAT2. + /// + static ::testing::AssertionResult + matchName(const char* nameexp1, const char* nameexp2, + const isc::dns::Name& name1, const isc::dns::Name& name2); }; } #endif // UNITTEST_UTIL_H diff --git a/src/lib/dns/tests/zone_checker_unittest.cc b/src/lib/dns/tests/zone_checker_unittest.cc deleted file mode 100644 index 8b13789179..0000000000 --- a/src/lib/dns/tests/zone_checker_unittest.cc +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/lib/dns/tsig.cc b/src/lib/dns/tsig.cc index ee5ba98e3c..7281d9ab04 100644 --- a/src/lib/dns/tsig.cc +++ b/src/lib/dns/tsig.cc @@ -6,34 +6,30 @@ #include <config.h> -#include <sys/time.h> - -#include <stdint.h> - -#include <cassert> -#include <vector> - -#include <boost/shared_ptr.hpp> - #include <exceptions/exceptions.h> - -#include <util/buffer.h> -#include <util/time_utilities.h> - +#include <cryptolink/cryptolink.h> +#include <cryptolink/crypto_hmac.h> #include <dns/rdataclass.h> #include <dns/rrclass.h> #include <dns/tsig.h> #include <dns/tsigerror.h> #include <dns/tsigkey.h> +#include <util/buffer.h> +#include <util/time_utilities.h> -#include <cryptolink/cryptolink.h> -#include <cryptolink/crypto_hmac.h> +#include <cassert> +#include <sys/time.h> +#include <stdint.h> +#include <vector> +#include <boost/shared_ptr.hpp> -using namespace std; using namespace isc::util; using namespace isc::cryptolink; using namespace isc::dns::rdata; +using namespace std; + + namespace isc { namespace dns { namespace { @@ -187,10 +183,11 @@ TSIGContext::TSIGContextImpl::digestPreviousMAC(HMACPtr hmac) { } void -TSIGContext::TSIGContextImpl::digestTSIGVariables( - HMACPtr hmac, uint16_t rrclass, uint32_t rrttl, uint64_t time_signed, - uint16_t fudge, uint16_t error, uint16_t otherlen, const void* otherdata, - bool time_variables_only) const { +TSIGContext::TSIGContextImpl::digestTSIGVariables(HMACPtr hmac, uint16_t rrclass, + uint32_t rrttl, uint64_t time_signed, + uint16_t fudge, uint16_t error, + uint16_t otherlen, const void* otherdata, + bool time_variables_only) const { // It's bit complicated, but we can still predict the necessary size of // the data to be digested. So we precompute it to avoid possible // reallocation inside OutputBuffer (not absolutely necessary, but this diff --git a/src/lib/dns/tsigrecord.cc b/src/lib/dns/tsigrecord.cc index 13b7e9acdd..1d36373b0c 100644 --- a/src/lib/dns/tsigrecord.cc +++ b/src/lib/dns/tsigrecord.cc @@ -6,16 +6,15 @@ #include <config.h> -#include <ostream> -#include <string> - -#include <util/buffer.h> - #include <dns/exceptions.h> #include <dns/messagerenderer.h> #include <dns/rrclass.h> #include <dns/rrttl.h> #include <dns/tsigrecord.h> +#include <util/buffer.h> + +#include <ostream> +#include <string> using namespace isc::util; using namespace isc::dns::rdata; @@ -105,7 +104,7 @@ toWireCommon(OUTPUT& output, const rdata::any::TSIG& rdata) { } } -int +uint32_t TSIGRecord::toWire(AbstractMessageRenderer& renderer) const { // If adding the TSIG would exceed the size limit, don't do it. if (renderer.getLength() + length_ > renderer.getLengthLimit()) { @@ -119,7 +118,7 @@ TSIGRecord::toWire(AbstractMessageRenderer& renderer) const { return (1); } -int +uint32_t TSIGRecord::toWire(OutputBuffer& buffer) const { key_name_.toWire(buffer); toWireCommon(buffer, rdata_); diff --git a/src/lib/dns/tsigrecord.h b/src/lib/dns/tsigrecord.h index f754b2f69e..1ae8f3dfc1 100644 --- a/src/lib/dns/tsigrecord.h +++ b/src/lib/dns/tsigrecord.h @@ -244,7 +244,7 @@ public: /// \param renderer DNS message rendering context that encapsulates the /// output buffer and name compression information. /// \return 1 if the TSIG RR fits in the message size limit; otherwise 0. - int toWire(AbstractMessageRenderer& renderer) const; + uint32_t toWire(AbstractMessageRenderer& renderer) const; /// \brief Render the \c TSIG RR in the wire format. /// @@ -252,7 +252,7 @@ public: /// except it renders the RR in an \c OutputBuffer and therefore /// does not care about message size limit. /// As a consequence it always returns 1. - int toWire(isc::util::OutputBuffer& buffer) const; + uint32_t toWire(isc::util::OutputBuffer& buffer) const; /// Convert the TSIG record to a string. /// diff --git a/src/lib/dns/txt_like.h b/src/lib/dns/txt_like.h new file mode 100644 index 0000000000..d0610e333b --- /dev/null +++ b/src/lib/dns/txt_like.h @@ -0,0 +1,237 @@ +// Copyright (C) 2011-2015,2021 Internet Systems Consortium, Inc. ("ISC") +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#ifndef TXT_LIKE_H +#define TXT_LIKE_H 1 + +#include <dns/master_lexer.h> +#include <dns/char_string.h> + +#include <stdint.h> +#include <algorithm> +#include <string> +#include <sstream> +#include <vector> + +namespace isc { +namespace dns { +namespace rdata { +namespace generic { +namespace detail { + +/// \brief \c rdata::TXTLikeImpl class represents the TXT-like RDATA for TXT +/// and SPF types. +/// +/// This class implements the basic interfaces inherited by the TXT and SPF +/// classes from the abstract \c rdata::Rdata class, and provides trivial +/// accessors to TXT-like RDATA. +template<class Type, uint16_t typeCode>class TXTLikeImpl { +public: + /// \brief Constructor from wire-format data. + /// + /// \param buffer A buffer storing the wire format data. + /// \param rdata_len The length of the RDATA in bytes, normally expected + /// to be the value of the RDLENGTH field of the corresponding RR. + /// + /// <b>Exceptions</b> + /// + /// \c InvalidRdataLength is thrown if rdata_len exceeds the maximum. + /// \c DNSMessageFORMERR is thrown if the RR is malformed. + TXTLikeImpl(util::InputBuffer& buffer, size_t rdata_len) { + if (rdata_len > MAX_RDLENGTH) { + isc_throw(InvalidRdataLength, "RDLENGTH too large: " << rdata_len); + } + + if (rdata_len == 0) { // note that this couldn't happen in the loop. + isc_throw(DNSMessageFORMERR, "Error in parsing " << + RRType(typeCode) << " RDATA: 0-length character string"); + } + + do { + const uint8_t len = buffer.readUint8(); + if (rdata_len < len + 1) { + isc_throw(DNSMessageFORMERR, "Error in parsing " << + RRType(typeCode) << + " RDATA: character string length is too large: " << + static_cast<int>(len)); + } + std::vector<uint8_t> data(len + 1); + data[0] = len; + buffer.readData(&data[0] + 1, len); + string_list_.push_back(data); + + rdata_len -= (len + 1); + } while (rdata_len > 0); + } + + /// \brief Constructor from string. + /// + /// \throw CharStringTooLong the parameter string length exceeds maximum. + /// \throw InvalidRdataText the method cannot process the parameter data + explicit TXTLikeImpl(const std::string& txtstr) { + std::istringstream ss(txtstr); + MasterLexer lexer; + lexer.pushSource(ss); + + try { + buildFromTextHelper(lexer); + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "Failed to construct " << + RRType(typeCode) << " RDATA from '" << txtstr << + "': extra new line"); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct " << + RRType(typeCode) << " RDATA from '" << txtstr << "': " + << ex.what()); + } + } + + /// \brief Constructor using the master lexer. + /// + /// \throw CharStringTooLong the parameter string length exceeds maximum. + /// \throw InvalidRdataText the method cannot process the parameter data + /// + /// \param lexer A \c MasterLexer object parsing a master file for this + /// RDATA. + TXTLikeImpl(MasterLexer& lexer) { + buildFromTextHelper(lexer); + } + +private: + void buildFromTextHelper(MasterLexer& lexer) { + while (true) { + const MasterToken& token = lexer.getNextToken( + MasterToken::QSTRING, true); + if (token.getType() != MasterToken::STRING && + token.getType() != MasterToken::QSTRING) { + break; + } + string_list_.push_back(std::vector<uint8_t>()); + stringToCharString(token.getStringRegion(), string_list_.back()); + } + + // Let upper layer handle eol/eof. + lexer.ungetToken(); + + if (string_list_.empty()) { + isc_throw(InvalidRdataText, "Failed to construct " << + RRType(typeCode) << " RDATA: empty input"); + } + } + +public: + /// \brief The copy constructor. + /// + /// Trivial for now, we could've used the default one. + TXTLikeImpl(const TXTLikeImpl& other) : + string_list_(other.string_list_) + {} + + /// \brief Render the TXT-like data in the wire format to an OutputBuffer + /// object. + /// + /// \param buffer An output buffer to store the wire data. + void + toWire(util::OutputBuffer& buffer) const { + for (std::vector<std::vector<uint8_t> >::const_iterator it = + string_list_.begin(); + it != string_list_.end(); + ++it) + { + buffer.writeData(&(*it)[0], (*it).size()); + } + } + + /// \brief Render the TXT-like data in the wire format to an + /// AbstractMessageRenderer object. + /// + /// \param buffer An output AbstractMessageRenderer to send the wire data + /// to. + void + toWire(AbstractMessageRenderer& renderer) const { + for (std::vector<std::vector<uint8_t> >::const_iterator it = + string_list_.begin(); + it != string_list_.end(); + ++it) + { + renderer.writeData(&(*it)[0], (*it).size()); + } + } + + /// \brief Convert the TXT-like data to a string. + /// + /// \return A \c string object that represents the TXT-like data. + std::string + toText() const { + std::string s; + + for (std::vector<std::vector<uint8_t> >::const_iterator it = + string_list_.begin(); it != string_list_.end(); ++it) + { + if (!s.empty()) { + s.push_back(' '); + } + s.push_back('"'); + s.append(charStringToString(*it)); + s.push_back('"'); + } + + return (s); + } + + /// \brief Compare two instances of TXT-like RDATA. + /// + /// It is up to the caller to make sure that \c other is an object of the + /// same \c TXTLikeImpl class. + /// + /// \param other the right-hand operand to compare against. + /// \return < 0 if \c this would be sorted before \c other. + /// \return 0 if \c this is identical to \c other in terms of sorting + /// order. + /// \return > 0 if \c this would be sorted after \c other. + int + compare(const TXTLikeImpl& other) const { + // This implementation is not efficient. Revisit this (TBD). + util::OutputBuffer this_buffer(0); + toWire(this_buffer); + uint8_t const* const this_data = (uint8_t const*)this_buffer.getData(); + const size_t this_len = this_buffer.getLength(); + + util::OutputBuffer other_buffer(0); + other.toWire(other_buffer); + uint8_t const* const other_data + = (uint8_t const*)other_buffer.getData(); + const size_t other_len = other_buffer.getLength(); + + const size_t cmplen = std::min(this_len, other_len); + const int cmp = memcmp(this_data, other_data, cmplen); + + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) ? 0 : + (this_len < other_len) ? -1 : 1); + } + } + +private: + /// Note: this is a prototype version; we may reconsider + /// this representation later. + std::vector<std::vector<uint8_t> > string_list_; +}; + +} // namespace detail +} // namespace generic +} // namespace rdata +} // namespace dns +} // namespace isc + +#endif // TXT_LIKE_H + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/util/unittests/wiredata.cc b/src/lib/util/unittests/wiredata.cc index c14c8f164d..ebec617a82 100644 --- a/src/lib/util/unittests/wiredata.cc +++ b/src/lib/util/unittests/wiredata.cc @@ -21,10 +21,8 @@ namespace unittests { void matchWireData(const void* expected_data, size_t expected_len, - const void* actual_data, size_t actual_len) -{ + const void* actual_data, size_t actual_len) { const size_t cmplen = std::min(expected_len, actual_len); - for (size_t i = 0; i < cmplen; ++i) { const int ebyte = static_cast<const uint8_t*>(expected_data)[i]; const int abyte = static_cast<const uint8_t*>(actual_data)[i]; |