Kea  1.9.9-git
d2_update_message.cc
Go to the documentation of this file.
1 // Copyright (C) 2013-2020 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 
9 #include <d2/d2_update_message.h>
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
The Name class encapsulates DNS names.
Definition: name.h:223
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 Rcode & getRcode() const
Return the Response Code of the message.
Definition: dns/message.cc:433
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:601
TSIG session context.
Definition: tsig.h:171
uint16_t getId() const
Returns message ID.
Exception indicating that QR flag has invalid value.
const QuestionIterator beginQuestion() const
Return an iterator corresponding to the beginning of the Question section of the message.
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
static const Rcode & NOERROR()
A constant object for the NOERROR Rcode (see Rcode::NOERROR_CODE).
Definition: rcode.h:220
5: Dynamic update (RFC2136)
Definition: opcode.h:41
void toWire(dns::AbstractMessageRenderer &renderer, dns::TSIGContext *const tsig_ctx=NULL)
Encode outgoing message into wire format.
static const RRType & SOA()
Definition: rrtype.h:461
qid_t getQid() const
Return the query ID given in the header section of the message.
Definition: dns/message.cc:419
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 clearSection(const Section section)
Remove all RRSets from the given Section.
Definition: dns/message.cc:568
boost::shared_ptr< D2Zone > D2ZonePtr
Definition: d2_zone.h:93
void setRcode(const Rcode &rcode)
Set the Response Code of the message.
Definition: dns/message.cc:441
QRFlag getQRFlag() const
Returns enum value indicating if the message is a REQUEST or RESPONSE.
DNS Response Codes (RCODEs) class.
Definition: rcode.h:40
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:401
void fromWire(const void *received_data, size_t bytes_received, dns::TSIGContext *const tsig_context=NULL)
Decode incoming message from the wire format.
const Opcode & getOpcode() const
Return the OPCODE given in the header section of the message.
Definition: dns/message.cc:450
The Message class encapsulates a standard DNS message.
Definition: message.h:143
void setRcode(const dns::Rcode &rcode)
Sets message RCode.
The RRClass class encapsulates DNS resource record classes.
Definition: rrclass.h:98
Exception indicating that Zone section contains invalid content.
static const Opcode & UPDATE()
A constant object for the UPDATE Opcode.
Definition: opcode.h:200
void setZone(const dns::Name &zone, const dns::RRClass &rrclass)
Sets the Zone record.
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
A standard DNS module exception that is thrown if a section iterator is being constructed for an inco...
Definition: message.h:47
void fromWire(isc::util::InputBuffer &buffer, ParseOptions options=PARSE_DEFAULT)
(Re)build a Message object from wire-format data.
Definition: dns/message.cc:635
The AbstractMessageRenderer class is an abstract base class that provides common interfaces for rende...
void addQuestion(QuestionPtr question)
Add a (pointer like object of) Question to the message.
Definition: dns/message.cc:585
void addRRset(const UpdateMsgSection section, const dns::RRsetPtr &rrset)
Adds an RRset to the specified section.
A generic exception that is thrown when an unexpected error condition occurs.
The D2Zone encapsulates the Zone section in DNS Update message.
Definition: d2_zone.h:32
bool getHeaderFlag(const HeaderFlag flag) const
Return whether the specified header flag bit is set in the header section.
Definition: dns/message.cc:391
D2UpdateMessage(const Direction direction=OUTBOUND)
Constructor used to create an instance of the DNS Update Message (either outgoing or incoming)...
QRFlag
Indicates whether DNS Update message is a REQUEST or RESPONSE.
Query (if cleared) or response (if set)
Definition: message.h:194
CodeValue getCode() const
Returns the Opcode code value.
Definition: opcode.h:78
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...
const RRClass & getClass() const
Returns the RR Type of the Question.
Definition: question.h:159
Defines the logger used by the top-level component of kea-dhcp-ddns.
const RRsetIterator beginSection(const Section section) const
Return an iterator corresponding to the beginning of the given section (other than Question) of the m...
SectionIterator is a templated class to provide standard-compatible iterators for Questions and RRset...
Definition: message.h:91
unsigned int getRRCount(const Section section) const
Returns the number of RRs contained in the given section.
Definition: dns/message.cc:491
void setOpcode(const Opcode &opcode)
Set the OPCODE of the header section of the message.
Definition: dns/message.cc:458
Section
Constants to specify sections of a DNS message.
Definition: message.h:233
TSIG errors.
Definition: tsigerror.h:22
std::string toText() const
Convert the TSIGError to a string.
Definition: tsigerror.cc:40
UpdateMsgSection
Identifies sections in the DNS Update Message.
void setId(const uint16_t id)
Sets message ID.
const TSIGRecord * getTSIGRecord() const
Return, if any, the TSIG record contained in the received message.
Definition: dns/message.cc:481
The InputBuffer class is a buffer abstraction for manipulating read-only data.
Definition: buffer.h:81
void setQid(qid_t qid)
Set the query ID of the header section of the message.
Definition: dns/message.cc:424
Direction
Indicates if the D2UpdateMessage object encapsulates Inbound or Outbound message. ...
D2ZonePtr getZone() const
Returns a pointer to the object representing Zone record.
TSIGError verify(const TSIGRecord *const record, const void *const data, const size_t data_len)
Verify a DNS message.
Definition: tsig.cc:428
const dns::Rcode & getRcode() const
Returns an object representing message RCode.
boost::shared_ptr< AbstractRRset > RRsetPtr
A pointer-like type pointing to an RRset object.
Definition: rrset.h:47
unsigned int getRRCount(const UpdateMsgSection section) const
Returns number of RRsets in the specified message section.
Exception indicating that a signed, inbound message failed to verify.
The Opcode class objects represent standard OPCODEs of the header section of DNS messages as defined ...
Definition: opcode.h:32
boost::shared_ptr< Question > QuestionPtr
A pointer-like type pointing to an Question object.
Definition: question.h:28
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:499
static const TSIGError & NOERROR()
A constant TSIG error object derived from Rcode::NOERROR()
Definition: tsigerror.h:219