Browse Source

Merge branch 'trac2522'

Paul Selkirk 12 years ago
parent
commit
ea97070cf6

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

@@ -24,39 +24,6 @@ from os.path import getmtime
 import re
 import sys
 
-# new_rdata_factory_users[] is a list of tuples of the form (rrtype,
-# rrclass). Items in the list use the (new) RdataFactory class, and
-# items which are not in the list use OldRdataFactory class.
-# Note: rrtype and rrclass must be specified in lowercase in
-# new_rdata_factory_users.
-#
-# Example:
-#     new_rdata_factory_users = [('a', 'in'), ('a', 'ch'), ('soa', 'generic')]
-new_rdata_factory_users = [('a', 'in'),
-                           ('aaaa', 'in'),
-                           ('afsdb', 'generic'),
-                           ('cname', 'generic'),
-                           ('dhcid', 'in'),
-                           ('dlv', 'generic'),
-                           ('dname', 'generic'),
-                           ('dnskey', 'generic'),
-                           ('ds', 'generic'),
-                           ('hinfo', 'generic'),
-                           ('naptr', 'generic'),
-                           ('mx', 'generic'),
-                           ('ns', 'generic'),
-                           ('nsec', 'generic'),
-                           ('nsec3', 'generic'),
-                           ('nsec3param', 'generic'),
-                           ('opt', 'generic'),
-                           ('ptr', 'generic'),
-                           ('rrsig', 'generic'),
-                           ('soa', 'generic'),
-                           ('spf', 'generic'),
-                           ('srv', 'in'),
-                           ('txt', 'generic')
-                          ]
-
 re_typecode = re.compile('([\da-z\-]+)_(\d+)')
 classcode2txt = {}
 typecode2txt = {}
