Commit a5014970 authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[2975] Stub implementation of the DNSClient class.

parent 0fc40f16
......@@ -54,6 +54,7 @@ b10_dhcp_ddns_SOURCES += d_controller.cc d_controller.h
b10_dhcp_ddns_SOURCES += d2_controller.cc d2_controller.h
b10_dhcp_ddns_SOURCES += d2_update_message.cc d2_update_message.h
b10_dhcp_ddns_SOURCES += d2_zone.cc d2_zone.h
b10_dhcp_ddns_SOURCES += dns_client.cc dns_client.h
nodist_b10_dhcp_ddns_SOURCES = d2_messages.h d2_messages.cc
EXTRA_DIST += d2_messages.mes
......@@ -61,6 +62,7 @@ EXTRA_DIST += d2_messages.mes
b10_dhcp_ddns_LDADD = $(top_builddir)/src/lib/log/libb10-log.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
b10_dhcp_ddns_LDADD += $(top_builddir)/src/lib/dns/libb10-dns++.la
......
......@@ -63,6 +63,10 @@ public:
isc::Exception(file, line, what) {}
};
class D2UpdateMessage;
/// @brief Pointer to the DNS Update Message.
typedef boost::shared_ptr<D2UpdateMessage> D2UpdateMessagePtr;
/// @brief The @c D2UpdateMessage encapsulates a DNS Update message.
///
......
// Copyright (C) 2013 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 <d2/dns_client.h>
#include <dns/messagerenderer.h>
namespace isc {
namespace d2 {
namespace {
// OutputBuffer objects are pre-allocated before data is written to them.
// This is a default number of bytes for the buffers we create within
// DNSClient class.
const size_t DEFAULT_BUFFER_SIZE = 128;
}
using namespace isc::util;
using namespace isc::asiolink;
using namespace isc::asiodns;
DNSClient::DNSClient(D2UpdateMessagePtr& response_placeholder,
Callback* callback)
: in_buf_(new OutputBuffer(DEFAULT_BUFFER_SIZE)),
response_(response_placeholder), callback_(callback) {
if (!response_) {
isc_throw(BadValue, "a pointer to an object to encapsulate the DNS"
" server must be provided; found NULL value");
}
}
void
DNSClient::operator()(IOFetch::Result result) {
// @todo Do something useful here. One of the useful things will be to parse
// incoming message if the result is SUCCESS.
// Once we are done with internal business, let's call a callback supplied
// by a caller.
if (callback_ != NULL) {
(*callback_)(result);
}
}
void
DNSClient::doUpdate(IOService& io_service,
const IOAddress& ns_addr,
const uint16_t ns_port,
D2UpdateMessage& update,
const int wait) {
// A renderer is used by the toWire function which creates the on-wire data
// from the DNS Update message. A renderer has its internal buffer where it
// renders data by default. However, this buffer can't be directly accessed.
// Fortunately, the renderer's API accepts user-supplied buffers. So, let's
// create our own buffer and pass it to the renderer so as the message is
// rendered to this buffer. Finally, we pass this buffer to IOFetch.
dns::MessageRenderer renderer;
OutputBufferPtr msg_buf(new OutputBuffer(DEFAULT_BUFFER_SIZE));
renderer.setBuffer(msg_buf.get());
// Render DNS Update message. This may throw a bunch of exceptions if
// invalid message object is given.
update.toWire(renderer);
// IOFetch has all the mechanisms that we need to perform asynchronous
// communication with the DNS server. The last but one argument points to
// this object as a completion callback for the message exchange. As a
// result operator()(IOFetch::Result) will be called.
IOFetch io_fetch(IOFetch::UDP, io_service, msg_buf, ns_addr, ns_port,
in_buf_, this, wait);
// Post the task to the task queue in the IO service. Caller will actually
// run these tasks by executing IOService::run.
io_service.post(io_fetch);
}
} // namespace d2
} // namespace isc
// Copyright (C) 2013 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.
#ifndef DNS_CLIENT_H
#define DNS_CLIENT_H
#include <d2/d2_update_message.h>
#include <asiolink/io_service.h>
#include <util/buffer.h>
#include <asiodns/io_fetch.h>
namespace isc {
namespace d2 {
/// @brief The @c DNSClient class handles communication with the DNS server.
///
/// Communication with the DNS server is asynchronous. Caller must provide a
/// callback, which will be invoked when the response from the DNS server is
/// received, a timeout has occured or IO service has been stopped for any
/// reason. The caller-supplied callback is called by the internal callback
/// operator implemented by @c DNSClient. This callback is responsible for
/// initializing the @c D2UpdateMessage instance which encapsulates the response
/// from the DNS. This initialization does not take place if the response from
/// DNS is not received.
///
/// Caller must supply a pointer to the @c D2UpdateMessage object, which will
/// encapsulate DNS response, through class constructor. An exception will be
/// thrown if the pointer is not initialized by the caller.
///
/// @todo Currently, only the stub implementation is available for this class.
/// The major missing piece is to create @c D2UpdateMessage object which will
/// encapsulate the response from the DNS server.
class DNSClient : public asiodns::IOFetch::Callback {
public:
/// @brief Callback for the @c DNSClient class.
///
/// This is is abstract class which represents the external callback for the
/// @c DNSClient. Caller must implement this class and supply its instance
/// in the @c DNSClient constructor to get callbacks when the DNS Update
/// exchange is complete (@see @c DNSClient).
class Callback {
public:
/// @brief Virtual destructor.
virtual ~Callback() { }
/// @brief Function operator implementing a callback.
///
/// @param result an @c asiodns::IOFetch::Result object representing
/// IO status code.
virtual void operator()(asiodns::IOFetch::Result result) = 0;
};
/// @brief Constructor.
///
/// @param response_placeholder Pointer to an object which will hold a
/// DNS server's response. Caller is responsible for allocating this object.
/// @param callback Pointer to an object implementing @c DNSClient::Callback
/// class. This object will be called when DNS message exchange completes or
/// if an error occurs. NULL value disables callback invocation.
DNSClient(D2UpdateMessagePtr& response_placeholder, Callback* callback);
/// @brief Virtual destructor, does nothing.
virtual ~DNSClient() { }
///
/// @name Copy constructor and assignment operator
///
/// Copy constructor and assignment operator are private because
/// @c DNSClient is a singleton class and its instance should not be copied.
//@{
private:
DNSClient(const DNSClient& source);
DNSClient& operator=(const DNSClient& source);
//@}
public:
/// @brief Function operator, implementing an internal callback.
///
/// This internal callback is called when the DNS update message exchange is
/// complete. It further invokes the external callback provided by a caller.
/// Before external callback is invoked, an object of the @c D2UpdateMessage
/// type, representing a response from the server is set.
///
/// @param result An @c asiodns::IOFetch::Result object representing status
/// code returned by the IO.
virtual void operator()(asiodns::IOFetch::Result result);
/// @brief Start asynchronous DNS Update.
///
/// This function starts asynchronous DNS Update and returns. The DNS Update
/// will be executed by the specified IO service. Once the message exchange
/// with a DNS server is complete, timeout occurs or IO operation is
/// interrupted, the caller-supplied callback function will be invoked.
///
/// An address and port of the DNS server is specified through the function
/// arguments so as the same instance of the @c DNSClient can be used to
/// initiate multiple message exchanges.
///
/// @param io_service IO service to be used to run the message exchange.
/// @param ns_addr DNS server address.
/// @param ns_port DNS server port.
/// @param update A DNS Update message to be sent to the server.
/// @param wait A timeout (in seconds) for the response. If a response is
/// not received within the timeout, exchange is interrupted. A negative
/// value disables timeout.
void doUpdate(asiolink::IOService& io_service,
const asiolink::IOAddress& ns_addr,
const uint16_t ns_port,
D2UpdateMessage& update,
const int wait = -1);
private:
/// A buffer holding server's response in the wire format.
util::OutputBufferPtr in_buf_;
/// A pointer to the caller-supplied object, encapsuating a response
/// from DNS.
D2UpdateMessagePtr response_;
/// A pointer to the external callback.
Callback* callback_;
};
} // namespace d2
} // namespace isc
#endif // DNS_CLIENT_H
......@@ -58,6 +58,7 @@ d2_unittests_SOURCES += ../d2_process.cc ../d2_process.h
d2_unittests_SOURCES += ../d2_controller.cc ../d2_controller.h
d2_unittests_SOURCES += ../d2_update_message.cc ../d2_update_message.h
d2_unittests_SOURCES += ../d2_zone.cc ../d2_zone.h
d2_unittests_SOURCES += ../dns_client.cc ../dns_client.h
d2_unittests_SOURCES += d_test_stubs.cc d_test_stubs.h
d2_unittests_SOURCES += d2_unittests.cc
d2_unittests_SOURCES += d2_process_unittests.cc
......@@ -65,6 +66,7 @@ d2_unittests_SOURCES += d_controller_unittests.cc
d2_unittests_SOURCES += d2_controller_unittests.cc
d2_unittests_SOURCES += d2_update_message_unittests.cc
d2_unittests_SOURCES += d2_zone_unittests.cc
d2_unittests_SOURCES += dns_client_unittests.cc
nodist_d2_unittests_SOURCES = ../d2_messages.h ../d2_messages.cc
d2_unittests_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
......@@ -72,6 +74,7 @@ d2_unittests_LDFLAGS = $(AM_LDFLAGS) $(GTEST_LDFLAGS)
d2_unittests_LDADD = $(GTEST_LDADD)
d2_unittests_LDADD += $(top_builddir)/src/lib/exceptions/libb10-exceptions.la
d2_unittests_LDADD += $(top_builddir)/src/lib/log/libb10-log.la
d2_unittests_LDADD += $(top_builddir)/src/lib/asiodns/libb10-asiodns.la
d2_unittests_LDADD += $(top_builddir)/src/lib/asiolink/libb10-asiolink.la
d2_unittests_LDADD += $(top_builddir)/src/lib/cc/libb10-cc.la
d2_unittests_LDADD += $(top_builddir)/src/lib/config/libb10-cfgclient.la
......
// Copyright (C) 2013 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 <config.h>
#include <d2/dns_client.h>
#include <asiodns/io_fetch.h>
#include <asiodns/logger.h>
#include <dns/rcode.h>
#include <dns/rrclass.h>
#include <boost/scoped_ptr.hpp>
#include <gtest/gtest.h>
using namespace std;
using namespace isc;
using namespace isc::asiolink;
using namespace isc::asiodns;
using namespace isc::d2;
using namespace isc;
using namespace isc::dns;
namespace {
const char* TEST_ADDRESS = "127.0.0.1";
const uint16_t TEST_PORT = 5301;
// @brief Test Fixture class.
//
// This test fixture class implements DNSClient::Callback so as it can be
// installed as a completion callback for tests it implements. This callback
// is called when a DDNS transaction (send and receive) completes. This allows
// for the callback function to direcetly access class members. In particular,
// the callback function can access IOService on which run() was called and
// call stop() on it.
class DNSClientTest : public virtual ::testing::Test, DNSClient::Callback {
public:
IOService service_;
D2UpdateMessagePtr response_;
IOFetch::Result result_;
// @brief Constructor.
//
// This constructor overrides the default logging level of asiodns logger to
// prevent it from emitting debug messages from IOFetch class. Such an error
// message can be emitted if timeout occurs when DNSClient class is
// waiting for a response. Some of the tests are checking DNSClient behavior
// in case when response from the server is not received. Tests output would
// become messy if such errors were logged.
DNSClientTest()
: service_(),
result_(IOFetch::SUCCESS) {
asiodns::logger.setSeverity(log::INFO);
response_.reset(new D2UpdateMessage(D2UpdateMessage::INBOUND));
}
// @brief Destructor.
//
// Sets the asiodns logging level back to DEBUG.
virtual ~DNSClientTest() {
asiodns::logger.setSeverity(log::DEBUG);
};
// @brief Exchange completion calback.
//
// This callback is called when the exchange with the DNS server is
// complete or an error occured. This includes the occurence of a timeout.
//
// @param result An error code returned by an IO.
virtual void operator()(IOFetch::Result result) {
result_ = result;
service_.stop();
}
// This test verifies that when invalid response placeholder object is
// passed to a constructor, constructor throws the appropriate exception.
// It also verifies that the constructor will not throw if the supplied
// callback object is NULL.
void runConstructorTest() {
D2UpdateMessagePtr null_response;
EXPECT_THROW(DNSClient(null_response, this), isc::BadValue);
EXPECT_NO_THROW(DNSClient(response_, NULL));
}
// This test verifies the DNSClient behavior when a server does not respond
// do the DNS Update message. In such case, the callback function is
// expected to be called and the TIME_OUT error code should be returned.
void runSendNoReceiveTest() {
// Create outgoing message. Simply set the required message fields:
// error code and Zone section. This is enough to create on-wire format
// of this message and send it.
D2UpdateMessage message(D2UpdateMessage::OUTBOUND);
ASSERT_NO_THROW(message.setRcode(Rcode(Rcode::NOERROR_CODE)));
ASSERT_NO_THROW(message.setZone(Name("example.com"), RRClass::IN()));
// Use scoped pointer so as we can declare dns_client in the function
// scope.
boost::scoped_ptr<DNSClient> dns_client;
// Constructor may throw if the response placehoder is NULL.
EXPECT_NO_THROW(dns_client.reset(new DNSClient(response_, this)));
IOService io_service;
// Set the response wait time to 0 so as our test is not hanging. This
// should cause instant timeout.
const int timeout = 0;
// The doUpdate() function starts asynchronous message exchange with DNS
// server. When message exchange is done or timeout occurs, the
// completion callback will be triggered. The doUpdate function returns
// immediately.
EXPECT_NO_THROW(dns_client->doUpdate(service_, IOAddress(TEST_ADDRESS),
TEST_PORT, message, timeout));
// This starts the execution of tasks posted to IOService. run() blocks
// until stop() is called in the completion callback function.
service_.run();
// If callback function was called it should have modified the default
// value of result_ with the TIME_OUT error code.
EXPECT_EQ(IOFetch::TIME_OUT, result_);
}
};
TEST_F(DNSClientTest, constructor) {
runConstructorTest();
}
TEST_F(DNSClientTest, timeout) {
runSendNoReceiveTest();
}
} // End of anonymous namespace
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment