1 // Copyright (C) 2010-2021 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #include <config.h>
8 
9 #include <algorithm>
10 #include <string>
11 #include <vector>
12 
13 #include <boost/shared_ptr.hpp>
14 #include <boost/foreach.hpp>
15 
16 #include <util/buffer.h>
17 #include <dns/messagerenderer.h>
18 #include <dns/name.h>
19 #include <dns/rrclass.h>
20 #include <dns/rrtype.h>
21 #include <dns/rrttl.h>
22 #include <dns/rrset.h>
23 
24 using namespace std;
25 using namespace isc::dns;
26 using namespace isc::util;
27 using namespace isc::dns::rdata;
28 
29 namespace isc {
30 namespace dns {
31 void
addRdata(const Rdata & rdata)32 AbstractRRset::addRdata(const Rdata& rdata) {
33     addRdata(createRdata(getType(), getClass(), rdata));
34 }
35 
36 string
toText() const37 AbstractRRset::toText() const {
38     string s;
39     RdataIteratorPtr it = getRdataIterator();
40 
41     // In the case of an empty rrset, just print name, ttl, class, and
42     // type
43     if (it->isLast()) {
44         // But only for class ANY or NONE
45         if (getClass() != RRClass::ANY() &&
46             getClass() != RRClass::NONE()) {
47             isc_throw(EmptyRRset, "toText() is attempted for an empty RRset");
48         }
49 
50         s += getName().toText() + " " + getTTL().toText() + " " +
51              getClass().toText() + " " + getType().toText() + "\n";
52         return (s);
53     }
54 
55     do {
56         s += getName().toText() + " " + getTTL().toText() + " " +
57              getClass().toText() + " " + getType().toText() + " " +
58              it->getCurrent().toText() + "\n";
59         it->next();
60     } while (!it->isLast());
61 
62     if (getRRsig()) {
63         s += getRRsig()->toText();
64     }
65 
66     return (s);
67 }
68 
69 namespace { // unnamed namespace
70 
71 // FIXME: This method's code should somehow be unified with
72 // BasicRRsetImpl::toWire() below to avoid duplication.
73 template <typename T>
74 inline unsigned int
rrsetToWire(const AbstractRRset & rrset,T & output,const size_t limit)75 rrsetToWire(const AbstractRRset& rrset, T& output, const size_t limit) {
76     unsigned int n = 0;
77     RdataIteratorPtr it = rrset.getRdataIterator();
78 
79     if (it->isLast()) {
80         // empty rrsets are only allowed for classes ANY and NONE
81         if (rrset.getClass() != RRClass::ANY() &&
82             rrset.getClass() != RRClass::NONE()) {
83             isc_throw(EmptyRRset, "toWire() is attempted for an empty RRset");
84         }
85 
86         // For an empty RRset, write the name, type, class and TTL once,
87         // followed by empty rdata.
88         rrset.getName().toWire(output);
89         rrset.getType().toWire(output);
90         rrset.getClass().toWire(output);
91         rrset.getTTL().toWire(output);
92         output.writeUint16(0);
93         // Still counts as 1 'rr'; it does show up in the message
94         return (1);
95     }
96 
97     // sort the set of Rdata based on rrset-order and sortlist, and possible
98     // other options.  Details to be considered.
99     do {
100         const size_t pos0 = output.getLength();
101         assert(pos0 < 65536);
102 
103         rrset.getName().toWire(output);
104         rrset.getType().toWire(output);
105         rrset.getClass().toWire(output);
106         rrset.getTTL().toWire(output);
107 
108         const size_t pos = output.getLength();
109         output.skip(sizeof(uint16_t)); // leave the space for RDLENGTH
110         it->getCurrent().toWire(output);
111         output.writeUint16At(output.getLength() - pos - sizeof(uint16_t), pos);
112 
113         if (limit > 0 && output.getLength() > limit) {
114             // truncation is needed
115             output.trim(output.getLength() - pos0);
116             return (n);
117         }
118 
119         it->next();
120         ++n;
121     } while (!it->isLast());
122 
123     return (n);
124 }
125 
126 } // end of unnamed namespace
127 
128 unsigned int
toWire(OutputBuffer & buffer) const129 AbstractRRset::toWire(OutputBuffer& buffer) const {
130     return (rrsetToWire<OutputBuffer>(*this, buffer, 0));
131 }
132 
133 unsigned int
toWire(AbstractMessageRenderer & renderer) const134 AbstractRRset::toWire(AbstractMessageRenderer& renderer) const {
135     const unsigned int rrs_written = rrsetToWire<AbstractMessageRenderer>(
136         *this, renderer, renderer.getLengthLimit());
137     if (getRdataCount() > rrs_written) {
138         renderer.setTruncated();
139     }
140     return (rrs_written);
141 }
142 
143 bool
isSameKind(const AbstractRRset & other) const144 AbstractRRset::isSameKind(const AbstractRRset& other) const {
145   // Compare classes last as they're likely to be identical. Compare
146   // names late in the list too, as these are expensive. So we compare
147   // types first, names second and classes last.
148   return (getType() == other.getType() &&
149           getName() == other.getName() &&
150           getClass() == other.getClass());
151 }
152 
153 ostream&
operator <<(ostream & os,const AbstractRRset & rrset)154 operator<<(ostream& os, const AbstractRRset& rrset) {
155     os << rrset.toText();
156     return (os);
157 }
158 
159 /// \brief This encapsulates the actual implementation of the \c BasicRRset
160 /// class.  It's hidden from applications.
161 class BasicRRsetImpl {
162 public:
BasicRRsetImpl(const Name & name,const RRClass & rrclass,const RRType & rrtype,const RRTTL & ttl)163     BasicRRsetImpl(const Name& name, const RRClass& rrclass,
164                    const RRType& rrtype, const RRTTL& ttl) :
165         name_(name), rrclass_(rrclass), rrtype_(rrtype), ttl_(ttl) {}
166 
167     unsigned int toWire(AbstractMessageRenderer& renderer, size_t limit) const;
168 
169     Name name_;
170     RRClass rrclass_;
171     RRType rrtype_;
172     RRTTL ttl_;
173     // XXX: "list" is not a good name: It in fact isn't a list; more conceptual
174     // name than a data structure name is generally better.  But since this
175     // is only used in the internal implementation we'll live with it.
176     vector<ConstRdataPtr> rdatalist_;
177 };
178 
179 // FIXME: This method's code should somehow be unified with
180 // rrsetToWire() above to avoid duplication.
181 unsigned int
toWire(AbstractMessageRenderer & renderer,size_t limit) const182 BasicRRsetImpl::toWire(AbstractMessageRenderer& renderer, size_t limit) const {
183     if (rdatalist_.empty()) {
184         // empty rrsets are only allowed for classes ANY and NONE
185         if (rrclass_ != RRClass::ANY() &&
186             rrclass_ != RRClass::NONE()) {
187             isc_throw(EmptyRRset, "toWire() is attempted for an empty RRset");
188         }
189 
190         // For an empty RRset, write the name, type, class and TTL once,
191         // followed by empty rdata.
192         name_.toWire(renderer);
193         rrtype_.toWire(renderer);
194         rrclass_.toWire(renderer);
195         ttl_.toWire(renderer);
196         renderer.writeUint16(0);
197         // Still counts as 1 'rr'; it does show up in the message
198         return (1);
199     }
200 
201     unsigned int n = 0;
202 
203     // sort the set of Rdata based on rrset-order and sortlist, and possible
204     // other options.  Details to be considered.
205     BOOST_FOREACH(const ConstRdataPtr& rdata, rdatalist_) {
206         const size_t pos0 = renderer.getLength();
207         assert(pos0 < 65536);
208 
209         name_.toWire(renderer);
210         rrtype_.toWire(renderer);
211         rrclass_.toWire(renderer);
212         ttl_.toWire(renderer);
213 
214         const size_t pos = renderer.getLength();
215         renderer.skip(sizeof(uint16_t)); // leave the space for RDLENGTH
216         rdata->toWire(renderer);
217         renderer.writeUint16At(renderer.getLength() - pos - sizeof(uint16_t),
218                                pos);
219 
220         if (limit > 0 && renderer.getLength() > limit) {
221             // truncation is needed
222             renderer.trim(renderer.getLength() - pos0);
223             return (n);
224         }
225         ++n;
226     }
227 
228     return (n);
229 }
230 
BasicRRset(const Name & name,const RRClass & rrclass,const RRType & rrtype,const RRTTL & ttl)231 BasicRRset::BasicRRset(const Name& name, const RRClass& rrclass,
232                        const RRType& rrtype, const RRTTL& ttl)
233 {
234     impl_ = new BasicRRsetImpl(name, rrclass, rrtype, ttl);
235 }
236 
~BasicRRset()237 BasicRRset::~BasicRRset() {
238     delete impl_;
239 }
240 
241 void
addRdata(ConstRdataPtr rdata)242 BasicRRset::addRdata(ConstRdataPtr rdata) {
243     impl_->rdatalist_.push_back(rdata);
244 }
245 
246 void
addRdata(const Rdata & rdata)247 BasicRRset::addRdata(const Rdata& rdata) {
248     AbstractRRset::addRdata(rdata);
249 }
250 
251 void
addRdata(const std::string & rdata_str)252 BasicRRset::addRdata(const std::string& rdata_str) {
253     addRdata(createRdata(getType(), getClass(), rdata_str));
254 }
255 
256 unsigned int
getRdataCount() const257 BasicRRset::getRdataCount() const {
258     return (impl_->rdatalist_.size());
259 }
260 
261 const Name&
getName() const262 BasicRRset::getName() const {
263     return (impl_->name_);
264 }
265 
266 const RRClass&
getClass() const267 BasicRRset::getClass() const {
268     return (impl_->rrclass_);
269 }
270 
271 const RRType&
getType() const272 BasicRRset::getType() const {
273     return (impl_->rrtype_);
274 }
275 
276 const RRTTL&
getTTL() const277 BasicRRset::getTTL() const {
278     return (impl_->ttl_);
279 }
280 
281 void
setTTL(const RRTTL & ttl)282 BasicRRset::setTTL(const RRTTL& ttl) {
283     impl_->ttl_ = ttl;
284 }
285 
286 string
toText() const287 BasicRRset::toText() const {
288     return (AbstractRRset::toText());
289 }
290 
291 uint16_t
getLength() const292 BasicRRset::getLength() const {
293     uint16_t length = 0;
294     RdataIteratorPtr it = getRdataIterator();
295 
296     if (it->isLast()) {
297         // empty rrsets are only allowed for classes ANY and NONE
298         if (getClass() != RRClass::ANY() &&
299             getClass() != RRClass::NONE()) {
300             isc_throw(EmptyRRset, "getLength() is attempted for an empty RRset");
301         }
302 
303         // For an empty RRset, write the name, type, class and TTL once,
304         // followed by empty rdata.
305         length += getName().getLength();
306         length += 2; // TYPE field
307         length += 2; // CLASS field
308         length += 4; // TTL field
309         length += 2; // RDLENGTH field (=0 in wire format)
310 
311         return (length);
312     }
313 
314     do {
315         // This is a size_t as some of the following additions may
316         // overflow due to a programming mistake somewhere.
317         size_t rrlen = 0;
318 
319         rrlen += getName().getLength();
320         rrlen += 2; // TYPE field
321         rrlen += 2; // CLASS field
322         rrlen += 4; // TTL field
323         rrlen += 2; // RDLENGTH field
324         rrlen += it->getCurrent().getLength();
325 
326         assert(length + rrlen < 65536);
327         length += rrlen;
328 
329         it->next();
330     } while (!it->isLast());
331 
332     return (length);
333 }
334 
335 unsigned int
toWire(OutputBuffer & buffer) const336 BasicRRset::toWire(OutputBuffer& buffer) const {
337     return (AbstractRRset::toWire(buffer));
338 }
339 
340 unsigned int
toWire(AbstractMessageRenderer & renderer) const341 BasicRRset::toWire(AbstractMessageRenderer& renderer) const {
342     const unsigned int rrs_written = impl_->toWire(renderer,
343                                                    renderer.getLengthLimit());
344     if (impl_->rdatalist_.size() > rrs_written) {
345         renderer.setTruncated();
346     }
347     return (rrs_written);
348 }
349 
RRset(const Name & name,const RRClass & rrclass,const RRType & rrtype,const RRTTL & ttl)350 RRset::RRset(const Name& name, const RRClass& rrclass,
351             const RRType& rrtype, const RRTTL& ttl) :
352     BasicRRset(name, rrclass, rrtype, ttl)
353 {
354     rrsig_ = RRsetPtr();
355 }
356 
~RRset()357 RRset::~RRset() {}
358 
359 unsigned int
getRRsigDataCount() const360 RRset::getRRsigDataCount() const {
361     if (rrsig_) {
362         return (rrsig_->getRdataCount());
363     } else {
364         return (0);
365     }
366 }
367 
368 uint16_t
getLength() const369 RRset::getLength() const {
370     uint16_t length = BasicRRset::getLength();
371 
372     if (rrsig_) {
373         const uint16_t rrsigs_length = rrsig_->getLength();
374         // the uint16_ts are promoted to ints during addition below, so
375         // it won't overflow a 16-bit register.
376         assert(length + rrsigs_length < 65536);
377         length += rrsigs_length;
378     }
379 
380     return (length);
381 }
382 
383 unsigned int
toWire(OutputBuffer & buffer) const384 RRset::toWire(OutputBuffer& buffer) const {
385     unsigned int rrs_written = BasicRRset::toWire(buffer);
386     if (getRdataCount() > rrs_written) {
387         return (rrs_written);
388     }
389 
390     if (rrsig_) {
391         rrs_written += rrsig_->toWire(buffer);
392     }
393 
394     return (rrs_written);
395 }
396 
397 unsigned int
toWire(AbstractMessageRenderer & renderer) const398 RRset::toWire(AbstractMessageRenderer& renderer) const {
399     unsigned int rrs_written = BasicRRset::toWire(renderer);
400     if (getRdataCount() > rrs_written) {
401         return (rrs_written);
402     }
403 
404     if (rrsig_) {
405         rrs_written += rrsig_->toWire(renderer);
406 
407         if (getRdataCount() + getRRsigDataCount() > rrs_written) {
408             renderer.setTruncated();
409         }
410     }
411 
412     return (rrs_written);
413 }
414 
415 namespace {
416 
417 class BasicRdataIterator : public RdataIterator {
418 public:
419     /// @brief Constructor.
BasicRdataIterator(const std::vector<rdata::ConstRdataPtr> & datavector)420     BasicRdataIterator(const std::vector<rdata::ConstRdataPtr>& datavector) :
421         datavector_(&datavector), it_(datavector_->begin()) {}
422 
423     /// @brief Destructor.
~BasicRdataIterator()424     ~BasicRdataIterator() {}
425 
426     /// @brief Set iterator at first position.
first()427     virtual void first() {
428         it_ = datavector_->begin();
429     }
430 
431     /// @brief Advance iterator.
next()432     virtual void next() {
433         ++it_;
434     }
435 
436     /// @brief Get value at current iterator position.
437     ///
438     /// @return The value at current iterator position.
getCurrent() const439     virtual const rdata::Rdata& getCurrent() const {
440         return (**it_);
441     }
442 
443     /// @brief Check if iterator has reached the end.
444     ///
445     /// @return true if iterator has reached the end, false otherwise.
isLast() const446     virtual bool isLast() const {
447         return (it_ == datavector_->end());
448     }
449 
450 private:
451     /// @brief Vector containing data.
452     const std::vector<rdata::ConstRdataPtr>* datavector_;
453 
454     /// @brief Iterator used to retrieve data.
455     std::vector<rdata::ConstRdataPtr>::const_iterator it_;
456 };
457 
458 }
459 
460 RdataIteratorPtr
getRdataIterator() const461 BasicRRset::getRdataIterator() const {
462     return (RdataIteratorPtr(new BasicRdataIterator(impl_->rdatalist_)));
463 }
464 
465 }
466 }
467