summaryrefslogtreecommitdiffstats
path: root/src/lib/dns/rrset.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/dns/rrset.cc')
-rw-r--r--src/lib/dns/rrset.cc466
1 files changed, 466 insertions, 0 deletions
diff --git a/src/lib/dns/rrset.cc b/src/lib/dns/rrset.cc
new file mode 100644
index 0000000..b217002
--- /dev/null
+++ b/src/lib/dns/rrset.cc
@@ -0,0 +1,466 @@
+// Copyright (C) 2010-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/.
+
+#include <config.h>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/foreach.hpp>
+
+#include <util/buffer.h>
+#include <dns/messagerenderer.h>
+#include <dns/name.h>
+#include <dns/rrclass.h>
+#include <dns/rrtype.h>
+#include <dns/rrttl.h>
+#include <dns/rrset.h>
+
+using namespace std;
+using namespace isc::dns;
+using namespace isc::util;
+using namespace isc::dns::rdata;
+
+namespace isc {
+namespace dns {
+void
+AbstractRRset::addRdata(const Rdata& rdata) {
+ addRdata(createRdata(getType(), getClass(), rdata));
+}
+
+string
+AbstractRRset::toText() const {
+ string s;
+ RdataIteratorPtr it = getRdataIterator();
+
+ // In the case of an empty rrset, just print name, ttl, class, and
+ // type
+ if (it->isLast()) {
+ // But only for class ANY or NONE
+ if (getClass() != RRClass::ANY() &&
+ getClass() != RRClass::NONE()) {
+ isc_throw(EmptyRRset, "toText() is attempted for an empty RRset");
+ }
+
+ s += getName().toText() + " " + getTTL().toText() + " " +
+ getClass().toText() + " " + getType().toText() + "\n";
+ return (s);
+ }
+
+ do {
+ s += getName().toText() + " " + getTTL().toText() + " " +
+ getClass().toText() + " " + getType().toText() + " " +
+ it->getCurrent().toText() + "\n";
+ it->next();
+ } while (!it->isLast());
+
+ if (getRRsig()) {
+ s += getRRsig()->toText();
+ }
+
+ return (s);
+}
+
+namespace { // unnamed namespace
+
+// FIXME: This method's code should somehow be unified with
+// BasicRRsetImpl::toWire() below to avoid duplication.
+template <typename T>
+inline unsigned int
+rrsetToWire(const AbstractRRset& rrset, T& output, const size_t limit) {
+ unsigned int n = 0;
+ RdataIteratorPtr it = rrset.getRdataIterator();
+
+ if (it->isLast()) {
+ // empty rrsets are only allowed for classes ANY and NONE
+ if (rrset.getClass() != RRClass::ANY() &&
+ rrset.getClass() != RRClass::NONE()) {
+ isc_throw(EmptyRRset, "toWire() is attempted for an empty RRset");
+ }
+
+ // For an empty RRset, write the name, type, class and TTL once,
+ // followed by empty rdata.
+ rrset.getName().toWire(output);
+ rrset.getType().toWire(output);
+ rrset.getClass().toWire(output);
+ rrset.getTTL().toWire(output);
+ output.writeUint16(0);
+ // Still counts as 1 'rr'; it does show up in the message
+ return (1);
+ }
+
+ // sort the set of Rdata based on rrset-order and sortlist, and possible
+ // other options. Details to be considered.
+ do {
+ const size_t pos0 = output.getLength();
+ assert(pos0 < 65536);
+
+ rrset.getName().toWire(output);
+ rrset.getType().toWire(output);
+ rrset.getClass().toWire(output);
+ rrset.getTTL().toWire(output);
+
+ const size_t pos = output.getLength();
+ output.skip(sizeof(uint16_t)); // leave the space for RDLENGTH
+ it->getCurrent().toWire(output);
+ output.writeUint16At(output.getLength() - pos - sizeof(uint16_t), pos);
+
+ if (limit > 0 && output.getLength() > limit) {
+ // truncation is needed
+ output.trim(output.getLength() - pos0);
+ return (n);
+ }
+
+ it->next();
+ ++n;
+ } while (!it->isLast());
+
+ return (n);
+}
+
+} // end of unnamed namespace
+
+unsigned int
+AbstractRRset::toWire(OutputBuffer& buffer) const {
+ return (rrsetToWire<OutputBuffer>(*this, buffer, 0));
+}
+
+unsigned int
+AbstractRRset::toWire(AbstractMessageRenderer& renderer) const {
+ const unsigned int rrs_written = rrsetToWire<AbstractMessageRenderer>(
+ *this, renderer, renderer.getLengthLimit());
+ if (getRdataCount() > rrs_written) {
+ renderer.setTruncated();
+ }
+ return (rrs_written);
+}
+
+bool
+AbstractRRset::isSameKind(const AbstractRRset& other) const {
+ // Compare classes last as they're likely to be identical. Compare
+ // names late in the list too, as these are expensive. So we compare
+ // types first, names second and classes last.
+ return (getType() == other.getType() &&
+ getName() == other.getName() &&
+ getClass() == other.getClass());
+}
+
+ostream&
+operator<<(ostream& os, const AbstractRRset& rrset) {
+ os << rrset.toText();
+ return (os);
+}
+
+/// \brief This encapsulates the actual implementation of the \c BasicRRset
+/// class. It's hidden from applications.
+class BasicRRsetImpl {
+public:
+ BasicRRsetImpl(const Name& name, const RRClass& rrclass,
+ const RRType& rrtype, const RRTTL& ttl) :
+ name_(name), rrclass_(rrclass), rrtype_(rrtype), ttl_(ttl) {}
+
+ unsigned int toWire(AbstractMessageRenderer& renderer, size_t limit) const;
+
+ Name name_;
+ RRClass rrclass_;
+ RRType rrtype_;
+ RRTTL ttl_;
+ // XXX: "list" is not a good name: It in fact isn't a list; more conceptual
+ // name than a data structure name is generally better. But since this
+ // is only used in the internal implementation we'll live with it.
+ vector<ConstRdataPtr> rdatalist_;
+};
+
+// FIXME: This method's code should somehow be unified with
+// rrsetToWire() above to avoid duplication.
+unsigned int
+BasicRRsetImpl::toWire(AbstractMessageRenderer& renderer, size_t limit) const {
+ if (rdatalist_.empty()) {
+ // empty rrsets are only allowed for classes ANY and NONE
+ if (rrclass_ != RRClass::ANY() &&
+ rrclass_ != RRClass::NONE()) {
+ isc_throw(EmptyRRset, "toWire() is attempted for an empty RRset");
+ }
+
+ // For an empty RRset, write the name, type, class and TTL once,
+ // followed by empty rdata.
+ name_.toWire(renderer);
+ rrtype_.toWire(renderer);
+ rrclass_.toWire(renderer);
+ ttl_.toWire(renderer);
+ renderer.writeUint16(0);
+ // Still counts as 1 'rr'; it does show up in the message
+ return (1);
+ }
+
+ unsigned int n = 0;
+
+ // sort the set of Rdata based on rrset-order and sortlist, and possible
+ // other options. Details to be considered.
+ BOOST_FOREACH(const ConstRdataPtr& rdata, rdatalist_) {
+ const size_t pos0 = renderer.getLength();
+ assert(pos0 < 65536);
+
+ name_.toWire(renderer);
+ rrtype_.toWire(renderer);
+ rrclass_.toWire(renderer);
+ ttl_.toWire(renderer);
+
+ const size_t pos = renderer.getLength();
+ renderer.skip(sizeof(uint16_t)); // leave the space for RDLENGTH
+ rdata->toWire(renderer);
+ renderer.writeUint16At(renderer.getLength() - pos - sizeof(uint16_t),
+ pos);
+
+ if (limit > 0 && renderer.getLength() > limit) {
+ // truncation is needed
+ renderer.trim(renderer.getLength() - pos0);
+ return (n);
+ }
+ ++n;
+ }
+
+ return (n);
+}
+
+BasicRRset::BasicRRset(const Name& name, const RRClass& rrclass,
+ const RRType& rrtype, const RRTTL& ttl)
+{
+ impl_ = new BasicRRsetImpl(name, rrclass, rrtype, ttl);
+}
+
+BasicRRset::~BasicRRset() {
+ delete impl_;
+}
+
+void
+BasicRRset::addRdata(ConstRdataPtr rdata) {
+ impl_->rdatalist_.push_back(rdata);
+}
+
+void
+BasicRRset::addRdata(const Rdata& rdata) {
+ AbstractRRset::addRdata(rdata);
+}
+
+void
+BasicRRset::addRdata(const std::string& rdata_str) {
+ addRdata(createRdata(getType(), getClass(), rdata_str));
+}
+
+unsigned int
+BasicRRset::getRdataCount() const {
+ return (impl_->rdatalist_.size());
+}
+
+const Name&
+BasicRRset::getName() const {
+ return (impl_->name_);
+}
+
+const RRClass&
+BasicRRset::getClass() const {
+ return (impl_->rrclass_);
+}
+
+const RRType&
+BasicRRset::getType() const {
+ return (impl_->rrtype_);
+}
+
+const RRTTL&
+BasicRRset::getTTL() const {
+ return (impl_->ttl_);
+}
+
+void
+BasicRRset::setTTL(const RRTTL& ttl) {
+ impl_->ttl_ = ttl;
+}
+
+string
+BasicRRset::toText() const {
+ return (AbstractRRset::toText());
+}
+
+uint16_t
+BasicRRset::getLength() const {
+ uint16_t length = 0;
+ RdataIteratorPtr it = getRdataIterator();
+
+ if (it->isLast()) {
+ // empty rrsets are only allowed for classes ANY and NONE
+ if (getClass() != RRClass::ANY() &&
+ getClass() != RRClass::NONE()) {
+ isc_throw(EmptyRRset, "getLength() is attempted for an empty RRset");
+ }
+
+ // For an empty RRset, write the name, type, class and TTL once,
+ // followed by empty rdata.
+ length += getName().getLength();
+ length += 2; // TYPE field
+ length += 2; // CLASS field
+ length += 4; // TTL field
+ length += 2; // RDLENGTH field (=0 in wire format)
+
+ return (length);
+ }
+
+ do {
+ // This is a size_t as some of the following additions may
+ // overflow due to a programming mistake somewhere.
+ size_t rrlen = 0;
+
+ rrlen += getName().getLength();
+ rrlen += 2; // TYPE field
+ rrlen += 2; // CLASS field
+ rrlen += 4; // TTL field
+ rrlen += 2; // RDLENGTH field
+ rrlen += it->getCurrent().getLength();
+
+ assert(length + rrlen < 65536);
+ length += rrlen;
+
+ it->next();
+ } while (!it->isLast());
+
+ return (length);
+}
+
+unsigned int
+BasicRRset::toWire(OutputBuffer& buffer) const {
+ return (AbstractRRset::toWire(buffer));
+}
+
+unsigned int
+BasicRRset::toWire(AbstractMessageRenderer& renderer) const {
+ const unsigned int rrs_written = impl_->toWire(renderer,
+ renderer.getLengthLimit());
+ if (impl_->rdatalist_.size() > rrs_written) {
+ renderer.setTruncated();
+ }
+ return (rrs_written);
+}
+
+RRset::RRset(const Name& name, const RRClass& rrclass,
+ const RRType& rrtype, const RRTTL& ttl) :
+ BasicRRset(name, rrclass, rrtype, ttl)
+{
+ rrsig_ = RRsetPtr();
+}
+
+RRset::~RRset() {}
+
+unsigned int
+RRset::getRRsigDataCount() const {
+ if (rrsig_) {
+ return (rrsig_->getRdataCount());
+ } else {
+ return (0);
+ }
+}
+
+uint16_t
+RRset::getLength() const {
+ uint16_t length = BasicRRset::getLength();
+
+ if (rrsig_) {
+ const uint16_t rrsigs_length = rrsig_->getLength();
+ // the uint16_ts are promoted to ints during addition below, so
+ // it won't overflow a 16-bit register.
+ assert(length + rrsigs_length < 65536);
+ length += rrsigs_length;
+ }
+
+ return (length);
+}
+
+unsigned int
+RRset::toWire(OutputBuffer& buffer) const {
+ unsigned int rrs_written = BasicRRset::toWire(buffer);
+ if (getRdataCount() > rrs_written) {
+ return (rrs_written);
+ }
+
+ if (rrsig_) {
+ rrs_written += rrsig_->toWire(buffer);
+ }
+
+ return (rrs_written);
+}
+
+unsigned int
+RRset::toWire(AbstractMessageRenderer& renderer) const {
+ unsigned int rrs_written = BasicRRset::toWire(renderer);
+ if (getRdataCount() > rrs_written) {
+ return (rrs_written);
+ }
+
+ if (rrsig_) {
+ rrs_written += rrsig_->toWire(renderer);
+
+ if (getRdataCount() + getRRsigDataCount() > rrs_written) {
+ renderer.setTruncated();
+ }
+ }
+
+ return (rrs_written);
+}
+
+namespace {
+
+class BasicRdataIterator : public RdataIterator {
+public:
+ /// @brief Constructor.
+ BasicRdataIterator(const std::vector<rdata::ConstRdataPtr>& datavector) :
+ datavector_(&datavector), it_(datavector_->begin()) {}
+
+ /// @brief Destructor.
+ ~BasicRdataIterator() {}
+
+ /// @brief Set iterator at first position.
+ virtual void first() {
+ it_ = datavector_->begin();
+ }
+
+ /// @brief Advance iterator.
+ virtual void next() {
+ ++it_;
+ }
+
+ /// @brief Get value at current iterator position.
+ ///
+ /// @return The value at current iterator position.
+ virtual const rdata::Rdata& getCurrent() const {
+ return (**it_);
+ }
+
+ /// @brief Check if iterator has reached the end.
+ ///
+ /// @return true if iterator has reached the end, false otherwise.
+ virtual bool isLast() const {
+ return (it_ == datavector_->end());
+ }
+
+private:
+ /// @brief Vector containing data.
+ const std::vector<rdata::ConstRdataPtr>* datavector_;
+
+ /// @brief Iterator used to retrieve data.
+ std::vector<rdata::ConstRdataPtr>::const_iterator it_;
+};
+
+}
+
+RdataIteratorPtr
+BasicRRset::getRdataIterator() const {
+ return (RdataIteratorPtr(new BasicRdataIterator(impl_->rdatalist_)));
+}
+
+}
+}