auth_srv_unittest.cc 26.7 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

    class MockSession : public AbstractSession {
    public:
        MockSession() :
            // by default we return a simple "success" message.
82 83 84
            msg_(Element::createFromString("{\"result\": [0, \"SUCCESS\"]}")),
            is_established_(false), establish_ok_(true), send_ok_(true),
            receive_ok_(true)
85 86 87 88 89 90 91 92
        {}
        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; }
93 94 95 96
        bool isEstablished() const { return (is_established_); }
        void disableEstablish() { establish_ok_ = false; }
        void disableSend() { send_ok_ = false; }
        void disableReceive() { receive_ok_ = false; }
97 98
    private:
        ElementPtr msg_;
99 100 101 102
        bool is_established_;
        bool establish_ok_;
        bool send_ok_;
        bool receive_ok_;
103 104
    };

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

JINMEI Tatuya's avatar
JINMEI Tatuya committed
137
    void createDataFromFile(const char* const datafile, int protocol);
138 139 140 141 142 143
    void createRequestMessage(const Opcode& opcode, const Name& request_name,
                              const RRClass& rrclass, const RRType& rrtype);
    void createRequestPacket(const Opcode& opcode, const Name& request_name,
                             const RRClass& rrclass, const RRType& rrtype,
                             int protocol);
    void createRequestPacket(int protocol);
144 145
};

146 147
void
AuthSrvTest::MockSession::establish(const char* socket_file UNUSED_PARAM) {
148 149 150 151
    if (!establish_ok_) {
        isc_throw(SessionError, "mock session is disabled for test");
    }
    is_established_ = true;
152 153 154
}

void
155
AuthSrvTest::MockSession::disconnect() {}
156 157 158 159 160 161 162

int
AuthSrvTest::MockSession::group_sendmsg(ElementPtr msg UNUSED_PARAM,
                                        string group UNUSED_PARAM,
                                        string instance UNUSED_PARAM,
                                        string to UNUSED_PARAM)
{
163 164 165
    if (!send_ok_) {
        isc_throw(XfroutError, "mock session send is disabled for test");
    }
166 167 168 169 170 171 172 173 174
    return (0);
}

bool
AuthSrvTest::MockSession::group_recvmsg(ElementPtr& envelope UNUSED_PARAM,
                                        ElementPtr& msg,
                                        bool nonblock UNUSED_PARAM,
                                        int seq UNUSED_PARAM)
{
175 176 177 178
    if (!receive_ok_) {
        isc_throw(XfroutError, "mock session receive is disabled for test");
    }

179 180 181 182
    msg = msg_;
    return (true);
}

183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
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_) {
207
        isc_throw(XfroutError, "xfrout connection send is disabled for test");
208 209 210 211
    }
    return (0);
}

212

213 214 215 216 217 218 219 220 221 222 223 224
// 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
225 226 227
AuthSrvTest::createDataFromFile(const char* const datafile,
                                const int protocol = IPPROTO_UDP)
{
228
    delete io_message;
229 230
    data.clear();

231
    delete endpoint;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
232
    endpoint = IOEndpoint::create(protocol, IOAddress("192.0.2.1"), 5300);
233
    UnitTestUtil::readWireData(datafile, data);
234
    io_message = new IOMessage(&data[0], data.size(),
235 236 237
                               protocol == IPPROTO_UDP ?
                               IOSocket::getDummyUDPSocket() :
                               IOSocket::getDummyTCPSocket(), *endpoint);
238 239
}

240
void
241 242 243 244
AuthSrvTest::createRequestMessage(const Opcode& opcode,
                                  const Name& request_name,
                                  const RRClass& rrclass,
                                  const RRType& rrtype)
245
{
246
    request_message.clear(Message::RENDER);
247
    request_message.setOpcode(opcode);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
248
    request_message.setQid(default_qid);
249
    request_message.addQuestion(Question(request_name, rrclass, rrtype));
250 251 252 253 254 255 256 257 258 259 260 261 262 263
}

void
AuthSrvTest::createRequestPacket(const Opcode& opcode,
                                 const Name& request_name,
                                 const RRClass& rrclass, const RRType& rrtype,
                                 const int protocol = IPPROTO_UDP)
{
    createRequestMessage(opcode, request_name, rrclass, rrtype);
    createRequestPacket(protocol);
}

void
AuthSrvTest::createRequestPacket(const int protocol = IPPROTO_UDP) {
264 265 266
    request_message.toWire(request_renderer);

    delete io_message;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
267
    endpoint = IOEndpoint::create(protocol, IOAddress("192.0.2.1"), 5300);
268 269
    io_message = new IOMessage(request_renderer.getData(),
                               request_renderer.getLength(),
270 271 272
                               protocol == IPPROTO_UDP ?
                               IOSocket::getDummyUDPSocket() :
                               IOSocket::getDummyTCPSocket(), *endpoint);
273 274
}

275 276 277 278 279 280 281 282 283 284
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());
285 286 287 288 289 290 291
    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()));
292 293 294 295 296 297 298 299 300 301 302

    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
303
        // the standard query (0) and notify(4)
304
        if (i == Opcode::NOTIFY().getCode()) {
Han Feng's avatar
Han Feng committed
305
            continue;
306
        }
307
        createDataFromFile("simplequery_fromWire");
308 309 310
        data[2] = ((i << 3) & 0xff);

        parse_message.clear(Message::PARSE);
311 312
        EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                              response_renderer));
313 314 315
        headerCheck(parse_message, default_qid, Rcode::NOTIMP(), i, QR_FLAG,
                    0, 0, 0, 0);
    }
316
}
317

318 319 320 321 322 323 324 325 326
// 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());
}

327 328
// Multiple questions.  Should result in FORMERR.
TEST_F(AuthSrvTest, multiQuestion) {
329
    createDataFromFile("multiquestion_fromWire");
330 331
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
332
    headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
333 334 335 336 337 338 339 340 341 342 343 344
                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());
345 346
}

347 348 349
// Incoming data doesn't even contain the complete header.  Must be silently
// dropped.
TEST_F(AuthSrvTest, shortMessage) {
350
    createDataFromFile("shortmessage_fromWire");
351 352
    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                           response_renderer));
353 354 355 356 357 358
}

// 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
359
    createDataFromFile("simpleresponse_fromWire");
360 361
    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                           response_renderer));
362 363 364

    // A response with a broken question section.  must be dropped rather than
    // returning FORMERR.
365
    createDataFromFile("shortresponse_fromWire");
366 367
    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                           response_renderer));
368 369

    // A response to iquery.  must be dropped rather than returning NOTIMP.
370
    createDataFromFile("iqueryresponse_fromWire");
371 372
    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                           response_renderer));
373 374 375 376
}

// Query with a broken question
TEST_F(AuthSrvTest, shortQuestion) {
377
    createDataFromFile("shortquestion_fromWire");
378 379
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
380 381 382 383 384
    // 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);
}
385

386 387
// Query with a broken answer section
TEST_F(AuthSrvTest, shortAnswer) {
388
    createDataFromFile("shortanswer_fromWire");
389 390
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
391 392 393

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

397 398 399 400 401 402
    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());
403 404
}

405 406
// Query with unsupported version of EDNS.
TEST_F(AuthSrvTest, ednsBadVers) {
407
    createDataFromFile("queryBadEDNS_fromWire");
408 409
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
410 411 412 413 414 415 416 417 418 419

    // 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
420 421
TEST_F(AuthSrvTest, AXFROverUDP) {
    // AXFR over UDP is invalid and should result in FORMERR.
422 423
    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                        RRType::AXFR(), IPPROTO_UDP);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
424 425 426 427 428 429
    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);
}

430 431
TEST_F(AuthSrvTest, AXFRSuccess) {
    EXPECT_FALSE(xfrout.isConnected());
432 433
    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                        RRType::AXFR(), IPPROTO_TCP);
434 435 436 437 438 439 440 441 442 443
    // 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();
444 445
    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                        RRType::AXFR(), IPPROTO_TCP);
446 447 448 449 450 451 452 453 454 455
    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.
456 457
    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                        RRType::AXFR(), IPPROTO_TCP);
458 459 460 461 462 463
    server.processMessage(*io_message, parse_message, response_renderer);
    EXPECT_TRUE(xfrout.isConnected());

    xfrout.disableSend();
    parse_message.clear(Message::PARSE);
    response_renderer.clear();
464 465
    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                        RRType::AXFR(), IPPROTO_TCP);
466 467 468 469 470 471 472 473 474 475 476 477 478 479
    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();
480 481
    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                        RRType::AXFR(), IPPROTO_TCP);
482 483 484 485 486 487 488 489 490
    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();
}

491 492 493 494
TEST_F(AuthSrvTest, notify) {
    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
                        RRType::SOA());
    request_message.setHeaderFlag(MessageFlag::AA());
495
    createRequestPacket(IPPROTO_UDP);
496
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
                                          response_renderer));
    headerCheck(parse_message, default_qid, Rcode::NOERROR(),
                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);

    // The question must be identical of the notify
    ConstQuestionPtr question = *parse_message.beginQuestion();
    EXPECT_EQ(Name("example.com"), question->getName());
    EXPECT_EQ(RRClass::IN(), question->getClass());
    EXPECT_EQ(RRType::SOA(), question->getType());
}

