Browse Source

[2389] updater lexer support in AAAA constructor.

it was already implemented, but more test cases and doc were now added.
some other cleanups are made.
JINMEI Tatuya 12 years ago
parent
commit
b791d34805

+ 63 - 10
src/lib/dns/rdata/in_1/aaaa_28.cc

@@ -24,6 +24,8 @@
 #include <stdint.h>
 #include <string.h>
 
+#include <cerrno>
+#include <cstring>
 #include <string>
 
 #include <arpa/inet.h> // XXX: for inet_pton/ntop(), not exist in C++ standards
@@ -35,24 +37,70 @@ using namespace isc::util;
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
 
-AAAA::AAAA(const std::string& addrstr) {
-    if (inet_pton(AF_INET6, addrstr.c_str(), &addr_) != 1) {
-        isc_throw(InvalidRdataText,
-                  "IN/AAAA RDATA construction from text failed: "
-                  "Address cannot be converted: " << addrstr);
+namespace {
+void
+convertToIPv6Addr(const char* src, size_t src_len, void* dst) {
+    if (src_len != strlen(src)) {
+        isc_throw(InvalidRdataText, "Bad IN/AAAA RDATA text: "
+                  "extra character: '" << src << "'");
     }
+    const int result = inet_pton(AF_INET6, src, dst);
+    if (result == 0) {
+        isc_throw(InvalidRdataText, "Bad IN/AAAA RDATA text: '" << src << "'");
+    } else if (result < 0) {
+        isc_throw(isc::Unexpected,
+                  "Unexpected failure in parsing IN/AAAA RDATA text: '"
+                  << src << "': " << std::strerror(errno));
+    }
+}
 }
 
+/// \brief Constructor from string.
+///
+/// The given string must be a valid textual representation of an IPv6
+/// address as specified in RFC1886.
+///
+/// No extra character should be contained in \c addrstr other than the
+/// textual address.  These include spaces and the nul character.
+///
+/// \throw InvalidRdata The text extracted by the lexer isn't recognized as
+/// a valid IPv6 address.
+/// \throw Unexpected Unexpected system error in conversion (this should be
+/// very rare).
+///
+/// \param addrstr Textual representation of IPv6 address to be used as the
+/// RDATA.
+AAAA::AAAA(const std::string& addrstr) {
+    convertToIPv6Addr(addrstr.c_str(), addrstr.size(), addr_);
+}
+
+/// \brief Constructor with a context of MasterLexer.
+///
+/// The \c lexer should point to the beginning of valid textual representation
+/// of a class IN AAAA RDATA.
+///
+/// The acceptable form of the textual address is generally the same as the
+/// string version of the constructor, but this version is slightly more
+/// flexible.  See the similar constructor of \c in::A class; the same
+/// notes apply here.
+///
+/// \throw MasterLexer::LexerError General parsing error such as missing field.
+/// \throw InvalidRdata The text extracted by the lexer isn't recognized as
+/// a valid IPv6 address.
+/// \throw Unexpected Unexpected system error in conversion (this should be
+/// very rare).
+///
+/// \param lexer A \c MasterLexer object parsing a master file for the
+/// RDATA to be created
 AAAA::AAAA(MasterLexer& lexer, const Name*,
            MasterLoader::Options, MasterLoaderCallbacks&)
 {
     const MasterToken& token = lexer.getNextToken(MasterToken::STRING);
-    if (inet_pton(AF_INET6, token.getStringRegion().beg, &addr_) != 1) {
-        isc_throw(InvalidRdataText, "Failed to convert '"
-                  << token.getString() << "' to IN/AAAA RDATA");
-    }
+    convertToIPv6Addr(token.getStringRegion().beg, token.getStringRegion().len,
+                      addr_);
 }
 
+/// \brief Copy constructor.
 AAAA::AAAA(InputBuffer& buffer, size_t rdata_len) {
     if (rdata_len != sizeof(addr_)) {
         isc_throw(DNSMessageFORMERR,
@@ -72,6 +120,7 @@ AAAA::AAAA(const AAAA& other) : Rdata() {
     memcpy(addr_, other.addr_, sizeof(addr_));
 }
 
+/// \brief Return a textual form of the underlying IPv6 address of the RDATA.
 void
 AAAA::toWire(OutputBuffer& buffer) const {
     buffer.writeData(&addr_, sizeof(addr_));
@@ -86,7 +135,8 @@ string
 AAAA::toText() const {
     char addr_string[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
 
-    if (inet_ntop(AF_INET6, &addr_, addr_string, sizeof(addr_string)) == NULL) {
+    if (inet_ntop(AF_INET6, &addr_, addr_string, sizeof(addr_string))
+        == NULL) {
         isc_throw(Unexpected,
                   "Failed to convert IN/AAAA RDATA to textual IPv6 address");
     }
@@ -94,6 +144,9 @@ AAAA::toText() const {
     return (string(addr_string));
 }
 
+/// \brief Compare two in::AAAA RDATAs.
+///
+/// In effect, it compares the two RDATA as an unsigned 128-bit integer.
 int
 AAAA::compare(const Rdata& other) const {
     const AAAA& other_a = dynamic_cast<const AAAA&>(other);

+ 62 - 5
src/lib/dns/tests/rdata_in_aaaa_unittest.cc

@@ -33,18 +33,75 @@ using namespace isc::dns::rdata;
 
 namespace {
 class Rdata_IN_AAAA_Test : public RdataTest {
-    // there's nothing to specialize
+protected:
+    Rdata_IN_AAAA_Test() : rdata_in_aaaa("2001:db8::1234") {}
+
+    // Common check to see the result of in::A Rdata construction either from
+    // std::string or with MasterLexer object.  If it's expected to succeed
+    // the result should be identical to the commonly used test data
+    // (rdata_in_a); otherwise it should result in the exception specified as
+    // the template parameter.
+    template <typename ExForString, typename ExForLexer>
+    void checkFromText(const string& in_aaaa_txt,
+                       bool throw_str_version = true,
+                       bool throw_lexer_version = true)
+    {
+        if (throw_str_version) {
+            EXPECT_THROW(in::AAAA in_aaaa(in_aaaa_txt), ExForString);
+        } else {
+            EXPECT_EQ(0, in::AAAA(in_aaaa_txt).compare(rdata_in_aaaa));
+        }
+
+        std::stringstream ss(in_aaaa_txt);
+        MasterLexer lexer;
+        lexer.pushSource(ss);
+        if (throw_lexer_version) {
+            EXPECT_THROW(in::AAAA soa(lexer, NULL, MasterLoader::DEFAULT,
+                                   loader_cb), ExForLexer);
+        } else {
+                EXPECT_EQ(0, in::AAAA(lexer, NULL, MasterLoader::DEFAULT,
+                           loader_cb).compare(rdata_in_aaaa));
+        }
+    }
+
+    const in::AAAA rdata_in_aaaa;
 };
 
-const in::AAAA rdata_in_aaaa("2001:db8::1234");
 const uint8_t wiredata_in_aaaa[] = {
     0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
     0x00, 0x00, 0x12, 0x34 };
 
 TEST_F(Rdata_IN_AAAA_Test, createFromText) {
-    rdata_in_aaaa.compare(in::AAAA(string("2001:db8::1234")));
-    EXPECT_THROW(in::AAAA("192.0.2.1"), InvalidRdataText);
-    EXPECT_THROW(in::AAAA("xxx"), InvalidRdataText);
+    // Normal case: no exception for either case, so the exception type
+    // doesn't matter.
+    checkFromText<isc::Exception, isc::Exception>("2001:db8::1234", false,
+                                                  false);
+
+    // should reject an IP4 address.
+    checkFromText<InvalidRdataText, InvalidRdataText>("192.0.2.1");
+    // or any meaningless text as an IPv6 address
+    checkFromText<InvalidRdataText, InvalidRdataText>("xxx");
+
+    // trailing white space: only string version throws
+    checkFromText<InvalidRdataText, InvalidRdataText>("2001:db8::1234  ",
+                                                      true, false);
+    // same for beginning white space.
+    checkFromText<InvalidRdataText, InvalidRdataText>("  2001:db8::1234",
+                                                      true, false);
+    // same for trailing non-space garbage (note that lexer version still
+    // ignore it; it's expected to be detected at a higher layer).
+    checkFromText<InvalidRdataText, InvalidRdataText>("2001:db8::1234 xxx",
+                                                      true, false);
+
+    // nul character after a valid textual representation.
+    string nul_after_addr = "2001:db8::1234";
+    nul_after_addr.push_back(0);
+    checkFromText<InvalidRdataText, InvalidRdataText>(nul_after_addr, true,
+                                                      true);
+
+    // a valid address surrounded by parentheses; only okay with lexer
+    checkFromText<InvalidRdataText, InvalidRdataText>("(2001:db8::1234)", true,
+                                                      false);
 }
 
 TEST_F(Rdata_IN_AAAA_Test, createFromWire) {

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

@@ -197,8 +197,8 @@ TEST_F(RdataTest, createRdataWithLexer) {
     EXPECT_FALSE(createRdata(RRType::AAAA(), RRClass::IN(), lexer, NULL,
                              MasterLoader::MANY_ERRORS, callbacks));
     callback.check(src_name, line, CreateRdataCallback::ERROR,
-                   "createRdata from text failed: Failed to convert "
-                   "'192.0.2.1' to IN/AAAA RDATA");
+                   "createRdata from text failed: Bad IN/AAAA RDATA text: "
+                   "'192.0.2.1'");
 
     // Input is valid and parse will succeed, but with a warning that the
     // file is not ended with a newline.