auth_srv_unittest.cc 42.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// 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.

15
#include <config.h>
16 17 18

#include <vector>

19 20
#include <boost/shared_ptr.hpp>

21 22 23 24 25
#include <gtest/gtest.h>

#include <dns/message.h>
#include <dns/messagerenderer.h>
#include <dns/name.h>
26
#include <dns/opcode.h>
27 28 29 30
#include <dns/rrclass.h>
#include <dns/rrtype.h>
#include <dns/rrttl.h>
#include <dns/rdataclass.h>
31
#include <dns/tsig.h>
32

33
#include <server_common/portconfig.h>
34
#include <server_common/keyring.h>
35

JINMEI Tatuya's avatar
JINMEI Tatuya committed
36
#include <datasrc/memory_datasrc.h>
37
#include <auth/auth_srv.h>
38
#include <auth/common.h>
39
#include <auth/statistics.h>
40

41
#include <dns/tests/unittest_util.h>
JINMEI Tatuya's avatar
JINMEI Tatuya committed
42
#include <testutils/dnsmessage_test.h>
43
#include <testutils/srv_test.h>
44
#include <testutils/portconfig.h>
45
#include <testutils/socket_request.h>
46

JINMEI Tatuya's avatar
JINMEI Tatuya committed
47
using namespace std;
48
using namespace isc::cc;
49
using namespace isc::dns;
50
using namespace isc::util;
51
using namespace isc::dns::rdata;
52
using namespace isc::data;
53
using namespace isc::xfr;
54 55
using namespace isc::asiodns;
using namespace isc::asiolink;
56
using namespace isc::testutils;
57
using namespace isc::server_common::portconfig;
Evan Hunt's avatar
Evan Hunt committed
58
using isc::UnitTestUtil;
59 60

namespace {
61
const char* const CONFIG_TESTDB =
62
    "{\"database_file\": \"" TEST_DATA_DIR "/example.sqlite3\"}";
63 64
// The following file must be non existent and must be non"creatable" (see
// the sqlite3 test).
65
const char* const BADCONFIG_TESTDB =
66
    "{ \"database_file\": \"" TEST_DATA_DIR "/nodir/notexist\"}";
67

68
class AuthSrvTest : public SrvTestBase {
69
protected:
70 71 72
    AuthSrvTest() :
        dnss_(ios_, NULL, NULL, NULL),
        server(true, xfrout),
73
        rrclass(RRClass::IN()),
Michal 'vorner' Vaner's avatar
Michal 'vorner' Vaner committed
74 75 76
        // The empty string is expected value of the parameter of
        // requestSocket, not the app_name (there's no fallback, it checks
        // the empty string is passed).
77
        sock_requestor_(dnss_, address_store_, 53210, "")
78 79
    {
        server.setDNSService(dnss_);
80
        server.setXfrinSession(&notify_session);
81
        server.setStatisticsSession(&statistics_session);
82
    }
83 84 85 86
    virtual void processMessage() {
        server.processMessage(*io_message, parse_message, response_obuffer,
                              &dnsserv);
    }
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

