Browse Source

Merge branch 'trac2426'

Mukund Sivaraman 11 years ago
parent
commit
0770d2df84
3 changed files with 74 additions and 67 deletions
  1. 70 63
      src/lib/dns/rdata.cc
  2. 1 1
      src/lib/dns/rdata.h
  3. 3 3
      src/lib/dns/tests/rdata_unittest.cc

+ 70 - 63
src/lib/dns/rdata.cc

@@ -15,6 +15,7 @@
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
 #include <util/buffer.h>
 #include <util/buffer.h>
+#include <util/encode/hex.h>
 
 
 #include <dns/name.h>
 #include <dns/name.h>
 #include <dns/messagerenderer.h>
 #include <dns/messagerenderer.h>
@@ -220,94 +221,100 @@ Generic::Generic(isc::util::InputBuffer& buffer, size_t rdata_len) {
     impl_ = new GenericImpl(data);
     impl_ = new GenericImpl(data);
 }
 }
 
 
-void
-Generic::constructHelper(const std::string& rdata_string) {
-    istringstream iss(rdata_string);
-    string unknown_mark;
-    iss >> unknown_mark;
-    if (unknown_mark != "\\#") {
+GenericImpl*
+Generic::constructFromLexer(MasterLexer& lexer) {
+    const MasterToken& token = lexer.getNextToken(MasterToken::STRING);
+    if (token.getString() != "\\#") {
         isc_throw(InvalidRdataText,
         isc_throw(InvalidRdataText,
-                  "Missing the special token (\\#) for generic RDATA encoding");
+                  "Missing the special token (\\#) for "
+                  "unknown RDATA encoding");
     }
     }
 
 
-    // RDLENGTH: read into a string so that we can easily reject invalid tokens
-    string rdlen_txt;
-    iss >> rdlen_txt;
-    istringstream iss_rdlen(rdlen_txt);
-    int32_t rdlen;
-    iss_rdlen >> rdlen;
-    if (iss_rdlen.rdstate() != ios::eofbit) {
-        isc_throw(InvalidRdataText,
-                  "Invalid representation for a generic RDLENGTH");
+    // Initialize with an absurd value.
+    uint32_t rdlen = 65536;
+
+    try {
+        rdlen = lexer.getNextToken(MasterToken::NUMBER).getNumber();
+    } catch (const MasterLexer::LexerError& ex) {
+        isc_throw(InvalidRdataLength,
+                  "Unknown RDATA length is invalid");
     }
     }
-    if (rdlen < 0 || rdlen > 0xffff) {
-        isc_throw(InvalidRdataLength, "RDATA length is out of range");
+
+    if (rdlen > 65535) {
+        isc_throw(InvalidRdataLength,
+                  "Unknown RDATA length is out of range: " << rdlen);
     }
     }
-    iss >> ws;                  // skip any white spaces
 
 
-    // Hexadecimal encoding of RDATA: each segment must consist of an even
-    // number of hex digits.
     vector<uint8_t> data;
     vector<uint8_t> data;
-    while (!iss.eof() && data.size() < rdlen) {
-        // extract two characters, which should compose a single byte of data.
-        char buf[2];
-        iss.read(buf, sizeof(buf));
-        if ((iss.rdstate() & (ios::badbit | ios::failbit)) != 0) {
-            isc_throw(InvalidRdataText,
-                      "Invalid hex encoding of generic RDATA");
+
+    if (rdlen > 0) {
+        string hex_txt;
+        string hex_part;
+        // Whitespace is allowed within hex data, 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)) {
+                // Unget the last read token as createRdata() expects us
+                // to leave it at the end-of-line or end-of-file when we
+                // return.
+                lexer.ungetToken();
+                break;
+            }
+            token.getString(hex_part);
+            hex_txt.append(hex_part);
         }
         }
 
 
-        // convert it to a single byte integer as a hex digit.
-        istringstream iss_byte(string(buf, sizeof(buf)));
-        unsigned int ch;
-        iss_byte >> hex >> ch;
-        if (iss_byte.rdstate() != ios::eofbit) {
+        try {
+            isc::util::encode::decodeHex(hex_txt, data);
+        } catch (const isc::BadValue& ex) {
             isc_throw(InvalidRdataText,
             isc_throw(InvalidRdataText,
-                      "Invalid hex encoding of generic RDATA");
+                      "Invalid hex encoding of generic RDATA: " << ex.what());
         }
         }
-        data.push_back(ch);
-        iss >> ws;              // skip spaces
-    }
-
-    if (!iss.eof()) {
-        isc_throw(InvalidRdataLength,
-                  "RDLENGTH is too small for generic RDATA");
     }
     }
 
 
     if (data.size() != rdlen) {
     if (data.size() != rdlen) {
         isc_throw(InvalidRdataLength,
         isc_throw(InvalidRdataLength,
-                  "Generic RDATA code doesn't match RDLENGTH");
+                  "Size of unknown RDATA hex data doesn't match RDLENGTH: "
+                  << data.size() << " vs. " << rdlen);
     }
     }
 
 
