dns_server_unittest.cc 29.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// 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 <config.h>
#include <gtest/gtest.h>

#include <asio.hpp>
#include <asiolink/io_endpoint.h>
#include <asiolink/io_error.h>
21
#include <asiodns/udp_server.h>
22
#include <asiodns/sync_udp_server.h>
23 24 25
#include <asiodns/tcp_server.h>
#include <asiodns/dns_answer.h>
#include <asiodns/dns_lookup.h>
26
#include <string>
27 28
#include <cstring>
#include <cerrno>
29 30
#include <csignal>
#include <unistd.h> //for alarm
31 32 33 34 35

#include <boost/shared_ptr.hpp>
#include <boost/bind.hpp>
#include <boost/function.hpp>

36 37
#include <sys/types.h>
#include <sys/socket.h>
38 39 40 41 42 43 44 45

/// The following tests focus on stop interface for udp and
/// tcp server, there are lots of things can be shared to test
/// both tcp and udp server, so they are in the same unittest

/// The general work flow for dns server, is that wait for user
/// query, once get one query, we will check the data is valid or
/// not, if it passed, we will try to loop up the question, then
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
46
/// compose the answer and finally send it back to user. The server
47 48 49 50 51 52 53
/// may be stopped at any point during this porcess, so the test strategy
/// is that we define 5 stop point and stop the server at these
/// 5 points, to check whether stop is successful
/// The 5 test points are :
///   Before the server start to run
///   After we get the query and check whether it's valid
///   After we lookup the query
54 55
///   After we compose the answer
///   After user gets the final result.
56 57 58 59 60

/// The standard about whether we stop the server successfully or not
/// is based on the fact that if the server is still running, the io
/// service won't quit since it will wait for some asynchronized event for
/// server. So if the io service block function run returns we assume
61
/// that the server is stopped. To avoid stop interface failure which
62 63
/// will block followed tests, using alarm signal to stop the blocking
/// io service
64 65 66 67 68 69 70
///
/// The whole test context including one server and one client, and
/// five stop checkpoints, we call them ServerStopper exclude the first
/// stop point. Once the unittest fired, the client will send message
/// to server, and the stopper may stop the server at the checkpoint, then
/// we check the client get feedback or not. Since there is no DNS logic
/// involved so the message sending between client and server is plain text
71
/// And the valid checker, question lookup and answer composition are dummy.
72

73 74
using namespace isc::asiolink;
using namespace isc::asiodns;
75
using namespace asio;
76

