BIND 10 master, updated. 678ce7c7ba26b9728ae3a870896e8c880a618918 [master] include notify's source IP in ZONEMGR_UNKNOWN_ZONE_NOTIFIED log msg.

BIND 10 source code commits bind10-changes at lists.isc.org
Fri Dec 7 01:51:23 UTC 2012


The branch, master has been updated
       via  678ce7c7ba26b9728ae3a870896e8c880a618918 (commit)
       via  303f3a44c2676bba5248d2e17b93823ac127c7c4 (commit)
       via  c366a4f8deb99014add34a7c79715beaeb1d63f3 (commit)
       via  ea88e8907ab138e2fa1c5271c8a03ec53b4b08d0 (commit)
       via  05fc975a3d825a10c8b9c8df509854f9f69d9c87 (commit)
       via  9002ffc08e7c7fba799e8b417a1d80b77a20973e (commit)
       via  de92f31ea5fc5991e10bc61c175373b67f78deab (commit)
       via  20ca327e9bcccb2e83ed437e7d1dfc9fa6d8fb3c (commit)
       via  ec4fcc79563b93bfc1e300c46287d8f102261d68 (commit)
       via  8201bad569bfda39027198bd920e8da6b8af9539 (commit)
       via  435309b12de7683b06fa29d0b2b9a0bb57fbd9ef (commit)
       via  59861491526ae8b4f61f94e8ad4a6926178d9aea (commit)
       via  9bdc1899a220d369cb7ca1b86fd9a2ddde44eeb9 (commit)
       via  1780e7b66ee62456880ac5ed0d93c9830f233163 (commit)
       via  e4036de3cb6229d38270225eff7a00f5c07a4573 (commit)
       via  7b148d962b1d211eab660ac79e6dcb37e1aa8831 (commit)
       via  c0d55602e3304a95d7956b401ab1ce6c55d11dc1 (commit)
       via  9f8aabb7951987f3e247ad4263e259d82211e5b3 (commit)
       via  5819758217d6e95348c722bafe0f79c1e28ee43a (commit)
       via  6edf83daa67ac95bb45c2975e0511a830e64ac59 (commit)
       via  31dd54a026b5427979a932f0735bacf8723ec7cb (commit)
       via  2c9904e1543d72dac392f1592580d9f8351f5e51 (commit)
       via  8da8a38bcf022e5ad4d99a64be6cda1e557c5692 (commit)
       via  5911431e8fcb1728b1a4765a6f3c1352c4879e8c (commit)
       via  5f03da43d3b7cc5225b64f085320d501c8e1cfde (commit)
       via  3ee8c9cdab87054662af5a67993c9a3ced593256 (commit)
       via  c0f25aade83aaf90bef0abd8bd7eff66cdaa7684 (commit)
       via  3666f50858de194cbca5f0e3e8d9fba747bb3250 (commit)
       via  2abaf1a0335fc470556c2506a9dc7ff002cca8d0 (commit)
      from  181226ac0ced78a1092c14d54764828637a4d814 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 678ce7c7ba26b9728ae3a870896e8c880a618918
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Thu Dec 6 17:50:53 2012 -0800

    [master] include notify's source IP in ZONEMGR_UNKNOWN_ZONE_NOTIFIED log msg.
    
    okayed on jabber.

commit 303f3a44c2676bba5248d2e17b93823ac127c7c4
Merge: 181226a c366a4f
Author: JINMEI Tatuya <jinmei at isc.org>
Date:   Thu Dec 6 17:50:27 2012 -0800

    [master] Merge branch 'trac2442'
    with fixing Conflicts for:
    	src/lib/dns/gen-rdatacode.py.in
    	src/lib/dns/tests/rdata_txt_like_unittest.cc

-----------------------------------------------------------------------

Summary of changes:
 src/bin/auth/tests/auth_srv_unittest.cc         |    2 +-
 src/bin/zonemgr/zonemgr.py.in                   |    3 +-
 src/bin/zonemgr/zonemgr_messages.mes            |    2 +-
 src/lib/dns/Makefile.am                         |    4 +
 src/lib/dns/gen-rdatacode.py.in                 |    3 +-
 src/lib/dns/python/tests/rdata_python_test.py   |    2 +-
 src/lib/dns/rdata/generic/detail/char_string.cc |   98 +++++++++++
 src/lib/dns/rdata/generic/detail/char_string.h  |   63 +++++++
 src/lib/dns/rdata/generic/detail/txt_like.h     |  123 +++++++------
 src/lib/dns/rdata/generic/spf_99.cc             |   26 ++-
 src/lib/dns/rdata/generic/spf_99.h              |    4 +-
 src/lib/dns/rdata/generic/txt_16.cc             |   18 +-
 src/lib/dns/rdata/generic/txt_16.h              |    4 +-
 src/lib/dns/tests/Makefile.am                   |    1 +
 src/lib/dns/tests/rdata_char_string_unittest.cc |  147 ++++++++++++++++
 src/lib/dns/tests/rdata_txt_like_unittest.cc    |  212 ++++++++++++++++++-----
 src/lib/dns/tests/testdata/rdata_txt_fromWire1  |    6 +-
 src/lib/util/python/gen_wiredata.py.in          |    2 +-
 18 files changed, 613 insertions(+), 107 deletions(-)
 create mode 100644 src/lib/dns/rdata/generic/detail/char_string.cc
 create mode 100644 src/lib/dns/rdata/generic/detail/char_string.h
 create mode 100644 src/lib/dns/tests/rdata_char_string_unittest.cc

-----------------------------------------------------------------------
diff --git a/src/bin/auth/tests/auth_srv_unittest.cc b/src/bin/auth/tests/auth_srv_unittest.cc
index 8015043..7f89fda 100644
--- a/src/bin/auth/tests/auth_srv_unittest.cc
+++ b/src/bin/auth/tests/auth_srv_unittest.cc
@@ -225,7 +225,7 @@ createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
     message.setHeaderFlag(Message::HEADERFLAG_AA);
     RRsetPtr rrset_version = RRsetPtr(new RRset(version_name, RRClass::CH(),
                                                 RRType::TXT(), RRTTL(0)));
