d2_update_message.cc 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. // Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #include <d2/d2_update_message.h>
  15. #include <dns/messagerenderer.h>
  16. #include <dns/name.h>
  17. #include <dns/opcode.h>
  18. #include <dns/question.h>
  19. namespace isc {
  20. namespace d2 {
  21. using namespace isc::dns;
  22. D2UpdateMessage::D2UpdateMessage(const Direction direction)
  23. : message_(direction == INBOUND ?
  24. dns::Message::PARSE : dns::Message::RENDER) {
  25. // If this object is to create an outgoing message, we have to
  26. // set the proper Opcode field and QR flag here.
  27. if (direction == OUTBOUND) {
  28. message_.setOpcode(Opcode(Opcode::UPDATE_CODE));
  29. message_.setHeaderFlag(dns::Message::HEADERFLAG_QR, false);
  30. message_.setRcode(Rcode::NOERROR());
  31. }
  32. }
  33. D2UpdateMessage::QRFlag
  34. D2UpdateMessage::getQRFlag() const {
  35. return (message_.getHeaderFlag(dns::Message::HEADERFLAG_QR) ?
  36. RESPONSE : REQUEST);
  37. }
  38. uint16_t
  39. D2UpdateMessage::getId() const {
  40. return (message_.getQid());
  41. }
  42. void
  43. D2UpdateMessage::setId(const uint16_t id) {
  44. message_.setQid(id);
  45. }
  46. const dns::Rcode&
  47. D2UpdateMessage::getRcode() const {
  48. return (message_.getRcode());
  49. }
  50. void
  51. D2UpdateMessage::setRcode(const dns::Rcode& rcode) {
  52. message_.setRcode(rcode);
  53. }
  54. unsigned int
  55. D2UpdateMessage::getRRCount(const UpdateMsgSection section) const {
  56. return (message_.getRRCount(ddnsToDnsSection(section)));
  57. }
  58. const dns::RRsetIterator
  59. D2UpdateMessage::beginSection(const UpdateMsgSection section) const {
  60. return (message_.beginSection(ddnsToDnsSection(section)));
  61. }
  62. const dns::RRsetIterator
  63. D2UpdateMessage::endSection(const UpdateMsgSection section) const {
  64. return (message_.endSection(ddnsToDnsSection(section)));
  65. }
  66. void
  67. D2UpdateMessage::setZone(const Name& zone, const RRClass& rrclass) {
  68. // The Zone data is kept in the underlying Question class. If there
  69. // is a record stored there already, we need to remove it, because
  70. // we may have at most one Zone record in the DNS Update message.
  71. if (message_.getRRCount(dns::Message::SECTION_QUESTION) > 0) {
  72. message_.clearSection(dns::Message::SECTION_QUESTION);
  73. }
  74. // Add the new record...
  75. Question question(zone, rrclass, RRType::SOA());
  76. message_.addQuestion(question);
  77. // ... and update the local class member holding the D2Zone object.
  78. zone_.reset(new D2Zone(question.getName(), question.getClass()));
  79. }
  80. D2ZonePtr
  81. D2UpdateMessage::getZone() const {
  82. return (zone_);
  83. }
  84. void
  85. D2UpdateMessage::addRRset(const UpdateMsgSection section,
  86. const dns::RRsetPtr& rrset) {
  87. if (section == SECTION_ZONE) {
  88. isc_throw(isc::BadValue, "unable to add RRset to the Zone section"
  89. " of the DNS Update message, use setZone instead");
  90. }
  91. message_.addRRset(ddnsToDnsSection(section), rrset);
  92. }
  93. void
  94. D2UpdateMessage::toWire(AbstractMessageRenderer& renderer,
  95. TSIGContext* const tsig_context) {
  96. // We are preparing the wire format of the message, meaning
  97. // that this message will be sent as a request to the DNS.
  98. // Therefore, we expect that this message is a REQUEST.
  99. if (getQRFlag() != REQUEST) {
  100. isc_throw(InvalidQRFlag, "QR flag must be cleared for the outgoing"
  101. " DNS Update message");
  102. }
  103. // According to RFC2136, the ZONE section may contain exactly one
  104. // record.
  105. if (getRRCount(SECTION_ZONE) != 1) {
  106. isc_throw(InvalidZoneSection, "Zone section of the DNS Update message"
  107. " must comprise exactly one record (RFC2136, section 2.3)");
  108. }
  109. message_.toWire(renderer, tsig_context);
  110. }
  111. void
  112. D2UpdateMessage::fromWire(const void* received_data, size_t bytes_received,
  113. dns::TSIGContext* const tsig_context) {
  114. // First, use the underlying dns::Message implementation to get the
  115. // contents of the DNS response message. Note that it may or may
  116. // not be the message that we are interested in, but needs to be
  117. // parsed so as we can check its ID, Opcode etc.
  118. isc::util::InputBuffer received_data_buffer(received_data, bytes_received);
  119. message_.fromWire(received_data_buffer);
  120. // If tsig_contex is not NULL, then we need to verify the message.
  121. if (tsig_context) {
  122. TSIGError error = tsig_context->verify(message_.getTSIGRecord(),
  123. received_data, bytes_received);
  124. if (error != TSIGError::NOERROR()) {
  125. isc_throw(TSIGVerifyError, "TSIG verification failed: "
  126. << error.toText());
  127. }
  128. }
  129. // This class exposes the getZone() function. This function will return
  130. // pointer to the D2Zone object if non-empty Zone section exists in the
  131. // received message. It will return NULL pointer if it doesn't exist.
  132. // The pointer is held in the D2UpdateMessage class member. We need to
  133. // update this pointer every time we parse the message.
  134. if (getRRCount(D2UpdateMessage::SECTION_ZONE) > 0) {
  135. // There is a Zone section in the received message. Replace
  136. // Zone pointer with the new value.
  137. QuestionPtr question = *message_.beginQuestion();
  138. // If the Zone counter is greater than 0 (which we have checked)
  139. // there must be a valid Question pointer stored in the message_
  140. // object. If there isn't, it is a programming error.
  141. assert(question);
  142. zone_.reset(new D2Zone(question->getName(), question->getClass()));
  143. } else {
  144. // Zone section doesn't hold any pointers, so set the pointer to NULL.
  145. zone_.reset();
  146. }
  147. // Check that the content of the received message is sane.
  148. // One of the basic checks to do is to verify that we have
  149. // received the DNS update message. If not, it can be dropped
  150. // or an error message can be printed. Other than that, we
  151. // will check that there is at most one Zone record and QR flag
  152. // is set.
  153. validateResponse();
  154. }
  155. dns::Message::Section
  156. D2UpdateMessage::ddnsToDnsSection(const UpdateMsgSection section) {
  157. /// The following switch maps the enumerator values from the
  158. /// DNS Update message to the corresponding enumerator values
  159. /// representing fields of the DNS message.
  160. switch(section) {
  161. case SECTION_ZONE :
  162. return (dns::Message::SECTION_QUESTION);
  163. case SECTION_PREREQUISITE:
  164. return (dns::Message::SECTION_ANSWER);
  165. case SECTION_UPDATE:
  166. return (dns::Message::SECTION_AUTHORITY);
  167. case SECTION_ADDITIONAL:
  168. return (dns::Message::SECTION_ADDITIONAL);
  169. default:
  170. ;
  171. }
  172. isc_throw(dns::InvalidMessageSection,
  173. "unknown message section " << section);
  174. }
  175. void
  176. D2UpdateMessage::validateResponse() const {
  177. // Verify that we are dealing with the DNS Update message. According to
  178. // RFC 2136, section 3.8 server will copy the Opcode from the query.
  179. // If we are dealing with a different type of message, we may simply
  180. // stop further processing, because it is likely that the message was
  181. // directed to someone else.
  182. if (message_.getOpcode() != Opcode::UPDATE()) {
  183. isc_throw(NotUpdateMessage, "received message is not a DDNS update,"
  184. << " received message code is "
  185. << message_.getOpcode().getCode());
  186. }
  187. // Received message should have QR flag set, which indicates that it is
  188. // a RESPONSE.
  189. if (getQRFlag() == REQUEST) {
  190. isc_throw(InvalidQRFlag, "received message should have QR flag set,"
  191. " to indicate that it is a RESPONSE message; the QR"
  192. << " flag in received message is unset");
  193. }
  194. // DNS server may copy a Zone record from the query message. Since query
  195. // must comprise exactly one Zone record (RFC 2136, section 2.3), the
  196. // response message may contain 1 record at most. It may also contain no
  197. // records if a server chooses not to copy Zone section.
  198. if (getRRCount(SECTION_ZONE) > 1) {
  199. isc_throw(InvalidZoneSection, "received message contains "
  200. << getRRCount(SECTION_ZONE) << " Zone records,"
  201. << " it should contain at most 1 record");
  202. }
  203. }
  204. } // namespace d2
  205. } // namespace isc