@@ -66,7 +33,7 @@ meta_types = {
     # Real meta types.  We won't have Rdata implement for them, but we need
     # RRType constants.
     '251': 'ixfr', '252': 'axfr', '255': 'any',
-    # Obsolete types.  We probalby won't implement Rdata for them, but it's
+    # Obsolete types.  We probably won't implement Rdata for them, but it's
     # better to have RRType constants.
     '3': 'md', '4': 'mf', '7': 'mb', '8': 'mg', '9': 'mr', '30': 'nxt',
     '38': 'a6', '254': 'maila',
@@ -375,28 +342,15 @@ def generate_rrparam(fileprefix, basemtime):
         indent = ' ' * 8
         typeandclassparams += indent
 
-        # By default, we use OldRdataFactory (see bug #2497). If you
-        # want to pick RdataFactory for a particular type, add it to
-        # new_rdata_factory_users.  Note that we explicitly generate (for
-        # optimization) class-independent ("generic") factories for class IN
-        # for optimization.
-        if (((type_txt.lower(), class_txt.lower()) in
-             new_rdata_factory_users) or
-            ((class_txt.lower() == 'in') and
-             ((type_txt.lower(), 'generic') in new_rdata_factory_users))):
-            rdf_class = 'RdataFactory'
-        else:
-            rdf_class = 'OldRdataFactory'
-
         if class_tuple[1] != 'generic':
             typeandclassparams += 'add("' + type_utxt + '", '
             typeandclassparams += str(type_code) + ', "' + class_utxt
             typeandclassparams += '", ' + str(class_code)
-            typeandclassparams += ', RdataFactoryPtr(new ' + rdf_class + '<'
+            typeandclassparams += ', RdataFactoryPtr(new ' + 'RdataFactory' + '<'
             typeandclassparams += class_txt + '::' + type_utxt + '>()));\n'
         else:
             typeandclassparams += 'add("' + type_utxt + '", ' + str(type_code)
-            typeandclassparams += ', RdataFactoryPtr(new ' + rdf_class + '<'
+            typeandclassparams += ', RdataFactoryPtr(new ' + 'RdataFactory' + '<'
             typeandclassparams += class_txt + '::' + type_utxt + '>()));\n'
 
     typeandclassparams += indent + '// Meta and non-implemented RR types\n'

+ 167 - 78
src/lib/dns/rdata/any_255/tsig_250.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -19,26 +19,28 @@
 #include <boost/lexical_cast.hpp>
 
 #include <util/buffer.h>
-#include <util/strutil.h>
 #include <util/encode/base64.h>
 
 #include <dns/messagerenderer.h>
 #include <dns/name.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
+#include <dns/rcode.h>
 #include <dns/tsigerror.h>
+#include <dns/rdata/generic/detail/lexer_util.h>
 
 using namespace std;
 using boost::lexical_cast;
 using namespace isc::util;
 using namespace isc::util::encode;
-using namespace isc::util::str;
+using namespace isc::dns;
+using isc::dns::rdata::generic::detail::createNameFromLexer;
 
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
 
-/// This is a straightforward representation of TSIG RDATA fields.
-struct TSIG::TSIGImpl {
+// straightforward representation of TSIG RDATA fields
+struct TSIGImpl {
     TSIGImpl(const Name& algorithm, uint64_t time_signed, uint16_t fudge,
              vector<uint8_t>& mac, uint16_t original_id, uint16_t error,
              vector<uint8_t>& other_data) :
@@ -68,99 +70,184 @@ struct TSIG::TSIGImpl {
     const vector<uint8_t> other_data_;
 };
 
+// helper function for string and lexer constructors
+TSIGImpl*
+TSIG::constructFromLexer(MasterLexer& lexer, const Name* origin) {
+    const Name& algorithm =
+        createNameFromLexer(lexer, origin ? origin : &Name::ROOT_NAME());
+
+    const string& time_txt =
+        lexer.getNextToken(MasterToken::STRING).getString();
+    uint64_t time_signed;
+    try {
+        time_signed = boost::lexical_cast<uint64_t>(time_txt);
+    } catch (const boost::bad_lexical_cast&) {
+        isc_throw(InvalidRdataText, "Invalid TSIG Time");
+    }
+    if ((time_signed >> 48) != 0) {
+        isc_throw(InvalidRdataText, "TSIG Time out of range");
+    }
+
+    const uint32_t fudge = lexer.getNextToken(MasterToken::NUMBER).getNumber();
+    if (fudge > 0xffff) {
+        isc_throw(InvalidRdataText, "TSIG Fudge out of range");
+    }
+    const uint32_t macsize =
+        lexer.getNextToken(MasterToken::NUMBER).getNumber();
+    if (macsize > 0xffff) {
+        isc_throw(InvalidRdataText, "TSIG MAC Size out of range");
+    }
+
+    const string& mac_txt = (macsize > 0) ?
+            lexer.getNextToken(MasterToken::STRING).getString() : "";
+    vector<uint8_t> mac;
+    decodeBase64(mac_txt, mac);
+    if (mac.size() != macsize) {
+        isc_throw(InvalidRdataText, "TSIG MAC Size and data are inconsistent");
+    }
+
+    const uint32_t orig_id =
+        lexer.getNextToken(MasterToken::NUMBER).getNumber();
+    if (orig_id > 0xffff) {
+        isc_throw(InvalidRdataText, "TSIG Original ID out of range");
+    }
+
+    const string& error_txt =
+        lexer.getNextToken(MasterToken::STRING).getString();
+    uint32_t error = 0;
+    // XXX: In the initial implementation we hardcode the mnemonics.
+    // We'll soon generalize this.
+    if (error_txt == "NOERROR") {
+        error = Rcode::NOERROR_CODE;
+    } else if (error_txt == "BADSIG") {
+        error = TSIGError::BAD_SIG_CODE;
+    } else if (error_txt == "BADKEY") {
+        error = TSIGError::BAD_KEY_CODE;
+    } else if (error_txt == "BADTIME") {
+        error = TSIGError::BAD_TIME_CODE;
+    } else {
+	/// we cast to uint32_t and range-check, because casting directly to
+	/// uint16_t will convert negative numbers to large positive numbers
+        try {
+            error = boost::lexical_cast<uint32_t>(error_txt);
+        } catch (const boost::bad_lexical_cast&) {
+            isc_throw(InvalidRdataText, "Invalid TSIG Error");
+        }
+        if (error > 0xffff) {
+            isc_throw(InvalidRdataText, "TSIG Error out of range");
+        }
+    }
+
+    const uint32_t otherlen =
+        lexer.getNextToken(MasterToken::NUMBER).getNumber();
+    if (otherlen > 0xffff) {
+        isc_throw(InvalidRdataText, "TSIG Other Len out of range");
+    }
+    const string otherdata_txt = (otherlen > 0) ?
+            lexer.getNextToken(MasterToken::STRING).getString() : "";
+    vector<uint8_t> other_data;
+    decodeBase64(otherdata_txt, other_data);
+    if (other_data.size() != otherlen) {
+        isc_throw(InvalidRdataText,
+                  "TSIG Other Data length does not match Other Len");
+    }
+    // RFC2845 says Other Data is "empty unless Error == BADTIME".
+    // However, we don't enforce that.
+
+    return (new TSIGImpl(algorithm, time_signed, fudge, mac, orig_id,
+                         error, other_data));
+}
+
 /// \brief Constructor from string.
 ///
+/// The given string must represent a valid TSIG 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.
+///
 /// \c tsig_str must be formatted as follows:
-/// \code <Alg> <Time> <Fudge> <MACsize> [<MAC>] <OrigID> <Error> <OtherLen> [<OtherData>]
+/// \code <Algorithm Name> <Time Signed> <Fudge> <MAC Size> [<MAC>]
+/// <Original ID> <Error> <Other Len> [<Other Data>]
 /// \endcode
-/// where
-/// - &lt;Alg&gt; is a valid textual representation of domain name.
-/// - &lt;Time&gt; is an unsigned 48-bit decimal integer.
-/// - &lt;MACSize&gt;, &lt;OrigID&gt;, and &lt;OtherLen&gt; are an unsigned
-///   16-bit decimal
-///   integer.
-/// - &lt;Error&gt; is an unsigned 16-bit decimal integer or a valid mnemonic
-///   for the Error field specified in RFC2845.  Currently, "BADSIG", "BADKEY",
-///   and "BADTIME" are supported (case sensitive).  In future versions
-///   other representations that are compatible with the DNS RCODE will be
-///   supported.
-/// - &lt;MAC&gt; and &lt;OtherData&gt; is a BASE-64 encoded string that does
-///   not contain space characters.
-///   When &lt;MACSize&gt; and &lt;OtherLen&gt; is 0, &lt;MAC&gt; and
-///   &lt;OtherData&gt; must not appear in \c tsig_str, respectively.
-/// - The decoded data of &lt;MAC&gt; is &lt;MACSize&gt; bytes of binary
-///   stream.
-/// - The decoded data of &lt;OtherData&gt; is &lt;OtherLen&gt; bytes of
-///   binary stream.
+///
+/// Note that, since the Algorithm Name field is defined to be "in domain name
+/// syntax", but it is not actually a domain name, it does not have to be
+/// fully qualified.
+///
+/// The Error field is an unsigned 16-bit decimal integer or a valid mnemonic
+/// as specified in RFC2845.  Currently, "NOERROR", "BADSIG", "BADKEY", and
+/// "BADTIME" are supported (case sensitive).  In future versions other
+/// representations that are compatible with the DNS RCODE may be supported.
+///
+/// The MAC and Other Data fields are base-64 encoded strings that do not
+/// contain space characters.
+/// If the MAC Size field is 0, the MAC field must not appear in \c tsig_str.
+/// If the Other Len field is 0, the Other Data field must not appear in
+/// \c tsig_str.
+/// The decoded data of the MAC field is MAC Size bytes of binary stream.
+/// The decoded data of the Other Data field is Other Len bytes of binary
+/// stream.
 ///
 /// An example of valid string is:
 /// \code "hmac-sha256. 853804800 300 3 AAAA 2845 0 0" \endcode
-/// In this example &lt;OtherData&gt; is missing because &lt;OtherLen&gt; is 0.
+/// In this example Other Data is missing because Other Len is 0.
 ///
 /// Note that RFC2845 does not define the standard presentation format
 /// of %TSIG RR, so the above syntax is implementation specific.
 /// This is, however, compatible with the format acceptable to BIND 9's
 /// RDATA parser.
 ///
-/// <b>Exceptions</b>
+/// \throw Others Exception from the Name constructors.
+/// \throw InvalidRdataText if any fields are out of their valid range,
+/// or are incorrect.
+/// \throw BadValue if MAC or Other Data is not validly encoded in base-64.
 ///
-/// If &lt;Alg&gt; is not a valid domain name, a corresponding exception from
-/// the \c Name class will be thrown;
-/// if &lt;MAC&gt; or &lt;OtherData&gt; is not validly encoded in BASE-64, an
-/// exception of class \c isc::BadValue will be thrown;
-/// if %any of the other bullet points above is not met, an exception of
-/// class \c InvalidRdataText will be thrown.
-/// This constructor internally involves resource allocation, and if it fails
-/// a corresponding standard exception will be thrown.
+/// \param tsig_str A string containing the RDATA to be created
 TSIG::TSIG(const std::string& tsig_str) : impl_(NULL) {
-    istringstream iss(tsig_str);
+    // 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 TSIGImpl that constructFromLexer() returns.
+    std::auto_ptr<TSIGImpl> impl_ptr(NULL);
 
     try {
-        const Name algorithm(getToken(iss));
-        const int64_t time_signed = tokenToNum<int64_t, 48>(getToken(iss));
-        const int32_t fudge = tokenToNum<int32_t, 16>(getToken(iss));
-        const int32_t macsize = tokenToNum<int32_t, 16>(getToken(iss));
-
-        const string mac_txt = (macsize > 0) ? getToken(iss) : "";
-        vector<uint8_t> mac;
-        decodeBase64(mac_txt, mac);
-        if (mac.size() != macsize) {
-            isc_throw(InvalidRdataText, "TSIG MAC size and data are inconsistent");
-        }
-
-        const int32_t orig_id = tokenToNum<int32_t, 16>(getToken(iss));
-
-        const string error_txt = getToken(iss);
-        int32_t error = 0;
-        // XXX: In the initial implementation we hardcode the mnemonics.
-        // We'll soon generalize this.
-        if (error_txt == "BADSIG") {
-            error = 16;
-        } else if (error_txt == "BADKEY") {
-            error = 17;
-        } else if (error_txt == "BADTIME") {
-            error = 18;
-        } else {
-            error = tokenToNum<int32_t, 16>(error_txt);
-        }
+        std::istringstream ss(tsig_str);
+        MasterLexer lexer;
+        lexer.pushSource(ss);
 
-        const int32_t otherlen = tokenToNum<int32_t, 16>(getToken(iss));
-        const string otherdata_txt = (otherlen > 0) ? getToken(iss) : "";
-        vector<uint8_t> other_data;
-        decodeBase64(otherdata_txt, other_data);
+        impl_ptr.reset(constructFromLexer(lexer, NULL));
 
-        if (!iss.eof()) {
-            isc_throw(InvalidRdataText, "Unexpected input for TSIG RDATA: " <<
-                    tsig_str);
+        if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
+            isc_throw(InvalidRdataText,
+                      "Extra input text for TSIG: " << tsig_str);
         }
+    } catch (const MasterLexer::LexerError& ex) {
+        isc_throw(InvalidRdataText,
+                  "Failed to construct TSIG from '" << tsig_str << "': "
+                  << ex.what());
+    }
 
-        impl_ = new TSIGImpl(algorithm, time_signed, fudge, mac, orig_id,
-                            error, other_data);
+    impl_ = impl_ptr.release();
+}
 
-    } catch (const StringTokenError& ste) {
-        isc_throw(InvalidRdataText, "Invalid TSIG text: " << ste.what() <<
-                  ": " << tsig_str);
-    }
+/// \brief Constructor with a context of MasterLexer.
+///
+/// The \c lexer should point to the beginning of valid textual
+/// representation of an TSIG RDATA.
+///
+/// See \c TSIG::TSIG(const std::string&) for description of the
+/// expected RDATA fields.
+///
+/// \throw MasterLexer::LexerError General parsing error such as
+/// missing field.
+/// \throw InvalidRdataText if any fields are out of their valid range,
+/// or are incorrect.
+///
+/// \param lexer A \c MasterLexer object parsing a master file for the
+/// RDATA to be created
+TSIG::TSIG(MasterLexer& lexer, const Name* origin,
+           MasterLoader::Options, MasterLoaderCallbacks&) :
+    impl_(constructFromLexer(lexer, origin))
+{
 }
 
 /// \brief Constructor from wire-format data.
@@ -183,7 +270,9 @@ TSIG::TSIG(const std::string& tsig_str) : impl_(NULL) {
 /// But this constructor does not use this parameter; if necessary, the caller
 /// must check consistency between the length parameter and the actual
 /// RDATA length.
-TSIG::TSIG(InputBuffer& buffer, size_t) : impl_(NULL) {
+TSIG::TSIG(InputBuffer& buffer, size_t) :
+    impl_(NULL)
+{
     Name algorithm(buffer);
 
     uint8_t time_signed_buf[6];
@@ -298,7 +387,7 @@ TSIG::toText() const {
 // toWire().
 template <typename Output>
 void
-TSIG::TSIGImpl::toWireCommon(Output& output) const {
+TSIGImpl::toWireCommon(Output& output) const {
     output.writeUint16(time_signed_ >> 32);
     output.writeUint32(time_signed_ & 0xffffffff);
     output.writeUint16(fudge_);

+ 6 - 8
src/lib/dns/rdata/any_255/tsig_250.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -18,14 +18,9 @@
 
 #include <string>
 
+#include <dns/name.h>
 #include <dns/rdata.h>
 
-namespace isc {
-namespace dns {
-class Name;
-}
-}
-
 // BEGIN_ISC_NAMESPACE
 
 // BEGIN_COMMON_DECLARATIONS
@@ -33,6 +28,8 @@ class Name;
 
 // BEGIN_RDATA_NAMESPACE
 
+struct TSIGImpl;
+
 /// \brief \c rdata::TSIG class represents the TSIG RDATA as defined %in
 /// RFC2845.
 ///
@@ -145,7 +142,8 @@ public:
     /// This method never throws an exception.
     const void* getOtherData() const;
 private:
-    struct TSIGImpl;
+    TSIGImpl* constructFromLexer(MasterLexer& lexer, const Name* origin);
+
     TSIGImpl* impl_;
 };
 

+ 7 - 1
src/lib/dns/rdata/ch_3/a_1.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -31,6 +31,12 @@ A::A(const std::string&) {
     // TBD
 }
 
+A::A(MasterLexer&, const Name*,
+     MasterLoader::Options, MasterLoaderCallbacks&)
+{
+    // TBD
+}
+
 A::A(InputBuffer&, size_t) {
     // TBD
 }

+ 48 - 25
src/lib/dns/rdata/generic/minfo_14.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -21,10 +21,12 @@
 #include <dns/name.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
+#include <dns/rdata/generic/detail/lexer_util.h>
 
 using namespace std;
 using namespace isc::dns;
 using namespace isc::util;
+using isc::dns::rdata::generic::detail::createNameFromLexer;
 
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
@@ -39,12 +41,10 @@ using namespace isc::util;
 /// An example of valid string is:
 /// \code "rmail.example.com. email.example.com." \endcode
 ///
-/// <b>Exceptions</b>
-///
-/// \exception InvalidRdataText The number of RDATA fields (must be 2) is
+/// \throw InvalidRdataText The number of RDATA fields (must be 2) is
 /// incorrect.
-/// \exception std::bad_alloc Memory allocation for names fails.
-/// \exception Other The constructor of the \c Name class will throw if the
+/// \throw std::bad_alloc Memory allocation for names fails.
+/// \throw Other The constructor of the \c Name class will throw if the
 /// names in the string is invalid.
 MINFO::MINFO(const std::string& minfo_str) :
     // We cannot construct both names in the initialization list due to the
@@ -52,21 +52,44 @@ MINFO::MINFO(const std::string& minfo_str) :
     // name and replace them later.
     rmailbox_(Name::ROOT_NAME()), emailbox_(Name::ROOT_NAME())
 {
-    istringstream iss(minfo_str);
-    string rmailbox_str, emailbox_str;
-    iss >> rmailbox_str >> emailbox_str;
-
-    // Validation: A valid MINFO RR must have exactly two fields.
-    if (iss.bad() || iss.fail()) {
-        isc_throw(InvalidRdataText, "Invalid MINFO text: " << minfo_str);
-    }
-    if (!iss.eof()) {
-        isc_throw(InvalidRdataText, "Invalid MINFO text (redundant field): "
-                  << minfo_str);
+    try {
+        std::istringstream ss(minfo_str);
+        MasterLexer lexer;
+        lexer.pushSource(ss);
+
+	rmailbox_ = createNameFromLexer(lexer, NULL);
+	emailbox_ = createNameFromLexer(lexer, NULL);
+
+        if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
+            isc_throw(InvalidRdataText, "extra input text for MINFO: "
+                      << minfo_str);
+        }
+    } catch (const MasterLexer::LexerError& ex) {
+        isc_throw(InvalidRdataText, "Failed to construct MINFO from '" <<
+                  minfo_str << "': " << ex.what());
     }
+}
 
-    rmailbox_ = Name(rmailbox_str);
-    emailbox_ = Name(emailbox_str);
+/// \brief Constructor with a context of MasterLexer.
+///
+/// The \c lexer should point to the beginning of valid textual representation
+/// of an MINFO RDATA.  The RMAILBOX and EMAILBOX fields can be non-absolute
+/// if \c origin is non-NULL, in which case \c origin is used to make them
+/// absolute.
+///
+/// \throw MasterLexer::LexerError General parsing error such as missing field.
+/// \throw Other Exceptions from the Name and constructors if construction of
+/// textual fields as these objects fail.
+///
+/// \param lexer A \c MasterLexer object parsing a master file for the
+/// RDATA to be created
+/// \param origin If non NULL, specifies the origin of SERVER when it
+/// is non-absolute.
+MINFO::MINFO(MasterLexer& lexer, const Name* origin,
+             MasterLoader::Options, MasterLoaderCallbacks&) :
+    rmailbox_(createNameFromLexer(lexer, origin)),
+    emailbox_(createNameFromLexer(lexer, origin))
+{
 }
 
 /// \brief Constructor from wire-format data.
@@ -75,8 +98,8 @@ MINFO::MINFO(const std::string& minfo_str) :
 /// length) for parsing.
 /// If necessary, the caller will check consistency.
 ///
-/// \exception std::bad_alloc Memory allocation for names fails.
-/// \exception Other The constructor of the \c Name class will throw if the
+/// \throw std::bad_alloc Memory allocation for names fails.
+/// \throw Other The constructor of the \c Name class will throw if the
 /// names in the wire is invalid.
 MINFO::MINFO(InputBuffer& buffer, size_t) :
     rmailbox_(buffer), emailbox_(buffer)
@@ -84,7 +107,7 @@ MINFO::MINFO(InputBuffer& buffer, size_t) :
 
 /// \brief Copy constructor.
 ///
-/// \exception std::bad_alloc Memory allocation fails in copying internal
+/// \throw std::bad_alloc Memory allocation fails in copying internal
 /// member variables (this should be very rare).
 MINFO::MINFO(const MINFO& other) :
     Rdata(), rmailbox_(other.rmailbox_), emailbox_(other.emailbox_)
@@ -95,7 +118,7 @@ MINFO::MINFO(const MINFO& other) :
 /// The output of this method is formatted as described in the "from string"
 /// constructor (\c MINFO(const std::string&))).
 ///
-/// \exception std::bad_alloc Internal resource allocation fails.
+/// \throw std::bad_alloc Internal resource allocation fails.
 ///
 /// \return A \c string object that represents the \c MINFO object.
 std::string
@@ -105,7 +128,7 @@ MINFO::toText() const {
 
 /// \brief Render the \c MINFO in the wire format without name compression.
 ///
-/// \exception std::bad_alloc Internal resource allocation fails.
+/// \throw std::bad_alloc Internal resource allocation fails.
 ///
 /// \param buffer An output buffer to store the wire data.
 void
@@ -128,7 +151,7 @@ MINFO::operator=(const MINFO& source) {
 /// As specified in RFC3597, TYPE MINFO is "well-known", the rmailbox and
 /// emailbox fields (domain names) will be compressed.
 ///
-/// \exception std::bad_alloc Internal resource allocation fails.
+/// \throw std::bad_alloc Internal resource allocation fails.
 ///
 /// \param renderer DNS message rendering context that encapsulates the
 /// output buffer and name compression information.

+ 3 - 3
src/lib/dns/rdata/generic/minfo_14.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -45,7 +45,7 @@ public:
 
     /// \brief Return the value of the rmailbox field.
     ///
-    /// \exception std::bad_alloc If resource allocation for the returned
+    /// \throw std::bad_alloc If resource allocation for the returned
     /// \c Name fails.
     ///
     /// \note
@@ -64,7 +64,7 @@ public:
 
     /// \brief Return the value of the emailbox field.
     ///
-    /// \exception std::bad_alloc If resource allocation for the returned
+    /// \throw std::bad_alloc If resource allocation for the returned
     /// \c Name fails.
     Name getEmailbox() const { return (emailbox_); }
 

+ 63 - 23
src/lib/dns/rdata/generic/rp_17.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -21,10 +21,12 @@
 #include <dns/name.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
+#include <dns/rdata/generic/detail/lexer_util.h>
 
 using namespace std;
 using namespace isc::dns;
 using namespace isc::util;
+using isc::dns::rdata::generic::detail::createNameFromLexer;
 
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
@@ -36,32 +38,54 @@ using namespace isc::util;
 /// \endcode
 /// where both fields must represent a valid domain name.
 ///
-/// \exception InvalidRdataText The number of RDATA fields (must be 2) is
+/// \throw InvalidRdataText The number of RDATA fields (must be 2) is
 /// incorrect.
-/// \exception Other The constructor of the \c Name class will throw if the
+/// \throw std::bad_alloc Memory allocation for names fails.
+/// \throw Other The constructor of the \c Name class will throw if the
 /// given name is invalid.
-/// \exception std::bad_alloc Memory allocation for names fails.
 RP::RP(const std::string& rp_str) :
     // We cannot construct both names in the initialization list due to the
     // necessary text processing, so we have to initialize them with a dummy
     // name and replace them later.
     mailbox_(Name::ROOT_NAME()), text_(Name::ROOT_NAME())
 {
-    istringstream iss(rp_str);
-    string mailbox_str, text_str;
-    iss >> mailbox_str >> text_str;
-
-    // Validation: A valid RP RR must have exactly two fields.
-    if (iss.bad() || iss.fail()) {
-        isc_throw(InvalidRdataText, "Invalid RP text: " << rp_str);
-    }
-    if (!iss.eof()) {
-        isc_throw(InvalidRdataText, "Invalid RP text (redundant field): "
-                  << rp_str);
+    try {
+        std::istringstream ss(rp_str);
+        MasterLexer lexer;
+        lexer.pushSource(ss);
+
+        mailbox_ = createNameFromLexer(lexer, NULL);
+        text_ = createNameFromLexer(lexer, NULL);
+
+        if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
+            isc_throw(InvalidRdataText, "extra input text for RP: "
+                      << rp_str);
+        }
+    } catch (const MasterLexer::LexerError& ex) {
+        isc_throw(InvalidRdataText, "Failed to construct RP from '" <<
+                  rp_str << "': " << ex.what());
     }
+}
 
-    mailbox_ = Name(mailbox_str);
-    text_ = Name(text_str);
+/// \brief Constructor with a context of MasterLexer.
+///
+/// The \c lexer should point to the beginning of valid textual representation
+/// of an RP RDATA.  The MAILBOX and TEXT fields can be non-absolute if \c
+/// origin is non-NULL, in which case \c origin is used to make them absolute.
+///
+/// \throw MasterLexer::LexerError General parsing error such as missing field.
+/// \throw Other Exceptions from the Name and constructors if construction of
+/// textual fields as these objects fail.
+///
+/// \param lexer A \c MasterLexer object parsing a master file for the
+/// RDATA to be created
+/// \param origin If non NULL, specifies the origin of SERVER when it
+/// is non-absolute.
+RP::RP(MasterLexer& lexer, const Name* origin,
+       MasterLoader::Options, MasterLoaderCallbacks&) :
+    mailbox_(createNameFromLexer(lexer, origin)),
+    text_(createNameFromLexer(lexer, origin))
+{
 }
 
 /// \brief Constructor from wire-format data.
@@ -70,15 +94,15 @@ RP::RP(const std::string& rp_str) :
 /// length) for parsing.
 /// If necessary, the caller will check consistency.
 ///
-/// \exception std::bad_alloc Memory allocation for names fails.
-/// \exception Other The constructor of the \c Name class will throw if the
+/// \throw std::bad_alloc Memory allocation for names fails.
+/// \throw Other The constructor of the \c Name class will throw if the
 /// names in the wire is invalid.
 RP::RP(InputBuffer& buffer, size_t) : mailbox_(buffer), text_(buffer) {
 }
 
 /// \brief Copy constructor.
 ///
-/// \exception std::bad_alloc Memory allocation fails in copying internal
+/// \throw std::bad_alloc Memory allocation fails in copying internal
 /// member variables (this should be very rare).
 RP::RP(const RP& other) :
     Rdata(), mailbox_(other.mailbox_), text_(other.text_)
@@ -89,7 +113,7 @@ RP::RP(const RP& other) :
 /// The output of this method is formatted as described in the "from string"
 /// constructor (\c RP(const std::string&))).
 ///
-/// \exception std::bad_alloc Internal resource allocation fails.
+/// \throw std::bad_alloc Internal resource allocation fails.
 ///
 /// \return A \c string object that represents the \c RP object.
 std::string
@@ -97,20 +121,36 @@ RP::toText() const {
     return (mailbox_.toText() + " " + text_.toText());
 }
 
+/// \brief Render the \c RP in the wire format without name compression.
+///
+/// \throw std::bad_alloc Internal resource allocation fails.
+///
+/// \param buffer An output buffer to store the wire data.
 void
 RP::toWire(OutputBuffer& buffer) const {
     mailbox_.toWire(buffer);
     text_.toWire(buffer);
 }
 
+/// \brief Render the \c RP in the wire format with taking into account
+/// compression.
+///
+// Type RP is not "well-known", and name compression must be disabled
+// per RFC3597.
+///
+/// \throw std::bad_alloc Internal resource allocation fails.
+///
+/// \param renderer DNS message rendering context that encapsulates the
+/// output buffer and name compression information.
 void
 RP::toWire(AbstractMessageRenderer& renderer) const {
-    // Type RP is not "well-known", and name compression must be disabled
-    // per RFC3597.
     renderer.writeName(mailbox_, false);
     renderer.writeName(text_, false);
 }
 
+/// \brief Compare two instances of \c RP RDATA.
+///
+/// See documentation in \c Rdata.
 int
 RP::compare(const Rdata& other) const {
     const RP& other_rp = dynamic_cast<const RP&>(other);

+ 5 - 7
src/lib/dns/rdata/generic/rp_17.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -49,9 +49,8 @@ public:
 
     /// \brief Return the value of the mailbox field.
     ///
-    /// This method normally does not throw an exception, but if resource
-    /// allocation for the returned \c Name object fails, a corresponding
-    /// standard exception will be thrown.
+    /// \throw std::bad_alloc If resource allocation for the returned
+    /// \c Name fails.
     ///
     /// \note
     /// Unlike the case of some other RDATA classes (such as
@@ -69,9 +68,8 @@ public:
 
     /// \brief Return the value of the text field.
     ///
-    /// This method normally does not throw an exception, but if resource
-    /// allocation for the returned \c Name object fails, a corresponding
-    /// standard exception will be thrown.
+    /// \throw std::bad_alloc If resource allocation for the returned
+    /// \c Name fails.
     Name getText() const { return (text_); }
 
 private:

+ 178 - 61
src/lib/dns/rdata/generic/sshfp_44.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -35,122 +35,239 @@ using namespace isc::util::encode;
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
 
-SSHFP::SSHFP(InputBuffer& buffer, size_t rdata_len) {
-    if (rdata_len < 2) {
-        isc_throw(InvalidRdataLength, "SSHFP record too short");
+struct SSHFPImpl {
+    // straightforward representation of SSHFP RDATA fields
+    SSHFPImpl(uint8_t algorithm, uint8_t fingerprint_type,
+              vector<uint8_t>& fingerprint) :
+        algorithm_(algorithm),
+        fingerprint_type_(fingerprint_type),
+        fingerprint_(fingerprint)
+    {}
+
+    uint8_t algorithm_;
+    uint8_t fingerprint_type_;
+    const vector<uint8_t> fingerprint_;
+};
+
+// helper function for string and lexer constructors
+SSHFPImpl*
+SSHFP::constructFromLexer(MasterLexer& lexer) {
+    const uint32_t algorithm =
+        lexer.getNextToken(MasterToken::NUMBER).getNumber();
+    if (algorithm > 255) {
+        isc_throw(InvalidRdataText, "SSHFP algorithm number out of range");
     }
 
-    algorithm_ = buffer.readUint8();
-    fingerprint_type_ = buffer.readUint8();
-
-    rdata_len -= 2;
-    if (rdata_len > 0) {
-        fingerprint_.resize(rdata_len);
-        buffer.readData(&fingerprint_[0], rdata_len);
+    const uint32_t fingerprint_type =
+        lexer.getNextToken(MasterToken::NUMBER).getNumber();
+    if (fingerprint_type > 255) {
+        isc_throw(InvalidRdataText, "SSHFP fingerprint type out of range");
     }
-}
-
-SSHFP::SSHFP(const std::string& sshfp_str) {
-    std::istringstream iss(sshfp_str);
-    std::stringbuf fingerprintbuf;
-    uint32_t algorithm, fingerprint_type;
 
-    iss >> algorithm >> fingerprint_type;
-    if (iss.bad() || iss.fail()) {
-        isc_throw(InvalidRdataText, "Invalid SSHFP text");
+    std::string fingerprint_str;
+    std::string fingerprint_substr;
+    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(fingerprint_substr);
+        fingerprint_str.append(fingerprint_substr);
     }
+    lexer.ungetToken();
 
-    if (algorithm > 255) {
-        isc_throw(InvalidRdataText, "SSHFP algorithm number out of range");
+    vector<uint8_t> fingerprint;
+    // If fingerprint is missing, it's OK. See the API documentation of the
+    // constructor.
+    if (fingerprint_str.size() > 0) {
+        decodeHex(fingerprint_str, fingerprint);
     }
 
-    if (fingerprint_type > 255) {
-        isc_throw(InvalidRdataText, "SSHFP fingerprint type out of range");
-    }
+    return (new SSHFPImpl(algorithm, fingerprint_type, fingerprint));
+}
+
+/// \brief Constructor from string.
+///
+/// The given string must represent a valid SSHFP 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 Algorithm and Fingerprint Type fields must be within their valid
+/// ranges, but are not constrained to the values defined in RFC4255.
+///
+/// The Fingerprint field may be absent, but if present it must contain a
+/// valid hex encoding of the fingerprint. For compatibility with BIND 9,
+/// whitespace is allowed in the hex text (RFC4255 is silent on the matter).
+///
+/// \throw InvalidRdataText if any fields are missing, out of their valid
+/// ranges, or incorrect.
+///
+/// \param sshfp_str A string containing the RDATA to be created
+SSHFP::SSHFP(const string& sshfp_str) :
+    impl_(NULL)
+{
+    // 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 SSHFPImpl that constructFromLexer() returns.
+    std::auto_ptr<SSHFPImpl> impl_ptr(NULL);
 
-    iss >> &fingerprintbuf;
     try {
-        decodeHex(fingerprintbuf.str(), fingerprint_);
+        std::istringstream ss(sshfp_str);
+        MasterLexer lexer;
+        lexer.pushSource(ss);
+
+        impl_ptr.reset(constructFromLexer(lexer));
+
+        if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
+            isc_throw(InvalidRdataText, "extra input text for SSHFP: "
+                      << sshfp_str);
+        }
+    } catch (const MasterLexer::LexerError& ex) {
+        isc_throw(InvalidRdataText, "Failed to construct SSHFP from '" <<
+                  sshfp_str << "': " << ex.what());
     } catch (const isc::BadValue& e) {
         isc_throw(InvalidRdataText,
                   "Bad SSHFP fingerprint: " << e.what());
     }
 
-    algorithm_ = algorithm;
-    fingerprint_type_ = fingerprint_type;
+    impl_ = impl_ptr.release();
 }
 
-SSHFP::SSHFP(uint8_t algorithm, uint8_t fingerprint_type,
-             const std::string& fingerprint)
+/// \brief Constructor with a context of MasterLexer.
+///
+/// The \c lexer should point to the beginning of valid textual representation
+/// of an SSHFP RDATA.
+///
+/// \throw MasterLexer::LexerError General parsing error such as missing field.
+/// \throw InvalidRdataText Fields are out of their valid range, or are
+/// incorrect.
+/// \throw BadValue Fingerprint is not a valid hex string.
+///
+/// \param lexer A \c MasterLexer object parsing a master file for the
+/// RDATA to be created
+SSHFP::SSHFP(MasterLexer& lexer, const Name*,
+             MasterLoader::Options, MasterLoaderCallbacks&) :
+    impl_(constructFromLexer(lexer))
 {
-    algorithm_ = algorithm;
-    fingerprint_type_ = fingerprint_type;
+}
+
+/// \brief Constructor from InputBuffer.
+///
+/// The passed buffer must contain a valid SSHFP RDATA.
+///
+/// The Algorithm and Fingerprint Type fields are not checked for unknown
+/// values.  It is okay for the fingerprint data to be missing (see the
+/// description of the constructor from string).
+SSHFP::SSHFP(InputBuffer& buffer, size_t rdata_len) {
+    if (rdata_len < 2) {
+        isc_throw(InvalidRdataLength, "SSHFP record too short");
+    }
+
+    const uint8_t algorithm = buffer.readUint8();
+    const uint8_t fingerprint_type = buffer.readUint8();
+
+    vector<uint8_t> fingerprint;
+    rdata_len -= 2;
+    if (rdata_len > 0) {
+        fingerprint.resize(rdata_len);
+        buffer.readData(&fingerprint[0], rdata_len);
+    }
+
+    impl_ = new SSHFPImpl(algorithm, fingerprint_type, fingerprint);
+}
 
+SSHFP::SSHFP(uint8_t algorithm, uint8_t fingerprint_type,
+             const string& fingerprint_txt) :
+    impl_(NULL)
+{
+    vector<uint8_t> fingerprint;
     try {
-        decodeHex(fingerprint, fingerprint_);
+        decodeHex(fingerprint_txt, fingerprint);
     } catch (const isc::BadValue& e) {
         isc_throw(InvalidRdataText, "Bad SSHFP fingerprint: " << e.what());
     }
+
+    impl_ = new SSHFPImpl(algorithm, fingerprint_type, fingerprint);
 }
 
 SSHFP::SSHFP(const SSHFP& other) :
-  Rdata(), algorithm_(other.algorithm_),
-  fingerprint_type_(other.fingerprint_type_),
-  fingerprint_(other.fingerprint_)
+        Rdata(), impl_(new SSHFPImpl(*other.impl_))
 {}
 
+SSHFP&
+SSHFP::operator=(const SSHFP& source) {
+    if (impl_ == source.impl_) {
+        return (*this);
+    }
+
+    SSHFPImpl* newimpl = new SSHFPImpl(*source.impl_);
+    delete impl_;
+    impl_ = newimpl;
+
+    return (*this);
+}
+
+SSHFP::~SSHFP() {
+    delete impl_;
+}
+
 void
 SSHFP::toWire(OutputBuffer& buffer) const {
-    buffer.writeUint8(algorithm_);
-    buffer.writeUint8(fingerprint_type_);
+    buffer.writeUint8(impl_->algorithm_);
+    buffer.writeUint8(impl_->fingerprint_type_);
 
-    if (!fingerprint_.empty()) {
-        buffer.writeData(&fingerprint_[0], fingerprint_.size());
+    if (!impl_->fingerprint_.empty()) {
+        buffer.writeData(&impl_->fingerprint_[0],
+                         impl_->fingerprint_.size());
     }
 }
 
 void
 SSHFP::toWire(AbstractMessageRenderer& renderer) const {
-    renderer.writeUint8(algorithm_);
-    renderer.writeUint8(fingerprint_type_);
+    renderer.writeUint8(impl_->algorithm_);
+    renderer.writeUint8(impl_->fingerprint_type_);
 
-    if (!fingerprint_.empty()) {
-        renderer.writeData(&fingerprint_[0], fingerprint_.size());
+    if (!impl_->fingerprint_.empty()) {
+        renderer.writeData(&impl_->fingerprint_[0],
+                           impl_->fingerprint_.size());
     }
 }
 
 string
 SSHFP::toText() const {
-    return (lexical_cast<string>(static_cast<int>(algorithm_)) +
-            " " + lexical_cast<string>(static_cast<int>(fingerprint_type_)) +
-            (fingerprint_.empty() ? "" : " " + encodeHex(fingerprint_)));
+    return (lexical_cast<string>(static_cast<int>(impl_->algorithm_)) + " " +
+            lexical_cast<string>(static_cast<int>(impl_->fingerprint_type_)) +
+            (impl_->fingerprint_.empty() ? "" :
+             " " + encodeHex(impl_->fingerprint_)));
 }
 
 int
 SSHFP::compare(const Rdata& other) const {
     const SSHFP& other_sshfp = dynamic_cast<const SSHFP&>(other);
 
-    /* This doesn't really make any sort of sense, but in the name of
-       consistency... */
-
-    if (algorithm_ < other_sshfp.algorithm_) {
+    if (impl_->algorithm_ < other_sshfp.impl_->algorithm_) {
         return (-1);
-    } else if (algorithm_ > other_sshfp.algorithm_) {
+    } else if (impl_->algorithm_ > other_sshfp.impl_->algorithm_) {
         return (1);
     }
 
-    if (fingerprint_type_ < other_sshfp.fingerprint_type_) {
+    if (impl_->fingerprint_type_ < other_sshfp.impl_->fingerprint_type_) {
         return (-1);
-    } else if (fingerprint_type_ > other_sshfp.fingerprint_type_) {
+    } else if (impl_->fingerprint_type_ >
+               other_sshfp.impl_->fingerprint_type_) {
         return (1);
     }
 
-    const size_t this_len = fingerprint_.size();
-    const size_t other_len = other_sshfp.fingerprint_.size();
+    const size_t this_len = impl_->fingerprint_.size();
+    const size_t other_len = other_sshfp.impl_->fingerprint_.size();
     const size_t cmplen = min(this_len, other_len);
 
     if (cmplen > 0) {
-        const int cmp = memcmp(&fingerprint_[0], &other_sshfp.fingerprint_[0],
+        const int cmp = memcmp(&impl_->fingerprint_[0],
+                               &other_sshfp.impl_->fingerprint_[0],
                                cmplen);
         if (cmp != 0) {
             return (cmp);
@@ -168,17 +285,17 @@ SSHFP::compare(const Rdata& other) const {
 
 uint8_t
 SSHFP::getAlgorithmNumber() const {
-    return (algorithm_);
+    return (impl_->algorithm_);
 }
 
 uint8_t
 SSHFP::getFingerprintType() const {
-    return (fingerprint_type_);
+    return (impl_->fingerprint_type_);
 }
 
 size_t
 SSHFP::getFingerprintLen() const {
-    return (fingerprint_.size());
+    return (impl_->fingerprint_.size());
 }
 
 // END_RDATA_NAMESPACE

+ 10 - 7
src/lib/dns/rdata/generic/sshfp_44.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -28,12 +28,17 @@
 
 // BEGIN_RDATA_NAMESPACE
 
+struct SSHFPImpl;
+
 class SSHFP : public Rdata {
 public:
     // BEGIN_COMMON_MEMBERS
     // END_COMMON_MEMBERS
 
-    SSHFP(uint8_t algorithm, uint8_t fingerprint_type, const std::string& fingerprint);
+    SSHFP(uint8_t algorithm, uint8_t fingerprint_type,
+          const std::string& fingerprint);
+    SSHFP& operator=(const SSHFP& source);
+    ~SSHFP();
 
     ///
     /// Specialized methods
@@ -43,11 +48,9 @@ public:
     size_t getFingerprintLen() const;
 
 private:
-    /// Note: this is a prototype version; we may reconsider
-    /// this representation later.
-    uint8_t algorithm_;
-    uint8_t fingerprint_type_;
-    std::vector<uint8_t> fingerprint_;
+    SSHFPImpl* constructFromLexer(MasterLexer& lexer);
+
+    SSHFPImpl* impl_;
 };
 
 // END_RDATA_NAMESPACE

+ 7 - 1
src/lib/dns/rdata/hs_4/a_1.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -31,6 +31,12 @@ A::A(const std::string&) {
     // TBD
 }
 
+A::A(MasterLexer&, const Name*,
+     MasterLoader::Options, MasterLoaderCallbacks&)
+{
+    // TBD
+}
+
 A::A(InputBuffer&, size_t) {
     // TBD
 }

+ 4 - 40
src/lib/dns/rrparamregistry-placeholder.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -41,35 +41,6 @@ using namespace isc::dns::rdata;
 namespace isc {
 namespace dns {
 
-namespace rdata {
-
-RdataPtr
-AbstractRdataFactory::create(MasterLexer& lexer, const Name*,
-                             MasterLoader::Options,
-                             MasterLoaderCallbacks&) const
-{
-    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();
-    }
-
-    return (create(s));
-}
-
-} // end of namespace isc::dns::rdata
-
 namespace {
 ///
 /// The following function and class are a helper to define case-insensitive
@@ -190,10 +161,8 @@ typedef map<RRTypeClass, RdataFactoryPtr> RdataFactoryMap;
 typedef map<RRType, RdataFactoryPtr> GenericRdataFactoryMap;
 
 template <typename T>
-class OldRdataFactory : public AbstractRdataFactory {
+class RdataFactory : public AbstractRdataFactory {
 public:
-    using AbstractRdataFactory::create;
-
     virtual RdataPtr create(const string& rdata_str) const
     {
         return (RdataPtr(new T(rdata_str)));
@@ -208,16 +177,11 @@ public:
     {
         return (RdataPtr(new T(dynamic_cast<const T&>(source))));
     }
-};
-
-template <typename T>
-class RdataFactory : public OldRdataFactory<T> {
-public:
-    using OldRdataFactory<T>::create;
 
     virtual RdataPtr create(MasterLexer& lexer, const Name* origin,
                             MasterLoader::Options options,
-                            MasterLoaderCallbacks& callbacks) const {
+                            MasterLoaderCallbacks& callbacks) const
+    {
         return (RdataPtr(new T(lexer, origin, options, callbacks)));
     }
 };

+ 5 - 12
src/lib/dns/rrparamregistry.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -55,11 +55,11 @@ namespace rdata {
 /// \brief The \c AbstractRdataFactory class is an abstract base class to
 /// encapsulate a set of Rdata factory methods in a polymorphic way.
 ///
-/// An external developers who want to introduce a new or experimental RR type
-/// are expected to define a corresponding derived class of \c
+/// An external developer who wants to introduce a new or experimental RR type
+/// is expected to define a corresponding derived class of \c
 /// AbstractRdataFactory and register it via \c RRParamRegistry.
 ///
-/// For other users of this API normally do not have to care about this class
+/// Other users of this API normally do not have to care about this class
 /// or its derived classes; this class is generally intended to be used
 /// as an internal utility of the API implementation.
 class AbstractRdataFactory {
@@ -125,16 +125,9 @@ public:
     /// of a specific RR type and class for \c RRParamRegistry::createRdata()
     /// that uses \c MasterLexer.  See its description for the expected
     /// behavior and meaning of the parameters.
-    ///
-    /// \note Right now this is not defined as a pure virtual method and
-    /// provides the default implementation.  This is an intermediate
-    /// workaround until we implement the underlying constructor for all
-    /// supported \c Rdata classes; once it's completed the workaround
-    /// default implementation should be removed and this method should become
-    /// pure virtual.
     virtual RdataPtr create(MasterLexer& lexer, const Name* origin,
                             MasterLoader::Options options,
-                            MasterLoaderCallbacks& callbacks) const;
+                            MasterLoaderCallbacks& callbacks) const = 0;
     //@}
 };
 

+ 80 - 33
src/lib/dns/tests/rdata_minfo_unittest.cc

@@ -1,6 +1,6 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 //
-// Permission to use, copy, modify, and/or distribute this software for generic
+// Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
 // copyright notice and this permission notice appear in all copies.
 //
@@ -12,6 +12,8 @@
 // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 // PERFORMANCE OF THIS SOFTWARE.
 
+#include <string>
+
 #include <util/buffer.h>
 #include <dns/exceptions.h>
 #include <dns/messagerenderer.h>
@@ -27,22 +29,62 @@
 
 using isc::UnitTestUtil;
 using namespace std;
-using namespace isc::dns;
 using namespace isc::util;
+using namespace isc::dns;
 using namespace isc::dns::rdata;
 
-// minfo text
-const char* const minfo_txt = "rmailbox.example.com. emailbox.example.com.";
-const char* const minfo_txt2 = "root.example.com. emailbox.example.com.";
-const char* const too_long_label = "01234567890123456789012345678901234567"
-                                   "89012345678901234567890123";
-
 namespace {
 class Rdata_MINFO_Test : public RdataTest {
-public:
+protected:
     Rdata_MINFO_Test():
-        rdata_minfo(string(minfo_txt)), rdata_minfo2(string(minfo_txt2)) {}
-
+        minfo_txt("rmailbox.example.com. emailbox.example.com."),
+        minfo_txt2("root.example.com. emailbox.example.com."),
+        too_long_label("01234567890123456789012345678901234567"
+                       "89012345678901234567890123."),
+        rdata_minfo(minfo_txt),
+        rdata_minfo2(minfo_txt2)
+    {}
+
+    void checkFromText_None(const string& rdata_str) {
+        checkFromText<generic::MINFO, isc::Exception, isc::Exception>(
+            rdata_str, rdata_minfo, false, false);
+    }
+
+    void checkFromText_LexerError(const string& rdata_str) {
+        checkFromText
+            <generic::MINFO, InvalidRdataText, MasterLexer::LexerError>(
+            rdata_str, rdata_minfo, true, true);
+    }
+
+    void checkFromText_TooLongLabel(const string& rdata_str) {
+        checkFromText<generic::MINFO, TooLongLabel, TooLongLabel>(
+            rdata_str, rdata_minfo, true, true);
+    }
+
+    void checkFromText_BadString(const string& rdata_str) {
+        checkFromText<generic::MINFO, InvalidRdataText, isc::Exception>(
+            rdata_str, rdata_minfo, true, false);
+    }
+
+    void checkFromText_EmptyLabel(const string& rdata_str) {
+        checkFromText<generic::MINFO, EmptyLabel, EmptyLabel>(
+            rdata_str, rdata_minfo, true, true);
+    }
+
+    void checkFromText_MissingOrigin(const string& rdata_str) {
+        checkFromText
+            <generic::MINFO, MissingNameOrigin, MissingNameOrigin>(
+                rdata_str, rdata_minfo, true, true);
+    }
+
+    void checkFromText_Origin(const string& rdata_str, const Name* origin) {
+        checkFromText<generic::MINFO, MissingNameOrigin, isc::Exception>(
+            rdata_str, rdata_minfo, true, false, origin);
+    }
+
+    const string minfo_txt;
+    const string minfo_txt2;
+    const string too_long_label;
     const generic::MINFO rdata_minfo;
     const generic::MINFO rdata_minfo2;
 };
@@ -54,24 +96,35 @@ TEST_F(Rdata_MINFO_Test, createFromText) {
 
     EXPECT_EQ(Name("root.example.com."), rdata_minfo2.getRmailbox());
     EXPECT_EQ(Name("emailbox.example.com."), rdata_minfo2.getEmailbox());
+
+    checkFromText_None(minfo_txt);
+
+    // origin defined for lexer constructor, but not string constructor
+    const Name origin("example.com");
+    checkFromText_Origin("rmailbox emailbox", &origin);
+
+    // lexer constructor accepts extra text, but string constructor doesn't
+    checkFromText_BadString("rmailbox.example.com. emailbox.example.com. "
+                            "extra.example.com.");
 }
 
 TEST_F(Rdata_MINFO_Test, badText) {
-    // incomplete text
-    EXPECT_THROW(generic::MINFO("root.example.com."),
-                 InvalidRdataText);
-    // number of fields (must be 2) is incorrect
-    EXPECT_THROW(generic::MINFO("root.example.com emailbox.example.com. "
-                                "example.com."),
-                 InvalidRdataText);
-    // bad rmailbox name
-    EXPECT_THROW(generic::MINFO("root.example.com. emailbox.example.com." +
-                                string(too_long_label)),
-                 TooLongLabel);
-    // bad emailbox name
-    EXPECT_THROW(generic::MINFO("root.example.com."  +
-                          string(too_long_label) + " emailbox.example.com."),
-                 TooLongLabel);
+    // too long names
+    checkFromText_TooLongLabel("root.example.com."  + too_long_label +
+                               " emailbox.example.com.");
+    checkFromText_TooLongLabel("root.example.com. emailbox.example.com." +
+                               too_long_label);
+
+    // invalid names
+    checkFromText_EmptyLabel("root..example.com. emailbox.example.com.");
+    checkFromText_EmptyLabel("root.example.com. emailbox..example.com.");
+
+    // missing name
+    checkFromText_LexerError("root.example.com.");
+
+    // missing origin
+    checkFromText_MissingOrigin("root.example.com emailbox.example.com.");
+    checkFromText_MissingOrigin("root.example.com. emailbox.example.com");
 }
 
 TEST_F(Rdata_MINFO_Test, createFromWire) {
@@ -103,12 +156,6 @@ TEST_F(Rdata_MINFO_Test, createFromWire) {
                  DNSMessageFORMERR);
 }
 
-TEST_F(Rdata_MINFO_Test, createFromLexer) {
-    EXPECT_EQ(0, rdata_minfo.compare(
-        *test::createRdataUsingLexer(RRType::MINFO(), RRClass::IN(),
-                                     minfo_txt)));
-}
-
 TEST_F(Rdata_MINFO_Test, assignment) {
     generic::MINFO copy((string(minfo_txt2)));
     copy = rdata_minfo;

+ 52 - 20
src/lib/dns/tests/rdata_rp_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -41,6 +41,38 @@ protected:
         obuffer(0)
     {}
 
+    void checkFromText_None(const string& rdata_str) {
+        checkFromText<generic::RP, isc::Exception, isc::Exception>(
+            rdata_str, rdata_rp, false, false);
+    }
+
+    void checkFromText_LexerError(const string& rdata_str) {
+        checkFromText
+            <generic::RP, InvalidRdataText, MasterLexer::LexerError>(
+                rdata_str, rdata_rp, true, true);
+    }
+
+    void checkFromText_BadString(const string& rdata_str) {
+        checkFromText<generic::RP, InvalidRdataText, isc::Exception>(
+            rdata_str, rdata_rp, true, false);
+    }
+
+    void checkFromText_EmptyLabel(const string& rdata_str) {
+        checkFromText<generic::RP, EmptyLabel, EmptyLabel>(
+            rdata_str, rdata_rp, true, true);
+    }
+
+    void checkFromText_MissingOrigin(const string& rdata_str) {
+        checkFromText
+            <generic::RP, MissingNameOrigin, MissingNameOrigin>(
+                rdata_str, rdata_rp, true, true);
+    }
+
+    void checkFromText_Origin(const string& rdata_str, const Name* origin) {
+        checkFromText<generic::RP, MissingNameOrigin, isc::Exception>(
+            rdata_str, rdata_rp, true, false, origin);
+    }
+
     const Name mailbox_name, text_name;
     const generic::RP rdata_rp; // commonly used test RDATA
     OutputBuffer obuffer;
@@ -52,17 +84,28 @@ TEST_F(Rdata_RP_Test, createFromText) {
     EXPECT_EQ(mailbox_name, rdata_rp.getMailbox());
     EXPECT_EQ(text_name, rdata_rp.getText());
 
-    // Invalid textual input cases follow:
-    // names are invalid
-    EXPECT_THROW(generic::RP("bad..name. rp-text.example.com"), EmptyLabel);
-    EXPECT_THROW(generic::RP("mailbox.example.com. bad..name"), EmptyLabel);
+    checkFromText_None("root.example.com. rp-text.example.com.");
+
+    // origin defined for lexer constructor, but not string constructor
+    const Name origin("example.com");
+    checkFromText_Origin("root rp-text", &origin);
+
+    // lexer constructor accepts extra text, but string constructor doesn't
+    checkFromText_BadString("root.example.com. rp-text.example.com. "
+                            "extra.example.com.");
+}
+
+TEST_F(Rdata_RP_Test, badText) {
+    // invalid names
+    checkFromText_EmptyLabel("root..example.com. rp-text.example.com.");
+    checkFromText_EmptyLabel("root.example.com. rp-text..example.com.");
 
     // missing field
-    EXPECT_THROW(generic::RP("mailbox.example.com."), InvalidRdataText);
+    checkFromText_LexerError("root.example.com.");
 
-    // redundant field
-    EXPECT_THROW(generic::RP("mailbox.example.com. rp-text.example.com. "
-                             "redundant.example."), InvalidRdataText);
+    // missing origin
+    checkFromText_MissingOrigin("root.example.com rp-text.example.com.");
+    checkFromText_MissingOrigin("root.example.com. rp-text.example.com");
 }
 
 TEST_F(Rdata_RP_Test, createFromWire) {
@@ -106,17 +149,6 @@ TEST_F(Rdata_RP_Test, createFromParams) {
     EXPECT_EQ(text_name, generic::RP(mailbox_name, text_name).getText());
 }
 
-TEST_F(Rdata_RP_Test, createFromLexer) {
-    EXPECT_EQ(0, rdata_rp.compare(
-        *test::createRdataUsingLexer(RRType::RP(), RRClass::IN(),
-                                     "root.example.com. "
-                                     "rp-text.example.com.")));
-
-    // Exceptions cause NULL to be returned.
-    EXPECT_FALSE(test::createRdataUsingLexer(RRType::RP(), RRClass::IN(),
-                                             "mailbox.example.com."));
-}
-
 TEST_F(Rdata_RP_Test, toWireBuffer) {
     // construct expected data
     UnitTestUtil::readWireData("rdata_rp_toWire1.wire", expected_wire);

+ 77 - 20
src/lib/dns/tests/rdata_sshfp_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2012  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2012-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -30,17 +30,50 @@
 
 using isc::UnitTestUtil;
 using namespace std;
+using namespace isc;
 using namespace isc::dns;
 using namespace isc::util;
 using namespace isc::dns::rdata;
 
 namespace {
 class Rdata_SSHFP_Test : public RdataTest {
-    // there's nothing to specialize
+protected:
+        Rdata_SSHFP_Test() :
+            sshfp_txt("2 1 123456789abcdef67890123456789abcdef67890"),
+            rdata_sshfp(sshfp_txt)
+        {}
+
+    void checkFromText_None(const string& rdata_str) {
+        checkFromText<generic::SSHFP, isc::Exception, isc::Exception>(
+            rdata_str, rdata_sshfp, false, false);
+    }
+
+    void checkFromText_InvalidText(const string& rdata_str) {
+        checkFromText<generic::SSHFP, InvalidRdataText, InvalidRdataText>(
+            rdata_str, rdata_sshfp, true, true);
+    }
+
+    void checkFromText_LexerError(const string& rdata_str) {
+        checkFromText
+            <generic::SSHFP, InvalidRdataText, MasterLexer::LexerError>(
+                rdata_str, rdata_sshfp, true, true);
+    }
+
+    void checkFromText_BadValue(const string& rdata_str) {
+        checkFromText<generic::SSHFP, InvalidRdataText, BadValue>(
+            rdata_str, rdata_sshfp, true, true);
+    }
+
+    void checkFromText_BadString(const string& rdata_str) {
+        checkFromText
+            <generic::SSHFP, InvalidRdataText, isc::Exception>(
+                rdata_str, rdata_sshfp, true, false);
+    }
+
+    const string sshfp_txt;
+    const generic::SSHFP rdata_sshfp;
 };
 
-const string sshfp_txt("2 1 123456789abcdef67890123456789abcdef67890");
-const generic::SSHFP rdata_sshfp(2, 1, "123456789abcdef67890123456789abcdef67890");
 const uint8_t rdata_sshfp_wiredata[] = {
     // algorithm
     0x02,
@@ -56,22 +89,23 @@ const uint8_t rdata_sshfp_wiredata[] = {
 
 TEST_F(Rdata_SSHFP_Test, createFromText) {
     // Basic test
-    const generic::SSHFP rdata_sshfp2(sshfp_txt);
-    EXPECT_EQ(0, rdata_sshfp2.compare(rdata_sshfp));
+    checkFromText_None(sshfp_txt);
 
     // With different spacing
-    const generic::SSHFP rdata_sshfp3("2 1   123456789abcdef67890123456789abcdef67890");
-    EXPECT_EQ(0, rdata_sshfp3.compare(rdata_sshfp));
+    checkFromText_None("2 1   123456789abcdef67890123456789abcdef67890");
 
     // Combination of lowercase and uppercase
-    const generic::SSHFP rdata_sshfp4("2 1   123456789ABCDEF67890123456789abcdef67890");
-    EXPECT_EQ(0, rdata_sshfp4.compare(rdata_sshfp));
-}
+    checkFromText_None("2 1 123456789ABCDEF67890123456789abcdef67890");
 
-TEST_F(Rdata_SSHFP_Test, createFromLexer) {
-    EXPECT_EQ(0, rdata_sshfp.compare(
-        *test::createRdataUsingLexer(RRType::SSHFP(), RRClass::IN(),
-                                     "2 1 123456789abcdef67890123456789abcdef67890")));
+    // spacing in the fingerprint field
+    checkFromText_None("2 1 123456789abcdef67890 123456789abcdef67890");
+
+    // multi-line fingerprint field
+    checkFromText_None("2 1 ( 123456789abcdef67890\n 123456789abcdef67890 )");
+
+    // string constructor throws if there's extra text,
+    // but lexer constructor doesn't
+    checkFromText_BadString(sshfp_txt + "\n" + sshfp_txt);
 }
 
 TEST_F(Rdata_SSHFP_Test, algorithmTypes) {
@@ -101,13 +135,30 @@ TEST_F(Rdata_SSHFP_Test, algorithmTypes) {
 }
 
 TEST_F(Rdata_SSHFP_Test, badText) {
-    EXPECT_THROW(const generic::SSHFP rdata_sshfp("1"), InvalidRdataText);
-    EXPECT_THROW(const generic::SSHFP rdata_sshfp("BUCKLE MY SHOES"), InvalidRdataText);
-    EXPECT_THROW(const generic::SSHFP rdata_sshfp("1 2 foo bar"), InvalidRdataText);
+    checkFromText_LexerError("1");
+    checkFromText_LexerError("ONE 2 123456789abcdef67890123456789abcdef67890");
+    checkFromText_LexerError("1 TWO 123456789abcdef67890123456789abcdef67890");
+    checkFromText_BadValue("1 2 BUCKLEMYSHOE");
+    checkFromText_BadValue(sshfp_txt + " extra text");
+
+    // yes, these are redundant to the last test cases in algorithmTypes
+    checkFromText_InvalidText(
+        "2345 1 123456789abcdef67890123456789abcdef67890");
+    checkFromText_InvalidText(
+        "2 1234 123456789abcdef67890123456789abcdef67890");
+
+    // negative values are trapped in the lexer rather than the constructor
+    checkFromText_LexerError("-2 1 123456789abcdef67890123456789abcdef67890");
+    checkFromText_LexerError("2 -1 123456789abcdef67890123456789abcdef67890");
 }
 
-TEST_F(Rdata_SSHFP_Test, copy) {
-    const generic::SSHFP rdata_sshfp2(rdata_sshfp);
+TEST_F(Rdata_SSHFP_Test, copyAndAssign) {
+    // Copy construct
+    generic::SSHFP rdata_sshfp2(rdata_sshfp);
+    EXPECT_EQ(0, rdata_sshfp.compare(rdata_sshfp2));
+
+    // Assignment, mainly to confirm it doesn't cause disruption.
+    rdata_sshfp2 = rdata_sshfp;
     EXPECT_EQ(0, rdata_sshfp.compare(rdata_sshfp2));
 }
 
@@ -160,6 +211,12 @@ TEST_F(Rdata_SSHFP_Test, createFromWire) {
                  InvalidBufferPosition);
 }
 
+TEST_F(Rdata_SSHFP_Test, createFromParams) {
+    const generic::SSHFP rdata_sshfp2(
+        2, 1, "123456789abcdef67890123456789abcdef67890");
+    EXPECT_EQ(0, rdata_sshfp2.compare(rdata_sshfp));
+}
+
 TEST_F(Rdata_SSHFP_Test, toText) {
     EXPECT_TRUE(boost::iequals(sshfp_txt, rdata_sshfp.toText()));
 

+ 130 - 68
src/lib/dns/tests/rdata_tsig_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -23,6 +23,7 @@
 #include <dns/rdataclass.h>
 #include <dns/rrclass.h>
 #include <dns/rrtype.h>
+#include <dns/tsigerror.h>
 
 #include <gtest/gtest.h>
 
@@ -31,34 +32,84 @@
 
 using isc::UnitTestUtil;
 using namespace std;
+using namespace isc;
 using namespace isc::dns;
 using namespace isc::util;
 using namespace isc::dns::rdata;
 
 namespace {
+
 class Rdata_TSIG_Test : public RdataTest {
 protected:
-    vector<uint8_t> expect_data;
-};
+    Rdata_TSIG_Test() :
+	// no MAC or Other Data
+        valid_text1("hmac-md5.sig-alg.reg.int. 1286779327 300 "
+                    "0 16020 BADKEY 0"),
+	// MAC but no Other Data
+        valid_text2("hmac-sha256. 1286779327 300 12 "
+                    "FAKEFAKEFAKEFAKE 16020 BADSIG 0"),
+	// MAC and Other Data
+        valid_text3("hmac-sha1. 1286779327 300 12 "
+                    "FAKEFAKEFAKEFAKE 16020 BADTIME 6 FAKEFAKE"),
+	// MAC and Other Data (with Error that doesn't expect Other Data)
+        valid_text4("hmac-sha1. 1286779327 300 12 "
+                    "FAKEFAKEFAKEFAKE 16020 BADSIG 6 FAKEFAKE"),
+	// numeric error code
+        valid_text5("hmac-sha256. 1286779327 300 12 "
+                    "FAKEFAKEFAKEFAKE 16020 2845 0"),
+        rdata_tsig(valid_text1)
+    {}
+
+    void checkFromText_None(const string& rdata_str) {
+        checkFromText<any::TSIG, isc::Exception, isc::Exception>(
+            rdata_str, rdata_tsig, false, false);
+    }
+
+    void checkFromText_InvalidText(const string& rdata_str) {
+        checkFromText<any::TSIG, InvalidRdataText, InvalidRdataText>(
+            rdata_str, rdata_tsig, true, true);
+    }
 
-const char* const valid_text1 = "hmac-md5.sig-alg.reg.int. 1286779327 300 "
-    "0 16020 BADKEY 0";
-const char* const valid_text2 = "hmac-sha256. 1286779327 300 12 "
-    "FAKEFAKEFAKEFAKE 16020 BADSIG 0";
+    void checkFromText_BadValue(const string& rdata_str) {
+        checkFromText<any::TSIG, BadValue, BadValue>(
+            rdata_str, rdata_tsig, true, true);
+    }
+
+    void checkFromText_LexerError(const string& rdata_str) {
+        checkFromText
+            <any::TSIG, InvalidRdataText, MasterLexer::LexerError>(
+                rdata_str, rdata_tsig, true, true);
+    }
 
-const char* const valid_text3 = "hmac-sha1. 1286779327 300 12 "
-    "FAKEFAKEFAKEFAKE 16020 BADTIME 6 FAKEFAKE";
-const char* const valid_text4 = "hmac-sha1. 1286779327 300 12 "
-    "FAKEFAKEFAKEFAKE 16020 BADSIG 6 FAKEFAKE";
-const char* const valid_text5 = "hmac-sha256. 1286779327 300 12 "
-    "FAKEFAKEFAKEFAKE 16020 2845 0"; // using numeric error code
-const char* const too_long_label = "012345678901234567890123456789"
-    "0123456789012345678901234567890123";
+    void checkFromText_TooLongLabel(const string& rdata_str) {
+        checkFromText<any::TSIG, TooLongLabel, TooLongLabel>(
+            rdata_str, rdata_tsig, true, true);
+    }
 
-// commonly used test RDATA
-const any::TSIG rdata_tsig((string(valid_text1)));
+    void checkFromText_EmptyLabel(const string& rdata_str) {
+        checkFromText<any::TSIG, EmptyLabel, EmptyLabel>(
+            rdata_str, rdata_tsig, true, true);
+    }
 
-TEST_F(Rdata_TSIG_Test, createFromText) {
+    void checkFromText_BadString(const string& rdata_str) {
+        checkFromText
+            <any::TSIG, InvalidRdataText, isc::Exception>(
+                rdata_str, rdata_tsig, true, false);
+    }
+
+    template <typename Output>
+    void toWireCommonChecks(Output& output) const;
+
+    const string valid_text1;
+    const string valid_text2;
+    const string valid_text3;
+    const string valid_text4;
+    const string valid_text5;
+    vector<uint8_t> expect_data;
+    const any::TSIG rdata_tsig; // commonly used test RDATA
+};
+
+TEST_F(Rdata_TSIG_Test, fromText) {
     // normal case.  it also tests getter methods.
     EXPECT_EQ(Name("hmac-md5.sig-alg.reg.int"), rdata_tsig.getAlgorithm());
     EXPECT_EQ(1286779327, rdata_tsig.getTimeSigned());
@@ -66,59 +117,80 @@ TEST_F(Rdata_TSIG_Test, createFromText) {
     EXPECT_EQ(0, rdata_tsig.getMACSize());
     EXPECT_EQ(static_cast<void*>(NULL), rdata_tsig.getMAC());
     EXPECT_EQ(16020, rdata_tsig.getOriginalID());
-    EXPECT_EQ(17, rdata_tsig.getError()); // TODO: use constant
+    EXPECT_EQ(TSIGError::BAD_KEY_CODE, rdata_tsig.getError());
     EXPECT_EQ(0, rdata_tsig.getOtherLen());
     EXPECT_EQ(static_cast<void*>(NULL), rdata_tsig.getOtherData());
 
-    any::TSIG tsig2((string(valid_text2)));
+    any::TSIG tsig2(valid_text2);
     EXPECT_EQ(12, tsig2.getMACSize());
-    EXPECT_EQ(16, tsig2.getError()); // TODO: use constant
+    EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig2.getError());
 
-    any::TSIG tsig3((string(valid_text3)));
+    any::TSIG tsig3(valid_text3);
     EXPECT_EQ(6, tsig3.getOtherLen());
 
     // The other data is unusual, but we don't reject it.
-    EXPECT_NO_THROW(any::TSIG(string(valid_text4)));
+    EXPECT_NO_THROW(any::TSIG tsig4(valid_text4));
 
     // numeric representation of TSIG error
-    any::TSIG tsig5((string(valid_text5)));
+    any::TSIG tsig5(valid_text5);
     EXPECT_EQ(2845, tsig5.getError());
 
-    //
-    // invalid cases
-    //
-    // there's a garbage parameter at the end
-    EXPECT_THROW(any::TSIG("foo 0 0 0 0 BADKEY 0 0"), InvalidRdataText);
-    // input is too short
-    EXPECT_THROW(any::TSIG("foo 0 0 0 0 BADKEY"), InvalidRdataText);
+    // not fully qualified algorithm name
+    any::TSIG tsig1("hmac-md5.sig-alg.reg.int 1286779327 300 "
+                    "0 16020 BADKEY 0");
+    EXPECT_EQ(0, tsig1.compare(rdata_tsig));
+
+    // multi-line rdata
+    checkFromText_None("hmac-md5.sig-alg.reg.int. ( 1286779327 300 \n"
+                       "0 16020 BADKEY 0 )");
+};
+
+TEST_F(Rdata_TSIG_Test, badText) {
+    // too many fields
+    checkFromText_BadString(valid_text1 + " 0 0");
+    // not enough fields
+    checkFromText_LexerError("foo 0 0 0 0 BADKEY");
     // bad domain name
-    EXPECT_THROW(any::TSIG(string(too_long_label) + "0 0 0 0 BADKEY 0"),
-                 TooLongLabel);
+    checkFromText_TooLongLabel(
+        "0123456789012345678901234567890123456789012345678901234567890123"
+        " 0 0 0 0 BADKEY 0");
+    checkFromText_EmptyLabel("foo..bar 0 0 0 0 BADKEY");
     // time is too large (2814...6 is 2^48)
-    EXPECT_THROW(any::TSIG("foo 281474976710656 0 0 0 BADKEY 0"),
-                 InvalidRdataText);
+    checkFromText_InvalidText("foo 281474976710656 0 0 0 BADKEY 0");
     // invalid time (negative)
-    EXPECT_THROW(any::TSIG("foo -1 0 0 0 BADKEY 0"), InvalidRdataText);
+    checkFromText_InvalidText("foo -1 0 0 0 BADKEY 0");
+    // invalid time (not a number)
+    checkFromText_InvalidText("foo TIME 0 0 0 BADKEY 0");
     // fudge is too large
-    EXPECT_THROW(any::TSIG("foo 0 65536 0 0 BADKEY 0"), InvalidRdataText);
+    checkFromText_InvalidText("foo 0 65536 0 0 BADKEY 0");
     // invalid fudge (negative)
-    EXPECT_THROW(any::TSIG("foo 0 -1 0 0 BADKEY 0"), InvalidRdataText);
+    checkFromText_LexerError("foo 0 -1 0 0 BADKEY 0");
+    // invalid fudge (not a number)
+    checkFromText_LexerError("foo 0 FUDGE 0 0 BADKEY 0");
     // MAC size is too large
-    EXPECT_THROW(any::TSIG("foo 0 0 65536 0 BADKEY 0"), InvalidRdataText);
+    checkFromText_InvalidText("foo 0 0 65536 0 BADKEY 0");
+    // invalide MAC size (negative)
+    checkFromText_LexerError("foo 0 0 -1 0 BADKEY 0");
+    // invalid MAC size (not a number)
+    checkFromText_LexerError("foo 0 0 MACSIZE 0 BADKEY 0");
     // MAC size and MAC mismatch
-    EXPECT_THROW(any::TSIG("foo 0 0 9 FAKE 0 BADKEY 0"), InvalidRdataText);
-    EXPECT_THROW(any::TSIG("foo 0 0 0 FAKE 0 BADKEY 0"), InvalidRdataText);
+    checkFromText_InvalidText("foo 0 0 9 FAKE 0 BADKEY 0");
     // MAC is bad base64
-    EXPECT_THROW(any::TSIG("foo 0 0 3 FAK= 0 BADKEY 0"), isc::BadValue);
+    checkFromText_BadValue("foo 0 0 3 FAK= 0 BADKEY 0");
     // Unknown error code
-    EXPECT_THROW(any::TSIG("foo 0 0 0 0 TEST 0"), InvalidRdataText);
+    checkFromText_InvalidText("foo 0 0 0 0 TEST 0");
     // Numeric error code is too large
-    EXPECT_THROW(any::TSIG("foo 0 0 0 0 65536 0"), InvalidRdataText);
+    checkFromText_InvalidText("foo 0 0 0 0 65536 0");
+    // Numeric error code is negative
+    checkFromText_InvalidText("foo 0 0 0 0 -1 0");
     // Other len is too large
-    EXPECT_THROW(any::TSIG("foo 0 0 0 0 NOERROR 65536 FAKE"), InvalidRdataText);
+    checkFromText_InvalidText("foo 0 0 0 0 NOERROR 65536 FAKE");
+    // Other len is negative
+    checkFromText_LexerError("foo 0 0 0 0 NOERROR -1 FAKE");
+    // invalid Other len
+    checkFromText_LexerError("foo 0 0 0 0 NOERROR LEN FAKE");
     // Other len and data mismatch
-    EXPECT_THROW(any::TSIG("foo 0 0 0 0 NOERROR 9 FAKE"), InvalidRdataText);
-    EXPECT_THROW(any::TSIG("foo 0 0 0 0 NOERROR 0 FAKE"), InvalidRdataText);
+    checkFromText_InvalidText("foo 0 0 0 0 NOERROR 9 FAKE");
 }
 
 void
@@ -221,12 +293,12 @@ TEST_F(Rdata_TSIG_Test, createFromParams) {
 
     const uint8_t fake_data[] = { 0x14, 0x02, 0x84, 0x14, 0x02, 0x84,
                                   0x14, 0x02, 0x84, 0x14, 0x02, 0x84 }; 
-    EXPECT_EQ(0, any::TSIG((string(valid_text2))).compare(
+    EXPECT_EQ(0, any::TSIG(valid_text2).compare(
                   any::TSIG(Name("hmac-sha256"), 1286779327, 300, 12,
                             fake_data, 16020, 16, 0, NULL)));
 
     const uint8_t fake_data2[] = { 0x14, 0x02, 0x84, 0x14, 0x02, 0x84 };
-    EXPECT_EQ(0, any::TSIG((string(valid_text3))).compare(
+    EXPECT_EQ(0, any::TSIG(valid_text3).compare(
                   any::TSIG(Name("hmac-sha1"), 1286779327, 300, 12,
                             fake_data, 16020, 18, 6, fake_data2)));
 
@@ -247,24 +319,14 @@ TEST_F(Rdata_TSIG_Test, createFromParams) {
                  isc::InvalidParameter);
 }
 
-TEST_F(Rdata_TSIG_Test, createFromLexer) {
-    EXPECT_EQ(0, rdata_tsig.compare(
-        *test::createRdataUsingLexer(RRType::TSIG(), RRClass::ANY(),
-                                     valid_text1)));
-
-    // Exceptions cause NULL to be returned.
-    EXPECT_FALSE(test::createRdataUsingLexer(RRType::TSIG(), RRClass::ANY(),
-                                             "foo 0 0 0 0 BADKEY 0 0"));
-}
-
 TEST_F(Rdata_TSIG_Test, assignment) {
-    any::TSIG copy((string(valid_text2)));
+    any::TSIG copy(valid_text2);
     copy = rdata_tsig;
     EXPECT_EQ(0, copy.compare(rdata_tsig));
 
     // Check if the copied data is valid even after the original is deleted
     any::TSIG* copy2 = new any::TSIG(rdata_tsig);
-    any::TSIG copy3((string(valid_text2)));
+    any::TSIG copy3(valid_text2);
     copy3 = *copy2;
     delete copy2;
     EXPECT_EQ(0, copy3.compare(rdata_tsig));
@@ -276,7 +338,7 @@ TEST_F(Rdata_TSIG_Test, assignment) {
 
 template <typename Output>
 void
-toWireCommonChecks(Output& output) {
+Rdata_TSIG_Test::toWireCommonChecks(Output& output) const {
     vector<uint8_t> expect_data;
 
     output.clear();
@@ -291,7 +353,7 @@ toWireCommonChecks(Output& output) {
 
     expect_data.clear();
     output.clear();
-    any::TSIG(string(valid_text2)).toWire(output);
+    any::TSIG(valid_text2).toWire(output);
     UnitTestUtil::readWireData("rdata_tsig_toWire2.wire", expect_data);
     expect_data.erase(expect_data.begin(), expect_data.begin() + 2);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
@@ -300,7 +362,7 @@ toWireCommonChecks(Output& output) {
 
     expect_data.clear();
     output.clear();
-    any::TSIG(string(valid_text3)).toWire(output);
+    any::TSIG(valid_text3).toWire(output);
     UnitTestUtil::readWireData("rdata_tsig_toWire3.wire", expect_data);
     expect_data.erase(expect_data.begin(), expect_data.begin() + 2);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
@@ -339,10 +401,10 @@ TEST_F(Rdata_TSIG_Test, toWireRenderer) {
 }
 
 TEST_F(Rdata_TSIG_Test, toText) {
-    EXPECT_EQ(string(valid_text1), rdata_tsig.toText());
-    EXPECT_EQ(string(valid_text2), any::TSIG(string(valid_text2)).toText());
-    EXPECT_EQ(string(valid_text3), any::TSIG(string(valid_text3)).toText());
-    EXPECT_EQ(string(valid_text5), any::TSIG(string(valid_text5)).toText());
+    EXPECT_EQ(valid_text1, rdata_tsig.toText());
+    EXPECT_EQ(valid_text2, any::TSIG(valid_text2).toText());
+    EXPECT_EQ(valid_text3, any::TSIG(valid_text3).toText());
+    EXPECT_EQ(valid_text5, any::TSIG(valid_text5).toText());
 }
 
 TEST_F(Rdata_TSIG_Test, compare) {

+ 5 - 2
src/lib/dns/tests/rrparamregistry_unittest.cc

@@ -1,4 +1,4 @@
-// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2010-2013  Internet Systems Consortium, Inc. ("ISC")
 //
 // Permission to use, copy, modify, and/or distribute this software for any
 // purpose with or without fee is hereby granted, provided that the above
@@ -108,13 +108,16 @@ TEST_F(RRParamRegistryTest, addError) {
 
 class TestRdataFactory : public AbstractRdataFactory {
 public:
-    using AbstractRdataFactory::create;
     virtual RdataPtr create(const string& rdata_str) const
     { return (RdataPtr(new in::A(rdata_str))); }
     virtual RdataPtr create(InputBuffer& buffer, size_t rdata_len) const
     { return (RdataPtr(new in::A(buffer, rdata_len))); }
     virtual RdataPtr create(const Rdata& source) const
     { return (RdataPtr(new in::A(dynamic_cast<const in::A&>(source)))); }
+    virtual RdataPtr create(MasterLexer& lexer, const Name* origin,
+                            MasterLoader::Options options,
+                            MasterLoaderCallbacks& callbacks) const
+    { return (RdataPtr(new in::A(lexer, origin, options, callbacks))); }
 };
 
 TEST_F(RRParamRegistryTest, addRemoveFactory) {