-    rrset_version->addRdata(generic::TXT(PACKAGE_STRING));
+    rrset_version->addRdata(generic::TXT("\"" PACKAGE_STRING "\""));
     message.addRRset(Message::SECTION_ANSWER, rrset_version);
 
     RRsetPtr rrset_version_ns = RRsetPtr(new RRset(apex_name, RRClass::CH(),
diff --git a/src/bin/zonemgr/zonemgr.py.in b/src/bin/zonemgr/zonemgr.py.in
index 8cb616d..86f89fa 100755
--- a/src/bin/zonemgr/zonemgr.py.in
+++ b/src/bin/zonemgr/zonemgr.py.in
@@ -193,7 +193,8 @@ class ZonemgrRefresh:
     def zone_handle_notify(self, zone_name_class, master):
         """Handle zone notify"""
         if (self._zone_not_exist(zone_name_class)):
-            logger.error(ZONEMGR_UNKNOWN_ZONE_NOTIFIED, zone_name_class[0], zone_name_class[1])
+            logger.error(ZONEMGR_UNKNOWN_ZONE_NOTIFIED, zone_name_class[0],
+                         zone_name_class[1], master)
             raise ZonemgrException("[b10-zonemgr] Notified zone (%s, %s) "
                                    "doesn't belong to zonemgr" % zone_name_class)
         self._set_zone_notifier_master(zone_name_class, master)
diff --git a/src/bin/zonemgr/zonemgr_messages.mes b/src/bin/zonemgr/zonemgr_messages.mes
index 88f8dcf..4f58271 100644
--- a/src/bin/zonemgr/zonemgr_messages.mes
+++ b/src/bin/zonemgr/zonemgr_messages.mes
@@ -138,7 +138,7 @@ zone, or, if this error appears without the administrator giving transfer
 commands, it can indicate an error in the program, as it should not have
 initiated transfers of unknown zones on its own.
 
-% ZONEMGR_UNKNOWN_ZONE_NOTIFIED notified zone %1 (class %2) is not known to the zone manager
+% ZONEMGR_UNKNOWN_ZONE_NOTIFIED notified zone %1/%2 from %3 is not known to the zone manager
 A NOTIFY was received but the zone that was the subject of the operation
 is not being managed by the zone manager.  This may indicate an error
 in the program (as the operation should not have been initiated if this
diff --git a/src/lib/dns/Makefile.am b/src/lib/dns/Makefile.am
index a611cb2..15f3421 100644
--- a/src/lib/dns/Makefile.am
+++ b/src/lib/dns/Makefile.am
@@ -21,6 +21,8 @@ EXTRA_DIST += rdata/ch_3/a_1.cc
 EXTRA_DIST += rdata/ch_3/a_1.h
 EXTRA_DIST += rdata/generic/cname_5.cc
 EXTRA_DIST += rdata/generic/cname_5.h
+EXTRA_DIST += rdata/generic/detail/char_string.cc
+EXTRA_DIST += rdata/generic/detail/char_string.h
 EXTRA_DIST += rdata/generic/detail/nsec_bitmap.cc
 EXTRA_DIST += rdata/generic/detail/nsec_bitmap.h
 EXTRA_DIST += rdata/generic/detail/nsec3param_common.cc
@@ -123,6 +125,8 @@ libb10_dns___la_SOURCES += tsigrecord.h tsigrecord.cc
 libb10_dns___la_SOURCES += character_string.h character_string.cc
 libb10_dns___la_SOURCES += master_loader_callbacks.h
 libb10_dns___la_SOURCES += master_loader.h
+libb10_dns___la_SOURCES += rdata/generic/detail/char_string.h
+libb10_dns___la_SOURCES += rdata/generic/detail/char_string.cc
 libb10_dns___la_SOURCES += rdata/generic/detail/nsec_bitmap.h
 libb10_dns___la_SOURCES += rdata/generic/detail/nsec_bitmap.cc
 libb10_dns___la_SOURCES += rdata/generic/detail/nsec3param_common.cc
diff --git a/src/lib/dns/gen-rdatacode.py.in b/src/lib/dns/gen-rdatacode.py.in
index 72b7674..2bf9de6 100755
--- a/src/lib/dns/gen-rdatacode.py.in
+++ b/src/lib/dns/gen-rdatacode.py.in
@@ -32,7 +32,8 @@ import sys
 #
 # Example:
 #     new_rdata_factory_users = [('a', 'in'), ('a', 'ch'), ('soa', 'generic')]
-new_rdata_factory_users = [('aaaa', 'in')]
+new_rdata_factory_users = [('aaaa', 'in'), ('txt', 'generic'),
+                           ('spf', 'generic')]
 
 re_typecode = re.compile('([\da-z]+)_(\d+)')
 classcode2txt = {}
diff --git a/src/lib/dns/python/tests/rdata_python_test.py b/src/lib/dns/python/tests/rdata_python_test.py
index 81dea5f..3b8f9ad 100644
--- a/src/lib/dns/python/tests/rdata_python_test.py
+++ b/src/lib/dns/python/tests/rdata_python_test.py
@@ -38,7 +38,7 @@ class RdataTest(unittest.TestCase):
         self.assertRaises(InvalidRdataText, Rdata, RRType("A"), RRClass("IN"),
                           "Invalid Rdata Text")
         self.assertRaises(CharStringTooLong, Rdata, RRType("TXT"),
-                          RRClass("IN"), ' ' * 256)
+                          RRClass("IN"), 'x' * 256)
         self.assertRaises(InvalidRdataLength, Rdata, RRType("TXT"),
                           RRClass("IN"), bytes(65536))
         self.assertRaises(DNSMessageFORMERR, Rdata, RRType("TXT"),
diff --git a/src/lib/dns/rdata/generic/detail/char_string.cc b/src/lib/dns/rdata/generic/detail/char_string.cc
new file mode 100644
index 0000000..fb4c9b4
--- /dev/null
+++ b/src/lib/dns/rdata/generic/detail/char_string.cc
@@ -0,0 +1,98 @@
+// Copyright (C) 2012  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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <exceptions/exceptions.h>
+
+#include <dns/rdata.h>
+#include <dns/master_lexer.h>
+#include <dns/rdata/generic/detail/char_string.h>
+
+#include <boost/lexical_cast.hpp>
+
+#include <cassert>
+#include <cctype>
+#include <cstring>
+#include <vector>
+
+#include <stdint.h>
+
+namespace isc {
+namespace dns {
+namespace rdata {
+namespace generic {
+namespace detail {
+
+namespace {
+// Convert a DDD form to the corresponding integer
+int
+decimalToNumber(const char* s, const char* s_end) {
+    if (s_end - s < 3) {
+        isc_throw(InvalidRdataText, "Escaped digits too short");
+    }
+
+    const std::string num_str(s, s + 3);
+    try {
+        const int i = boost::lexical_cast<int>(num_str);
+        if (i > 255) {
+            isc_throw(InvalidRdataText, "Escaped digits too large: "
+                      << num_str);
+        }
+        return (i);
+    } catch (const boost::bad_lexical_cast&) {
+        isc_throw(InvalidRdataText,
+                  "Invalid form for escaped digits: " << num_str);
+    }
+}
+}
+
+void
+strToCharString(const MasterToken::StringRegion& str_region,
+                CharString& result)
+{
+    // make a space for the 1-byte length field; filled in at the end
+    result.push_back(0);
+
+    bool escape = false;
+    const char* s = str_region.beg;
+    const char* const s_end = str_region.beg + str_region.len;
+
+    for (size_t n = str_region.len; n != 0; --n, ++s) {
+        int c = (*s & 0xff);
+        if (escape && std::isdigit(c) != 0) {
+            c = decimalToNumber(s, s_end);
+            assert(n >= 3);
+            n -= 2;
+            s += 2;
+        } else if (!escape && c == '\\') {
+            escape = true;
+            continue;
+        }
+        escape = false;
+        result.push_back(c);
+    }
+    if (escape) {               // terminated by non-escaped '\'
+        isc_throw(InvalidRdataText, "character-string ends with '\\'");
+    }
+    if (result.size() > MAX_CHARSTRING_LEN + 1) { // '+ 1' due to the len field
+        isc_throw(CharStringTooLong, "character-string is too long: " <<
+                  (result.size() - 1) << "(+1) characters");
+    }
+    result[0] = result.size() - 1;
+}
+
+} // end of detail
+} // end of generic
+} // end of rdata
+} // end of dns
+} // end of isc
diff --git a/src/lib/dns/rdata/generic/detail/char_string.h b/src/lib/dns/rdata/generic/detail/char_string.h
new file mode 100644
index 0000000..702af04
--- /dev/null
+++ b/src/lib/dns/rdata/generic/detail/char_string.h
@@ -0,0 +1,63 @@
+// Copyright (C) 2012  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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#ifndef DNS_RDATA_CHARSTRING_H
+#define DNS_RDATA_CHARSTRING_H 1
+
+#include <dns/master_lexer.h>
+
+#include <vector>
+#include <stdint.h>
+
+namespace isc {
+namespace dns {
+namespace rdata {
+namespace generic {
+namespace detail {
+
+/// \brief Type for DNS character string.
+///
+/// A character string can contain any unsigned 8-bit value, so this cannot
+/// be the bare char basis.
+typedef std::vector<uint8_t> CharString;
+
+/// \brief Convert a DNS character-string into corresponding binary data.
+///
+/// This helper function takes a string object that is expected to be a
+/// textual representation of a valid DNS character-string, and dumps
+/// the corresponding binary sequence in the given placeholder (passed
+/// via the \c result parameter).  It handles escape notations of
+/// character-strings with a backslash ('\'), and checks the length
+/// restriction.
+///
+/// \throw CharStringTooLong The resulting binary data are too large for a
+/// valid character-string.
+/// \throw InvalidRdataText Other syntax errors.
+///
+/// \brief str_region A string that represents a character-string.
+/// \brief result A placeholder vector where the resulting data are to be
+/// stored.  Expected to be empty, but it's not checked.
+void strToCharString(const MasterToken::StringRegion& str_region,
+                     CharString& result);
+
+} // namespace detail
+} // namespace generic
+} // namespace rdata
+} // namespace dns
+} // namespace isc
+#endif  // DNS_RDATA_CHARSTRING_H
+
+// Local Variables:
+// mode: c++
+// End:
diff --git a/src/lib/dns/rdata/generic/detail/txt_like.h b/src/lib/dns/rdata/generic/detail/txt_like.h
index fdab6bf..d1916e3 100644
--- a/src/lib/dns/rdata/generic/detail/txt_like.h
+++ b/src/lib/dns/rdata/generic/detail/txt_like.h
@@ -15,13 +15,20 @@
 #ifndef TXT_LIKE_H
 #define TXT_LIKE_H 1
 
+#include <dns/master_lexer.h>
+#include <dns/rdata/generic/detail/char_string.h>
+
 #include <stdint.h>
 
 #include <string>
+#include <sstream>
 #include <vector>
 
-using namespace std;
-using namespace isc::util;
+namespace isc {
+namespace dns {
+namespace rdata {
+namespace generic {
+namespace detail {
 
 /// \brief \c rdata::TXTLikeImpl class represents the TXT-like RDATA for TXT
 /// and SPF types.
@@ -41,7 +48,7 @@ public:
     ///
     /// \c InvalidRdataLength is thrown if rdata_len exceeds the maximum.
     /// \c DNSMessageFORMERR is thrown if the RR is misformed.
-    TXTLikeImpl(InputBuffer& buffer, size_t rdata_len) {
+    TXTLikeImpl(util::InputBuffer& buffer, size_t rdata_len) {
         if (rdata_len > MAX_RDLENGTH) {
             isc_throw(InvalidRdataLength, "RDLENGTH too large: " << rdata_len);
         }
@@ -59,7 +66,7 @@ public:
                           " RDATA: character string length is too large: " <<
                           static_cast<int>(len));
             }
-            vector<uint8_t> data(len + 1);
+            std::vector<uint8_t> data(len + 1);
             data[0] = len;
             buffer.readData(&data[0] + 1, len);
             string_list_.push_back(data);
@@ -70,46 +77,61 @@ public:
 
     /// \brief Constructor from string.
     ///
-    /// <b>Exceptions</b>
-    ///
-    /// \c CharStringTooLong is thrown if the parameter string length exceeds
-    /// maximum.
-    /// \c InvalidRdataText is thrown if the method cannot process the
-    /// parameter data.
+    /// \throw CharStringTooLong the parameter string length exceeds maximum.
+    /// \throw InvalidRdataText the method cannot process the parameter data
     explicit TXTLikeImpl(const std::string& txtstr) {
-        // TBD: this is a simple, incomplete implementation that only supports
-        // a single character-string.
+        std::istringstream ss(txtstr);
+        MasterLexer lexer;
+        lexer.pushSource(ss);
+
+        try {
+            buildFromTextHelper(lexer);
+            if (lexer.getNextToken().getType() != MasterToken::END_OF_FILE) {
+                isc_throw(InvalidRdataText, "Failed to construct " <<
+                          RRType(typeCode) << " RDATA from '" << txtstr <<
+                          "': extra new line");
+            }
+        } catch (const MasterLexer::LexerError& ex) {
+            isc_throw(InvalidRdataText, "Failed to construct " <<
+                      RRType(typeCode) << " RDATA from '" << txtstr << "': "
+                      << ex.what());
+        }
+    }
 
-        size_t length = txtstr.size();
-        size_t pos_begin = 0;
+    /// \brief Constructor using the master lexer.
+    ///
+    /// \throw CharStringTooLong the parameter string length exceeds maximum.
+    /// \throw InvalidRdataText the method cannot process the parameter data
+    ///
+    /// \param lexer A \c MasterLexer object parsing a master file for this
+    /// RDATA.
+    TXTLikeImpl(MasterLexer& lexer) {
+        buildFromTextHelper(lexer);
+    }
 
-        if (length > 1 && txtstr[0] == '"' && txtstr[length - 1] == '"') {
-            pos_begin = 1;
-            length -= 2;
+private:
+    void buildFromTextHelper(MasterLexer& lexer) {
+        while (true) {
+            const MasterToken& token = lexer.getNextToken(
+                MasterToken::QSTRING, true);
+            if (token.getType() != MasterToken::STRING &&
+                token.getType() != MasterToken::QSTRING) {
+                break;
+            }
+            string_list_.push_back(std::vector<uint8_t>());
+            strToCharString(token.getStringRegion(), string_list_.back());
         }
 
-        if (length > MAX_CHARSTRING_LEN) {
-            isc_throw(CharStringTooLong, RRType(typeCode) <<
-                      " RDATA construction from text:"
-                      " string length is too long: " << length);
-        }
+        // Let upper layer handle eol/eof.
+        lexer.ungetToken();
 
-        // TBD: right now, we don't support escaped characters
-        if (txtstr.find('\\') != string::npos) {
-            isc_throw(InvalidRdataText, RRType(typeCode) <<
-                      " RDATA from text:"
-                      " escaped character is currently not supported: " <<
-                      txtstr);
+        if (string_list_.empty()) {
+            isc_throw(InvalidRdataText, "Failed to construct" <<
+                      RRType(typeCode) << " RDATA: empty input");
         }
-
-        vector<uint8_t> data;
-        data.reserve(length + 1);
-        data.push_back(length);
-        data.insert(data.end(), txtstr.begin() + pos_begin,
-                    txtstr.begin() + pos_begin + length);
-        string_list_.push_back(data);
     }
 
+public:
     /// \brief The copy constructor.
     ///
     /// Trivial for now, we could've used the default one.
@@ -122,9 +144,9 @@ public:
     ///
     /// \param buffer An output buffer to store the wire data.
     void
-    toWire(OutputBuffer& buffer) const {
-        for (vector<vector<uint8_t> >::const_iterator it =
-                                                          string_list_.begin();
+    toWire(util::OutputBuffer& buffer) const {
+        for (std::vector<std::vector<uint8_t> >::const_iterator it =
+                 string_list_.begin();
              it != string_list_.end();
              ++it)
         {
@@ -139,8 +161,8 @@ public:
     /// to.
     void
     toWire(AbstractMessageRenderer& renderer) const {
-        for (vector<vector<uint8_t> >::const_iterator it =
-                                                          string_list_.begin();
+        for (std::vector<std::vector<uint8_t> >::const_iterator it =
+                 string_list_.begin();
              it != string_list_.end();
              ++it)
         {
@@ -151,14 +173,14 @@ public:
     /// \brief Convert the TXT-like data to a string.
     ///
     /// \return A \c string object that represents the TXT-like data.
-    string
+    std::string
     toText() const {
-        string s;
+        std::string s;
 
         // XXX: this implementation is not entirely correct.  for example, it
         // should escape double-quotes if they appear in the character string.
-        for (vector<vector<uint8_t> >::const_iterator it =
-                                                          string_list_.begin();
+        for (std::vector<std::vector<uint8_t> >::const_iterator it =
+                 string_list_.begin();
              it != string_list_.end();
              ++it)
         {
@@ -189,7 +211,7 @@ public:
         OutputBuffer this_buffer(0);
         toWire(this_buffer);
         uint8_t const* const this_data = (uint8_t const*)this_buffer.getData();
-        size_t this_len = this_buffer.getLength();
+        const size_t this_len = this_buffer.getLength();
 
         OutputBuffer other_buffer(0);
         other.toWire(other_buffer);
@@ -214,11 +236,14 @@ private:
     std::vector<std::vector<uint8_t> > string_list_;
 };
 
-// END_RDATA_NAMESPACE
-// END_ISC_NAMESPACE
+}      // namespace detail
+}      // namespace generic
+}      // namespace rdata
+}      // namespace dns
+}      // namespace isc
 
 #endif //  TXT_LIKE_H
 
-// Local Variables: 
+// Local Variables:
 // mode: c++
-// End: 
+// End:
diff --git a/src/lib/dns/rdata/generic/spf_99.cc b/src/lib/dns/rdata/generic/spf_99.cc
index aa3e4a1..4bf24e9 100644
--- a/src/lib/dns/rdata/generic/spf_99.cc
+++ b/src/lib/dns/rdata/generic/spf_99.cc
@@ -24,18 +24,17 @@
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
 
+/// This class implements the basic interfaces inherited from the abstract
+/// \c rdata::Rdata class. The semantics of the class is provided by
+/// a copy of instantiated TXTLikeImpl class common to both TXT and SPF.
+#include <dns/rdata/generic/detail/txt_like.h>
+
 using namespace std;
 using namespace isc::util;
 
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
 
-/// This class implements the basic interfaces inherited from the abstract
-/// \c rdata::Rdata class. The semantics of the class is provided by
-/// a copy of instantiated TXTLikeImpl class common to both TXT and SPF.
-
-#include <dns/rdata/generic/detail/txt_like.h>
-
 /// \brief The assignment operator
 ///
 /// It internally allocates a resource, and if it fails a corresponding
@@ -67,6 +66,21 @@ SPF::SPF(InputBuffer& buffer, size_t rdata_len) :
     impl_(new SPFImpl(buffer, rdata_len))
 {}
 
+/// \brief Constructor using the master lexer.
+///
+/// This implementation only uses the \c lexer parameters; others are
+/// ignored.
+///
+/// \throw CharStringTooLong the parameter string length exceeds maximum.
+/// \throw InvalidRdataText the method cannot process the parameter data
+///
+/// \param lexer A \c MasterLexer object parsing a master file for this
+/// RDATA.
+SPF::SPF(MasterLexer& lexer, const Name*, MasterLoader::Options,
+         MasterLoaderCallbacks&) :
+    impl_(new SPFImpl(lexer))
+{}
+
 /// \brief Constructor from string.
 ///
 /// It internally allocates a resource, and if it fails a corresponding
diff --git a/src/lib/dns/rdata/generic/spf_99.h b/src/lib/dns/rdata/generic/spf_99.h
index 04ac99b..eece47b 100644
--- a/src/lib/dns/rdata/generic/spf_99.h
+++ b/src/lib/dns/rdata/generic/spf_99.h
@@ -28,7 +28,9 @@
 
 // BEGIN_RDATA_NAMESPACE
 
+namespace detail {
 template<class Type, uint16_t typeCode> class TXTLikeImpl;
+}
 
 /// \brief \c rdata::SPF class represents the SPF RDATA as defined %in
 /// RFC4408.
@@ -65,7 +67,7 @@ public:
     const std::vector<std::vector<uint8_t> >& getString() const;
 
 private:
-    typedef TXTLikeImpl<SPF, 99> SPFImpl;
+    typedef isc::dns::rdata::generic::detail::TXTLikeImpl<SPF, 99> SPFImpl;
     SPFImpl* impl_;
 };
 
diff --git a/src/lib/dns/rdata/generic/txt_16.cc b/src/lib/dns/rdata/generic/txt_16.cc
index 418bc05..1bd2eb1 100644
--- a/src/lib/dns/rdata/generic/txt_16.cc
+++ b/src/lib/dns/rdata/generic/txt_16.cc
@@ -23,6 +23,7 @@
 #include <dns/messagerenderer.h>
 #include <dns/rdata.h>
 #include <dns/rdataclass.h>
+#include <dns/rdata/generic/detail/txt_like.h>
 
 using namespace std;
 using namespace isc::util;
@@ -30,8 +31,6 @@ using namespace isc::util;
 // BEGIN_ISC_NAMESPACE
 // BEGIN_RDATA_NAMESPACE
 
-#include <dns/rdata/generic/detail/txt_like.h>
-
 TXT&
 TXT::operator=(const TXT& source) {
     if (impl_ == source.impl_) {
@@ -53,6 +52,21 @@ TXT::TXT(InputBuffer& buffer, size_t rdata_len) :
     impl_(new TXTImpl(buffer, rdata_len))
 {}
 
+/// \brief Constructor using the master lexer.
+///
+/// This implementation only uses the \c lexer parameters; others are
+/// ignored.
+///
+/// \throw CharStringTooLong the parameter string length exceeds maximum.
+/// \throw InvalidRdataText the method cannot process the parameter data
+///
+/// \param lexer A \c MasterLexer object parsing a master file for this
+/// RDATA.
+TXT::TXT(MasterLexer& lexer, const Name*, MasterLoader::Options,
+         MasterLoaderCallbacks&) :
+    impl_(new TXTImpl(lexer))
+{}
+
 TXT::TXT(const std::string& txtstr) :
     impl_(new TXTImpl(txtstr))
 {}
diff --git a/src/lib/dns/rdata/generic/txt_16.h b/src/lib/dns/rdata/generic/txt_16.h
index d99d69b..e434085 100644
--- a/src/lib/dns/rdata/generic/txt_16.h
+++ b/src/lib/dns/rdata/generic/txt_16.h
@@ -28,7 +28,9 @@
 
 // BEGIN_RDATA_NAMESPACE
 
+namespace detail {
 template<class Type, uint16_t typeCode> class TXTLikeImpl;
+}
 
 class TXT : public Rdata {
 public:
@@ -39,7 +41,7 @@ public:
     ~TXT();
 
 private:
-    typedef TXTLikeImpl<TXT, 16> TXTImpl;
+    typedef isc::dns::rdata::generic::detail::TXTLikeImpl<TXT, 16> TXTImpl;
     TXTImpl* impl_;
 };
 
diff --git a/src/lib/dns/tests/Makefile.am b/src/lib/dns/tests/Makefile.am
index 12f8f2f..d5690d4 100644
--- a/src/lib/dns/tests/Makefile.am
+++ b/src/lib/dns/tests/Makefile.am
@@ -36,6 +36,7 @@ run_unittests_SOURCES += opcode_unittest.cc
 run_unittests_SOURCES += rcode_unittest.cc
 run_unittests_SOURCES += rdata_unittest.h rdata_unittest.cc
 run_unittests_SOURCES += rdatafields_unittest.cc
+run_unittests_SOURCES += rdata_char_string_unittest.cc
 run_unittests_SOURCES += rdata_in_a_unittest.cc rdata_in_aaaa_unittest.cc
 run_unittests_SOURCES += rdata_ns_unittest.cc rdata_soa_unittest.cc
 run_unittests_SOURCES += rdata_txt_like_unittest.cc
diff --git a/src/lib/dns/tests/rdata_char_string_unittest.cc b/src/lib/dns/tests/rdata_char_string_unittest.cc
new file mode 100644
index 0000000..9d23622
--- /dev/null
+++ b/src/lib/dns/tests/rdata_char_string_unittest.cc
@@ -0,0 +1,147 @@
+// Copyright (C) 2012  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
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#include <util/unittests/wiredata.h>
+
+#include <dns/rdata.h>
+#include <dns/rdata/generic/detail/char_string.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using isc::dns::rdata::generic::detail::CharString;
+using isc::dns::rdata::generic::detail::strToCharString;
+using isc::util::unittests::matchWireData;
+
+namespace {
+const uint8_t test_charstr[] = {
+    sizeof("Test String") - 1,
+    'T', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g'
+};
+
+class CharStringTest : public ::testing::Test {
+protected:
+    CharStringTest() :
+        // char-string representation for test data using two types of escape
+        // ('r' = 114)
+        test_str("Test\\ St\\114ing")
+    {
+        str_region.beg = &test_str[0];
+        str_region.len = test_str.size();
+    }
+    CharString chstr;           // place holder
+    const std::string test_str;
+    MasterToken::StringRegion str_region;
+};
+
+MasterToken::StringRegion
+createStringRegion(const std::string& str) {
+    MasterToken::StringRegion region;
+    region.beg = &str[0]; // note std ensures this works even if str is empty
+    region.len = str.size();
+    return (region);
+}
+
+TEST_F(CharStringTest, normalConversion) {
+    uint8_t tmp[3];             // placeholder for expected sequence
+
+    strToCharString(str_region, chstr);
+    matchWireData(test_charstr, sizeof(test_charstr), &chstr[0], chstr.size());
+
+    // Empty string
+    chstr.clear();
+    strToCharString(createStringRegion(""), chstr);
+    tmp[0] = 0;
+    matchWireData(tmp, 1, &chstr[0], chstr.size());
+
+    // Possible largest char string
+    chstr.clear();
+    std::string long_str(255, 'x');
+    strToCharString(createStringRegion(long_str), chstr);
+    std::vector<uint8_t> expected;
+    expected.push_back(255);    // len of char string
+    expected.insert(expected.end(), long_str.begin(), long_str.end());
+    matchWireData(&expected[0], expected.size(), &chstr[0], chstr.size());
+
+    // Same data as the previous case, but the original string is longer than
+    // the max; this shouldn't be rejected
+    chstr.clear();
+    long_str.at(254) = '\\';    // replace the last 'x' with '\'
+    long_str.append("120");     // 'x' = 120
+    strToCharString(createStringRegion(long_str), chstr);
+    matchWireData(&expected[0], expected.size(), &chstr[0], chstr.size());
+
+    // Escaped '\'
+    chstr.clear();
+    tmp[0] = 1;
+    tmp[1] = '\\';
+    strToCharString(createStringRegion("\\\\"), chstr);
+    matchWireData(tmp, 2, &chstr[0], chstr.size());
+
+    // Boundary values for \DDD
+    chstr.clear();
+    tmp[0] = 1;
+    tmp[1] = 0;
+    strToCharString(createStringRegion("\\000"), chstr);
+    matchWireData(tmp, 2, &chstr[0], chstr.size());
+
+    chstr.clear();
+    strToCharString(createStringRegion("\\255"), chstr);
+    tmp[0] = 1;
+    tmp[1] = 255;
+    matchWireData(tmp, 2, &chstr[0], chstr.size());
+
+    // Another digit follows DDD; it shouldn't cause confusion
+    chstr.clear();
+    strToCharString(createStringRegion("\\2550"), chstr);
+    tmp[0] = 2;                 // string len is now 2
+    tmp[2] = '0';
+    matchWireData(tmp, 3, &chstr[0], chstr.size());
+}
+
+TEST_F(CharStringTest, badConversion) {
+    // string cannot exceed 255 bytes
+    EXPECT_THROW(strToCharString(createStringRegion(std::string(256, 'a')),
+                                 chstr),
+                 CharStringTooLong);
+
+    // input string ending with (non escaped) '\'
+    chstr.clear();
+    EXPECT_THROW(strToCharString(createStringRegion("foo\\"), chstr),
+                 InvalidRdataText);
+}
+
+TEST_F(CharStringTest, badDDD) {
+    // Check various type of bad form of \DDD
+
+    // Not a number
+    EXPECT_THROW(strToCharString(createStringRegion("\\1a2"), chstr),
+                 InvalidRdataText);
+    EXPECT_THROW(strToCharString(createStringRegion("\\12a"), chstr),
+                 InvalidRdataText);
+
+    // Not in the range of uint8_t
+    EXPECT_THROW(strToCharString(createStringRegion("\\256"), chstr),
+                 InvalidRdataText);
+
+    // Short buffer
+    EXPECT_THROW(strToCharString(createStringRegion("\\42"), chstr),
+                 InvalidRdataText);
+}
+
+} // unnamed namespace
diff --git a/src/lib/dns/tests/rdata_txt_like_unittest.cc b/src/lib/dns/tests/rdata_txt_like_unittest.cc
index b894fae..b0a572d 100644
--- a/src/lib/dns/tests/rdata_txt_like_unittest.cc
+++ b/src/lib/dns/tests/rdata_txt_like_unittest.cc
@@ -17,17 +17,25 @@
 #include <util/buffer.h>
 #include <dns/exceptions.h>
 #include <dns/rdataclass.h>
-#include <gtest/gtest.h>
 
 #include <dns/tests/unittest_util.h>
 #include <dns/tests/rdata_unittest.h>
 
+#include <gtest/gtest.h>
+
+#include <boost/bind.hpp>
+
+#include <string>
+#include <sstream>
+#include <vector>
+
 using isc::UnitTestUtil;
 using namespace std;
 using namespace isc::dns;
 using namespace isc::util;
 using namespace isc::dns::rdata;
 
+namespace {
 
 template<class T>
 class RRTYPE : public RRType {
@@ -38,36 +46,42 @@ public:
 template<> RRTYPE<generic::TXT>::RRTYPE() : RRType(RRType::TXT()) {}
 template<> RRTYPE<generic::SPF>::RRTYPE() : RRType(RRType::SPF()) {}
 
-namespace {
 const uint8_t wiredata_txt_like[] = {
-    sizeof("Test String") - 1,
-    'T', 'e', 's', 't', ' ', 'S', 't', 'r', 'i', 'n', 'g'
+    sizeof("Test-String") - 1,
+    'T', 'e', 's', 't', '-', 'S', 't', 'r', 'i', 'n', 'g'
 };
 
 const uint8_t wiredata_nulltxt[] = { 0 };
-vector<uint8_t> wiredata_longesttxt(256, 'a');
+
+// For lexer-based constructor
+void
+dummyCallback(const string&, size_t, const string&) {
+}
 
 template<class TXT_LIKE>
 class Rdata_TXT_LIKE_Test : public RdataTest {
 protected:
-    Rdata_TXT_LIKE_Test() {
+    Rdata_TXT_LIKE_Test() :
+        callback(boost::bind(&dummyCallback, _1, _2, _3)),
+        loader_cb(callback, callback),
+        wiredata_longesttxt(256, 'a'),
+        rdata_txt_like("Test-String"),
+        rdata_txt_like_empty("\"\""),
+        rdata_txt_like_quoted("\"Test-String\"")
+    {
         wiredata_longesttxt[0] = 255; // adjust length
     }
 
-    static const TXT_LIKE rdata_txt_like;
-    static const TXT_LIKE rdata_txt_like_empty;
-    static const TXT_LIKE rdata_txt_like_quoted;
-};
-
-template<class TXT_LIKE>
-const TXT_LIKE Rdata_TXT_LIKE_Test<TXT_LIKE>::rdata_txt_like("Test String");
-
-template<class TXT_LIKE>
-const TXT_LIKE Rdata_TXT_LIKE_Test<TXT_LIKE>::rdata_txt_like_empty("");
+private:
+    const MasterLoaderCallbacks::IssueCallback callback;
 
-template<class TXT_LIKE>
-const TXT_LIKE Rdata_TXT_LIKE_Test<TXT_LIKE>::rdata_txt_like_quoted
-                                                          ("\"Test String\"");
+protected:
+    MasterLoaderCallbacks loader_cb;
+    vector<uint8_t> wiredata_longesttxt;
+    const TXT_LIKE rdata_txt_like;
+    const TXT_LIKE rdata_txt_like_empty;
+    const TXT_LIKE rdata_txt_like_quoted;
+};
 
 // The list of types we want to test.
 typedef testing::Types<generic::TXT, generic::SPF> Implementations;
@@ -75,37 +89,155 @@ typedef testing::Types<generic::TXT, generic::SPF> Implementations;
 TYPED_TEST_CASE(Rdata_TXT_LIKE_Test, Implementations);
 
 TYPED_TEST(Rdata_TXT_LIKE_Test, createFromText) {
-    // normal case is covered in toWireBuffer.
+    // Below we check the behavior for the "from text" constructors, both
+    // from std::string and with MasterLexer.  The underlying implementation
+    // is the same, so both should work exactly same, but we confirm both
+    // cases.
+
+    const std::string multi_line = "(\n \"Test-String\" )";
+    const std::string escaped_txt = "Test\\045Strin\\g";
+
+    // test input for the lexer version
+    std::stringstream ss;
+    ss << "Test-String\n";
+    ss << "\"Test-String\"\n";   // explicitly surrounded by '"'s
+    ss << multi_line << "\n";   // multi-line text with ()
+    ss << escaped_txt << "\n";   // using the two types of escape with '\'
+    ss << "\"\"\n";              // empty string (note: still valid char-str)
+    ss << string(255, 'a') << "\n"; // Longest possible character-string.
+    ss << string(256, 'a') << "\n"; // char-string too long
+    ss << "\"Test-String\\\"\n";    // unbalanced quote
+    ss << "\"Test-String\\\"\"\n";
+    this->lexer.pushSource(ss);
+
+    // commonly used Rdata to compare below, created from wire
+    ConstRdataPtr const rdata =
+        this->rdataFactoryFromFile(RRTYPE<TypeParam>(),
+                                   RRClass("IN"), "rdata_txt_fromWire1");
+
+    // normal case is covered in toWireBuffer.  First check the std::string
+    // case, then with MasterLexer.  For the latter, we need to read and skip
+    // '\n'.  These apply to most of the other cases below.
+    EXPECT_EQ(0, this->rdata_txt_like.compare(*rdata));
+    EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS,
+                           this->loader_cb).compare(*rdata));
+    EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
 
     // surrounding double-quotes shouldn't change the result.
-    EXPECT_EQ(0, this->rdata_txt_like.compare(this->rdata_txt_like_quoted));
+    EXPECT_EQ(0, this->rdata_txt_like_quoted.compare(*rdata));
+    EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS,
+                           this->loader_cb).compare(*rdata));
+    EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
+
+    // multi-line input with ()
+    EXPECT_EQ(0, TypeParam(multi_line).compare(*rdata));
+    EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS,
+                           this->loader_cb).compare(*rdata));
+    EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
+
+    // for the same data using escape
+    EXPECT_EQ(0, TypeParam(escaped_txt).compare(*rdata));
+    EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS,
+                           this->loader_cb).compare(*rdata));
+    EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
 
     // Null character-string.
     this->obuffer.clear();
-    TypeParam(string("")).toWire(this->obuffer);
+    TypeParam(string("\"\"")).toWire(this->obuffer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        this->obuffer.getData(),
-                        this->obuffer.getLength(),
+                        this->obuffer.getData(), this->obuffer.getLength(),
                         wiredata_nulltxt, sizeof(wiredata_nulltxt));
+    this->obuffer.clear();
+    TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, this->loader_cb).
+        toWire(this->obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        this->obuffer.getData(), this->obuffer.getLength(),
+                        wiredata_nulltxt, sizeof(wiredata_nulltxt));
+    EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
 
     // Longest possible character-string.
     this->obuffer.clear();
     TypeParam(string(255, 'a')).toWire(this->obuffer);
     EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
-                        this->obuffer.getData(),
-                        this->obuffer.getLength(),
-                        &wiredata_longesttxt[0], wiredata_longesttxt.size());
+                        this->obuffer.getData(), this->obuffer.getLength(),
+                        &this->wiredata_longesttxt[0],
+                        this->wiredata_longesttxt.size());
+    this->obuffer.clear();
+    TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS, this->loader_cb).
+        toWire(this->obuffer);
+    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
+                        this->obuffer.getData(), this->obuffer.getLength(),
+                        &this->wiredata_longesttxt[0],
+                        this->wiredata_longesttxt.size());
+    EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
 
     // Too long text for a valid character-string.
     EXPECT_THROW(TypeParam(string(256, 'a')), CharStringTooLong);
+    EXPECT_THROW(TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS,
+                           this->loader_cb), CharStringTooLong);
+    EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
 
     // The escape character makes the double quote a part of character-string,
     // so this is invalid input and should be rejected.
-    EXPECT_THROW(TypeParam("\"Test String\\\""), InvalidRdataText);
+    EXPECT_THROW(TypeParam("\"Test-String\\\""), InvalidRdataText);
+    EXPECT_THROW(TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS,
+                           this->loader_cb), MasterLexer::LexerError);
+    EXPECT_EQ(MasterToken::END_OF_LINE, this->lexer.getNextToken().getType());
+}
+
+TYPED_TEST(Rdata_TXT_LIKE_Test, createMultiStringsFromText) {
+    // Tests for "from text" variants construction with various forms of
+    // multi character-strings.
+
+    std::vector<std::string > texts;
+    texts.push_back("\"Test-String\" \"Test-String\""); // most common form
+    texts.push_back("\"Test-String\"\"Test-String\"");  // no space between'em
+    texts.push_back("\"Test-String\" Test-String");  // no '"' for one
+    texts.push_back("\"Test-String\"Test-String"); // and no space either
+    texts.push_back("Test-String \"Test-String\""); // no '"' for the other
+    // This one currently doesn't work
+    //texts.push_back("Test-String\"Test-String\""); // and no space either
+
+    std::stringstream ss;
+    for (std::vector<std::string >::const_iterator it = texts.begin();
+         it != texts.end(); ++it) {
+        ss << *it << "\n";
+    }
+    this->lexer.pushSource(ss);
+
+    // The corresponding Rdata built from wire to compare in the checks below.
+    ConstRdataPtr const rdata =
+        this->rdataFactoryFromFile(RRTYPE<TypeParam>(),
+                                   RRClass("IN"), "rdata_txt_fromWire3.wire");
+
+    // Confirm we can construct the Rdata from the test text, both from
+    // std::string and with lexer, and that matches the from-wire data.
+    for (std::vector<std::string >::const_iterator it = texts.begin();
+         it != texts.end(); ++it) {
+        SCOPED_TRACE(*it);
+        EXPECT_EQ(0, TypeParam(*it).compare(*rdata));
+
+        EXPECT_EQ(0, TypeParam(this->lexer, NULL, MasterLoader::MANY_ERRORS,
+                               this->loader_cb).compare(*rdata));
+        EXPECT_EQ(MasterToken::END_OF_LINE,
+                  this->lexer.getNextToken().getType());
+    }
+}
+
+TYPED_TEST(Rdata_TXT_LIKE_Test, createFromTextExtra) {
+    // This is for the std::string version only: the input must end with EOF;
+    // an extra new-line will result in an exception.
+    EXPECT_THROW(TypeParam("\"Test-String\"\n"), InvalidRdataText);
+    // Same if there's a space before '\n'
+    EXPECT_THROW(TypeParam("\"Test-String\" \n"), InvalidRdataText);
+}
 
-    // Terminating double-quote is provided, so this is valid, but in this
-    // version of implementation we reject escaped characters.
-    EXPECT_THROW(TypeParam("\"Test String\\\"\""), InvalidRdataText);
+TYPED_TEST(Rdata_TXT_LIKE_Test, fromTextEmpty) {
+    // If the input text doesn't contain any character-string, it should be
+    // rejected
+    EXPECT_THROW(TypeParam(""), InvalidRdataText);
+    EXPECT_THROW(TypeParam(" "), InvalidRdataText); // even with a space
+    EXPECT_THROW(TypeParam("(\n)"), InvalidRdataText); // or multi-line with ()
 }
 
 void
@@ -129,13 +261,15 @@ makeLargest(vector<uint8_t>& data) {
 
 TYPED_TEST(Rdata_TXT_LIKE_Test, createFromWire) {
     EXPECT_EQ(0, this->rdata_txt_like.compare(
-                  *this->rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"),
-                                        "rdata_txt_fromWire1")));
+                  *this->rdataFactoryFromFile(RRTYPE<TypeParam>(),
+                                              RRClass("IN"),
+                                              "rdata_txt_fromWire1")));
 
     // Empty character string
     EXPECT_EQ(0, this->rdata_txt_like_empty.compare(
-                  *this->rdataFactoryFromFile(RRTYPE<TypeParam>(), RRClass("IN"),
-                                        "rdata_txt_fromWire2.wire")));
+                  *this->rdataFactoryFromFile(RRTYPE<TypeParam>(),
+                                              RRClass("IN"),
+                                              "rdata_txt_fromWire2.wire")));
 
     // Multiple character strings
     this->obuffer.clear();
