// timeout is hit. This will result in test failure.
class DNSClientTest : public virtual D2StatTest, DNSClient::Callback {
public:
+ /// @brief The IOService which handles IO operations.
IOService service_;
+
+ /// @brief The UDP socket.
+ std::unique_ptr<udp::socket> socket_;
+
+ /// @brief The UDP socket endpoint.
+ std::unique_ptr<udp::endpoint> endpoint_;
+
+ /// @brief DNS client response.
D2UpdateMessagePtr response_;
+
+ /// @brief The status of the DNS client update callback.
DNSClient::Status status_;
+
+ /// @brief The receive buffer.
uint8_t receive_buffer_[MAX_SIZE];
+
+ /// @brief The DNS client performing DNS update.
DNSClientPtr dns_client_;
+
+ /// @brief The flag which indicates if the response should be corrupted.
bool corrupt_response_;
+
+ /// @brief The flag which indicates if a response is expected.
bool expect_response_;
+
+ /// @brief The timeout timer.
asiolink::IntervalTimer test_timer_;
+
+ /// @brief The number of received DNS updates.
int received_;
+
+ /// @brief The number of expected DNS updates.
int expected_;
+ /// @brief The flag which indicates if the server should continue
+ /// with receiving DNS updates.
+ bool go_on_;
+
/// @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_(),
- status_(DNSClient::SUCCESS),
- corrupt_response_(false),
- expect_response_(true),
- test_timer_(service_),
- received_(0), expected_(0) {
+ ///
+ /// 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_(), socket_(), endpoint_(),
+ status_(DNSClient::SUCCESS), corrupt_response_(false),
+ expect_response_(true), test_timer_(service_),
+ received_(0), expected_(0), go_on_(false) {
asiodns::logger.setSeverity(isc::log::INFO);
response_.reset();
dns_client_.reset(new DNSClient(response_, this));
}
/// @brief Destructor
- //
- // Sets the asiodns logging level back to DEBUG.
+ ///
+ /// Sets the asiodns logging level back to DEBUG.
virtual ~DNSClientTest() {
asiodns::logger.setSeverity(isc::log::DEBUG);
};
/// @brief Exchange completion callback
- //
- // This callback is called when the exchange with the DNS server is
- // complete or an error occurred. This includes the occurrence of a timeout.
- //
- // @param status A status code returned by DNSClient.
+ ///
+ /// This callback is called when the exchange with the DNS server is
+ /// complete or an error occurred. This includes the occurrence of a timeout.
+ ///
+ /// @param status A status code returned by DNSClient.
virtual void operator()(DNSClient::Status status) {
status_ = status;
- if (!expected_ || (expected_ == ++received_))
- {
+ if (!expected_ || (expected_ == ++received_)) {
service_.stop();
}
}
/// @brief Handler invoked when test timeout is hit
- //
- // This callback stops all running (hanging) tasks on IO service.
+ ///
+ /// This callback stops all running (hanging) tasks on IO service.
void testTimeoutHandler() {
service_.stop();
FAIL() << "Test timeout hit.";
}
/// @brief Handler invoked when test request is received
- //
- // This callback handler is installed when performing async read on a
- // socket to emulate reception of the DNS Update request by a server.
- // As a result, this handler will send an appropriate DNS Update response
- // message back to the address from which the request has come.
- //
- // @param socket A pointer to a socket used to receive a query and send a
- // response.
- // @param remote A pointer to an object which specifies the host (address
- // and port) from which a request has come.
- // @param receive_length A length (in bytes) of the received data.
- // @param corrupt_response A bool value which indicates that the server's
- // response should be invalid (true) or valid (false)
+ ///
+ /// This callback handler is installed when performing async read on a
+ /// socket to emulate reception of the DNS Update request by a server.
+ /// As a result, this handler will send an appropriate DNS Update response
+ /// message back to the address from which the request has come.
+ ///
+ /// @param socket A pointer to a socket used to receive a query and send a
+ /// response.
+ /// @param remote A pointer to an object which specifies the host (address
+ /// and port) from which a request has come.
+ /// @param receive_length A length (in bytes) of the received data.
+ /// @param corrupt_response A bool value which indicates that the server's
+ /// response should be invalid (true) or valid (false)
void udpReceiveHandler(udp::socket* socket, udp::endpoint* remote,
size_t receive_length, const bool corrupt_response) {
// The easiest way to create a response message is to copy the entire
socket->send_to(boost::asio::buffer(response_buf.getData(),
response_buf.getLength()),
*remote);
+
+ if (go_on_) {
+ socket_->async_receive_from(boost::asio::buffer(receive_buffer_,
+ sizeof(receive_buffer_)),
+ *endpoint_,
+ std::bind(&DNSClientTest::udpReceiveHandler,
+ this, socket_.get(),
+ endpoint_.get(), ph::_2,
+ corrupt_response));
+ }
}
/// @brief Request handler for testing clients using TSIG
- //
- // This callback handler is installed when performing async read on a
- // socket to emulate reception of the DNS Update request with TSIG by a
- // server. As a result, this handler will send an appropriate DNS Update
- // response message back to the address from which the request has come.
- //
- // @param socket A pointer to a socket used to receive a query and send a
- // response.
- // @param remote A pointer to an object which specifies the host (address
- // and port) from which a request has come.
- // @param receive_length A length (in bytes) of the received data.
- // @param corrupt_response A bool value which indicates that the server's
- // response should be invalid (true) or valid (false)
- // @param client_key TSIG key the server should use to verify the inbound
- // request. If the pointer is NULL, the server will not attempt to
- // verify the request.
- // @param server_key TSIG key the server should use to sign the outbound
- // request. If the pointer is NULL, the server will not sign the outbound
- // response. If the pointer is not NULL and not the same value as the
- // client_key, the server will use a new context to sign the response then
- // the one used to verify it. This allows us to simulate the server
- // signing with the wrong key.
+ ///
+ /// This callback handler is installed when performing async read on a
+ /// socket to emulate reception of the DNS Update request with TSIG by a
+ /// server. As a result, this handler will send an appropriate DNS Update
+ /// response message back to the address from which the request has come.
+ ///
+ /// @param socket A pointer to a socket used to receive a query and send a
+ /// response.
+ /// @param remote A pointer to an object which specifies the host (address
+ /// and port) from which a request has come.
+ /// @param receive_length A length (in bytes) of the received data.
+ /// @param client_key TSIG key the server should use to verify the inbound
+ /// request. If the pointer is NULL, the server will not attempt to
+ /// verify the request.
+ /// @param server_key TSIG key the server should use to sign the outbound
+ /// request. If the pointer is NULL, the server will not sign the outbound
+ /// response. If the pointer is not NULL and not the same value as the
+ /// client_key, the server will use a new context to sign the response then
+ /// the one used to verify it. This allows us to simulate the server
+ /// signing with the wrong key.
void TSIGReceiveHandler(udp::socket* socket, udp::endpoint* remote,
size_t receive_length,
D2TsigKeyPtr client_key,
response.toWire(renderer, context.get());
// A response message is now ready to send. Send it!
- socket->send_to(boost::asio::buffer(renderer.getData(), renderer.getLength()),
+ socket->send_to(boost::asio::buffer(renderer.getData(),
+ renderer.getLength()),
*remote);
}
- // 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.
+ /// @brief This test verifies that when invalid response placeholder object
+ /// is passed to a constructor which throws the appropriate exception.
+ /// It also verifies that the constructor will not throw if the supplied
+ /// callback object is NULL.
void runConstructorTest() {
EXPECT_NO_THROW(DNSClient(response_, NULL, DNSClient::UDP));
isc::NotImplemented);
}
- // This test verifies that it accepted timeout values belong to the range of
- // <0, DNSClient::getMaxTimeout()>.
+ /// @brief This test verifies that it accepted timeout values belong to the
+ /// range of <0, DNSClient::getMaxTimeout()>.
void runInvalidTimeoutTest() {
expect_response_ = false;
isc::BadValue);
}
- // 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.
+ /// @brief 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() {
// We expect no response from a server.
expect_response_ = false;
// completion callback will be triggered. The doUpdate function returns
// immediately.
EXPECT_NO_THROW(dns_client_->doUpdate(service_, IOAddress(TEST_ADDRESS),
- TEST_PORT, message, timeout));
+ TEST_PORT, message, timeout));
// This starts the execution of tasks posted to IOService. run() blocks
// until stop() is called in the completion callback function.
}
- // This test verifies that DNSClient can send DNS Update and receive a
- // corresponding response from a server.
+ /// @brief This test verifies that DNSClient can send DNS Update and receive
+ /// a corresponding response from a server.
void runSendReceiveTest(const bool corrupt_response,
const bool two_sends) {
+ go_on_ = two_sends;
corrupt_response_ = corrupt_response;
// Create a request DNS Update message.
// responses. The reuse address option is set so as both sockets can
// use the same address. This new socket is bound to the test address
// and port, where requests will be sent.
- udp::socket udp_socket(service_.get_io_service(), boost::asio::ip::udp::v4());
- udp_socket.set_option(socket_base::reuse_address(true));
- udp_socket.bind(udp::endpoint(address::from_string(TEST_ADDRESS),
- TEST_PORT));
+ socket_.reset(new udp::socket(service_.get_io_service(),
+ boost::asio::ip::udp::v4()));
+ socket_->set_option(socket_base::reuse_address(true));
+ socket_->bind(udp::endpoint(address::from_string(TEST_ADDRESS),
+ TEST_PORT));
// Once socket is created, we can post an IO request to receive some
// packet from this socket. This is asynchronous operation and
// nothing is received until another IO request to send a query is
// Callback function will send a response to this address and port.
// The last parameter holds a length of the received request. It is
// required to construct a response.
- udp::endpoint remote;
- udp_socket.async_receive_from(boost::asio::buffer(receive_buffer_,
- sizeof(receive_buffer_)),
- remote,
- std::bind(&DNSClientTest::udpReceiveHandler,
- this, &udp_socket, &remote, ph::_2,
- corrupt_response));
+ endpoint_.reset(new udp::endpoint());
+ socket_->async_receive_from(boost::asio::buffer(receive_buffer_,
+ sizeof(receive_buffer_)),
+ *endpoint_,
+ std::bind(&DNSClientTest::udpReceiveHandler,
+ this, socket_.get(),
+ endpoint_.get(), ph::_2,
+ corrupt_response));
// The socket is now ready to receive the data. Let's post some request
// message then. Set timeout to some reasonable value to make sure that
// "send" and "receive" operations.
service_.run();
- udp_socket.close();
+ socket_->close();
// Since the callback, operator(), calls stop() on the io_service,
// we must reset it in order for subsequent calls to run() or
service_.get_io_service().reset();
}
- // Performs a single request-response exchange with or without TSIG
- //
- // @param client_key TSIG passed to dns_client and also used by the
- // "server" to verify the request.
- // request.
- // @param server_key TSIG key the "server" should use to sign the response.
- // If this is NULL, then client_key is used.
- // @param should_pass indicates if the test should pass.
+ /// @brief Performs a single request-response exchange with or without TSIG.
+ ///
+ /// @param client_key TSIG passed to dns_client and also used by the
+ /// "server" to verify the request.
+ /// @param server_key TSIG key the "server" should use to sign the response.
+ /// If this is NULL, then client_key is used.
+ /// @param should_pass indicates if the test should pass.
void runTSIGTest(D2TsigKeyPtr client_key, D2TsigKeyPtr server_key,
bool should_pass = true) {
// Tell operator() method if we expect an invalid response.
// valid. Constructor should throw exceptions when parameters are invalid.
TEST_F(DNSClientTest, constructor) {
runConstructorTest();
+ StatMap stats_upd = {
+ { "update-sent", 0},
+ { "update-signed", 0},
+ { "update-unsigned", 0},
+ { "update-success", 0},
+ { "update-timeout", 0},
+ { "update-error", 0}
+ };
+ checkStats(stats_upd);
}
// This test verifies that the maximal allowed timeout value is maximal int
// Verify that timeout is reported when no response is received from DNS.
TEST_F(DNSClientTest, timeout) {
runSendNoReceiveTest();
+ StatMap stats_upd = {
+ { "update-sent", 1},
+ { "update-signed", 0},
+ { "update-unsigned", 1},
+ { "update-success", 0},
+ { "update-timeout", 1},
+ { "update-error", 0}
+ };
+ checkStats(stats_upd);
}
// Verify that exception is thrown when invalid (too high) timeout value is
TEST_F(DNSClientTest, sendReceive) {
// false means that server response is not corrupted.
runSendReceiveTest(false, false);
+ StatMap stats_upd = {
+ { "update-sent", 1},
+ { "update-signed", 0},
+ { "update-unsigned", 1},
+ { "update-success", 1},
+ { "update-timeout", 0},
+ { "update-error", 0}
+ };
+ checkStats(stats_upd);
}
// Verify that the DNSClient reports an error when the response is received from
TEST_F(DNSClientTest, sendReceiveCorrupted) {
// true means that server's response is corrupted.
runSendReceiveTest(true, false);
+ StatMap stats_upd = {
+ { "update-sent", 1},
+ { "update-signed", 0},
+ { "update-unsigned", 1},
+ { "update-success", 0},
+ { "update-timeout", 0},
+ { "update-error", 1}
+ };
+ checkStats(stats_upd);
}
// Verify that it is possible to use the same DNSClient instance to
runSendReceiveTest(false, false);
runSendReceiveTest(false, false);
EXPECT_EQ(2, received_);
+ StatMap stats_upd = {
+ { "update-sent", 2},
+ { "update-signed", 0},
+ { "update-unsigned", 2},
+ { "update-success", 2},
+ { "update-timeout", 0},
+ { "update-error", 0}
+ };
+ checkStats(stats_upd);
}
// Verify that it is possible to use the DNSClient instance to perform the
// 2. send
// 3. receive
// 4. receive
-// @todo THIS Test does not function. The method runSendReceive only
-// schedules one "server" receive. In other words only one request is
-// listened for and then received. Once it is received, the operator()
-// method calls stop() on the io_service, which causes the second receive
-// to be cancelled. It is also unclear, what the asio layer does with a
-// second receive on the same socket.
-TEST_F(DNSClientTest, DISABLED_concurrentSendReceive) {
+TEST_F(DNSClientTest, concurrentSendReceive) {
runSendReceiveTest(false, true);
+ StatMap stats_upd = {
+ { "update-sent", 2},
+ { "update-signed", 0},
+ { "update-unsigned", 2},
+ { "update-success", 2},
+ { "update-timeout", 0},
+ { "update-error", 0}
+ };
+ checkStats(stats_upd);
}
} // End of anonymous namespace