77
namespace {
78
const char* const server_ip = "::1";
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
79
const int server_port = 5553;
80
const char* const server_port_str = "5553";
81 82
//message client send to udp server, which isn't dns package
//just for simple testing
83
const char* const query_message = "Kea is awesome";
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
84 85

// \brief provide capacity to derived class the ability
86
// to stop DNSServer at certain point
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
87 88
class ServerStopper {
    public:
89
        ServerStopper() : server_to_stop_(NULL) {}
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
90
        virtual ~ServerStopper(){}
91

92 93
        void setServerToStop(DNSServer& server) {
            server_to_stop_ = &server;
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
94
        }
95

96 97
        void stopServer() const {
            if (server_to_stop_) {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
98
                server_to_stop_->stop();
99
            }
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
100
        }
101

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
102 103 104
    private:
        DNSServer* server_to_stop_;
};
105

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
106
// \brief no lookup logic at all,just provide a checkpoint to stop the server
hanfeng's avatar
hanfeng committed
107
class DummyLookup : public DNSLookup, public ServerStopper {
108 109 110 111
public:
    DummyLookup() :
        allow_resume_(true)
    { }
112
    virtual void operator()(const IOMessage& io_message,
113 114 115 116 117 118
            isc::dns::MessagePtr message,
            isc::dns::MessagePtr answer_message,
            isc::util::OutputBufferPtr buffer,
            DNSServer* server) const {
        stopServer();
        if (allow_resume_) {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
119 120
            server->resume(true);
        }
121 122 123
    }
    // If you want it not to call resume, set this to false
    bool allow_resume_;
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
124 125 126 127 128 129 130 131 132
};

// \brief copy the data received from user to the answer part
//  provide checkpoint to stop server
class SimpleAnswer : public DNSAnswer, public ServerStopper {
    public:
        void operator()(const IOMessage& message,
                isc::dns::MessagePtr query_message,
                isc::dns::MessagePtr answer_message,
133
                isc::util::OutputBufferPtr buffer) const
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
134 135 136
        {
            //copy what we get from user
            buffer->writeData(message.getData(), message.getDataSize());
137
            stopServer();
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
138
        }
139

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
140
};
141

142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
/// \brief Mixture of DummyLookup and SimpleAnswer: build the answer in the
/// lookup callback.  Used with SyncUDPServer.
class SyncDummyLookup : public DummyLookup {
public:
    virtual void operator()(const IOMessage& io_message,
                            isc::dns::MessagePtr message,
                            isc::dns::MessagePtr answer_message,
                            isc::util::OutputBufferPtr buffer,
                            DNSServer* server) const
    {
        buffer->writeData(io_message.getData(), io_message.getDataSize());
        stopServer();
        if (allow_resume_) {
            server->resume(true);
        }
    }
};

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
160
// \brief simple client, send one string to server and wait for response
161
//  in case, server stopped and client can't get response, there is a timer wait
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
162 163
//  for specified seconds (the value is just a estimate since server process logic is quite
//  simple, and all the intercommunication is local) then cancel the waiting.
hanfeng's avatar
hanfeng committed
164
class SimpleClient : public ServerStopper {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
165 166 167
    public:
    static const size_t MAX_DATA_LEN = 256;
    SimpleClient(asio::io_service& service,
168
                 unsigned int wait_server_time_out)
169
    {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
170 171
        wait_for_response_timer_.reset(new deadline_timer(service));
        received_data_ = new char[MAX_DATA_LEN];
Jelte Jansen's avatar
Jelte Jansen committed
172
        received_data_len_ = 0;
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
173 174
        wait_server_time_out_ = wait_server_time_out;
    }
175

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
176 177 178
    virtual ~SimpleClient() {
        delete [] received_data_;
    }
179

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
180 181 182
    void setGetFeedbackCallback(boost::function<void()>& func) {
        get_response_call_back_ = func;
    }
183

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
184 185 186 187 188 189 190 191 192 193 194 195
    virtual void sendDataThenWaitForFeedback(const std::string& data)  = 0;
    virtual std::string getReceivedData() const = 0;

