Kea 2.5.7
d2_update_message.cc
Go to the documentation of this file.
1// Copyright (C) 2013-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
10#include <dns/messagerenderer.h>
11#include <dns/name.h>
12#include <dns/opcode.h>
13#include <dns/question.h>
14
15namespace isc {
16namespace d2 {
17
18using namespace isc::dns;
19
21 : message_(direction == INBOUND ?
22 dns::Message::PARSE : dns::Message::RENDER) {
23 // If this object is to create an outgoing message, we have to
24 // set the proper Opcode field and QR flag here.
25 if (direction == OUTBOUND) {
28 message_.setRcode(Rcode::NOERROR());
29 }
30}
31
36}
37
38uint16_t
40 return (message_.getQid());
41}
42
43void
44D2UpdateMessage::setId(const uint16_t id) {
45 message_.setQid(id);
46}
47
48
49const dns::Rcode&
51 return (message_.getRcode());
52}
53
54void
56 message_.setRcode(rcode);
57}
58
59unsigned int
61 return (message_.getRRCount(ddnsToDnsSection(section)));
62}
63
66 return (message_.beginSection(ddnsToDnsSection(section)));
67}
68
71 return (message_.endSection(ddnsToDnsSection(section)));
72}
73
74void
75D2UpdateMessage::setZone(const Name& zone, const RRClass& rrclass) {
76 // The Zone data is kept in the underlying Question class. If there
77 // is a record stored there already, we need to remove it, because
78 // we may have at most one Zone record in the DNS Update message.
81 }
82 // Add the new record...
83 Question question(zone, rrclass, RRType::SOA());
84 message_.addQuestion(question);
85 // ... and update the local class member holding the D2Zone object.
86 zone_.reset(new D2Zone(question.getName(), question.getClass()));
87}
88
91 return (zone_);
92}
93
94void
96 const dns::RRsetPtr& rrset) {
97 if (section == SECTION_ZONE) {
98 isc_throw(isc::BadValue, "unable to add RRset to the Zone section"
99 " of the DNS Update message, use setZone instead");
100 }
101 message_.addRRset(ddnsToDnsSection(section), rrset);
102}
103
104void
106 TSIGContext* const tsig_context) {
107 // We are preparing the wire format of the message, meaning
108 // that this message will be sent as a request to the DNS.
109 // Therefore, we expect that this message is a REQUEST.
110 if (getQRFlag() != REQUEST) {
111 isc_throw(InvalidQRFlag, "QR flag must be cleared for the outgoing"
112 " DNS Update message");
113 }
114 // According to RFC2136, the ZONE section may contain exactly one
115 // record.
116 if (getRRCount(SECTION_ZONE) != 1) {
117 isc_throw(InvalidZoneSection, "Zone section of the DNS Update message"
118 " must comprise exactly one record (RFC2136, section 2.3)");
119 }
120 message_.toWire(renderer, tsig_context);
121}
122
123void
124D2UpdateMessage::fromWire(const void* received_data, size_t bytes_received,
125 dns::TSIGContext* const tsig_context) {
126 // First, use the underlying dns::Message implementation to get the
127 // contents of the DNS response message. Note that it may or may
128 // not be the message that we are interested in, but needs to be
129 // parsed so as we can check its ID, Opcode etc.
130 isc::util::InputBuffer received_data_buffer(received_data, bytes_received);
131 message_.fromWire(received_data_buffer);
132
133 // If tsig_context is not NULL, then we need to verify the message.
134 if (tsig_context) {
135 TSIGError error = tsig_context->verify(message_.getTSIGRecord(),
136 received_data, bytes_received);
137 if (error != TSIGError::NOERROR()) {
138 isc_throw(TSIGVerifyError, "TSIG verification failed: "
139 << error.toText());
140 }
141 }
142
143 // This class exposes the getZone() function. This function will return
144 // pointer to the D2Zone object if non-empty Zone section exists in the
145 // received message. It will return NULL pointer if it doesn't exist.
146 // The pointer is held in the D2UpdateMessage class member. We need to
147 // update this pointer every time we parse the message.
149 // There is a Zone section in the received message. Replace
150 // Zone pointer with the new value.
151 QuestionPtr question = *message_.beginQuestion();
152 // If the Zone counter is greater than 0 (which we have checked)
153 // there must be a valid Question pointer stored in the message_
154 // object. If there isn't, it is a programming error.
155 if (!question) {
156 isc_throw(isc::Unexpected, "question is null?!");
157 }
158 zone_.reset(new D2Zone(question->getName(), question->getClass()));
159
160 } else {
161 // Zone section doesn't hold any pointers, so set the pointer to NULL.
162 zone_.reset();
163
164 }
165 // Check that the content of the received message is sane.
166 // One of the basic checks to do is to verify that we have
167 // received the DNS update message. If not, it can be dropped
168 // or an error message can be printed. Other than that, we
169 // will check that there is at most one Zone record and QR flag
170 // is set.
171 validateResponse();
172}
173
175D2UpdateMessage::ddnsToDnsSection(const UpdateMsgSection section) {
179 switch(section) {
180 case SECTION_ZONE :
182
185
186 case SECTION_UPDATE:
188
191
192 default:
193 ;
194 }
196 "unknown message section " << section);
197}
198
199void
200D2UpdateMessage::validateResponse() const {
201 // Verify that we are dealing with the DNS Update message. According to
202 // RFC 2136, section 3.8 server will copy the Opcode from the query.
203 // If we are dealing with a different type of message, we may simply
204 // stop further processing, because it is likely that the message was
205 // directed to someone else.
206 if (message_.getOpcode() != Opcode::UPDATE()) {
207 isc_throw(NotUpdateMessage, "received message is not a DDNS update,"
208 << " received message code is "
209 << message_.getOpcode().getCode());
210 }
211 // Received message should have QR flag set, which indicates that it is
212 // a RESPONSE.
213 if (getQRFlag() == REQUEST) {
214 isc_throw(InvalidQRFlag, "received message should have QR flag set,"
215 " to indicate that it is a RESPONSE message; the QR"
216 << " flag in received message is unset");
217 }
218 // DNS server may copy a Zone record from the query message. Since query
219 // must comprise exactly one Zone record (RFC 2136, section 2.3), the
220 // response message may contain 1 record at most. It may also contain no
221 // records if a server chooses not to copy Zone section.
222 if (getRRCount(SECTION_ZONE) > 1) {
223 isc_throw(InvalidZoneSection, "received message contains "
224 << getRRCount(SECTION_ZONE) << " Zone records,"
225 << " it should contain at most 1 record");
226 }
227}
228
229} // namespace d2
230} // namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A generic exception that is thrown when an unexpected error condition occurs.
D2ZonePtr getZone() const
Returns a pointer to the object representing Zone record.
void setId(const uint16_t id)
Sets message ID.
UpdateMsgSection
Identifies sections in the DNS Update Message.
QRFlag
Indicates whether DNS Update message is a REQUEST or RESPONSE.
unsigned int getRRCount(const UpdateMsgSection section) const
Returns number of RRsets in the specified message section.
const dns::RRsetIterator beginSection(const UpdateMsgSection section) const
Return iterators pointing to the beginning of the list of RRsets, which belong to the specified secti...
void fromWire(const void *received_data, size_t bytes_received, dns::TSIGContext *const tsig_context=NULL)
Decode incoming message from the wire format.
const dns::RRsetIterator endSection(const UpdateMsgSection section) const
Return iterators pointing to the end of the list of RRsets, which belong to the specified section.
void toWire(dns::AbstractMessageRenderer &renderer, dns::TSIGContext *const tsig_ctx=NULL)
Encode outgoing message into wire format.
Direction
Indicates if the D2UpdateMessage object encapsulates Inbound or Outbound message.
QRFlag getQRFlag() const
Returns enum value indicating if the message is a REQUEST or RESPONSE.
void setRcode(const dns::Rcode &rcode)
Sets message RCode.
D2UpdateMessage(const Direction direction=OUTBOUND)
Constructor used to create an instance of the DNS Update Message (either outgoing or incoming).
uint16_t getId() const
Returns message ID.
void addRRset(const UpdateMsgSection section, const dns::RRsetPtr &rrset)
Adds an RRset to the specified section.
void setZone(const dns::Name &zone, const dns::RRClass &rrclass)
Sets the Zone record.
const dns::Rcode & getRcode() const
Returns an object representing message RCode.
The D2Zone encapsulates the Zone section in DNS Update message.
Definition: d2_zone.h:32
Exception indicating that QR flag has invalid value.
Exception indicating that Zone section contains invalid content.
Exception indicating that a signed, inbound message failed to verify.
The AbstractMessageRenderer class is an abstract base class that provides common interfaces for rende...
A standard DNS module exception that is thrown if a section iterator is being constructed for an inco...
Definition: message.h:47
The Message class encapsulates a standard DNS message.
Definition: message.h:154
const QuestionIterator beginQuestion() const
Return an iterator corresponding to the beginning of the Question section of the message.
@ HEADERFLAG_QR
Query (if cleared) or response (if set)
Definition: message.h:205
const Rcode & getRcode() const
Return the Response Code of the message.
Definition: dns/message.cc:438
bool getHeaderFlag(const HeaderFlag flag) const
Return whether the specified header flag bit is set in the header section.
Definition: dns/message.cc:396
Section
Constants to specify sections of a DNS message.
Definition: message.h:244
@ SECTION_ADDITIONAL
Additional section.
Definition: message.h:248
@ SECTION_AUTHORITY
Authority section.
Definition: message.h:247
@ SECTION_ANSWER
Answer section.
Definition: message.h:246
@ SECTION_QUESTION
Question section
Definition: message.h:245
void clearSection(const Section section)
Remove all RRSets from the given Section.
Definition: dns/message.cc:571
unsigned int getRRCount(const Section section) const
Returns the number of RRs contained in the given section.
Definition: dns/message.cc:496
void setOpcode(const Opcode &opcode)
Set the OPCODE of the header section of the message.
Definition: dns/message.cc:463
void addRRset(const Section section, RRsetPtr rrset)
Add a (pointer like object of) RRset to the given section of the message.
Definition: dns/message.cc:504
void fromWire(isc::util::InputBuffer &buffer, ParseOptions options=PARSE_DEFAULT)
(Re)build a Message object from wire-format data.
Definition: dns/message.cc:638
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.
Definition: dns/message.cc:486
void setHeaderFlag(const HeaderFlag flag, const bool on=true)
Set or clear the specified header flag bit in the header section.
Definition: dns/message.cc:406
const Opcode & getOpcode() const
Return the OPCODE given in the header section of the message.
Definition: dns/message.cc:455
void addQuestion(QuestionPtr question)
Add a (pointer like object of) Question to the message.
Definition: dns/message.cc:588
void setQid(qid_t qid)
Set the query ID of the header section of the message.
Definition: dns/message.cc:429
qid_t getQid() const
Return the query ID given in the header section of the message.
Definition: dns/message.cc:424
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.
Definition: dns/message.cc:446
void toWire(AbstractMessageRenderer &renderer, TSIGContext *tsig_ctx=NULL)
Render the message in wire formant into a message renderer object with (or without) TSIG.
Definition: dns/message.cc:604
The Name class encapsulates DNS names.
Definition: name.h:223
The Opcode class objects represent standard OPCODEs of the header section of DNS messages as defined ...
Definition: opcode.h:32
static const Opcode & UPDATE()
A constant object for the UPDATE Opcode.
Definition: opcode.h:200
CodeValue getCode() const
Returns the Opcode code value.
Definition: opcode.h:78
@ UPDATE_CODE
5: Dynamic update (RFC2136)
Definition: opcode.h:41
The Question class encapsulates the common search key of DNS lookup, consisting of owner name,...
Definition: question.h:95
const Name & getName() const
Returns the owner name of the Question.
Definition: question.h:143
const RRClass & getClass() const
Returns the RR Type of the Question.
Definition: question.h:159
The RRClass class encapsulates DNS resource record classes.
Definition: rrclass.h:98
static const RRType & SOA()
Definition: rrtype.h:461
DNS Response Codes (RCODEs) class.
Definition: rcode.h:40
static const Rcode & NOERROR()
A constant object for the NOERROR Rcode (see Rcode::NOERROR_CODE).
Definition: rcode.h:220
SectionIterator is a templated class to provide standard-compatible iterators for Questions and RRset...
Definition: message.h:91
TSIG session context.
Definition: tsig.h:171
virtual TSIGError verify(const TSIGRecord *const record, const void *const data, const size_t data_len)
Verify a DNS message.
Definition: tsig.cc:423
TSIG errors.
Definition: tsigerror.h:22
static const TSIGError & NOERROR()
A constant TSIG error object derived from Rcode::NOERROR()
Definition: tsigerror.h:219
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition: buffer.h:81
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
boost::shared_ptr< D2Zone > D2ZonePtr
Definition: d2_zone.h:93
boost::shared_ptr< Question > QuestionPtr
A pointer-like type pointing to an Question object.
Definition: question.h:31
boost::shared_ptr< AbstractRRset > RRsetPtr
A pointer-like type pointing to an RRset object.
Definition: rrset.h:53
Defines the logger used by the top-level component of kea-lfc.