Browse Source

Merge branch 'trac2386'

Mukund Sivaraman 12 years ago
parent
commit
dc0f34afb1

+ 3 - 0
src/lib/dns/gen-rdatacode.py.in

@@ -34,11 +34,14 @@ import sys
 #     new_rdata_factory_users = [('a', 'in'), ('a', 'ch'), ('soa', 'generic')]
 new_rdata_factory_users = [('a', 'in'), ('aaaa', 'in'),
                            ('cname', 'generic'),
+                           ('dlv', 'generic'),
                            ('dname', 'generic'),
+                           ('ds', 'generic'),
                            ('hinfo', 'generic'),
                            ('naptr', 'generic'),
                            ('mx', 'generic'),
                            ('ns', 'generic'),
+                           ('nsec', 'generic'),
                            ('ptr', 'generic'),
                            ('soa', 'generic'),
                            ('spf', 'generic'),

+ 81 - 21
src/lib/dns/rdata/generic/detail/ds_like.h

@@ -58,49 +58,109 @@ template <class Type, uint16_t typeCode> class DSLikeImpl {
 public:
     /// \brief Constructor from string.
     ///
-    /// <b>Exceptions</b>
+    /// The given string must represent a valid DS-like 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 tag field must be a valid decimal representation of an
+    /// unsigned 16-bit integer. The protocol and algorithm fields must
+    /// be valid decimal representations of unsigned 8-bit integers
+    /// respectively. The digest field may contain whitespace.
+    ///
+    /// \throw InvalidRdataText if any fields are out of their valid range.
     ///
-    /// \c InvalidRdataText is thrown if the method cannot process the
-    /// parameter data for any of the number of reasons.
+    /// \param ds_str A string containing the RDATA to be created
     DSLikeImpl(const std::string& ds_str) {
-        std::istringstream iss(ds_str);
-        // peekc should be of iss's char_type for isspace to work
-        std::istringstream::char_type peekc;
-        std::stringbuf digestbuf;
-        uint32_t tag, algorithm, digest_type;
-
-        iss >> tag >> algorithm >> digest_type;
-        if (iss.bad() || iss.fail()) {
+        try {
+            std::istringstream ss(ds_str);
+            MasterLexer lexer;
+            lexer.pushSource(ss);
+
+            constructFromLexer(lexer);
+
+            if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
+                isc_throw(InvalidRdataText,
+                          "Extra input text for " << RRType(typeCode) << ": "
+                          << ds_str);
+            }
+        } catch (const MasterLexer::LexerError& ex) {
             isc_throw(InvalidRdataText,
-                      "Invalid " << RRType(typeCode) << " text");
+                      "Failed to construct " << RRType(typeCode) << " from '" <<
+                      ds_str << "': " << ex.what());
         }
+    }
+
+    /// \brief Constructor with a context of MasterLexer.
+    ///
+    /// The \c lexer should point to the beginning of valid textual
+    /// representation of a DS-like RDATA.
+    ///
+    /// The tag field must be a valid decimal representation of an
+    /// unsigned 16-bit integer. The protocol and algorithm fields must
+    /// be valid decimal representations of unsigned 8-bit integers
+    /// respectively.
+    ///
+    /// \throw MasterLexer::LexerError General parsing error such as
+    /// missing field.
+    /// \throw InvalidRdataText if any fields are out of their valid range.
+    ///
+    /// \param lexer A \c MasterLexer object parsing a master file for the
+    /// RDATA to be created
+    DSLikeImpl(MasterLexer& lexer, const Name*, MasterLoader::Options,
+               MasterLoaderCallbacks&)
+    {
+        constructFromLexer(lexer);
+    }
+
+private:
+    void constructFromLexer(MasterLexer& lexer) {
+        const uint32_t tag =
+            lexer.getNextToken(MasterToken::NUMBER).getNumber();
         if (tag > 0xffff) {
             isc_throw(InvalidRdataText,
-                      RRType(typeCode) << " tag out of range");
+                      "Invalid " << RRType(typeCode) << " tag: " << tag);
         }
+
+        const uint32_t algorithm =
+            lexer.getNextToken(MasterToken::NUMBER).getNumber();
         if (algorithm > 0xff) {
             isc_throw(InvalidRdataText,
-                      RRType(typeCode) << " algorithm out of range");
+                      "Invalid " << RRType(typeCode) << " algorithm: "
+                      << algorithm);
         }
