// 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 #include #include #include #include #include #include #include #include #include using namespace isc::dns; using namespace isc::util; using namespace isc::asiolink; namespace isc { namespace testutils { const char* const DEFAULT_REMOTE_ADDRESS = "192.0.2.1"; const uint16_t DEFAULT_REMOTE_PORT = 53210; SrvTestBase::SrvTestBase() : request_message(Message::RENDER), parse_message(new Message(Message::PARSE)), response_message(new Message(Message::RENDER)), 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), response_obuffer(new OutputBuffer(0)) {} void SrvTestBase::createDataFromFile(const char* const datafile, const int protocol) { data.clear(); endpoint.reset(IOEndpoint::create(protocol, IOAddress(DEFAULT_REMOTE_ADDRESS), DEFAULT_REMOTE_PORT)); UnitTestUtil::readWireData(datafile, data); io_sock = (protocol == IPPROTO_UDP) ? &IOSocket::getDummyUDPSocket() : &IOSocket::getDummyTCPSocket(); io_message.reset(new IOMessage(&data[0], data.size(), *io_sock, *endpoint)); } void SrvTestBase::createRequestPacket(Message& message, const int protocol, TSIGContext* context, const char* const remote_address, uint16_t remote_port) { if (context == NULL) { message.toWire(request_renderer); } else { message.toWire(request_renderer, *context); } endpoint.reset(IOEndpoint::create(protocol, IOAddress(remote_address), remote_port)); io_sock = (protocol == IPPROTO_UDP) ? &IOSocket::getDummyUDPSocket() : &IOSocket::getDummyTCPSocket(); io_message.reset(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 opcodes we support. if (i == isc::dns::Opcode::QUERY().getCode() || i == isc::dns::Opcode::NOTIFY().getCode() || i == isc::dns::Opcode::UPDATE().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 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); } } // end of namespace testutils } // end of namespace isc // Local Variables: // mode: c++ // End: