Kea 2.7.5
dns/message.cc
Go to the documentation of this file.
1// Copyright (C) 2009-2024 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_;
100
101 uint16_t flags_; // wire-format representation of header flags.
102
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
144 opcode_placeholder_(Opcode(0)) {
145 init();
146}
147
148void
150 flags_ = 0;
151 qid_ = 0;
152 rcode_ = 0;
153 opcode_ = 0;
154 edns_ = EDNSPtr();
156
157 for (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
227
228 const bool partial_ok_;
229
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<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<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<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<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<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;
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
699
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<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; count < counts_[section]; ++count) {
742 // We need to remember the start position for TSIG processing
743 const size_t start_position = buffer.getPosition();
744
745 const Name name(buffer);
746
747 // buffer must store at least RR TYPE, RR CLASS, TTL, and RDLEN.
748 if ((buffer.getLength() - buffer.getPosition()) <
749 3 * sizeof(uint16_t) + sizeof(uint32_t)) {
750 isc_throw(DNSMessageFORMERR, sectiontext[section] <<
751 " section too short: " <<
752 (buffer.getLength() - buffer.getPosition()) << " bytes");
753 }
754
755 const RRType rrtype(buffer.readUint16());
756 const RRClass rrclass(buffer.readUint16());
757 const RRTTL ttl(buffer.readUint32());
758 const size_t rdlen = buffer.readUint16();
759
760 // If class is ANY or NONE, rdlength may be zero, to signal
761 // an empty RRset.
762 // (the class check must be done to differentiate from RRTypes
763 // that can have zero length rdata
764 if ((rrclass == RRClass::ANY() || rrclass == RRClass::NONE()) &&
765 rdlen == 0) {
766 addRR(section, name, rrclass, rrtype, ttl, options);
767 ++added;
768 continue;
769 }
770 ConstRdataPtr rdata = createRdata(rrtype, rrclass, buffer, rdlen);
771
772 if (rrtype == RRType::OPT()) {
773 addEDNS(section, name, rrclass, rrtype, ttl, *rdata);
774 } else if (rrtype == RRType::TSIG()) {
775 addTSIG(section, count, buffer, start_position, name, rrclass, ttl,
776 *rdata);
777 } else {
778 addRR(section, name, rrclass, rrtype, ttl, rdata, options);
779 ++added;
780 }
781 }
782
783 return (added);
784}
785
786void
788 const RRClass& rrclass, const RRType& rrtype,
789 const RRTTL& ttl, ConstRdataPtr rdata,
790 Message::ParseOptions options) {
791 if ((options & Message::PRESERVE_ORDER) == 0) {
792 vector<RRsetPtr>::iterator it =
793 find_if(rrsets_[section].begin(), rrsets_[section].end(),
794 MatchRR(name, rrtype, rrclass));
795 if (it != rrsets_[section].end()) {
796 (*it)->setTTL(min((*it)->getTTL(), ttl));
797 (*it)->addRdata(rdata);
798 return;
799 }
800 }
801 RRsetPtr rrset(new RRset(name, rrclass, rrtype, ttl));
802 rrset->addRdata(rdata);
803 rrsets_[section].push_back(rrset);
804}
805
806void
808 const RRClass& rrclass, const RRType& rrtype,
809 const RRTTL& ttl, Message::ParseOptions options) {
810 if ((options & Message::PRESERVE_ORDER) == 0) {
811 vector<RRsetPtr>::iterator it =
812 find_if(rrsets_[section].begin(), rrsets_[section].end(),
813 MatchRR(name, rrtype, rrclass));
814 if (it != rrsets_[section].end()) {
815 (*it)->setTTL(min((*it)->getTTL(), ttl));
816 return;
817 }
818 }
819 RRsetPtr rrset(new RRset(name, rrclass, rrtype, ttl));
820 rrsets_[section].push_back(rrset);
821}
822
823void
825 const RRClass& rrclass, const RRType& rrtype,
826 const RRTTL& ttl, const Rdata& rdata) {
827 if (section != Message::SECTION_ADDITIONAL) {
829 "EDNS OPT RR found in an invalid section");
830 }
831 if (edns_) {
832 isc_throw(DNSMessageFORMERR, "multiple EDNS OPT RR found");
833 }
834
835 uint8_t extended_rcode;
836 edns_ = ConstEDNSPtr(createEDNSFromRR(name, rrclass, rrtype, ttl, rdata,
837 extended_rcode));
838 setRcode(Rcode(rcode_->getCode(), extended_rcode));
839}
840
841void
842MessageImpl::addTSIG(Message::Section section, unsigned int count,
843 const InputBuffer& buffer, size_t start_position,
844 const Name& name, const RRClass& rrclass,
845 const RRTTL& ttl, const Rdata& rdata) {
846 if (section != Message::SECTION_ADDITIONAL) {
848 "TSIG RR found in an invalid section");
849 }
850 if (count != counts_[section] - 1) {
851 isc_throw(DNSMessageFORMERR, "TSIG RR is not the last record");
852 }
853 // This check will never fail as the multiple TSIG RR case is
854 // caught before by the not the last record check...
855 if (tsig_rr_) {
856 isc_throw(DNSMessageFORMERR, "multiple TSIG RRs found");
857 }
858 tsig_rr_ = ConstTSIGRecordPtr(new TSIGRecord(name, rrclass,
859 ttl, rdata,
860 buffer.getPosition() -
861 start_position));
862}
863
864namespace {
865template <typename T>
866struct SectionFormatter {
867 SectionFormatter(const Message::Section section, string& output) :
868 section_(section), output_(output) {
869 }
870
871 void operator()(const T& entry) {
872 if (section_ == Message::SECTION_QUESTION) {
873 output_ += ";";
874 output_ += entry->toText();
875 output_ += "\n";
876 } else {
877 output_ += entry->toText();
878 }
879 }
880
882
883 string& output_;
884};
885}
886
887string
889 if (!impl_->rcode_) {
891 "Message::toText() attempted without Rcode set");
892 }
893 if (!impl_->opcode_) {
895 "Message::toText() attempted without Opcode set");
896 }
897
898 string s;
899
900 s += ";; ->>HEADER<<- opcode: " + impl_->opcode_->toText();
901 // for simplicity we don't consider extended rcode (unlike BIND9)
902 s += ", status: " + impl_->rcode_->toText();
903 s += ", id: " + boost::lexical_cast<string>(impl_->qid_);
904 s += "\n;; flags:";
906 s += " qr";
907 }
909 s += " aa";
910 }
912 s += " tc";
913 }
915 s += " rd";
916 }
918 s += " ra";
919 }
921 s += " ad";
922 }
924 s += " cd";
925 }
926
927 // for simplicity, don't consider the update case for now
928 s += "; QUERY: " + // note: not "QUESTION" to be compatible with BIND 9 dig
929 lexical_cast<string>(impl_->counts_[SECTION_QUESTION]);
930 s += ", ANSWER: " +
931 lexical_cast<string>(impl_->counts_[SECTION_ANSWER]);
932 s += ", AUTHORITY: " +
933 lexical_cast<string>(impl_->counts_[SECTION_AUTHORITY]);
934
935 unsigned int arcount = impl_->counts_[SECTION_ADDITIONAL];
936 if (impl_->edns_) {
937 ++arcount;
938 }
939 if (impl_->tsig_rr_) {
940 ++arcount;
941 }
942 s += ", ADDITIONAL: " + lexical_cast<string>(arcount) + "\n";
943
944 if (impl_->edns_) {
945 s += "\n;; OPT PSEUDOSECTION:\n";
946 s += impl_->edns_->toText();
947 }
948
949 if (!impl_->questions_.empty()) {
950 s += "\n;; " +
951 string(sectiontext[SECTION_QUESTION]) + " SECTION:\n";
952 for_each(impl_->questions_.begin(), impl_->questions_.end(),
953 SectionFormatter<QuestionPtr>(SECTION_QUESTION, s));
954 }
955 if (!impl_->rrsets_[SECTION_ANSWER].empty()) {
956 s += "\n;; " +
957 string(sectiontext[SECTION_ANSWER]) + " SECTION:\n";
958 for_each(impl_->rrsets_[SECTION_ANSWER].begin(),
959 impl_->rrsets_[SECTION_ANSWER].end(),
960 SectionFormatter<RRsetPtr>(SECTION_ANSWER, s));
961 }
962 if (!impl_->rrsets_[SECTION_AUTHORITY].empty()) {
963 s += "\n;; " +
964 string(sectiontext[SECTION_AUTHORITY]) + " SECTION:\n";
965 for_each(impl_->rrsets_[SECTION_AUTHORITY].begin(),
966 impl_->rrsets_[SECTION_AUTHORITY].end(),
967 SectionFormatter<RRsetPtr>(SECTION_AUTHORITY, s));
968 }
969 if (!impl_->rrsets_[SECTION_ADDITIONAL].empty()) {
970 s += "\n;; " +
971 string(sectiontext[SECTION_ADDITIONAL]) +
972 " SECTION:\n";
973 for_each(impl_->rrsets_[SECTION_ADDITIONAL].begin(),
974 impl_->rrsets_[SECTION_ADDITIONAL].end(),
975 SectionFormatter<RRsetPtr>(SECTION_ADDITIONAL, s));
976 }
977
978 if (impl_->tsig_rr_) {
979 s += "\n;; TSIG PSEUDOSECTION:\n";
980 s += impl_->tsig_rr_->toText();
981 }
982
983 return (s);
984}
985
986void
988 impl_->init();
989 impl_->mode_ = mode;
990}
991
992void
993Message::appendSection(const Section section, const Message& source) {
994 if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
995 isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
996 }
997
998 if (section == SECTION_QUESTION) {
999 for (auto qi = source.beginQuestion(); qi != source.endQuestion(); ++qi) {
1000 addQuestion(*qi);
1001 }
1002 } else {
1003 for (auto rrsi = source.beginSection(section); rrsi != source.endSection(section); ++rrsi) {
1004 addRRset(section, *rrsi);
1005 }
1006 }
1007}
1008
1009void
1011 if (impl_->mode_ != Message::PARSE) {
1013 "makeResponse() is performed in non-parse mode");
1014 }
1015
1016 impl_->mode_ = Message::RENDER;
1017
1018 impl_->edns_ = EDNSPtr();
1019 impl_->flags_ &= MESSAGE_REPLYPRESERVE;
1021
1022 impl_->rrsets_[SECTION_ANSWER].clear();
1023 impl_->counts_[SECTION_ANSWER] = 0;
1024 impl_->rrsets_[SECTION_AUTHORITY].clear();
1025 impl_->counts_[SECTION_AUTHORITY] = 0;
1026 impl_->rrsets_[SECTION_ADDITIONAL].clear();
1027 impl_->counts_[SECTION_ADDITIONAL] = 0;
1028}
1029
1033template <typename T>
1035 SectionIteratorImpl(const typename vector<T>::const_iterator& it) :
1036 it_(it) {
1037 }
1038
1039 typename vector<T>::const_iterator it_;
1040};
1041
1042template <typename T>
1043SectionIterator<T>::SectionIterator(const SectionIteratorImpl<T>& impl) {
1044 impl_ = new SectionIteratorImpl<T>(impl.it_);
1045}
1046
1047template <typename T>
1049 delete impl_;
1050}
1051
1052template <typename T>
1053SectionIterator<T>::SectionIterator(const SectionIterator<T>& source) :
1054 impl_(new SectionIteratorImpl<T>(source.impl_->it_)) {
1055}
1056
1057template <typename T>
1058void
1059SectionIterator<T>::operator=(const SectionIterator<T>& source) {
1060 if (impl_ == source.impl_) {
1061 return;
1062 }
1063 SectionIteratorImpl<T>* newimpl =
1064 new SectionIteratorImpl<T>(source.impl_->it_);
1065 delete impl_;
1066 impl_ = newimpl;
1067}
1068
1069template <typename T>
1070SectionIterator<T>&
1072 ++(impl_->it_);
1073 return (*this);
1074}
1075
1076template <typename T>
1077SectionIterator<T>
1079 SectionIterator<T> tmp(*this);
1080 ++(*this);
1081 return (tmp);
1082}
1083
1084template <typename T>
1085const T&
1087 return (*(impl_->it_));
1088}
1089
1090template <typename T>
1091const T*
1093 return (&(operator*()));
1094}
1095
1096template <typename T>
1097bool
1098SectionIterator<T>::operator==(const SectionIterator<T>& other) const {
1099 return (impl_->it_ == other.impl_->it_);
1100}
1101
1102template <typename T>
1103bool
1104SectionIterator<T>::operator!=(const SectionIterator<T>& other) const {
1105 return (impl_->it_ != other.impl_->it_);
1106}
1107
1112template class SectionIterator<QuestionPtr>;
1113template class SectionIterator<RRsetPtr>;
1114
1115namespace {
1116typedef SectionIteratorImpl<QuestionPtr> QuestionIteratorImpl;
1117typedef SectionIteratorImpl<RRsetPtr> RRsetIteratorImpl;
1118}
1119
1123const QuestionIterator
1125 return (QuestionIterator(QuestionIteratorImpl(impl_->questions_.begin())));
1126}
1127
1128const QuestionIterator
1130 return (QuestionIterator(QuestionIteratorImpl(impl_->questions_.end())));
1131}
1132
1136const SectionIterator<RRsetPtr>
1137Message::beginSection(const Section section) const {
1138 if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
1139 isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
1140 }
1141 if (section == SECTION_QUESTION) {
1143 "RRset iterator is requested for question");
1144 }
1145
1146 return (RRsetIterator(RRsetIteratorImpl(impl_->rrsets_[section].begin())));
1147}
1148
1149const SectionIterator<RRsetPtr>
1150Message::endSection(const Section section) const {
1151 if (static_cast<int>(section) >= MessageImpl::NUM_SECTIONS) {
1152 isc_throw(OutOfRange, "Invalid message section: " << static_cast<int>(section));
1153 }
1154 if (section == SECTION_QUESTION) {
1156 "RRset iterator is requested for question");
1157 }
1158
1159 return (RRsetIterator(RRsetIteratorImpl(impl_->rrsets_[section].end())));
1160}
1161
1162ostream&
1163operator<<(ostream& os, const Message& message) {
1164 return (os << message.toText());
1165}
1166} // end of namespace dns
1167} // end of namespace isc
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...
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
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
unsigned int counter_
const RRClass & rrclass_
AbstractMessageRenderer & renderer_
string & output_
const Name & name_
bool truncated_
const Message::Section section_
const RRType & rrtype_
const bool partial_ok_
#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)