+
+        const uint32_t digest_type =
+            lexer.getNextToken(MasterToken::NUMBER).getNumber();
         if (digest_type > 0xff) {
             isc_throw(InvalidRdataText,
-                      RRType(typeCode) << " digest type out of range");
+                      "Invalid " << RRType(typeCode) << " digest type: "
+                      << digest_type);
         }
 
-        iss.read(&peekc, 1);
-        if (!iss.good() || !isspace(peekc, iss.getloc())) {
-            isc_throw(InvalidRdataText,
-                      RRType(typeCode) << " presentation format error");
+        std::string digest;
+        while (true) {
+            const MasterToken& token = lexer.getNextToken();
+            if (token.getType() != MasterToken::STRING) {
+                break;
+            }
+            digest.append(token.getString());
         }
 
-        iss >> &digestbuf;
+        lexer.ungetToken();
+
+        if (digest.size() == 0) {
+            isc_throw(InvalidRdataText,
+                      "Missing " << RRType(typeCode) << " digest");
+        }
 
         tag_ = tag;
         algorithm_ = algorithm;
         digest_type_ = digest_type;
-        decodeHex(digestbuf.str(), digest_);
+        decodeHex(digest, digest_);
     }
 
+public:
     /// \brief Constructor from wire-format data.
     ///
     /// \param buffer A buffer storing the wire format data.

+ 55 - 0
src/lib/dns/rdata/generic/detail/nsec_bitmap.cc

@@ -118,6 +118,61 @@ buildBitmapsFromText(const char* const rrtype_name,
     }
 }
 
+// Note: this function shares common code with buildBitmapsFromText()
+// above, but it is expected that buildBitmapsFromText() will be deleted
+// entirely once the transition to MasterLexer is done for all dependent
+// RR types. So a common method is not made from the two.
+void
+buildBitmapsFromLexer(const char* const rrtype_name,
+                      MasterLexer& lexer, vector<uint8_t>& typebits)
+{
+    uint8_t bitmap[8 * 1024];       // 64k bits
+    memset(bitmap, 0, sizeof(bitmap));
+
+    bool have_rrtypes = false;
+    while (true) {
+        const MasterToken& token = lexer.getNextToken();
+        if (token.getType() != MasterToken::STRING) {
+            break;
+        }
+
+        have_rrtypes = true;
+        std::string type_str;
+        try {
+            type_str = token.getString();
+            const int code = RRType(type_str).getCode();
+            bitmap[code / 8] |= (0x80 >> (code % 8));
+        } catch (const InvalidRRType&) {
+            isc_throw(InvalidRdataText, "Invalid RRtype in "
+                      << rrtype_name << " bitmap: " << type_str);
+        }
+    }
+
+    lexer.ungetToken();
+
+    if (!have_rrtypes) {
+         isc_throw(InvalidRdataText,
+                   rrtype_name << " record does not end with RR type mnemonic");
+    }
+
+    for (int window = 0; window < 256; ++window) {
+        int octet;
+        for (octet = 31; octet >= 0; octet--) {
+            if (bitmap[window * 32 + octet] != 0) {
+                break;
+            }
+        }
+        if (octet < 0) {
+            continue;
+        }
+        typebits.push_back(window);
+        typebits.push_back(octet + 1);
+        for (int i = 0; i <= octet; ++i) {
+            typebits.push_back(bitmap[window * 32 + i]);
+        }
+    }
+}
+
 void
 bitmapsToText(const vector<uint8_t>& typebits, ostringstream& oss) {
     // In the following loop we use string::at() rather than operator[].

+ 22 - 0
src/lib/dns/rdata/generic/detail/nsec_bitmap.h

@@ -15,6 +15,8 @@
 #ifndef NSECBITMAP_H
 #define NSECBITMAP_H 1
 
+#include <dns/master_lexer.h>
+
 #include <stdint.h>
 
 #include <sstream>
@@ -75,6 +77,26 @@ void buildBitmapsFromText(const char* const rrtype_name,
                           std::istringstream& iss,
                           std::vector<uint8_t>& typebits);
 
+/// \brief Convert textual sequence of RR types read from a lexer into
+/// type bitmaps.
+///
+/// See the other variant above for description.
+///
+/// \exception InvalidRdataText Data read from the given lexer does not
+/// meet the assumption (e.g. including invalid form of RR type, not
+/// ending with an RR type string).
+///
+/// \param rrtype_name Either "NSEC" or "NSEC3"; used as part of exception
+/// messages.
+/// \param lexer MasterLexer that provides consists of a complete
+/// sequence of textual lexemes of RR types for which the corresponding
+/// bits are set.
+/// \param typebits A placeholder for the resulting bitmaps.  Expected to be
+/// empty, but it's not checked.
+void buildBitmapsFromLexer(const char* const rrtype_name,
+                           isc::dns::MasterLexer& lexer,
+                           std::vector<uint8_t>& typebits);
+
 /// \brief Convert type bitmaps to textual sequence of RR types.
 ///
 /// This function converts wire-format data of type bitmaps for NSEC/NSEC3

+ 5 - 0
src/lib/dns/rdata/generic/dlv_32769.cc

@@ -45,6 +45,11 @@ DLV::DLV(InputBuffer& buffer, size_t rdata_len) :
     impl_(new DLVImpl(buffer, rdata_len))
 {}
 
+DLV::DLV(MasterLexer& lexer, const Name* origin, MasterLoader::Options options,
+         MasterLoaderCallbacks& callbacks) :
+    impl_(new DLVImpl(lexer, origin, options, callbacks))
+{}
+
 /// \brief Copy constructor
 ///
 /// A copy of the implementation object is allocated and constructed.

+ 5 - 0
src/lib/dns/rdata/generic/ds_43.cc

@@ -39,6 +39,11 @@ DS::DS(InputBuffer& buffer, size_t rdata_len) :
     impl_(new DSImpl(buffer, rdata_len))
 {}
 
+DS::DS(MasterLexer& lexer, const Name* origin, MasterLoader::Options options,
+       MasterLoaderCallbacks& callbacks) :
+    impl_(new DSImpl(lexer, origin, options, callbacks))
+{}
+
 DS::DS(const DS& source) :
     Rdata(), impl_(new DSImpl(*source.impl_))
 {}

+ 74 - 14
src/lib/dns/rdata/generic/nsec_47.cc

@@ -27,6 +27,7 @@
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
 #include <dns/rdata/generic/detail/nsec_bitmap.h>
+#include <dns/rdata/generic/detail/lexer_util.h>
 
 #include <stdio.h>
 #include <time.h>
@@ -35,6 +36,7 @@ using namespace std;
 using namespace isc::util;
 using namespace isc::util::encode;
 using namespace isc::dns::rdata::generic::detail::nsec;
+using isc::dns::rdata::generic::detail::createNameFromLexer;
 
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
@@ -49,24 +51,50 @@ struct NSECImpl {
     vector<uint8_t> typebits_;
 };
 
+/// \brief Constructor from string.
+///
+/// The given string must represent a valid NSEC 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 Next Domain Name field must be absolute since there's no
+/// parameter that specifies the origin name; if it is not absolute,
+/// \c MissingNameOrigin exception will be thrown.  This must not be
+/// represented as a quoted string.
+///
+/// The type mnemonics must be valid, and separated by whitespace. If
+/// any invalid mnemonics are found, InvalidRdataText exception is
+/// thrown.
+///
+/// \throw MissingNameOrigin Thrown when the Next Domain Name is not absolute.
+/// \throw InvalidRdataText if any fields are out of their valid range.
+///
+/// \param nsec_str A string containing the RDATA to be created
 NSEC::NSEC(const std::string& nsec_str) :
     impl_(NULL)
 {
-    istringstream iss(nsec_str);
-    string nextname;
-
-    iss >> nextname;
-    if (iss.bad() || iss.fail()) {
-        isc_throw(InvalidRdataText, "Invalid NSEC name");
-    }
-    if (iss.eof()) {
-        isc_throw(InvalidRdataText, "NSEC bitmap is missing");
+    try {
+        std::istringstream ss(nsec_str);
+        MasterLexer lexer;
+        lexer.pushSource(ss);
+
+        const Name origin_name(createNameFromLexer(lexer, NULL));
+
+        vector<uint8_t> typebits;
+        buildBitmapsFromLexer("NSEC", lexer, typebits);
+
+        impl_ = new NSECImpl(origin_name, typebits);
+
+        if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
+            isc_throw(InvalidRdataText,
+                      "Extra input text for NSEC: " << nsec_str);
+        }
+    } catch (const MasterLexer::LexerError& ex) {
+        isc_throw(InvalidRdataText,
+                  "Failed to construct NSEC from '" << nsec_str << "': "
+                  << ex.what());
     }
-
-    vector<uint8_t> typebits;
-    buildBitmapsFromText("NSEC", iss, typebits);
-
-    impl_ = new NSECImpl(Name(nextname), typebits);
 }
 
 NSEC::NSEC(InputBuffer& buffer, size_t rdata_len) {
@@ -87,6 +115,38 @@ NSEC::NSEC(InputBuffer& buffer, size_t rdata_len) {
     impl_ = new NSECImpl(nextname, typebits);
 }
 
+/// \brief Constructor with a context of MasterLexer.
+///
+/// The \c lexer should point to the beginning of valid textual
+/// representation of an NSEC RDATA.
+///
+/// The Next Domain Name 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.
+///
+/// The type mnemonics must be valid, and separated by whitespace. If
+/// any invalid mnemonics are found, InvalidRdataText exception is
+/// thrown.
+///
+/// \throw MasterLexer::LexerError General parsing error such as
+/// missing field.
+/// \throw MissingNameOrigin Thrown when the Next Domain Name is not
+/// absolute and \c origin is NULL.
+/// \throw InvalidRdataText if any fields are out of their valid range.
+///
+/// \param lexer A \c MasterLexer object parsing a master file for the
+/// RDATA to be created
+NSEC::NSEC(MasterLexer& lexer, const Name* origin, MasterLoader::Options,
+           MasterLoaderCallbacks&)
+{
+    const Name origin_name(createNameFromLexer(lexer, origin));
+
+    vector<uint8_t> typebits;
+    buildBitmapsFromLexer("NSEC", lexer, typebits);
+
+    impl_ = new NSECImpl(origin_name, typebits);
+}
+
 NSEC::NSEC(const NSEC& source) :
     Rdata(), impl_(new NSECImpl(*source.impl_))
 {}

+ 26 - 1
src/lib/dns/tests/rdata_ds_like_unittest.cc

@@ -61,6 +61,14 @@ typedef testing::Types<generic::DS, generic::DLV> Implementations;
 
 TYPED_TEST_CASE(Rdata_DS_LIKE_Test, Implementations);
 
+TYPED_TEST(Rdata_DS_LIKE_Test, createFromText) {
+    // It's valid for the digest's presentation format to contain
+    // spaces. See RFC4034 section 5.3.
+    EXPECT_EQ(0, this->rdata_ds_like.compare(
+        TypeParam("12892 5 2 F1E184C0E1D615D20EB3C223ACED3B03C773DD952D5F0EB5"
+                  "C777 58 6DE18  \t DA6B5")));
+}
+
 TYPED_TEST(Rdata_DS_LIKE_Test, toText_DS_LIKE) {
     EXPECT_EQ(ds_like_txt, this->rdata_ds_like.toText());
 }
@@ -90,9 +98,26 @@ TYPED_TEST(Rdata_DS_LIKE_Test, createFromLexer_DS_LIKE) {
         *test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(),
                                      ds_like_txt)));
 
