auth_srv_unittest.cc 27.3 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
#include <boost/function.hpp>

21 22 23 24 25 26 27 28 29
#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>

30
#include <cc/data.h>
31
#include <cc/session.h>
32

33 34
#include <xfr/xfrout_client.h>

35
#include <auth/auth_srv.h>
36
#include <auth/asio_link.h>
37 38 39 40 41

#include <dns/tests/unittest_util.h>

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

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

56
class AuthSrvTest : public ::testing::Test {
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
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_;
    };
79 80 81 82 83

    class MockSession : public AbstractSession {
    public:
        MockSession() :
            // by default we return a simple "success" message.
84 85 86
            msg_(Element::createFromString("{\"result\": [0, \"SUCCESS\"]}")),
            is_established_(false), establish_ok_(true), send_ok_(true),
            receive_ok_(true)
87 88 89 90 91 92 93
        {}
        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);
94 95 96 97 98 99 100 101 102 103 104 105 106 107
        virtual void subscribe(string group UNUSED_PARAM,
                               string instance UNUSED_PARAM)
        {}
        virtual void unsubscribe(string group UNUSED_PARAM,
                                 string instance UNUSED_PARAM)
        {}
        virtual void startRead(
            boost::function<void()> read_callback UNUSED_PARAM)
        {}
        virtual int reply(ElementPtr& envelope UNUSED_PARAM,
                          ElementPtr& newmsg UNUSED_PARAM)
        { return (0); }
        virtual bool hasQueuedMsgs() { return (false); }

108
        void setMessage(ElementPtr msg) { msg_ = msg; }
109 110 111 112
        bool isEstablished() const { return (is_established_); }
        void disableEstablish() { establish_ok_ = false; }
        void disableSend() { send_ok_ = false; }
        void disableReceive() { receive_ok_ = false; }
113 114
    private:
        ElementPtr msg_;
115 116 117 118
        bool is_established_;
        bool establish_ok_;
        bool send_ok_;
        bool receive_ok_;
119 120
    };

121
protected:
122
    AuthSrvTest() : server(xfrout),
123
                    request_message(Message::RENDER),
124
                    parse_message(Message::PARSE), default_qid(0x1035),
125
                    opcode(Opcode(Opcode::QUERY())), qname("www.example.com"),
126
                    qclass(RRClass::IN()), qtype(RRType::A()),
127
                    io_message(NULL), endpoint(NULL), request_obuffer(0),
128
                    request_renderer(request_obuffer),
129
                    response_obuffer(0), response_renderer(response_obuffer)
130 131 132
    {
        server.setSession(&notify_session);
    }
133 134
    ~AuthSrvTest() {
        delete io_message;
135
        delete endpoint;
136
    }
137
    MockSession notify_session;
138
    MockXfroutClient xfrout;
139 140 141
    AuthSrv server;
    Message request_message;
    Message parse_message;
142 143 144 145 146
    const qid_t default_qid;
    const Opcode opcode;
    const Name qname;
    const RRClass qclass;
    const RRType qtype;
147 148
    IOMessage* io_message;
    const IOEndpoint* endpoint;
149 150 151 152 153
    OutputBuffer request_obuffer;
    MessageRenderer request_renderer;
    OutputBuffer response_obuffer;
    MessageRenderer response_renderer;
    vector<uint8_t> data;
154

JINMEI Tatuya's avatar
JINMEI Tatuya committed
155
    void createDataFromFile(const char* const datafile, int protocol);
156 157 158 159 160 161
    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);
162 163
};

164 165
void
AuthSrvTest::MockSession::establish(const char* socket_file UNUSED_PARAM) {
166 167 168 169
    if (!establish_ok_) {
        isc_throw(SessionError, "mock session is disabled for test");
    }
    is_established_ = true;
170 171 172
}

void
173
AuthSrvTest::MockSession::disconnect() {}
174 175 176 177 178 179 180

