Kea 2.7.6
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
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:44
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.
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 clearSection(const Section section)
Remove all RRSets from the given Section.
Section
Constants to specify sections of a DNS message.
Definition message.h:242
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.
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.
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.
void setHeaderFlag(const HeaderFlag flag, const bool on=true)
Set or clear the specified header flag bit in the header section.
const Opcode & getOpcode() const
Return the OPCODE given in the header section of the message.
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.
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
static const Opcode & UPDATE()
A constant object for the UPDATE Opcode.
Definition opcode.h:208
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:88
const Name & getName() const
Returns the owner name of the Question.
Definition question.h:136
const RRClass & getClass() const
Returns the RR Type of the Question.
Definition question.h:156
The RRClass class encapsulates DNS resource record classes.
Definition rrclass.h:89
static const RRType & SOA()
Definition rrtype.h:291
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:228
SectionIterator is a templated class to provide standard-compatible iterators for Questions and RRset...
Definition message.h:88
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:419
TSIG errors.
Definition tsigerror.h:22
static const TSIGError & NOERROR()
A constant TSIG error object derived from Rcode::NOERROR()
Definition tsigerror.h:227
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:24
boost::shared_ptr< AbstractRRset > RRsetPtr
A pointer-like type pointing to an RRset object.
Definition rrset.h:50
Defines the logger used by the top-level component of kea-lfc.