+    // Whitespace is okay
+    EXPECT_EQ(0, this->rdata_ds_like.compare(
+        *test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(),
+                                     "12892 5 2 F1E184C0E1D615D20EB3C223ACED3B0"
+                                     "3C773DD952D5F0EB5C777 58 6DE18  \t DA6B5"
+             )));
+
     // Exceptions cause NULL to be returned.
+
+    // Bad tag
+    EXPECT_FALSE(test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(),
+                                             "65536 5 2 BEEF"));
+
+    // Bad algorithm
+    EXPECT_FALSE(test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(),
+                                             "1024 256 2 BEEF"));
+
+    // Bad digest type
     EXPECT_FALSE(test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(),
-                                             "99999 5 2 BEEF"));
+                                             "2048 2 256 BEEF"));
 }
 
 TYPED_TEST(Rdata_DS_LIKE_Test, assignment_DS_LIKE) {

+ 8 - 2
src/lib/dns/tests/rdata_nsec_unittest.cc

@@ -72,6 +72,12 @@ TEST_F(Rdata_NSEC_Test, createFromLexer_NSEC) {
         *test::createRdataUsingLexer(RRType::NSEC(), RRClass::IN(),
                                      nsec_txt)));
 
+    // test::createRdataUsingLexer() constructs relative to
+    // "example.org." origin.
+    EXPECT_EQ(0, generic::NSEC("www2.example.org. CNAME RRSIG NSEC").compare(
+        *test::createRdataUsingLexer(RRType::NSEC(), RRClass::IN(),
+                                     "www2 CNAME RRSIG NSEC")));
+
     // Exceptions cause NULL to be returned.
     EXPECT_FALSE(test::createRdataUsingLexer(RRType::NSEC(), RRClass::IN(),
                                              "www.isc.org."));
@@ -108,9 +114,9 @@ TEST_F(Rdata_NSEC_Test, getNextName) {
 
 TEST_F(Rdata_NSEC_Test, compare) {
     // trivial case: self equivalence
-    EXPECT_EQ(0, generic::NSEC("example A").
+    EXPECT_EQ(0, generic::NSEC("example. A").
               compare(generic::NSEC("example. A")));
-    EXPECT_EQ(0, generic::NSEC("EXAMPLE A"). // should be case insensitive
+    EXPECT_EQ(0, generic::NSEC("EXAMPLE. A"). // should be case insensitive
               compare(generic::NSEC("example. A")));
 
     // comparison attempt between incompatible RR types should be rejected

+ 1 - 7
src/lib/dns/tests/rdata_nsecbitmap_unittest.cc

@@ -168,12 +168,6 @@ TYPED_TEST(NSECLikeBitmapTest, createFromWire) {
                  DNSMessageFORMERR);
 }
 
-TYPED_TEST(NSECLikeBitmapTest, badText) {
-    // redundant space after the sequence
-    EXPECT_THROW(this->fromText(this->getCommonText() + "A "),
-                 InvalidRdataText);
-}
-
 // This tests the result of toText() with various kinds of NSEC/NSEC3 bitmaps.
 // It also tests the "from text" constructor as a result.
 TYPED_TEST(NSECLikeBitmapTest, toText) {
@@ -230,7 +224,7 @@ TYPED_TEST(NSECLikeBitmapTest, compare) {
 
 // NSEC bitmaps must not be empty
 TEST_F(NSECBitmapTest, emptyMap) {
-    EXPECT_THROW(this->fromText("next.example").toText(), InvalidRdataText);
+    EXPECT_THROW(this->fromText("next.example.").toText(), InvalidRdataText);
 
     EXPECT_THROW(this->rdataFactoryFromFile(this->getType(), RRClass::IN(),
                                             (this->getWireFilePrefix() +