summaryrefslogtreecommitdiffstats
path: root/src/lib/d2srv/d2_update_message.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/d2srv/d2_update_message.h')
-rw-r--r--src/lib/d2srv/d2_update_message.h357
1 files changed, 357 insertions, 0 deletions
diff --git a/src/lib/d2srv/d2_update_message.h b/src/lib/d2srv/d2_update_message.h
new file mode 100644
index 0000000..8365128
--- /dev/null
+++ b/src/lib/d2srv/d2_update_message.h
@@ -0,0 +1,357 @@
+// Copyright (C) 2013-2021 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef D2_UPDATE_MESSAGE_H
+#define D2_UPDATE_MESSAGE_H
+
+#include <d2srv/d2_zone.h>
+#include <dns/message.h>
+#include <dns/name.h>
+#include <dns/rcode.h>
+#include <dns/rrclass.h>
+#include <dns/rrset.h>
+#include <dns/tsig.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 Exception indicating that a signed, inbound message failed to verify
+///
+/// This exception is thrown when TSIG verification of a DNS server's response
+/// fails.
+class TSIGVerifyError : public Exception {
+public:
+ TSIGVerifyError(const char* file, size_t line, const char* what) :
+ isc::Exception(file, line, what) {}
+};
+
+
+class D2UpdateMessage;
+
+/// @brief Pointer to the DNS Update Message.
+typedef boost::shared_ptr<D2UpdateMessage> D2UpdateMessagePtr;
+
+/// @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 whether 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.
+ ///
+ /// If given a TSIG context, this method will pass the context down into
+ /// dns::Message.toWire() method which signs the message using the context.
+ ///
+ /// 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.
+ /// @param tsig_ctx A TSIG context that is to be used for signing the
+ /// message. If NULL the message will not be signed.
+ void toWire(dns::AbstractMessageRenderer& renderer,
+ dns::TSIGContext* const tsig_ctx = NULL);
+
+ /// @brief Decode incoming message from the wire format.
+ ///
+ /// This function decodes the DNS Update message stored in the buffer
+ /// specified by the function argument. If given a TSIG context, then
+ /// the function will first attempt to use that context to verify the
+ /// message signature. If verification fails a TSIGVerifyError exception
+ /// will be thrown. The function then 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 received_data buffer holding DNS Update message to be parsed.
+ /// @param bytes_received the number of bytes in received_data
+ /// @param tsig_context A TSIG context that is to be used to verify the
+ /// message. If NULL TSIG verification will not be attempted.
+ void fromWire(const void* received_data, size_t bytes_received,
+ dns::TSIGContext* const tsig_context = NULL);
+ //@}
+
+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