    void startTimer() {
        wait_for_response_timer_->cancel();
        wait_for_response_timer_->
            expires_from_now(boost::posix_time::
                             seconds(wait_server_time_out_));
        wait_for_response_timer_->
            async_wait(boost::bind(&SimpleClient::stopWaitingforResponse,
                                   this));
    }
196

197 198
    void cancelTimer() { wait_for_response_timer_->cancel(); }

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
199 200 201
    void getResponseCallBack(const asio::error_code& error, size_t
                             received_bytes)
    {
202
        cancelTimer();
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
203 204 205 206
        if (!error)
            received_data_len_ = received_bytes;
        if (!get_response_call_back_.empty()) {
            get_response_call_back_();
207
        }
208
        stopServer();
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
209
    }
210 211


Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
212 213
    protected:
    virtual void stopWaitingforResponse() = 0;
214

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
215 216 217 218 219 220
    boost::shared_ptr<deadline_timer> wait_for_response_timer_;
    char* received_data_;
    size_t received_data_len_;
    boost::function<void()> get_response_call_back_;
    unsigned int wait_server_time_out_;
};
221 222 223



hanfeng's avatar
hanfeng committed
224
class UDPClient : public SimpleClient {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
225
    public:
226
    //After 1 second without feedback client will stop wait
227
    static const unsigned int SERVER_TIME_OUT = 1;
228

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
229
    UDPClient(asio::io_service& service, const ip::udp::endpoint& server) :
230
        SimpleClient(service, SERVER_TIME_OUT)
231
    {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
232 233
        server_ = server;
        socket_.reset(new ip::udp::socket(service));
234
        socket_->open(ip::udp::v6());
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
235
    }
236 237


Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
238 239 240 241 242 243 244 245 246 247
    void sendDataThenWaitForFeedback(const std::string& data) {
        received_data_len_ = 0;
        socket_->send_to(buffer(data.c_str(), data.size() + 1), server_);
        socket_->async_receive_from(buffer(received_data_, MAX_DATA_LEN),
                                    received_from_,
                                    boost::bind(&SimpleClient::
                                                getResponseCallBack, this, _1,
                                                _2));
        startTimer();
    }
248

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
249
    virtual std::string getReceivedData() const {
250 251
        return (received_data_len_ == 0 ? std::string("") :
                                std::string(received_data_));
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
252
    }
253

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
254 255 256 257
    private:
    void stopWaitingforResponse() {
        socket_->close();
    }
258

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
259 260 261 262
    boost::shared_ptr<ip::udp::socket> socket_;
    ip::udp::endpoint server_;
    ip::udp::endpoint received_from_;
};
263 264


hanfeng's avatar
hanfeng committed
265
class TCPClient : public SimpleClient {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
266
    public:
267 268
    // after 2 seconds without feedback client will stop wait,
    // this includes connect, send message and recevice message
269
    static const unsigned int SERVER_TIME_OUT = 2;
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
270
    TCPClient(asio::io_service& service, const ip::tcp::endpoint& server)
271
        : SimpleClient(service, SERVER_TIME_OUT),
272
          send_data_delay_(0), send_data_len_delay_(0)
273
    {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
274 275
        server_ = server;
        socket_.reset(new ip::tcp::socket(service));
276
        socket_->open(ip::tcp::v6());
277
        send_delay_timer_.reset(new deadline_timer(service));
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
278
    }
279 280


Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
281 282 283 284 285 286 287 288
    virtual void sendDataThenWaitForFeedback(const std::string &data) {
        received_data_len_ = 0;
        data_to_send_ = data;
        data_to_send_len_ = data.size() + 1;
        socket_->async_connect(server_, boost::bind(&TCPClient::connectHandler,
                                                    this, _1));
        startTimer();
    }
289

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
290
    virtual std::string getReceivedData() const {
291 292
        return (received_data_len_ == 0 ? std::string("") :
                                std::string(received_data_ + 2));
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
293
    }
294

295 296 297 298
    /// Set the delay before the data len is sent (in seconds)
    /// If this is non-zero, the actual data is never sent
    /// (it is used to test timeout, in which case the connection
    /// should have been closed by the other side anyway)
299 300
    void setSendDataLenDelay(size_t send_data_len_delay) {
        send_data_len_delay_ = send_data_len_delay;
301 302
    }

303 304
    /// Set the delay before the packet data itself is sent
    /// (in seconds)
305 306
    void setSendDataDelay(size_t send_data_delay) {
        send_data_delay_ = send_data_delay;
307 308
    }

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
309 310 311 312
    private:
    void stopWaitingforResponse() {
        socket_->close();
    }
313

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
314
    void connectHandler(const asio::error_code& error) {
315
        if (!error) {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
316
            data_to_send_len_ = htons(data_to_send_len_);
317
            sleep(send_data_len_delay_);
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
318 319 320
            socket_->async_send(buffer(&data_to_send_len_, 2),
                                boost::bind(&TCPClient::sendMessageBodyHandler,
                                            this, _1, _2));
321
        }
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
322
    }
323

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
324 325 326
    void sendMessageBodyHandler(const asio::error_code& error,
                                size_t send_bytes)
    {
327
        if (!error && send_bytes == 2 && send_data_len_delay_ == 0) {
328 329 330 331 332 333 334 335 336 337 338 339
            // We cannot block here (such as by sleep(3)) since otherwise
            // the ASIO events may not reliably handled in the server side
            // as the test expects.  So we use async_wait, and make sure the
            // control will be given back to the IO service.
            if (send_data_delay_ > 0) {
                send_delay_timer_->expires_from_now(boost::posix_time::
                                                    seconds(send_data_delay_));
                send_delay_timer_->async_wait(
                    boost::bind(&TCPClient::sendMessageData, this));
                return;
            }
            sendMessageData();
340
        }
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
341
    }
342

343 344 345 346 347 348 349
    void sendMessageData() {
        socket_->async_send(buffer(data_to_send_.c_str(),
                                   data_to_send_.size() + 1),
                            boost::bind(&TCPClient::finishSendHandler, this,
                                        _1, _2));
    }

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
350
    void finishSendHandler(const asio::error_code& error, size_t send_bytes) {
351 352 353
        if (error) {
            getResponseCallBack(error, 0);
        } else if (send_bytes == data_to_send_.size() + 1) {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
354 355 356
            socket_->async_receive(buffer(received_data_, MAX_DATA_LEN),
                   boost::bind(&SimpleClient::getResponseCallBack, this, _1,
                               _2));
357
        }
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
358
    }
359

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
360 361 362 363
    boost::shared_ptr<ip::tcp::socket> socket_;
    ip::tcp::endpoint server_;
    std::string data_to_send_;
    uint16_t data_to_send_len_;
364
    boost::shared_ptr<deadline_timer> send_delay_timer_;
365 366 367