-    impl_ = new GenericImpl(data);
+    return (new GenericImpl(data));
 }
 }
 
 
-Generic::Generic(const std::string& rdata_string) {
-    constructHelper(rdata_string);
-}
-
-Generic::Generic(MasterLexer& lexer, const Name*,
-                 MasterLoader::Options,
-                 MasterLoaderCallbacks&)
+Generic::Generic(const std::string& rdata_string) :
+    impl_(NULL)
 {
 {
-    std::string s;
+    // 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 GenericImpl that constructFromLexer() returns.
+    std::auto_ptr<GenericImpl> impl_ptr(NULL);
 
 
-    while (true) {
-        const MasterToken& token = lexer.getNextToken();
-        if ((token.getType() == MasterToken::END_OF_FILE) ||
-            (token.getType() == MasterToken::END_OF_LINE)) {
-            lexer.ungetToken(); // let the upper layer handle the end-of token
-            break;
-        }
+    try {
+        std::istringstream ss(rdata_string);
+        MasterLexer lexer;
+        lexer.pushSource(ss);
 
 
-        if (!s.empty()) {
-            s += " ";
-        }
+        impl_ptr.reset(constructFromLexer(lexer));
 
 
-        s += token.getString();
+        if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
+            isc_throw(InvalidRdataText, "extra input text for unknown RDATA: "
+                      << rdata_string);
+        }
+    } catch (const MasterLexer::LexerError& ex) {
+        isc_throw(InvalidRdataText, "Failed to construct unknown RDATA "
+                  "from '" << rdata_string << "': " << ex.what());
     }
     }
 
 
-    constructHelper(s);
+    impl_ = impl_ptr.release();
+}
+
+Generic::Generic(MasterLexer& lexer, const Name*,
+                 MasterLoader::Options,
+                 MasterLoaderCallbacks&) :
+    impl_(constructFromLexer(lexer))
+{
 }
 }
 
 
 Generic::~Generic() {
 Generic::~Generic() {

+ 1 - 1
src/lib/dns/rdata.h

@@ -393,7 +393,7 @@ public:
     //@}
     //@}
 
 
 private:
 private:
-    void constructHelper(const std::string& rdata_string);
+    GenericImpl* constructFromLexer(MasterLexer& lexer);
 
 
     GenericImpl* impl_;
     GenericImpl* impl_;
 };
 };

+ 3 - 3
src/lib/dns/tests/rdata_unittest.cc

@@ -301,14 +301,14 @@ TEST_F(Rdata_Unknown_Test, createFromText) {
     // the length should be 16-bit unsigned integer
     // the length should be 16-bit unsigned integer
     EXPECT_THROW(generic::Generic("\\# 65536 a1b2c30d"), InvalidRdataLength);
     EXPECT_THROW(generic::Generic("\\# 65536 a1b2c30d"), InvalidRdataLength);
     EXPECT_THROW(generic::Generic("\\# -1 a1b2c30d"), InvalidRdataLength);
     EXPECT_THROW(generic::Generic("\\# -1 a1b2c30d"), InvalidRdataLength);
-    EXPECT_THROW(generic::Generic("\\# 1.1 a1"), InvalidRdataText);
+    EXPECT_THROW(generic::Generic("\\# 1.1 a1"), InvalidRdataLength);
     EXPECT_THROW(generic::Generic("\\# 0a 00010203040506070809"),
     EXPECT_THROW(generic::Generic("\\# 0a 00010203040506070809"),
-                 InvalidRdataText);
+                 InvalidRdataLength);
     // should reject if the special token is missing.
     // should reject if the special token is missing.
     EXPECT_THROW(generic::Generic("4 a1b2c30d"), InvalidRdataText);
     EXPECT_THROW(generic::Generic("4 a1b2c30d"), InvalidRdataText);
     // the special token, the RDLENGTH and the data must be space separated.
     // the special token, the RDLENGTH and the data must be space separated.
     EXPECT_THROW(generic::Generic("\\#0"), InvalidRdataText);
     EXPECT_THROW(generic::Generic("\\#0"), InvalidRdataText);
-    EXPECT_THROW(generic::Generic("\\# 1ff"), InvalidRdataText);
+    EXPECT_THROW(generic::Generic("\\# 1ff"), InvalidRdataLength);
 }
 }
 
 
 TEST_F(Rdata_Unknown_Test, createFromWire) {
 TEST_F(Rdata_Unknown_Test, createFromWire) {