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