    size_t send_data_delay_;
    size_t send_data_len_delay_;
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
368 369
};

370 371 372
// \brief provide the context which including two clients and
// two servers, UDP client will only communicate with UDP server, same for TCP
// client
373
//
374
// This is only the active part of the test. We run the test case four times, once
375
// for each type of initialization (once when giving it the address and port,
376 377
// once when giving the file descriptor) multiplied by once for each type of UDP
// server (UDPServer and SyncUDPServer), to ensure it works exactly the same.
378
template<class UDPServerClass>
379
class DNSServerTestBase : public::testing::Test {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
380
    protected:
381
        DNSServerTestBase() :
382 383
            server_address_(ip::address::from_string(server_ip)),
            lookup_(new DummyLookup()),
384
            sync_lookup_(new SyncDummyLookup()),
385 386 387 388 389 390
            answer_(new SimpleAnswer()),
            udp_client_(new UDPClient(service,
                                      ip::udp::endpoint(server_address_,
                                                         server_port))),
            tcp_client_(new TCPClient(service,
                                      ip::tcp::endpoint(server_address_,
391
                                                        server_port)))
392 393
        {
            current_service = &service;
394
        }
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
395

396
        ~ DNSServerTestBase() {
397
            if (udp_server_) {
398 399
                udp_server_->stop();
            }
400
            if (tcp_server_) {
401 402
                tcp_server_->stop();
            }
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
403
            delete lookup_;
404
            delete sync_lookup_;
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
405 406 407
            delete answer_;
            delete udp_client_;
            delete tcp_client_;
408 409 410
            // No delete here. The service is not allocated by new, but as our
            // member. This only references it, so just cleaning the pointer.
            current_service = NULL;
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
411
        }
412

413 414
        void testStopServerByStopper(DNSServer& server, SimpleClient* client,
                                     ServerStopper* stopper)
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
415
        {
416
            static const unsigned int IO_SERVICE_TIME_OUT = 5;
417 418
            io_service_is_time_out = false;
            stopper->setServerToStop(server);
419
            server();
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
420
            client->sendDataThenWaitForFeedback(query_message);
421 422 423
            // Since thread hasn't been introduced into the tool box, using
            // signal to make sure run function will eventually return even
            // server stop failed
424 425
            void (*prev_handler)(int) =
                std::signal(SIGALRM, DNSServerTestBase::stopIOService);
426
            current_service = &service;
427
            alarm(IO_SERVICE_TIME_OUT);
428
            service.run();
hanfeng's avatar
hanfeng committed
429
            service.reset();
430 431
            //cancel scheduled alarm
            alarm(0);
432
            std::signal(SIGALRM, prev_handler);
433 434
        }

435
        static void stopIOService(int _no_use_parameter) {
436
            io_service_is_time_out = true;
437 438 439
            if (current_service != NULL) {
                current_service->stop();
            }
440 441 442 443
        }

