Kea 3.0.0
dns/message.cc
Go to the documentation of this file.
1// Copyright (C) 2009-2025 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
10#include <dns/edns.h>
11#include <dns/exceptions.h>
12#include <dns/message.h>
13#include <dns/messagerenderer.h>
14#include <dns/name.h>
15#include <dns/opcode.h>
16#include <dns/rcode.h>
17#include <dns/question.h>
18#include <dns/rdataclass.h>
19#include <dns/rrclass.h>
20#include <dns/rrtype.h>
21#include <dns/rrttl.h>
22#include <dns/rrset.h>
23#include <dns/tsig.h>
24#include <util/buffer.h>
25
26#include <stdint.h>
27#include <algorithm>
28#include <cassert>
29#include <string>
30#include <sstream>
31#include <vector>
32#include <boost/lexical_cast.hpp>
33#include <boost/shared_ptr.hpp>
34
35using namespace isc::dns::rdata;
36using namespace isc::util;
37
38using namespace std;
39using boost::lexical_cast;
40
41namespace isc {
42namespace dns {
43
44namespace {
45// protocol constants
46const size_t HEADERLEN = 12;
47
48const unsigned int OPCODE_MASK = 0x7800;
49const unsigned int OPCODE_SHIFT = 11;
50const unsigned int RCODE_MASK = 0x000f;
51
52// This diagram shows the wire-format representation of the 2nd 16 bits of
53// the DNS header section, which contain all defined flag bits.
54//
55// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
56// |QR| Opcode |AA|TC|RD|RA| |AD|CD| RCODE |
57// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
58// 1 0 0 0| 0 1 1 1| 1 0 1 1| 0 0 0 0|
59// 0x8 0x7 0xb 0x0
60//
61// This mask covers all the flag bits, and those bits only.
62// Note: we reject a "flag" the is not covered by this mask in some of the
63// public methods. This means our current definition is not fully extendable;
64// applications cannot introduce a new flag bit temporarily without modifying
65// the source code.
66const unsigned int HEADERFLAG_MASK = 0x87b0;
67
68// This is a set of flag bits that should be preserved when building a reply
69// from a request.
70// Note: we assume the specific definition of HEADERFLAG_xx. We may change
71// the definition in future, in which case we need to adjust this definition,
72// too (see also the description about the Message::HeaderFlag type).
73const uint16_t MESSAGE_REPLYPRESERVE = (Message::HEADERFLAG_RD |
75
76const char* const sectiontext[] = {
77 "QUESTION",
78 "ANSWER",
79 "AUTHORITY",
80 "ADDITIONAL"
81};
82}
83
85public:
87 // Open issues: should we rather have a header in wire-format
88 // for efficiency?
91
92 // We want to use null for [op,r]code_ to mean the code being not
93 // correctly parsed or set. We store the real code object in
94 // xxcode_placeholder_ and have xxcode_ refer to it when the object
95 // is valid.
96 const Rcode* rcode_;
101 uint16_t flags_; // wire-format representation of header flags.
104 static const unsigned int NUM_SECTIONS = 4; // TODO: revisit this design
105 int counts_[NUM_SECTIONS]; // TODO: revisit this definition
106 vector<QuestionPtr> questions_;
107 vector<RRsetPtr> rrsets_[NUM_SECTIONS];
110
111 // RRsetsSorter* sorter_; : TODO
112
113 void init();
114 void setOpcode(const Opcode& opcode);
115 void setRcode(const Rcode& rcode);
116 int parseQuestion(InputBuffer& buffer);
117 int parseSection(const Message::Section section, InputBuffer& buffer,
118 Message::ParseOptions options);
119 void addRR(Message::Section section, const Name& name,
120 const RRClass& rrclass, const RRType& rrtype,
121 const RRTTL& ttl, ConstRdataPtr rdata,
122 Message::ParseOptions options);
123 // There are also times where an RR needs to be added that
124 // represents an empty RRset. There is no Rdata in that case
125 void addRR(Message::Section section, const Name& name,
126 const RRClass& rrclass, const RRType& rrtype,
127 const RRTTL& ttl, Message::ParseOptions options);
128 void addEDNS(Message::Section section, const Name& name,
129 const RRClass& rrclass, const RRType& rrtype,
130 const RRTTL& ttl, const Rdata& rdata);
131 void addTSIG(Message::Section section, unsigned int count,
132 const InputBuffer& buffer, size_t start_position,
133 const Name& name, const RRClass& rrclass,
134 const RRTTL& ttl, const Rdata& rdata);
135 void toWire(AbstractMessageRenderer& renderer, TSIGContext* tsig_ctx);
136};
137
139typedef boost::shared_ptr<MessageImpl> MessageImplPtr;
140
142 mode_(mode),
143 rcode_placeholder_(Rcode(0)), // for placeholders the value doesn't matter
145 init();
146}
147
148void
150 flags_ = 0;
151 qid_ = 0;
152 rcode_ = 0;
153 opcode_ = 0;
154 edns_ = EDNSPtr();
156
157 for (unsigned int i = 0; i < NUM_SECTIONS; ++i) {
158 counts_[i] = 0;
159 }
160
161 header_parsed_ = false;
162 questions_.clear();
166}
167
168void
173
174void
179
180namespace {
181// This helper class is used by MessageImpl::toWire() to render a set of
182// RRsets of a specific section of message to a given MessageRenderer.
183//
184// A RenderSection object is expected to be used with a QuestionIterator or
185// SectionIterator. Its operator() is called for each RRset as the iterator
186// iterates over the corresponding section, and it renders the RRset to
187// the given MessageRenderer, while counting the number of RRs (note: not
188// RRsets) successfully rendered. If the MessageRenderer reports the need
189// for truncation (via its isTruncated() method), the RenderSection object
190// stops rendering further RRsets. In addition, unless partial_ok (given on
191// construction) is true, it removes any RRs that are partially rendered
192// from the MessageRenderer.
193//
194// On the completion of rendering the entire section, the owner of the
195// RenderSection object can get the number of rendered RRs via the
196// getTotalCount() method.
197template <typename T>
198struct RenderSection {
199 RenderSection(AbstractMessageRenderer& renderer, const bool partial_ok) :
200 counter_(0), renderer_(renderer), partial_ok_(partial_ok),
201 truncated_(false) {
202 }
203
204 void operator()(const T& entry) {
205 // If it's already truncated, ignore the rest of the section.
206 if (truncated_) {
207 return;
208 }
209 const size_t pos0 = renderer_.getLength();
210 counter_ += entry->toWire(renderer_);
211 if (renderer_.isTruncated()) {
212 truncated_ = true;
213 if (!partial_ok_) {
214 // roll back to the end of the previous RRset.
215 renderer_.trim(renderer_.getLength() - pos0);
216 }
217 }
218 }
219
220 unsigned int getTotalCount() {
221 return (counter_);
222 }
223
224 unsigned int counter_;
225
226 AbstractMessageRenderer& renderer_;
227
228 const bool partial_ok_;
229
230 bool truncated_;
231};
232}
233
234void
236 if (mode_ != Message::RENDER) {
238 "Message rendering attempted in non render mode");
239 }
240 if (!rcode_) {
242 "Message rendering attempted without Rcode set");
243 }
244 if (!opcode_) {
246 "Message rendering attempted without Opcode set");
247 }
248
249 // Reserve the space for TSIG (if needed) so that we can handle truncation
250 // case correctly later when that happens. orig_xxx variables remember
251 // some configured parameters of renderer in case they are needed in
252 // truncation processing below.
253 const size_t tsig_len = (tsig_ctx ? tsig_ctx->getTSIGLength() : 0);
254 const size_t orig_msg_len_limit = renderer.getLengthLimit();
255 const AbstractMessageRenderer::CompressMode orig_compress_mode =
256 renderer.getCompressMode();
257
258 // We are going to skip soon, so we need to clear the renderer
259 // But we'll leave the length limit and the compress mode intact
260 // (or shortened in case of TSIG)
261 renderer.clear();
262 renderer.setCompressMode(orig_compress_mode);
263
264 if (tsig_len > 0) {
265 if (tsig_len > orig_msg_len_limit) {
266 isc_throw(InvalidParameter, "Failed to render DNS message: "
267 "too small limit for a TSIG (" <<
268 orig_msg_len_limit << ")");
269 }
270 renderer.setLengthLimit(orig_msg_len_limit - tsig_len);
271 } else {
272 renderer.setLengthLimit(orig_msg_len_limit);
273 }
274
275 // reserve room for the header
276 if (renderer.getLengthLimit() < HEADERLEN) {
277 isc_throw(InvalidParameter, "Failed to render DNS message: "
278 "too small limit for a Header");
279 }
280 renderer.skip(HEADERLEN);
281
282 uint16_t qdcount =
283 for_each(questions_.begin(), questions_.end(),
284 RenderSection<QuestionPtr>(renderer, false)).getTotalCount();
285
286 // TODO: sort RRsets in each section based on configuration policy.
287 uint16_t ancount = 0;
288 if (!renderer.isTruncated()) {
289 ancount =
290 for_each(rrsets_[Message::SECTION_ANSWER].begin(),
292 RenderSection<RRsetPtr>(renderer, true)).getTotalCount();
293 }
294 uint16_t nscount = 0;
295 if (!renderer.isTruncated()) {
296 nscount =
297 for_each(rrsets_[Message::SECTION_AUTHORITY].begin(),
299 RenderSection<RRsetPtr>(renderer, true)).getTotalCount();
300 }
301 uint16_t arcount = 0;
302 if (renderer.isTruncated()) {
304 } else {
305 arcount =
306 for_each(rrsets_[Message::SECTION_ADDITIONAL].begin(),
308 RenderSection<RRsetPtr>(renderer, false)).getTotalCount();
309 }
310
311 // Add EDNS OPT RR if necessary. Basically, we add it only when EDNS
312 // has been explicitly set. However, if the RCODE would require it and
313 // no EDNS has been set we generate a temporary local EDNS and use it.
314 if (!renderer.isTruncated()) {
315 ConstEDNSPtr local_edns = edns_;
316 if (!local_edns && rcode_->getExtendedCode() != 0) {
317 local_edns = ConstEDNSPtr(new EDNS());
318 }
319 if (local_edns) {
320 arcount += local_edns->toWire(renderer, rcode_->getExtendedCode());
321 }
322 }
323
324 // If we're adding a TSIG to a truncated message, clear all RRsets
325 // from the message except for the question before adding the TSIG.
326 // If even (some of) the question doesn't fit, don't include it.
327 if (tsig_ctx && renderer.isTruncated()) {
328 renderer.clear();
329 renderer.setLengthLimit(orig_msg_len_limit - tsig_len);
330 renderer.setCompressMode(orig_compress_mode);
331 renderer.skip(HEADERLEN);
332 qdcount = for_each(questions_.begin(), questions_.end(),
333 RenderSection<QuestionPtr>(renderer,
334 false)).getTotalCount();
335 ancount = 0;
336 nscount = 0;
337 arcount = 0;
338 }
339
340 // Adjust the counter buffer.
341 // XXX: these may not be equal to the number of corresponding entries
342 // in rrsets_[] or questions_ if truncation occurred or an EDNS OPT RR
343 // was inserted. This is not good, and we should revisit the entire
344 // design.
349
350 // fill in the header
351 size_t header_pos = 0;
352 renderer.writeUint16At(qid_, header_pos);
353 header_pos += sizeof(uint16_t);
354
355 uint16_t codes_and_flags =
356 (opcode_->getCode() << OPCODE_SHIFT) & OPCODE_MASK;
357 codes_and_flags |= (rcode_->getCode() & RCODE_MASK);
358 codes_and_flags |= (flags_ & HEADERFLAG_MASK);
359 renderer.writeUint16At(codes_and_flags, header_pos);
360 header_pos += sizeof(uint16_t);
361 // TODO: should avoid repeated pattern
362 renderer.writeUint16At(qdcount, header_pos);
363 header_pos += sizeof(uint16_t);
364 renderer.writeUint16At(ancount, header_pos);
365 header_pos += sizeof(uint16_t);
366 renderer.writeUint16At(nscount, header_pos);
367 header_pos += sizeof(uint16_t);
368 renderer.writeUint16At(arcount, header_pos);
369
370 // Add TSIG, if necessary, at the end of the message.
371 if (tsig_ctx) {
372 // Release the reserved space in the renderer.
373 renderer.setLengthLimit(orig_msg_len_limit);
374
375 const int tsig_count =
376 tsig_ctx->sign(qid_, renderer.getData(),
377 renderer.getLength())->toWire(renderer);
378 if (tsig_count != 1) {
379 isc_throw(Unexpected, "Failed to render a TSIG RR");
380 }
381
382 // update the ARCOUNT for the TSIG RR. Note that for a sane DNS
383 // message arcount should never overflow to 0.
384 renderer.writeUint16At(++arcount, header_pos);
385 }
386}
387
389 impl_(new MessageImpl(mode)) {
390}
391
392bool
394 if (flag == 0 || (flag & ~HEADERFLAG_MASK) != 0) {
396 "Message::getHeaderFlag:: Invalid flag is specified: " <<
397 "0x" << std::hex << flag);
398 }
399 return ((impl_->flags_ & flag) != 0);
400}
401
402void
403Message::setHeaderFlag(const HeaderFlag flag, const bool on) {
404 if (impl_->mode_ != Message::RENDER) {
406 "setHeaderFlag performed in non-render mode");
407 }
408 if (flag == 0 || (flag & ~HEADERFLAG_MASK) != 0) {
410 "Message::getHeaderFlag:: Invalid flag is specified: " <<
411 "0x" << std::hex << static_cast<int>(flag));
412 }
413 if (on) {
414 impl_->flags_ |= flag;
415 } else {
416 impl_->flags_ &= ~flag;
417 }
418}
419
420qid_t
422 return (impl_->qid_);
423}
424
425void
427 if (impl_->mode_ != Message::RENDER) {
429 "setQid performed in non-render mode");
430 }
431 impl_->qid_ = qid;
432}
433
434const Rcode&
436 if (!impl_->rcode_) {
437 isc_throw(InvalidMessageOperation, "getRcode attempted before set");
438 }
439 return (*impl_->rcode_);
440}
441
442void
444 if (impl_->mode_ != Message::RENDER) {
446 "setRcode performed in non-render mode");
447 }
448 impl_->setRcode(rcode);
449}
450
451const Opcode&
453 if (!impl_->opcode_) {
454 isc_throw(InvalidMessageOperation, "getOpcode attempted before set");
455 }
456 return (*impl_->opcode_);
457}
458
459void
461 if (impl_->mode_ != Message::RENDER) {
463 "setOpcode performed in non-render mode");
464 }
465 impl_->setOpcode(opcode);
466}
467
470 return (impl_->edns_);
471}
472
473void
475 if (impl_->mode_ != Message::RENDER) {
477 "setEDNS performed in non-render mode");
478 }
479 impl_->edns_ = edns;
480}
481
482const TSIGRecord*
484 if (impl_->mode_ != Message::PARSE) {
486 "getTSIGRecord performed in non-parse mode");
487 }
488
489 return (impl_->tsig_rr_.get());
490}
491
492unsigned int
493Message::getRRCount(const Section section) const {
494 if (static_cast<unsigned int>(section) >= MessageImpl::NUM_SECTIONS) {
495 isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
496 }
497 return (impl_->counts_[section]);
498}
499
500void
501Message::addRRset(const Section section, RRsetPtr rrset) {
502 if (!rrset) {
504 "null RRset is given to Message::addRRset");
505 }
506 if (impl_->mode_ != Message::RENDER) {
508 "addRRset performed in non-render mode");
509 }
510 if (static_cast<unsigned int>(section) >= MessageImpl::NUM_SECTIONS) {
511 isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
512 }
513
514 impl_->rrsets_[section].push_back(rrset);
515 impl_->counts_[section] += rrset->getRdataCount();
516 impl_->counts_[section] += rrset->getRRsigDataCount();
517}
518
519bool
520Message::hasRRset(const Section section, const Name& name,
521 const RRClass& rrclass, const RRType& rrtype) const {
522 if (static_cast<unsigned int>(section) >= MessageImpl::NUM_SECTIONS) {
523 isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
524 }
525
526 for (auto const& r : impl_->rrsets_[section]) {
527 if (r->getClass() == rrclass &&
528 r->getType() == rrtype &&
529 r->getName() == name) {
530 return (true);
531 }
532 }
533
534 return (false);
535}
536
537bool
538Message::hasRRset(const Section section, const RRsetPtr& rrset) const {
539 return (hasRRset(section, rrset->getName(),
540 rrset->getClass(), rrset->getType()));
541}
542
543bool
544Message::removeRRset(const Section section, RRsetIterator& iterator) {
545 if (static_cast<unsigned int>(section) >= MessageImpl::NUM_SECTIONS) {
546 isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
547 }
548
549 bool removed = false;
550 for (auto i = impl_->rrsets_[section].begin(); i != impl_->rrsets_[section].end(); ++i) {
551 if (((*i)->getName() == (*iterator)->getName()) &&
552 ((*i)->getClass() == (*iterator)->getClass()) &&
553 ((*i)->getType() == (*iterator)->getType())) {
554
555 // Found the matching RRset so remove it & ignore rest
556 impl_->counts_[section] -= (*iterator)->getRdataCount();
557 impl_->counts_[section] -= (*iterator)->getRRsigDataCount();
558 impl_->rrsets_[section].erase(i);
559 removed = true;
560 break;
561 }
562 }
563
564 return (removed);
565}
566
567void
569 if (impl_->mode_ != Message::RENDER) {
571 "clearSection performed in non-render mode");
572 }
573 if (static_cast<unsigned int>(section) >= MessageImpl::NUM_SECTIONS) {
574 isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
575 }
576 if (section == Message::SECTION_QUESTION) {
577 impl_->questions_.clear();
578 } else {
579 impl_->rrsets_[section].clear();
580 }
581 impl_->counts_[section] = 0;
582}
583
584void
586 if (impl_->mode_ != Message::RENDER) {
588 "addQuestion performed in non-render mode");
589 }
590
591 impl_->questions_.push_back(question);
592 ++impl_->counts_[SECTION_QUESTION];
593}
594
595void
597 addQuestion(QuestionPtr(new Question(question)));
598}
599
600void
602 impl_->toWire(renderer, tsig_ctx);
603}
604
605void
607 if (impl_->mode_ != Message::PARSE) {
609 "Message parse attempted in non parse mode");
610 }
611
612 if (impl_->header_parsed_) {
613 return;
614 }
615
616 if ((buffer.getLength() - buffer.getPosition()) < HEADERLEN) {
617 isc_throw(MessageTooShort, "Malformed DNS message (short length): "
618 << buffer.getLength() - buffer.getPosition());
619 }
620
621 impl_->qid_ = buffer.readUint16();
622 const uint16_t codes_and_flags = buffer.readUint16();
623 impl_->setOpcode(Opcode((codes_and_flags & OPCODE_MASK) >> OPCODE_SHIFT));
624 impl_->setRcode(Rcode(codes_and_flags & RCODE_MASK));
625 impl_->flags_ = (codes_and_flags & HEADERFLAG_MASK);
626 impl_->counts_[SECTION_QUESTION] = buffer.readUint16();
627 impl_->counts_[SECTION_ANSWER] = buffer.readUint16();
628 impl_->counts_[SECTION_AUTHORITY] = buffer.readUint16();
629 impl_->counts_[SECTION_ADDITIONAL] = buffer.readUint16();
630
631 impl_->header_parsed_ = true;
632}
633
634void
636 if (impl_->mode_ != Message::PARSE) {
638 "Message parse attempted in non parse mode");
639 }
640
641 // Clear any old parsed data
643
644 buffer.setPosition(0);
645 parseHeader(buffer);
646
647 impl_->counts_[SECTION_QUESTION] = impl_->parseQuestion(buffer);
648 impl_->counts_[SECTION_ANSWER] =
649 impl_->parseSection(SECTION_ANSWER, buffer, options);
650 impl_->counts_[SECTION_AUTHORITY] =
651 impl_->parseSection(SECTION_AUTHORITY, buffer, options);
652 impl_->counts_[SECTION_ADDITIONAL] =
653 impl_->parseSection(SECTION_ADDITIONAL, buffer, options);
654}
655
656int
658 unsigned int added = 0;
659
660 for (unsigned int count = 0;
661 static_cast<int>(count) < counts_[Message::SECTION_QUESTION];
662 ++count) {
663 const Name name(buffer);
664
665 if ((buffer.getLength() - buffer.getPosition()) <
666 2 * sizeof(uint16_t)) {
667 isc_throw(DNSMessageFORMERR, "Question section too short: " <<
668 (buffer.getLength() - buffer.getPosition()) << " bytes");
669 }
670 const RRType rrtype(buffer.readUint16());
671 const RRClass rrclass(buffer.readUint16());
672
673 // XXX: need a duplicate check. We might also want to have an
674 // optimized algorithm that requires the question section contain
675 // exactly one RR.
676
677 questions_.push_back(QuestionPtr(new Question(name, rrclass, rrtype)));
678 ++added;
679 }
680
681 return (added);
682}
683
684namespace {
685struct MatchRR {
686 MatchRR(const Name& name, const RRType& rrtype, const RRClass& rrclass) :
687 name_(name), rrtype_(rrtype), rrclass_(rrclass) {
688 }
689
690 bool operator()(const RRsetPtr& rrset) const {
691 return (rrset->getType() == rrtype_ &&
692 rrset->getClass() == rrclass_ &&
693 rrset->getName() == name_);
694 }
695
696 const Name& name_;
697
698 const RRType& rrtype_;
699
700 const RRClass& rrclass_;
701};
702}
703
704// Note about design decision:
705// we need some type specific processing here, including EDNS and TSIG.
706// how much we should generalize/hardcode the special logic is subject
707// to discussion. In terms of modularity it would be ideal to introduce
708// an abstract class (say "MessageAttribute") and let other such
709// concrete notions as EDNS or TSIG inherit from it. Then we would
710// just do:
711// message->addAttribute(rrtype, rrclass, buffer);
712// to create and attach type-specific concrete object to the message.
713//
714// A major downside of this approach is, as usual, complexity due to
715// indirection and performance penalty. Also, it may not be so easy
716// to separate the processing logic because in many cases we'll need
717// parse context for which the message class is responsible (e.g.
718// to check the EDNS OPT RR only appears in the additional section,
719// and appears only once).
720//
721// Another point to consider is that we may not need so many special
722// types other than EDNS and TSIG (and when and if we implement it,
723// SIG(0)); newer optional attributes of the message would more likely
724// be standardized as new flags or options of EDNS. If that's the case,
725// introducing an abstract class with all the overhead and complexity
726// may not make much sense.
727//
728// Conclusion: don't over-generalize type-specific logic for now.
729// introduce separate concrete classes, and move context-independent
730// logic to that class; processing logic dependent on parse context
731// is hardcoded here.
732int
734 InputBuffer& buffer, Message::ParseOptions options) {
735 if (static_cast<unsigned int>(section) >= MessageImpl::NUM_SECTIONS) {
736 isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
737 }
738
739 unsigned int added = 0;
740
741 for (unsigned int count = 0;
742 static_cast<int>(count) < counts_[section];
743 ++count) {
744 // We need to remember the start position for TSIG processing
745 const size_t start_position = buffer.getPosition();
746
747 const Name name(buffer);
748
749 // buffer must store at least RR TYPE, RR CLASS, TTL, and RDLEN.
750 if ((buffer.getLength() - buffer.getPosition()) <
751 3 * sizeof(uint16_t) + sizeof(uint32_t)) {
752 isc_throw(DNSMessageFORMERR, sectiontext[section] <<
753 " section too short: " <<
754 (buffer.getLength() - buffer.getPosition()) << " bytes");
755 }
756
757 const RRType rrtype(buffer.readUint16());
758 const RRClass rrclass(buffer.readUint16());
759 const RRTTL ttl(buffer.readUint32());
760 const size_t rdlen = buffer.readUint16();
761
762 // If class is ANY or NONE, rdlength may be zero, to signal
763 // an empty RRset.
764 // (the class check must be done to differentiate from RRTypes
765 // that can have zero length rdata
766 if ((rrclass == RRClass::ANY() || rrclass == RRClass::NONE()) &&
767 rdlen == 0) {
768 addRR(section, name, rrclass, rrtype, ttl, options);
769 ++added;
770 continue;
771 }
772 ConstRdataPtr rdata = createRdata(rrtype, rrclass, buffer, rdlen);
773
774 if (rrtype == RRType::OPT()) {
775 addEDNS(section, name, rrclass, rrtype, ttl, *rdata);
776 } else if (rrtype == RRType::TSIG()) {
777 addTSIG(section, count, buffer, start_position, name, rrclass, ttl,
778 *rdata);
779 } else {
780 addRR(section, name, rrclass, rrtype, ttl, rdata, options);
781 ++added;
782 }
783 }
784
785 return (added);
786}
787
788void
790 const RRClass& rrclass, const RRType& rrtype,
791 const RRTTL& ttl, ConstRdataPtr rdata,
792 Message::ParseOptions options) {
793 if ((options & Message::PRESERVE_ORDER) == 0) {
795 find_if(rrsets_[section].begin(), rrsets_[section].end(),
796 MatchRR(name, rrtype, rrclass));
797 if (it != rrsets_[section].end()) {
798 (*it)->setTTL(min((*it)->getTTL(), ttl));
799 (*it)->addRdata(rdata);
800 return;
801 }
802 }
803 RRsetPtr rrset(new RRset(name, rrclass, rrtype, ttl));
804 rrset->addRdata(rdata);
805 rrsets_[section].push_back(rrset);
806}
807
808void
810 const RRClass& rrclass, const RRType& rrtype,
811 const RRTTL& ttl, Message::ParseOptions options) {
812 if ((options & Message::PRESERVE_ORDER) == 0) {
814 find_if(rrsets_[section].begin(), rrsets_[section].end(),
815 MatchRR(name, rrtype, rrclass));
816 if (it != rrsets_[section].end()) {
817 (*it)->setTTL(min((*it)->getTTL(), ttl));
818 return;
819 }
820 }
821 RRsetPtr rrset(new RRset(name, rrclass, rrtype, ttl));
822 rrsets_[section].push_back(rrset);
823}
824
825void
827 const RRClass& rrclass, const RRType& rrtype,
828 const RRTTL& ttl, const Rdata& rdata) {
829 if (section != Message::SECTION_ADDITIONAL) {
831 "EDNS OPT RR found in an invalid section");
832 }
833 if (edns_) {
834 isc_throw(DNSMessageFORMERR, "multiple EDNS OPT RR found");
835 }
836
837 uint8_t extended_rcode;
838 edns_ = ConstEDNSPtr(createEDNSFromRR(name, rrclass, rrtype, ttl, rdata,
839 extended_rcode));
840 setRcode(Rcode(rcode_->getCode(), extended_rcode));
841}
842
843void
844MessageImpl::addTSIG(Message::Section section, unsigned int count,
845 const InputBuffer& buffer, size_t start_position,
846 const Name& name, const RRClass& rrclass,
847 const RRTTL& ttl, const Rdata& rdata) {
848 if (section != Message::SECTION_ADDITIONAL) {
850 "TSIG RR found in an invalid section");
851 }
852 if (static_cast<int>(count) != counts_[section] - 1) {
853 isc_throw(DNSMessageFORMERR, "TSIG RR is not the last record");
854 }
855 // This check will never fail as the multiple TSIG RR case is
856 // caught before by the not the last record check...
857 if (tsig_rr_) {
858 isc_throw(DNSMessageFORMERR, "multiple TSIG RRs found");
859 }
861 ttl, rdata,
862 buffer.getPosition() -
863 start_position));
864}
865
866namespace {
867template <typename T>
868struct SectionFormatter {
869 SectionFormatter(const Message::Section section, string& output) :
870 section_(section), output_(output) {
871 }
872
873 void operator()(const T& entry) {
874 if (section_ == Message::SECTION_QUESTION) {
875 output_ += ";";
876 output_ += entry->toText();
877 output_ += "\n";
878 } else {
879 output_ += entry->toText();
880 }
881 }
882
883 const Message::Section section_;
884
885 string& output_;
886};
887}
888
889string
891 if (!impl_->rcode_) {
893 "Message::toText() attempted without Rcode set");
894 }
895 if (!impl_->opcode_) {
897 "Message::toText() attempted without Opcode set");
898 }
899
900 string s;
901
902 s += ";; ->>HEADER<<- opcode: " + impl_->opcode_->toText();
903 // for simplicity we don't consider extended rcode (unlike BIND9)
904 s += ", status: " + impl_->rcode_->toText();
905 s += ", id: " + boost::lexical_cast<string>(impl_->qid_);
906 s += "\n;; flags:";
908 s += " qr";
909 }
911 s += " aa";
912 }
914 s += " tc";
915 }
917 s += " rd";
918 }
920 s += " ra";
921 }
923 s += " ad";
924 }
926 s += " cd";
927 }
928
929 // for simplicity, don't consider the update case for now
930 s += "; QUERY: " + // note: not "QUESTION" to be compatible with BIND 9 dig
931 lexical_cast<string>(impl_->counts_[SECTION_QUESTION]);
932 s += ", ANSWER: " +
933 lexical_cast<string>(impl_->counts_[SECTION_ANSWER]);
934 s += ", AUTHORITY: " +
935 lexical_cast<string>(impl_->counts_[SECTION_AUTHORITY]);
936
937 unsigned int arcount = impl_->counts_[SECTION_ADDITIONAL];
938 if (impl_->edns_) {
939 ++arcount;
940 }
941 if (impl_->tsig_rr_) {
942 ++arcount;
943 }
944 s += ", ADDITIONAL: " + lexical_cast<string>(arcount) + "\n";
945
946 if (impl_->edns_) {
947 s += "\n;; OPT PSEUDOSECTION:\n";
948 s += impl_->edns_->toText();
949 }
950
951 if (!impl_->questions_.empty()) {
952 s += "\n;; " +
953 string(sectiontext[SECTION_QUESTION]) + " SECTION:\n";
954 for_each(impl_->questions_.begin(), impl_->questions_.end(),
955 SectionFormatter<QuestionPtr>(SECTION_QUESTION, s));
956 }
957 if (!impl_->rrsets_[SECTION_ANSWER].empty()) {
958 s += "\n;; " +
959 string(sectiontext[SECTION_ANSWER]) + " SECTION:\n";
960 for_each(impl_->rrsets_[SECTION_ANSWER].begin(),
961 impl_->rrsets_[SECTION_ANSWER].end(),
962 SectionFormatter<RRsetPtr>(SECTION_ANSWER, s));
963 }
964 if (!impl_->rrsets_[SECTION_AUTHORITY].empty()) {
965 s += "\n;; " +
966 string(sectiontext[SECTION_AUTHORITY]) + " SECTION:\n";
967 for_each(impl_->rrsets_[SECTION_AUTHORITY].begin(),
968 impl_->rrsets_[SECTION_AUTHORITY].end(),
969 SectionFormatter<RRsetPtr>(SECTION_AUTHORITY, s));
970 }
971 if (!impl_->rrsets_[SECTION_ADDITIONAL].empty()) {
972 s += "\n;; " +
973 string(sectiontext[SECTION_ADDITIONAL]) +
974 " SECTION:\n";
975 for_each(impl_->rrsets_[SECTION_ADDITIONAL].begin(),
976 impl_->rrsets_[SECTION_ADDITIONAL].end(),
977 SectionFormatter<RRsetPtr>(SECTION_ADDITIONAL, s));
978 }
979
980 if (impl_->tsig_rr_) {
981 s += "\n;; TSIG PSEUDOSECTION:\n";
982 s += impl_->tsig_rr_->toText();
983 }
984
985 return (s);
986}
987
988void
990 impl_->init();
991 impl_->mode_ = mode;
992}
993
994void
995Message::appendSection(const Section section, const Message& source) {
996 if (static_cast<unsigned int>(section) >= MessageImpl::NUM_SECTIONS) {
997 isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
998 }
999
1000 if (section == SECTION_QUESTION) {
1001 for (auto qi = source.beginQuestion(); qi != source.endQuestion(); ++qi) {
1002 addQuestion(*qi);
1003 }
1004 } else {
1005 for (auto rrsi = source.beginSection(section); rrsi != source.endSection(section); ++rrsi) {
1006 addRRset(section, *rrsi);
1007 }
1008 }
1009}
1010
1011void
1013 if (impl_->mode_ != Message::PARSE) {
1015 "makeResponse() is performed in non-parse mode");
1016 }
1017
1018 impl_->mode_ = Message::RENDER;
1019
1020 impl_->edns_ = EDNSPtr();
1021 impl_->flags_ &= MESSAGE_REPLYPRESERVE;
1023
1024 impl_->rrsets_[SECTION_ANSWER].clear();
1025 impl_->counts_[SECTION_ANSWER] = 0;
1026 impl_->rrsets_[SECTION_AUTHORITY].clear();
1027 impl_->counts_[SECTION_AUTHORITY] = 0;
1028 impl_->rrsets_[SECTION_ADDITIONAL].clear();
1029 impl_->counts_[SECTION_ADDITIONAL] = 0;
1030}
1031
1035template <typename T>
1038 it_(it) {
1039 }
1040
1042};
1043
1044template <typename T>
1048
1049template <typename T>
1051 delete impl_;
1052}
1053
1054template <typename T>
1056 impl_(new SectionIteratorImpl<T>(source.impl_->it_)) {
1057}
1058
1059template <typename T>
1060void
1062 if (impl_ == source.impl_) {
1063 return;
1064 }
1065 SectionIteratorImpl<T>* newimpl =
1066 new SectionIteratorImpl<T>(source.impl_->it_);
1067 delete impl_;
1068 impl_ = newimpl;
1069}
1070
1071template <typename T>
1074 ++(impl_->it_);
1075 return (*this);
1076}
1077
1078template <typename T>
1081 SectionIterator<T> tmp(*this);
1082 ++(*this);
1083 return (tmp);
1084}
1085
1086template <typename T>
1087const T&
1089 return (*(impl_->it_));
1090}
1091
1092template <typename T>
1093const T*
1095 return (&(operator*()));
1096}
1097
1098template <typename T>
1099bool
1101 return (impl_->it_ == other.impl_->it_);
1102}
1103
1104template <typename T>
1105bool
1107 return (impl_->it_ != other.impl_->it_);
1108}
1109
1114template class SectionIterator<QuestionPtr>;
1115template class SectionIterator<RRsetPtr>;
1116
1117namespace {
1118typedef SectionIteratorImpl<QuestionPtr> QuestionIteratorImpl;
1119typedef SectionIteratorImpl<RRsetPtr> RRsetIteratorImpl;
1120}
1121
1125const QuestionIterator
1127 return (QuestionIterator(QuestionIteratorImpl(impl_->questions_.begin())));
1128}
1129
1130const QuestionIterator
1132 return (QuestionIterator(QuestionIteratorImpl(impl_->questions_.end())));
1133}
1134
1139Message::beginSection(const Section section) const {
1140 if (static_cast<unsigned int>(section) >= MessageImpl::NUM_SECTIONS) {
1141 isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
1142 }
1143 if (section == SECTION_QUESTION) {
1145 "RRset iterator is requested for question");
1146 }
1147
1148 return (RRsetIterator(RRsetIteratorImpl(impl_->rrsets_[section].begin())));
1149}
1150
1152Message::endSection(const Section section) const {
1153 if (static_cast<unsigned int>(section) >= MessageImpl::NUM_SECTIONS) {
1154 isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
1155 }
1156 if (section == SECTION_QUESTION) {
1158 "RRset iterator is requested for question");
1159 }
1160
1161 return (RRsetIterator(RRsetIteratorImpl(impl_->rrsets_[section].end())));
1162}
1163
1164ostream&
1165operator<<(ostream& os, const Message& message) {
1166 return (os << message.toText());
1167}
1168} // end of namespace dns
1169} // end of namespace isc
The RRType class encapsulates DNS resource record types.
Definition rrtype.h:96
A generic exception that is thrown if a parameter given to a method or function is considered invalid...
A generic exception that is thrown if a parameter given to a method would refer to or modify out-of-r...
A generic exception that is thrown when an unexpected error condition occurs.
The AbstractMessageRenderer class is an abstract base class that provides common interfaces for rende...
virtual void setLengthLimit(size_t len)=0
Set the maximum length of rendered data that can fit in the corresponding DNS message without truncat...
size_t getLength() const
Return the length of data written in the internal buffer.
virtual void clear()
Clear the internal buffer and other internal resources.
virtual size_t getLengthLimit() const =0
Return the maximum length of rendered data that can fit in the corresponding DNS message without trun...
virtual void setCompressMode(CompressMode mode)=0
Set the compression mode of the renderer class object.
const void * getData() const
Return a pointer to the head of the data stored in the internal buffer.
virtual CompressMode getCompressMode() const =0
Return the compression mode of the renderer class object.
virtual bool isTruncated() const =0
Return whether truncation has occurred while rendering.
void skip(size_t len)
Insert a specified length of gap at the end of the buffer.
void writeUint16At(uint16_t data, size_t pos)
Write an unsigned 16-bit integer in host byte order at the specified position of the internal buffer ...
CompressMode
Compression mode constants.
The EDNS class represents the EDNS OPT RR defined in RFC2671.
Definition edns.h:121
A standard DNS module exception that is thrown if a Message class method is called that is prohibited...
Definition message.h:55
A standard DNS module exception that is thrown if a section iterator is being constructed for an inco...
Definition message.h:44
const Opcode * opcode_
void toWire(AbstractMessageRenderer &renderer, TSIGContext *tsig_ctx)
void addRR(Message::Section section, const Name &name, const RRClass &rrclass, const RRType &rrtype, const RRTTL &ttl, ConstRdataPtr rdata, Message::ParseOptions options)
static const unsigned int NUM_SECTIONS
const Rcode * rcode_
vector< QuestionPtr > questions_
vector< RRsetPtr > rrsets_[NUM_SECTIONS]
int parseSection(const Message::Section section, InputBuffer &buffer, Message::ParseOptions options)
MessageImpl(Message::Mode mode)
void setOpcode(const Opcode &opcode)
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 addEDNS(Message::Section section, const Name &name, const RRClass &rrclass, const RRType &rrtype, const RRTTL &ttl, const Rdata &rdata)
ConstTSIGRecordPtr tsig_rr_
int parseQuestion(InputBuffer &buffer)
void setRcode(const Rcode &rcode)
int counts_[NUM_SECTIONS]
Message::Mode mode_
A standard DNS module exception that is thrown if a wire format message parser encounters a short len...
Definition message.h:33
The Message class encapsulates a standard DNS message.
Definition message.h:152
const QuestionIterator beginQuestion() const
Return an iterator corresponding to the beginning of the Question section of the message.
const QuestionIterator endQuestion() const
Return an iterator corresponding to the end of the Question section of the message.
void toWire(AbstractMessageRenderer &renderer, TSIGContext *tsig_ctx=0)
Render the message in wire formant into a message renderer object with (or without) TSIG.
const Rcode & getRcode() const
Return the Response Code of the message.
bool getHeaderFlag(const HeaderFlag flag) const
Return whether the specified header flag bit is set in the header section.
void clear(Mode mode)
Clear the message content (if any) and reinitialize it in the specified mode.
void clearSection(const Section section)
Remove all RRSets from the given Section.
Section
Constants to specify sections of a DNS message.
Definition message.h:242
ParseOptions
Parse options.
Definition message.h:596
Mode
Constants to specify the operation mode of the Message.
Definition message.h:155
unsigned int getRRCount(const Section section) const
Returns the number of RRs contained in the given section.
void setOpcode(const Opcode &opcode)
Set the OPCODE of the header section of the message.
bool removeRRset(const Section section, RRsetIterator &iterator)
Remove RRSet from Message.
void addRRset(const Section section, RRsetPtr rrset)
Add a (pointer like object of) RRset to the given section of the message.
void fromWire(isc::util::InputBuffer &buffer, ParseOptions options=PARSE_DEFAULT)
(Re)build a Message object from wire-format data.
HeaderFlag
Constants for flag bit fields of a DNS message header.
Definition message.h:202
const RRsetIterator endSection(const Section section) const
Return an iterator corresponding to the end of the given section (other than Question) of the message...
const TSIGRecord * getTSIGRecord() const
Return, if any, the TSIG record contained in the received message.
std::string toText() const
Convert the Message to a string.
void makeResponse()
Prepare for making a response from a request.
void setHeaderFlag(const HeaderFlag flag, const bool on=true)
Set or clear the specified header flag bit in the header section.
ConstEDNSPtr getEDNS() const
Return, if any, the EDNS associated with the message.
const Opcode & getOpcode() const
Return the OPCODE given in the header section of the message.
void parseHeader(isc::util::InputBuffer &buffer)
Parse the header section of the Message.
bool hasRRset(const Section section, const Name &name, const RRClass &rrclass, const RRType &rrtype) const
Determine whether the given section already has an RRset matching the given name, RR class and RR typ...
void appendSection(const Section section, const Message &source)
Adds all rrsets from the source the given section in the source message to the same section of this m...
void addQuestion(QuestionPtr question)
Add a (pointer like object of) Question to the message.
void setQid(qid_t qid)
Set the query ID of the header section of the message.
qid_t getQid() const
Return the query ID given in the header section of the message.
const RRsetIterator beginSection(const Section section) const
Return an iterator corresponding to the beginning of the given section (other than Question) of the m...
void setRcode(const Rcode &rcode)
Set the Response Code of the message.
Message(Mode mode)
The constructor.
void setEDNS(ConstEDNSPtr edns)
Set EDNS for the message.
The Name class encapsulates DNS names.
Definition name.h:219
The Opcode class objects represent standard OPCODEs of the header section of DNS messages as defined ...
Definition opcode.h:32
The Question class encapsulates the common search key of DNS lookup, consisting of owner name,...
Definition question.h:88
The RRClass class encapsulates DNS resource record classes.
Definition rrclass.h:89
static const RRClass & ANY()
Definition rrclass.h:298
static const RRClass & NONE()
Definition rrclass.h:316
The RRTTL class encapsulates TTLs used in DNS resource records.
Definition rrttl.h:51
The RRType class encapsulates DNS resource record types.
Definition rrtype.h:96
static const RRType & TSIG()
Definition rrtype.h:339
static const RRType & OPT()
Definition rrtype.h:297
The RRset class is a concrete derived class of BasicRRset which contains a pointer to an additional R...
Definition rrset.h:844
DNS Response Codes (RCODEs) class.
Definition rcode.h:40
SectionIterator is a templated class to provide standard-compatible iterators for Questions and RRset...
Definition message.h:88
const T & operator*() const
const T * operator->() const
bool operator!=(const SectionIterator< T > &other) const
bool operator==(const SectionIterator< T > &other) const
SectionIterator< T > & operator++()
void operator=(const SectionIterator< T > &source)
TSIG session context.
Definition tsig.h:171
virtual ConstTSIGRecordPtr sign(const uint16_t qid, const void *const data, const size_t data_len)
Sign a DNS message.
Definition tsig.cc:336
virtual size_t getTSIGLength() const
Return the expected length of TSIG RR after sign()
Definition tsig.cc:287
TSIG resource record.
Definition tsigrecord.h:51
The Rdata class is an abstract base class that provides a set of common interfaces to manipulate conc...
Definition rdata.h:120
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition buffer.h:81
uint32_t readUint32()
Read an unsigned 32-bit integer in network byte order from the buffer, and return it.
Definition buffer.h:196
void setPosition(size_t position)
Set the read position of the buffer to the given value.
Definition buffer.h:112
size_t getPosition() const
Return the current read position.
Definition buffer.h:101
size_t getLength() const
Return the length of the data stored in the buffer.
Definition buffer.h:96
uint16_t readUint16()
Read an unsigned 16-bit integer in network byte order from the buffer, and return it.
Definition buffer.h:166
STL iterator class.
STL iterator class.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< const Rdata > ConstRdataPtr
Definition rdata.h:69
RdataPtr createRdata(const RRType &rrtype, const RRClass &rrclass, const std::string &rdata_string)
Create RDATA of a given pair of RR type and class from a string.
Definition rdata.cc:54
boost::shared_ptr< MessageImpl > MessageImplPtr
Pointer to the MessageImpl object.
boost::shared_ptr< const TSIGRecord > ConstTSIGRecordPtr
A pointer-like type pointing to an immutable TSIGRecord object.
Definition tsigrecord.h:283
uint16_t qid_t
Definition message.h:72
boost::shared_ptr< Question > QuestionPtr
A pointer-like type pointing to an Question object.
Definition question.h:24
SectionIterator< QuestionPtr > QuestionIterator
Definition message.h:113
boost::shared_ptr< AbstractRRset > RRsetPtr
A pointer-like type pointing to an RRset object.
Definition rrset.h:50
ostream & operator<<(std::ostream &os, const EDNS &edns)
Insert the EDNS as a string into stream.
Definition edns.cc:163
EDNS * createEDNSFromRR(const Name &name, const RRClass &rrclass, const RRType &rrtype, const RRTTL &ttl, const Rdata &rdata, uint8_t &extended_rcode)
Create a new EDNS object from a set of RR parameters, also providing the extended RCODE value.
Definition edns.cc:149
boost::shared_ptr< const EDNS > ConstEDNSPtr
A pointer-like type pointing to an immutable EDNS object.
Definition edns.h:35
boost::shared_ptr< EDNS > EDNSPtr
A pointer-like type pointing to an EDNS object.
Definition edns.h:32
SectionIterator< RRsetPtr > RRsetIterator
Definition message.h:114
Defines the logger used by the top-level component of kea-lfc.
Template version of Section Iterator.
vector< T >::const_iterator it_
SectionIteratorImpl(const typename vector< T >::const_iterator &it)