auth_srv_unittest.cc 42.7 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
    // Helper for checking Rcode statistic counters;
    // Checks for one specific Rcode statistics counter value
90 91 92 93 94 95
    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);
    }
96

97 98 99 100 101 102
    // Checks whether all Rcode counters are set to zero
    void checkAllRcodeCountersZero() {
        for (int i = 0; i < 17; i++) {
            checkRcodeCounter(Rcode(i), 0);
        }
    }
103

104 105 106 107 108 109 110 111 112 113 114 115
    // 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);
            }
        }
    }
116 117
    IOService ios_;
    DNSService dnss_;
118
    MockSession statistics_session;
119
    MockXfroutClient xfrout;
120
    AuthSrv server;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
121
    const RRClass rrclass;
122
    vector<uint8_t> response_data;
123
    AddressList address_store_;
124
    TestSocketRequestor sock_requestor_;
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 174 175 176
// 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());
177
    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
}

// 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,
196
                                     response_message,
197 198
                                     response_obuffer, &dnsserv);
    (*server.getDNSAnswerProvider())(*io_message, parse_message,
199
                                     response_message, response_obuffer);
200 201 202 203 204 205 206 207 208 209 210 211

    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,
212
                                     response_message,
213 214
                                     response_obuffer, &dnsserv);
    (*server.getDNSAnswerProvider())(*io_message, parse_message,
215
                                     response_message, response_obuffer);
216 217 218 219 220 221 222 223 224

    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());
}

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

// Multiple questions.  Should result in FORMERR.
TEST_F(AuthSrvTest, multiQuestion) {
234
    multiQuestion();
235
    checkAllRcodeCountersZeroExcept(Rcode::FORMERR(), 1);
236 237
}

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

// 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) {
248
    response();
249
    checkAllRcodeCountersZero();
250 251 252 253
}

// Query with a broken question
TEST_F(AuthSrvTest, shortQuestion) {
254
    shortQuestion();
255
    checkAllRcodeCountersZeroExcept(Rcode::FORMERR(), 1);
256
}
257

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

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

JINMEI Tatuya's avatar
JINMEI Tatuya committed
270
TEST_F(AuthSrvTest, AXFROverUDP) {
271
    axfrOverUDP();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
272 273
}

274 275
TEST_F(AuthSrvTest, AXFRSuccess) {
    EXPECT_FALSE(xfrout.isConnected());
Evan Hunt's avatar
Evan Hunt committed
276
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
277 278
                                       Name("example.com"), RRClass::IN(),
                                       RRType::AXFR());
Evan Hunt's avatar
Evan Hunt committed
279
    createRequestPacket(request_message, IPPROTO_TCP);
280 281
    // 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
282 283
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
284
    EXPECT_TRUE(xfrout.isConnected());
285
    checkAllRcodeCountersZero();
286 287
}

288 289 290 291
// 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
292
    const TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
293 294
    TSIGContext context(key);
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
295 296
                                       Name("version.bind"), RRClass::CH(),
                                       RRType::TXT());
297 298
    createRequestPacket(request_message, IPPROTO_UDP, &context);

299
    // Run the message through the server
300
    boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
301 302
    keyring->add(key);
    server.setTSIGKeyRing(&keyring);
303 304 305 306 307 308 309 310 311 312
    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);
313
    m.fromWire(ib);
314 315

    const TSIGRecord* tsig = m.getTSIGRecord();
316
    ASSERT_TRUE(tsig != NULL) << "Missing TSIG signature";
317 318 319 320
    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";
321 322

    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
323 324 325 326
}

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

    // Process the message, but use a different key there
336
    boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
337
    server.setTSIGKeyRing(&keyring);
338 339 340 341 342 343 344 345 346
    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);
347
    m.fromWire(ib);
348 349

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

    checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
357 358 359 360 361 362 363 364
}

// 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
365 366
                                       Name("version.bind"), RRClass::CH(),
                                       RRType::TXT());
367 368
    createRequestPacket(request_message, IPPROTO_UDP, &context);

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

    EXPECT_TRUE(dnsserv.hasAnswer());
377
    headerCheck(*parse_message, default_qid, TSIGError::BAD_SIG().toRcode(),
378 379 380 381
                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);
382
    m.fromWire(ib);
383 384

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

    checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