    // Helper for checking Rcode statistic counters
    void checkRcodeCounter(const Rcode& rcode, int expected_value) {
        EXPECT_EQ(expected_value, server.getCounter(rcode)) <<
                  "Expected Rcode count for " << rcode.toText() <<
                  " " << expected_value << ", was: " <<
                  server.getCounter(rcode);
    }
    // Checks whether all Rcode counters are set to zero
    void checkAllRcodeCountersZero() {
        for (int i = 0; i < 17; i++) {
            checkRcodeCounter(Rcode(i), 0);
        }
    }
    // Checks whether all Rcode counters are set to zero except the given
    // rcode (it is checked to be set to 'value')
    void checkAllRcodeCountersZeroExcept(const Rcode& rcode, int value) {
        for (int i = 0; i < 17; i++) {
            const Rcode rc(i);
            if (rc == rcode) {
                checkRcodeCounter(Rcode(i), value);
            } else {
                checkRcodeCounter(Rcode(i), 0);
            }
        }
    }
113 114
    IOService ios_;
    DNSService dnss_;
115
    MockSession statistics_session;
116
    MockXfroutClient xfrout;
117
    AuthSrv server;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
118
    const RRClass rrclass;
119
    vector<uint8_t> response_data;
120
    AddressList address_store_;
121
    TestSocketRequestor sock_requestor_;
122 123
};

124 125 126 127 128 129 130 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 160 161 162 163 164 165 166 167 168 169 170 171 172 173
// A helper function that builds a response to version.bind/TXT/CH that
// should be identical to the response from our builtin (static) data source
// by default.  The resulting wire-format data will be stored in 'data'.
void
createBuiltinVersionResponse(const qid_t qid, vector<uint8_t>& data) {
    const Name version_name("version.bind");
    Message message(Message::RENDER);

    UnitTestUtil::createRequestMessage(message, Opcode::QUERY(),
                                       qid, version_name,
                                       RRClass::CH(), RRType::TXT());
    message.setHeaderFlag(Message::HEADERFLAG_QR);
    message.setHeaderFlag(Message::HEADERFLAG_AA);
    RRsetPtr rrset_version = RRsetPtr(new RRset(version_name, RRClass::CH(),
                                                RRType::TXT(), RRTTL(0)));
    rrset_version->addRdata(generic::TXT(PACKAGE_STRING));
    message.addRRset(Message::SECTION_ANSWER, rrset_version);

    RRsetPtr rrset_version_ns = RRsetPtr(new RRset(version_name, RRClass::CH(),
                                                   RRType::NS(), RRTTL(0)));
    rrset_version_ns->addRdata(generic::NS(version_name));
    message.addRRset(Message::SECTION_AUTHORITY, rrset_version_ns);

    OutputBuffer obuffer(0);
    MessageRenderer renderer(obuffer);
    message.toWire(renderer);

    data.clear();
    data.assign(static_cast<const uint8_t*>(renderer.getData()),
                static_cast<const uint8_t*>(renderer.getData()) +
                renderer.getLength());
}

// In the following tests we confirm the response data is rendered in
// wire format in the expected way.

// The most primitive check: checking the result of the processMessage()
// method
TEST_F(AuthSrvTest, builtInQuery) {
    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                       default_qid, Name("version.bind"),
                                       RRClass::CH(), RRType::TXT());
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer,
                          &dnsserv);
    createBuiltinVersionResponse(default_qid, response_data);
    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
                        response_obuffer->getData(),
                        response_obuffer->getLength(),
                        &response_data[0], response_data.size());
174
    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
}

// Same test emulating the UDPServer class behavior (defined in libasiolink).
// This is not a good test in that it assumes internal implementation details
// of UDPServer, but we've encountered a regression due to the introduction
// of that class, so we add a test for that case to prevent such a regression
// in future.
// Besides, the generalization of UDPServer is probably too much for the
// authoritative only server in terms of performance, and it's quite likely
// we need to drop it for the authoritative server implementation.
// At that point we can drop this test, too.
TEST_F(AuthSrvTest, builtInQueryViaDNSServer) {
    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                       default_qid, Name("version.bind"),
                                       RRClass::CH(), RRType::TXT());
    createRequestPacket(request_message, IPPROTO_UDP);

    (*server.getDNSLookupProvider())(*io_message, parse_message,
193
                                     response_message,
194 195
                                     response_obuffer, &dnsserv);
    (*server.getDNSAnswerProvider())(*io_message, parse_message,
196
                                     response_message, response_obuffer);
197 198 199 200 201 202 203 204 205 206 207 208

    createBuiltinVersionResponse(default_qid, response_data);
    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
                        response_obuffer->getData(),
                        response_obuffer->getLength(),
                        &response_data[0], response_data.size());
}

