auth_srv_unittest.cc 19.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

// $Id$

17 18
#include <config.h>

19 20 21 22 23 24 25 26 27
#include <gtest/gtest.h>

#include <dns/buffer.h>
#include <dns/name.h>
#include <dns/message.h>
#include <dns/messagerenderer.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>

28
#include <cc/data.h>
29
#include <cc/session.h>
30

31 32
#include <xfr/xfrout_client.h>

33
#include <auth/auth_srv.h>
34
#include <auth/asio_link.h>
35 36 37 38 39

#include <dns/tests/unittest_util.h>

using isc::UnitTestUtil;
using namespace std;
40
using namespace isc::cc;
41
using namespace isc::dns;
42
using namespace isc::data;
43
using namespace isc::xfr;
44
using namespace asio_link;
45 46

namespace {
47 48
const char* CONFIG_TESTDB =
    "{\"database_file\": \"" TEST_DATA_DIR "/example.sqlite3\"}";
49 50 51
// The following file must be non existent and must be non"creatable" (see
// the sqlite3 test).
const char* BADCONFIG_TESTDB =
52
    "{ \"database_file\": \"" TEST_DATA_DIR "/nodir/notexist\"}";
53

54
class AuthSrvTest : public ::testing::Test {
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
private:
    class MockXfroutClient : public AbstractXfroutClient {
    public:
        MockXfroutClient() :
            is_connected_(false), connect_ok_(true), send_ok_(true),
            disconnect_ok_(true)
        {}
        virtual void connect();
        virtual void disconnect();
        virtual int sendXfroutRequestInfo(int tcp_sock, const void* msg_data,
                                          uint16_t msg_len);
        bool isConnected() const { return (is_connected_); }
        void disableConnect() { connect_ok_ = false; }
        void disableDisconnect() { disconnect_ok_ = false; }
        void enableDisconnect() { disconnect_ok_ = true; }
        void disableSend() { send_ok_ = false; }
    private:
        bool is_connected_;
        bool connect_ok_;
        bool send_ok_;
        bool disconnect_ok_;
    };
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94

