Parcourir la source

[1136] Implement the SSHFP type, from RFC 4255

Mukund Sivaraman il y a 13 ans
Parent
commit
ea5ac57d50

+ 164 - 0
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 <config.h>
+
+#include <string>
+
+#include <boost/lexical_cast.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+#include <util/encode/hex.h>
+#include <dns/name.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+
+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<string>(static_cast<int>(algorithm_)) +
+            " " + lexical_cast<string>(static_cast<int>(fingerprint_type_)) +
+            " " + encodeHex(fingerprint_));
+}
+
+int
+SSHFP::compare(const Rdata& other) const {
+    const SSHFP& other_sshfp = dynamic_cast<const SSHFP&>(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

+ 58 - 0
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 <stdint.h>
+
+#include <string>
+
+#include <dns/name.h>
+#include <dns/rdata.h>
+
+// 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<uint8_t> fingerprint_;
+};
+
+// END_RDATA_NAMESPACE
+// END_ISC_NAMESPACE
+// END_HEADER_GUARD
+
+// Local Variables:
+// mode: c++
+// End:

+ 1 - 0
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

+ 79 - 0
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 <algorithm>
+#include <string>
+
+#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>
+
+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());
+}
+}

+ 2 - 0
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

+ 8 - 0
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

+ 6 - 0
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]

+ 7 - 0
src/lib/dns/tests/testdata/rdata_sshfp_fromWire2.spec

@@ -0,0 +1,7 @@
+#
+# SSHFP RDATA
+#
+[custom]
+sections: sshfp
+[sshfp]
+fingerprint: 123456789abcdef67890123456789abcdef67890

+ 23 - 0
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.