int
AuthSrvTest::MockSession::group_sendmsg(ElementPtr msg UNUSED_PARAM,
                                        string group UNUSED_PARAM,
                                        string instance UNUSED_PARAM,
                                        string to UNUSED_PARAM)
{
181 182 183
    if (!send_ok_) {
        isc_throw(XfroutError, "mock session send is disabled for test");
    }
184 185 186 187 188 189 190 191 192
    return (0);
}

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

197 198 199 200
    msg = msg_;
    return (true);
}

201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
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_) {
225
        isc_throw(XfroutError, "xfrout connection send is disabled for test");
226 227 228 229
    }
    return (0);
}

230

231 232 233 234 235 236 237 238 239 240 241 242
// 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
243 244 245
AuthSrvTest::createDataFromFile(const char* const datafile,
                                const int protocol = IPPROTO_UDP)
{
246
    delete io_message;
247 248
    data.clear();

249
    delete endpoint;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
250
    endpoint = IOEndpoint::create(protocol, IOAddress("192.0.2.1"), 5300);
251
    UnitTestUtil::readWireData(datafile, data);
252
    io_message = new IOMessage(&data[0], data.size(),
253 254 255
                               protocol == IPPROTO_UDP ?
                               IOSocket::getDummyUDPSocket() :
                               IOSocket::getDummyTCPSocket(), *endpoint);
256 257
}

258
void
259 260 261 262
AuthSrvTest::createRequestMessage(const Opcode& opcode,
                                  const Name& request_name,
                                  const RRClass& rrclass,
                                  const RRType& rrtype)
263
{
264
    request_message.clear(Message::RENDER);
265
    request_message.setOpcode(opcode);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
266
    request_message.setQid(default_qid);
267
    request_message.addQuestion(Question(request_name, rrclass, rrtype));
268 269 270 271 272 273 274 275 276 277 278 279 280 281
}

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) {
282 283 284
    request_message.toWire(request_renderer);

    delete io_message;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
285
    endpoint = IOEndpoint::create(protocol, IOAddress("192.0.2.1"), 5300);
286 287
    io_message = new IOMessage(request_renderer.getData(),
                               request_renderer.getLength(),
288 289 290
                               protocol == IPPROTO_UDP ?
                               IOSocket::getDummyUDPSocket() :
                               IOSocket::getDummyTCPSocket(), *endpoint);
291 292
}

293 294 295 296 297 298 299 300 301 302
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());
303 304 305 306 307 308 309
    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()));
310 311 312 313 314 315 316 317 318 319 320

    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
321
        // the standard query (0) and notify(4)
322
        if (i == Opcode::NOTIFY().getCode()) {
Han Feng's avatar
Han Feng committed
323
            continue;
324
        }
325
        createDataFromFile("simplequery_fromWire");
326 327 328
        data[2] = ((i << 3) & 0xff);

        parse_message.clear(Message::PARSE);
329 330
        EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                              response_renderer));
331 332 333
        headerCheck(parse_message, default_qid, Rcode::NOTIMP(), i, QR_FLAG,
                    0, 0, 0, 0);
    }
334
}
335

336 337 338 339 340 341 342 343 344
// 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());
}

345 346
// Multiple questions.  Should result in FORMERR.
TEST_F(AuthSrvTest, multiQuestion) {
347
    createDataFromFile("multiquestion_fromWire");
348 349
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
350
    headerCheck(parse_message, default_qid, Rcode::FORMERR(), opcode.getCode(),
351 352 353 354 355 356 357 358 359 360 361 362
                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());
363 364
}

365 366 367
// Incoming data doesn't even contain the complete header.  Must be silently
// dropped.
TEST_F(AuthSrvTest, shortMessage) {
368
    createDataFromFile("shortmessage_fromWire");
369 370
    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                           response_renderer));
371 372 373 374 375 376
}

// 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
377
    createDataFromFile("simpleresponse_fromWire");
378 379
    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                           response_renderer));
380 381 382

    // A response with a broken question section.  must be dropped rather than
    // returning FORMERR.
383
    createDataFromFile("shortresponse_fromWire");
384 385
    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                           response_renderer));