        bool serverStopSucceed() const {
            return (!io_service_is_time_out);
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
444
        }
445

446
        asio::io_service service;
447
        const ip::address server_address_;
448 449
        DummyLookup* lookup_;     // we need to replace it in some cases
        SyncDummyLookup*  const sync_lookup_;
450 451 452
        SimpleAnswer* const answer_;
        UDPClient*    const udp_client_;
        TCPClient*    const tcp_client_;
453 454
        boost::shared_ptr<UDPServerClass> udp_server_;
        boost::shared_ptr<TCPServer> tcp_server_;
455 456 457

        // To access them in signal handle function, the following
        // variables have to be static.
458
        static asio::io_service* current_service;
459
        static bool io_service_is_time_out;
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
460 461
};

462
// Initialization (by the file descriptor)
463 464
template<class UDPServerClass>
class FdInit : public DNSServerTestBase<UDPServerClass> {
465 466 467 468 469 470
private:
    // Opens the file descriptor for us
    // It uses the low-level C api, as it seems to be the easiest way to get
    // a raw file descriptor. It also is what the socket creator does and this
    // API is aimed to it.
    int getFd(int type) {
471 472 473 474 475 476 477 478 479 480 481 482
        struct addrinfo hints;
        memset(&hints, 0, sizeof(hints));
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = type;
        hints.ai_protocol = (type == SOCK_STREAM) ? IPPROTO_TCP : IPPROTO_UDP;
        hints.ai_flags = AI_NUMERICSERV | AI_NUMERICHOST;

        struct addrinfo* res;
        const int error = getaddrinfo(server_ip, server_port_str,
                                      &hints, &res);
        if (error != 0) {
            isc_throw(IOError, "getaddrinfo failed: " << gai_strerror(error));
483
        }
484 485

        int sock;
486
        const int on(1);
487 488 489 490
        // Go as far as you can and stop on failure
        // Create the socket
        // set the options
        // and bind it
491 492 493 494 495
        const bool failed((sock = socket(res->ai_family, res->ai_socktype,
                                         res->ai_protocol)) == -1 ||
                          setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on,
                                     sizeof(on)) == -1 ||
                          bind(sock, res->ai_addr, res->ai_addrlen) == -1);
496 497 498 499 500 501
        // No matter if it succeeded or not, free the address info
        freeaddrinfo(res);
        if (failed) {
            if (sock != -1) {
                close(sock);
            }
502
            return (-1);
503 504
        } else {
            return (sock);
505 506 507
        }
    }
protected:
508
    // Using SetUp here so we can ASSERT_*
509
    void SetUp() {
510 511 512 513 514
        const int fd_udp(getFd(SOCK_DGRAM));
        ASSERT_NE(-1, fd_udp) << strerror(errno);
        this->udp_server_ = createServer(fd_udp, AF_INET6);
        const int fd_tcp(getFd(SOCK_STREAM));
        ASSERT_NE(-1, fd_tcp) << strerror(errno);
515 516 517
        this->tcp_server_ =
            boost::shared_ptr<TCPServer>(new TCPServer(
                                             this->service, fd_tcp, AF_INET6,
518
                                             this->lookup_,
519
                                             this->answer_));
520
    }
521 522 523

    // A helper factory of the tested UDP server class: allow customization
    // by template specialization.
524 525 526
    boost::shared_ptr<UDPServerClass> createServer(int fd, int af) {
        return (boost::shared_ptr<UDPServerClass>(
                    new UDPServerClass(this->service, fd, af,
527
                                       this->lookup_,
528
                                       this->answer_)));
529
    }
530 531
};

532 533
// Specialization for SyncUDPServer.  It needs to use SyncDummyLookup.
template<>
534
boost::shared_ptr<SyncUDPServer>
535 536 537
FdInit<SyncUDPServer>::createServer(int fd, int af) {
    delete this->lookup_;
    this->lookup_ = new SyncDummyLookup;
538
    return (SyncUDPServer::create(this->service, fd, af, this->lookup_));
539 540
}

