From: Marcin Siodelski Date: Tue, 25 Jun 2013 14:30:32 +0000 (+0200) Subject: [2976] Implemented unit tests for D2UpdateMessage setters. X-Git-Tag: bind10-1.2.0beta1-release~368^2~7 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=5e8a8cc152ef5cd151f99389f475b2a16832fb9c;p=thirdparty%2Fkea.git [2976] Implemented unit tests for D2UpdateMessage setters. --- diff --git a/src/bin/d2/d2_update_message.cc b/src/bin/d2/d2_update_message.cc index ab8ff16abf..f7fbe25e4e 100644 --- a/src/bin/d2/d2_update_message.cc +++ b/src/bin/d2/d2_update_message.cc @@ -25,8 +25,12 @@ using namespace isc::dns; D2UpdateMessage::D2UpdateMessage(const bool parse) : message_(parse ? dns::Message::PARSE : dns::Message::RENDER) { + // If this object is to create an outgoing message, we have to + // set the proper Opcode field and QR flag here. if (!parse) { message_.setOpcode(Opcode(Opcode::UPDATE_CODE)); + message_.setHeaderFlag(dns::Message::HEADERFLAG_QR, false); + } } @@ -36,20 +40,14 @@ D2UpdateMessage::getQRFlag() const { RESPONSE : REQUEST); } -void -D2UpdateMessage::setQRFlag(const QRFlag flag) { - bool on = (flag == RESPONSE ? true : false); - message_.setHeaderFlag(dns::Message::HEADERFLAG_QR, on); -} - uint16_t -D2UpdateMessage::getQid() const { +D2UpdateMessage::getId() const { return (message_.getQid()); } void -D2UpdateMessage::setQid(const uint16_t qid) { - message_.setQid(qid); +D2UpdateMessage::setId(const uint16_t id) { + message_.setQid(id); } @@ -80,12 +78,16 @@ D2UpdateMessage::endSection(const UpdateMsgSection section) const { void D2UpdateMessage::setZone(const Name& zone, const RRClass& rrclass) { + // The Zone data is kept in the underlying Question class. If there + // is a record stored there already, we need to remove it, because + // we may have at most one Zone record in the DNS Update message. if (message_.getRRCount(dns::Message::SECTION_QUESTION) > 0) { message_.clearSection(dns::Message::SECTION_QUESTION); } - + // Add the new record... Question question(zone, rrclass, RRType::SOA()); message_.addQuestion(question); + // ... and update the local class member holding the D2Zone object. zone_.reset(new D2Zone(question.getName(), question.getClass())); } @@ -98,22 +100,19 @@ void D2UpdateMessage::addRRset(const UpdateMsgSection section, const dns::RRsetPtr& rrset) { message_.addRRset(ddnsToDnsSection(section), rrset); - -} - -bool -D2UpdateMessage::hasRRset(const UpdateMsgSection section, const dns::Name& name, - const dns::RRClass& rrclass, const dns::RRType& rrtype) { - return (message_.hasRRset(ddnsToDnsSection(section), name, rrclass, rrtype)); -} - -bool -D2UpdateMessage::hasRRset(const UpdateMsgSection section, const dns::RRsetPtr &rrset) { - return (message_.hasRRset(ddnsToDnsSection(section), rrset)); } void D2UpdateMessage::toWire(AbstractMessageRenderer& renderer) { + // We are preparing the wire format of the message, meaning + // that this message will be sent as a request to the DNS. + // Therefore, we expect that this message is a REQUEST. + if (getQRFlag() != REQUEST) { + isc_throw(InvalidQRFlag, "QR flag must be cleared for the outgoing" + " DNS Update message"); + } + // According to RFC2136, the ZONE section may contain exactly one + // record. if (getRRCount(SECTION_ZONE) != 1) { isc_throw(InvalidZoneSection, "Zone section of the DNS Update message" " must comprise exactly one record (RFC2136, section 2.3)"); @@ -123,16 +122,36 @@ D2UpdateMessage::toWire(AbstractMessageRenderer& renderer) { void D2UpdateMessage::fromWire(isc::util::InputBuffer& buffer) { + // First, use the underlying dns::Message implementation to get the + // contents of the DNS response message. Note that it may or may + // not be the message that we are interested in, but needs to be + // parsed so as we can check its ID, Opcode etc. message_.fromWire(buffer); + // This class implements exposes the getZone() function. This function + // will return pointer to the D2Zone object if non-empty Zone + // section exists in the received message. It will return NULL pointer + // if it doesn't exist. The pointer is held in the D2UpdateMessage class + // member. We need to update this pointer every time we parse the + // message. if (getRRCount(D2UpdateMessage::SECTION_ZONE) > 0) { + // There is a Zone section in the received message. Replace + // Zone pointer with the new value. QuestionPtr question = *message_.beginQuestion(); assert(question); zone_.reset(new D2Zone(question->getName(), question->getClass())); } else { + // Zone section doesn't hold any pointers, so set the pointer to NULL. zone_.reset(); } + // Check that the content of the received message is sane. + // One of the basic checks to do is to verify that we have + // received the DNS update message. If not, it can be dropped + // or an error message can be printed. Other than that, we + // will check that there is at most one Zone record and QR flag + // is set. + validate(); } dns::Message::Section @@ -153,6 +172,33 @@ D2UpdateMessage::ddnsToDnsSection(const UpdateMsgSection section) { "unknown message section " << section); } +void +D2UpdateMessage::validate() const { + // Verify that we are dealing with the DNS Update message. According to + // RFC 2136, section 3.8 server will copy the Opcode from the query. + // If we are dealing with a different type of message, we may simply + // stop further processing, because it is likely that the message was + // directed to someone else. + if (message_.getOpcode() != Opcode::UPDATE()) { + isc_throw(NotUpdateMessage, "received message is not a DDNS update, received" + " message code is " << message_.getOpcode().getCode()); + } + // Received message should have QR flag set, which indicates that it is + // a RESPONSE. + if (getQRFlag() == REQUEST) { + isc_throw(NotUpdateMessage, "received message should should have QR flag set," + " to indicate that it is a RESPONSE message, the QR flag is unset"); + } + // DNS server may copy a Zone record from the query message. Since query must + // comprise exactly one Zone record (RFC 2136, section 2.3), the response message + // may contain 1 record at most. It may also contain no records if a server + // chooses not to copy Zone section. + if (getRRCount(SECTION_ZONE) > 1) { + isc_throw(InvalidZoneSection, "received message contains " << getRRCount(SECTION_ZONE) + << " Zone records, it should contain at most 1 record"); + } +} + } // namespace d2 } // namespace isc diff --git a/src/bin/d2/d2_update_message.h b/src/bin/d2/d2_update_message.h index 3979a0a2c3..5be983c9c6 100644 --- a/src/bin/d2/d2_update_message.h +++ b/src/bin/d2/d2_update_message.h @@ -39,6 +39,30 @@ public: isc::Exception(file, line, what) {} }; +/// @brief Exception indicating that QR flag has invalid value. +/// +/// This exception is thrown when QR flag has invalid value for +/// the operation performed on the particular message. For instance, +/// the QR flag must be set to indicate that the given message is +/// a RESPONSE when @c D2UpdateMessage::fromWire is performed. +/// The QR flag must be cleared when @c D2UpdateMessage::toWire +/// is executed. +class InvalidQRFlag : public Exception { +public: + InvalidQRFlag(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + +/// @brief Exception indicating that the parsed message is not DNS Update. +/// +/// This exception is thrown when decoding the DNS message which is not +/// a DNS Update. +class NotUpdateMessage : public Exception { +public: + NotUpdateMessage(const char* file, size_t line, const char* what) : + isc::Exception(file, line, what) {} +}; + /// @brief The @c D2UpdateMessage encapsulates a DNS Update message. /// @@ -80,11 +104,9 @@ public: QRFlag getQRFlag() const; - void setQRFlag(const QRFlag flag); + uint16_t getId() const; - uint16_t getQid() const; - - void setQid(const uint16_t qid); + void setId(const uint16_t qid); const dns::Rcode& getRcode() const; @@ -102,15 +124,6 @@ public: void addRRset(const UpdateMsgSection section, const dns::RRsetPtr& rrset); - bool hasRRset(const UpdateMsgSection section, const dns::Name& name, - const dns::RRClass& rrclass, const dns::RRType& rrtype); - - bool hasRRset(const UpdateMsgSection section, const dns::RRsetPtr &rrset); - - void clearSection(const UpdateMsgSection section); - - void clear(const bool parse_mode); - void toWire(dns::AbstractMessageRenderer& renderer); void fromWire(isc::util::InputBuffer& buffer); @@ -119,6 +132,7 @@ public: private: static dns::Message::Section ddnsToDnsSection(const UpdateMsgSection section); + void validate() const; dns::Message message_; D2ZonePtr zone_; diff --git a/src/bin/d2/tests/d2_update_message_unittests.cc b/src/bin/d2/tests/d2_update_message_unittests.cc index 5f179fff6b..3883fc79c4 100644 --- a/src/bin/d2/tests/d2_update_message_unittests.cc +++ b/src/bin/d2/tests/d2_update_message_unittests.cc @@ -76,6 +76,45 @@ public: } }; +// This test verifies that DNS Update message ID can be set using +// setId function. +TEST_F(D2UpdateMessageTest, setId) { + D2UpdateMessage msg; + EXPECT_EQ(0, msg.getId()); + msg.setId(0x1234); + EXPECT_EQ(0x1234, msg.getId()); +} + +// This test verifies that the DNS Update message RCODE can be set +// using setRcode function. +TEST_F(D2UpdateMessageTest, setRcode) { + D2UpdateMessage msg; + msg.setRcode(Rcode::NOERROR()); + EXPECT_EQ(Rcode::NOERROR().getCode(), msg.getRcode().getCode()); + + msg.setRcode(Rcode::NOTIMP()); + EXPECT_EQ(Rcode::NOTIMP().getCode(), msg.getRcode().getCode()); +} + +// This test verifies that the Zone section in the DNS Update message +// can be set. +TEST_F(D2UpdateMessageTest, setZone) { + D2UpdateMessage msg; + D2ZonePtr zone = msg.getZone(); + EXPECT_FALSE(zone); + msg.setZone(Name("example.com"), RRClass::ANY()); + zone = msg.getZone(); + EXPECT_TRUE(zone); + EXPECT_EQ("example.com.", zone->getName().toText()); + EXPECT_EQ(RRClass::ANY().getCode(), zone->getClass().getCode()); + + msg.setZone(Name("foo.example.com"), RRClass::NONE()); + zone = msg.getZone(); + EXPECT_TRUE(zone); + EXPECT_EQ("foo.example.com.", zone->getName().toText()); + EXPECT_EQ(RRClass::NONE().getCode(), zone->getClass().getCode()); +} + // This test verifies that the DNS message is properly decoded from the // wire format. TEST_F(D2UpdateMessageTest, fromWire) { @@ -158,7 +197,7 @@ TEST_F(D2UpdateMessageTest, fromWire) { ASSERT_NO_THROW(msg.fromWire(buf)); // Check that the message header is valid. - EXPECT_EQ(0x05AF, msg.getQid()); + EXPECT_EQ(0x05AF, msg.getId()); EXPECT_EQ(D2UpdateMessage::RESPONSE, msg.getQRFlag()); EXPECT_EQ(Rcode::YXDOMAIN_CODE, msg.getRcode().getCode()); @@ -225,9 +264,7 @@ TEST_F(D2UpdateMessageTest, fromWire) { TEST_F(D2UpdateMessageTest, toWire) { D2UpdateMessage msg; // Set message ID. - msg.setQid(0x1234); - // Make it a Request message by setting the QR flag to 0. - msg.setQRFlag(D2UpdateMessage::REQUEST); + msg.setId(0x1234); // Rcode to NOERROR. msg.setRcode(Rcode(Rcode::NOERROR_CODE));