BIND 10 master, updated. f33bdd59c6a8c8ea883f11578b463277d01c2b70 [master] Merge branch 'trac3007'. Adds DHCP-DDNS request message class, NameChangeRequest.

BIND 10 source code commits bind10-changes at lists.isc.org
Tue Jul 2 20:52:34 UTC 2013


The branch, master has been updated
       via  f33bdd59c6a8c8ea883f11578b463277d01c2b70 (commit)
       via  e773dac499b952107dfc5e34290ec421b8bbf7c6 (commit)
       via  1a0742f1fd61234a6ec395729b0c4c4cad64aaac (commit)
       via  e7c52fec35a1f6a49351f58877d21fd43b4082e0 (commit)
       via  20b0efb43cff8020da949df03ce13d58ee6e4eac (commit)
       via  5b0a2957d3a806ce84ad86dd7789edf3ef495d92 (commit)
       via  b4b1eca7131cedca72a8973718881c1f18d2010f (commit)
      from  a3e8b0ad37ebb999e29c390332045bc9babfdc3c (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 f33bdd59c6a8c8ea883f11578b463277d01c2b70
Merge: a3e8b0a e773dac
Author: Thomas Markwalder <tmark at isc.org>
Date:   Tue Jul 2 16:51:40 2013 -0400

    [master] Merge branch 'trac3007'. Adds DHCP-DDNS
    request message class, NameChangeRequest.

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

Summary of changes:
 src/bin/d2/Makefile.am            |    2 +
 src/bin/d2/ncr_msg.cc             |  485 +++++++++++++++++++++++++++++++++++
 src/bin/d2/ncr_msg.h              |  503 +++++++++++++++++++++++++++++++++++++
 src/bin/d2/tests/Makefile.am      |    4 +
 src/bin/d2/tests/ncr_unittests.cc |  428 +++++++++++++++++++++++++++++++
 5 files changed, 1422 insertions(+)
 create mode 100644 src/bin/d2/ncr_msg.cc
 create mode 100644 src/bin/d2/ncr_msg.h
 create mode 100644 src/bin/d2/tests/ncr_unittests.cc

-----------------------------------------------------------------------
diff --git a/src/bin/d2/Makefile.am b/src/bin/d2/Makefile.am
index aa87354..667c11e 100644
--- a/src/bin/d2/Makefile.am
+++ b/src/bin/d2/Makefile.am
@@ -56,6 +56,7 @@ b10_dhcp_ddns_SOURCES += d2_config.cc d2_config.h
 b10_dhcp_ddns_SOURCES += d2_cfg_mgr.cc d2_cfg_mgr.h
 b10_dhcp_ddns_SOURCES += d2_update_message.cc d2_update_message.h
 b10_dhcp_ddns_SOURCES += d2_zone.cc d2_zone.h
+b10_dhcp_ddns_SOURCES += ncr_msg.cc ncr_msg.h
 
 nodist_b10_dhcp_ddns_SOURCES = d2_messages.h d2_messages.cc
 EXTRA_DIST += d2_messages.mes
@@ -67,6 +68,7 @@ b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la 
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
+b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
 
 b10_dhcp_ddnsdir = $(pkgdatadir)
 b10_dhcp_ddns_DATA = dhcp-ddns.spec
diff --git a/src/bin/d2/ncr_msg.cc b/src/bin/d2/ncr_msg.cc
new file mode 100644
index 0000000..c1254cb
--- /dev/null
+++ b/src/bin/d2/ncr_msg.cc
@@ -0,0 +1,485 @@
+// Copyright (C) 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
+// 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 <d2/ncr_msg.h>
+#include <asiolink/io_address.h>
+#include <asiolink/io_error.h>
+
+#include <sstream>
+
+namespace isc {
+namespace d2 {
+
+using namespace boost::posix_time;
+
+/********************************* D2Dhcid ************************************/
+
+D2Dhcid::D2Dhcid() {
+}
+
+D2Dhcid::D2Dhcid(const std::string& data) {
+    fromStr(data);
+}
+
+void
+D2Dhcid::fromStr(const std::string& data) {
+    bytes_.clear();
+    try {
+        isc::util::encode::decodeHex(data, bytes_);
+    } catch (const isc::Exception& ex) {
+        isc_throw(NcrMessageError, "Invalid data in Dhcid:" << ex.what());
+    }
+}
+
+std::string
+D2Dhcid::toStr() const {
+    return (isc::util::encode::encodeHex(bytes_));
+
+
+}
+
+
+/**************************** NameChangeRequest ******************************/
+
+NameChangeRequest::NameChangeRequest()
+    : change_type_(CHG_ADD), forward_change_(false),
+    reverse_change_(false), fqdn_(""), ip_address_(""),
+    dhcid_(), lease_expires_on_(), lease_length_(0), status_(ST_NEW) {
+}
+
+NameChangeRequest::NameChangeRequest(const NameChangeType change_type,
+            const bool forward_change, const bool reverse_change,
+            const std::string& fqdn, const std::string& ip_address,
+            const D2Dhcid& dhcid,
+            const boost::posix_time::ptime& lease_expires_on,
+            const uint32_t lease_length)
+    : change_type_(change_type), forward_change_(forward_change),
+    reverse_change_(reverse_change), fqdn_(fqdn), ip_address_(ip_address),
+    dhcid_(dhcid), lease_expires_on_(new ptime(lease_expires_on)),
+    lease_length_(lease_length), status_(ST_NEW) {
+
+    // Validate the contents. This will throw a NcrMessageError if anything
+    // is invalid.
+    validateContent();
+}
+
+NameChangeRequestPtr
+NameChangeRequest::fromFormat(const NameChangeFormat format,
+                              isc::util::InputBuffer& buffer) {
+    // Based on the format requested, pull the marshalled request from
+    // InputBuffer and pass it into the appropriate format-specific factory.
+    NameChangeRequestPtr ncr;
+    switch (format) {
+    case FMT_JSON: {
+        try {
+            // Get the length of the JSON text.
+            size_t len = buffer.readUint16();
+
+            // Read the text from the buffer into a vector.
+            std::vector<uint8_t> vec;
+            buffer.readVector(vec, len);
+
+            // Turn the vector into a string.
+            std::string string_data(vec.begin(), vec.end());
+
+            // Pass the string of JSON text into JSON factory to create the
+            // NameChangeRequest instance.  Note the factory may throw
+            // NcrMessageError.
+            ncr = NameChangeRequest::fromJSON(string_data);
+        } catch (isc::util::InvalidBufferPosition& ex) {
+            // Read error accessing data in InputBuffer.
+            isc_throw(NcrMessageError, "fromFormat: buffer read error: "
+                      << ex.what());
+        }
+
+        break;
+        }
+    default:
+        // Programmatic error, shouldn't happen.
+        isc_throw(NcrMessageError, "fromFormat - invalid format");
+        break;
+    }
+
+    return (ncr);
+}
+
+void
+NameChangeRequest::toFormat(const NameChangeFormat format,
+                            isc::util::OutputBuffer& buffer) const {
+    // Based on the format requested, invoke the appropriate format handler
+    // which will marshal this request's contents into the OutputBuffer.
+    switch (format) {
+    case FMT_JSON: {
+        // Invoke toJSON to create a JSON text of this request's contents.
+        std::string json = toJSON();
+        uint16_t length = json.size();
+
+        // Write the length of the JSON text to the OutputBuffer first, then
+        // write the JSON text itself.
+        buffer.writeUint16(length);
+        buffer.writeData(json.c_str(), length);
+        break;
+        }
+    default:
+        // Programmatic error, shouldn't happen.
+        isc_throw(NcrMessageError, "toFormat - invalid format");
+        break;
+    }
+}
+
+NameChangeRequestPtr
+NameChangeRequest::fromJSON(const std::string& json) {
+    // This method leverages the existing JSON parsing provided by isc::data
+    // library.  Should this prove to be a performance issue, it may be that
+    // lighter weight solution would be appropriate.
+
+    // Turn the string of JSON text into an Element set.
+    isc::data::ElementPtr elements;
+    try {
+        elements = isc::data::Element::fromJSON(json);
+    } catch (isc::data::JSONError& ex) {
+        isc_throw(NcrMessageError,
+                  "Malformed NameChangeRequest JSON: " << ex.what());
+    }
+
+    // Get a map of the Elements, keyed by element name.
+    ElementMap element_map = elements->mapValue();
+    isc::data::ConstElementPtr element;
+
+    // Use default constructor to create a "blank" NameChangeRequest.
+    NameChangeRequestPtr ncr(new NameChangeRequest());
+
+    // For each member of NameChangeRequest, find its element in the map and
+    // call the appropriate Element-based setter.  These setters may throw
+    // NcrMessageError if the given Element is the wrong type or its data
+    // content is lexically invalid.   If the element is NOT found in the
+    // map, getElement will throw NcrMessageError indicating the missing
+    // member. Currently there are no optional values.
+    element = ncr->getElement("change_type", element_map);
+    ncr->setChangeType(element);
+
+    element = ncr->getElement("forward_change", element_map);
+    ncr->setForwardChange(element);
+
+    element = ncr->getElement("reverse_change", element_map);
+    ncr->setReverseChange(element);
+
+    element = ncr->getElement("fqdn", element_map);
+    ncr->setFqdn(element);
+
+    element = ncr->getElement("ip_address", element_map);
+    ncr->setIpAddress(element);
+
+    element = ncr->getElement("dhcid", element_map);
+    ncr->setDhcid(element);
+
+    element = ncr->getElement("lease_expires_on", element_map);
+    ncr->setLeaseExpiresOn(element);
+
+    element = ncr->getElement("lease_length", element_map);
+    ncr->setLeaseLength(element);
+
+    // All members were in the Element set and were correct lexically. Now
+    // validate the overall content semantically.  This will throw an
+    // NcrMessageError if anything is amiss.
+    ncr->validateContent();
+
+    // Everything is valid, return the new instance.
+    return (ncr);
+}
+
+std::string
+NameChangeRequest::toJSON() const  {
+    // Create a JSON string of this request's contents.  Note that this method
+    // does NOT use the isc::data library as generating the output is straight
+    // forward.
+    std::ostringstream stream;
+
+    stream << "{\"change_type\":" << getChangeType() << ","
+        << "\"forward_change\":"
+        << (isForwardChange() ? "true" : "false") << ","
+        << "\"reverse_change\":"
+        << (isReverseChange() ? "true" : "false") << ","
+        << "\"fqdn\":\"" << getFqdn() << "\","
+        << "\"ip_address\":\"" << getIpAddress() << "\","
+        << "\"dhcid\":\"" << getDhcid().toStr() << "\","
+        << "\"lease_expires_on\":\""  << getLeaseExpiresOnStr() << "\","
+        << "\"lease_length\":" << getLeaseLength() << "}";
+
+    return (stream.str());
+}
+
+
+void
+NameChangeRequest::validateContent() {
+    //@todo This is an initial implementation which provides a minimal amount
+    // of validation.  FQDN, DHCID, and IP Address members are all currently
+    // strings, these may be replaced with richer classes.
+    if (fqdn_ == "") {
+        isc_throw(NcrMessageError, "FQDN cannot be blank");
+    }
+
+    // Validate IP Address.
+    try {
+        isc::asiolink::IOAddress io_addr(ip_address_);
+    } catch (const isc::asiolink::IOError& ex) {
+        isc_throw(NcrMessageError,
+                  "Invalid ip address string for ip_address: " << ip_address_);
+    }
+
+    // Validate the DHCID.
+    if (dhcid_.getBytes().size()  == 0) {
+        isc_throw(NcrMessageError, "DHCID cannot be blank");
+    }
+
+    // Validate lease expiration.
+    if (lease_expires_on_->is_not_a_date_time()) {
+        isc_throw(NcrMessageError, "Invalid value for lease_expires_on");
+    }
+
+    // Ensure the request specifies at least one direction to update.
+    if (!forward_change_ && !reverse_change_) {
+        isc_throw(NcrMessageError,
+                  "Invalid Request, forward and reverse flags are both false");
+    }
+}
+
+isc::data::ConstElementPtr
+NameChangeRequest::getElement(const std::string& name,
+                              const ElementMap& element_map) const {
+    // Look for "name" in the element map.
+    ElementMap::const_iterator it = element_map.find(name);
+    if (it == element_map.end()) {
+        // Didn't find the element, so throw.
+        isc_throw(NcrMessageError,
+                  "NameChangeRequest value missing for: " << name );
+    }
+
+    // Found the element, return it.
+    return (it->second);
+}
+
+void
+NameChangeRequest::setChangeType(const NameChangeType value) {
+    change_type_ = value;
+}
+
+
+void
+NameChangeRequest::setChangeType(isc::data::ConstElementPtr element) {
+    long raw_value = -1;
+    try {
+        // Get the element's integer value.
+        raw_value = element->intValue();
+    } catch (isc::data::TypeError& ex) {
+        // We expect a integer Element type, don't have one.
+        isc_throw(NcrMessageError,
+                  "Wrong data type for change_type: " << ex.what());
+    }
+
+    if ((raw_value != CHG_ADD) && (raw_value != CHG_REMOVE)) {
+        // Value is not a valid change type.
+        isc_throw(NcrMessageError,
+                  "Invalid data value for change_type: " << raw_value);
+    }
+
+    // Good to go, make the assignment.
+    setChangeType(static_cast<NameChangeType>(raw_value));
+}
+
+void
+NameChangeRequest::setForwardChange(const bool value) {
+    forward_change_ = value;
+}
+
+void
+NameChangeRequest::setForwardChange(isc::data::ConstElementPtr element) {
+    bool value;
+    try {
+        // Get the element's boolean value.
+        value = element->boolValue();
+    } catch (isc::data::TypeError& ex) {
+        // We expect a boolean Element type, don't have one.
+        isc_throw(NcrMessageError,
+                  "Wrong data type for forward_change :" << ex.what());
+    }
+
+    // Good to go, make the assignment.
+    setForwardChange(value);
+}
+
+void
+NameChangeRequest::setReverseChange(const bool value) {
+    reverse_change_ = value;
+}
+
+void
+NameChangeRequest::setReverseChange(isc::data::ConstElementPtr element) {
+    bool value;
+    try {
+        // Get the element's boolean value.
+        value = element->boolValue();
+    } catch (isc::data::TypeError& ex) {
+        // We expect a boolean Element type, don't have one.
+        isc_throw(NcrMessageError,
+                  "Wrong data type for reverse_change :" << ex.what());
+    }
+
+    // Good to go, make the assignment.
+    setReverseChange(value);
+}
+
+
+void
+NameChangeRequest::setFqdn(isc::data::ConstElementPtr element) {
+    setFqdn(element->stringValue());
+}
+
+void
+NameChangeRequest::setFqdn(const std::string& value) {
+    fqdn_ = value;
+}
+
+void
+NameChangeRequest::setIpAddress(const std::string& value) {
+    ip_address_ = value;
+}
+
+
+void
+NameChangeRequest::setIpAddress(isc::data::ConstElementPtr element) {
+    setIpAddress(element->stringValue());
+}
+
+
+void
+NameChangeRequest::setDhcid(const std::string& value) {
+    dhcid_.fromStr(value);
+}
+
+void
+NameChangeRequest::setDhcid(isc::data::ConstElementPtr element) {
+    setDhcid(element->stringValue());
+}
+
+std::string
+NameChangeRequest::getLeaseExpiresOnStr() const {
+    if (!lease_expires_on_) {
+        // This is a programmatic error, should not happen.
+        isc_throw(NcrMessageError,
+            "lease_expires_on_ is null, cannot convert to string");
+    }
+
+    // Return the ISO date-time string for the value of lease_expires_on_.
+    return (to_iso_string(*lease_expires_on_));
+}
+
+void
+NameChangeRequest::setLeaseExpiresOn(const std::string&  value) {
+    try {
+        // Create a new ptime instance from the ISO date-time string in value
+        // add assign it to lease_expires_on_.
+        ptime* tptr = new ptime(from_iso_string(value));
+        lease_expires_on_.reset(tptr);
+    } catch(...) {
+        // We were given an invalid string, so throw.
+        isc_throw(NcrMessageError,
+            "Invalid ISO date-time string: [" << value << "]");
+    }
+
+}
+
+void
+NameChangeRequest::setLeaseExpiresOn(const boost::posix_time::ptime&  value) {
+    if (lease_expires_on_->is_not_a_date_time()) {
+        isc_throw(NcrMessageError, "Invalid value for lease_expires_on");
+    }
+
+    // Go to go, make the assignment.
+    lease_expires_on_.reset(new ptime(value));
+}
+
+void NameChangeRequest::setLeaseExpiresOn(isc::data::ConstElementPtr element) {
+    // Pull out the string value and pass it into the string setter.
+    setLeaseExpiresOn(element->stringValue());
+}
+
+void
+NameChangeRequest::setLeaseLength(const uint32_t value) {
+    lease_length_ = value;
+}
+
+void
+NameChangeRequest::setLeaseLength(isc::data::ConstElementPtr element) {
+    long value = -1;
+    try {
+        // Get the element's integer value.
+        value = element->intValue();
+    } catch (isc::data::TypeError& ex) {
+        // We expect a integer Element type, don't have one.
+        isc_throw(NcrMessageError,
+                  "Wrong data type for lease_length: " << ex.what());
+    }
+
+    // Make sure we the range is correct and value is positive.
+    if (value > std::numeric_limits<uint32_t>::max()) {
+        isc_throw(NcrMessageError, "lease_length value " << value <<
+                "is too large for unsigned 32-bit integer.");
+    }
+    if (value < 0) {
+        isc_throw(NcrMessageError, "lease_length value " << value <<
+             "is negative.  It must greater than or equal to zero ");
+    }
+
+    // Good to go, make the assignment.
+    setLeaseLength(static_cast<uint32_t>(value));
+}
+
+void
+NameChangeRequest::setStatus(const NameChangeStatus value) {
+    status_ = value;
+}
+
+std::string
+NameChangeRequest::toText() const {
+    std::ostringstream stream;
+
+    stream << "Type: " << static_cast<int>(change_type_) << " (";
+    switch (change_type_) {
+    case CHG_ADD:
+        stream << "CHG_ADD)\n";
+        break;
+    case CHG_REMOVE:
+        stream << "CHG_REMOVE)\n";
+        break;
+    default:
+        // Shouldn't be possible.
+        stream << "Invalid Value\n";
+    }
+
+    stream << "Forward Change: " << (forward_change_ ? "yes" : "no")
+           << std::endl
+           << "Reverse Change: " << (reverse_change_ ? "yes" : "no")
+           << std::endl
+           << "FQDN: [" << fqdn_ << "]" << std::endl
+           << "IP Address: [" << ip_address_  << "]" << std::endl
+           << "DHCID: [" << dhcid_.toStr() << "]" << std::endl
+           << "Lease Expires On: " << getLeaseExpiresOnStr() << std::endl
+           << "Lease Length: " << lease_length_ << std::endl;
+
+    return (stream.str());
+}
+
+}; // end of isc::dhcp namespace
+}; // end of isc namespace
diff --git a/src/bin/d2/ncr_msg.h b/src/bin/d2/ncr_msg.h
new file mode 100644
index 0000000..5e7846b
--- /dev/null
+++ b/src/bin/d2/ncr_msg.h
@@ -0,0 +1,503 @@
+// Copyright (C) 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
+// 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 NCR_MSG_H
+#define NCR_MSG_H
+
+/// @file ncr_msg.h
+/// @brief This file provides the classes needed to embody, compose, and
+/// decompose DNS update requests that are sent by DHCP-DDNS clients to
+/// DHCP-DDNS. These requests are referred to as NameChangeRequests.
+
+#include <cc/data.h>
+#include <exceptions/exceptions.h>
+#include <util/buffer.h>
+#include <util/encode/hex.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+
+#include <time.h>
+#include <string>
+
+namespace isc {
+namespace d2 {
+
+/// @brief Exception thrown when NameChangeRequest marshalling error occurs.
+class NcrMessageError : public isc::Exception {
+public:
+    NcrMessageError(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) { };
+};
+
+/// @brief Defines the types of DNS updates that can be requested.
+enum NameChangeType {
+  CHG_ADD,
+  CHG_REMOVE
+};
+
+/// @brief Defines the runtime processing status values for requests.
+enum NameChangeStatus  {
+  ST_NEW,
+  ST_PENDING,
+  ST_COMPLETED,
+  ST_FAILED,
+};
+
+/// @brief Defines the list of data wire formats supported.
+enum NameChangeFormat {
+  FMT_JSON
+};
+
+/// @brief Container class for handling the DHCID value within a
+/// NameChangeRequest. It provides conversion to and from string for JSON
+/// formatting, but stores the data internally as unsigned bytes.
+class D2Dhcid {
+public:
+    /// @brief Default constructor
+    D2Dhcid();
+
+    /// @brief Constructor - Creates a new instance, populated by converting
+    /// a given string of digits into an array of unsigned bytes.
+    ///
+    /// @param data is a string of hexadecimal digits. The format is simply
+    /// a contiguous stream of digits, with no delimiters. For example a string
+    /// containing "14A3" converts to a byte array containing:  0x14, 0xA3.
+    ///
+    /// @throw throws a NcrMessageError if the input data contains non-digits
+    /// or there is an odd number of digits.
+    D2Dhcid(const std::string& data);
+
+    /// @brief Returns the DHCID value as a string of hexadecimal digits.
+    ///
+    /// @return returns a string containing a contiguous stream of digits.
+    std::string toStr() const;
+
+    /// @brief Sets the DHCID value based on the given string.
+    ///
+    /// @param data is a string of hexadecimal digits. The format is simply
+    /// a contiguous stream of digits, with no delimiters. For example a string
+    /// containing "14A3" converts to a byte array containing:  0x14, 0xA3.
+    ///
+    /// @throw throws a NcrMessageError if the input data contains non-digits
+    /// or there is an odd number of digits.
+    void fromStr(const std::string& data);
+
+    /// @brief Returns a reference to the DHCID byte vector.
+    ///
+    /// @return returns a reference to the vector.
+    const std::vector<uint8_t>& getBytes() {
+        return (bytes_);
+    }
+
+private:
+    /// @brief Storage for the DHCID value in unsigned bytes.
+    std::vector<uint8_t> bytes_;
+};
+
+/// @brief Defines a pointer to a ptime.
+/// NameChangeRequest member(s) that are timestamps are ptime instances.
+/// Boost ptime was chosen because it supports converting to and from ISO
+/// strings in GMT.  The Unix style time.h classes convert to GMT but
+/// conversion back assumes local time.  This is problematic if the "wire"
+/// format is string (i.e. JSON) and the request were to cross time zones.
+/// Additionally, time_t values should never be used directly so shipping them
+/// as string integers across platforms could theoretically be a problem.
+typedef boost::shared_ptr<boost::posix_time::ptime> TimePtr;
+
+class NameChangeRequest;
+/// @brief Defines a pointer to a NameChangeRequest.
+typedef boost::shared_ptr<NameChangeRequest> NameChangeRequestPtr;
+
+/// @brief Defines a map of Elements, keyed by their string name.
+typedef std::map<std::string, isc::data::ConstElementPtr> ElementMap;
+
+/// @brief  Represents a DHCP-DDNS client request.
+/// This class is used by DHCP-DDNS clients (e.g. DHCP4, DHCP6) to
+/// request DNS updates.  Each message contains a single DNS change (either an
+/// add/update or a remove) for a single FQDN.  It provides marshalling services
+/// for moving instances to and from the wire.  Currently, the only format
+/// supported is JSON, however the class provides an interface such that other
+/// formats can be readily supported.
+class NameChangeRequest {
+public:
+    /// @brief Default Constructor.
+    NameChangeRequest();
+
+    /// @brief Constructor.  Full constructor, which provides parameters for
+    /// all of the class members, except status.
+    ///
+    /// @param change_type the type of change (Add or Update)
+    /// @param forward_change indicates if this change should be sent to forward
+    /// DNS servers.
+    /// @param reverse_change indicates if this change should be sent to reverse
+    /// DNS servers.
+    /// @param fqdn the domain name whose pointer record(s) should be
+    /// updated.
+    /// @param ip_address the ip address leased to the given FQDN.
+    /// @param dhcid the lease client's unique DHCID.
+    /// @param lease_expires_on a timestamp containing the date/time the lease 
+    /// expires.
+    /// @param lease_length the amount of time in seconds for which the
+    /// lease is valid (TTL).
+    NameChangeRequest(const NameChangeType change_type,
+                      const bool forward_change, const bool reverse_change,
+                      const std::string& fqdn, const std::string& ip_address,
+                      const D2Dhcid& dhcid,
+                      const boost::posix_time::ptime& lease_expires_on,
+                      const uint32_t lease_length);
+
+    /// @brief Static method for creating a NameChangeRequest from a
+    /// buffer containing a marshalled request in a given format.
+    ///
+    /// When the format is:
+    ///
+    /// JSON: The buffer is expected to contain a two byte unsigned integer
+    /// which specified the length of the JSON text; followed by the JSON
+    /// text itself.  This method attempts to extract "length" characters
+    /// from the buffer. This data is used to create a character string that
+    /// is than treated as JSON which is then parsed into the data needed
+    /// to create a request instance.
+    ///
+    /// (NOTE currently only JSON is supported.)
+    ///
+    /// @param format indicates the data format to use
+    /// @param buffer is the input buffer containing the marshalled request
+    ///
+    /// @return returns a pointer to the new NameChangeRequest
+    ///
+    /// @throw throws NcrMessageError if an error occurs creating new
+    /// request.
+    static NameChangeRequestPtr fromFormat(const NameChangeFormat format,
+                                           isc::util::InputBuffer& buffer);
+
+    /// @brief Instance method for marshalling the contents of the request
+    /// into the given buffer in the given format.
+    ///
+    /// When the format is:
+    ///
+    /// JSON: Upon completion, the buffer will contain a two byte unsigned
+    /// integer which specifies the length of the JSON text; followed by the
+    /// JSON text itself. The JSON text contains the names and values for all
+    /// the request data needed to reassemble the request on the receiving
+    /// end. The JSON text in the buffer is NOT null-terminated.
+    ///
+    /// (NOTE currently only JSON is supported.)
+    ///
+    /// @param format indicates the data format to use
+    /// @param buffer is the output buffer to which the request should be
+    /// marshalled.
+    void toFormat(const NameChangeFormat format,
+                  isc::util::OutputBuffer& buffer) const;
+
+    /// @brief Static method for creating a NameChangeRequest from a
+    /// string containing a JSON rendition of a request.
+    ///
+    /// @param json is a string containing the JSON text
+    ///
+    /// @return returns a pointer to the new NameChangeRequest
+    ///
+    /// @throw throws NcrMessageError if an error occurs creating new request.
+    static NameChangeRequestPtr fromJSON(const std::string& json);
+
+    /// @brief Instance method for marshalling the contents of the request
+    /// into a string of JSON text.
+    ///
+    /// @return returns a string containing the JSON rendition of the request
+    std::string toJSON() const;
+
+    /// @brief Validates the content of a populated request.  This method is
+    /// used by both the full constructor and from-wire marshalling to ensure
+    /// that the request is content valid.  Currently it enforces the
+    /// following rules:
+    ///
+    ///  - FQDN must not be blank.
+    ///  - The IP address must be a valid address.
+    ///  - The DHCID must not be blank.
+    ///  - The lease expiration date must be a valid date/time.
+    ///  - That at least one of the two direction flags, forward change and
+    ///    reverse change is true.
+    ///
+    /// @todo This is an initial implementation which provides a minimal amount
+    /// of validation.  FQDN, DHCID, and IP Address members are all currently
+    /// strings, these may be replaced with richer classes.
+    ///
+    /// @throw throws a NcrMessageError if the request content violates any
+    /// of the validation rules.
+    void validateContent();
+
+    /// @brief Fetches the request change type.
+    ///
+    /// @return returns the change type
+    NameChangeType getChangeType() const {
+        return (change_type_);
+    }
+
+    /// @brief Sets the change type to the given value.
+    ///
+    /// @param value is the NameChangeType value to assign to the request.
+    void setChangeType(const NameChangeType value);
+
+    /// @brief Sets the change type to the value of the given Element.
+    ///
+    /// @param element is an integer Element containing the change type value.
+    ///
+    /// @throw throws a NcrMessageError if the element is not an integer
+    /// Element or contains an invalid value.
+    void setChangeType(isc::data::ConstElementPtr element);
+
+    /// @brief Checks forward change flag.
+    ///
+    /// @return returns a true if the forward change flag is true.
+    bool isForwardChange() const {
+        return (forward_change_);
+    }
+
+    /// @brief Sets the forward change flag to the given value.
+    ///
+    /// @param value contains the new value to assign to the forward change
+    /// flag
+    void setForwardChange(const bool value);
+
+    /// @brief Sets the forward change flag to the value of the given Element.
+    ///
+    /// @param element is a boolean Element containing the forward change flag
+    /// value.
+    ///
+    /// @throw throws a NcrMessageError if the element is not a boolean
+    /// Element
+    void setForwardChange(isc::data::ConstElementPtr element);
+
+    /// @brief Checks reverse change flag.
+    ///
+    /// @return returns a true if the reverse change flag is true.
+    bool isReverseChange() const {
+        return (reverse_change_);
+    }
+
+    /// @brief Sets the reverse change flag to the given value.
+    ///
+    /// @param value contains the new value to assign to the reverse change
+    /// flag
+    void setReverseChange(const bool value);
+
+    /// @brief Sets the reverse change flag to the value of the given Element.
+    ///
+    /// @param element is a boolean Element containing the reverse change flag
+    /// value.
+    ///
+    /// @throw throws a NcrMessageError if the element is not a boolean
+    /// Element
+    void setReverseChange(isc::data::ConstElementPtr element);
+
+    /// @brief Fetches the request FQDN
+    ///
+    /// @return returns a string containing the FQDN
+    const std::string getFqdn() const {
+        return (fqdn_);
+    }
+
+    /// @brief Sets the FQDN to the given value.
+    ///
+    /// @param value contains the new value to assign to the FQDN
+    void setFqdn(const std::string& value);
+
+    /// @brief Sets the FQDN to the value of the given Element.
+    ///
+    /// @param element is a string Element containing the FQDN
+    ///
+    /// @throw throws a NcrMessageError if the element is not a string
+    /// Element
+    void setFqdn(isc::data::ConstElementPtr element);
+
+    /// @brief Fetches the request IP address.
+    ///
+    /// @return returns a string containing the IP address
+    const std::string& getIpAddress() const {
+        return (ip_address_);
+    }
+
+    /// @brief Sets the IP address to the given value.
+    ///
+    /// @param value contains the new value to assign to the IP address
+    void setIpAddress(const std::string& value);
+
+    /// @brief Sets the IP address to the value of the given Element.
+    ///
+    /// @param element is a string Element containing the IP address
+    ///
+    /// @throw throws a NcrMessageError if the element is not a string
+    /// Element
+    void setIpAddress(isc::data::ConstElementPtr element);
+
+    /// @brief Fetches the request DHCID
+    ///
+    /// @return returns a reference to the request's D2Dhcid
+    const D2Dhcid& getDhcid() const {
+        return (dhcid_);
+    }
+
+    /// @brief Sets the DHCID based on the given string value.
+    ///
+    /// @param value is a string of hexadecimal digits. The format is simply
+    /// a contiguous stream of digits, with no delimiters. For example a string
+    /// containing "14A3" converts to a byte array containing:  0x14, 0xA3.
+    ///
+    /// @throw throws a NcrMessageError if the input data contains non-digits
+    /// or there is an odd number of digits.
+    void setDhcid(const std::string& value);
+
+    /// @brief Sets the DHCID based on the value of the given Element.
+    ///
+    /// @param element is a string Element containing the string of hexadecimal
+    /// digits. (See setDhcid(std::string&) above.)
+    ///
+    /// @throw throws a NcrMessageError if the input data contains non-digits
+    /// or there is an odd number of digits.
+    void setDhcid(isc::data::ConstElementPtr element);
+
+    /// @brief Fetches the request lease expiration as a timestamp.
+    ///
+    /// @return returns a pointer to the ptime containing the lease expiration
+    const TimePtr& getLeaseExpiresOn() const {
+        return (lease_expires_on_);
+    }
+
+    /// @brief Fetches the request lease expiration as string.
+    ///
+    /// The format of the string returned is:
+    ///
+    ///    YYYYMMDDTHHMMSS where T is the date-time separator
+    ///
+    /// Example: 18:54:54 June 26, 2013 would be: 20130626T185455
+    ///
+    /// @return returns a ISO date-time string of the lease expiration.
+    std::string getLeaseExpiresOnStr() const;
+
+    /// @brief Sets the lease expiration to given ptime value.
+    ///
+    /// @param value is the ptime value to assign to the lease expiration.
+    ///
+    /// @throw throws a NcrMessageError if the value is not a valid
+    /// timestamp.
+    void setLeaseExpiresOn(const boost::posix_time::ptime& value);
+
+    /// @brief Sets the lease expiration based on the given string.
+    ///
+    /// @param value is an ISO date-time string from which to set the
+    /// lease expiration. The format of the input is:
+    ///
+    ///    YYYYMMDDTHHMMSS where T is the date-time separator
+    ///
+    /// Example: 18:54:54 June 26, 2013 would be: 20130626T185455
+    ///
+    /// @throw throws a NcrMessageError if the ISO string is invalid.
+    void setLeaseExpiresOn(const std::string& value);
+
+    /// @brief Sets the lease expiration based on the given Element.
+    ///
+    /// @param element is string Element containing an ISO date-time string.
+    ///
+    /// @throw throws a NcrMessageError if the element is not a string
+    /// Element, or if the element value is an invalid ISO date-time string.
+    void setLeaseExpiresOn(isc::data::ConstElementPtr element);
+
+    /// @brief Fetches the request lease length.
+    ///
+    /// @return returns an integer containing the lease length
+    uint32_t getLeaseLength() const {
+        return (lease_length_);
+    }
+
+    /// @brief Sets the lease length to the given value.
+    ///
+    /// @param value contains the new value to assign to the lease length
+    void setLeaseLength(const uint32_t value);
+
+    /// @brief Sets the lease length to the value of the given Element.
+    ///
+    /// @param element is a integer Element containing the lease length
+    ///
+    /// @throw throws a NcrMessageError if the element is not a string
+    /// Element
+    void setLeaseLength(isc::data::ConstElementPtr element);
+
+    /// @brief Fetches the request status.
+    ///
+    /// @return returns the request status as a NameChangeStatus
+    NameChangeStatus getStatus() const {
+        return (status_);
+    }
+
+    /// @brief Sets the request status to the given value.
+    ///
+    /// @param value contains the new value to assign to request status
+    void setStatus(const NameChangeStatus value);
+
+    /// @brief Given a name, finds and returns an element from a map of
+    /// elements.
+    ///
+    /// @param name is the name of the desired element
+    /// @param element_map is the map of elements to search
+    ///
+    /// @return returns a pointer to the element if located
+    /// @throw throws a NcrMessageError if the element cannot be found within
+    /// the map
+    isc::data::ConstElementPtr getElement(const std::string& name,
+                                          const ElementMap& element_map) const;
+
+    /// @brief Returns a text rendition of the contents of the request.
+    /// This method is primarily for logging purposes.
+    ///
+    /// @return returns a string containing the text.
+    std::string toText() const;
+
+private:
+    /// @brief Denotes the type of this change as either an Add or a Remove.
+    NameChangeType change_type_;
+
+    /// @brief Indicates if this change should sent to forward DNS servers.
+    bool forward_change_;
+
+    /// @brief Indicates if this change should sent to reverse DNS servers.
+    bool reverse_change_;
+
+    /// @brief The domain name whose DNS entry(ies) are to be updated.
+    /// @todo Currently, this is a std::string but may be replaced with
+    /// dns::Name which provides additional validation and domain name
+    /// manipulation.
+    std::string fqdn_;
+
+    /// @brief The ip address leased to the FQDN.
+    std::string ip_address_;
+
+    /// @brief The lease client's unique DHCID.
+    /// @todo Currently, this is uses D2Dhcid it but may be replaced with
+    /// dns::DHCID which provides additional validation.
+    D2Dhcid dhcid_;
+
+    /// @brief The date-time the lease expires.
+    TimePtr lease_expires_on_;
+
+    /// @brief The amount of time in seconds for which the lease is valid (TTL).
+    uint32_t lease_length_;
+
+    /// @brief The processing status of the request.  Used internally.
+    NameChangeStatus status_;
+};
+
+
+}; // end of isc::d2 namespace
+}; // end of isc namespace
+
+#endif
diff --git a/src/bin/d2/tests/Makefile.am b/src/bin/d2/tests/Makefile.am
index 14518d5..53b284e 100644
--- a/src/bin/d2/tests/Makefile.am
+++ b/src/bin/d2/tests/Makefile.am
@@ -61,6 +61,7 @@ d2_unittests_SOURCES += ../d2_config.cc ../d2_config.h
 d2_unittests_SOURCES += ../d2_cfg_mgr.cc ../d2_cfg_mgr.h
 d2_unittests_SOURCES += ../d2_update_message.cc ../d2_update_message.h
 d2_unittests_SOURCES += ../d2_zone.cc ../d2_zone.h
+d2_unittests_SOURCES += ../ncr_msg.cc ../ncr_msg.h
 d2_unittests_SOURCES += d_test_stubs.cc d_test_stubs.h
 d2_unittests_SOURCES += d2_unittests.cc
 d2_unittests_SOURCES += d2_process_unittests.cc
@@ -70,6 +71,7 @@ d2_unittests_SOURCES += d_cfg_mgr_unittests.cc
 d2_unittests_SOURCES += d2_cfg_mgr_unittests.cc
 d2_unittests_SOURCES += d2_update_message_unittests.cc
 d2_unittests_SOURCES += d2_zone_unittests.cc
+d2_unittests_SOURCES += ncr_unittests.cc
 nodist_d2_unittests_SOURCES = ../d2_messages.h ../d2_messages.cc
 
 d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
@@ -82,6 +84,8 @@ d2_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/dhcpsrv/libb10-dhcpsrv.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
+d2_unittests_LDADD += $(top_builddir)/src/lib/util/libb10-util.la
+
 endif
 
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/d2/tests/ncr_unittests.cc b/src/bin/d2/tests/ncr_unittests.cc
new file mode 100644
index 0000000..70031c0
--- /dev/null
+++ b/src/bin/d2/tests/ncr_unittests.cc
@@ -0,0 +1,428 @@
+// Copyright (C) 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
+// 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 <d2/ncr_msg.h>
+
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+
+using namespace boost::posix_time;
+
+namespace {
+
+/// @brief Defines a list of valid JSON NameChangeRequest renditions.
+/// They are used as input to test conversion from JSON.
+const char *valid_msgs[] =
+{
+    // Valid Add.
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"19620121T132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Valid Remove.
+     "{"
+     " \"change_type\" : 1 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"19620121T132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+     // Valid Add with IPv6 address
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"fe80::2acf:e9ff:fe12:e56f\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"19620121T132405\" , "
+     " \"lease_length\" : 1300 "
+     "}"
+};
+
+/// @brief Defines a list of invalid JSON NameChangeRequest renditions.
+/// They are used as input to test conversion from JSON.
+const char *invalid_msgs[] =
+{
+    // Invalid change type.
+     "{"
+     " \"change_type\" : 7 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"19620121T132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Invalid forward change.
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : \"bogus\" , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"19620121T132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Invalid reverse change.
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : 500 , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"19620121T132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Forward and reverse change both false.
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : false , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"19620121T132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Blank FQDN
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"19620121T132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Bad IP address
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"xxxxxx\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"19620121T132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Blank DHCID
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"\" , "
+     " \"lease_expires_on\" : \"19620121T132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Odd number of digits in DHCID
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3\" , "
+     " \"lease_expires_on\" : \"19620121T132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Text in DHCID
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"THIS IS BOGUS!!!\" , "
+     " \"lease_expires_on\" : \"19620121T132405\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Invalid lease expiration string
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"Wed Jun 26 13:46:46 EDT 2013\" , "
+     " \"lease_length\" : 1300 "
+     "}",
+    // Non-integer for lease length.
+     "{"
+     " \"change_type\" : 0 , "
+     " \"forward_change\" : true , "
+     " \"reverse_change\" : false , "
+     " \"fqdn\" : \"walah.walah.com\" , "
+     " \"ip_address\" : \"192.168.2.1\" , "
+     " \"dhcid\" : \"010203040A7F8E3D\" , "
+     " \"lease_expires_on\" : \"19620121T132405\" , "
+     " \"lease_length\" : \"BOGUS\" "
+     "}"
+
+};
+
+/// @brief Tests the NameChangeRequest constructors.
+/// This test verifies that:
+/// 1. Default constructor works.
+/// 2. "Full" constructor, when given valid parameter values, works.
+/// 3. "Full" constructor, given a blank FQDN fails
+/// 4. "Full" constructor, given an invalid IP Address FQDN fails
+/// 5. "Full" constructor, given a blank DHCID fails
+/// 6. "Full" constructor, given an invalid lease expiration fails
+/// 7. "Full" constructor, given false for both forward and reverse fails
+TEST(NameChangeRequestTest, constructionTests) {
+    // Verify the default constructor works.
+    NameChangeRequestPtr ncr;
+    EXPECT_NO_THROW(ncr.reset(new NameChangeRequest()));
+    EXPECT_TRUE(ncr);
+
+    // Verify that full constructor works.
+    ptime expiry(second_clock::universal_time());
+    D2Dhcid dhcid("010203040A7F8E3D");
+
+    EXPECT_NO_THROW(ncr.reset(new NameChangeRequest(
+                    CHG_ADD, true, true, "walah.walah.com",
+                    "192.168.1.101", dhcid, expiry, 1300)));
+    EXPECT_TRUE(ncr);
+    ncr.reset();
+
+    // Verify blank FQDN is detected.
+    EXPECT_THROW(NameChangeRequest(CHG_ADD, true, true, "",
+                 "192.168.1.101", dhcid, expiry, 1300), NcrMessageError);
+
+    // Verify that an invalid IP address is detected.
+    EXPECT_THROW(NameChangeRequest(CHG_ADD, true, true, "valid.fqdn",
+                 "xxx.168.1.101", dhcid, expiry, 1300), NcrMessageError);
+
+    // Verify that a blank DHCID is detected.
+    D2Dhcid blank_dhcid;
+    EXPECT_THROW(NameChangeRequest(CHG_ADD, true, true, "walah.walah.com",
+                 "192.168.1.101", blank_dhcid, expiry, 1300), NcrMessageError);
+
+    // Verify that an invalid lease expiration is detected.
+    ptime blank_expiry;
+    EXPECT_THROW(NameChangeRequest(CHG_ADD, true, true, "valid.fqdn",
+                 "192.168.1.101", dhcid, blank_expiry, 1300), NcrMessageError);
+
+    // Verify that one or both of direction flags must be true.
+    EXPECT_THROW(NameChangeRequest(CHG_ADD, false, false, "valid.fqdn",
+                "192.168.1.101", dhcid, expiry, 1300), NcrMessageError);
+
+}
+
+/// @brief Tests the basic workings of D2Dhcid to and from string conversions.
+/// It verifies that:
+/// 1. DHCID input strings must contain an even number of characters
+/// 2. DHCID input strings must contain only hexadecimal character digits
+/// 3. A valid DHCID string converts correctly.
+/// 4. Converting a D2Dhcid to a string works correctly.
+TEST(NameChangeRequestTest, dhcidTest) {
+    D2Dhcid dhcid;
+
+    // Odd number of digits should be rejected.
+    std::string test_str = "010203040A7F8E3";
+    EXPECT_THROW(dhcid.fromStr(test_str), NcrMessageError);
+
+    // Non digit content should be rejected.
+    test_str = "0102BOGUSA7F8E3D";
+    EXPECT_THROW(dhcid.fromStr(test_str), NcrMessageError);
+
+    // Verify that valid input converts into a proper byte array.
+    test_str = "010203040A7F8E3D";
+    ASSERT_NO_THROW(dhcid.fromStr(test_str));
+
+    // Create a test vector of expected byte contents.
+    const uint8_t bytes[] = { 0x1, 0x2, 0x3, 0x4, 0xA, 0x7F, 0x8E, 0x3D };
+    std::vector<uint8_t> expected_bytes(bytes, bytes + sizeof(bytes));
+
+    // Fetch the byte vector from the dhcid and verify if equals the expected
+    // content.
+    const std::vector<uint8_t>& converted_bytes = dhcid.getBytes();
+    EXPECT_EQ(expected_bytes.size(), converted_bytes.size());
+    EXPECT_TRUE (std::equal(expected_bytes.begin(),
+                            expected_bytes.begin()+expected_bytes.size(),
+                            converted_bytes.begin()));
+
+    // Convert the new dhcid back to string and verify it matches the original
+    // DHCID input string.
+    std::string next_str = dhcid.toStr();
+    EXPECT_EQ(test_str, next_str);
+}
+
+/// @brief Tests that boost::posix_time library functions as expected.
+/// This test verifies that converting ptimes to and from ISO strings
+/// works properly. This test is perhaps unnecessary but just to avoid any
+/// OS specific surprises it is better safe than sorry.
+TEST(NameChangeRequestTest, boostTime) {
+   // Create a ptime with the time now.
+   ptime pt1(second_clock::universal_time());
+
+   // Get the ISO date-time string.
+   std::string pt1_str = to_iso_string(pt1);
+
+   // Use the ISO date-time string to create a new ptime.
+   ptime pt2 = from_iso_string(pt1_str);
+
+   // Verify the two times are equal.
+   EXPECT_EQ (pt1, pt2);
+}
+
+/// @brief Verifies the fundamentals of converting from and to JSON.
+/// It verifies that:
+/// 1. A NameChangeRequest can be created from a valid JSON string.
+/// 2. A valid JSON string can be created from a NameChangeRequest
+TEST(NameChangeRequestTest, basicJsonTest) {
+    // Define valid JSON rendition of a request.
+    std::string msg_str = "{"
+                            "\"change_type\":1,"
+                            "\"forward_change\":true,"
+                            "\"reverse_change\":false,"
+                            "\"fqdn\":\"walah.walah.com\","
+                            "\"ip_address\":\"192.168.2.1\","
+                            "\"dhcid\":\"010203040A7F8E3D\","
+                            "\"lease_expires_on\":\"19620121T132405\","
+                            "\"lease_length\":1300"
+                          "}";
+
+    // Verify that a NameChangeRequests can be instantiated from the
+    // a valid JSON rendition.
+    NameChangeRequestPtr ncr;
+    ASSERT_NO_THROW(ncr  = NameChangeRequest::fromJSON(msg_str));
+    ASSERT_TRUE(ncr);
+
+    // Verify that the JSON string created by the new request equals the
+    // original input string.
+    std::string json_str = ncr->toJSON();
+    EXPECT_EQ(msg_str, json_str);
+}
+
+/// @brief Tests a variety of invalid JSON message strings.
+/// This test iterates over a list of JSON messages, each containing a single
+/// content error. The list of messages is defined by the global array,
+/// invalid_messages. Currently that list contains the following invalid
+/// conditions:
+///  1. Invalid change type
+///  2. Invalid forward change
+///  3. Invalid reverse change
+///  4. Forward and reverse change both false
+///  5. Invalid forward change
+///  6. Blank FQDN
+///  7. Bad IP address
+///  8. Blank DHCID
+///  9. Odd number of digits in DHCID
+/// 10. Text in DHCID
+/// 11. Invalid lease expiration string
+/// 12. Non-integer for lease length.
+/// If more permutations arise they can easily be added to the list.
+TEST(NameChangeRequestTest, invalidMsgChecks) {
+    // Iterate over the list of JSON strings, attempting to create a
+    // NameChangeRequest. The attempt should throw a NcrMessageError.
+    int num_msgs = sizeof(invalid_msgs)/sizeof(char*);
+    for (int i = 0; i < num_msgs; i++) {
+        EXPECT_THROW(NameChangeRequest::fromJSON(invalid_msgs[i]),
+                     NcrMessageError) << "Invalid message not caught idx: "
+                     << i << std::endl << " text:[" << invalid_msgs[i] << "]"
+                     << std::endl;
+    }
+}
+
+/// @brief Tests a variety of valid JSON message strings.
+/// This test iterates over a list of JSON messages, each containing a single
+/// valid request rendition. The list of messages is defined by the global
+/// array, valid_messages. Currently that list contains the following valid
+/// messages:
+///  1. Valid, IPv4 Add
+///  2. Valid, IPv4 Remove
+///  3. Valid, IPv6 Add
+/// If more permutations arise they can easily be added to the list.
+TEST(NameChangeRequestTest, validMsgChecks) {
+    // Iterate over the list of JSON strings, attempting to create a
+    // NameChangeRequest. The attempt should succeed.
+    int num_msgs = sizeof(valid_msgs)/sizeof(char*);
+    for (int i = 0; i < num_msgs; i++) {
+        EXPECT_NO_THROW(NameChangeRequest::fromJSON(valid_msgs[i]))
+                        << "Valid message failed,  message idx: " << i
+                        << std::endl << " text:[" << valid_msgs[i] << "]"
+                        << std::endl;
+    }
+}
+
+/// @brief Tests converting to and from JSON via isc::util buffer classes.
+/// This test verifies that:
+/// 1. A NameChangeRequest can be rendered in JSON written to an OutputBuffer
+/// 2. A InputBuffer containing a valid JSON request rendition can be used
+/// to create a NameChangeRequest.
+TEST(NameChangeRequestTest, toFromBufferTest) {
+    // Define a string containing a valid JSON NameChangeRequest rendition.
+    std::string msg_str = "{"
+                            "\"change_type\":1,"
+                            "\"forward_change\":true,"
+                            "\"reverse_change\":false,"
+                            "\"fqdn\":\"walah.walah.com\","
+                            "\"ip_address\":\"192.168.2.1\","
+                            "\"dhcid\":\"010203040A7F8E3D\","
+                            "\"lease_expires_on\":\"19620121T132405\","
+                            "\"lease_length\":1300"
+                          "}";
+
+    // Create a request from JSON directly.
+    NameChangeRequestPtr ncr;
+    ASSERT_NO_THROW(ncr = NameChangeRequest::fromJSON(msg_str));
+
+    // Verify that we output the request as JSON text to a buffer
+    // without error.
+    isc::util::OutputBuffer output_buffer(1024);
+    ASSERT_NO_THROW(ncr->toFormat(FMT_JSON, output_buffer));
+
+    // Make an InputBuffer from the OutputBuffer.
+    isc::util::InputBuffer input_buffer(output_buffer.getData(),
+                                        output_buffer.getLength());
+
+    // Verify that we can create a new request from the InputBuffer.
+    NameChangeRequestPtr ncr2;
+    ASSERT_NO_THROW(ncr2 =
+                    NameChangeRequest::fromFormat(FMT_JSON, input_buffer));
+
+    // Convert the new request to JSON directly.
+    std::string final_str = ncr2->toJSON();
+
+    // Verify that the final string matches the original.
+    ASSERT_EQ(final_str, msg_str);
+}
+
+
+} // end of anonymous namespace
+



More information about the bind10-changes mailing list