    class MockSession : public AbstractSession {
    public:
        MockSession() :
            // by default we return a simple "success" message.
            msg_(Element::createFromString("{\"result\": [0, \"SUCCESS\"]}"))
        {}
        virtual void establish(const char* socket_file);
        virtual void disconnect();
        virtual int group_sendmsg(ElementPtr msg, string group,
                                  string instance, string to);
        virtual bool group_recvmsg(ElementPtr& envelope, ElementPtr& msg,
                                   bool nonblock, int seq);
        void setMessage(ElementPtr msg) { msg_ = msg; }
    private:
        ElementPtr msg_;
    };

95
protected:
96 97
    AuthSrvTest() : server(notify_session, xfrout),
                    request_message(Message::RENDER),
98
                    parse_message(Message::PARSE), default_qid(0x1035),
99
                    opcode(Opcode(Opcode::QUERY())), qname("www.example.com"),
100
                    qclass(RRClass::IN()), qtype(RRType::A()),
101
                    io_message(NULL), endpoint(NULL), request_obuffer(0),
102
                    request_renderer(request_obuffer),
103 104
                    response_obuffer(0), response_renderer(response_obuffer)
    {}
105 106
    ~AuthSrvTest() {
        delete io_message;
107
        delete endpoint;
108
    }
109
    MockSession notify_session;
110
    MockXfroutClient xfrout;
111 112 113
    AuthSrv server;
    Message request_message;
    Message parse_message;
114 115 116 117 118
    const qid_t default_qid;
    const Opcode opcode;
    const Name qname;
    const RRClass qclass;
    const RRType qtype;
119 120
    IOMessage* io_message;
    const IOEndpoint* endpoint;
121 122 123 124 125
    OutputBuffer request_obuffer;
    MessageRenderer request_renderer;
    OutputBuffer response_obuffer;
    MessageRenderer response_renderer;
    vector<uint8_t> data;
126

JINMEI Tatuya's avatar
JINMEI Tatuya committed
127
    void createDataFromFile(const char* const datafile, int protocol);
128
    void createRequest(const Opcode& opcode, const Name& request_name,
JINMEI Tatuya's avatar
JINMEI Tatuya committed
129 130
                       const RRClass& rrclass, const RRType& rrtype,
                       int protocol);
131 132
};

133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
void
AuthSrvTest::MockSession::establish(const char* socket_file UNUSED_PARAM) {
}

void
AuthSrvTest::MockSession::disconnect() {
}

int
AuthSrvTest::MockSession::group_sendmsg(ElementPtr msg UNUSED_PARAM,
                                        string group UNUSED_PARAM,
                                        string instance UNUSED_PARAM,
                                        string to UNUSED_PARAM)
{
    return (0);
}

bool
AuthSrvTest::MockSession::group_recvmsg(ElementPtr& envelope UNUSED_PARAM,
                                        ElementPtr& msg,
                                        bool nonblock UNUSED_PARAM,
                                        int seq UNUSED_PARAM)
{
    msg = msg_;
    return (true);
}

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
void
AuthSrvTest::MockXfroutClient::connect() {
    if (!connect_ok_) {
        isc_throw(XfroutError, "xfrout connection disabled for test");
    }
    is_connected_ = true;
}

void
AuthSrvTest::MockXfroutClient::disconnect() {
    if (!disconnect_ok_) {
        isc_throw(XfroutError,
                  "closing xfrout connection is disabled for test");
    }
    is_connected_ = false;
}

int
AuthSrvTest::MockXfroutClient::sendXfroutRequestInfo(
    const int tcp_sock UNUSED_PARAM,
    const void* msg_data UNUSED_PARAM,
    const uint16_t msg_len UNUSED_PARAM)
{
    if (!send_ok_) {
        isc_throw(XfroutError, "xfrout connection send for test");
    }
    return (0);
}

189

190 191 192 193 194 195 196 197 198 199 200 201
// These are flags to indicate whether the corresponding flag bit of the
// DNS header is to be set in the test cases.  (Note that the flag values
// is irrelevant to their wire-format values)
const unsigned int QR_FLAG = 0x1;
const unsigned int AA_FLAG = 0x2;
const unsigned int TC_FLAG = 0x4;
const unsigned int RD_FLAG = 0x8;
const unsigned int RA_FLAG = 0x10;
const unsigned int AD_FLAG = 0x20;
const unsigned int CD_FLAG = 0x40;

void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
202 203 204
AuthSrvTest::createDataFromFile(const char* const datafile,
                                const int protocol = IPPROTO_UDP)
{
205
    delete io_message;
206 207
    data.clear();

208
    delete endpoint;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
209
    endpoint = IOEndpoint::create(protocol, IOAddress("192.0.2.1"), 5300);
210
    UnitTestUtil::readWireData(datafile, data);
211
    io_message = new IOMessage(&data[0], data.size(),
212 213 214
                               protocol == IPPROTO_UDP ?
                               IOSocket::getDummyUDPSocket() :
                               IOSocket::getDummyTCPSocket(), *endpoint);
215 216
}

217 218
void
AuthSrvTest::createRequest(const Opcode& opcode, const Name& request_name,
JINMEI Tatuya's avatar
JINMEI Tatuya committed
219 220
                           const RRClass& rrclass, const RRType& rrtype,
                           const int protocol = IPPROTO_UDP)
221
{
222
    request_message.clear(Message::RENDER);
223
    request_message.setOpcode(opcode);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
224
    request_message.setQid(default_qid);
225 226 227 228
    request_message.addQuestion(Question(request_name, rrclass, rrtype));
    request_message.toWire(request_renderer);

    delete io_message;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
229
    endpoint = IOEndpoint::create(protocol, IOAddress("192.0.2.1"), 5300);
230 231
    io_message = new IOMessage(request_renderer.getData(),
                               request_renderer.getLength(),
232 233 234
                               protocol == IPPROTO_UDP ?
                               IOSocket::getDummyUDPSocket() :
                               IOSocket::getDummyTCPSocket(), *endpoint);
235 236
}

237 238 239 240 241 242 243 244 245 246
void
headerCheck(const Message& message, const qid_t qid, const Rcode& rcode,
            const uint16_t opcodeval, const unsigned int flags,
            const unsigned int qdcount,
            const unsigned int ancount, const unsigned int nscount,
            const unsigned int arcount)
{
    EXPECT_EQ(qid, message.getQid());
    EXPECT_EQ(rcode, message.getRcode());
    EXPECT_EQ(opcodeval, message.getOpcode().getCode());
247 248 249 250 251 252 253
    EXPECT_EQ((flags & QR_FLAG) != 0, message.getHeaderFlag(MessageFlag::QR()));
    EXPECT_EQ((flags & AA_FLAG) != 0, message.getHeaderFlag(MessageFlag::AA()));
    EXPECT_EQ((flags & TC_FLAG) != 0, message.getHeaderFlag(MessageFlag::TC()));
    EXPECT_EQ((flags & RA_FLAG) != 0, message.getHeaderFlag(MessageFlag::RA()));
    EXPECT_EQ((flags & RD_FLAG) != 0, message.getHeaderFlag(MessageFlag::RD()));
    EXPECT_EQ((flags & AD_FLAG) != 0, message.getHeaderFlag(MessageFlag::AD()));
    EXPECT_EQ((flags & CD_FLAG) != 0, message.getHeaderFlag(MessageFlag::CD()));
254 255 256 257 258 259 260 261 262 263 264

    EXPECT_EQ(qdcount, message.getRRCount(Section::QUESTION()));
    EXPECT_EQ(ancount, message.getRRCount(Section::ANSWER()));
    EXPECT_EQ(nscount, message.getRRCount(Section::AUTHORITY()));
    EXPECT_EQ(arcount, message.getRRCount(Section::ADDITIONAL()));
}

// Unsupported requests.  Should result in NOTIMP.
TEST_F(AuthSrvTest, unsupportedRequest) {
    for (unsigned int i = 1; i < 16; ++i) {
        // set Opcode to 'i', which iterators over all possible codes except
Han Feng's avatar
Han Feng committed
265 266 267
        // the standard query (0) and notify(4)
        if (i == 4)
            continue;
268
        createDataFromFile("simplequery_fromWire");
269 270 271
        data[2] = ((i << 3) & 0xff);

        parse_message.clear(Message::PARSE);
272 273
        EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                              response_renderer));
274 275 276
        headerCheck(parse_message, default_qid, Rcode::NOTIMP(), i, QR_FLAG,
                    0, 0, 0, 0);
    }
