|
@@ -1,728 +0,0 @@
|
|
|
-// Copyright (C) 2011 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 <algorithm>
|
|
|
-#include <cstdlib>
|
|
|
-#include <iomanip>
|
|
|
-#include <iostream>
|
|
|
-#include <string>
|
|
|
-#include <vector>
|
|
|
-
|
|
|
-#include <gtest/gtest.h>
|
|
|
-#include <boost/bind.hpp>
|
|
|
-
|
|
|
-#include <asio.hpp>
|
|
|
-
|
|
|
-#include <util/buffer.h>
|
|
|
-#include <util/io_utilities.h>
|
|
|
-
|
|
|
-#include <dns/question.h>
|
|
|
-#include <dns/message.h>
|
|
|
-#include <dns/messagerenderer.h>
|
|
|
-#include <dns/opcode.h>
|
|
|
-#include <dns/name.h>
|
|
|
-#include <dns/rcode.h>
|
|
|
-#include <dns/rrtype.h>
|
|
|
-#include <dns/rrset.h>
|
|
|
-#include <dns/rrttl.h>
|
|
|
-#include <dns/rdata.h>
|
|
|
-
|
|
|
-#include <util/io_utilities.h>
|
|
|
-#include <asiodns/dns_service.h>
|
|
|
-#include <asiodns/io_fetch.h>
|
|
|
-#include <asiolink/io_address.h>
|
|
|
-#include <asiolink/io_endpoint.h>
|
|
|
-#include <asiolink/io_service.h>
|
|
|
-#include <resolve/recursive_query.h>
|
|
|
-#include <resolve/resolver_interface.h>
|
|
|
-
|
|
|
-using namespace asio;
|
|
|
-using namespace asio::ip;
|
|
|
-using namespace isc::asiolink;
|
|
|
-using namespace isc::dns;
|
|
|
-using namespace isc::dns::rdata;
|
|
|
-using namespace isc::util;
|
|
|
-using namespace isc::resolve;
|
|
|
-using namespace std;
|
|
|
-
|
|
|
-/// RecursiveQuery Test - 2
|
|
|
-///
|
|
|
-/// The second part of the RecursiveQuery unit tests, this attempts to get the
|
|
|
-/// RecursiveQuery object to follow a set of referrals for "www.example.org" to
|
|
|
-/// and to invoke TCP fallback on one of the queries. In particular, we expect
|
|
|
-/// that the test will do the following in an attempt to resolve
|
|
|
-/// www.example.org:
|
|
|
-///
|
|
|
-/// - Send question over UDP to "root" - get referral to "org".
|
|
|
-/// - Send question over UDP to "org" - get referral to "example.org" with TC bit set.
|
|
|
-/// - Send question over TCP to "org" - get referral to "example.org".
|
|
|
-/// - Send question over UDP to "example.org" - get response for www.example.org.
|
|
|
-///
|
|
|
-/// (The order of queries is set in this way in order to also test that after a
|
|
|
-/// failover to TCP, queries revert to UDP).
|
|
|
-///
|
|
|
-/// By using the "test_server_" element of RecursiveQuery, all queries are
|
|
|
-/// directed to one or other of the "servers" in the RecursiveQueryTest2 class,
|
|
|
-/// regardless of the glue returned in referrals.
|
|
|
-
|
|
|
-namespace {
|
|
|
-const char* const TEST_ADDRESS = "127.0.0.1"; ///< Servers are on this address
|
|
|
-const uint16_t TEST_PORT = 5301; ///< ... and this port
|
|
|
-const size_t BUFFER_SIZE = 1024; ///< For all buffers
|
|
|
-const char* const WWW_EXAMPLE_ORG = "192.0.2.254";
|
|
|
- ///< Address of www.example.org
|
|
|
-} //end anonymous namespace
|
|
|
-
|
|
|
-namespace isc {
|
|
|
-namespace asiodns {
|
|
|
-
|
|
|
-// As the test is fairly long and complex, debugging "print" statements have
|
|
|
-// been left in although they are disabled. Set the following to "true" to
|
|
|
-// enable them.
|
|
|
-const bool DEBUG_PRINT = false;
|
|
|
-
|
|
|
-class MockResolver : public isc::resolve::ResolverInterface {
|
|
|
-public:
|
|
|
- virtual void resolve(const QuestionPtr& question,
|
|
|
- const ResolverInterface::CallbackPtr& callback) {
|
|
|
- }
|
|
|
-
|
|
|
- virtual ~MockResolver() {}
|
|
|
-};
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-/// \brief Test fixture for the RecursiveQuery Test
|
|
|
-class RecursiveQueryTest2 : public virtual ::testing::Test
|
|
|
-{
|
|
|
-public:
|
|
|
-
|
|
|
- /// \brief Status of query
|
|
|
- ///
|
|
|
- /// Set before the query and then by each "server" when responding.
|
|
|
- enum QueryStatus {
|
|
|
- NONE = 0, ///< Default
|
|
|
- UDP_ROOT = 1, ///< Query root server over UDP
|
|
|
- UDP_ORG = 2, ///< Query ORG server over UDP
|
|
|
- TCP_ORG = 3, ///< Query ORG server over TCP
|
|
|
- UDP_EXAMPLE_ORG_BAD = 4, ///< Query EXAMPLE.ORG server over UDP
|
|
|
- ///< (return malformed packet)
|
|
|
- UDP_EXAMPLE_ORG = 5, ///< Query EXAMPLE.ORG server over UDP
|
|
|
- COMPLETE = 6 ///< Query is complete
|
|
|
- };
|
|
|
-
|
|
|
- // Common stuff
|
|
|
- bool debug_; ///< Set true for debug print
|
|
|
- IOService service_; ///< Service to run everything
|
|
|
- DNSService dns_service_; ///< Resolver is part of "server"
|
|
|
- QuestionPtr question_; ///< What to ask
|
|
|
- QueryStatus last_; ///< What was the last state
|
|
|
- QueryStatus expected_; ///< Expected next state
|
|
|
- OutputBufferPtr question_buffer_; ///< Question we expect to receive
|
|
|
- boost::shared_ptr<MockResolver> resolver_; ///< Mock resolver
|
|
|
- isc::nsas::NameserverAddressStore* nsas_; ///< Nameserver address store
|
|
|
- isc::cache::ResolverCache cache_; ///< Resolver cache
|
|
|
-
|
|
|
- // Data for TCP Server
|
|
|
- size_t tcp_cumulative_; ///< Cumulative TCP data received
|
|
|
- tcp::endpoint tcp_endpoint_; ///< Endpoint for TCP receives
|
|
|
- size_t tcp_length_; ///< Expected length value
|
|
|
- uint8_t tcp_receive_buffer_[BUFFER_SIZE]; ///< Receive buffer for TCP I/O
|
|
|
- OutputBufferPtr tcp_send_buffer_; ///< Send buffer for TCP I/O
|
|
|
- tcp::socket tcp_socket_; ///< Socket used by TCP server
|
|
|
-
|
|
|
- /// Data for UDP
|
|
|
- udp::endpoint udp_remote_; ///< Endpoint for UDP receives
|
|
|
- size_t udp_length_; ///< Expected length value
|
|
|
- uint8_t udp_receive_buffer_[BUFFER_SIZE]; ///< Receive buffer for UDP I/O
|
|
|
- OutputBufferPtr udp_send_buffer_; ///< Send buffer for UDP I/O
|
|
|
- udp::socket udp_socket_; ///< Socket used by UDP server
|
|
|
-
|
|
|
- /// Some of the tests cause an 'active' running query to be created, but
|
|
|
- /// don't complete the framework that makes that query delete itself.
|
|
|
- /// This member can be used to store it so that it is deleted automatically
|
|
|
- /// when the test is finished.
|
|
|
- AbstractRunningQuery* running_query_;
|
|
|
-
|
|
|
- /// \brief Constructor
|
|
|
- RecursiveQueryTest2() :
|
|
|
- debug_(DEBUG_PRINT),
|
|
|
- service_(),
|
|
|
- dns_service_(service_, NULL, NULL),
|
|
|
- question_(new Question(Name("www.example.org"), RRClass::IN(), RRType::A())),
|
|
|
- last_(NONE),
|
|
|
- expected_(NONE),
|
|
|
- question_buffer_(new OutputBuffer(BUFFER_SIZE)),
|
|
|
- resolver_(new MockResolver()),
|
|
|
- nsas_(new isc::nsas::NameserverAddressStore(resolver_)),
|
|
|
- tcp_cumulative_(0),
|
|
|
- tcp_endpoint_(asio::ip::address::from_string(TEST_ADDRESS), TEST_PORT),
|
|
|
- tcp_length_(0),
|
|
|
- tcp_receive_buffer_(),
|
|
|
- tcp_send_buffer_(new OutputBuffer(BUFFER_SIZE)),
|
|
|
- tcp_socket_(service_.get_io_service()),
|
|
|
- udp_remote_(),
|
|
|
- udp_length_(0),
|
|
|
- udp_receive_buffer_(),
|
|
|
- udp_send_buffer_(new OutputBuffer(BUFFER_SIZE)),
|
|
|
- udp_socket_(service_.get_io_service(), udp::v4()),
|
|
|
- running_query_(NULL)
|
|
|
- {}
|
|
|
-
|
|
|
- ~RecursiveQueryTest2() {
|
|
|
- // It would delete itself, but after the io_service_, which could
|
|
|
- // segfailt in case there were unhandled requests
|
|
|
- resolver_.reset();
|
|
|
- // In a similar note, we wait until the resolver has been cleaned up
|
|
|
- // until deleting and active test running_query_
|
|
|
- delete running_query_;
|
|
|
- delete nsas_;
|
|
|
- }
|
|
|
-
|
|
|
- /// \brief Set Common Message Bits
|
|
|
- ///
|
|
|
- /// Sets up the common bits of a response message returned by the handlers.
|
|
|
- ///
|
|
|
- /// \param msg Message buffer in RENDER mode.
|
|
|
- /// \param qid QID to set the message to
|
|
|
- void setCommonMessage(isc::dns::Message& msg, uint16_t qid = 0) {
|
|
|
- msg.setQid(qid);
|
|
|
- msg.setHeaderFlag(Message::HEADERFLAG_QR);
|
|
|
- msg.setOpcode(Opcode::QUERY());
|
|
|
- msg.setHeaderFlag(Message::HEADERFLAG_AA);
|
|
|
- msg.setRcode(Rcode::NOERROR());
|
|
|
- msg.addQuestion(*question_);
|
|
|
- }
|
|
|
-
|
|
|
- /// \brief Set Referral to "org"
|
|
|
- ///
|
|
|
- /// Sets up the passed-in message (expected to be in "RENDER" mode to
|
|
|
- /// indicate a referral to fictitious .org nameservers.
|
|
|
- ///
|
|
|
- /// \param msg Message to update with referral information.
|
|
|
- void setReferralOrg(isc::dns::Message& msg) {
|
|
|
- if (debug_) {
|
|
|
- cout << "setReferralOrg(): creating referral to .org nameservers" << endl;
|
|
|
- }
|
|
|
-
|
|
|
- // Do a referral to org. We'll define all NS records as "in-zone"
|
|
|
- // nameservers (and supply glue) to avoid the possibility of the
|
|
|
- // resolver starting another recursive query to resolve the address of
|
|
|
- // a nameserver.
|
|
|
- RRsetPtr org_ns(new RRset(Name("org."), RRClass::IN(), RRType::NS(), RRTTL(300)));
|
|
|
- org_ns->addRdata(createRdata(RRType::NS(), RRClass::IN(), "ns1.org."));
|
|
|
- org_ns->addRdata(createRdata(RRType::NS(), RRClass::IN(), "ns2.org."));
|
|
|
- msg.addRRset(Message::SECTION_AUTHORITY, org_ns);
|
|
|
-
|
|
|
- RRsetPtr org_ns1(new RRset(Name("ns1.org."), RRClass::IN(), RRType::A(), RRTTL(300)));
|
|
|
- org_ns1->addRdata(createRdata(RRType::A(), RRClass::IN(), "192.0.2.1"));
|
|
|
- msg.addRRset(Message::SECTION_ADDITIONAL, org_ns1);
|
|
|
-
|
|
|
- RRsetPtr org_ns2(new RRset(Name("ns2.org."), RRClass::IN(), RRType::A(), RRTTL(300)));
|
|
|
- org_ns2->addRdata(createRdata(RRType::A(), RRClass::IN(), "192.0.2.2"));
|
|
|
- msg.addRRset(Message::SECTION_ADDITIONAL, org_ns2);
|
|
|
- }
|
|
|
-
|
|
|
- /// \brief Set Referral to "example.org"
|
|
|
- ///
|
|
|
- /// Sets up the passed-in message (expected to be in "RENDER" mode to
|
|
|
- /// indicate a referral to fictitious example.org nameservers.
|
|
|
- ///
|
|
|
- /// \param msg Message to update with referral information.
|
|
|
- void setReferralExampleOrg(isc::dns::Message& msg) {
|
|
|
- if (debug_) {
|
|
|
- cout << "setReferralExampleOrg(): creating referral to example.org nameservers" << endl;
|
|
|
- }
|
|
|
-
|
|
|
- // Do a referral to example.org. As before, we'll define all NS
|
|
|
- // records as "in-zone" nameservers (and supply glue) to avoid the
|
|
|
- // possibility of the resolver starting another recursive query to look
|
|
|
- // up the address of the nameserver.
|
|
|
- RRsetPtr example_org_ns(new RRset(Name("example.org."), RRClass::IN(), RRType::NS(), RRTTL(300)));
|
|
|
- example_org_ns->addRdata(createRdata(RRType::NS(), RRClass::IN(), "ns1.example.org."));
|
|
|
- example_org_ns->addRdata(createRdata(RRType::NS(), RRClass::IN(), "ns2.example.org."));
|
|
|
- msg.addRRset(Message::SECTION_AUTHORITY, example_org_ns);
|
|
|
-
|
|
|
- RRsetPtr example_org_ns1(new RRset(Name("ns1.example.org."), RRClass::IN(), RRType::A(), RRTTL(300)));
|
|
|
- example_org_ns1->addRdata(createRdata(RRType::A(), RRClass::IN(), "192.0.2.11"));
|
|
|
- msg.addRRset(Message::SECTION_ADDITIONAL, example_org_ns1);
|
|
|
-
|
|
|
- RRsetPtr example_org_ns2(new RRset(Name("ns2.example.org."), RRClass::IN(), RRType::A(), RRTTL(300)));
|
|
|
- example_org_ns2->addRdata(createRdata(RRType::A(), RRClass::IN(), "192.0.2.21"));
|
|
|
- msg.addRRset(Message::SECTION_ADDITIONAL, example_org_ns2);
|
|
|
- }
|
|
|
-
|
|
|
- /// \brief Set Answer to "www.example.org"
|
|
|
- ///
|
|
|
- /// Sets up the passed-in message (expected to be in "RENDER" mode) to
|
|
|
- /// indicate an authoritative answer to www.example.org.
|
|
|
- ///
|
|
|
- /// \param msg Message to update with referral information.
|
|
|
- void setAnswerWwwExampleOrg(isc::dns::Message& msg) {
|
|
|
- if (debug_) {
|
|
|
- cout << "setAnswerWwwExampleOrg(): creating answer for www.example.org" << endl;
|
|
|
- }
|
|
|
-
|
|
|
- // Give a response for www.example.org.
|
|
|
- RRsetPtr www_example_org_a(new RRset(Name("www.example.org."), RRClass::IN(), RRType::A(), RRTTL(300)));
|
|
|
- www_example_org_a->addRdata(createRdata(RRType::A(), RRClass::IN(), WWW_EXAMPLE_ORG));
|
|
|
- msg.addRRset(Message::SECTION_ANSWER, www_example_org_a);
|
|
|
-
|
|
|
- // ... and add the Authority and Additional sections. (These are the
|
|
|
- // same as in the referral to example.org from the .org nameserver.)
|
|
|
- setReferralExampleOrg(msg);
|
|
|
- }
|
|
|
-
|
|
|
- /// \brief UDP Receive Handler
|
|
|
- ///
|
|
|
- /// This is invoked when a message is received over UDP from the
|
|
|
- /// RecursiveQuery object under test. It formats an answer and sends it
|
|
|
- /// asynchronously, with the UdpSendHandler method being specified as the
|
|
|
- /// completion handler.
|
|
|
- ///
|
|
|
- /// \param ec ASIO error code, completion code of asynchronous I/O issued
|
|
|
- /// by the "server" to receive data.
|
|
|
- /// \param length Amount of data received.
|
|
|
- void udpReceiveHandler(asio::error_code ec = asio::error_code(),
|
|
|
- size_t length = 0)
|
|
|
- {
|
|
|
- if (debug_) {
|
|
|
- cout << "udpReceiveHandler(): error = " << ec.value() <<
|
|
|
- ", length = " << length << ", last state = " << last_ <<
|
|
|
- ", expected state = " << expected_ << endl;
|
|
|
- }
|
|
|
-
|
|
|
- // Expected state should be one greater than the last state.
|
|
|
- EXPECT_EQ(static_cast<int>(expected_), static_cast<int>(last_) + 1);
|
|
|
- last_ = expected_;
|
|
|
-
|
|
|
- // The QID in the incoming data is random so set it to 0 for the
|
|
|
- // data comparison check. (It is set to 0 in the buffer containing
|
|
|
- // the expected data.)
|
|
|
- // And check that question we received is what was expected.
|
|
|
- uint16_t qid = checkReceivedPacket(udp_receive_buffer_, length);
|
|
|
-
|
|
|
- // The message returned depends on what state we are in. Set up
|
|
|
- // common stuff first: bits not mentioned are set to 0.
|
|
|
- Message msg(Message::RENDER);
|
|
|
- setCommonMessage(msg, qid);
|
|
|
-
|
|
|
- // In the case of UDP_EXAMPLE_ORG_BAD, we shall mangle the
|
|
|
- // response
|
|
|
- bool mangle_response = false;
|
|
|
-
|
|
|
- // Set up state-dependent bits:
|
|
|
- switch (expected_) {
|
|
|
- case UDP_ROOT:
|
|
|
- // Return a referral to org. We then expect to query the "org"
|
|
|
- // nameservers over UDP next.
|
|
|
- setReferralOrg(msg);
|
|
|
- expected_ = UDP_ORG;
|
|
|
- break;
|
|
|
-
|
|
|
- case UDP_ORG:
|
|
|
- // Return a referral to example.org. We explicitly set the TC bit to
|
|
|
- // force a repeat query to the .org nameservers over TCP.
|
|
|
- setReferralExampleOrg(msg);
|
|
|
- if (debug_) {
|
|
|
- cout << "udpReceiveHandler(): setting TC bit" << endl;
|
|
|
- }
|
|
|
- msg.setHeaderFlag(Message::HEADERFLAG_TC);
|
|
|
- expected_ = TCP_ORG;
|
|
|
- break;
|
|
|
-
|
|
|
- case UDP_EXAMPLE_ORG_BAD:
|
|
|
- // Return the answer to the question.
|
|
|
- setAnswerWwwExampleOrg(msg);
|
|
|
- // Mangle the response to enfore another query
|
|
|
- mangle_response = true;
|
|
|
- expected_ = UDP_EXAMPLE_ORG;
|
|
|
- break;
|
|
|
-
|
|
|
- case UDP_EXAMPLE_ORG:
|
|
|
- // Return the answer to the question.
|
|
|
- setAnswerWwwExampleOrg(msg);
|
|
|
- expected_ = COMPLETE;
|
|
|
- break;
|
|
|
-
|
|
|
- default:
|
|
|
- FAIL() << "UdpReceiveHandler called with unknown state";
|
|
|
- }
|
|
|
-
|
|
|
- // Convert to wire format
|
|
|
- udp_send_buffer_->clear();
|
|
|
- MessageRenderer renderer;
|
|
|
- renderer.setBuffer(udp_send_buffer_.get());
|
|
|
- msg.toWire(renderer);
|
|
|
-
|
|
|
- if (mangle_response) {
|
|
|
- // mangle the packet a bit
|
|
|
- // set additional to one more
|
|
|
- udp_send_buffer_->writeUint8At(3, 11);
|
|
|
- }
|
|
|
-
|
|
|
- // Return a message back to the IOFetch object (after setting the
|
|
|
- // expected length of data for the check in the send handler).
|
|
|
- udp_length_ = udp_send_buffer_->getLength();
|
|
|
- udp_socket_.async_send_to(asio::buffer(udp_send_buffer_->getData(),
|
|
|
- udp_send_buffer_->getLength()),
|
|
|
- udp_remote_,
|
|
|
- boost::bind(&RecursiveQueryTest2::udpSendHandler,
|
|
|
- this, _1, _2));
|
|
|
- }
|
|
|
-
|
|
|
- /// \brief UDP Send Handler
|
|
|
- ///
|
|
|
- /// Called when a send operation of the UDP server (i.e. a response
|
|
|
- /// being sent to the RecursiveQuery) has completed, this re-issues
|
|
|
- /// a read call.
|
|
|
- ///
|
|
|
- /// \param ec Completion error code of the send.
|
|
|
- /// \param length Actual number of bytes sent.
|
|
|
- void udpSendHandler(asio::error_code ec = asio::error_code(),
|
|
|
- size_t length = 0)
|
|
|
- {
|
|
|
- if (debug_) {
|
|
|
- cout << "udpSendHandler(): error = " << ec.value() <<
|
|
|
- ", length = " << length << endl;
|
|
|
- }
|
|
|
-
|
|
|
- // Check send was OK
|
|
|
- EXPECT_EQ(0, ec.value());
|
|
|
- EXPECT_EQ(udp_length_, length);
|
|
|
-
|
|
|
- // Reissue the receive call to await the next message.
|
|
|
- udp_socket_.async_receive_from(
|
|
|
- asio::buffer(udp_receive_buffer_, sizeof(udp_receive_buffer_)),
|
|
|
- udp_remote_,
|
|
|
- boost::bind(&RecursiveQueryTest2::udpReceiveHandler, this, _1, _2));
|
|
|
- }
|
|
|
-
|
|
|
- /// \brief Completion Handler for Accepting TCP Data
|
|
|
- ///
|
|
|
- /// Called when the remote system connects to the "TCP server". It issues
|
|
|
- /// an asynchronous read on the socket to read data.
|
|
|
- ///
|
|
|
- /// \param socket Socket on which data will be received
|
|
|
- /// \param ec Boost error code, value should be zero.
|
|
|
- void tcpAcceptHandler(asio::error_code ec = asio::error_code(),
|
|
|
- size_t length = 0)
|
|
|
- {
|
|
|
- if (debug_) {
|
|
|
- cout << "tcpAcceptHandler(): error = " << ec.value() <<
|
|
|
- ", length = " << length << endl;
|
|
|
- }
|
|
|
-
|
|
|
- // Expect that the accept completed without a problem.
|
|
|
- EXPECT_EQ(0, ec.value());
|
|
|
-
|
|
|
- // Initiate a read on the socket, indicating that nothing has yet been
|
|
|
- // received.
|
|
|
- tcp_cumulative_ = 0;
|
|
|
- tcp_socket_.async_receive(
|
|
|
- asio::buffer(tcp_receive_buffer_, sizeof(tcp_receive_buffer_)),
|
|
|
- boost::bind(&RecursiveQueryTest2::tcpReceiveHandler, this, _1, _2));
|
|
|
- }
|
|
|
-
|
|
|
- /// \brief Completion Handler for Receiving TCP Data
|
|
|
- ///
|
|
|
- /// Reads data from the RecursiveQuery object and loops, reissuing reads,
|
|
|
- /// until all the message has been read. It then returns an appropriate
|
|
|
- /// response.
|
|
|
- ///
|
|
|
- /// \param socket Socket to use to send the answer
|
|
|
- /// \param ec ASIO error code, completion code of asynchronous I/O issued
|
|
|
- /// by the "server" to receive data.
|
|
|
- /// \param length Amount of data received.
|
|
|
- void tcpReceiveHandler(asio::error_code ec = asio::error_code(),
|
|
|
- size_t length = 0)
|
|
|
- {
|
|
|
- if (debug_) {
|
|
|
- cout << "tcpReceiveHandler(): error = " << ec.value() <<
|
|
|
- ", length = " << length <<
|
|
|
- ", cumulative = " << tcp_cumulative_ << endl;
|
|
|
- }
|
|
|
-
|
|
|
- // Expect that the receive completed without a problem.
|
|
|
- EXPECT_EQ(0, ec.value());
|
|
|
-
|
|
|
- // Have we received all the data? We know this by checking if the two-
|
|
|
- // byte length count in the message is equal to the data received.
|
|
|
- tcp_cumulative_ += length;
|
|
|
- bool complete = false;
|
|
|
- if (tcp_cumulative_ > 2) {
|
|
|
- uint16_t dns_length = readUint16(tcp_receive_buffer_,
|
|
|
- sizeof(tcp_receive_buffer_));
|
|
|
- complete = ((dns_length + 2) == tcp_cumulative_);
|
|
|
- }
|
|
|
-
|
|
|
- if (!complete) {
|
|
|
- if (debug_) {
|
|
|
- cout << "tcpReceiveHandler(): read not complete, " <<
|
|
|
- "issuing another read" << endl;
|
|
|
- }
|
|
|
-
|
|
|
- // Not complete yet, issue another read.
|
|
|
- tcp_socket_.async_receive(
|
|
|
- asio::buffer(tcp_receive_buffer_ + tcp_cumulative_,
|
|
|
- sizeof(tcp_receive_buffer_) - tcp_cumulative_),
|
|
|
- boost::bind(&RecursiveQueryTest2::tcpReceiveHandler, this, _1, _2));
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Have received a TCP message. Expected state should be one greater
|
|
|
- // than the last state.
|
|
|
- EXPECT_EQ(static_cast<int>(expected_), static_cast<int>(last_) + 1);
|
|
|
- last_ = expected_;
|
|
|
-
|
|
|
- // Check that question we received is what was expected. Note that we
|
|
|
- // have to ignore the two-byte header in order to parse the message.
|
|
|
- qid_t qid = checkReceivedPacket(tcp_receive_buffer_ + 2, length - 2);
|
|
|
-
|
|
|
- // Return a message back. This is a referral to example.org, which
|
|
|
- // should result in another query over UDP. Note the setting of the
|
|
|
- // QID in the returned message with what was in the received message.
|
|
|
- Message msg(Message::RENDER);
|
|
|
- setCommonMessage(msg, qid);
|
|
|
- setReferralExampleOrg(msg);
|
|
|
-
|
|
|
- // Convert to wire format
|
|
|
- // Use a temporary renderer for the dns wire data (we copy it
|
|
|
- // to the 'real' buffer below)
|
|
|
- MessageRenderer renderer;
|
|
|
- msg.toWire(renderer);
|
|
|
-
|
|
|
- // Expected next state (when checked) is the UDP query to example.org.
|
|
|
- // Also, take this opportunity to clear the accumulated read count in
|
|
|
- // readiness for the next read. (If any - at present, there is only
|
|
|
- // one read in the test, although extensions to this test suite could
|
|
|
- // change that.)
|
|
|
- expected_ = UDP_EXAMPLE_ORG_BAD;
|
|
|
- tcp_cumulative_ = 0;
|
|
|
-
|
|
|
- // Unless we go through a callback loop we cannot simply use
|
|
|
- // async_send() multiple times, so we cannot send the size first
|
|
|
- // followed by the actual data. We copy them to a new buffer
|
|
|
- // first
|
|
|
- tcp_send_buffer_->clear();
|
|
|
- tcp_send_buffer_->writeUint16(renderer.getLength());
|
|
|
- tcp_send_buffer_->writeData(renderer.getData(), renderer.getLength());
|
|
|
- tcp_socket_.async_send(asio::buffer(tcp_send_buffer_->getData(),
|
|
|
- tcp_send_buffer_->getLength()),
|
|
|
- boost::bind(
|
|
|
- &RecursiveQueryTest2::tcpSendHandler, this,
|
|
|
- tcp_send_buffer_->getLength(), _1, _2));
|
|
|
- }
|
|
|
-
|
|
|
- /// \brief Completion Handler for Sending TCP data
|
|
|
- ///
|
|
|
- /// Called when the asynchronous send of data back to the RecursiveQuery
|
|
|
- /// by the TCP "server" in this class has completed. (This send has to
|
|
|
- /// be asynchronous because control needs to return to the caller in order
|
|
|
- /// for the IOService "run()" method to be called to run the handlers.)
|
|
|
- ///
|
|
|
- /// \param expected_length Number of bytes that were expected to have been sent.
|
|
|
- /// \param ec Boost error code, value should be zero.
|
|
|
- /// \param length Number of bytes sent.
|
|
|
- void tcpSendHandler(size_t expected_length = 0,
|
|
|
- asio::error_code ec = asio::error_code(),
|
|
|
- size_t length = 0)
|
|
|
- {
|
|
|
- if (debug_) {
|
|
|
- cout << "tcpSendHandler(): error = " << ec.value() <<
|
|
|
- ", length = " << length <<
|
|
|
- ", (expected length = " << expected_length << ")" << endl;
|
|
|
- }
|
|
|
- EXPECT_EQ(0, ec.value()); // Expect no error
|
|
|
- EXPECT_EQ(expected_length, length); // And that amount sent is as expected
|
|
|
- }
|
|
|
-
|
|
|
- /// \brief Check Received Packet
|
|
|
- ///
|
|
|
- /// Checks the packet received from the RecursiveQuery object to ensure
|
|
|
- /// that the question is what is expected.
|
|
|
- ///
|
|
|
- /// \param data Start of data. This is the start of the received buffer in
|
|
|
- /// the case of UDP data, and an offset into the buffer past the
|
|
|
- /// count field for TCP data.
|
|
|
- /// \param length Length of data.
|
|
|
- /// \return The QID of the message
|
|
|
- qid_t checkReceivedPacket(uint8_t* data, size_t length) {
|
|
|
-
|
|
|
- // Decode the received buffer.
|
|
|
- InputBuffer buffer(data, length);
|
|
|
- Message message(Message::PARSE);
|
|
|
- message.fromWire(buffer);
|
|
|
-
|
|
|
- // Check the packet.
|
|
|
- EXPECT_FALSE(message.getHeaderFlag(Message::HEADERFLAG_QR));
|
|
|
-
|
|
|
- Question question = **(message.beginQuestion());
|
|
|
- EXPECT_TRUE(question == *question_);
|
|
|
-
|
|
|
- return message.getQid();
|
|
|
- }
|
|
|
-};
|
|
|
-
|
|
|
-/// \brief Resolver Callback Object
|
|
|
-///
|
|
|
-/// Holds the success and failure callback methods for the resolver
|
|
|
-class ResolverCallback : public isc::resolve::ResolverInterface::Callback {
|
|
|
-public:
|
|
|
- /// \brief Constructor
|
|
|
- ResolverCallback(IOService& service) :
|
|
|
- service_(service), run_(false), status_(false), debug_(DEBUG_PRINT)
|
|
|
- {}
|
|
|
-
|
|
|
- /// \brief Destructor
|
|
|
- virtual ~ResolverCallback()
|
|
|
- {}
|
|
|
-
|
|
|
- /// \brief Resolver Callback Success
|
|
|
- ///
|
|
|
- /// Called if the resolver detects that the call has succeeded.
|
|
|
- ///
|
|
|
- /// \param response Answer to the question.
|
|
|
- virtual void success(const isc::dns::MessagePtr response) {
|
|
|
- if (debug_) {
|
|
|
- cout << "ResolverCallback::success(): answer received" << endl;
|
|
|
- cout << response->toText() << endl;
|
|
|
- }
|
|
|
-
|
|
|
- // There should be one RR each in the question and answer sections, and
|
|
|
- // two RRs in each of the the authority and additional sections.
|
|
|
- EXPECT_EQ(1, response->getRRCount(Message::SECTION_QUESTION));
|
|
|
- EXPECT_EQ(1, response->getRRCount(Message::SECTION_ANSWER));
|
|
|
- EXPECT_EQ(2, response->getRRCount(Message::SECTION_AUTHORITY));
|
|
|
- EXPECT_EQ(2, response->getRRCount(Message::SECTION_ADDITIONAL));
|
|
|
-
|
|
|
- // Check the answer - that the RRset is there...
|
|
|
- EXPECT_TRUE(response->hasRRset(Message::SECTION_ANSWER,
|
|
|
- RRsetPtr(new RRset(Name("www.example.org."),
|
|
|
- RRClass::IN(),
|
|
|
- RRType::A(),
|
|
|
- RRTTL(300)))));
|
|
|
- const RRsetIterator rrset_i = response->beginSection(Message::SECTION_ANSWER);
|
|
|
-
|
|
|
- // ... get iterator into the Rdata of this RRset and point to first
|
|
|
- // element...
|
|
|
- const RdataIteratorPtr rdata_i = (*rrset_i)->getRdataIterator();
|
|
|
- rdata_i->first();
|
|
|
-
|
|
|
- // ... and check it is what we expect.
|
|
|
- EXPECT_EQ(string(WWW_EXAMPLE_ORG), rdata_i->getCurrent().toText());
|
|
|
-
|
|
|
- // Flag completion
|
|
|
- run_ = true;
|
|
|
- status_ = true;
|
|
|
-
|
|
|
- service_.stop(); // Cause run() to exit.
|
|
|
- }
|
|
|
-
|
|
|
- /// \brief Resolver Failure Completion
|
|
|
- ///
|
|
|
- /// Called if the resolver detects that the resolution has failed.
|
|
|
- virtual void failure() {
|
|
|
- if (debug_) {
|
|
|
- cout << "ResolverCallback::success(): resolution failure" << endl;
|
|
|
- }
|
|
|
- FAIL() << "Resolver reported completion failure";
|
|
|
-
|
|
|
- // Flag completion
|
|
|
- run_ = true;
|
|
|
- status_ = false;
|
|
|
-
|
|
|
- service_.stop(); // Cause run() to exit.
|
|
|
- }
|
|
|
-
|
|
|
- /// \brief Return status of "run" flag
|
|
|
- bool getRun() const {
|
|
|
- return (run_);
|
|
|
- }
|
|
|
-
|
|
|
- /// \brief Return "status" flag
|
|
|
- bool getStatus() const {
|
|
|
- return (status_);
|
|
|
- }
|
|
|
-
|
|
|
-private:
|
|
|
- IOService& service_; ///< Service handling the run queue
|
|
|
- bool run_; ///< Set true when completion handler run
|
|
|
- bool status_; ///< Set true for success, false on error
|
|
|
- bool debug_; ///< Debug flag
|
|
|
-};
|
|
|
-
|
|
|
-// Sets up the UDP and TCP "servers", then tries a resolution.
|
|
|
-
|
|
|
-TEST_F(RecursiveQueryTest2, Resolve) {
|
|
|
- // Set up the UDP server and issue the first read. The endpoint from which
|
|
|
- // the query is sent is put in udp_endpoint_ when the read completes, which
|
|
|
- // is referenced in the callback as the place to which the response is sent.
|
|
|
- udp_socket_.set_option(socket_base::reuse_address(true));
|
|
|
- udp_socket_.bind(udp::endpoint(address::from_string(TEST_ADDRESS), TEST_PORT));
|
|
|
- udp_socket_.async_receive_from(asio::buffer(udp_receive_buffer_,
|
|
|
- sizeof(udp_receive_buffer_)),
|
|
|
- udp_remote_,
|
|
|
- boost::bind(&RecursiveQueryTest2::udpReceiveHandler,
|
|
|
- this, _1, _2));
|
|
|
-
|
|
|
- // Set up the TCP server and issue the accept. Acceptance will cause the
|
|
|
- // read to be issued.
|
|
|
- tcp::acceptor acceptor(service_.get_io_service(),
|
|
|
- tcp::endpoint(tcp::v4(), TEST_PORT));
|
|
|
- acceptor.async_accept(tcp_socket_,
|
|
|
- boost::bind(&RecursiveQueryTest2::tcpAcceptHandler,
|
|
|
- this, _1, 0));
|
|
|
-
|
|
|
- // Set up the RecursiveQuery object. We will also test that it correctly records
|
|
|
- // RTT times by setting up a RTT recorder object as well.
|
|
|
- std::vector<std::pair<std::string, uint16_t> > upstream; // Empty
|
|
|
- std::vector<std::pair<std::string, uint16_t> > upstream_root; // Empty
|
|
|
- RecursiveQuery query(dns_service_, *nsas_, cache_,
|
|
|
- upstream, upstream_root);
|
|
|
- query.setTestServer(TEST_ADDRESS, TEST_PORT);
|
|
|
-
|
|
|
- boost::shared_ptr<RttRecorder> recorder(new RttRecorder());
|
|
|
- query.setRttRecorder(recorder);
|
|
|
-
|
|
|
- // Set up callback to receive notification that the query has completed.
|
|
|
- isc::resolve::ResolverInterface::CallbackPtr
|
|
|
- resolver_callback(new ResolverCallback(service_));
|
|
|
-
|
|
|
- // Kick off the resolution process. We expect the first question to go to
|
|
|
- // "root".
|
|
|
- expected_ = UDP_ROOT;
|
|
|
- running_query_ = query.resolve(question_, resolver_callback);
|
|
|
- service_.run();
|
|
|
-
|
|
|
- // Check what ran. (We have to cast the callback to ResolverCallback as we
|
|
|
- // lost the information on the derived class when we used a
|
|
|
- // ResolverInterface::CallbackPtr to store a pointer to it.)
|
|
|
- ResolverCallback* rc = static_cast<ResolverCallback*>(resolver_callback.get());
|
|
|
- EXPECT_TRUE(rc->getRun());
|
|
|
- EXPECT_TRUE(rc->getStatus());
|
|
|
-
|
|
|
- // Finally, check that all the RTTs were "reasonable" (defined here as
|
|
|
- // being below 2 seconds). This is an explicit check to test that the
|
|
|
- // variables in the RTT calculation are at least being initialized; if they
|
|
|
- // weren't, we would expect some absurdly high answers.
|
|
|
- vector<uint32_t> rtt = recorder->getRtt();
|
|
|
- EXPECT_GT(rtt.size(), 0);
|
|
|
- for (vector<uint32_t>::size_type i = 0; i < rtt.size(); ++i) {
|
|
|
- EXPECT_LT(rtt[i], 2000);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-} // namespace asiodns
|
|
|
-} // namespace isc
|