// Same type of test as builtInQueryViaDNSServer but for an error response.
TEST_F(AuthSrvTest, iqueryViaDNSServer) {
    createDataFromFile("iquery_fromWire.wire");
    (*server.getDNSLookupProvider())(*io_message, parse_message,
209
                                     response_message,
210 211
                                     response_obuffer, &dnsserv);
    (*server.getDNSAnswerProvider())(*io_message, parse_message,
212
                                     response_message, response_obuffer);
213 214 215 216 217 218 219 220 221

    UnitTestUtil::readWireData("iquery_response_fromWire.wire",
                               response_data);
    EXPECT_PRED_FORMAT4(UnitTestUtil::matchWireData,
                        response_obuffer->getData(),
                        response_obuffer->getLength(),
                        &response_data[0], response_data.size());
}

222 223
// Unsupported requests.  Should result in NOTIMP.
TEST_F(AuthSrvTest, unsupportedRequest) {
224
    unsupportedRequest();
225 226
    // unsupportedRequest tries 14 different opcodes
    checkAllRcodeCountersZeroExcept(Rcode::NOTIMP(), 14);
227
}
228 229 230

// Multiple questions.  Should result in FORMERR.
TEST_F(AuthSrvTest, multiQuestion) {
231
    multiQuestion();
232
    checkAllRcodeCountersZeroExcept(Rcode::FORMERR(), 1);
233 234
}

235 236 237
// Incoming data doesn't even contain the complete header.  Must be silently
// dropped.
TEST_F(AuthSrvTest, shortMessage) {
238
    shortMessage();
239
    checkAllRcodeCountersZero();
240 241 242 243 244
}

// 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) {
245
    response();
246
    checkAllRcodeCountersZero();
247 248 249 250
}

// Query with a broken question
TEST_F(AuthSrvTest, shortQuestion) {
251
    shortQuestion();
252
    checkAllRcodeCountersZeroExcept(Rcode::FORMERR(), 1);
253
}
254

255 256
// Query with a broken answer section
TEST_F(AuthSrvTest, shortAnswer) {
257
    shortAnswer();
258
    checkAllRcodeCountersZeroExcept(Rcode::FORMERR(), 1);
259 260
}

261 262
// Query with unsupported version of EDNS.
TEST_F(AuthSrvTest, ednsBadVers) {
263
    ednsBadVers();
264
    checkAllRcodeCountersZeroExcept(Rcode::BADVERS(), 1);
265 266
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
267
TEST_F(AuthSrvTest, AXFROverUDP) {
268
    axfrOverUDP();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
269 270
}

271 272
TEST_F(AuthSrvTest, AXFRSuccess) {
    EXPECT_FALSE(xfrout.isConnected());
Evan Hunt's avatar
Evan Hunt committed
273
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
274 275
                                       Name("example.com"), RRClass::IN(),
                                       RRType::AXFR());
Evan Hunt's avatar
Evan Hunt committed
276
    createRequestPacket(request_message, IPPROTO_TCP);
277 278
    // On success, the AXFR query has been passed to a separate process,
    // so we shouldn't have to respond.
Evan Hunt's avatar
Evan Hunt committed
279 280
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
281
    EXPECT_TRUE(xfrout.isConnected());
282
    checkAllRcodeCountersZero();
283 284
}

285 286 287 288
// Try giving the server a TSIG signed request and see it can anwer signed as
// well
TEST_F(AuthSrvTest, TSIGSigned) {
    // Prepare key, the client message, etc
289
    const TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
290 291
    TSIGContext context(key);
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
292 293
                                       Name("version.bind"), RRClass::CH(),
                                       RRType::TXT());
294 295
    createRequestPacket(request_message, IPPROTO_UDP, &context);

296
    // Run the message through the server
297
    boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
298 299
    keyring->add(key);
    server.setTSIGKeyRing(&keyring);
300 301 302 303 304 305 306 307 308 309
    server.processMessage(*io_message, parse_message, response_obuffer,
                          &dnsserv);

    // What did we get?
    EXPECT_TRUE(dnsserv.hasAnswer());
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
                opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
    // We need to parse the message ourself, or getTSIGRecord won't work
    InputBuffer ib(response_obuffer->getData(), response_obuffer->getLength());
    Message m(Message::PARSE);
310
    m.fromWire(ib);
311 312

    const TSIGRecord* tsig = m.getTSIGRecord();
313
    ASSERT_TRUE(tsig != NULL) << "Missing TSIG signature";
314 315 316 317
    TSIGError error(context.verify(tsig, response_obuffer->getData(),
                                   response_obuffer->getLength()));
    EXPECT_EQ(TSIGError::NOERROR(), error) <<
        "The server signed the response, but it doesn't seem to be valid";
318 319

    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
320 321 322 323
}

// Give the server a signed request, but don't give it the key. It will
// not be able to verify it, returning BADKEY
324
TEST_F(AuthSrvTest, TSIGSignedBadKey) {
325 326 327
    TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
    TSIGContext context(key);
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
328 329
                                       Name("version.bind"), RRClass::CH(),
                                       RRType::TXT());
330 331 332
    createRequestPacket(request_message, IPPROTO_UDP, &context);

    // Process the message, but use a different key there
333
    boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
334
    server.setTSIGKeyRing(&keyring);
335 336 337 338 339 340 341 342 343
    server.processMessage(*io_message, parse_message, response_obuffer,
                          &dnsserv);

    EXPECT_TRUE(dnsserv.hasAnswer());
    headerCheck(*parse_message, default_qid, TSIGError::BAD_KEY().toRcode(),
                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
    // We need to parse the message ourself, or getTSIGRecord won't work
    InputBuffer ib(response_obuffer->getData(), response_obuffer->getLength());
    Message m(Message::PARSE);
344
    m.fromWire(ib);
345 346

    const TSIGRecord* tsig = m.getTSIGRecord();
347
    ASSERT_TRUE(tsig != NULL) <<
348
        "Missing TSIG signature (we should have one even at error)";
349
    EXPECT_EQ(TSIGError::BAD_KEY_CODE, tsig->getRdata().getError());
350 351
    EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
        "It should be unsigned with this error";
352 353

    checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
354 355 356 357 358 359 360 361
}

// Give the server a signed request, but signed by a different key
// (with the same name). It should return BADSIG
TEST_F(AuthSrvTest, TSIGBadSig) {
    TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
    TSIGContext context(key);
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
362 363
                                       Name("version.bind"), RRClass::CH(),
                                       RRType::TXT());
364 365
    createRequestPacket(request_message, IPPROTO_UDP, &context);

366
    // Process the message, but use a different key there
367
    boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
368 369
    keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
    server.setTSIGKeyRing(&keyring);
370 371 372 373
    server.processMessage(*io_message, parse_message, response_obuffer,
                          &dnsserv);

    EXPECT_TRUE(dnsserv.hasAnswer());
374
    headerCheck(*parse_message, default_qid, TSIGError::BAD_SIG().toRcode(),
375 376 377 378
                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
    // We need to parse the message ourself, or getTSIGRecord won't work
    InputBuffer ib(response_obuffer->getData(), response_obuffer->getLength());
    Message m(Message::PARSE);
379
    m.fromWire(ib);
380 381

    const TSIGRecord* tsig = m.getTSIGRecord();
382
    ASSERT_TRUE(tsig != NULL) <<
383
        "Missing TSIG signature (we should have one even at error)";
384
    EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig->getRdata().getError());
385 386
    EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
        "It should be unsigned with this error";
387 388

    checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
389 390
}

391 392 393 394 395 396 397 398 399 400 401 402 403
// Give the server a signed unsupported request with a bad signature.
// This checks the server first verifies the signature before anything
// else.
TEST_F(AuthSrvTest, TSIGCheckFirst) {
    TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
    TSIGContext context(key);
    // Pass a wrong opcode there. The server shouldn't know what to do
    // about it.
    UnitTestUtil::createRequestMessage(request_message, Opcode::RESERVED14(),
                                       default_qid, Name("version.bind"),
                                       RRClass::CH(), RRType::TXT());
    createRequestPacket(request_message, IPPROTO_UDP, &context);

404
    // Process the message, but use a different key there
405
    boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
406 407
    keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
    server.setTSIGKeyRing(&keyring);
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
    server.processMessage(*io_message, parse_message, response_obuffer,
                          &dnsserv);

    EXPECT_TRUE(dnsserv.hasAnswer());
    headerCheck(*parse_message, default_qid, TSIGError::BAD_SIG().toRcode(),
                Opcode::RESERVED14().getCode(), QR_FLAG, 0, 0, 0, 0);
    // We need to parse the message ourself, or getTSIGRecord won't work
    InputBuffer ib(response_obuffer->getData(), response_obuffer->getLength());
    Message m(Message::PARSE);
    m.fromWire(ib);

    const TSIGRecord* tsig = m.getTSIGRecord();
    ASSERT_TRUE(tsig != NULL) <<
        "Missing TSIG signature (we should have one even at error)";
    EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig->getRdata().getError());
    EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
        "It should be unsigned with this error";
425 426 427
    // TSIG should have failed, and so the per opcode counter shouldn't be
    // incremented.
    EXPECT_EQ(0, server.getCounter(Opcode::RESERVED14()));
428 429

    checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
430 431
}

432 433 434
TEST_F(AuthSrvTest, AXFRConnectFail) {
    EXPECT_FALSE(xfrout.isConnected()); // check prerequisite
    xfrout.disableConnect();
Evan Hunt's avatar
Evan Hunt committed
435
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
436 437
                                       Name("example.com"), RRClass::IN(),
                                       RRType::AXFR());
Evan Hunt's avatar
Evan Hunt committed
438 439 440
    createRequestPacket(request_message, IPPROTO_TCP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
441
    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
442 443 444 445 446 447 448
                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.
Evan Hunt's avatar
Evan Hunt committed
449
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
450 451
                                       Name("example.com"), RRClass::IN(),
                                       RRType::AXFR());
Evan Hunt's avatar
Evan Hunt committed
452 453
    createRequestPacket(request_message, IPPROTO_TCP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
454
    EXPECT_TRUE(xfrout.isConnected());
455 456

    xfrout.disableSend();
457 458
    parse_message->clear(Message::PARSE);
    response_obuffer->clear();
Evan Hunt's avatar
Evan Hunt committed
459
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
460 461
                                       Name("example.com"), RRClass::IN(),
                                       RRType::AXFR());
Evan Hunt's avatar
Evan Hunt committed
462 463 464
    createRequestPacket(request_message, IPPROTO_TCP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
465
    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
466 467 468 469 470 471 472 473 474 475 476
                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();
Evan Hunt's avatar
Evan Hunt committed
477
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
478 479
                                       Name("example.com"), RRClass::IN(),
                                       RRType::AXFR());
Evan Hunt's avatar
Evan Hunt committed
480
    createRequestPacket(request_message, IPPROTO_TCP);
481
    EXPECT_THROW(server.processMessage(*io_message, parse_message,
Evan Hunt's avatar
Evan Hunt committed
482
                                       response_obuffer, &dnsserv),
483 484 485 486 487 488 489
                 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();
}

490 491 492 493
TEST_F(AuthSrvTest, IXFRConnectFail) {
    EXPECT_FALSE(xfrout.isConnected()); // check prerequisite
    xfrout.disableConnect();
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
494 495
                                       Name("example.com"), RRClass::IN(),
                                       RRType::IXFR());
496 497 498 499 500 501 502 503 504 505 506 507
    createRequestPacket(request_message, IPPROTO_TCP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
    EXPECT_FALSE(xfrout.isConnected());
}

TEST_F(AuthSrvTest, IXFRSendFail) {
    // first send a valid query, making the connection with the xfr process
    // open.
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
508 509
                                       Name("example.com"), RRClass::IN(),
                                       RRType::IXFR());
510 511 512 513 514 515 516 517
    createRequestPacket(request_message, IPPROTO_TCP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(xfrout.isConnected());

    xfrout.disableSend();
    parse_message->clear(Message::PARSE);
    response_obuffer->clear();
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
518 519
                                       Name("example.com"), RRClass::IN(),
                                       RRType::IXFR());
520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
    createRequestPacket(request_message, IPPROTO_TCP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
    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, IXFRDisconnectFail) {
    // In our usage disconnect() shouldn't fail.  So we'll see the exception
    // should it be thrown.
    xfrout.disableSend();
    xfrout.disableDisconnect();
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
536 537
                                       Name("example.com"), RRClass::IN(),
                                       RRType::IXFR());
538 539 540 541 542 543 544 545 546 547
    createRequestPacket(request_message, IPPROTO_TCP);
    EXPECT_THROW(server.processMessage(*io_message, parse_message,
                                       response_obuffer, &dnsserv),
                 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();
}

548
TEST_F(AuthSrvTest, notify) {
Jelte Jansen's avatar
Jelte Jansen committed
549 550 551
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
552
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
553 554 555
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
556 557 558

    // An internal command message should have been created and sent to an
    // external module.  Check them.
Evan Hunt's avatar
Evan Hunt committed
559
    EXPECT_EQ("Zonemgr", notify_session.getMessageDest());
560
    EXPECT_EQ("notify",
Evan Hunt's avatar
Evan Hunt committed
561
              notify_session.getSentMessage()->get("command")->get(0)->stringValue());
562
    ConstElementPtr notify_args =
Evan Hunt's avatar
Evan Hunt committed
563
        notify_session.getSentMessage()->get("command")->get(1);
564 565 566
    EXPECT_EQ("example.com.", notify_args->get("zone_name")->stringValue());
    EXPECT_EQ(DEFAULT_REMOTE_ADDRESS,
              notify_args->get("master")->stringValue());
567
    EXPECT_EQ("IN", notify_args->get("zone_class")->stringValue());
568 569

    // On success, the server should return a response to the notify.
570
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
571 572
                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);

573
    // The question must be identical to that of the received notify
574
    ConstQuestionPtr question = *parse_message->beginQuestion();
575 576 577
    EXPECT_EQ(Name("example.com"), question->getName());
    EXPECT_EQ(RRClass::IN(), question->getClass());
    EXPECT_EQ(RRType::SOA(), question->getType());
578 579

    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
580 581
}

582 583
TEST_F(AuthSrvTest, notifyForCHClass) {
    // Same as the previous test, but for the CH RRClass.
Jelte Jansen's avatar
Jelte Jansen committed
584 585 586
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::CH(), RRType::SOA());
587
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
588 589 590
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
591 592 593

    // Other conditions should be the same, so simply confirm the RR class is
    // set correctly.
594
    ConstElementPtr notify_args =
Evan Hunt's avatar
Evan Hunt committed
595
        notify_session.getSentMessage()->get("command")->get(1);
596
    EXPECT_EQ("CH", notify_args->get("zone_class")->stringValue());
597 598
}

599 600 601
TEST_F(AuthSrvTest, notifyEmptyQuestion) {
    request_message.clear(Message::RENDER);
    request_message.setOpcode(Opcode::NOTIFY());
602
    request_message.setRcode(Rcode::NOERROR());
603
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
604 605
    request_message.setQid(default_qid);
    request_message.toWire(request_renderer);
Evan Hunt's avatar
Evan Hunt committed
606 607 608
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
609
    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
610 611 612 613
                Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyMultiQuestions) {
Jelte Jansen's avatar
Jelte Jansen committed
614 615 616
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
617 618 619
    // add one more SOA question
    request_message.addQuestion(Question(Name("example.com"), RRClass::IN(),
                                         RRType::SOA()));
620
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
621 622 623
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
624
    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
625 626 627 628
                Opcode::NOTIFY().getCode(), QR_FLAG, 2, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyNonSOAQuestion) {
Jelte Jansen's avatar
Jelte Jansen committed
629 630 631
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::NS());
632
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
633 634 635
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
636
    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
637 638 639 640 641
                Opcode::NOTIFY().getCode(), QR_FLAG, 1, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyWithoutAA) {
    // implicitly leave the AA bit off.  our implementation will accept it.
Jelte Jansen's avatar
Jelte Jansen committed
642 643 644
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
Evan Hunt's avatar
Evan Hunt committed
645 646 647
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
648
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
649 650 651 652
                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyWithErrorRcode) {
Jelte Jansen's avatar
Jelte Jansen committed
653 654 655
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
656
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
657
    request_message.setRcode(Rcode::SERVFAIL());
Evan Hunt's avatar
Evan Hunt committed
658 659 660
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
661
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
662
                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
Han Feng's avatar
Han Feng committed
663 664
}

665
TEST_F(AuthSrvTest, notifyWithoutSession) {
666
    server.setXfrinSession(NULL);
667

Jelte Jansen's avatar
Jelte Jansen committed
668 669 670
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
671
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
672
    createRequestPacket(request_message, IPPROTO_UDP);
673 674 675

    // we simply ignore the notify and let it be resent if an internal error
    // happens.
Evan Hunt's avatar
Evan Hunt committed
676 677
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
678 679 680 681 682
}

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

Jelte Jansen's avatar
Jelte Jansen committed
683 684 685
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
686
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
687
    createRequestPacket(request_message, IPPROTO_UDP);
688

Evan Hunt's avatar
Evan Hunt committed
689 690
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
691 692 693 694 695
}

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

Jelte Jansen's avatar
Jelte Jansen committed
696 697 698
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
699
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
700 701 702
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
703 704 705
}

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

Jelte Jansen's avatar
Jelte Jansen committed
708 709 710
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
711
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
712 713 714
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
715 716 717 718
}

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

Jelte Jansen's avatar
Jelte Jansen committed
721 722 723
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
724
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
725 726 727
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
728 729
}

730
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
731
updateConfig(AuthSrv* server, const char* const config_data,
732 733
             const bool expect_success)
{
734
    ConstElementPtr config_answer =
JINMEI Tatuya's avatar
JINMEI Tatuya committed
735
        server->updateConfig(Element::fromJSON(config_data));
736 737 738
    EXPECT_EQ(Element::map, config_answer->getType());
    EXPECT_TRUE(config_answer->contains("result"));

739
    ConstElementPtr result = config_answer->get("result");
740
    EXPECT_EQ(Element::list, result->getType());
741
    EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue());
742 743 744 745
}

// Install a Sqlite3 data source with testing data.
TEST_F(AuthSrvTest, updateConfig) {
746
    updateConfig(&server, CONFIG_TESTDB, true);
747 748 749 750

    // 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.
751
    createDataFromFile("examplequery_fromWire.wire");
Evan Hunt's avatar
Evan Hunt committed
752 753
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
754
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
755 756 757 758
                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}

TEST_F(AuthSrvTest, datasourceFail) {
759
    updateConfig(&server, CONFIG_TESTDB, true);
760 761 762 763 764

    // 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.
765
    createDataFromFile("badExampleQuery_fromWire.wire");
Evan Hunt's avatar
Evan Hunt committed
766 767
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
768
    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
769
                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
770
}
771 772 773 774 775 776 777 778 779

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.
780
    createDataFromFile("examplequery_fromWire.wire");
Evan Hunt's avatar
Evan Hunt committed
781 782
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
783
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
784 785
                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
786

