From: Mukund Sivaraman Date: Tue, 14 Jan 2014 04:06:57 +0000 (+0530) Subject: [2185] Add support for the TLSA RR type X-Git-Tag: bind10-1.2.0beta1-release~41^2~6 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d88bd0463689b5d44ff6617ce080bb75f256bf96;p=thirdparty%2Fkea.git [2185] Add support for the TLSA RR type --- diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am index bda4e85333..b16c40c47f 100644 --- a/src/lib/dns/Makefile.am +++ b/src/lib/dns/Makefile.am @@ -70,6 +70,8 @@ EXTRA_DIST += rdata/generic/spf_99.cc EXTRA_DIST += rdata/generic/spf_99.h EXTRA_DIST += rdata/generic/sshfp_44.cc EXTRA_DIST += rdata/generic/sshfp_44.h +EXTRA_DIST += rdata/generic/tlsa_52.cc +EXTRA_DIST += rdata/generic/tlsa_52.h EXTRA_DIST += rdata/generic/txt_16.cc EXTRA_DIST += rdata/generic/txt_16.h EXTRA_DIST += rdata/generic/minfo_14.cc diff --git a/src/lib/dns/gen-rdatacode.py.in b/src/lib/dns/gen-rdatacode.py.in index 3fd3b33112..0c5bdf48d9 100755 --- a/src/lib/dns/gen-rdatacode.py.in +++ b/src/lib/dns/gen-rdatacode.py.in @@ -41,7 +41,7 @@ meta_types = { '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', '52': 'tlsa', '55': 'hip', '103': 'unspec', + '45': 'ipseckey', '55': 'hip', '103': 'unspec', '104': 'nid', '105': 'l32', '106': 'l64', '107': 'lp', '249': 'tkey', '253': 'mailb', '256': 'uri', '257': 'caa' } diff --git a/src/lib/dns/rdata/generic/tlsa_52.cc b/src/lib/dns/rdata/generic/tlsa_52.cc new file mode 100644 index 0000000000..fdf3bc32d3 --- /dev/null +++ b/src/lib/dns/rdata/generic/tlsa_52.cc @@ -0,0 +1,338 @@ +// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; +using boost::lexical_cast; +using namespace isc::util; +using namespace isc::util::encode; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +struct TLSAImpl { + // straightforward representation of TLSA RDATA fields + TLSAImpl(uint8_t certificate_usage, uint8_t selector, + uint8_t matching_type, const vector& data) : + certificate_usage_(certificate_usage), + selector_(selector), + matching_type_(matching_type), + data_(data) + {} + + uint8_t certificate_usage_; + uint8_t selector_; + uint8_t matching_type_; + const vector data_; +}; + +// helper function for string and lexer constructors +TLSAImpl* +TLSA::constructFromLexer(MasterLexer& lexer) { + const uint32_t certificate_usage = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (certificate_usage > 255) { + isc_throw(InvalidRdataText, + "TLSA certificate usage field out of range"); + } + + const uint32_t selector = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (selector > 255) { + isc_throw(InvalidRdataText, + "TLSA selector field out of range"); + } + + const uint32_t matching_type = + lexer.getNextToken(MasterToken::NUMBER).getNumber(); + if (matching_type > 255) { + isc_throw(InvalidRdataText, + "TLSA matching type field out of range"); + } + + std::string certificate_assoc_data; + std::string data_substr; + 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(data_substr); + certificate_assoc_data.append(data_substr); + } + lexer.ungetToken(); + + vector data; + // If certificate association data is missing, it's OK. See the API + // documentation of the constructor. + if (certificate_assoc_data.size() > 0) { + try { + decodeHex(certificate_assoc_data, data); + } catch (const isc::BadValue& e) { + isc_throw(InvalidRdataText, + "Bad TLSA certificate association data: " << e.what()); + } + } + + return (new TLSAImpl(certificate_usage, selector, matching_type, data)); +} + +/// \brief Constructor from string. +/// +/// The given string must represent a valid TLSA 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 Certificate Usage, Selector and Matching Type fields must be +/// within their valid ranges, but are not constrained to the values +/// defined in RFC6698. +/// +/// The Certificate Association Data Field field may be absent, but if +/// present it must contain a valid hex encoding of the data. Whitespace +/// is allowed in the hex text. +/// +/// \throw InvalidRdataText if any fields are missing, out of their +/// valid ranges, or are incorrect, or Certificate Association Data is +/// not a valid hex string. +/// +/// \param tlsa_str A string containing the RDATA to be created +TLSA::TLSA(const string& tlsa_str) : + impl_(NULL) +{ + // We use auto_ptr here because if there is an exception in this + // constructor, the destructor is not called and there could be a + // leak of the TLSAImpl that constructFromLexer() returns. + std::auto_ptr impl_ptr(NULL); + + try { + std::istringstream ss(tlsa_str); + MasterLexer lexer; + lexer.pushSource(ss); + + impl_ptr.reset(constructFromLexer(lexer)); + + if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) { + isc_throw(InvalidRdataText, "extra input text for TLSA: " + << tlsa_str); + } + } catch (const MasterLexer::LexerError& ex) { + isc_throw(InvalidRdataText, "Failed to construct TLSA from '" << + tlsa_str << "': " << ex.what()); + } + + impl_ = impl_ptr.release(); +} + +/// \brief Constructor with a context of MasterLexer. +/// +/// The \c lexer should point to the beginning of valid textual representation +/// of an TLSA RDATA. +/// +/// \throw MasterLexer::LexerError General parsing error such as missing field. +/// \throw InvalidRdataText Fields are out of their valid range, or are +/// incorrect, or Certificate Association Data is not a valid hex string. +/// +/// \param lexer A \c MasterLexer object parsing a master file for the +/// RDATA to be created +TLSA::TLSA(MasterLexer& lexer, const Name*, + MasterLoader::Options, MasterLoaderCallbacks&) : + impl_(constructFromLexer(lexer)) +{ +} + +/// \brief Constructor from InputBuffer. +/// +/// The passed buffer must contain a valid TLSA RDATA. +/// +/// The Certificate Usage, Selector and Matching Type fields must be +/// within their valid ranges, but are not constrained to the values +/// defined in RFC6698. It is okay for the certificate association data +/// to be missing (see the description of the constructor from string). +TLSA::TLSA(InputBuffer& buffer, size_t rdata_len) { + if (rdata_len < 3) { + isc_throw(InvalidRdataLength, "TLSA record too short"); + } + + const uint8_t certificate_usage = buffer.readUint8(); + const uint8_t selector = buffer.readUint8(); + const uint8_t matching_type = buffer.readUint8(); + + vector data; + rdata_len -= 3; + if (rdata_len > 0) { + data.resize(rdata_len); + buffer.readData(&data[0], rdata_len); + } + + impl_ = new TLSAImpl(certificate_usage, selector, matching_type, data); +} + +TLSA::TLSA(uint8_t certificate_usage, uint8_t selector, + uint8_t matching_type, const std::string& certificate_assoc_data) : + impl_(NULL) +{ + vector data; + try { + decodeHex(certificate_assoc_data, data); + } catch (const isc::BadValue& e) { + isc_throw(InvalidRdataText, + "Bad TLSA certificate association data: " << e.what()); + } + + impl_ = new TLSAImpl(certificate_usage, selector, matching_type, data); +} + +TLSA::TLSA(const TLSA& other) : + Rdata(), impl_(new TLSAImpl(*other.impl_)) +{} + +TLSA& +TLSA::operator=(const TLSA& source) { + if (this == &source) { + return (*this); + } + + TLSAImpl* newimpl = new TLSAImpl(*source.impl_); + delete impl_; + impl_ = newimpl; + + return (*this); +} + +TLSA::~TLSA() { + delete impl_; +} + +void +TLSA::toWire(OutputBuffer& buffer) const { + buffer.writeUint8(impl_->certificate_usage_); + buffer.writeUint8(impl_->selector_); + buffer.writeUint8(impl_->matching_type_); + + if (!impl_->data_.empty()) { + buffer.writeData(&impl_->data_[0], + impl_->data_.size()); + } +} + +void +TLSA::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint8(impl_->certificate_usage_); + renderer.writeUint8(impl_->selector_); + renderer.writeUint8(impl_->matching_type_); + + if (!impl_->data_.empty()) { + renderer.writeData(&impl_->data_[0], + impl_->data_.size()); + } +} + +string +TLSA::toText() const { + return (lexical_cast(static_cast(impl_->certificate_usage_)) + " " + + lexical_cast(static_cast(impl_->selector_)) + " " + + lexical_cast(static_cast(impl_->matching_type_)) + + (impl_->data_.empty() ? "" : + " " + encodeHex(impl_->data_))); +} + +int +TLSA::compare(const Rdata& other) const { + const TLSA& other_tlsa = dynamic_cast(other); + + if (impl_->certificate_usage_ < other_tlsa.impl_->certificate_usage_) { + return (-1); + } else if (impl_->certificate_usage_ > + other_tlsa.impl_->certificate_usage_) { + return (1); + } + + if (impl_->selector_ < other_tlsa.impl_->selector_) { + return (-1); + } else if (impl_->selector_ > other_tlsa.impl_->selector_) { + return (1); + } + + if (impl_->matching_type_ < other_tlsa.impl_->matching_type_) { + return (-1); + } else if (impl_->matching_type_ > + other_tlsa.impl_->matching_type_) { + return (1); + } + + const size_t this_len = impl_->data_.size(); + const size_t other_len = other_tlsa.impl_->data_.size(); + const size_t cmplen = min(this_len, other_len); + + if (cmplen > 0) { + const int cmp = memcmp(&impl_->data_[0], + &other_tlsa.impl_->data_[0], + cmplen); + if (cmp != 0) { + return (cmp); + } + } + + if (this_len == other_len) { + return (0); + } else if (this_len < other_len) { + return (-1); + } else { + return (1); + } +} + +uint8_t +TLSA::getCertificateUsage() const { + return (impl_->certificate_usage_); +} + +uint8_t +TLSA::getSelector() const { + return (impl_->selector_); +} + +uint8_t +TLSA::getMatchingType() const { + return (impl_->matching_type_); +} + +const std::vector& +TLSA::getData() const { + return (impl_->data_); +} + +size_t +TLSA::getDataLength() const { + return (impl_->data_.size()); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/tlsa_52.h b/src/lib/dns/rdata/generic/tlsa_52.h new file mode 100644 index 0000000000..7d8c69a3d1 --- /dev/null +++ b/src/lib/dns/rdata/generic/tlsa_52.h @@ -0,0 +1,65 @@ +// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +// BEGIN_HEADER_GUARD + +#include + +#include +#include + +#include +#include + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +struct TLSAImpl; + +class TLSA : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + TLSA(uint8_t certificate_usage, uint8_t selector, + uint8_t matching_type, const std::string& certificate_assoc_data); + TLSA& operator=(const TLSA& source); + ~TLSA(); + + /// + /// Specialized methods + /// + uint8_t getCertificateUsage() const; + uint8_t getSelector() const; + uint8_t getMatchingType() const; + const std::vector& getData() const; + size_t getDataLength() const; + +private: + TLSAImpl* constructFromLexer(MasterLexer& lexer); + + TLSAImpl* impl_; +}; + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE +// END_HEADER_GUARD + +// Local Variables: +// mode: c++ +// End: diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am index 5029681a32..2557228b65 100644 --- a/src/lib/dns/tests/Makefile.am +++ b/src/lib/dns/tests/Makefile.am @@ -59,6 +59,7 @@ 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 diff --git a/src/lib/dns/tests/rdata_tlsa_unittest.cc b/src/lib/dns/tests/rdata_tlsa_unittest.cc new file mode 100644 index 0000000000..3e494c2623 --- /dev/null +++ b/src/lib/dns/tests/rdata_tlsa_unittest.cc @@ -0,0 +1,336 @@ +// Copyright (C) 2014 Internet Systems Consortium, Inc. ("ISC") +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice appear in all copies. +// +// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH +// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, +// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE +// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +// PERFORMANCE OF THIS SOFTWARE. + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +using isc::UnitTestUtil; +using namespace std; +using namespace isc; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; + +namespace { +class Rdata_TLSA_Test : public RdataTest { +protected: + Rdata_TLSA_Test() : + tlsa_txt("0 0 1 d2abde240d7cd3ee6b4b28c54df034b9" + "7983a1d16e8a410e4561cb106618e971"), + rdata_tlsa(tlsa_txt) + {} + + void checkFromText_None(const string& rdata_str) { + checkFromText( + rdata_str, rdata_tlsa, false, false); + } + + void checkFromText_InvalidText(const string& rdata_str) { + checkFromText( + rdata_str, rdata_tlsa, true, true); + } + + void checkFromText_LexerError(const string& rdata_str) { + checkFromText + ( + rdata_str, rdata_tlsa, true, true); + } + + void checkFromText_BadString(const string& rdata_str) { + checkFromText + ( + rdata_str, rdata_tlsa, true, false); + } + + const string tlsa_txt; + const generic::TLSA rdata_tlsa; +}; + +const uint8_t rdata_tlsa_wiredata[] = { + // certificate usage + 0x00, + // selector + 0x00, + // matching type + 0x01, + // certificate association data + 0xd2, 0xab, 0xde, 0x24, 0x0d, 0x7c, 0xd3, 0xee, + 0x6b, 0x4b, 0x28, 0xc5, 0x4d, 0xf0, 0x34, 0xb9, + 0x79, 0x83, 0xa1, 0xd1, 0x6e, 0x8a, 0x41, 0x0e, + 0x45, 0x61, 0xcb, 0x10, 0x66, 0x18, 0xe9, 0x71 +}; + +TEST_F(Rdata_TLSA_Test, createFromText) { + // Basic test + checkFromText_None(tlsa_txt); + + // With different spacing + checkFromText_None("0 0 1 d2abde240d7cd3ee6b4b28c54df034b9" + "7983a1d16e8a410e4561cb106618e971"); + + // Combination of lowercase and uppercase + checkFromText_None("0 0 1 D2ABDE240D7CD3EE6B4B28C54DF034B9" + "7983a1d16e8a410e4561cb106618e971"); + + // spacing in the certificate association data field + checkFromText_None("0 0 1 d2abde240d7cd3ee6b4b28c54df034b9" + " 7983a1d16e8a410e4561cb106618e971"); + + // multi-line certificate association data field + checkFromText_None("0 0 1 ( d2abde240d7cd3ee6b4b28c54df034b9\n" + " 7983a1d16e8a410e4561cb106618e971 )"); + + // string constructor throws if there's extra text, + // but lexer constructor doesn't + checkFromText_BadString(tlsa_txt + "\n" + tlsa_txt); +} + +TEST_F(Rdata_TLSA_Test, fields) { + // Some of these may not be RFC conformant, but we relax the check + // in our code to work with other field values that may show up in + // the future. + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("1 0 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("2 0 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("3 0 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("128 0 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("255 0 1 12ab")); + + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 1 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 2 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 3 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 128 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 255 1 12ab")); + + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 0 1 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 0 2 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 0 3 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 0 128 12ab")); + EXPECT_NO_THROW(const generic::TLSA rdata_tlsa("0 0 255 12ab")); + + // > 255 would be broken + EXPECT_THROW(const generic::TLSA rdata_tlsa("256 0 1 12ab"), + InvalidRdataText); + EXPECT_THROW(const generic::TLSA rdata_tlsa("0 256 1 12ab"), + InvalidRdataText); + EXPECT_THROW(const generic::TLSA rdata_tlsa("0 0 256 12ab"), + InvalidRdataText); +} + +TEST_F(Rdata_TLSA_Test, badText) { + checkFromText_LexerError("1"); + checkFromText_LexerError("ONE 2 3 123456789abcdef67890123456789abcdef67890"); + checkFromText_LexerError("1 TWO 3 123456789abcdef67890123456789abcdef67890"); + checkFromText_LexerError("1 2 THREE 123456789abcdef67890123456789abcdef67890"); + checkFromText_InvalidText("1 2 3 BAABAABLACKSHEEP"); + checkFromText_InvalidText(tlsa_txt + " extra text"); + + // yes, these are redundant to the last test cases in the .fields + // test + checkFromText_InvalidText( + "2345 1 2 123456789abcdef67890123456789abcdef67890"); + checkFromText_InvalidText( + "3 1234 4 123456789abcdef67890123456789abcdef67890"); + checkFromText_InvalidText( + "5 6 1234 123456789abcdef67890123456789abcdef67890"); + + // negative values are trapped in the lexer rather than the + // constructor + checkFromText_LexerError("-2 0 1 123456789abcdef67890123456789abcdef67890"); + checkFromText_LexerError("0 -2 1 123456789abcdef67890123456789abcdef67890"); + checkFromText_LexerError("0 0 -2 123456789abcdef67890123456789abcdef67890"); +} + +TEST_F(Rdata_TLSA_Test, copyAndAssign) { + // Copy construct + generic::TLSA rdata_tlsa2(rdata_tlsa); + EXPECT_EQ(0, rdata_tlsa.compare(rdata_tlsa2)); + + // Assignment, mainly to confirm it doesn't cause disruption. + rdata_tlsa2 = rdata_tlsa; + EXPECT_EQ(0, rdata_tlsa.compare(rdata_tlsa2)); +} + +TEST_F(Rdata_TLSA_Test, createFromWire) { + // Basic test + EXPECT_EQ(0, rdata_tlsa.compare( + *rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire"))); + // Combination of lowercase and uppercase + EXPECT_EQ(0, rdata_tlsa.compare( + *rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire2"))); + // certificate_usage=0, selector=0, matching_type=1 + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire3.wire")); + + // certificate_usage=255, selector=0, matching_type=1 + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire4.wire")); + + // certificate_usage=0, selector=255, matching_type=1 + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire5.wire")); + + // certificate_usage=0, selector=0, matching_type=255 + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire6.wire")); + + // certificate_usage=3, selector=1, matching_type=2 + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire7.wire")); + + // short certificate association data + EXPECT_NO_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire8.wire")); + + // certificate association data is shorter than rdata len + EXPECT_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire9"), + InvalidBufferPosition); + + // certificate association data is missing + EXPECT_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire10"), + InvalidBufferPosition); + + // all RDATA is missing + EXPECT_THROW(rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire11"), + InvalidBufferPosition); +} + +TEST_F(Rdata_TLSA_Test, createFromParams) { + const generic::TLSA rdata_tlsa2( + 0, 0, 1, "d2abde240d7cd3ee6b4b28c54df034b9" + "7983a1d16e8a410e4561cb106618e971"); + EXPECT_EQ(0, rdata_tlsa2.compare(rdata_tlsa)); +} + +TEST_F(Rdata_TLSA_Test, toText) { + EXPECT_TRUE(boost::iequals(tlsa_txt, rdata_tlsa.toText())); + + const string tlsa_txt2("3 2 1"); + const generic::TLSA rdata_tlsa2(tlsa_txt2); + EXPECT_TRUE(boost::iequals(tlsa_txt2, rdata_tlsa2.toText())); + + const generic::TLSA rdata_tlsa3("3 2 1 "); + EXPECT_TRUE(boost::iequals(tlsa_txt2, rdata_tlsa3.toText())); +} + +TEST_F(Rdata_TLSA_Test, toWire) { + this->obuffer.clear(); + rdata_tlsa.toWire(this->obuffer); + + EXPECT_EQ(sizeof (rdata_tlsa_wiredata), + this->obuffer.getLength()); + + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + this->obuffer.getData(), + this->obuffer.getLength(), + rdata_tlsa_wiredata, sizeof(rdata_tlsa_wiredata)); +} + +TEST_F(Rdata_TLSA_Test, compare) { + const generic::TLSA rdata_tlsa2("0 0 0"); + EXPECT_EQ(-1, rdata_tlsa2.compare(rdata_tlsa)); + EXPECT_EQ(1, rdata_tlsa.compare(rdata_tlsa2)); +} + +TEST_F(Rdata_TLSA_Test, getCertificateUsage) { + EXPECT_EQ(0, rdata_tlsa.getCertificateUsage()); +} + +TEST_F(Rdata_TLSA_Test, getSelector) { + EXPECT_EQ(0, rdata_tlsa.getSelector()); +} + +TEST_F(Rdata_TLSA_Test, getMatchingType) { + EXPECT_EQ(1, rdata_tlsa.getMatchingType()); +} + +TEST_F(Rdata_TLSA_Test, getDataLength) { + EXPECT_EQ(32, rdata_tlsa.getDataLength()); +} + +TEST_F(Rdata_TLSA_Test, emptyCertificateAssociationDataFromWire) { + const uint8_t rdf_wiredata[] = { + // certificate usage + 0x03, + // selector + 0x01, + // matching type + 0x02, + }; + + const generic::TLSA rdf = + dynamic_cast + (*rdataFactoryFromFile(RRType("TLSA"), RRClass("IN"), + "rdata_tlsa_fromWire12")); + + EXPECT_EQ(3, rdf.getCertificateUsage()); + EXPECT_EQ(1, rdf.getSelector()); + EXPECT_EQ(2, rdf.getMatchingType()); + EXPECT_EQ(0, rdf.getDataLength()); + + this->obuffer.clear(); + rdf.toWire(this->obuffer); + + EXPECT_EQ(3, this->obuffer.getLength()); + + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + this->obuffer.getData(), + this->obuffer.getLength(), + rdf_wiredata, sizeof(rdf_wiredata)); +} + +TEST_F(Rdata_TLSA_Test, emptyCertificateAssociationDataFromString) { + const generic::TLSA rdata_tlsa2("3 2 1"); + const uint8_t rdata_tlsa2_wiredata[] = { + // certificate usage + 0x03, + // selector + 0x02, + // matching type + 0x01 + }; + + EXPECT_EQ(3, rdata_tlsa2.getCertificateUsage()); + EXPECT_EQ(2, rdata_tlsa2.getSelector()); + EXPECT_EQ(1, rdata_tlsa2.getMatchingType()); + EXPECT_EQ(0, rdata_tlsa2.getDataLength()); + + this->obuffer.clear(); + rdata_tlsa2.toWire(this->obuffer); + + EXPECT_EQ(3, this->obuffer.getLength()); + + EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData, + this->obuffer.getData(), + this->obuffer.getLength(), + rdata_tlsa2_wiredata, sizeof(rdata_tlsa2_wiredata)); +} +} diff --git a/src/lib/dns/tests/testdata/.gitignore b/src/lib/dns/tests/testdata/.gitignore index a0a14f4a19..8079354c05 100644 --- a/src/lib/dns/tests/testdata/.gitignore +++ b/src/lib/dns/tests/testdata/.gitignore @@ -87,6 +87,12 @@ /rdata_sshfp_fromWire6.wire /rdata_sshfp_fromWire7.wire /rdata_sshfp_fromWire8.wire +/rdata_tlsa_fromWire3.wire +/rdata_tlsa_fromWire4.wire +/rdata_tlsa_fromWire5.wire +/rdata_tlsa_fromWire6.wire +/rdata_tlsa_fromWire7.wire +/rdata_tlsa_fromWire8.wire /rdata_tsig_fromWire1.wire /rdata_tsig_fromWire2.wire /rdata_tsig_fromWire3.wire diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am index b6d7e35ada..15c3b3e7bb 100644 --- a/src/lib/dns/tests/testdata/Makefile.am +++ b/src/lib/dns/tests/testdata/Makefile.am @@ -56,6 +56,9 @@ BUILT_SOURCES += rdata_afsdb_toWire1.wire rdata_afsdb_toWire2.wire BUILT_SOURCES += rdata_soa_toWireUncompressed.wire BUILT_SOURCES += rdata_txt_fromWire2.wire rdata_txt_fromWire3.wire BUILT_SOURCES += rdata_txt_fromWire4.wire rdata_txt_fromWire5.wire +BUILT_SOURCES += rdata_tlsa_fromWire3.wire rdata_tlsa_fromWire4.wire +BUILT_SOURCES += rdata_tlsa_fromWire5.wire rdata_tlsa_fromWire6.wire +BUILT_SOURCES += rdata_tlsa_fromWire7.wire rdata_tlsa_fromWire8.wire BUILT_SOURCES += rdata_tsig_fromWire1.wire rdata_tsig_fromWire2.wire BUILT_SOURCES += rdata_tsig_fromWire3.wire rdata_tsig_fromWire4.wire BUILT_SOURCES += rdata_tsig_fromWire5.wire rdata_tsig_fromWire6.wire @@ -159,6 +162,12 @@ EXTRA_DIST += rrcode16_fromWire1 rrcode16_fromWire2 EXTRA_DIST += rrcode32_fromWire1 rrcode32_fromWire2 EXTRA_DIST += rrset_toWire1 rrset_toWire2 EXTRA_DIST += rrset_toWire3 rrset_toWire4 +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 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire new file mode 100644 index 0000000000..38e279ceca --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire @@ -0,0 +1,4 @@ +# 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 new file mode 100644 index 0000000000..67cecb15de --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire10 @@ -0,0 +1,6 @@ +# 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 new file mode 100644 index 0000000000..4b8ec930e2 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire11 @@ -0,0 +1,4 @@ +# 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 new file mode 100644 index 0000000000..71c7b9ceb5 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire12 @@ -0,0 +1,4 @@ +# 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 new file mode 100644 index 0000000000..36ce27806f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire2 @@ -0,0 +1,4 @@ +# 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 new file mode 100644 index 0000000000..39c8057d6a --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire3.spec @@ -0,0 +1,7 @@ +# +# TLSA RDATA +# +[custom] +sections: tlsa +[tlsa] +certificate_usage: 0 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire4.spec b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire4.spec new file mode 100644 index 0000000000..d97ae6a9e8 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire4.spec @@ -0,0 +1,7 @@ +# +# TLSA RDATA +# +[custom] +sections: tlsa +[tlsa] +certificate_usage: 255 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire5.spec b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire5.spec new file mode 100644 index 0000000000..cc3e296b84 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire5.spec @@ -0,0 +1,7 @@ +# +# TLSA RDATA +# +[custom] +sections: tlsa +[tlsa] +selector: 255 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire6.spec b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire6.spec new file mode 100644 index 0000000000..eed0ab90a6 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire6.spec @@ -0,0 +1,7 @@ +# +# TLSA RDATA +# +[custom] +sections: tlsa +[tlsa] +matching_type: 255 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire7.spec b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire7.spec new file mode 100644 index 0000000000..576df1ef36 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire7.spec @@ -0,0 +1,9 @@ +# +# TLSA RDATA +# +[custom] +sections: tlsa +[tlsa] +certificate_usage: 3 +selector: 1 +matching_type: 2 diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire8.spec b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire8.spec new file mode 100644 index 0000000000..ef5c108dff --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire8.spec @@ -0,0 +1,7 @@ +# +# TLSA RDATA +# +[custom] +sections: tlsa +[tlsa] +certificate_association_data: '0123' diff --git a/src/lib/dns/tests/testdata/rdata_tlsa_fromWire9 b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire9 new file mode 100644 index 0000000000..fc4560af51 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_tlsa_fromWire9 @@ -0,0 +1,7 @@ +# 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/util/python/gen_wiredata.py.in b/src/lib/util/python/gen_wiredata.py.in index b3858b6b26..1251f9252e 100755 --- a/src/lib/util/python/gen_wiredata.py.in +++ b/src/lib/util/python/gen_wiredata.py.in @@ -352,7 +352,7 @@ dict_rrtype = { 'none' : 0, 'a' : 1, 'ns' : 2, 'md' : 3, 'mf' : 4, 'cname' : 5, 'srv' : 33, 'naptr' : 35, 'kx' : 36, 'cert' : 37, 'a6' : 38, 'dname' : 39, 'opt' : 41, 'apl' : 42, 'ds' : 43, 'sshfp' : 44, 'ipseckey' : 45, 'rrsig' : 46, 'nsec' : 47, 'dnskey' : 48, - 'dhcid' : 49, 'nsec3' : 50, 'nsec3param' : 51, 'hip' : 55, + 'dhcid' : 49, 'nsec3' : 50, 'nsec3param' : 51, 'tlsa' : 52, 'hip' : 55, 'spf' : 99, 'unspec' : 103, 'tkey' : 249, 'tsig' : 250, 'dlv' : 32769, 'ixfr' : 251, 'axfr' : 252, 'mailb' : 253, 'maila' : 254, 'any' : 255 } @@ -1156,6 +1156,32 @@ class RRSIG(RR): f.write('# Tag=%d Signer=%s and Signature\n' % (self.tag, self.signer)) f.write('%04x %s %s\n' % (self.tag, name_wire, sig_wire)) +class TLSA(RR): + '''Implements rendering TLSA RDATA in the test data format. + + Configurable parameters are as follows (see the description of the + same name of attribute for the default value): + - certificate_usage (int): The certificate usage field value. + - selector (int): The selector field value. + - matching_type (int): The matching type field value. + - certificate_association_data (string): The certificate association data. + ''' + certificate_usage = 0 + selector = 0 + matching_type = 1 + certificate_association_data = 'd2abde240d7cd3ee6b4b28c54df034b97983a1d16e8a410e4561cb106618e971' + def dump(self, f): + if self.rdlen is None: + self.rdlen = 2 + (len(self.certificate_association_data) / 2) + else: + self.rdlen = int(self.rdlen) + self.dump_header(f, self.rdlen) + f.write('# CERTIFICATE_USAGE=%d SELECTOR=%d MATCHING_TYPE=%d CERTIFICATE_ASSOCIATION_DATA=%s\n' %\ + (self.certificate_usage, self.selector, self.matching_type,\ + self.certificate_association_data)) + f.write('%02x %02x %02x %s\n' % (self.certificate_usage, self.selector, self.matching_type,\ + self.certificate_association_data)) + class TSIG(RR): '''Implements rendering TSIG RDATA in the test data format.