Kea  2.5.3
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 
15 namespace isc {
16 namespace d2 {
17 
18 using 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 
34  return (message_.getHeaderFlag(dns::Message::HEADERFLAG_QR) ?
35  RESPONSE : REQUEST);
36 }
37 
38 uint16_t
40  return (message_.getQid());
41 }
42 
43 void
44 D2UpdateMessage::setId(const uint16_t id) {
45  message_.setQid(id);
46 }
47 
48 
49 const dns::Rcode&
51  return (message_.getRcode());
52 }
53 
54 void
56  message_.setRcode(rcode);
57 }
58 
59 unsigned 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 
74 void
75 D2UpdateMessage::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.
79  if (message_.getRRCount(dns::Message::SECTION_QUESTION) > 0) {
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 
94 void
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 
104 void
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 
123 void
124 D2UpdateMessage::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 
175 D2UpdateMessage::ddnsToDnsSection(const UpdateMsgSection section) {
179  switch(section) {
180  case SECTION_ZONE :
182 
185 
186  case SECTION_UPDATE:
188 
189  case SECTION_ADDITIONAL:
191 
192  default:
193  ;
194  }
196  "unknown message section " << section);
197 }
198 
199 void
200 D2UpdateMessage::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:439
bool getHeaderFlag(const HeaderFlag flag) const
Return whether the specified header flag bit is set in the header section.
Definition: dns/message.cc:397
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:573
unsigned int getRRCount(const Section section) const
Returns the number of RRs contained in the given section.
Definition: dns/message.cc:497
void setOpcode(const Opcode &opcode)
Set the OPCODE of the header section of the message.
Definition: dns/message.cc:464
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:505
void fromWire(isc::util::InputBuffer &buffer, ParseOptions options=PARSE_DEFAULT)
(Re)build a Message object from wire-format data.
Definition: dns/message.cc:640
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:487
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:407
const Opcode & getOpcode() const
Return the OPCODE given in the header section of the message.
Definition: dns/message.cc:456
void addQuestion(QuestionPtr question)
Add a (pointer like object of) Question to the message.
Definition: dns/message.cc:590
void setQid(qid_t qid)
Set the query ID of the header section of the message.
Definition: dns/message.cc:430
qid_t getQid() const
Return the query ID given in the header section of the message.
Definition: dns/message.cc:425
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:447
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:606
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 RRClass & getClass() const
Returns the RR Type of the Question.
Definition: question.h:159
const Name & getName() const
Returns the owner name of the Question.
Definition: question.h:143
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
@ error
Definition: db_log.h:116
boost::shared_ptr< Question > QuestionPtr
A pointer-like type pointing to an Question object.
Definition: question.h:28
boost::shared_ptr< AbstractRRset > RRsetPtr
A pointer-like type pointing to an RRset object.
Definition: rrset.h:47
Defines the logger used by the top-level component of kea-lfc.