787
TEST_F(AuthSrvTest, updateWithInMemoryClient) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
788 789 790 791
    // Test configuring memory data source.  Detailed test cases are covered
    // in the configuration tests.  We only check the AuthSrv interface here.

    // By default memory data source isn't enabled
792
    EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
JINMEI Tatuya's avatar
JINMEI Tatuya committed
793 794 795
    updateConfig(&server,
                 "{\"datasources\": [{\"type\": \"memory\"}]}", true);
    // after successful configuration, we should have one (with empty zoneset).
796 797
    ASSERT_NE(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
    EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount());
Jerry's avatar
Jerry committed
798

799
    // The memory data source is empty, should return REFUSED rcode.
Jerry's avatar
Jerry committed
800
    createDataFromFile("examplequery_fromWire.wire");
801 802 803 804 805 806 807
    server.processMessage(*io_message, parse_message, response_obuffer,
                          &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
    headerCheck(*parse_message, default_qid, Rcode::REFUSED(),
                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
}

808
TEST_F(AuthSrvTest, chQueryWithInMemoryClient) {
809 810 811 812 813 814 815 816 817 818 819
    // Configure memory data source for class IN
    updateConfig(&server, "{\"datasources\": "
                 "[{\"class\": \"IN\", \"type\": \"memory\"}]}", true);

    // This shouldn't affect the result of class CH query
    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                       default_qid, Name("version.bind"),
                                       RRClass::CH(), RRType::TXT());
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer,
                          &dnsserv);
820
    EXPECT_TRUE(dnsserv.hasAnswer());
821 822
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
                opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
823 824
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
825 826 827 828 829 830 831 832 833
TEST_F(AuthSrvTest, cacheSlots) {
    // simple check for the get/set operations
    server.setCacheSlots(10);    // 10 = arbitrary choice
    EXPECT_EQ(10, server.getCacheSlots());

    // 0 is a valid size
    server.setCacheSlots(0);
    EXPECT_EQ(00, server.getCacheSlots());
}
834

835 836 837
// Submit UDP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterUDPNormal) {
    // The counter should be initialized to 0.
838
    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
839 840 841 842 843 844 845
    // Create UDP message and process.
    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::NS());
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer,
                          &dnsserv);
846
    // After processing UDP query, the counter should be 1.
847
    EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
848 849 850 851
    // The counter for SUCCESS responses should also be one
    EXPECT_EQ(1, server.getCounter(Opcode::QUERY()));
    // The counter for REFUSED responses should also be one, the rest zero
    checkAllRcodeCountersZeroExcept(Rcode::REFUSED(), 1);
852 853
}

854 855 856
// Submit TCP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPNormal) {
    // The counter should be initialized to 0.
857
    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
858 859 860 861 862 863 864
    // Create TCP message and process.
    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::NS());
    createRequestPacket(request_message, IPPROTO_TCP);
    server.processMessage(*io_message, parse_message, response_obuffer,
                          &dnsserv);
865
    // After processing TCP query, the counter should be 1.
866
    EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
867 868 869 870
    // The counter for SUCCESS responses should also be one
    EXPECT_EQ(1, server.getCounter(Opcode::QUERY()));
    // The counter for REFUSED responses should also be one, the rest zero
    checkAllRcodeCountersZeroExcept(Rcode::REFUSED(), 1);
871 872 873 874 875
}

// Submit TCP AXFR query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
    // The counter should be initialized to 0.
876
    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
877 878 879 880
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                         Name("example.com"), RRClass::IN(), RRType::AXFR());
    createRequestPacket(request_message, IPPROTO_TCP);
    // On success, the AXFR query has been passed to a separate process,
Jelte Jansen's avatar
Jelte Jansen committed
881
    // so auth itself shouldn't respond.
882
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
Jelte Jansen's avatar
Jelte Jansen committed
883
    EXPECT_FALSE(dnsserv.hasAnswer());
884
    // After processing TCP AXFR query, the counter should be 1.
885
    EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
886 887
    // No rcodes should be incremented
    checkAllRcodeCountersZero();