TEST_F(AuthSrvTest, notifyEmptyQuestion) {
    request_message.clear(Message::RENDER);
    request_message.setOpcode(Opcode::NOTIFY());
    request_message.setHeaderFlag(MessageFlag::AA());
    request_message.setQid(default_qid);
    request_message.toWire(request_renderer);
    createRequestPacket(IPPROTO_UDP);
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
    headerCheck(parse_message, default_qid, Rcode::FORMERR(),
                Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyMultiQuestions) {
    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
                        RRType::SOA());
    // add one more SOA question
    request_message.addQuestion(Question(Name("example.com"), RRClass::IN(),
                                         RRType::SOA()));
    request_message.setHeaderFlag(MessageFlag::AA());
    createRequestPacket(IPPROTO_UDP);
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
    headerCheck(parse_message, default_qid, Rcode::FORMERR(),
                Opcode::NOTIFY().getCode(), QR_FLAG, 2, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyNonSOAQuestion) {
    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
                        RRType::NS());
    request_message.setHeaderFlag(MessageFlag::AA());
    createRequestPacket(IPPROTO_UDP);
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
    headerCheck(parse_message, default_qid, Rcode::FORMERR(),
                Opcode::NOTIFY().getCode(), QR_FLAG, 1, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyWithoutAA) {
    // implicitly leave the AA bit off.  our implementation will accept it.
    createRequestPacket(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
                        RRType::SOA());
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
    headerCheck(parse_message, default_qid, Rcode::NOERROR(),
                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyWithErrorRcode) {
    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
                        RRType::SOA());
    request_message.setHeaderFlag(MessageFlag::AA());
    request_message.setRcode(Rcode::SERVFAIL());
    createRequestPacket(IPPROTO_UDP);
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
564 565
    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
566 567
}

568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630
TEST_F(AuthSrvTest, notifyEstablishFail) {
    EXPECT_FALSE(notify_session.isEstablished()); // check prerequisite
    notify_session.disableEstablish();

    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
                        RRType::SOA());
    request_message.setHeaderFlag(MessageFlag::AA());
    createRequestPacket(IPPROTO_UDP);

    // we simply ignore the notify and let it be resent if an internal error
    // happens.
    EXPECT_FALSE(server.processMessage(*io_message, parse_message,
                                       response_renderer));
    EXPECT_FALSE(notify_session.isEstablished());
}

TEST_F(AuthSrvTest, notifySendFail) {
    notify_session.disableSend();

    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
                        RRType::SOA());
    request_message.setHeaderFlag(MessageFlag::AA());
    createRequestPacket(IPPROTO_UDP);
    EXPECT_FALSE(server.processMessage(*io_message, parse_message,
                                       response_renderer));
    // we don't disconnect the session due to a send error.
    EXPECT_TRUE(notify_session.isEstablished());
}

TEST_F(AuthSrvTest, notifyReceiveFail) {
    notify_session.disableReceive();

    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
                        RRType::SOA());
    request_message.setHeaderFlag(MessageFlag::AA());
    createRequestPacket(IPPROTO_UDP);
    EXPECT_FALSE(server.processMessage(*io_message, parse_message,
                                       response_renderer));
}

TEST_F(AuthSrvTest, notifyWithBogusSessionMessage) {
    notify_session.setMessage(Element::createFromString("{\"foo\": 1}"));

    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
                        RRType::SOA());
    request_message.setHeaderFlag(MessageFlag::AA());
    createRequestPacket(IPPROTO_UDP);
    EXPECT_FALSE(server.processMessage(*io_message, parse_message,
                                       response_renderer));
}

TEST_F(AuthSrvTest, notifyWithSessionMessageError) {
    notify_session.setMessage(
        Element::createFromString("{\"result\": [1, \"FAIL\"]}"));

    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
                        RRType::SOA());
    request_message.setHeaderFlag(MessageFlag::AA());
    createRequestPacket(IPPROTO_UDP);
    EXPECT_FALSE(server.processMessage(*io_message, parse_message,
                                       response_renderer));
}

631
void
632 633 634
updateConfig(AuthSrv* server, const char* const dbfile,
             const bool expect_success)
{
635 636 637 638 639 640 641
    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());
642
    EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue());
643 644 645 646
}

// Install a Sqlite3 data source with testing data.
TEST_F(AuthSrvTest, updateConfig) {
647
    updateConfig(&server, CONFIG_TESTDB, true);
648 649 650 651

    // 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.
652
    createDataFromFile("examplequery_fromWire");
653 654
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
655 656 657 658 659
    headerCheck(parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}

TEST_F(AuthSrvTest, datasourceFail) {
660
    updateConfig(&server, CONFIG_TESTDB, true);
661 662 663 664 665

    // 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.
666
    createDataFromFile("badExampleQuery_fromWire");
667 668
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
669 670 671
    headerCheck(parse_message, default_qid, Rcode::SERVFAIL(), opcode.getCode(),
                QR_FLAG, 1, 0, 0, 0);
}
672 673 674 675 676 677 678 679 680

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.
681
    createDataFromFile("examplequery_fromWire");
682 683
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
684 685 686
    headerCheck(parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}
687
}