From: Mukund Sivaraman Date: Thu, 8 Mar 2012 03:53:41 +0000 (+0530) Subject: [1136] Implement the SSHFP type, from RFC 4255 X-Git-Tag: trac2351_base~226^2~116^2~127^2~8^2~11 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ea5ac57d508a17611cfae9d9ea1c238f59d52c51;p=thirdparty%2Fkea.git [1136] Implement the SSHFP type, from RFC 4255 --- diff --git a/src/lib/dns/rdata/generic/sshfp_44.cc b/src/lib/dns/rdata/generic/sshfp_44.cc new file mode 100644 index 0000000000..6320fd93ce --- /dev/null +++ b/src/lib/dns/rdata/generic/sshfp_44.cc @@ -0,0 +1,164 @@ +// Copyright (C) 2012 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 + +using namespace std; +using namespace boost; +using namespace isc::util; +using namespace isc::util::encode; + +// BEGIN_ISC_NAMESPACE +// BEGIN_RDATA_NAMESPACE + +SSHFP::SSHFP(InputBuffer& buffer, size_t rdata_len) +{ + if (rdata_len < 2) { + isc_throw(InvalidRdataLength, "SSHFP record too short"); + } + + algorithm_ = buffer.readUint8(); + fingerprint_type_ = buffer.readUint8(); + + rdata_len -= 2; + fingerprint_.resize(rdata_len); + buffer.readData(&fingerprint_[0], rdata_len); +} + +SSHFP::SSHFP(const std::string& sshfp_str) +{ + std::istringstream iss(sshfp_str); + // peekc should be of iss's char_type for isspace to work + std::istringstream::char_type peekc; + std::stringbuf fingerprintbuf; + uint32_t algorithm, fingerprint_type; + + iss >> algorithm >> fingerprint_type; + if (iss.bad() || iss.fail()) { + isc_throw(InvalidRdataText, "Invalid SSHFP text"); + } + if ((algorithm < 1) || (algorithm > 2)) { + isc_throw(InvalidRdataText, "SSHFP algorithm number out of range"); + } + if (fingerprint_type != 1) { + isc_throw(InvalidRdataText, "SSHFP fingerprint type out of range"); + } + + iss.read(&peekc, 1); + if (!iss.good() || !isspace(peekc, iss.getloc())) { + isc_throw(InvalidRdataText, "SSHFP presentation format error"); + } + + iss >> &fingerprintbuf; + + algorithm_ = algorithm; + fingerprint_type_ = fingerprint_type; + decodeHex(fingerprintbuf.str(), fingerprint_); +} + +SSHFP::SSHFP(uint8_t algorithm, uint8_t fingerprint_type, const string& fingerprint) +{ + if ((algorithm < 1) || (algorithm > 2)) { + isc_throw(InvalidRdataText, "SSHFP algorithm number out of range"); + } + if (fingerprint_type != 1) { + isc_throw(InvalidRdataText, "SSHFP fingerprint type out of range"); + } + + algorithm_ = algorithm; + fingerprint_type_ = fingerprint_type; + decodeHex(fingerprint, fingerprint_); +} + +SSHFP::SSHFP(const SSHFP& other) : + Rdata(), algorithm_(other.algorithm_), fingerprint_type_(other.fingerprint_type_), fingerprint_(other.fingerprint_) +{} + +void +SSHFP::toWire(OutputBuffer& buffer) const { + buffer.writeUint8(algorithm_); + buffer.writeUint8(fingerprint_type_); + buffer.writeData(&fingerprint_[0], fingerprint_.size()); +} + +void +SSHFP::toWire(AbstractMessageRenderer& renderer) const { + renderer.writeUint8(algorithm_); + renderer.writeUint8(fingerprint_type_); + renderer.writeData(&fingerprint_[0], fingerprint_.size()); +} + +string +SSHFP::toText() const { + return (lexical_cast(static_cast(algorithm_)) + + " " + lexical_cast(static_cast(fingerprint_type_)) + + " " + encodeHex(fingerprint_)); +} + +int +SSHFP::compare(const Rdata& other) const { + const SSHFP& other_sshfp = dynamic_cast(other); + + /* This doesn't really make any sort of sense, but in the name of + consistency... */ + + if (algorithm_ < other_sshfp.algorithm_) { + return (-1); + } else if (algorithm_ > other_sshfp.algorithm_) { + return (1); + } + + if (fingerprint_type_ < other_sshfp.fingerprint_type_) { + return (-1); + } else if (fingerprint_type_ > other_sshfp.fingerprint_type_) { + return (1); + } + + size_t this_len = fingerprint_.size(); + size_t other_len = other_sshfp.fingerprint_.size(); + size_t cmplen = min(this_len, other_len); + int cmp = memcmp(&fingerprint_[0], &other_sshfp.fingerprint_[0], cmplen); + if (cmp != 0) { + return (cmp); + } else { + return ((this_len == other_len) + ? 0 : (this_len < other_len) ? -1 : 1); + } +} + +uint8_t +SSHFP::getSSHFPAlgorithmNumber() const { + return (algorithm_); +} + +uint8_t +SSHFP::getSSHFPFingerprintType() const { + return (fingerprint_type_); +} + +// END_RDATA_NAMESPACE +// END_ISC_NAMESPACE diff --git a/src/lib/dns/rdata/generic/sshfp_44.h b/src/lib/dns/rdata/generic/sshfp_44.h new file mode 100644 index 0000000000..473dc2365a --- /dev/null +++ b/src/lib/dns/rdata/generic/sshfp_44.h @@ -0,0 +1,58 @@ +// Copyright (C) 2010 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 + +// BEGIN_ISC_NAMESPACE + +// BEGIN_COMMON_DECLARATIONS +// END_COMMON_DECLARATIONS + +// BEGIN_RDATA_NAMESPACE + +class SSHFP : public Rdata { +public: + // BEGIN_COMMON_MEMBERS + // END_COMMON_MEMBERS + + SSHFP(uint8_t algorithm, uint8_t fingerprint_type, const std::string& fingerprint); + + /// + /// Specialized methods + /// + uint8_t getSSHFPAlgorithmNumber() const; + uint8_t getSSHFPFingerprintType() const; + +private: + /// Note: this is a prototype version; we may reconsider + /// this representation later. + uint8_t algorithm_; + uint8_t fingerprint_type_; + std::vector fingerprint_; +}; + +// 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 7ba0314a8b..26b4630358 100644 --- a/src/lib/dns/tests/Makefile.am +++ b/src/lib/dns/tests/Makefile.am @@ -33,6 +33,7 @@ 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_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 diff --git a/src/lib/dns/tests/rdata_sshfp_unittest.cc b/src/lib/dns/tests/rdata_sshfp_unittest.cc new file mode 100644 index 0000000000..39e72e9a42 --- /dev/null +++ b/src/lib/dns/tests/rdata_sshfp_unittest.cc @@ -0,0 +1,79 @@ +// Copyright (C) 2012 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 + +using isc::UnitTestUtil; +using namespace std; +using namespace isc::dns; +using namespace isc::util; +using namespace isc::dns::rdata; + +namespace { +class Rdata_SSHFP_Test : public RdataTest { + // there's nothing to specialize +}; + +const string sshfp_txt("2 1 123456789abcdef67890123456789abcdef67890"); +const generic::SSHFP rdata_sshfp(2, 1, "123456789abcdef67890123456789abcdef67890"); + +TEST_F(Rdata_SSHFP_Test, createFromText) { + const generic::SSHFP rdata_sshfp2(sshfp_txt); + EXPECT_EQ(0, rdata_sshfp2.compare(rdata_sshfp)); +} + +TEST_F(Rdata_SSHFP_Test, badText) { + EXPECT_THROW(const generic::SSHFP rdata_sshfp("1"), InvalidRdataText); + EXPECT_THROW(const generic::SSHFP rdata_sshfp("1 2"), InvalidRdataText); + EXPECT_THROW(const generic::SSHFP rdata_sshfp("BUCKLE MY SHOES"), InvalidRdataText); + EXPECT_THROW(const generic::SSHFP rdata_sshfp("1 2 foo bar"), InvalidRdataText); +} + +TEST_F(Rdata_SSHFP_Test, copy) { + const generic::SSHFP rdata_sshfp2(rdata_sshfp); + EXPECT_EQ(0, rdata_sshfp.compare(rdata_sshfp2)); +} + +TEST_F(Rdata_SSHFP_Test, createFromWire) { + EXPECT_EQ(0, rdata_sshfp.compare( + *rdataFactoryFromFile(RRType("SSHFP"), RRClass("IN"), + "rdata_sshfp_fromWire.wire"))); + // TBD: more tests +} + +TEST_F(Rdata_SSHFP_Test, toText) { + EXPECT_EQ("2 1 123456789abcdef67890123456789abcdef67890", rdata_sshfp.toText()); +} + +TEST_F(Rdata_SSHFP_Test, getSSHFPAlgorithmNumber) { + EXPECT_EQ(2, rdata_sshfp.getSSHFPAlgorithmNumber()); +} + +TEST_F(Rdata_SSHFP_Test, getSSHFPFingerprintType) { + EXPECT_EQ(1, rdata_sshfp.getSSHFPFingerprintType()); +} +} diff --git a/src/lib/dns/tests/testdata/Makefile.am b/src/lib/dns/tests/testdata/Makefile.am index fdf102530c..a93e2f3534 100644 --- a/src/lib/dns/tests/testdata/Makefile.am +++ b/src/lib/dns/tests/testdata/Makefile.am @@ -44,6 +44,7 @@ BUILT_SOURCES += rdata_rp_fromWire1.wire rdata_rp_fromWire2.wire BUILT_SOURCES += rdata_rp_fromWire3.wire rdata_rp_fromWire4.wire BUILT_SOURCES += rdata_rp_fromWire5.wire rdata_rp_fromWire6.wire BUILT_SOURCES += rdata_rp_toWire1.wire rdata_rp_toWire2.wire +BUILT_SOURCES += rdata_sshfp_fromWire1.wire rdata_sshfp_fromWire2.wire BUILT_SOURCES += rdata_afsdb_fromWire1.wire rdata_afsdb_fromWire2.wire BUILT_SOURCES += rdata_afsdb_fromWire3.wire rdata_afsdb_fromWire4.wire BUILT_SOURCES += rdata_afsdb_fromWire5.wire @@ -125,6 +126,7 @@ 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_fromWire1.spec rdata_sshfp_fromWire2.spec 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 diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire.wire b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire.wire new file mode 100644 index 0000000000..c7059e1452 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire.wire @@ -0,0 +1,8 @@ +### +### 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_fromWire1.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.spec new file mode 100644 index 0000000000..e28a62fb0f --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire1.spec @@ -0,0 +1,6 @@ +# +# A simplest form of SSHFP: all default parameters +# +[custom] +sections: sshfp +[sshfp] diff --git a/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec new file mode 100644 index 0000000000..59a336e592 --- /dev/null +++ b/src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec @@ -0,0 +1,7 @@ +# +# SSHFP RDATA +# +[custom] +sections: sshfp +[sshfp] +fingerprint: 123456789abcdef67890123456789abcdef67890 diff --git a/src/lib/util/python/gen_wiredata.py.in b/src/lib/util/python/gen_wiredata.py.in index 8b3eac044f..f997701540 100755 --- a/src/lib/util/python/gen_wiredata.py.in +++ b/src/lib/util/python/gen_wiredata.py.in @@ -822,6 +822,29 @@ class RP(RR): f.write('# MAILBOX=%s TEXT=%s\n' % (self.mailbox, self.text)) f.write('%s %s\n' % (mailbox_wire, text_wire)) +class SSHFP(RR): + '''Implements rendering SSHFP RDATA in the test data format. + + Configurable parameters are as follows (see the description of the + same name of attribute for the default value): + - algorithm (int): The algorithm number. + - fingerprint_type (int): The fingerprint type. + - fingerprint (string): The fingerprint. + ''' + algorithm = 2 + fingerprint_type = 1 + fingerprint = '123456789abcdef67890123456789abcdef67890' + def dump(self, f): + if self.rdlen is None: + self.rdlen = 2 + (len(self.fingerprint) / 2) + else: + self.rdlen = int(self.rdlen) + self.dump_header(f, self.rdlen) + f.write('# ALGORITHM=%d FINGERPRINT_TYPE=%d FINGERPRINT=%s\n' % (self.algorithm, + self.fingerprint_type, + self.fingerprint)) + f.write('%02x %02x %s\n' % (self.algorithm, self.fingerprint_type, self.fingerprint)) + class MINFO(RR): '''Implements rendering MINFO RDATA in the test data format.