386 387

    // A response to iquery.  must be dropped rather than returning NOTIMP.
388
    createDataFromFile("iqueryresponse_fromWire");
389 390
    EXPECT_EQ(false, server.processMessage(*io_message, parse_message,
                                           response_renderer));
391 392 393 394
}

// Query with a broken question
TEST_F(AuthSrvTest, shortQuestion) {
395
    createDataFromFile("shortquestion_fromWire");
396 397
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
398 399 400 401 402
    // 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);
}
403

404 405
// Query with a broken answer section
TEST_F(AuthSrvTest, shortAnswer) {
406
    createDataFromFile("shortanswer_fromWire");
407 408
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
409 410 411

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

415 416 417 418 419 420
    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());
421 422
}

423 424
// Query with unsupported version of EDNS.
TEST_F(AuthSrvTest, ednsBadVers) {
425
    createDataFromFile("queryBadEDNS_fromWire");
426 427
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
428 429 430 431 432 433 434 435 436 437

    // 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
438 439
TEST_F(AuthSrvTest, AXFROverUDP) {
    // AXFR over UDP is invalid and should result in FORMERR.
440 441
    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                        RRType::AXFR(), IPPROTO_UDP);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
442 443 444 445 446 447
    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);
}

448 449
TEST_F(AuthSrvTest, AXFRSuccess) {
    EXPECT_FALSE(xfrout.isConnected());
450 451
    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                        RRType::AXFR(), IPPROTO_TCP);
452 453 454 455 456 457 458 459 460 461
    // 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();
462 463
    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                        RRType::AXFR(), IPPROTO_TCP);
464 465 466 467 468 469 470 471 472 473
    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.
474 475
    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                        RRType::AXFR(), IPPROTO_TCP);
476 477 478 479 480 481
    server.processMessage(*io_message, parse_message, response_renderer);
    EXPECT_TRUE(xfrout.isConnected());

    xfrout.disableSend();
    parse_message.clear(Message::PARSE);
    response_renderer.clear();
482 483
    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                        RRType::AXFR(), IPPROTO_TCP);
484 485 486 487 488 489 490 491 492 493 494 495 496 497
    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();
498 499
    createRequestPacket(opcode, Name("example.com"), RRClass::IN(),
                        RRType::AXFR(), IPPROTO_TCP);
500 501 502 503 504 505 506 507 508
    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();
}

509 510 511 512
TEST_F(AuthSrvTest, notify) {
    createRequestMessage(Opcode::NOTIFY(), Name("example.com"), RRClass::IN(),
                        RRType::SOA());
    request_message.setHeaderFlag(MessageFlag::AA());
513
    createRequestPacket(IPPROTO_UDP);
514
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
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 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581
                                          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));
582 583
    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
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 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
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));
}

649
void
650 651 652
updateConfig(AuthSrv* server, const char* const dbfile,
             const bool expect_success)
{
653 654 655 656 657 658 659
    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());
660
    EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue());
661 662 663 664
}

// Install a Sqlite3 data source with testing data.
TEST_F(AuthSrvTest, updateConfig) {
665
    updateConfig(&server, CONFIG_TESTDB, true);
666 667 668 669

    // 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.
670
    createDataFromFile("examplequery_fromWire");
671 672
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
673 674 675 676 677
    headerCheck(parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}

TEST_F(AuthSrvTest, datasourceFail) {
678
    updateConfig(&server, CONFIG_TESTDB, true);
679 680 681 682 683

    // 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.
684
    createDataFromFile("badExampleQuery_fromWire");
685 686
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
687 688 689
    headerCheck(parse_message, default_qid, Rcode::SERVFAIL(), opcode.getCode(),
                QR_FLAG, 1, 0, 0, 0);
}
690 691 692 693 694 695 696 697 698

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.
699
    createDataFromFile("examplequery_fromWire");
700 701
    EXPECT_EQ(true, server.processMessage(*io_message, parse_message,
                                          response_renderer));
702 703 704
    headerCheck(parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}
705
}