541 542 543 544
// This makes it the template as gtest wants it.
template<class Parent>
class DNSServerTest : public Parent { };

545
typedef ::testing::Types<FdInit<UDPServer>, FdInit<SyncUDPServer> >
546
    ServerTypes;
547 548
TYPED_TEST_CASE(DNSServerTest, ServerTypes);

549 550 551 552 553
// Some tests work only for SyncUDPServer, some others work only for
// (non Sync)UDPServer.  We specialize these tests.
typedef FdInit<UDPServer> AsyncServerTest;
typedef FdInit<SyncUDPServer> SyncServerTest;

554
typedef ::testing::Types<UDPServer, SyncUDPServer> UDPServerTypes;
555 556 557 558 559 560
TYPED_TEST_CASE(DNSServerTestBase, UDPServerTypes);

template<class UDPServerClass>
bool DNSServerTestBase<UDPServerClass>::io_service_is_time_out = false;
template<class UDPServerClass>
asio::io_service* DNSServerTestBase<UDPServerClass>::current_service(NULL);
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
561 562 563

// Test whether server stopped successfully after client get response
// client will send query and start to wait for response, once client
564
// get response, UDP server will be stopped, the io service won't quit
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
565
// if udp server doesn't stop successfully.
566
TYPED_TEST(DNSServerTest, stopUDPServerAfterOneQuery) {
567
    this->testStopServerByStopper(*this->udp_server_, this->udp_client_,
568 569 570
                                  this->udp_client_);
    EXPECT_EQ(query_message, this->udp_client_->getReceivedData());
    EXPECT_TRUE(this->serverStopSucceed());
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
571
}
572

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
573
// Test whether udp server stopped successfully before server start to serve
574 575
TYPED_TEST(DNSServerTest, stopUDPServerBeforeItStartServing) {
    this->udp_server_->stop();
576
    this->testStopServerByStopper(*this->udp_server_, this->udp_client_,
577
                                  this->udp_client_);
578 579
    EXPECT_EQ(std::string(""), this->udp_client_->getReceivedData());
    EXPECT_TRUE(this->serverStopSucceed());
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
580
}
581

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
582
// Test whether udp server stopped successfully during query lookup
583
TYPED_TEST(DNSServerTest, stopUDPServerDuringQueryLookup) {
584
    this->testStopServerByStopper(*this->udp_server_, this->udp_client_,
585 586 587
                                  this->lookup_);
    EXPECT_EQ(std::string(""), this->udp_client_->getReceivedData());
    EXPECT_TRUE(this->serverStopSucceed());
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
588
}
589

590 591 592 593
// Test whether UDP server stopped successfully during composing answer.
// Only works for (non-sync) server because SyncUDPServer doesn't use answer
// callback.
TEST_F(AsyncServerTest, stopUDPServerDuringPrepareAnswer) {
594
    testStopServerByStopper(*udp_server_, udp_client_, answer_);
595 596
    EXPECT_EQ(std::string(""), udp_client_->getReceivedData());
    EXPECT_TRUE(serverStopSucceed());
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
597
}
598

599 600
void
stopServerManyTimes(DNSServer *server, unsigned int times) {
601
    for (unsigned int i = 0; i < times; ++i) {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
602
        server->stop();
603
    }
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
604
}
605

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
606 607
// Test whether udp server stop interface can be invoked several times without
// throw any exception
608
TYPED_TEST(DNSServerTest, stopUDPServerMoreThanOnce) {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
609 610
    ASSERT_NO_THROW({
        boost::function<void()> stop_server_3_times
611
            = boost::bind(stopServerManyTimes, this->udp_server_.get(), 3);
612
        this->udp_client_->setGetFeedbackCallback(stop_server_3_times);
613
        this->testStopServerByStopper(*this->udp_server_,
614 615
                                      this->udp_client_, this->udp_client_);
        EXPECT_EQ(query_message, this->udp_client_->getReceivedData());
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
616
    });
617
    EXPECT_TRUE(this->serverStopSucceed());
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
618
}
619