277
}
278

279 280 281 282 283 284 285 286 287
// Simple API check
TEST_F(AuthSrvTest, verbose) {
    EXPECT_FALSE(server.getVerbose());
    server.setVerbose(true);
    EXPECT_TRUE(server.getVerbose());
    server.setVerbose(false);
    EXPECT_FALSE(server.getVerbose());
}

288 289
// Multiple questions.  Should result in FORMERR.
TEST_F(AuthSrvTest, multiQuestion) {
290
    createDataFromFile("multiquestion_fromWire");
291 292
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
293
    headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
294 295 296 297 298 299 300 301 302 303 304 305
                QR_FLAG, 2, 0, 0, 0);

    QuestionIterator qit = parse_message.beginQuestion();
    EXPECT_EQ(Name("example.com"), (*qit)->getName());
    EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
    EXPECT_EQ(RRType::A(), (*qit)->getType());
    ++qit;
    EXPECT_EQ(Name("example.com"), (*qit)->getName());
    EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
    EXPECT_EQ(RRType::AAAA(), (*qit)->getType());
    ++qit;
    EXPECT_TRUE(qit == parse_message.endQuestion());
306 307
}

308 309 310
// Incoming data doesn't even contain the complete header.  Must be silently
// dropped.
TEST_F(AuthSrvTest, shortMessage) {
311
    createDataFromFile("shortmessage_fromWire");
312 313
    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                           response_renderer));
