|
@@ -0,0 +1,272 @@
|
|
|
|
+// Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
|
|
|
|
+//
|
|
|
|
+// Permission to use, copy, modify, and/or distribute this software for any
|
|
|
|
+// purpose with or without fee is hereby granted, provided that the above
|
|
|
|
+// copyright notice and this permission notice appear in all copies.
|
|
|
|
+//
|
|
|
|
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
|
|
|
|
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
|
|
+// AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
|
|
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
|
|
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
|
|
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
|
|
+// PERFORMANCE OF THIS SOFTWARE.
|
|
|
|
+
|
|
|
|
+#include <netinet/in.h>
|
|
|
|
+
|
|
|
|
+#include <dns/message.h>
|
|
|
|
+#include <dns/rcode.h>
|
|
|
|
+
|
|
|
|
+#include <asiolink/asiolink.h>
|
|
|
|
+
|
|
|
|
+#include <dns/tests/unittest_util.h>
|
|
|
|
+
|
|
|
|
+#include <testutils/srv_test.h>
|
|
|
|
+
|
|
|
|
+using namespace isc::dns;
|
|
|
|
+using namespace asiolink;
|
|
|
|
+
|
|
|
|
+namespace isc {
|
|
|
|
+namespace testutils {
|
|
|
|
+const char* const DEFAULT_REMOTE_ADDRESS = "192.0.2.1";
|
|
|
|
+
|
|
|
|
+const unsigned int QR_FLAG = 0x1;
|
|
|
|
+const unsigned int AA_FLAG = 0x2;
|
|
|
|
+const unsigned int TC_FLAG = 0x4;
|
|
|
|
+const unsigned int RD_FLAG = 0x8;
|
|
|
|
+const unsigned int RA_FLAG = 0x10;
|
|
|
|
+const unsigned int AD_FLAG = 0x20;
|
|
|
|
+const unsigned int CD_FLAG = 0x40;
|
|
|
|
+
|
|
|
|
+SrvTestBase::SrvTestBase() : request_message(Message::RENDER),
|
|
|
|
+ parse_message(new Message(Message::PARSE)),
|
|
|
|
+ default_qid(0x1035),
|
|
|
|
+ opcode(Opcode(Opcode::QUERY())),
|
|
|
|
+ qname("www.example.com"),
|
|
|
|
+ qclass(RRClass::IN()),
|
|
|
|
+ qtype(RRType::A()), io_sock(NULL),
|
|
|
|
+ io_message(NULL), endpoint(NULL),
|
|
|
|
+ request_obuffer(0),
|
|
|
|
+ request_renderer(request_obuffer),
|
|
|
|
+ response_obuffer(new OutputBuffer(0))
|
|
|
|
+{}
|
|
|
|
+
|
|
|
|
+SrvTestBase::~SrvTestBase() {
|
|
|
|
+ delete io_message;
|
|
|
|
+ delete endpoint;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+SrvTestBase::createDataFromFile(const char* const datafile,
|
|
|
|
+ const int protocol)
|
|
|
|
+{
|
|
|
|
+ delete io_message;
|
|
|
|
+ data.clear();
|
|
|
|
+
|
|
|
|
+ delete endpoint;
|
|
|
|
+
|
|
|
|
+ endpoint = IOEndpoint::create(protocol,
|
|
|
|
+ IOAddress(DEFAULT_REMOTE_ADDRESS), 5300);
|
|
|
|
+ UnitTestUtil::readWireData(datafile, data);
|
|
|
|
+ io_sock = (protocol == IPPROTO_UDP) ? &IOSocket::getDummyUDPSocket() :
|
|
|
|
+ &IOSocket::getDummyTCPSocket();
|
|
|
|
+ io_message = new IOMessage(&data[0], data.size(), *io_sock, *endpoint);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+SrvTestBase::createRequestPacket(Message& message,
|
|
|
|
+ const int protocol)
|
|
|
|
+{
|
|
|
|
+ message.toWire(request_renderer);
|
|
|
|
+
|
|
|
|
+ delete io_message;
|
|
|
|
+
|
|
|
|
+ endpoint = IOEndpoint::create(protocol,
|
|
|
|
+ IOAddress(DEFAULT_REMOTE_ADDRESS), 5300);
|
|
|
|
+ io_sock = (protocol == IPPROTO_UDP) ? &IOSocket::getDummyUDPSocket() :
|
|
|
|
+ &IOSocket::getDummyTCPSocket();
|
|
|
|
+ io_message = new IOMessage(request_renderer.getData(),
|
|
|
|
+ request_renderer.getLength(),
|
|
|
|
+ *io_sock, *endpoint);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Unsupported requests. Should result in NOTIMP.
|
|
|
|
+void
|
|
|
|
+SrvTestBase::unsupportedRequest() {
|
|
|
|
+ for (unsigned int i = 0; i < 16; ++i) {
|
|
|
|
+ // set Opcode to 'i', which iterators over all possible codes except
|
|
|
|
+ // the standard query and notify
|
|
|
|
+ if (i == isc::dns::Opcode::QUERY().getCode() ||
|
|
|
|
+ i == isc::dns::Opcode::NOTIFY().getCode()) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+ createDataFromFile("simplequery_fromWire.wire");
|
|
|
|
+ data[2] = ((i << 3) & 0xff);
|
|
|
|
+
|
|
|
|
+ parse_message->clear(isc::dns::Message::PARSE);
|
|
|
|
+ processMessage();
|
|
|
|
+ EXPECT_TRUE(dnsserv.hasAnswer());
|
|
|
|
+ headerCheck(*parse_message, default_qid, isc::dns::Rcode::NOTIMP(), i,
|
|
|
|
+ QR_FLAG, 0, 0, 0, 0);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Multiple questions. Should result in FORMERR.
|
|
|
|
+void
|
|
|
|
+SrvTestBase::multiQuestion() {
|
|
|
|
+ createDataFromFile("multiquestion_fromWire.wire");
|
|
|
|
+ processMessage();
|
|
|
|
+ EXPECT_TRUE(dnsserv.hasAnswer());
|
|
|
|
+ headerCheck(*parse_message, default_qid, isc::dns::Rcode::FORMERR(),
|
|
|
|
+ opcode.getCode(), QR_FLAG, 2, 0, 0, 0);
|
|
|
|
+
|
|
|
|
+ isc::dns::QuestionIterator qit = parse_message->beginQuestion();
|
|
|
|
+ EXPECT_EQ(isc::dns::Name("example.com"), (*qit)->getName());
|
|
|
|
+ EXPECT_EQ(isc::dns::RRClass::IN(), (*qit)->getClass());
|
|
|
|
+ EXPECT_EQ(isc::dns::RRType::A(), (*qit)->getType());
|
|
|
|
+ ++qit;
|
|
|
|
+ EXPECT_EQ(isc::dns::Name("example.com"), (*qit)->getName());
|
|
|
|
+ EXPECT_EQ(isc::dns::RRClass::IN(), (*qit)->getClass());
|
|
|
|
+ EXPECT_EQ(isc::dns::RRType::AAAA(), (*qit)->getType());
|
|
|
|
+ ++qit;
|
|
|
|
+ EXPECT_TRUE(qit == parse_message->endQuestion());
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Incoming data doesn't even contain the complete header. Must be silently
|
|
|
|
+// dropped.
|
|
|
|
+void
|
|
|
|
+SrvTestBase::shortMessage() {
|
|
|
|
+ createDataFromFile("shortmessage_fromWire");
|
|
|
|
+ processMessage();
|
|
|
|
+ EXPECT_FALSE(dnsserv.hasAnswer());
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Response messages. Must be silently dropped, whether it's a valid response
|
|
|
|
+// or malformed or could otherwise cause a protocol error.
|
|
|
|
+void
|
|
|
|
+SrvTestBase::response() {
|
|
|
|
+ // A valid (although unusual) response
|
|
|
|
+ createDataFromFile("simpleresponse_fromWire.wire");
|
|
|
|
+ processMessage();
|
|
|
|
+ EXPECT_FALSE(dnsserv.hasAnswer());
|
|
|
|
+
|
|
|
|
+ // A response with a broken question section. must be dropped rather than
|
|
|
|
+ //returning FORMERR.
|
|
|
|
+ createDataFromFile("shortresponse_fromWire");
|
|
|
|
+ processMessage();
|
|
|
|
+ EXPECT_FALSE(dnsserv.hasAnswer());
|
|
|
|
+
|
|
|
|
+ // A response to iquery. must be dropped rather than returning NOTIMP.
|
|
|
|
+ createDataFromFile("iqueryresponse_fromWire.wire");
|
|
|
|
+ processMessage();
|
|
|
|
+ EXPECT_FALSE(dnsserv.hasAnswer());
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Query with a broken question
|
|
|
|
+void
|
|
|
|
+SrvTestBase::shortQuestion() {
|
|
|
|
+ createDataFromFile("shortquestion_fromWire");
|
|
|
|
+ processMessage();
|
|
|
|
+ EXPECT_TRUE(dnsserv.hasAnswer());
|
|
|
|
+ // Since the query's question is broken, the question section of the
|
|
|
|
+ // response should be empty.
|
|
|
|
+ headerCheck(*parse_message, default_qid, isc::dns::Rcode::FORMERR(),
|
|
|
|
+ opcode.getCode(), QR_FLAG, 0, 0, 0, 0);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Query with a broken answer section
|
|
|
|
+void
|
|
|
|
+SrvTestBase::shortAnswer() {
|
|
|
|
+ createDataFromFile("shortanswer_fromWire.wire");
|
|
|
|
+ processMessage();
|
|
|
|
+ EXPECT_TRUE(dnsserv.hasAnswer());
|
|
|
|
+
|
|
|
|
+ // This is a bogus query, but question section is valid. So the response
|
|
|
|
+ // should copy the question section.
|
|
|
|
+ headerCheck(*parse_message, default_qid, isc::dns::Rcode::FORMERR(),
|
|
|
|
+ opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
|
|
|
|
+
|
|
|
|
+ isc::dns::QuestionIterator qit = parse_message->beginQuestion();
|
|
|
|
+ EXPECT_EQ(isc::dns::Name("example.com"), (*qit)->getName());
|
|
|
|
+ EXPECT_EQ(isc::dns::RRClass::IN(), (*qit)->getClass());
|
|
|
|
+ EXPECT_EQ(isc::dns::RRType::A(), (*qit)->getType());
|
|
|
|
+ ++qit;
|
|
|
|
+ EXPECT_TRUE(qit == parse_message->endQuestion());
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Query with unsupported version of EDNS.
|
|
|
|
+void
|
|
|
|
+SrvTestBase::ednsBadVers() {
|
|
|
|
+ createDataFromFile("queryBadEDNS_fromWire.wire");
|
|
|
|
+ processMessage();
|
|
|
|
+ EXPECT_TRUE(dnsserv.hasAnswer());
|
|
|
|
+
|
|
|
|
+ // The response must have an EDNS OPT RR in the additional section,
|
|
|
|
+ // it will be added automatically at the render time.
|
|
|
|
+ // Note that the DNSSEC DO bit is cleared even if this bit in the query
|
|
|
|
+ // is set. This is a limitation of the current implementation.
|
|
|
|
+ headerCheck(*parse_message, default_qid, isc::dns::Rcode::BADVERS(),
|
|
|
|
+ opcode.getCode(), QR_FLAG, 1, 0, 0, 1);
|
|
|
|
+ EXPECT_FALSE(parse_message->getEDNS()); // EDNS isn't added at this point
|
|
|
|
+
|
|
|
|
+ isc::dns::InputBuffer ib(response_obuffer->getData(),
|
|
|
|
+ response_obuffer->getLength());
|
|
|
|
+ isc::dns::Message parsed(isc::dns::Message::PARSE);
|
|
|
|
+ parsed.fromWire(ib);
|
|
|
|
+ EXPECT_EQ(isc::dns::Rcode::BADVERS(), parsed.getRcode());
|
|
|
|
+ isc::dns::ConstEDNSPtr edns(parsed.getEDNS());
|
|
|
|
+ ASSERT_TRUE(edns);
|
|
|
|
+ EXPECT_FALSE(edns->getDNSSECAwareness());
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+SrvTestBase::axfrOverUDP() {
|
|
|
|
+ // AXFR over UDP is invalid and should result in FORMERR.
|
|
|
|
+ UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
|
|
|
|
+ isc::dns::Name("example.com"),
|
|
|
|
+ isc::dns::RRClass::IN(),
|
|
|
|
+ isc::dns::RRType::AXFR());
|
|
|
|
+ createRequestPacket(request_message, IPPROTO_UDP);
|
|
|
|
+ processMessage();
|
|
|
|
+ EXPECT_TRUE(dnsserv.hasAnswer());
|
|
|
|
+ headerCheck(*parse_message, default_qid, isc::dns::Rcode::FORMERR(),
|
|
|
|
+ opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void
|
|
|
|
+headerCheck(const Message& message, const qid_t qid, const Rcode& rcode,
|
|
|
|
+ const uint16_t opcodeval, const unsigned int flags,
|
|
|
|
+ const unsigned int qdcount,
|
|
|
|
+ const unsigned int ancount, const unsigned int nscount,
|
|
|
|
+ const unsigned int arcount)
|
|
|
|
+{
|
|
|
|
+ EXPECT_EQ(qid, message.getQid());
|
|
|
|
+ EXPECT_EQ(rcode, message.getRcode());
|
|
|
|
+ EXPECT_EQ(opcodeval, message.getOpcode().getCode());
|
|
|
|
+ EXPECT_EQ((flags & QR_FLAG) != 0,
|
|
|
|
+ message.getHeaderFlag(Message::HEADERFLAG_QR));
|
|
|
|
+ EXPECT_EQ((flags & AA_FLAG) != 0,
|
|
|
|
+ message.getHeaderFlag(Message::HEADERFLAG_AA));
|
|
|
|
+ EXPECT_EQ((flags & TC_FLAG) != 0,
|
|
|
|
+ message.getHeaderFlag(Message::HEADERFLAG_TC));
|
|
|
|
+ EXPECT_EQ((flags & RA_FLAG) != 0,
|
|
|
|
+ message.getHeaderFlag(Message::HEADERFLAG_RA));
|
|
|
|
+ EXPECT_EQ((flags & RD_FLAG) != 0,
|
|
|
|
+ message.getHeaderFlag(Message::HEADERFLAG_RD));
|
|
|
|
+ EXPECT_EQ((flags & AD_FLAG) != 0,
|
|
|
|
+ message.getHeaderFlag(Message::HEADERFLAG_AD));
|
|
|
|
+ EXPECT_EQ((flags & CD_FLAG) != 0,
|
|
|
|
+ message.getHeaderFlag(Message::HEADERFLAG_CD));
|
|
|
|
+
|
|
|
|
+ EXPECT_EQ(qdcount, message.getRRCount(Message::SECTION_QUESTION));
|
|
|
|
+ EXPECT_EQ(ancount, message.getRRCount(Message::SECTION_ANSWER));
|
|
|
|
+ EXPECT_EQ(nscount, message.getRRCount(Message::SECTION_AUTHORITY));
|
|
|
|
+ EXPECT_EQ(arcount, message.getRRCount(Message::SECTION_ADDITIONAL));
|
|
|
|
+}
|
|
|
|
+} // end of namespace testutils
|
|
|
|
+} // end of namespace isc
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// Local Variables:
|
|
|
|
+// mode: c++
|
|
|
|
+// End:
|