392 393
}

394 395 396 397 398 399 400 401 402 403 404 405 406
// 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);

407
    // Process the message, but use a different key there
408
    boost::shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
409 410
    keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
    server.setTSIGKeyRing(&keyring);
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427
    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";
428 429 430
    // TSIG should have failed, and so the per opcode counter shouldn't be
    // incremented.
    EXPECT_EQ(0, server.getCounter(Opcode::RESERVED14()));
431 432

    checkAllRcodeCountersZeroExcept(Rcode::NOTAUTH(), 1);
433 434
}

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

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

493 494 495 496
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
497 498
                                       Name("example.com"), RRClass::IN(),
                                       RRType::IXFR());
499 500 501 502 503 504 505 506 507 508 509 510
    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
511 512
                                       Name("example.com"), RRClass::IN(),
                                       RRType::IXFR());
513 514 515 516 517 518 519 520
    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
521 522
                                       Name("example.com"), RRClass::IN(),
                                       RRType::IXFR());
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
    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
539 540
                                       Name("example.com"), RRClass::IN(),
                                       RRType::IXFR());
541 542 543 544 545 546 547 548 549 550
    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();
}

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

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

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

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

    checkAllRcodeCountersZeroExcept(Rcode::NOERROR(), 1);
583 584
}

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

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

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

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

TEST_F(AuthSrvTest, notifyNonSOAQuestion) {
Jelte Jansen's avatar
Jelte Jansen committed
632 633 634
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::NS());
635
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
636 637 638
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
639
    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
640 641 642 643 644
                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
645 646 647
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
Evan Hunt's avatar
Evan Hunt committed
648 649 650
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
651
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
652 653 654 655
                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
}

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

668
TEST_F(AuthSrvTest, notifyWithoutSession) {
669
    server.setXfrinSession(NULL);
670

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

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

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

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

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

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

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

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

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

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

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

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

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

// Install a Sqlite3 data source with testing data.
TEST_F(AuthSrvTest, updateConfig) {
749
    updateConfig(&server, CONFIG_TESTDB, true);
750 751 752 753

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

TEST_F(AuthSrvTest, datasourceFail) {
762
    updateConfig(&server, CONFIG_TESTDB, true);
763 764 765 766 767

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

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

790
TEST_F(AuthSrvTest, updateWithInMemoryClient) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
791 792 793 794
    // 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
795
    EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
JINMEI Tatuya's avatar
JINMEI Tatuya committed
796 797 798
    updateConfig(&server,
                 "{\"datasources\": [{\"type\": \"memory\"}]}", true);
    // after successful configuration, we should have one (with empty zoneset).
799 800
    ASSERT_NE(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
    EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount());
Jerry's avatar
Jerry committed
801

802
    // The memory data source is empty, should return REFUSED rcode.
Jerry's avatar
Jerry committed
803
    createDataFromFile("examplequery_fromWire.wire");
804 805 806 807 808 809 810
    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);
}

811
TEST_F(AuthSrvTest, chQueryWithInMemoryClient) {
812 813 814 815 816 817 818 819 820 821 822
    // 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);
823
    EXPECT_TRUE(dnsserv.hasAnswer());
824 825
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
                opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
826 827
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
828 829 830 831 832 833 834 835 836
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());
}
837

838 839 840
// Submit UDP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterUDPNormal) {
    // The counter should be initialized to 0.
841
    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
842 843 844 845 846 847 848
    // 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);
849
    // After processing UDP query, the counter should be 1.
850
    EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
Jelte Jansen's avatar
Jelte Jansen committed
851
    // The counter for opcode Query should also be one
852 853 854
    EXPECT_EQ(1, server.getCounter(Opcode::QUERY()));
    // The counter for REFUSED responses should also be one, the rest zero
    checkAllRcodeCountersZeroExcept(Rcode::REFUSED(), 1);
855 856
}

857 858 859
// Submit TCP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPNormal) {
    // The counter should be initialized to 0.
860
    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
861 862 863 864 865 866 867
    // 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);
868
    // After processing TCP query, the counter should be 1.
869
    EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
870 871 872 873
    // 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);
874 875 876 877 878
}

// Submit TCP AXFR query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
    // The counter should be initialized to 0.
879
    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
880 881 882 883
    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
884
    // so auth itself shouldn't respond.