@@ -188,7 +322,7 @@ TYPED_TEST(Rdata_TXT_LIKE_Test, createFromWire) {
 TYPED_TEST(Rdata_TXT_LIKE_Test, createFromLexer) {
     EXPECT_EQ(0, this->rdata_txt_like.compare(
         *test::createRdataUsingLexer(RRTYPE<TypeParam>(), RRClass::IN(),
-                                     "Test String")));
+                                     "Test-String")));
 }
 
 TYPED_TEST(Rdata_TXT_LIKE_Test, toWireBuffer) {
@@ -208,7 +342,7 @@ TYPED_TEST(Rdata_TXT_LIKE_Test, toWireRenderer) {
 }
 
 TYPED_TEST(Rdata_TXT_LIKE_Test, toText) {
-    EXPECT_EQ("\"Test String\"", this->rdata_txt_like.toText());
+    EXPECT_EQ("\"Test-String\"", this->rdata_txt_like.toText());
 }
 
 TYPED_TEST(Rdata_TXT_LIKE_Test, assignment) {
@@ -238,8 +372,8 @@ TYPED_TEST(Rdata_TXT_LIKE_Test, compare) {
 
     EXPECT_EQ(TypeParam(txt1).compare(TypeParam(txt1)), 0);
 
-    EXPECT_LT(TypeParam("").compare(TypeParam(txt1)), 0);
-    EXPECT_GT(TypeParam(txt1).compare(TypeParam("")), 0);
+    EXPECT_LT(TypeParam("\"\"").compare(TypeParam(txt1)), 0);
+    EXPECT_GT(TypeParam(txt1).compare(TypeParam("\"\"")), 0);
 
     EXPECT_LT(TypeParam(txt1).compare(TypeParam(txt2)), 0);
     EXPECT_GT(TypeParam(txt2).compare(TypeParam(txt1)), 0);
diff --git a/src/lib/dns/tests/testdata/rdata_txt_fromWire1 b/src/lib/dns/tests/testdata/rdata_txt_fromWire1
index 2c51efe..95980db 100644
--- a/src/lib/dns/tests/testdata/rdata_txt_fromWire1
+++ b/src/lib/dns/tests/testdata/rdata_txt_fromWire1
@@ -1,9 +1,9 @@
 #
 # various kinds of TXT RDATA stored in an input buffer
 #
-# Valid RDATA for "Test String"
+# Valid RDATA for "Test-String"
 #
 # RDLENGHT=12 bytes
  00 0c
-#    T  e  s  t     S  t  r  i  n  g
- 0b 54 65 73 74 20 53 74 72 69 6e 67
+#    T  e  s  t  -  S  t  r  i  n  g
+ 0b 54 65 73 74 2d 53 74 72 69 6e 67
diff --git a/src/lib/util/python/gen_wiredata.py.in b/src/lib/util/python/gen_wiredata.py.in
index f997701..55724c9 100755
--- a/src/lib/util/python/gen_wiredata.py.in
+++ b/src/lib/util/python/gen_wiredata.py.in
@@ -770,7 +770,7 @@ class TXT(RR):
 
     nstring = 1
     stringlen = None
-    string = 'Test String'
+    string = 'Test-String'
 
     def dump(self, f):
         stringlen_list = []



More information about the bind10-changes mailing list