314 315 316 317 318 319
}

// Response messages.  Must be silently dropped, whether it's a valid response
// or malformed or could otherwise cause a protocol error.
TEST_F(AuthSrvTest, response) {
    // A valid (although unusual) response
320
    createDataFromFile("simpleresponse_fromWire");
321 322
    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                           response_renderer));
323 324 325

    // A response with a broken question section.  must be dropped rather than
    // returning FORMERR.
326
    createDataFromFile("shortresponse_fromWire");
327 328
    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                           response_renderer));
329 330

    // A response to iquery.  must be dropped rather than returning NOTIMP.
331
    createDataFromFile("iqueryresponse_fromWire");
332 333
    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                           response_renderer));
334 335 336 337
}

// Query with a broken question
TEST_F(AuthSrvTest, shortQuestion) {
338
    createDataFromFile("shortquestion_fromWire");
339 340
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
341 342 343 344 345
    // Since the query's question is broken, the question section of the
    // response should be empty.
    headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
                QR_FLAG, 0, 0, 0, 0);
}
346

347 348
// Query with a broken answer section
TEST_F(AuthSrvTest, shortAnswer) {
349
    createDataFromFile("shortanswer_fromWire");
350 351
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
352 353 354

    // This is a bogus query, but question section is valid.  So the response
    // should copy the question section.
355 356 357
    headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
                QR_FLAG, 1, 0, 0, 0);

358 359 360 361 362 363
    QuestionIterator qit = parse_message.beginQuestion();
    EXPECT_EQ(Name("example.com"), (*qit)->getName());
    EXPECT_EQ(RRClass::IN(), (*qit)->getClass());
    EXPECT_EQ(RRType::A(), (*qit)->getType());
    ++qit;
    EXPECT_TRUE(qit == parse_message.endQuestion());
364 365
}

366 367
// Query with unsupported version of EDNS.
TEST_F(AuthSrvTest, ednsBadVers) {
368
    createDataFromFile("queryBadEDNS_fromWire");
369 370
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
371 372 373 374 375 376 377 378 379 380

    // The response must have an EDNS OPT RR in the additional section.
    // Note that the DNSSEC DO bit is cleared even if this bit in the query
    // is set.  This is a limitation of the current implementation.
    headerCheck(parse_message, default_qid, Rcode::BADVERS(), opcode.getCode(),
                QR_FLAG, 1, 0, 0, 1);
    EXPECT_EQ(4096, parse_message.getUDPSize());
    EXPECT_FALSE(parse_message.isDNSSECSupported());
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
381 382 383 384 385 386 387 388 389 390
TEST_F(AuthSrvTest, AXFROverUDP) {
    // AXFR over UDP is invalid and should result in FORMERR.
    createRequest(opcode, Name("example.com"), RRClass::IN(), RRType::AXFR(),
                  IPPROTO_UDP);
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
    headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
                QR_FLAG, 1, 0, 0, 0);
}

391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
TEST_F(AuthSrvTest, AXFRSuccess) {
    EXPECT_FALSE(xfrout.isConnected());
    createRequest(opcode, Name("example.com"), RRClass::IN(), RRType::AXFR(),
                  IPPROTO_TCP);
    // On success, the AXFR query has been passed to a separate process,
    // so we shouldn't have to respond.
    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                           response_renderer));
    EXPECT_TRUE(xfrout.isConnected());
}

