summaryrefslogtreecommitdiffstats
path: root/src/lib/dns/message.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:15:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:15:43 +0000
commitf5f56e1a1c4d9e9496fcb9d81131066a964ccd23 (patch)
tree49e44c6f87febed37efb953ab5485aa49f6481a7 /src/lib/dns/message.cc
parentInitial commit. (diff)
downloadisc-kea-f5f56e1a1c4d9e9496fcb9d81131066a964ccd23.tar.xz
isc-kea-f5f56e1a1c4d9e9496fcb9d81131066a964ccd23.zip
Adding upstream version 2.4.1.upstream/2.4.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib/dns/message.cc')
-rw-r--r--src/lib/dns/message.cc1176
1 files changed, 1176 insertions, 0 deletions
diff --git a/src/lib/dns/message.cc b/src/lib/dns/message.cc
new file mode 100644
index 0000000..fe05a3e
--- /dev/null
+++ b/src/lib/dns/message.cc
@@ -0,0 +1,1176 @@
+// Copyright (C) 2009-2023 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/.
+
+#include <config.h>
+
+#include <stdint.h>
+
+#include <algorithm>
+#include <cassert>
+#include <string>
+#include <sstream>
+#include <vector>
+
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <exceptions/exceptions.h>
+
+#include <util/buffer.h>
+
+#include <dns/edns.h>
+#include <dns/exceptions.h>
+#include <dns/message.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/opcode.h>
+#include <dns/rcode.h>
+#include <dns/question.h>
+#include <dns/rdataclass.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+#include <dns/rrset.h>
+#include <dns/tsig.h>
+
+using namespace std;
+using boost::lexical_cast;
+using namespace isc::dns::rdata;
+using namespace isc::util;
+
+namespace isc {
+namespace dns {
+
+namespace {
+// protocol constants
+const size_t HEADERLEN = 12;
+
+const unsigned int OPCODE_MASK = 0x7800;
+const unsigned int OPCODE_SHIFT = 11;
+const unsigned int RCODE_MASK = 0x000f;
+
+// This diagram shows the wire-format representation of the 2nd 16 bits of
+// the DNS header section, which contain all defined flag bits.
+//
+// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+// |QR| Opcode |AA|TC|RD|RA| |AD|CD| RCODE |
+// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+// 1 0 0 0| 0 1 1 1| 1 0 1 1| 0 0 0 0|
+// 0x8 0x7 0xb 0x0
+//
+// This mask covers all the flag bits, and those bits only.
+// Note: we reject a "flag" the is not covered by this mask in some of the
+// public methods. This means our current definition is not fully extendable;
+// applications cannot introduce a new flag bit temporarily without modifying
+// the source code.
+const unsigned int HEADERFLAG_MASK = 0x87b0;
+
+// This is a set of flag bits that should be preserved when building a reply
+// from a request.
+// Note: we assume the specific definition of HEADERFLAG_xx. We may change
+// the definition in future, in which case we need to adjust this definition,
+// too (see also the description about the Message::HeaderFlag type).
+const uint16_t MESSAGE_REPLYPRESERVE = (Message::HEADERFLAG_RD |
+ Message::HEADERFLAG_CD);
+
+const char* const sectiontext[] = {
+ "QUESTION",
+ "ANSWER",
+ "AUTHORITY",
+ "ADDITIONAL"
+};
+}
+
+class MessageImpl {
+public:
+ MessageImpl(Message::Mode mode);
+ // Open issues: should we rather have a header in wire-format
+ // for efficiency?
+ Message::Mode mode_;
+ qid_t qid_;
+
+ // We want to use NULL for [op,r]code_ to mean the code being not
+ // correctly parsed or set. We store the real code object in
+ // xxcode_placeholder_ and have xxcode_ refer to it when the object
+ // is valid.
+ const Rcode* rcode_;
+ Rcode rcode_placeholder_;
+ const Opcode* opcode_;
+ Opcode opcode_placeholder_;
+
+ uint16_t flags_; // wire-format representation of header flags.
+
+ bool header_parsed_;
+ static const unsigned int NUM_SECTIONS = 4; // TODO: revisit this design
+ int counts_[NUM_SECTIONS]; // TODO: revisit this definition
+ vector<QuestionPtr> questions_;
+ vector<RRsetPtr> rrsets_[NUM_SECTIONS];
+ ConstEDNSPtr edns_;
+ ConstTSIGRecordPtr tsig_rr_;
+
+ // RRsetsSorter* sorter_; : TODO
+
+ void init();
+ void setOpcode(const Opcode& opcode);
+ void setRcode(const Rcode& rcode);
+ int parseQuestion(InputBuffer& buffer);
+ int parseSection(const Message::Section section, InputBuffer& buffer,
+ Message::ParseOptions options);
+ void addRR(Message::Section section, const Name& name,
+ const RRClass& rrclass, const RRType& rrtype,
+ const RRTTL& ttl, ConstRdataPtr rdata,
+ Message::ParseOptions options);
+ // There are also times where an RR needs to be added that
+ // represents an empty RRset. There is no Rdata in that case
+ void addRR(Message::Section section, const Name& name,
+ const RRClass& rrclass, const RRType& rrtype,
+ const RRTTL& ttl, Message::ParseOptions options);
+ void addEDNS(Message::Section section, const Name& name,
+ const RRClass& rrclass, const RRType& rrtype,
+ const RRTTL& ttl, const Rdata& rdata);
+ void addTSIG(Message::Section section, unsigned int count,
+ const InputBuffer& buffer, size_t start_position,
+ const Name& name, const RRClass& rrclass,
+ const RRTTL& ttl, const Rdata& rdata);
+ void toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx);
+};
+
+/// @brief Pointer to the @ref MessageImpl object.
+typedef boost::shared_ptr<MessageImpl> MessageImplPtr;
+
+MessageImpl::MessageImpl(Message::Mode mode) :
+ mode_(mode),
+ rcode_placeholder_(Rcode(0)), // for placeholders the value doesn't matter
+ opcode_placeholder_(Opcode(0)) {
+ init();
+}
+
+void
+MessageImpl::init() {
+ flags_ = 0;
+ qid_ = 0;
+ rcode_ = NULL;
+ opcode_ = NULL;
+ edns_ = EDNSPtr();
+ tsig_rr_ = ConstTSIGRecordPtr();
+
+ for (int i = 0; i < NUM_SECTIONS; ++i) {
+ counts_[i] = 0;
+ }
+
+ header_parsed_ = false;
+ questions_.clear();
+ rrsets_[Message::SECTION_ANSWER].clear();
+ rrsets_[Message::SECTION_AUTHORITY].clear();
+ rrsets_[Message::SECTION_ADDITIONAL].clear();
+}
+
+void
+MessageImpl::setOpcode(const Opcode& opcode) {
+ opcode_placeholder_ = opcode;
+ opcode_ = &opcode_placeholder_;
+}
+
+void
+MessageImpl::setRcode(const Rcode& rcode) {
+ rcode_placeholder_ = rcode;
+ rcode_ = &rcode_placeholder_;
+}
+
+namespace {
+// This helper class is used by MessageImpl::toWire() to render a set of
+// RRsets of a specific section of message to a given MessageRenderer.
+//
+// A RenderSection object is expected to be used with a QuestionIterator or
+// SectionIterator. Its operator() is called for each RRset as the iterator
+// iterates over the corresponding section, and it renders the RRset to
+// the given MessageRenderer, while counting the number of RRs (note: not
+// RRsets) successfully rendered. If the MessageRenderer reports the need
+// for truncation (via its isTruncated() method), the RenderSection object
+// stops rendering further RRsets. In addition, unless partial_ok (given on
+// construction) is true, it removes any RRs that are partially rendered
+// from the MessageRenderer.
+//
+// On the completion of rendering the entire section, the owner of the
+// RenderSection object can get the number of rendered RRs via the
+// getTotalCount() method.
+template <typename T>
+struct RenderSection {
+ RenderSection(AbstractMessageRenderer& renderer, const bool partial_ok) :
+ counter_(0), renderer_(renderer), partial_ok_(partial_ok),
+ truncated_(false) {
+ }
+
+ void operator()(const T& entry) {
+ // If it's already truncated, ignore the rest of the section.
+ if (truncated_) {
+ return;
+ }
+ const size_t pos0 = renderer_.getLength();
+ counter_ += entry->toWire(renderer_);
+ if (renderer_.isTruncated()) {
+ truncated_ = true;
+ if (!partial_ok_) {
+ // roll back to the end of the previous RRset.
+ renderer_.trim(renderer_.getLength() - pos0);
+ }
+ }
+ }
+
+ unsigned int getTotalCount() {
+ return (counter_);
+ }
+
+ unsigned int counter_;
+
+ AbstractMessageRenderer& renderer_;
+
+ const bool partial_ok_;
+
+ bool truncated_;
+};
+}
+
+void
+MessageImpl::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
+ if (mode_ != Message::RENDER) {
+ isc_throw(InvalidMessageOperation,
+ "Message rendering attempted in non render mode");
+ }
+ if (rcode_ == NULL) {
+ isc_throw(InvalidMessageOperation,
+ "Message rendering attempted without Rcode set");
+ }
+ if (opcode_ == NULL) {
+ isc_throw(InvalidMessageOperation,
+ "Message rendering attempted without Opcode set");
+ }
+
+ // Reserve the space for TSIG (if needed) so that we can handle truncation
+ // case correctly later when that happens. orig_xxx variables remember
+ // some configured parameters of renderer in case they are needed in
+ // truncation processing below.
+ const size_t tsig_len = (tsig_ctx != NULL) ? tsig_ctx->getTSIGLength() : 0;
+ const size_t orig_msg_len_limit = renderer.getLengthLimit();
+ const AbstractMessageRenderer::CompressMode orig_compress_mode =
+ renderer.getCompressMode();
+
+ // We are going to skip soon, so we need to clear the renderer
+ // But we'll leave the length limit and the compress mode intact
+ // (or shortened in case of TSIG)
+ renderer.clear();
+ renderer.setCompressMode(orig_compress_mode);
+
+ if (tsig_len > 0) {
+ if (tsig_len > orig_msg_len_limit) {
+ isc_throw(InvalidParameter, "Failed to render DNS message: "
+ "too small limit for a TSIG (" <<
+ orig_msg_len_limit << ")");
+ }
+ renderer.setLengthLimit(orig_msg_len_limit - tsig_len);
+ } else {
+ renderer.setLengthLimit(orig_msg_len_limit);
+ }
+
+ // reserve room for the header
+ if (renderer.getLengthLimit() < HEADERLEN) {
+ isc_throw(InvalidParameter, "Failed to render DNS message: "
+ "too small limit for a Header");
+ }
+ renderer.skip(HEADERLEN);
+
+ uint16_t qdcount =
+ for_each(questions_.begin(), questions_.end(),
+ RenderSection<QuestionPtr>(renderer, false)).getTotalCount();
+
+ // TODO: sort RRsets in each section based on configuration policy.
+ uint16_t ancount = 0;
+ if (!renderer.isTruncated()) {
+ ancount =
+ for_each(rrsets_[Message::SECTION_ANSWER].begin(),
+ rrsets_[Message::SECTION_ANSWER].end(),
+ RenderSection<RRsetPtr>(renderer, true)).getTotalCount();
+ }
+ uint16_t nscount = 0;
+ if (!renderer.isTruncated()) {
+ nscount =
+ for_each(rrsets_[Message::SECTION_AUTHORITY].begin(),
+ rrsets_[Message::SECTION_AUTHORITY].end(),
+ RenderSection<RRsetPtr>(renderer, true)).getTotalCount();
+ }
+ uint16_t arcount = 0;
+ if (renderer.isTruncated()) {
+ flags_ |= Message::HEADERFLAG_TC;
+ } else {
+ arcount =
+ for_each(rrsets_[Message::SECTION_ADDITIONAL].begin(),
+ rrsets_[Message::SECTION_ADDITIONAL].end(),
+ RenderSection<RRsetPtr>(renderer, false)).getTotalCount();
+ }
+
+ // Add EDNS OPT RR if necessary. Basically, we add it only when EDNS
+ // has been explicitly set. However, if the RCODE would require it and
+ // no EDNS has been set we generate a temporary local EDNS and use it.
+ if (!renderer.isTruncated()) {
+ ConstEDNSPtr local_edns = edns_;
+ if (!local_edns && rcode_->getExtendedCode() != 0) {
+ local_edns = ConstEDNSPtr(new EDNS());
+ }
+ if (local_edns) {
+ arcount += local_edns->toWire(renderer, rcode_->getExtendedCode());
+ }
+ }
+
+ // If we're adding a TSIG to a truncated message, clear all RRsets
+ // from the message except for the question before adding the TSIG.
+ // If even (some of) the question doesn't fit, don't include it.
+ if (tsig_ctx != NULL && renderer.isTruncated()) {
+ renderer.clear();
+ renderer.setLengthLimit(orig_msg_len_limit - tsig_len);
+ renderer.setCompressMode(orig_compress_mode);
+ renderer.skip(HEADERLEN);
+ qdcount = for_each(questions_.begin(), questions_.end(),
+ RenderSection<QuestionPtr>(renderer,
+ false)).getTotalCount();
+ ancount = 0;
+ nscount = 0;
+ arcount = 0;
+ }
+
+ // Adjust the counter buffer.
+ // XXX: these may not be equal to the number of corresponding entries
+ // in rrsets_[] or questions_ if truncation occurred or an EDNS OPT RR
+ // was inserted. This is not good, and we should revisit the entire
+ // design.
+ counts_[Message::SECTION_QUESTION] = qdcount;
+ counts_[Message::SECTION_ANSWER] = ancount;
+ counts_[Message::SECTION_AUTHORITY] = nscount;
+ counts_[Message::SECTION_ADDITIONAL] = arcount;
+
+ // fill in the header
+ size_t header_pos = 0;
+ renderer.writeUint16At(qid_, header_pos);
+ header_pos += sizeof(uint16_t);
+
+ uint16_t codes_and_flags =
+ (opcode_->getCode() << OPCODE_SHIFT) & OPCODE_MASK;
+ codes_and_flags |= (rcode_->getCode() & RCODE_MASK);
+ codes_and_flags |= (flags_ & HEADERFLAG_MASK);
+ renderer.writeUint16At(codes_and_flags, header_pos);
+ header_pos += sizeof(uint16_t);
+ // TODO: should avoid repeated pattern
+ renderer.writeUint16At(qdcount, header_pos);
+ header_pos += sizeof(uint16_t);
+ renderer.writeUint16At(ancount, header_pos);
+ header_pos += sizeof(uint16_t);
+ renderer.writeUint16At(nscount, header_pos);
+ header_pos += sizeof(uint16_t);
+ renderer.writeUint16At(arcount, header_pos);
+
+ // Add TSIG, if necessary, at the end of the message.
+ if (tsig_ctx != NULL) {
+ // Release the reserved space in the renderer.
+ renderer.setLengthLimit(orig_msg_len_limit);
+
+ const int tsig_count =
+ tsig_ctx->sign(qid_, renderer.getData(),
+ renderer.getLength())->toWire(renderer);
+ if (tsig_count != 1) {
+ isc_throw(Unexpected, "Failed to render a TSIG RR");
+ }
+
+ // update the ARCOUNT for the TSIG RR. Note that for a sane DNS
+ // message arcount should never overflow to 0.
+ renderer.writeUint16At(++arcount, header_pos);
+ }
+}
+
+Message::Message(Mode mode) :
+ impl_(new MessageImpl(mode)) {
+}
+
+bool
+Message::getHeaderFlag(const HeaderFlag flag) const {
+ if (flag == 0 || (flag & ~HEADERFLAG_MASK) != 0) {
+ isc_throw(InvalidParameter,
+ "Message::getHeaderFlag:: Invalid flag is specified: " <<
+ "0x" << std::hex << flag);
+ }
+ return ((impl_->flags_ & flag) != 0);
+}
+
+void
+Message::setHeaderFlag(const HeaderFlag flag, const bool on) {
+ if (impl_->mode_ != Message::RENDER) {
+ isc_throw(InvalidMessageOperation,
+ "setHeaderFlag performed in non-render mode");
+ }
+ if (flag == 0 || (flag & ~HEADERFLAG_MASK) != 0) {
+ isc_throw(InvalidParameter,
+ "Message::getHeaderFlag:: Invalid flag is specified: " <<
+ "0x" << std::hex << static_cast<int>(flag));
+ }
+ if (on) {
+ impl_->flags_ |= flag;
+ } else {
+ impl_->flags_ &= ~flag;
+ }
+}
+
+qid_t
+Message::getQid() const {
+ return (impl_->qid_);
+}
+
+void
+Message::setQid(qid_t qid) {
+ if (impl_->mode_ != Message::RENDER) {
+ isc_throw(InvalidMessageOperation,
+ "setQid performed in non-render mode");
+ }
+ impl_->qid_ = qid;
+}
+
+const Rcode&
+Message::getRcode() const {
+ if (impl_->rcode_ == NULL) {
+ isc_throw(InvalidMessageOperation, "getRcode attempted before set");
+ }
+ return (*impl_->rcode_);
+}
+
+void
+Message::setRcode(const Rcode& rcode) {
+ if (impl_->mode_ != Message::RENDER) {
+ isc_throw(InvalidMessageOperation,
+ "setRcode performed in non-render mode");
+ }
+ impl_->setRcode(rcode);
+}
+
+const Opcode&
+Message::getOpcode() const {
+ if (impl_->opcode_ == NULL) {
+ isc_throw(InvalidMessageOperation, "getOpcode attempted before set");
+ }
+ return (*impl_->opcode_);
+}
+
+void
+Message::setOpcode(const Opcode& opcode) {
+ if (impl_->mode_ != Message::RENDER) {
+ isc_throw(InvalidMessageOperation,
+ "setOpcode performed in non-render mode");
+ }
+ impl_->setOpcode(opcode);
+}
+
+ConstEDNSPtr
+Message::getEDNS() const {
+ return (impl_->edns_);
+}
+
+void
+Message::setEDNS(ConstEDNSPtr edns) {
+ if (impl_->mode_ != Message::RENDER) {
+ isc_throw(InvalidMessageOperation,
+ "setEDNS performed in non-render mode");
+ }
+ impl_->edns_ = edns;
+}
+
+const TSIGRecord*
+Message::getTSIGRecord() const {
+ if (impl_->mode_ != Message::PARSE) {
+ isc_throw(InvalidMessageOperation,
+ "getTSIGRecord performed in non-parse mode");
+ }
+
+ return (impl_->tsig_rr_.get());
+}
+
+unsigned int
+Message::getRRCount(const Section section) const {
+ if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
+ isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
+ }
+ return (impl_->counts_[section]);
+}
+
+void
+Message::addRRset(const Section section, RRsetPtr rrset) {
+ if (!rrset) {
+ isc_throw(InvalidParameter,
+ "NULL RRset is given to Message::addRRset");
+ }
+ if (impl_->mode_ != Message::RENDER) {
+ isc_throw(InvalidMessageOperation,
+ "addRRset performed in non-render mode");
+ }
+ if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
+ isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
+ }
+
+ impl_->rrsets_[section].push_back(rrset);
+ impl_->counts_[section] += rrset->getRdataCount();
+ impl_->counts_[section] += rrset->getRRsigDataCount();
+}
+
+bool
+Message::hasRRset(const Section section, const Name& name,
+ const RRClass& rrclass, const RRType& rrtype) const {
+ if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
+ isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
+ }
+
+ BOOST_FOREACH(ConstRRsetPtr r, impl_->rrsets_[section]) {
+ if (r->getClass() == rrclass &&
+ r->getType() == rrtype &&
+ r->getName() == name) {
+ return (true);
+ }
+ }
+
+ return (false);
+}
+
+bool
+Message::hasRRset(const Section section, const RRsetPtr& rrset) const {
+ return (hasRRset(section, rrset->getName(),
+ rrset->getClass(), rrset->getType()));
+}
+
+bool
+Message::removeRRset(const Section section, RRsetIterator& iterator) {
+ if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
+ isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
+ }
+
+ bool removed = false;
+ for (vector<RRsetPtr>::iterator i = impl_->rrsets_[section].begin();
+ i != impl_->rrsets_[section].end(); ++i) {
+ if (((*i)->getName() == (*iterator)->getName()) &&
+ ((*i)->getClass() == (*iterator)->getClass()) &&
+ ((*i)->getType() == (*iterator)->getType())) {
+
+ // Found the matching RRset so remove it & ignore rest
+ impl_->counts_[section] -= (*iterator)->getRdataCount();
+ impl_->counts_[section] -= (*iterator)->getRRsigDataCount();
+ impl_->rrsets_[section].erase(i);
+ removed = true;
+ break;
+ }
+ }
+
+ return (removed);
+}
+
+void
+Message::clearSection(const Section section) {
+ if (impl_->mode_ != Message::RENDER) {
+ isc_throw(InvalidMessageOperation,
+ "clearSection performed in non-render mode");
+ }
+ if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
+ isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
+ }
+ if (section == Message::SECTION_QUESTION) {
+ impl_->questions_.clear();
+ } else {
+ impl_->rrsets_[section].clear();
+ }
+ impl_->counts_[section] = 0;
+}
+
+void
+Message::addQuestion(const QuestionPtr question) {
+ if (impl_->mode_ != Message::RENDER) {
+ isc_throw(InvalidMessageOperation,
+ "addQuestion performed in non-render mode");
+ }
+
+ impl_->questions_.push_back(question);
+ ++impl_->counts_[SECTION_QUESTION];
+}
+
+void
+Message::addQuestion(const Question& question) {
+ addQuestion(QuestionPtr(new Question(question)));
+}
+
+void
+Message::toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx) {
+ impl_->toWire(renderer, tsig_ctx);
+}
+
+void
+Message::parseHeader(InputBuffer& buffer) {
+ if (impl_->mode_ != Message::PARSE) {
+ isc_throw(InvalidMessageOperation,
+ "Message parse attempted in non parse mode");
+ }
+
+ if (impl_->header_parsed_) {
+ return;
+ }
+
+ if ((buffer.getLength() - buffer.getPosition()) < HEADERLEN) {
+ isc_throw(MessageTooShort, "Malformed DNS message (short length): "
+ << buffer.getLength() - buffer.getPosition());
+ }
+
+ impl_->qid_ = buffer.readUint16();
+ const uint16_t codes_and_flags = buffer.readUint16();
+ impl_->setOpcode(Opcode((codes_and_flags & OPCODE_MASK) >> OPCODE_SHIFT));
+ impl_->setRcode(Rcode(codes_and_flags & RCODE_MASK));
+ impl_->flags_ = (codes_and_flags & HEADERFLAG_MASK);
+ impl_->counts_[SECTION_QUESTION] = buffer.readUint16();
+ impl_->counts_[SECTION_ANSWER] = buffer.readUint16();
+ impl_->counts_[SECTION_AUTHORITY] = buffer.readUint16();
+ impl_->counts_[SECTION_ADDITIONAL] = buffer.readUint16();
+
+ impl_->header_parsed_ = true;
+}
+
+void
+Message::fromWire(InputBuffer& buffer, ParseOptions options) {
+ if (impl_->mode_ != Message::PARSE) {
+ isc_throw(InvalidMessageOperation,
+ "Message parse attempted in non parse mode");
+ }
+
+ // Clear any old parsed data
+ clear(Message::PARSE);
+
+ buffer.setPosition(0);
+ parseHeader(buffer);
+
+ impl_->counts_[SECTION_QUESTION] = impl_->parseQuestion(buffer);
+ impl_->counts_[SECTION_ANSWER] =
+ impl_->parseSection(SECTION_ANSWER, buffer, options);
+ impl_->counts_[SECTION_AUTHORITY] =
+ impl_->parseSection(SECTION_AUTHORITY, buffer, options);
+ impl_->counts_[SECTION_ADDITIONAL] =
+ impl_->parseSection(SECTION_ADDITIONAL, buffer, options);
+}
+
+int
+MessageImpl::parseQuestion(InputBuffer& buffer) {
+ unsigned int added = 0;
+
+ for (unsigned int count = 0;
+ count < counts_[Message::SECTION_QUESTION];
+ ++count) {
+ const Name name(buffer);
+
+ if ((buffer.getLength() - buffer.getPosition()) <
+ 2 * sizeof(uint16_t)) {
+ isc_throw(DNSMessageFORMERR, "Question section too short: " <<
+ (buffer.getLength() - buffer.getPosition()) << " bytes");
+ }
+ const RRType rrtype(buffer.readUint16());
+ const RRClass rrclass(buffer.readUint16());
+
+ // XXX: need a duplicate check. We might also want to have an
+ // optimized algorithm that requires the question section contain
+ // exactly one RR.
+
+ questions_.push_back(QuestionPtr(new Question(name, rrclass, rrtype)));
+ ++added;
+ }
+
+ return (added);
+}
+
+namespace {
+struct MatchRR {
+ MatchRR(const Name& name, const RRType& rrtype, const RRClass& rrclass) :
+ name_(name), rrtype_(rrtype), rrclass_(rrclass) {
+ }
+
+ bool operator()(const RRsetPtr& rrset) const {
+ return (rrset->getType() == rrtype_ &&
+ rrset->getClass() == rrclass_ &&
+ rrset->getName() == name_);
+ }
+
+ const Name& name_;
+
+ const RRType& rrtype_;
+
+ const RRClass& rrclass_;
+};
+}
+
+// Note about design decision:
+// we need some type specific processing here, including EDNS and TSIG.
+// how much we should generalize/hardcode the special logic is subject
+// to discussion. In terms of modularity it would be ideal to introduce
+// an abstract class (say "MessageAttribute") and let other such
+// concrete notions as EDNS or TSIG inherit from it. Then we would
+// just do:
+// message->addAttribute(rrtype, rrclass, buffer);
+// to create and attach type-specific concrete object to the message.
+//
+// A major downside of this approach is, as usual, complexity due to
+// indirection and performance penalty. Also, it may not be so easy
+// to separate the processing logic because in many cases we'll need
+// parse context for which the message class is responsible (e.g.
+// to check the EDNS OPT RR only appears in the additional section,
+// and appears only once).
+//
+// Another point to consider is that we may not need so many special
+// types other than EDNS and TSIG (and when and if we implement it,
+// SIG(0)); newer optional attributes of the message would more likely
+// be standardized as new flags or options of EDNS. If that's the case,
+// introducing an abstract class with all the overhead and complexity
+// may not make much sense.
+//
+// Conclusion: don't over-generalize type-specific logic for now.
+// introduce separate concrete classes, and move context-independent
+// logic to that class; processing logic dependent on parse context
+// is hardcoded here.
+int
+MessageImpl::parseSection(const Message::Section section,
+ InputBuffer& buffer, Message::ParseOptions options) {
+ if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
+ isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
+ }
+
+ unsigned int added = 0;
+
+ for (unsigned int count = 0; count < counts_[section]; ++count) {
+ // We need to remember the start position for TSIG processing
+ const size_t start_position = buffer.getPosition();
+
+ const Name name(buffer);
+
+ // buffer must store at least RR TYPE, RR CLASS, TTL, and RDLEN.
+ if ((buffer.getLength() - buffer.getPosition()) <
+ 3 * sizeof(uint16_t) + sizeof(uint32_t)) {
+ isc_throw(DNSMessageFORMERR, sectiontext[section] <<
+ " section too short: " <<
+ (buffer.getLength() - buffer.getPosition()) << " bytes");
+ }
+
+ const RRType rrtype(buffer.readUint16());
+ const RRClass rrclass(buffer.readUint16());
+ const RRTTL ttl(buffer.readUint32());
+ const size_t rdlen = buffer.readUint16();
+
+ // If class is ANY or NONE, rdlength may be zero, to signal
+ // an empty RRset.
+ // (the class check must be done to differentiate from RRTypes
+ // that can have zero length rdata
+ if ((rrclass == RRClass::ANY() || rrclass == RRClass::NONE()) &&
+ rdlen == 0) {
+ addRR(section, name, rrclass, rrtype, ttl, options);
+ ++added;
+ continue;
+ }
+ ConstRdataPtr rdata = createRdata(rrtype, rrclass, buffer, rdlen);
+
+ if (rrtype == RRType::OPT()) {
+ addEDNS(section, name, rrclass, rrtype, ttl, *rdata);
+ } else if (rrtype == RRType::TSIG()) {
+ addTSIG(section, count, buffer, start_position, name, rrclass, ttl,
+ *rdata);
+ } else {
+ addRR(section, name, rrclass, rrtype, ttl, rdata, options);
+ ++added;
+ }
+ }
+
+ return (added);
+}
+
+void
+MessageImpl::addRR(Message::Section section, const Name& name,
+ const RRClass& rrclass, const RRType& rrtype,
+ const RRTTL& ttl, ConstRdataPtr rdata,
+ Message::ParseOptions options) {
+ if ((options & Message::PRESERVE_ORDER) == 0) {
+ vector<RRsetPtr>::iterator it =
+ find_if(rrsets_[section].begin(), rrsets_[section].end(),
+ MatchRR(name, rrtype, rrclass));
+ if (it != rrsets_[section].end()) {
+ (*it)->setTTL(min((*it)->getTTL(), ttl));
+ (*it)->addRdata(rdata);
+ return;
+ }
+ }
+ RRsetPtr rrset(new RRset(name, rrclass, rrtype, ttl));
+ rrset->addRdata(rdata);
+ rrsets_[section].push_back(rrset);
+}
+
+void
+MessageImpl::addRR(Message::Section section, const Name& name,
+ const RRClass& rrclass, const RRType& rrtype,
+ const RRTTL& ttl, Message::ParseOptions options) {
+ if ((options & Message::PRESERVE_ORDER) == 0) {
+ vector<RRsetPtr>::iterator it =
+ find_if(rrsets_[section].begin(), rrsets_[section].end(),
+ MatchRR(name, rrtype, rrclass));
+ if (it != rrsets_[section].end()) {
+ (*it)->setTTL(min((*it)->getTTL(), ttl));
+ return;
+ }
+ }
+ RRsetPtr rrset(new RRset(name, rrclass, rrtype, ttl));
+ rrsets_[section].push_back(rrset);
+}
+
+void
+MessageImpl::addEDNS(Message::Section section, const Name& name,
+ const RRClass& rrclass, const RRType& rrtype,
+ const RRTTL& ttl, const Rdata& rdata) {
+ if (section != Message::SECTION_ADDITIONAL) {
+ isc_throw(DNSMessageFORMERR,
+ "EDNS OPT RR found in an invalid section");
+ }
+ if (edns_) {
+ isc_throw(DNSMessageFORMERR, "multiple EDNS OPT RR found");
+ }
+
+ uint8_t extended_rcode;
+ edns_ = ConstEDNSPtr(createEDNSFromRR(name, rrclass, rrtype, ttl, rdata,
+ extended_rcode));
+ setRcode(Rcode(rcode_->getCode(), extended_rcode));
+}
+
+void
+MessageImpl::addTSIG(Message::Section section, unsigned int count,
+ const InputBuffer& buffer, size_t start_position,
+ const Name& name, const RRClass& rrclass,
+ const RRTTL& ttl, const Rdata& rdata) {
+ if (section != Message::SECTION_ADDITIONAL) {
+ isc_throw(DNSMessageFORMERR,
+ "TSIG RR found in an invalid section");
+ }
+ if (count != counts_[section] - 1) {
+ isc_throw(DNSMessageFORMERR, "TSIG RR is not the last record");
+ }
+ // This check will never fail as the multiple TSIG RR case is
+ // caught before by the not the last record check...
+ if (tsig_rr_) {
+ isc_throw(DNSMessageFORMERR, "multiple TSIG RRs found");
+ }
+ tsig_rr_ = ConstTSIGRecordPtr(new TSIGRecord(name, rrclass,
+ ttl, rdata,
+ buffer.getPosition() -
+ start_position));
+}
+
+namespace {
+template <typename T>
+struct SectionFormatter {
+ SectionFormatter(const Message::Section section, string& output) :
+ section_(section), output_(output) {
+ }
+
+ void operator()(const T& entry) {
+ if (section_ == Message::SECTION_QUESTION) {
+ output_ += ";";
+ output_ += entry->toText();
+ output_ += "\n";
+ } else {
+ output_ += entry->toText();
+ }
+ }
+
+ const Message::Section section_;
+
+ string& output_;
+};
+}
+
+string
+Message::toText() const {
+ if (impl_->rcode_ == NULL) {
+ isc_throw(InvalidMessageOperation,
+ "Message::toText() attempted without Rcode set");
+ }
+ if (impl_->opcode_ == NULL) {
+ isc_throw(InvalidMessageOperation,
+ "Message::toText() attempted without Opcode set");
+ }
+
+ string s;
+
+ s += ";; ->>HEADER<<- opcode: " + impl_->opcode_->toText();
+ // for simplicity we don't consider extended rcode (unlike BIND9)
+ s += ", status: " + impl_->rcode_->toText();
+ s += ", id: " + boost::lexical_cast<string>(impl_->qid_);
+ s += "\n;; flags:";
+ if (getHeaderFlag(HEADERFLAG_QR)) {
+ s += " qr";
+ }
+ if (getHeaderFlag(HEADERFLAG_AA)) {
+ s += " aa";
+ }
+ if (getHeaderFlag(HEADERFLAG_TC)) {
+ s += " tc";
+ }
+ if (getHeaderFlag(HEADERFLAG_RD)) {
+ s += " rd";
+ }
+ if (getHeaderFlag(HEADERFLAG_RA)) {
+ s += " ra";
+ }
+ if (getHeaderFlag(HEADERFLAG_AD)) {
+ s += " ad";
+ }
+ if (getHeaderFlag(HEADERFLAG_CD)) {
+ s += " cd";
+ }
+
+ // for simplicity, don't consider the update case for now
+ s += "; QUERY: " + // note: not "QUESTION" to be compatible with BIND 9 dig
+ lexical_cast<string>(impl_->counts_[SECTION_QUESTION]);
+ s += ", ANSWER: " +
+ lexical_cast<string>(impl_->counts_[SECTION_ANSWER]);
+ s += ", AUTHORITY: " +
+ lexical_cast<string>(impl_->counts_[SECTION_AUTHORITY]);
+
+ unsigned int arcount = impl_->counts_[SECTION_ADDITIONAL];
+ if (impl_->edns_ != NULL) {
+ ++arcount;
+ }
+ if (impl_->tsig_rr_ != NULL) {
+ ++arcount;
+ }
+ s += ", ADDITIONAL: " + lexical_cast<string>(arcount) + "\n";
+
+ if (impl_->edns_ != NULL) {
+ s += "\n;; OPT PSEUDOSECTION:\n";
+ s += impl_->edns_->toText();
+ }
+
+ if (!impl_->questions_.empty()) {
+ s += "\n;; " +
+ string(sectiontext[SECTION_QUESTION]) + " SECTION:\n";
+ for_each(impl_->questions_.begin(), impl_->questions_.end(),
+ SectionFormatter<QuestionPtr>(SECTION_QUESTION, s));
+ }
+ if (!impl_->rrsets_[SECTION_ANSWER].empty()) {
+ s += "\n;; " +
+ string(sectiontext[SECTION_ANSWER]) + " SECTION:\n";
+ for_each(impl_->rrsets_[SECTION_ANSWER].begin(),
+ impl_->rrsets_[SECTION_ANSWER].end(),
+ SectionFormatter<RRsetPtr>(SECTION_ANSWER, s));
+ }
+ if (!impl_->rrsets_[SECTION_AUTHORITY].empty()) {
+ s += "\n;; " +
+ string(sectiontext[SECTION_AUTHORITY]) + " SECTION:\n";
+ for_each(impl_->rrsets_[SECTION_AUTHORITY].begin(),
+ impl_->rrsets_[SECTION_AUTHORITY].end(),
+ SectionFormatter<RRsetPtr>(SECTION_AUTHORITY, s));
+ }
+ if (!impl_->rrsets_[SECTION_ADDITIONAL].empty()) {
+ s += "\n;; " +
+ string(sectiontext[SECTION_ADDITIONAL]) +
+ " SECTION:\n";
+ for_each(impl_->rrsets_[SECTION_ADDITIONAL].begin(),
+ impl_->rrsets_[SECTION_ADDITIONAL].end(),
+ SectionFormatter<RRsetPtr>(SECTION_ADDITIONAL, s));
+ }
+
+ if (impl_->tsig_rr_ != NULL) {
+ s += "\n;; TSIG PSEUDOSECTION:\n";
+ s += impl_->tsig_rr_->toText();
+ }
+
+ return (s);
+}
+
+void
+Message::clear(Mode mode) {
+ impl_->init();
+ impl_->mode_ = mode;
+}
+
+void
+Message::appendSection(const Section section, const Message& source) {
+ if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
+ isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
+ }
+
+ if (section == SECTION_QUESTION) {
+ for (QuestionIterator qi = source.beginQuestion();
+ qi != source.endQuestion();
+ ++qi) {
+ addQuestion(*qi);
+ }
+ } else {
+ for (RRsetIterator rrsi = source.beginSection(section);
+ rrsi != source.endSection(section);
+ ++rrsi) {
+ addRRset(section, *rrsi);
+ }
+ }
+}
+
+void
+Message::makeResponse() {
+ if (impl_->mode_ != Message::PARSE) {
+ isc_throw(InvalidMessageOperation,
+ "makeResponse() is performed in non-parse mode");
+ }
+
+ impl_->mode_ = Message::RENDER;
+
+ impl_->edns_ = EDNSPtr();
+ impl_->flags_ &= MESSAGE_REPLYPRESERVE;
+ setHeaderFlag(HEADERFLAG_QR, true);
+
+ impl_->rrsets_[SECTION_ANSWER].clear();
+ impl_->counts_[SECTION_ANSWER] = 0;
+ impl_->rrsets_[SECTION_AUTHORITY].clear();
+ impl_->counts_[SECTION_AUTHORITY] = 0;
+ impl_->rrsets_[SECTION_ADDITIONAL].clear();
+ impl_->counts_[SECTION_ADDITIONAL] = 0;
+}
+
+///
+/// Template version of Section Iterator
+///
+template <typename T>
+struct SectionIteratorImpl {
+ SectionIteratorImpl(const typename vector<T>::const_iterator& it) :
+ it_(it) {
+ }
+
+ typename vector<T>::const_iterator it_;
+};
+
+template <typename T>
+SectionIterator<T>::SectionIterator(const SectionIteratorImpl<T>& impl) {
+ impl_ = new SectionIteratorImpl<T>(impl.it_);
+}
+
+template <typename T>
+SectionIterator<T>::~SectionIterator() {
+ delete impl_;
+}
+
+template <typename T>
+SectionIterator<T>::SectionIterator(const SectionIterator<T>& source) :
+ impl_(new SectionIteratorImpl<T>(source.impl_->it_)) {
+}
+
+template <typename T>
+void
+SectionIterator<T>::operator=(const SectionIterator<T>& source) {
+ if (impl_ == source.impl_) {
+ return;
+ }
+ SectionIteratorImpl<T>* newimpl =
+ new SectionIteratorImpl<T>(source.impl_->it_);
+ delete impl_;
+ impl_ = newimpl;
+}
+
+template <typename T>
+SectionIterator<T>&
+SectionIterator<T>::operator++() {
+ ++(impl_->it_);
+ return (*this);
+}
+
+template <typename T>
+SectionIterator<T>
+SectionIterator<T>::operator++(int) {
+ SectionIterator<T> tmp(*this);
+ ++(*this);
+ return (tmp);
+}
+
+template <typename T>
+const T&
+SectionIterator<T>::operator*() const {
+ return (*(impl_->it_));
+}
+
+template <typename T>
+const T*
+SectionIterator<T>::operator->() const {
+ return (&(operator*()));
+}
+
+template <typename T>
+bool
+SectionIterator<T>::operator==(const SectionIterator<T>& other) const {
+ return (impl_->it_ == other.impl_->it_);
+}
+
+template <typename T>
+bool
+SectionIterator<T>::operator!=(const SectionIterator<T>& other) const {
+ return (impl_->it_ != other.impl_->it_);
+}
+
+///
+/// We need to explicitly instantiate these template classes because these
+/// are public classes but defined in this implementation file.
+///
+template class SectionIterator<QuestionPtr>;
+template class SectionIterator<RRsetPtr>;
+
+namespace {
+typedef SectionIteratorImpl<QuestionPtr> QuestionIteratorImpl;
+typedef SectionIteratorImpl<RRsetPtr> RRsetIteratorImpl;
+}
+
+///
+/// Question iterator
+///
+const QuestionIterator
+Message::beginQuestion() const {
+ return (QuestionIterator(QuestionIteratorImpl(impl_->questions_.begin())));
+}
+
+const QuestionIterator
+Message::endQuestion() const {
+ return (QuestionIterator(QuestionIteratorImpl(impl_->questions_.end())));
+}
+
+///
+/// RRsets iterators
+///
+const SectionIterator<RRsetPtr>
+Message::beginSection(const Section section) const {
+ if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
+ isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
+ }
+ if (section == SECTION_QUESTION) {
+ isc_throw(InvalidMessageSection,
+ "RRset iterator is requested for question");
+ }
+
+ return (RRsetIterator(RRsetIteratorImpl(impl_->rrsets_[section].begin())));
+}
+
+const SectionIterator<RRsetPtr>
+Message::endSection(const Section section) const {
+ if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
+ isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
+ }
+ if (section == SECTION_QUESTION) {
+ isc_throw(InvalidMessageSection,
+ "RRset iterator is requested for question");
+ }
+
+ return (RRsetIterator(RRsetIteratorImpl(impl_->rrsets_[section].end())));
+}
+
+ostream&
+operator<<(ostream& os, const Message& message) {
+ return (os << message.toText());
+}
+} // end of namespace dns
+} // end of namespace isc