Parcourir la 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 il y a 12 ans
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.