TEST_F(AuthSrvTest, AXFRConnectFail) {
    EXPECT_FALSE(xfrout.isConnected()); // check prerequisite
    xfrout.disableConnect();
    createRequest(opcode, Name("example.com"), RRClass::IN(), RRType::AXFR(),
                  IPPROTO_TCP);
    EXPECT_TRUE(server.processMessage(*io_message, parse_message,
                                      response_renderer));
    headerCheck(parse_message, default_qid, Rcode::SERVFAIL(),
                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
    EXPECT_FALSE(xfrout.isConnected());
}

TEST_F(AuthSrvTest, AXFRSendFail) {
    // first send a valid query, making the connection with the xfr process
    // open.
    createRequest(opcode, Name("example.com"), RRClass::IN(), RRType::AXFR(),
                  IPPROTO_TCP);
    server.processMessage(*io_message, parse_message, response_renderer);
    EXPECT_TRUE(xfrout.isConnected());

    xfrout.disableSend();
    parse_message.clear(Message::PARSE);
    response_renderer.clear();
    createRequest(opcode, Name("example.com"), RRClass::IN(), RRType::AXFR(),
                  IPPROTO_TCP);
    EXPECT_TRUE(server.processMessage(*io_message, parse_message,
                                      response_renderer));
    headerCheck(parse_message, default_qid, Rcode::SERVFAIL(),
                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);

    // The connection should have been closed due to the send failure.
    EXPECT_FALSE(xfrout.isConnected());
}

TEST_F(AuthSrvTest, AXFRDisconnectFail) {
    // In our usage disconnect() shouldn't fail.  So we'll see the exception
    // should it be thrown.
    xfrout.disableSend();
    xfrout.disableDisconnect();
    createRequest(opcode, Name("example.com"), RRClass::IN(), RRType::AXFR(),
                  IPPROTO_TCP);
    EXPECT_THROW(server.processMessage(*io_message, parse_message,
                                       response_renderer),
                 XfroutError);
    EXPECT_TRUE(xfrout.isConnected());
    // XXX: we need to re-enable disconnect.  otherwise an exception would be
    // thrown via the destructor of the server.
    xfrout.enableDisconnect();
}

Han Feng's avatar
Han Feng committed
452
TEST_F(AuthSrvTest, notifyInTest) {
453 454
    createRequest(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
                  RRType::SOA());
455
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
456
                                           response_renderer));
457 458
    headerCheck(parse_message, default_qid, Rcode::NOERROR(),
                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
Han Feng's avatar
Han Feng committed
459 460
}

461
void
462 463 464
updateConfig(AuthSrv* server, const char* const dbfile,
             const bool expect_success)
{
465 466 467 468 469 470 471
    const ElementPtr config_answer =
        server->updateConfig(Element::createFromString(dbfile));
    EXPECT_EQ(Element::map, config_answer->getType());
    EXPECT_TRUE(config_answer->contains("result"));

    const ElementPtr result = config_answer->get("result");
    EXPECT_EQ(Element::list, result->getType());
472
    EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue());
473 474 475 476
}

// Install a Sqlite3 data source with testing data.
TEST_F(AuthSrvTest, updateConfig) {
477
    updateConfig(&server, CONFIG_TESTDB, true);
478 479 480 481

    // query for existent data in the installed data source.  The resulting
    // response should have the AA flag on, and have an RR in each answer
    // and authority section.
482
    createDataFromFile("examplequery_fromWire");
483 484
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
485 486 487 488 489
    headerCheck(parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}

TEST_F(AuthSrvTest, datasourceFail) {
490
    updateConfig(&server, CONFIG_TESTDB, true);
491 492 493 494 495

    // This query will hit a corrupted entry of the data source (the zoneload
    // tool and the data source itself naively accept it).  This will result
    // in a SERVFAIL response, and the answer and authority sections should
    // be empty.
496
    createDataFromFile("badExampleQuery_fromWire");
497 498
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
499 500 501
    headerCheck(parse_message, default_qid, Rcode::SERVFAIL(), opcode.getCode(),
                QR_FLAG, 1, 0, 0, 0);
}
502 503 504 505 506 507 508 509 510

TEST_F(AuthSrvTest, updateConfigFail) {
    // First, load a valid data source.
    updateConfig(&server, CONFIG_TESTDB, true);

    // Next, try to update it with a non-existent one.  This should fail.
    updateConfig(&server, BADCONFIG_TESTDB, false);

    // The original data source should still exist.
511
    createDataFromFile("examplequery_fromWire");
512 513
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
514 515 516
    headerCheck(parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}
517
}