BIND 10 master, updated. eac5e751473e238dee1ebf16491634a1fbea25e2 [master] Merge branch 'trac2976'

BIND 10 source code commits bind10-changes at lists.isc.org
Mon Jul 1 17:12:30 UTC 2013


The branch, master has been updated
       via  eac5e751473e238dee1ebf16491634a1fbea25e2 (commit)
       via  7f644c2532ecf0f3dab826a091fbe900dc5352f4 (commit)
       via  a81a52389a3ce477db992da27cf09cb1adc0e49e (commit)
       via  c5b11e54a93c528d549a66206f0ff2c2ddf4273d (commit)
       via  5eea9d893ec4ac21e89f8b3b0f3e68af625eed48 (commit)
       via  5d9916e93986dbeb890cbce2782d67b9f535adb4 (commit)
       via  a63b3077dd102512b9002e204a955f5b8d783ff4 (commit)
       via  16156f814b54dffdc0304016aa41b0746c613d3a (commit)
       via  5e8a8cc152ef5cd151f99389f475b2a16832fb9c (commit)
       via  570fc14873220c13f62e622fd3f227a73ae9a106 (commit)
       via  010bda2e96f5763e6eec226ac1cee96bce2588e5 (commit)
       via  87fc0368a47156638c189fbf071471ee0d9c4463 (commit)
       via  2b6d8c99eedfc5e77477461963bff7ae031192bf (commit)
       via  b021224a6314396eccfb9c7e0af223474079e864 (commit)
      from  351b82d0ed7d964803be52b433706377d7b2f780 (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 eac5e751473e238dee1ebf16491634a1fbea25e2
Merge: 351b82d 7f644c2
Author: Marcin Siodelski <marcin at isc.org>
Date:   Mon Jul 1 19:00:06 2013 +0200

    [master] Merge branch 'trac2976'

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

Summary of changes:
 src/bin/d2/Makefile.am                          |    3 +
 src/bin/d2/d2_update_message.cc                 |  221 +++++++++
 src/bin/d2/d2_update_message.h                  |  337 +++++++++++++
 src/bin/d2/{d2_log.h => d2_zone.cc}             |   22 +-
 src/bin/d2/d2_zone.h                            |  117 +++++
 src/bin/d2/tests/Makefile.am                    |    5 +
 src/bin/d2/tests/d2_update_message_unittests.cc |  591 +++++++++++++++++++++++
 src/bin/d2/tests/d2_zone_unittests.cc           |   75 +++
 8 files changed, 1362 insertions(+), 9 deletions(-)
 create mode 100644 src/bin/d2/d2_update_message.cc
 create mode 100644 src/bin/d2/d2_update_message.h
 copy src/bin/d2/{d2_log.h => d2_zone.cc} (71%)
 create mode 100644 src/bin/d2/d2_zone.h
 create mode 100644 src/bin/d2/tests/d2_update_message_unittests.cc
 create mode 100644 src/bin/d2/tests/d2_zone_unittests.cc

-----------------------------------------------------------------------
diff --git a/src/bin/d2/Makefile.am b/src/bin/d2/Makefile.am
index 7cf3019..6f889db 100644
--- a/src/bin/d2/Makefile.am
+++ b/src/bin/d2/Makefile.am
@@ -52,6 +52,8 @@ b10_dhcp_ddns_SOURCES += d_process.h
 b10_dhcp_ddns_SOURCES += d2_process.cc d2_process.h
 b10_dhcp_ddns_SOURCES += d_controller.cc d_controller.h
 b10_dhcp_ddns_SOURCES += d2_controller.cc d2_controller.h
+b10_dhcp_ddns_SOURCES += d2_update_message.cc d2_update_message.h
+b10_dhcp_ddns_SOURCES += d2_zone.cc d2_zone.h
 
 nodist_b10_dhcp_ddns_SOURCES = d2_messages.h d2_messages.cc
 EXTRA_DIST += d2_messages.mes
@@ -61,6 +63,7 @@ b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
 b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
 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/dns/libb10-dns++.la
 
 b10_dhcp_ddnsdir = $(pkgdatadir)
 b10_dhcp_ddns_DATA = dhcp-ddns.spec
diff --git a/src/bin/d2/d2_update_message.cc b/src/bin/d2/d2_update_message.cc
new file mode 100644
index 0000000..71fb9f3
--- /dev/null
+++ b/src/bin/d2/d2_update_message.cc
@@ -0,0 +1,221 @@
+// 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/d2_update_message.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/opcode.h>
+#include <dns/question.h>
+
+namespace isc {
+namespace d2 {
+
+using namespace isc::dns;
+
+D2UpdateMessage::D2UpdateMessage(const Direction direction)
+    : message_(direction == INBOUND ?
+               dns::Message::PARSE : dns::Message::RENDER) {
+    // If this object is to create an outgoing message, we have to
+    // set the proper Opcode field and QR flag here.
+    if (direction == OUTBOUND) {
+        message_.setOpcode(Opcode(Opcode::UPDATE_CODE));
+        message_.setHeaderFlag(dns::Message::HEADERFLAG_QR, false);
+
+    }
+}
+
+D2UpdateMessage::QRFlag
+D2UpdateMessage::getQRFlag() const {
+    return (message_.getHeaderFlag(dns::Message::HEADERFLAG_QR) ?
+            RESPONSE : REQUEST);
+}
+
+uint16_t
+D2UpdateMessage::getId() const {
+    return (message_.getQid());
+}
+
+void
+D2UpdateMessage::setId(const uint16_t id) {
+    message_.setQid(id);
+}
+
+
+const dns::Rcode&
+D2UpdateMessage::getRcode() const {
+    return (message_.getRcode());
+}
+
+void
+D2UpdateMessage::setRcode(const dns::Rcode& rcode) {
+    message_.setRcode(rcode);
+}
+
+unsigned int
+D2UpdateMessage::getRRCount(const UpdateMsgSection section) const {
+    return (message_.getRRCount(ddnsToDnsSection(section)));
+}
+
+const dns::RRsetIterator
+D2UpdateMessage::beginSection(const UpdateMsgSection section) const {
+    return (message_.beginSection(ddnsToDnsSection(section)));
+}
+
+const dns::RRsetIterator
+D2UpdateMessage::endSection(const UpdateMsgSection section) const {
+    return (message_.endSection(ddnsToDnsSection(section)));
+}
+
+void
+D2UpdateMessage::setZone(const Name& zone, const RRClass& rrclass) {
+    // The Zone data is kept in the underlying Question class. If there
+    // is a record stored there already, we need to remove it, because
+    // we may have at most one Zone record in the DNS Update message.
+    if (message_.getRRCount(dns::Message::SECTION_QUESTION) > 0) {
+        message_.clearSection(dns::Message::SECTION_QUESTION);
+    }
+    // Add the new record...
+    Question question(zone, rrclass, RRType::SOA());
+    message_.addQuestion(question);
+    // ... and update the local class member holding the D2Zone object.
+    zone_.reset(new D2Zone(question.getName(), question.getClass()));
+}
+
+D2ZonePtr
+D2UpdateMessage::getZone() const {
+    return (zone_);
+}
+
+void
+D2UpdateMessage::addRRset(const UpdateMsgSection section,
+                          const dns::RRsetPtr& rrset) {
+    if (section == SECTION_ZONE) {
+        isc_throw(isc::BadValue, "unable to add RRset to the Zone section"
+                  " of the DNS Update message, use setZone instead");
+    }
+    message_.addRRset(ddnsToDnsSection(section), rrset);
+}
+
+void
+D2UpdateMessage::toWire(AbstractMessageRenderer& renderer) {
+    // We are preparing the wire format of the message, meaning
+    // that this message will be sent as a request to the DNS.
+    // Therefore, we expect that this message is a REQUEST.
+    if (getQRFlag() != REQUEST) {
+        isc_throw(InvalidQRFlag, "QR flag must be cleared for the outgoing"
+                  " DNS Update message");
+    }
+    // According to RFC2136, the ZONE section may contain exactly one
+    // record.
+    if (getRRCount(SECTION_ZONE) != 1) {
+        isc_throw(InvalidZoneSection, "Zone section of the DNS Update message"
+                  " must comprise exactly one record (RFC2136, section 2.3)");
+    }
+    message_.toWire(renderer);
+}
+
+void
+D2UpdateMessage::fromWire(isc::util::InputBuffer& buffer) {
+    // First, use the underlying dns::Message implementation to get the
+    // contents of the DNS response message. Note that it may or may
+    // not be the message that we are interested in, but needs to be
+    // parsed so as we can check its ID, Opcode etc.
+    message_.fromWire(buffer);
+    // This class exposes the getZone() function. This function will return
+    // pointer to the D2Zone object if non-empty Zone section exists in the
+    // received message. It will return NULL pointer if it doesn't exist.
+    // The pointer is held in the D2UpdateMessage class member. We need to
+    // update this pointer every time we parse the message.
+    if (getRRCount(D2UpdateMessage::SECTION_ZONE) > 0) {
+        // There is a Zone section in the received message. Replace
+        // Zone pointer with the new value.
+        QuestionPtr question = *message_.beginQuestion();
+        // If the Zone counter is greater than 0 (which we have checked)
+        // there must be a valid Question pointer stored in the message_
+        // object. If there isn't, it is a programming error.
+        assert(question);
+        zone_.reset(new D2Zone(question->getName(), question->getClass()));
+
+    } else {
+        // Zone section doesn't hold any pointers, so set the pointer to NULL.
+        zone_.reset();
+
+    }
+    // Check that the content of the received message is sane.
+    // One of the basic checks to do is to verify that we have
+    // received the DNS update message. If not, it can be dropped
+    // or an error message can be printed. Other than that, we
+    // will check that there is at most one Zone record and QR flag
+    // is set.
+    validateResponse();
+}
+
+dns::Message::Section
+D2UpdateMessage::ddnsToDnsSection(const UpdateMsgSection section) {
+    /// The following switch maps the enumerator values from the
+    /// DNS Update message to the corresponding enumerator values
+    /// representing fields of the DNS message.
+    switch(section) {
+    case SECTION_ZONE :
+        return (dns::Message::SECTION_QUESTION);
+
+    case SECTION_PREREQUISITE:
+        return (dns::Message::SECTION_ANSWER);
+
+    case SECTION_UPDATE:
+        return (dns::Message::SECTION_AUTHORITY);
+
+    case SECTION_ADDITIONAL:
+        return (dns::Message::SECTION_ADDITIONAL);
+
+    default:
+        ;
+    }
+    isc_throw(dns::InvalidMessageSection,
+              "unknown message section " << section);
+}
+
+void
+D2UpdateMessage::validateResponse() const {
+    // Verify that we are dealing with the DNS Update message. According to
+    // RFC 2136, section 3.8 server will copy the Opcode from the query.
+    // If we are dealing with a different type of message, we may simply
+    // stop further processing, because it is likely that the message was
+    // directed to someone else.
+    if (message_.getOpcode() != Opcode::UPDATE()) {
+        isc_throw(NotUpdateMessage, "received message is not a DDNS update,"
+                  << " received message code is "
+                  << message_.getOpcode().getCode());
+    }
+    // Received message should have QR flag set, which indicates that it is
+    // a RESPONSE.
+    if (getQRFlag() == REQUEST) {
+        isc_throw(InvalidQRFlag, "received message should have QR flag set,"
+                  " to indicate that it is a RESPONSE message; the QR"
+                  << " flag in received message is unset");
+    }
+    // DNS server may copy a Zone record from the query message. Since query
+    // must comprise exactly one Zone record (RFC 2136, section 2.3), the
+    // response message may contain 1 record at most. It may also contain no
+    // records if a server chooses not to copy Zone section.
+    if (getRRCount(SECTION_ZONE) > 1) {
+        isc_throw(InvalidZoneSection, "received message contains "
+                  << getRRCount(SECTION_ZONE) << " Zone records,"
+                  << " it should contain at most 1 record");
+    }
+}
+
+} // namespace d2
+} // namespace isc
+
diff --git a/src/bin/d2/d2_update_message.h b/src/bin/d2/d2_update_message.h
new file mode 100644
index 0000000..98e98a8
--- /dev/null
+++ b/src/bin/d2/d2_update_message.h
@@ -0,0 +1,337 @@
+// 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 D2_UPDATE_MESSAGE_H
+#define D2_UPDATE_MESSAGE_H
+
+#include <d2/d2_zone.h>
+#include <dns/message.h>
+#include <dns/name.h>
+#include <dns/rcode.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+
+#include <map>
+
+namespace isc {
+namespace d2 {
+
+/// @brief Exception indicating that Zone section contains invalid content.
+///
+/// This exception is thrown when ZONE section of the DNS Update message
+/// is invalid. According to RFC2136, section 2.3, the zone section is
+/// allowed to contain exactly one record. When Request message contains
+/// more records or is empty, this exception is thrown.
+class InvalidZoneSection : public Exception {
+public:
+    InvalidZoneSection(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// @brief Exception indicating that QR flag has invalid value.
+///
+/// This exception is thrown when QR flag has invalid value for
+/// the operation performed on the particular message. For instance,
+/// the QR flag must be set to indicate that the given message is
+/// a RESPONSE when @c D2UpdateMessage::fromWire is performed.
+/// The QR flag must be cleared when @c D2UpdateMessage::toWire
+/// is executed.
+class InvalidQRFlag : public Exception {
+public:
+    InvalidQRFlag(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+/// @brief Exception indicating that the parsed message is not DNS Update.
+///
+/// This exception is thrown when decoding the DNS message which is not
+/// a DNS Update.
+class NotUpdateMessage : public Exception {
+public:
+    NotUpdateMessage(const char* file, size_t line, const char* what) :
+        isc::Exception(file, line, what) {}
+};
+
+
+/// @brief The @c D2UpdateMessage encapsulates a DNS Update message.
+///
+/// This class represents the DNS Update message. Functions exposed by this
+/// class allow to specify the data sections carried by the message and create
+/// an on-wire format of this message. This class is also used to decode
+/// messages received from the DNS server in the on-wire format.
+///
+/// <b>Design choice:</b> A dedicated class has been created to encapsulate
+/// DNS Update message because existing @c isc::dns::Message is designed to
+/// support regular DNS messages (described in RFC 1035) only. Although DNS
+/// Update has the same format, particular sections serve different purposes.
+/// In order to avoid rewrite of significant portions of @c isc::dns::Message
+/// class, this class is implemented in-terms-of @c isc::dns::Message class
+/// to reuse its functionality where possible.
+class D2UpdateMessage {
+public:
+
+    /// @brief Indicates if the @c D2UpdateMessage object encapsulates Inbound
+    /// or Outbound message.
+    enum Direction {
+        INBOUND,
+        OUTBOUND
+    };
+
+    /// @brief Indicates whether DNS Update message is a REQUEST or RESPONSE.
+    enum QRFlag {
+        REQUEST,
+        RESPONSE
+    };
+
+    /// @brief Identifies sections in the DNS Update Message.
+    ///
+    /// Each message comprises message Header and may contain the following
+    /// sections:
+    /// - ZONE
+    /// - PREREQUISITE
+    /// - UPDATE
+    /// - ADDITIONAL
+    ///
+    /// The enum elements are used by functions such as @c getRRCount (to get
+    /// the number of records in a corresponding section) and @c beginSection
+    /// and @c endSection (to access data in the corresponding section).
+    enum UpdateMsgSection {
+        SECTION_ZONE,
+        SECTION_PREREQUISITE,
+        SECTION_UPDATE,
+        SECTION_ADDITIONAL
+    };
+
+public:
+    /// @brief Constructor used to create an instance of the DNS Update Message
+    /// (either outgoing or incoming).
+    ///
+    /// This constructor is used to create an instance of either incoming or
+    /// outgoing DNS Update message. The boolean argument indicates wheteher it
+    /// is incoming (true) or outgoing (false) message. For incoming messages
+    /// the @c D2UpdateMessage::fromWire function is used to parse on-wire data.
+    /// For outgoing messages, modifier functions should be used to set the
+    /// message contents and @c D2UpdateMessage::toWire function to create
+    /// on-wire data.
+    ///
+    /// @param direction indicates if this is an inbound or outbound message.
+    D2UpdateMessage(const Direction direction = OUTBOUND);
+
+    ///
+    /// @name Copy constructor and assignment operator
+    ///
+    /// Copy constructor and assignment operator are private because we assume
+    /// there will be no need to copy messages on the client side.
+    //@{
+private:
+    D2UpdateMessage(const D2UpdateMessage& source);
+    D2UpdateMessage& operator=(const D2UpdateMessage& source);
+    //@}
+
+public:
+
+    /// @brief Returns enum value indicating if the message is a
+    /// REQUEST or RESPONSE
+    ///
+    /// The returned value is REQUEST if the message is created as an outgoing
+    /// message. In such case the QR flag bit in the message header is cleared.
+    /// The returned value is RESPONSE if the message is created as an incoming
+    /// message and the QR flag bit was set in the received message header.
+    ///
+    /// @return An enum value indicating whether the message is a
+    /// REQUEST or RESPONSE.
+    QRFlag getQRFlag() const;
+
+    /// @brief Returns message ID.
+    ///
+    /// @return message ID.
+    uint16_t getId() const;
+
+    /// @brief Sets message ID.
+    ///
+    /// @param id 16-bit value of the message id.
+    void setId(const uint16_t id);
+
+    /// @brief Returns an object representing message RCode.
+    ///
+    /// @return An object representing message RCode.
+    const dns::Rcode& getRcode() const;
+
+    /// @brief Sets message RCode.
+    ///
+    /// @param rcode An object representing message RCode.
+    void setRcode(const dns::Rcode& rcode);
+
+    /// @brief Returns number of RRsets in the specified message section.
+    ///
+    /// @param section An @c UpdateMsgSection enum specifying a message section
+    /// for which the number of RRsets is to be returned.
+    ///
+    /// @return A number of RRsets in the specified message section.
+    unsigned int getRRCount(const UpdateMsgSection section) const;
+
+    /// @name Functions returning iterators to RRsets in message sections.
+    ///
+    //@{
+    /// @brief Return iterators pointing to the beginning of the list of RRsets,
+    /// which belong to the specified section.
+    ///
+    /// @param section An @c UpdateMsgSection enum specifying a message section
+    /// for which the iterator should be returned.
+    ///
+    /// @return An iterator pointing to the beginning of the list of the
+    /// RRsets, which belong to the specified section.
+    const dns::RRsetIterator beginSection(const UpdateMsgSection section) const;
+
+    /// @brief Return iterators pointing to the end of the list of RRsets,
+    /// which belong to the specified section.
+    ///
+    /// @param section An @c UpdateMsgSection enum specifying a message section
+    /// for which the iterator should be returned.
+    ///
+    /// @return An iterator pointing to the end of the list of the
+    /// RRsets, which belong to the specified section.
+    const dns::RRsetIterator endSection(const UpdateMsgSection section) const;
+    //@}
+
+    /// @brief Sets the Zone record.
+    ///
+    /// This function creates the @c D2Zone object, representing a Zone record
+    /// for the outgoing message. If the Zone record is already set, it is
+    /// replaced by the new record being set by this function. The RRType for
+    /// the record is always SOA.
+    ///
+    /// @param zone A name of the zone being updated.
+    /// @param rrclass A class of the zone record.
+    void setZone(const dns::Name& zone, const dns::RRClass& rrclass);
+
+    /// @brief Returns a pointer to the object representing Zone record.
+    ///
+    /// @return A pointer to the object representing Zone record.
+    D2ZonePtr getZone() const;
+
+    /// @brief Adds an RRset to the specified section.
+    ///
+    /// This function may throw exception if the specified section is
+    /// out of bounds or Zone section update is attempted. For Zone
+    /// section @c D2UpdateMessage::setZone function should be used instead.
+    /// Also, this function expects that @c rrset argument is non-NULL.
+    ///
+    /// @param section A message section where the RRset should be added.
+    /// @param rrset A reference to a RRset which should be added.
+    void addRRset(const UpdateMsgSection section, const dns::RRsetPtr& rrset);
+
+    /// @name Functions to handle message encoding and decoding.
+    ///
+    //@{
+    /// @brief Encode outgoing message into wire format.
+    ///
+    /// This function encodes the DNS Update into the wire format. The format of
+    /// such a message is described in the RFC2136, section 2. Some of the
+    /// sections which belong to encoded message may be empty. If a particular
+    /// message section is empty (does not comprise any RRs), the corresponding
+    /// counter in the message header is set to 0. These counters are: PRCOUNT,
+    /// UPCOUNT, ADCOUNT for the Prerequisites, Update RRs and Additional Data
+    /// RRs respectively. The ZOCOUNT must be equal to 1 because RFC2136
+    /// requires that the message comprises exactly one Zone record.
+    ///
+    /// This function does not guarantee exception safety. However, exceptions
+    /// should be rare because @c D2UpdateMessage class API prevents invalid
+    /// use of the class. The typical case, when this function may throw an
+    /// exception is when this it is called on the object representing
+    /// incoming (instead of outgoing) message. In such case, the QR field
+    /// will be set to RESPONSE, which is invalid setting when calling this
+    /// function.
+    ///
+    /// @param renderer A renderer object used to generate the message wire
+    /// format.
+    void toWire(dns::AbstractMessageRenderer& renderer);
+
+    /// @brief Decode incoming message from the wire format.
+    ///
+    /// This function decodes the DNS Update message stored in the buffer
+    /// specified by the function argument. In the first turn, this function
+    /// parses message header and extracts the section counters: ZOCOUNT,
+    /// PRCOUNT, UPCOUNT and ADCOUNT. Using these counters, function identifies
+    /// message sections, which follow message header. These sections can be
+    /// later accessed using: @c D2UpdateMessage::getZone,
+    /// @c D2UpdateMessage::beginSection and @c D2UpdateMessage::endSection
+    /// functions.
+    ///
+    /// This function is NOT exception safe. It signals message decoding errors
+    /// through exceptions. Message decoding error may occur if the received
+    /// message does not conform to the general DNS Message format, specified in
+    /// RFC 1035. Errors which are specific to DNS Update messages include:
+    /// - Invalid Opcode - not an UPDATE.
+    /// - Invalid QR flag - the QR bit should be set to indicate that the
+    /// message is the server response.
+    /// - The number of records in the Zone section is greater than 1.
+    ///
+    /// @param buffer input buffer, holding DNS Update message to be parsed.
+    void fromWire(isc::util::InputBuffer& buffer);
+    //@}
+
+private:
+    /// Maps the values of the @c UpdateMessageSection field to the
+    /// corresponding values in the @c isc::dns::Message class. This
+    /// mapping is required here because this class uses @c isc::dns::Message
+    /// class to do the actual processing of the DNS Update message.
+    ///
+    /// @param section An enum indicating the section for which the
+    /// corresponding  enum value from @c isc::dns::Message will be returned.
+    ///
+    /// @return The enum value indicating the section in the DNS message
+    /// represented by the @c isc::dns::Message class.
+    static
+    dns::Message::Section ddnsToDnsSection(const UpdateMsgSection section);
+
+    /// @brief Checks received response message for correctness.
+    ///
+    /// This function verifies that the received response from a server is
+    /// correct. Currently this function checks the following:
+    /// - Opcode is 'DNS Update',
+    /// - QR flag is RESPONSE (flag bit is set),
+    /// - Zone section comprises at most one record.
+    ///
+    /// The function will throw exception if any of the conditions above are
+    /// not met.
+    ///
+    /// @throw isc::d2::NotUpdateMessage if invalid Opcode.
+    /// @throw isc::d2::InvalidQRFlag if QR flag is not set to RESPONSE
+    /// @throw isc::d2::InvalidZone section, if Zone section comprises more
+    /// than one record.
+    void validateResponse() const;
+
+    /// @brief An object representing DNS Message which is used by the
+    /// implementation of @c D2UpdateMessage to perform low level.
+    ///
+    /// Declaration of this object pollutes the header with the details
+    /// of @c D2UpdateMessage implementation. It might be cleaner to use
+    /// Pimpl idiom to hide this object in an D2UpdateMessageImpl. However,
+    /// it would bring additional complications to the implementation
+    /// while the benefit would low - this header is not a part of any
+    /// common library. Therefore, if implementation is changed, modification of
+    /// private members of this class in the header has low impact.
+    dns::Message message_;
+
+    /// @brief Holds a pointer to the object, representing Zone in the DNS
+    /// Update.
+    D2ZonePtr zone_;
+
+};
+
+} // namespace d2
+} // namespace isc
+
+#endif // D2_UPDATE_MESSAGE_H
diff --git a/src/bin/d2/d2_zone.cc b/src/bin/d2/d2_zone.cc
new file mode 100644
index 0000000..96aa2bb
--- /dev/null
+++ b/src/bin/d2/d2_zone.cc
@@ -0,0 +1,36 @@
+// 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/d2_zone.h>
+
+namespace isc {
+namespace d2 {
+
+D2Zone::D2Zone(const dns::Name& name, const dns::RRClass& rrclass)
+    : name_(name), rrclass_(rrclass) {
+}
+
+std::string D2Zone::toText() const {
+    return (name_.toText() + " " + rrclass_.toText() + " SOA\n");
+}
+
+std::ostream&
+operator<<(std::ostream& os, const D2Zone& zone) {
+    os << zone.toText();
+    return (os);
+}
+
+} // namespace d2
+} // namespace isc
+
diff --git a/src/bin/d2/d2_zone.h b/src/bin/d2/d2_zone.h
new file mode 100644
index 0000000..60d43c8
--- /dev/null
+++ b/src/bin/d2/d2_zone.h
@@ -0,0 +1,117 @@
+// 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 D2_ZONE_H
+#define D2_ZONE_H
+
+#include <dns/name.h>
+#include <dns/rrclass.h>
+
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace d2 {
+
+/// @brief The @c D2Zone encapsulates the Zone section in DNS Update message.
+///
+/// This class is used by the @c D2UpdateMessage to encapsulate the Zone section
+/// of the DNS Update message. Class members hold corresponding values of
+/// section's fields: NAME, CLASS. This class does not hold the RTYPE field
+/// value because RTYPE is always equal to SOA for DNS Update message (see
+/// RFC 2136, section 2.3).
+///
+/// Note, that this @c D2Zone class neither exposes functions to decode messages
+/// from wire format nor to encode to wire format. This is not needed, because
+/// @c isc::d2::D2UpdateMessage class uses @c D2Zone only to return the parsed
+/// Zone information to the caller. Internally, D2UpdateMessage parses and
+/// stores Zone section using @c isc::dns::Question class, and the @c toWire
+/// and @c fromWire functions of the @c isc::dns::Question class are used.
+class D2Zone {
+public:
+    /// @brief Constructor from Name and RRClass.
+    ///
+    /// @param name The name of the Zone.
+    /// @param rrclass The RR class of the Zone.
+    D2Zone(const dns::Name& name, const dns::RRClass& rrclass);
+
+    ///
+    /// @name Getters
+    ///
+    //@{
+    /// @brief Returns the Zone name.
+    ///
+    /// @return A reference to the Zone name.
+    const dns::Name& getName() const { return (name_); }
+
+    /// @brief Returns the Zone class.
+    ///
+    /// @return A reference to the Zone class.
+    const dns::RRClass& getClass() const { return (rrclass_); }
+    //@}
+
+    /// @brief Returns text representation of the Zone.
+    ///
+    /// This function concatenates the name of the Zone, Class and Type.
+    /// The type is always SOA.
+    ///
+    /// @return A text representation of the Zone.
+    std::string toText() const;
+
+    ///
+    /// @name Comparison Operators
+    ///
+    //@{
+    /// @brief Equality operator to compare @c D2Zone objects in query and
+    /// response messages.
+    ///
+    /// @param rhs Zone to compare against.
+    ///
+    /// @return true if name and class are equal, false otherwise.
+    bool operator==(const D2Zone& rhs) const {
+        return ((rrclass_ == rhs.rrclass_) && (name_ == rhs.name_));
+    }
+
+    /// @brief Inequality operator to compare @c D2Zone objects in query and
+    /// response messages.
+    ///
+    /// @param rhs Zone to compare against.
+    ///
+    /// @return true if any of name or class are unequal, false otherwise.
+    bool operator!=(const D2Zone& rhs) const {
+        return (!operator==(rhs));
+    }
+    //@}
+
+private:
+    dns::Name name_;       ///< Holds the Zone name.
+    dns::RRClass rrclass_; ///< Holds the Zone class.
+};
+
+typedef boost::shared_ptr<D2Zone> D2ZonePtr;
+
+/// @brief Insert the @c D2Zone as a string into stream.
+///
+/// @param os A @c std::ostream object on which the insertion operation is
+/// performed.
+/// @param zone A reference to the @c D2Zone object output by the
+/// operation.
+///
+/// @return A reference to the same @c std::ostream object referenced by
+/// parameter @c os after the insertion operation.
+std::ostream& operator<<(std::ostream& os, const D2Zone& zone);
+
+} // namespace d2
+} // namespace isc
+
+#endif // D2_ZONE_H
diff --git a/src/bin/d2/tests/Makefile.am b/src/bin/d2/tests/Makefile.am
index bd7773b..a7b37ce 100644
--- a/src/bin/d2/tests/Makefile.am
+++ b/src/bin/d2/tests/Makefile.am
@@ -56,11 +56,15 @@ d2_unittests_SOURCES += ../d_process.h
 d2_unittests_SOURCES += ../d_controller.cc ../d2_controller.h
 d2_unittests_SOURCES += ../d2_process.cc ../d2_process.h
 d2_unittests_SOURCES += ../d2_controller.cc ../d2_controller.h
+d2_unittests_SOURCES += ../d2_update_message.cc ../d2_update_message.h
+d2_unittests_SOURCES += ../d2_zone.cc ../d2_zone.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
 d2_unittests_SOURCES += d_controller_unittests.cc
 d2_unittests_SOURCES += d2_controller_unittests.cc
+d2_unittests_SOURCES += d2_update_message_unittests.cc
+d2_unittests_SOURCES += d2_zone_unittests.cc
 nodist_d2_unittests_SOURCES = ../d2_messages.h ../d2_messages.cc
 
 d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
@@ -71,6 +75,7 @@ d2_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
 d2_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
 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/dns/libb10-dns++.la
 endif
 
 noinst_PROGRAMS = $(TESTS)
diff --git a/src/bin/d2/tests/d2_update_message_unittests.cc b/src/bin/d2/tests/d2_update_message_unittests.cc
new file mode 100644
index 0000000..28e01c7
--- /dev/null
+++ b/src/bin/d2/tests/d2_update_message_unittests.cc
@@ -0,0 +1,591 @@
+// 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 <config.h>
+
+#include <d2/d2_update_message.h>
+#include <d2/d2_zone.h>
+#include <dns/messagerenderer.h>
+#include <dns/rdataclass.h>
+#include <dns/rdata.h>
+#include <dns/rrttl.h>
+
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+using namespace isc::dns;
+using namespace isc::dns::rdata;
+using namespace isc::util;
+
+namespace {
+
+// @brief Test fixture class for testing D2UpdateMessage object.
+class D2UpdateMessageTest : public ::testing::Test {
+public:
+    // @brief Constructor.
+    //
+    // Does nothing.
+    D2UpdateMessageTest() { }
+
+    // @brief Destructor.
+    //
+    // Does nothing.
+    ~D2UpdateMessageTest() { };
+
+    // @brief Return string representation of the name encoded in wire format.
+    //
+    // This function reads the number of bytes specified in the second
+    // argument from the buffer. It doesn't check if buffer has sufficient
+    // length for reading given number of bytes. Caller should verify it
+    // prior to calling this function.
+    //
+    // @param buf input buffer, its internal pointer will be moved to
+    //        the position after a name being read from it.
+    // @param name_length length of the name stored in the buffer
+    // @param no_zero_byte if true it indicates that the given buffer does not
+    //        comprise the zero byte, which signals end of the name. This is
+    //        the case, when dealing with compressed messages which don't have
+    //        this byte.
+    //
+    // @return string representation of the name.
+    std::string readNameFromWire(InputBuffer& buf, size_t name_length,
+                                 const bool no_zero_byte = false) {
+        std::vector<uint8_t> name_data;
+        // Create another InputBuffer which holds only the name in the wire
+        // format.
+        buf.readVector(name_data, name_length);
+        if (no_zero_byte) {
+            ++name_length;
+            name_data.push_back(0);
+        }
+        InputBuffer name_buf(&name_data[0], name_length);
+        // Parse the name and return its textual representation.
+        Name name(name_buf);
+        return (name.toText());
+    }
+};
+
+// This test verifies that DNS Update message ID can be set using
+// setId function.
+TEST_F(D2UpdateMessageTest, setId) {
+    // Message ID is initialized to 0.
+    D2UpdateMessage msg;
+    EXPECT_EQ(0, msg.getId());
+    // Override the default value and verify that it has been set.
+    msg.setId(0x1234);
+    EXPECT_EQ(0x1234, msg.getId());
+}
+
+// This test verifies that the DNS Update message RCODE can be set
+// using setRcode function.
+TEST_F(D2UpdateMessageTest, setRcode) {
+    D2UpdateMessage msg;
+    // Rcode must be explicitly set before it is accessed.
+    msg.setRcode(Rcode::NOERROR());
+    EXPECT_EQ(Rcode::NOERROR().getCode(), msg.getRcode().getCode());
+    // Let's override current value to make sure that getter does
+    // not return fixed value.
+    msg.setRcode(Rcode::NOTIMP());
+    EXPECT_EQ(Rcode::NOTIMP().getCode(), msg.getRcode().getCode());
+}
+
+// This test verifies that the Zone section in the DNS Update message
+// can be set.
+TEST_F(D2UpdateMessageTest, setZone) {
+    D2UpdateMessage msg;
+    // The zone pointer is initialized to NULL.
+    D2ZonePtr zone = msg.getZone();
+    EXPECT_FALSE(zone);
+    // Let's create a new Zone and check that it is returned
+    // via getter.
+    msg.setZone(Name("example.com"), RRClass::ANY());
+    zone = msg.getZone();
+    EXPECT_TRUE(zone);
+    EXPECT_EQ("example.com.", zone->getName().toText());
+    EXPECT_EQ(RRClass::ANY().getCode(), zone->getClass().getCode());
+
+    // Now, let's check that the existing Zone object can be
+    // overriden with a new one.
+    msg.setZone(Name("foo.example.com"), RRClass::NONE());
+    zone = msg.getZone();
+    EXPECT_TRUE(zone);
+    EXPECT_EQ("foo.example.com.", zone->getName().toText());
+    EXPECT_EQ(RRClass::NONE().getCode(), zone->getClass().getCode());
+}
+
+// This test verifies that the DNS message is properly decoded from the
+// wire format.
+TEST_F(D2UpdateMessageTest, fromWire) {
+    // The following table holds the DNS response in on-wire format.
+    // This message comprises the following sections:
+    // - HEADER
+    // - PREREQUISITE section with one RR
+    // - UPDATE section with 1 RR.
+    // Such a response may be generated by the DNS server as a result
+    // of copying the contents of the REQUEST message sent by DDNS client.
+    const uint8_t bin_msg[] = {
+        // HEADER section starts here (see RFC 2136, section 2).
+        0x05, 0xAF, // ID=0x05AF
+        0xA8, 0x6,  // QR=1, Opcode=6, RCODE=YXDOMAIN
+        0x0, 0x1,   // ZOCOUNT=1
+        0x0, 0x2,   // PRCOUNT=2
+        0x0, 0x1,   // UPCOUNT=1
+        0x0, 0x0,   // ADCOUNT=0
+
+        // Zone section starts here. The The first field comprises
+        // the Zone name encoded as a set of labels, each preceded
+        // by a length of the following label. The whole Zone name is
+        // terminated with a NULL char.
+        // For Zone section format see (RFC 2136, section 2.3).
+        0x7, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example (7 is length)
+        0x3, 0x63, 0x6F, 0x6D, //.com. (0x3 is a length)
+        0x0,      // NULL character terminates the Zone name.
+        0x0, 0x6, // ZTYPE='SOA'
+        0x0, 0x1, // ZCLASS='IN'
+
+        // Prerequisite section starts here. This section comprises two
+        // prerequisites:
+        // - 'Name is not in use'
+        // - 'Name is in use'
+        // See RFC 2136, section 2.4 for the format of Prerequisite section.
+        // Each prerequisite RR starts with its name. It is expressed in the
+        // compressed format as described in RFC 1035, section 4.1.4. The first
+        // label is expressed as in case of non-compressed name. It is preceded
+        // by the length value. The following two bytes are the pointer to the
+        // offset in the message where 'example.com' was used. That is, in the
+        // Zone name at offset 12. Pointer starts with two bits set - they
+        // mark start of the pointer.
+
+        // First prerequisite. NONE class indicates that the update requires
+        // that the name 'foo.example.com' is not use/
+        0x03, 0x66, 0x6F, 0x6F, // foo.
+        0xC0, 0x0C,             // pointer to example.com.
+        0x0, 0x1C,              // TYPE=AAAA
+        0x0, 0xFE,              // CLASS=NONE
+        0x0, 0x0, 0x0, 0x0,     // TTL=0
+        0x0, 0x0,               // RDLENGTH=0
+
+        // Second prerequisite. ANY class indicates tha the update requires
+        // that the name 'bar.example.com' exists.
+        0x03, 0x62, 0x61, 0x72, // bar.
+        0xC0, 0x0C,             // pointer to example.com.
+        0x0, 0x1C,              // TYPE=AAAA
+        0x0, 0xFF,              // CLASS=ANY
+        0x0, 0x0, 0x0, 0x0,     // TTL=0
+        0x0, 0x0,               // RDLENGTH=0
+
+        // Update section starts here. The format of this section conforms to
+        // RFC 2136, section 2.5. The name of the RR is again expressed in
+        // compressed format. The two pointer bytes point to the offset in the
+        // message where 'foo.example.com' was used already - 29.
+        0xC0, 0x1D,             // pointer to foo.example.com.
+        0x0, 0x1C,              // TYPE=AAAA
+        0x0, 0x1,               // CLASS=IN
+        0xAA, 0xBB, 0xCC, 0xDD, // TTL=0xAABBCCDD
+        0x0, 0x10,              // RDLENGTH=16
+        // The following 16 bytes of RDATA hold IPv6 address: 2001:db8:1::1.
+        0x20, 0x01, 0x0D, 0xB8, 0x00, 0x01, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
+    };
+    InputBuffer buf(bin_msg, sizeof(bin_msg));
+
+    // Create an object to be used to decode the message from the wire format.
+    D2UpdateMessage msg(D2UpdateMessage::INBOUND);
+    // Decode the message.
+    ASSERT_NO_THROW(msg.fromWire(buf));
+
+    // Check that the message header is valid.
+    EXPECT_EQ(0x05AF, msg.getId());
+    EXPECT_EQ(D2UpdateMessage::RESPONSE, msg.getQRFlag());
+    EXPECT_EQ(Rcode::YXDOMAIN_CODE, msg.getRcode().getCode());
+
+    // The ZOCOUNT must contain exactly one zone. If it does, we should get
+    // the name, class and type of the zone and verify they are valid.
+    ASSERT_EQ(1, msg.getRRCount(D2UpdateMessage::SECTION_ZONE));
+    D2ZonePtr zone = msg.getZone();
+    ASSERT_TRUE(zone);
+    EXPECT_EQ("example.com.", zone->getName().toText());
+    EXPECT_EQ(RRClass::IN().getCode(), zone->getClass().getCode());
+
+    // Check the Prerequisite section. It should contain two records.
+    ASSERT_EQ(2, msg.getRRCount(D2UpdateMessage::SECTION_PREREQUISITE));
+
+    // Proceed to the first prerequisite.
+    RRsetIterator rrset_it =
+        msg.beginSection(D2UpdateMessage::SECTION_PREREQUISITE);
+    RRsetPtr prereq1 = *rrset_it;
+    ASSERT_TRUE(prereq1);
+    // Check record fields.
+    EXPECT_EQ("foo.example.com.", prereq1->getName().toText()); // NAME
+    EXPECT_EQ(RRType::AAAA().getCode(), prereq1->getType().getCode()); // TYPE
+    EXPECT_EQ(RRClass::NONE().getCode(),
+              prereq1->getClass().getCode()); // CLASS
+    EXPECT_EQ(0, prereq1->getTTL().getValue()); // TTL
+    EXPECT_EQ(0, prereq1->getRdataCount()); // RDLENGTH
+
+    // Move to next prerequisite section.
+    ++rrset_it;
+    RRsetPtr prereq2 = *rrset_it;
+    ASSERT_TRUE(prereq2);
+    // Check record fields.
+    EXPECT_EQ("bar.example.com.", prereq2->getName().toText()); // NAME
+    EXPECT_EQ(RRType::AAAA().getCode(), prereq2->getType().getCode()); // TYPE
+    EXPECT_EQ(RRClass::ANY().getCode(), prereq2->getClass().getCode()); // CLASS
+    EXPECT_EQ(0, prereq2->getTTL().getValue()); // TTL
+    EXPECT_EQ(0, prereq2->getRdataCount()); // RDLENGTH
+
+    // Check the Update section. There is only one record, so beginSection()
+    // should return the pointer to this sole record.
+    ASSERT_EQ(1, msg.getRRCount(D2UpdateMessage::SECTION_UPDATE));
+    rrset_it = msg.beginSection(D2UpdateMessage::SECTION_UPDATE);
+    RRsetPtr update = *rrset_it;
+    ASSERT_TRUE(update);
+    // Check the record fields.
+    EXPECT_EQ("foo.example.com.", update->getName().toText()); // NAME
+    EXPECT_EQ(RRType::AAAA().getCode(), update->getType().getCode()); // TYPE
+    EXPECT_EQ(RRClass::IN().getCode(), update->getClass().getCode()); // CLASS
+    EXPECT_EQ(0xAABBCCDD, update->getTTL().getValue()); // TTL
+    // There should be exactly one record holding the IPv6 address.
+    // This record can be accessed using RdataIterator. This record
+    // can be compared with the reference record, holding expected IPv6
+    // address using compare function.
+    ASSERT_EQ(1, update->getRdataCount());
+    RdataIteratorPtr rdata_it = update->getRdataIterator();
+    ASSERT_TRUE(rdata_it);
+    in::AAAA rdata_ref("2001:db8:1::1");
+    EXPECT_EQ(0, rdata_ref.compare(rdata_it->getCurrent()));
+
+    // @todo: at this point we don't test Additional Data records. We may
+    // consider implementing tests for it in the future.
+}
+
+// This test verifies that the fromWire function throws appropriate exception
+// if the message being parsed comprises invalid Opcode (is not a DNS Update).
+TEST_F(D2UpdateMessageTest, fromWireInvalidOpcode) {
+    // This is a binary representation of the DNS message.
+    // It comprises invalid Opcode=3, expected value is 6
+    // (Update).
+    const uint8_t bin_msg[] = {
+        0x05, 0xAF, // ID=0x05AF
+        0x98, 0x6,  // QR=1, Opcode=3, RCODE=YXDOMAIN
+        0x0, 0x0,   // ZOCOUNT=0
+        0x0, 0x0,   // PRCOUNT=0
+        0x0, 0x0,   // UPCOUNT=0
+        0x0, 0x0    // ADCOUNT=0
+    };
+    InputBuffer buf(bin_msg, sizeof(bin_msg));
+    // The 'true' argument passed to the constructor turns the
+    // message into the parse mode in which the fromWire function
+    // can be used to decode the binary mesasage data.
+    D2UpdateMessage msg(D2UpdateMessage::INBOUND);
+    // When using invalid Opcode, the fromWire function should
+    // throw NotUpdateMessage exception.
+    EXPECT_THROW(msg.fromWire(buf), isc::d2::NotUpdateMessage);
+}
+
+// This test verifies that the fromWire function throws appropriate exception
+// if the message being parsed comprises invalid QR flag. The QR bit is
+// expected to be set to indicate that the message is a RESPONSE.
+TEST_F(D2UpdateMessageTest, fromWireInvalidQRFlag) {
+    // This is a binary representation of the DNS message.
+    // It comprises invalid QR flag = 0.
+    const uint8_t bin_msg[] = {
+        0x05, 0xAF, // ID=0x05AF
+        0x28, 0x6,  // QR=0, Opcode=6, RCODE=YXDOMAIN
+        0x0, 0x0,   // ZOCOUNT=0
+        0x0, 0x0,   // PRCOUNT=0
+        0x0, 0x0,   // UPCOUNT=0
+        0x0, 0x0    // ADCOUNT=0
+    };
+    InputBuffer buf(bin_msg, sizeof(bin_msg));
+    // The 'true' argument passed to the constructor turns the
+    // message into the parse mode in which the fromWire function
+    // can be used to decode the binary mesasage data.
+    D2UpdateMessage msg(D2UpdateMessage::INBOUND);
+    // When using invalid QR flag, the fromWire function should
+    // throw InvalidQRFlag exception.
+    EXPECT_THROW(msg.fromWire(buf), isc::d2::InvalidQRFlag);
+}
+
+// This test verifies that the fromWire function throws appropriate exception
+// if the message being parsed comprises more than one (two in this case)
+// Zone records.
+TEST_F(D2UpdateMessageTest, fromWireTooManyZones) {
+    // This is a binary representation of the DNS message. This message
+    // comprises two Zone records.
+    const uint8_t bin_msg[] = {
+        0x05, 0xAF, // ID=0x05AF
+        0xA8, 0x6,  // QR=1, Opcode=6, RCODE=YXDOMAIN
+        0x0, 0x2,   // ZOCOUNT=2
+        0x0, 0x0,   // PRCOUNT=0
+        0x0, 0x0,   // UPCOUNT=0
+        0x0, 0x0,   // ADCOUNT=0
+
+        // Start first Zone record.
+        0x7, 0x65, 0x78, 0x61, 0x6D, 0x70, 0x6C, 0x65, // example (7 is length)
+        0x3, 0x63, 0x6F, 0x6D, //.com. (0x3 is a length)
+        0x0,      // NULL character terminates the Zone name.
+        0x0, 0x6, // ZTYPE='SOA'
+        0x0, 0x1, // ZCLASS='IN'
+
+        // Start second Zone record. Presence of this record should result
+        // in error when parsing this message.
+        0x3, 0x63, 0x6F, 0x6D, // com. (0x3 is a length)
+        0x0,      // NULL character terminates the Zone name.
+        0x0, 0x6, // ZTYPE='SOA'
+        0x0, 0x1  // ZCLASS='IN'
+    };
+    InputBuffer buf(bin_msg, sizeof(bin_msg));
+
+    // The 'true' argument passed to the constructor turns the
+    // message into the parse mode in which the fromWire function
+    // can be used to decode the binary mesasage data.
+    D2UpdateMessage msg(D2UpdateMessage::INBOUND);
+    // When parsing a message with more than one Zone record,
+    // exception should be thrown.
+    EXPECT_THROW(msg.fromWire(buf), isc::d2::InvalidZoneSection);
+}
+
+// This test verifies that the wire format of the message is produced
+// in the render mode.
+TEST_F(D2UpdateMessageTest, toWire) {
+    D2UpdateMessage msg;
+    // Set message ID.
+    msg.setId(0x1234);
+    // Rcode to NOERROR.
+    msg.setRcode(Rcode(Rcode::NOERROR_CODE));
+
+    // Set Zone section. This section must comprise exactly
+    // one Zone. toWire function would fail if Zone is not set.
+    msg.setZone(Name("example.com"), RRClass::IN());
+
+    // Set prerequisities.
+
+    // 'Name Is Not In Use' prerequisite (RFC 2136, section 2.4.5)
+    RRsetPtr prereq1(new RRset(Name("foo.example.com"), RRClass::NONE(),
+                               RRType::ANY(), RRTTL(0)));
+    msg.addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq1);
+
+    // 'Name is In Use' prerequisite (RFC 2136, section 2.4.4)
+    RRsetPtr prereq2(new RRset(Name("bar.example.com"), RRClass::ANY(),
+                               RRType::ANY(), RRTTL(0)));
+    msg.addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq2);
+
+    // Set Update Section.
+
+    // Create RR holding a name being added. This RR is constructed
+    // in conformance to RFC 2136, section 2.5.1.
+    RRsetPtr updaterr1(new RRset(Name("foo.example.com"), RRClass::IN(),
+                                 RRType::A(), RRTTL(10)));
+    // RR record is of the type A, thus RDATA holds 4 octet Internet
+    // address. This address is 10.10.1.1.
+    char rdata1[] = {
+        0xA, 0xA , 0x1, 0x1
+    };
+    InputBuffer buf_rdata1(rdata1, 4);
+    updaterr1->addRdata(createRdata(RRType::A(), RRClass::IN(), buf_rdata1,
+                                    buf_rdata1.getLength()));
+    // Add the RR to the message.
+    msg.addRRset(D2UpdateMessage::SECTION_UPDATE, updaterr1);
+
+    // Render message into the wire format.
+    MessageRenderer renderer;
+    ASSERT_NO_THROW(msg.toWire(renderer));
+
+    // Make sure that created packet is not truncated.
+    ASSERT_EQ(77, renderer.getLength());
+
+    // Create input buffer from the rendered data. InputBuffer
+    // is handy to validate the byte contents of the rendered
+    // message.
+    InputBuffer buf(renderer.getData(), renderer.getLength());
+
+    // Start validating the message header.
+
+    // Verify message ID.
+    EXPECT_EQ(0x1234, buf.readUint16());
+    // The 2-bytes following message ID comprise the following fields:
+    // - QR - 1 bit indicating that it is REQUEST. Should be 0.
+    // - Opcode - 4 bits which should hold value of 5 indicating this is
+    //            an Update message. Binary form is "0101".
+    // - Z - These bits are unused for Update Message and should be 0.
+    // - RCODE - Response code, set to NOERROR for REQUEST. It is 0.
+    //8706391835
+    // The binary value is:
+    //   0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
+    // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+    // | QR|     Opcode    |           Z               |     RCODE     |
+    // +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+    // | 0 | 0   1   0   1 | 0   0   0   0   0   0   0 | 0   0   0   0 |
+    // +---+---+---+-------+---+---+---+---+---+---+---+---+---+---+---+
+    // and the hexadecimal representation is 0x2800.
+    EXPECT_EQ(0x2800, buf.readUint16());
+
+    // ZOCOUNT - holds the number of zones for the update. For Request
+    // message it must be exactly one record (RFC2136, section 2.3).
+    EXPECT_EQ(1, buf.readUint16());
+
+    // PRCOUNT - holds the number of prerequisites. Earlier we have added
+    // two prerequisites. Thus, expect that this conter is 2.
+    EXPECT_EQ(2, buf.readUint16());
+
+    // UPCOUNT - holds the number of RRs in the Update Section. We have
+    // added 1 RR, which adds the name foo.example.com to the Zone.
+    EXPECT_EQ(1, buf.readUint16());
+
+    // ADCOUNT - holds the number of RRs in the Additional Data Section.
+    EXPECT_EQ(0, buf.readUint16());
+
+    // Start validating the Zone section. This section comprises the
+    // following data:
+    // - ZNAME
+    // - ZTYPE
+    // - ZCLASS
+
+    // ZNAME holds 'example.com.' encoded as set of labels. Each label
+    // is preceded by its length. The name is ended with the byte holding
+    // zero value. This yields the total size of the name in wire format
+    // of 13 bytes.
+
+    // The simplest way to convert the name from wire format to a string
+    // is to use dns::Name class. It should be ok to rely on the Name class
+    // to decode the name, because it is unit tested elswhere.
+    std::string zone_name = readNameFromWire(buf, 13);
+    EXPECT_EQ("example.com.", zone_name);
+
+    // ZTYPE of the Zone section must be SOA according to RFC 2136,
+    // section 2.3.
+    EXPECT_EQ(RRType::SOA().getCode(), buf.readUint16());
+
+    // ZCLASS of the Zone section is IN.
+    EXPECT_EQ(RRClass::IN().getCode(), buf.readUint16());
+
+    // Start checks on Prerequisite section. Each prerequisite comprises
+    // the following fields:
+    // - NAME - name of the RR in wire format
+    // - TYPE - two octets with one of the RR TYPE codes
+    // - CLASS - two octets with one of the RR CLASS codes
+    // - TTL - a 32-bit signed integer specifying Time-To-Live
+    // - RDLENGTH - length of the RDATA field
+    // - RDATA - a variable length string of octets containing
+    //           resource data.
+    // In case of this message, we expect to have two prerequisite RRs.
+    // Their structure is checked below.
+
+    // First prerequisite should comprise the 'Name is not in use prerequisite'
+    // for 'foo.example.com'.
+
+    // Check the name first. Message renderer is using compression for domain
+    // names as described in RFC 1035, section 4.1.4. The name in this RR is
+    // foo.example.com. The name of the zone is example.com and it has occured
+    // in this message already at offset 12 (the size of the header is 12).
+    // Therefore, name of this RR is encoded as 'foo', followed by a pointer
+    // to offset in this message where the remainder of this name was used.
+    // This pointer has the following format:
+    // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // | 1  1|                 OFFSET                  |
+    // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // | 1  1| 0  0  0  0  0  0  0  0  0  0  1  1  0  0|
+    // +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+    // which has a following hexadecimal representation: 0xC00C
+
+    // Let's read the non-compressed part first - 'foo.'
+    std::string name_prereq1 = readNameFromWire(buf, 4, true);
+    EXPECT_EQ("foo.", name_prereq1);
+    // The remaining two bytes hold the pointer to 'example.com'.
+    EXPECT_EQ(0xC00C, buf.readUint16());
+    // TYPE is ANY
+    EXPECT_EQ(RRType::ANY().getCode(), buf.readUint16());
+    // CLASS is NONE
+    EXPECT_EQ(RRClass::NONE().getCode(), buf.readUint16());
+    // TTL is a 32-but value, expecting 0
+    EXPECT_EQ(0, buf.readUint32());
+    // There is no RDATA, so RDLENGTH is 0
+    EXPECT_EQ(0, buf.readUint16());
+
+    // Start checking second prerequisite.
+
+    std::string name_prereq2 = readNameFromWire(buf, 4, true);
+    EXPECT_EQ("bar.", name_prereq2);
+    // The remaining two bytes hold the pointer to 'example.com'.
+    EXPECT_EQ(0xC00C, buf.readUint16());
+    // TYPE is ANY
+    EXPECT_EQ(RRType::ANY().getCode(), buf.readUint16());
+    // CLASS is ANY
+    EXPECT_EQ(RRClass::ANY().getCode(), buf.readUint16());
+    // TTL is a 32-but value, expecting 0
+    EXPECT_EQ(0, buf.readUint32());
+    // There is no RDATA, so RDLENGTH is 0
+    EXPECT_EQ(0, buf.readUint16());
+
+    // Start checking Update section. This section contains RRset with
+    // one A RR.
+
+    // The name of the RR is 'foo.example.com'. It is encoded in the
+    // compressed format - as a pointer to the name of prerequisite 1.
+    // This name is in offset 0x1D in this message.
+    EXPECT_EQ(0xC01D, buf.readUint16());
+    // TYPE is A
+    EXPECT_EQ(RRType::A().getCode(), buf.readUint16());
+    // CLASS is IN (same as zone class)
+    EXPECT_EQ(RRClass::IN().getCode(), buf.readUint16());
+    // TTL is a 32-but value, set here to 10.
+    EXPECT_EQ(10, buf.readUint32());
+    // For A records, the RDATA comprises the 4-byte Internet address.
+    // So, RDLENGTH is 4.
+    EXPECT_EQ(4, buf.readUint16());
+    // We have stored the following address in RDATA field: 10.10.1.1
+    // (which is 0A 0A 01 01) in hexadecimal format.
+    EXPECT_EQ(0x0A0A0101, buf.readUint32());
+
+    // @todo: consider extending this test to verify Additional Data
+    // section.
+}
+
+// This test verifies that an attempt to call toWire function on the
+// received message will result in an exception.
+TEST_F(D2UpdateMessageTest, toWireInvalidQRFlag) {
+    // This is a binary representation of the DNS message.
+    // This message is valid and should be parsed with no
+    // error.
+    const uint8_t bin_msg[] = {
+        0x05, 0xAF, // ID=0x05AF
+        0xA8, 0x6,  // QR=1, Opcode=6, RCODE=YXDOMAIN
+        0x0, 0x0,   // ZOCOUNT=0
+        0x0, 0x0,   // PRCOUNT=0
+        0x0, 0x0,   // UPCOUNT=0
+        0x0, 0x0    // ADCOUNT=0
+    };
+
+    InputBuffer buf(bin_msg, sizeof(bin_msg));
+    // The 'true' argument passed to the constructor turns the
+    // message into the parse mode in which the fromWire function
+    // can be used to decode the binary mesasage data.
+    D2UpdateMessage msg(D2UpdateMessage::INBOUND);
+    ASSERT_NO_THROW(msg.fromWire(buf));
+
+    // The message is parsed. The QR Flag should now indicate that
+    // it is a Response message.
+    ASSERT_EQ(D2UpdateMessage::RESPONSE, msg.getQRFlag());
+
+    // An attempt to call toWire on the Response message should
+    // result in the InvalidQRFlag exception.
+    MessageRenderer renderer;
+    EXPECT_THROW(msg.toWire(renderer), isc::d2::InvalidQRFlag);
+}
+
+} // End of anonymous namespace
diff --git a/src/bin/d2/tests/d2_zone_unittests.cc b/src/bin/d2/tests/d2_zone_unittests.cc
new file mode 100644
index 0000000..853cdbe
--- /dev/null
+++ b/src/bin/d2/tests/d2_zone_unittests.cc
@@ -0,0 +1,75 @@
+// 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 <config.h>
+#include <d2/d2_zone.h>
+#include <gtest/gtest.h>
+
+using namespace std;
+using namespace isc;
+using namespace isc::d2;
+using namespace isc::dns;
+
+namespace {
+
+// This test verifies that Zone object is created and its constructor sets
+// appropriate values for its members.
+TEST(D2ZoneTest, constructor) {
+    // Create first object.
+    D2Zone zone1(Name("example.com"), RRClass::ANY());
+    EXPECT_EQ("example.com.", zone1.getName().toText());
+    EXPECT_EQ(RRClass::ANY().getCode(), zone1.getClass().getCode());
+    // Create another object to make sure that constructor doesn't assign
+    // fixed values, but they change when constructor's parameters change.
+    D2Zone zone2(Name("foo.example.com"), RRClass::IN());
+    EXPECT_EQ("foo.example.com.", zone2.getName().toText());
+    EXPECT_EQ(RRClass::IN().getCode(), zone2.getClass().getCode());
+}
+
+// This test verifies that toText() function returns text representation of
+// of the zone in expected format.
+TEST(D2ZoneTest, toText) {
+    // Create first object.
+    D2Zone zone1(Name("example.com"), RRClass::ANY());
+    EXPECT_EQ("example.com. ANY SOA\n", zone1.toText());
+    // Create another object with different parameters to make sure that the
+    // function's output changes accordingly.
+    D2Zone zone2(Name("foo.example.com"), RRClass::IN());
+    EXPECT_EQ("foo.example.com. IN SOA\n", zone2.toText());
+}
+
+// This test verifies that the equality and inequality operators behave as
+// expected.
+TEST(D2ZoneTest, compare) {
+    const Name a("a"), b("b");
+    const RRClass in(RRClass::IN()), any(RRClass::ANY());
+
+    // Equality check
+    EXPECT_TRUE(D2Zone(a, any) == D2Zone(a, any));
+    EXPECT_FALSE(D2Zone(a, any) != D2Zone(a, any));
+
+    // Inequality check, objects differ by class.
+    EXPECT_FALSE(D2Zone(a, any) == D2Zone(a, in));
+    EXPECT_TRUE(D2Zone(a, any) != D2Zone(a, in));
+
+    // Inequality check, objects differ by name.
+    EXPECT_FALSE(D2Zone(a, any) == D2Zone(b, any));
+    EXPECT_TRUE(D2Zone(a, any) != D2Zone(b, any));
+
+    // Inequality check, objects differ by name and class.
+    EXPECT_FALSE(D2Zone(a, any) == D2Zone(b, in));
+    EXPECT_TRUE(D2Zone(a, any) != D2Zone(b, in));
+}
+
+} // End of anonymous namespace



More information about the bind10-changes mailing list