620
TYPED_TEST(DNSServerTest, stopTCPServerAfterOneQuery) {
621
    this->testStopServerByStopper(*this->tcp_server_, this->tcp_client_,
622 623 624
                                  this->tcp_client_);
    EXPECT_EQ(query_message, this->tcp_client_->getReceivedData());
    EXPECT_TRUE(this->serverStopSucceed());
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
625
}
626

627
TYPED_TEST(DNSServerTest, TCPTimeoutOnLen) {
628 629
    this->tcp_server_->setTCPRecvTimeout(100);
    this->tcp_client_->setSendDataLenDelay(2);
630
    this->testStopServerByStopper(*this->tcp_server_, this->tcp_client_,
631 632 633 634 635 636
                                  this->tcp_client_);
    EXPECT_EQ("", this->tcp_client_->getReceivedData());
    EXPECT_FALSE(this->serverStopSucceed());
}

TYPED_TEST(DNSServerTest, TCPTimeout) {
637 638 639
    // set delay higher than timeout
    this->tcp_server_->setTCPRecvTimeout(100);
    this->tcp_client_->setSendDataDelay(2);
640
    this->testStopServerByStopper(*this->tcp_server_, this->tcp_client_,
641 642
                                  this->tcp_client_);
    EXPECT_EQ("", this->tcp_client_->getReceivedData());
643 644 645 646 647 648 649
    EXPECT_TRUE(this->serverStopSucceed());
}

TYPED_TEST(DNSServerTest, TCPNoTimeout) {
    // set delay lower than timeout
    this->tcp_server_->setTCPRecvTimeout(3000);
    this->tcp_client_->setSendDataDelay(1);
650
    this->testStopServerByStopper(*this->tcp_server_, this->tcp_client_,
651
                                  this->tcp_client_);
652
    EXPECT_EQ("Kea is awesome", this->tcp_client_->getReceivedData());
653
    EXPECT_TRUE(this->serverStopSucceed());
654
}
655

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
656
// Test whether tcp server stopped successfully before server start to serve
657 658
TYPED_TEST(DNSServerTest, stopTCPServerBeforeItStartServing) {
    this->tcp_server_->stop();
659
    this->testStopServerByStopper(*this->tcp_server_, this->tcp_client_,
660 661 662
                                  this->tcp_client_);
    EXPECT_EQ(std::string(""), this->tcp_client_->getReceivedData());
    EXPECT_TRUE(this->serverStopSucceed());
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
663
}
664 665


Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
666
// Test whether tcp server stopped successfully during query lookup
667
TYPED_TEST(DNSServerTest, stopTCPServerDuringQueryLookup) {
668
    this->testStopServerByStopper(*this->tcp_server_, this->tcp_client_,
669
                                  this->lookup_);
670 671
    EXPECT_EQ(std::string(""), this->tcp_client_->getReceivedData());
    EXPECT_TRUE(this->serverStopSucceed());
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
672
}
673

Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
674
// Test whether tcp server stopped successfully during composing answer
675
TYPED_TEST(DNSServerTest, stopTCPServerDuringPrepareAnswer) {
676
    this->testStopServerByStopper(*this->tcp_server_, this->tcp_client_,
677
                                  this->answer_);
678 679
    EXPECT_EQ(std::string(""), this->tcp_client_->getReceivedData());
    EXPECT_TRUE(this->serverStopSucceed());
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
680
}
681 682


Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
683 684
// Test whether tcp server stop interface can be invoked several times without
// throw any exception
685
TYPED_TEST(DNSServerTest, stopTCPServeMoreThanOnce) {
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
686 687
    ASSERT_NO_THROW({
        boost::function<void()> stop_server_3_times
688
            = boost::bind(stopServerManyTimes, this->tcp_server_.get(), 3);
689
        this->tcp_client_->setGetFeedbackCallback(stop_server_3_times);
690
        this->testStopServerByStopper(*this->tcp_server_, this->tcp_client_,
691 692
                                      this->tcp_client_);
        EXPECT_EQ(query_message, this->tcp_client_->getReceivedData());
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
693
    });
694
    EXPECT_TRUE(this->serverStopSucceed());
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
695
}
696

