Browse Source

[2426] Support generic version of rdata::createRdata(text) in generic RDATA

Also adjust tests correcting the expected behaviour.
Mukund Sivaraman 11 years ago
parent
commit
4976235a26
3 changed files with 59 additions and 64 deletions
  1. 55 60
      src/lib/dns/rdata.cc
  2. 1 1
      src/lib/dns/rdata.h
  3. 3 3
      src/lib/dns/tests/rdata_unittest.cc

+ 55 - 60
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>
@@ -212,93 +213,87 @@ Generic::Generic(isc::util::InputBuffer& buffer, size_t rdata_len) {
 }
 }
 
 
 void
 void
-Generic::constructHelper(const std::string& rdata_string) {
-    istringstream iss(rdata_string);
-    string unknown_mark;
-    iss >> unknown_mark;
-    if (unknown_mark != "\\#") {
+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");
+    uint32_t rdlen;
+
+    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)) {
+                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) {
+        lexer.ungetToken();
+
+        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");
+                  "Unknown RDATA hex data doesn't match RDLENGTH: "
+                  << data.size() << " vs. " << rdlen);
     }
     }
 
 
     impl_ = new GenericImpl(data);
     impl_ = new GenericImpl(data);
 }
 }
 
 
 Generic::Generic(const std::string& rdata_string) {
 Generic::Generic(const std::string& rdata_string) {
-    constructHelper(rdata_string);
+    try {
+        std::istringstream ss(rdata_string);
+        MasterLexer lexer;
+        lexer.pushSource(ss);
+
+        constructFromLexer(lexer);
+
+        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());
+    }
 }
 }
 
 
 Generic::Generic(MasterLexer& lexer, const Name*,
 Generic::Generic(MasterLexer& lexer, const Name*,
                  MasterLoader::Options,
                  MasterLoader::Options,
                  MasterLoaderCallbacks&)
                  MasterLoaderCallbacks&)
 {
 {
-    std::string s;
-
-    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;
-        }
-
-        if (!s.empty()) {
-            s += " ";
-        }
-
-        s += token.getString();
-    }
-
-    constructHelper(s);
+    constructFromLexer(lexer);
 }
 }
 
 
 Generic::~Generic() {
 Generic::~Generic() {

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

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

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

@@ -290,14 +290,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) {