697
// It raises an exception when invalid address family is passed
698 699
// The parameter here doesn't mean anything
TYPED_TEST(DNSServerTestBase, invalidFamily) {
700 701
    // We abuse DNSServerTestBase for this test, as we don't need the
    // initialization.
702
    EXPECT_THROW(TCPServer(this->service, 0, AF_UNIX,
703 704
                           this->lookup_, this->answer_),
                 isc::InvalidParameter);
705 706
}

707 708 709 710
TYPED_TEST(DNSServerTest, invalidFamilyUDP) {
    EXPECT_THROW(this->createServer(0, AF_UNIX), isc::InvalidParameter);
}

711
// It raises an exception when invalid address family is passed
712
TYPED_TEST(DNSServerTestBase, invalidTCPFD) {
713 714
    // We abuse DNSServerTestBase for this test, as we don't need the
    // initialization.
715 716 717 718 719 720
    /*
     FIXME: The UDP server doesn't fail reliably with an invalid FD.
     We need to find a way to trigger it reliably (it seems epoll
     asio backend does fail as it tries to insert it right away, but
     not the others, maybe we could make it run this at last on epoll-based
     systems).
721
    EXPECT_THROW(UDPServer(service, -1, AF_INET, lookup_,
722
                           answer_), isc::asiolink::IOError);
723
    */
724
    EXPECT_THROW(TCPServer(this->service, -1, AF_INET,
725 726
                           this->lookup_, this->answer_),
                 isc::asiolink::IOError);
727 728
}

729
TYPED_TEST(DNSServerTest, DISABLED_invalidUDPFD) {
730 731 732 733 734 735 736
    /*
     FIXME: The UDP server doesn't fail reliably with an invalid FD.
     We need to find a way to trigger it reliably (it seems epoll
     asio backend does fail as it tries to insert it right away, but
     not the others, maybe we could make it run this at least on epoll-based
     systems).
    */
737
    EXPECT_THROW(this->createServer(-1, AF_INET), isc::asiolink::IOError);
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
738
}
739

740 741 742 743
// Check it rejects some of the unsupported operations
TEST_F(SyncServerTest, unsupportedOps) {
    EXPECT_THROW(udp_server_->clone(), isc::Unexpected);
    EXPECT_THROW(udp_server_->asyncLookup(), isc::Unexpected);
744 745 746
}

// Check it rejects forgotten resume (eg. insists that it is synchronous)
747 748
TEST_F(SyncServerTest, mustResume) {
    lookup_->allow_resume_ = false;
749
    ASSERT_THROW(testStopServerByStopper(*udp_server_, udp_client_, lookup_),
750 751 752
                 isc::Unexpected);
}

753 754
// SyncUDPServer doesn't allow NULL lookup callback.
TEST_F(SyncServerTest, nullLookupCallback) {
755
    EXPECT_THROW(SyncUDPServer::create(service, 0, AF_INET, NULL),
756 757 758
                 isc::InvalidParameter);
}

759 760 761 762 763 764 765 766 767 768 769 770 771
TEST_F(SyncServerTest, resetUDPServerBeforeEvent) {
    // Reset the UDP server object after starting and before it would get
    // an event from io_service (in this case abort event).  The following
    // sequence confirms it's shut down immediately, and without any
    // disruption.

    // Since we'll stop the server run() should immediately return, and
    // it's very unlikely to cause hangup.  But we'll make very sure it
    // doesn't happen.
    const unsigned int IO_SERVICE_TIME_OUT = 5;
    (*udp_server_)();
    udp_server_->stop();
    udp_server_.reset();
772
    void (*prev_handler)(int) = std::signal(SIGALRM, stopIOService);
773 774 775 776 777 778 779 780
    current_service = &service;
    alarm(IO_SERVICE_TIME_OUT);
    service.run();
    alarm(0);
    std::signal(SIGALRM, prev_handler);
    EXPECT_FALSE(io_service_is_time_out);
}

781
}