auth_srv_unittest.cc 37 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 26 27 28 29
#include <gtest/gtest.h>

#include <dns/message.h>
#include <dns/messagerenderer.h>
#include <dns/name.h>
#include <dns/rrclass.h>
#include <dns/rrtype.h>
#include <dns/rrttl.h>
#include <dns/rdataclass.h>
30
#include <dns/tsig.h>
31

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

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

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

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

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

67
class AuthSrvTest : public SrvTestBase {
68
protected:
69 70 71 72 73 74
    AuthSrvTest() :
        dnss_(ios_, NULL, NULL, NULL),
        server(true, xfrout),
        rrclass(RRClass::IN())
    {
        server.setDNSService(dnss_);
75
        server.setXfrinSession(&notify_session);
76
        server.setStatisticsSession(&statistics_session);
77
    }
78 79 80 81
    virtual void processMessage() {
        server.processMessage(*io_message, parse_message, response_obuffer,
                              &dnsserv);
    }
82 83
    IOService ios_;
    DNSService dnss_;
84
    MockSession statistics_session;
85
    MockXfroutClient xfrout;
86
    AuthSrv server;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
87
    const RRClass rrclass;
88
    vector<uint8_t> response_data;
89 90
};

91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 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
// 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());
}

// 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,
159
                                     response_message,
160 161
                                     response_obuffer, &dnsserv);
    (*server.getDNSAnswerProvider())(*io_message, parse_message,
162
                                     response_message, response_obuffer);
163 164 165 166 167 168 169 170 171 172 173 174

    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,
175
                                     response_message,
176 177
                                     response_obuffer, &dnsserv);
    (*server.getDNSAnswerProvider())(*io_message, parse_message,
178
                                     response_message, response_obuffer);
179 180 181 182 183 184 185 186 187

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

188 189
// Unsupported requests.  Should result in NOTIMP.
TEST_F(AuthSrvTest, unsupportedRequest) {
190
    unsupportedRequest();
191
}
192 193 194

// Multiple questions.  Should result in FORMERR.
TEST_F(AuthSrvTest, multiQuestion) {
195
    multiQuestion();
196 197
}

198 199 200
// Incoming data doesn't even contain the complete header.  Must be silently
// dropped.
TEST_F(AuthSrvTest, shortMessage) {
201
    shortMessage();
202 203 204 205 206
}

// 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) {
207
    response();
208 209 210 211
}

// Query with a broken question
TEST_F(AuthSrvTest, shortQuestion) {
212
    shortQuestion();
213
}
214

215 216
// Query with a broken answer section
TEST_F(AuthSrvTest, shortAnswer) {
217
    shortAnswer();
218 219
}

220 221
// Query with unsupported version of EDNS.
TEST_F(AuthSrvTest, ednsBadVers) {
222
    ednsBadVers();
223 224
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
225
TEST_F(AuthSrvTest, AXFROverUDP) {
226
    axfrOverUDP();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
227 228
}

229 230
TEST_F(AuthSrvTest, AXFRSuccess) {
    EXPECT_FALSE(xfrout.isConnected());
Evan Hunt's avatar
Evan Hunt committed
231 232 233
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                         Name("example.com"), RRClass::IN(), RRType::AXFR());
    createRequestPacket(request_message, IPPROTO_TCP);
234 235
    // 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
236 237
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
238
    EXPECT_TRUE(xfrout.isConnected());
239 240
}

241 242 243 244
// 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
245
    const TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
246 247 248 249 250
    TSIGContext context(key);
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                         Name("version.bind"), RRClass::CH(), RRType::TXT());
    createRequestPacket(request_message, IPPROTO_UDP, &context);

251
    // Run the message through the server
252 253 254
    shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
    keyring->add(key);
    server.setTSIGKeyRing(&keyring);
255 256 257 258 259 260 261 262 263 264
    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);
265
    m.fromWire(ib);
266 267

    const TSIGRecord* tsig = m.getTSIGRecord();
268
    ASSERT_TRUE(tsig != NULL) << "Missing TSIG signature";
269 270 271 272 273 274 275 276
    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";
}

// Give the server a signed request, but don't give it the key. It will
// not be able to verify it, returning BADKEY
277
TEST_F(AuthSrvTest, TSIGSignedBadKey) {
278 279 280 281 282 283 284
    TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
    TSIGContext context(key);
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                         Name("version.bind"), RRClass::CH(), RRType::TXT());
    createRequestPacket(request_message, IPPROTO_UDP, &context);

    // Process the message, but use a different key there
285 286
    shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
    server.setTSIGKeyRing(&keyring);
287 288 289 290 291 292 293 294 295
    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);
296
    m.fromWire(ib);
297 298

    const TSIGRecord* tsig = m.getTSIGRecord();
299
    ASSERT_TRUE(tsig != NULL) <<
300
        "Missing TSIG signature (we should have one even at error)";
301
    EXPECT_EQ(TSIGError::BAD_KEY_CODE, tsig->getRdata().getError());
302 303
    EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
        "It should be unsigned with this error";
304 305 306 307 308 309 310 311 312 313 314
}

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

315 316 317 318
    // Process the message, but use a different key there
    shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
    keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
    server.setTSIGKeyRing(&keyring);
319 320 321 322
    server.processMessage(*io_message, parse_message, response_obuffer,
                          &dnsserv);

    EXPECT_TRUE(dnsserv.hasAnswer());
323
    headerCheck(*parse_message, default_qid, TSIGError::BAD_SIG().toRcode(),
324 325 326 327
                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);
328
    m.fromWire(ib);
329 330

    const TSIGRecord* tsig = m.getTSIGRecord();
331
    ASSERT_TRUE(tsig != NULL) <<
332
        "Missing TSIG signature (we should have one even at error)";
333
    EXPECT_EQ(TSIGError::BAD_SIG_CODE, tsig->getRdata().getError());
334 335
    EXPECT_EQ(0, tsig->getRdata().getMACSize()) <<
        "It should be unsigned with this error";
336 337
}

338 339 340 341 342 343 344 345 346 347 348 349 350
// 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);

351 352 353 354
    // Process the message, but use a different key there
    shared_ptr<TSIGKeyRing> keyring(new TSIGKeyRing);
    keyring->add(TSIGKey("key:QkFECg==:hmac-sha1"));
    server.setTSIGKeyRing(&keyring);
355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
    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";
}

374 375 376
TEST_F(AuthSrvTest, AXFRConnectFail) {
    EXPECT_FALSE(xfrout.isConnected()); // check prerequisite
    xfrout.disableConnect();
Evan Hunt's avatar
Evan Hunt committed
377 378 379 380 381
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                         Name("example.com"), RRClass::IN(), RRType::AXFR());
    createRequestPacket(request_message, IPPROTO_TCP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
382
    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
383 384 385 386 387 388 389
                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
390 391 392 393
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                         Name("example.com"), RRClass::IN(), RRType::AXFR());
    createRequestPacket(request_message, IPPROTO_TCP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
394
    EXPECT_TRUE(xfrout.isConnected());
395 396

    xfrout.disableSend();
397 398
    parse_message->clear(Message::PARSE);
    response_obuffer->clear();
Evan Hunt's avatar
Evan Hunt committed
399 400 401 402 403
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                         Name("example.com"), RRClass::IN(), RRType::AXFR());
    createRequestPacket(request_message, IPPROTO_TCP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
404
    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
405 406 407 408 409 410 411 412 413 414 415
                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
416 417 418
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                         Name("example.com"), RRClass::IN(), RRType::AXFR());
    createRequestPacket(request_message, IPPROTO_TCP);
419
    EXPECT_THROW(server.processMessage(*io_message, parse_message,
Evan Hunt's avatar
Evan Hunt committed
420
                                       response_obuffer, &dnsserv),
421 422 423 424 425 426 427
                 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();
}

428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
TEST_F(AuthSrvTest, IXFRConnectFail) {
    EXPECT_FALSE(xfrout.isConnected()); // check prerequisite
    xfrout.disableConnect();
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                         Name("example.com"), RRClass::IN(), RRType::IXFR());
    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,
                         Name("example.com"), RRClass::IN(), RRType::IXFR());
    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,
                         Name("example.com"), RRClass::IN(), RRType::IXFR());
    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,
                         Name("example.com"), RRClass::IN(), RRType::IXFR());
    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();
}

482
TEST_F(AuthSrvTest, notify) {
Evan Hunt's avatar
Evan Hunt committed
483 484
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
485
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
486 487 488
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
489 490 491

    // An internal command message should have been created and sent to an
    // external module.  Check them.
Evan Hunt's avatar
Evan Hunt committed
492
    EXPECT_EQ("Zonemgr", notify_session.getMessageDest());
493
    EXPECT_EQ("notify",
Evan Hunt's avatar
Evan Hunt committed
494
              notify_session.getSentMessage()->get("command")->get(0)->stringValue());
495
    ConstElementPtr notify_args =
Evan Hunt's avatar
Evan Hunt committed
496
        notify_session.getSentMessage()->get("command")->get(1);
497 498 499
    EXPECT_EQ("example.com.", notify_args->get("zone_name")->stringValue());
    EXPECT_EQ(DEFAULT_REMOTE_ADDRESS,
              notify_args->get("master")->stringValue());
500
    EXPECT_EQ("IN", notify_args->get("zone_class")->stringValue());
501 502

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

506
    // The question must be identical to that of the received notify
507
    ConstQuestionPtr question = *parse_message->beginQuestion();
508 509 510 511 512
    EXPECT_EQ(Name("example.com"), question->getName());
    EXPECT_EQ(RRClass::IN(), question->getClass());
    EXPECT_EQ(RRType::SOA(), question->getType());
}

513 514
TEST_F(AuthSrvTest, notifyForCHClass) {
    // Same as the previous test, but for the CH RRClass.
Evan Hunt's avatar
Evan Hunt committed
515 516
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::CH(), RRType::SOA());
517
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
518 519 520
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
521 522 523

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

529 530 531
TEST_F(AuthSrvTest, notifyEmptyQuestion) {
    request_message.clear(Message::RENDER);
    request_message.setOpcode(Opcode::NOTIFY());
532
    request_message.setRcode(Rcode::NOERROR());
533
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
534 535
    request_message.setQid(default_qid);
    request_message.toWire(request_renderer);
Evan Hunt's avatar
Evan Hunt committed
536 537 538
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
539
    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
540 541 542 543
                Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyMultiQuestions) {
Evan Hunt's avatar
Evan Hunt committed
544 545
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
546 547 548
    // add one more SOA question
    request_message.addQuestion(Question(Name("example.com"), RRClass::IN(),
                                         RRType::SOA()));
549
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
550 551 552
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
553
    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
554 555 556 557
                Opcode::NOTIFY().getCode(), QR_FLAG, 2, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyNonSOAQuestion) {
Evan Hunt's avatar
Evan Hunt committed
558 559
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::NS());
560
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
561 562 563
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
564
    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
565 566 567 568 569
                Opcode::NOTIFY().getCode(), QR_FLAG, 1, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyWithoutAA) {
    // implicitly leave the AA bit off.  our implementation will accept it.
Evan Hunt's avatar
Evan Hunt committed
570 571 572 573 574
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
575
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
576 577 578 579
                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyWithErrorRcode) {
Evan Hunt's avatar
Evan Hunt committed
580 581
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
582
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
583
    request_message.setRcode(Rcode::SERVFAIL());
Evan Hunt's avatar
Evan Hunt committed
584 585 586
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
587
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
588
                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
Han Feng's avatar
Han Feng committed
589 590
}

591
TEST_F(AuthSrvTest, notifyWithoutSession) {
592
    server.setXfrinSession(NULL);
593

Evan Hunt's avatar
Evan Hunt committed
594 595
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
596
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
597
    createRequestPacket(request_message, IPPROTO_UDP);
598 599 600

    // we simply ignore the notify and let it be resent if an internal error
    // happens.
Evan Hunt's avatar
Evan Hunt committed
601 602
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
603 604 605 606 607
}

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

Evan Hunt's avatar
Evan Hunt committed
608 609
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
610
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
611
    createRequestPacket(request_message, IPPROTO_UDP);
612

Evan Hunt's avatar
Evan Hunt committed
613 614
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
615 616 617 618 619
}

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

Evan Hunt's avatar
Evan Hunt committed
620 621
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
622
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
623 624 625
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
626 627 628
}

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

Evan Hunt's avatar
Evan Hunt committed
631 632
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
633
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
634 635 636
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
637 638 639 640
}

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

Evan Hunt's avatar
Evan Hunt committed
643 644
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
645
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
646 647 648
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
649 650
}

651
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
652
updateConfig(AuthSrv* server, const char* const config_data,
653 654
             const bool expect_success)
{
655
    ConstElementPtr config_answer =
JINMEI Tatuya's avatar
JINMEI Tatuya committed
656
        server->updateConfig(Element::fromJSON(config_data));
657 658 659
    EXPECT_EQ(Element::map, config_answer->getType());
    EXPECT_TRUE(config_answer->contains("result"));

660
    ConstElementPtr result = config_answer->get("result");
661
    EXPECT_EQ(Element::list, result->getType());
662
    EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue());
663 664 665 666
}

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

    // 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.
672
    createDataFromFile("examplequery_fromWire.wire");
Evan Hunt's avatar
Evan Hunt committed
673 674
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
675
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
676 677 678 679
                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}

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

    // 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.
686
    createDataFromFile("badExampleQuery_fromWire.wire");
Evan Hunt's avatar
Evan Hunt committed
687 688
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
689
    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
690
                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
691
}
692 693 694 695 696 697 698 699 700

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.
701
    createDataFromFile("examplequery_fromWire.wire");
Evan Hunt's avatar
Evan Hunt committed
702 703
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
704
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
705 706
                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
707

708
TEST_F(AuthSrvTest, updateWithInMemoryClient) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
709 710 711 712
    // 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
713
    EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
JINMEI Tatuya's avatar
JINMEI Tatuya committed
714 715 716
    updateConfig(&server,
                 "{\"datasources\": [{\"type\": \"memory\"}]}", true);
    // after successful configuration, we should have one (with empty zoneset).
717 718
    ASSERT_NE(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
    EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount());
Jerry's avatar
Jerry committed
719

720
    // The memory data source is empty, should return REFUSED rcode.
Jerry's avatar
Jerry committed
721
    createDataFromFile("examplequery_fromWire.wire");
722 723 724 725 726 727 728
    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);
}

729
TEST_F(AuthSrvTest, chQueryWithInMemoryClient) {
730 731 732 733 734 735 736 737 738 739 740
    // 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);
741
    EXPECT_TRUE(dnsserv.hasAnswer());
742 743
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
                opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
744 745
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
746 747 748 749 750 751 752 753 754
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());
}
755

756 757 758
// Submit UDP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterUDPNormal) {
    // The counter should be initialized to 0.
759
    EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_UDP_QUERY));
760 761 762 763 764 765 766
    // 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);
767
    // After processing UDP query, the counter should be 1.
768
    EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_UDP_QUERY));
769 770
}

771 772 773
// Submit TCP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPNormal) {
    // The counter should be initialized to 0.
774
    EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
775 776 777 778 779 780 781
    // 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);
782
    // After processing TCP query, the counter should be 1.
783
    EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
784 785 786 787 788
}

// Submit TCP AXFR query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
    // The counter should be initialized to 0.
789
    EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
790 791 792 793 794 795
    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,
    // so we shouldn't have to respond.
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
796
    // After processing TCP AXFR query, the counter should be 1.
797
    EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
798 799
}

800 801 802 803 804 805 806 807 808 809 810 811 812 813
// Submit TCP IXFR query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
    // The counter should be initialized to 0.
    EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                         Name("example.com"), RRClass::IN(), RRType::IXFR());
    createRequestPacket(request_message, IPPROTO_TCP);
    // On success, the AXFR query has been passed to a separate process,
    // so we shouldn't have to respond.
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    // After processing TCP AXFR query, the counter should be 1.
    EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
}

814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830
// class for queryCounterUnexpected test
// getProtocol() returns IPPROTO_IP
class DummyUnknownSocket : public IOSocket {
public:
    DummyUnknownSocket() {}
    virtual int getNative() const { return (0); }
    virtual int getProtocol() const { return (IPPROTO_IP); }
};

// function for queryCounterUnexpected test
// returns a reference to a static object of DummyUnknownSocket
IOSocket&
getDummyUnknownSocket() {
    static DummyUnknownSocket socket;
    return (socket);
}

831
// Submit unexpected type of query and check it throws isc::Unexpected
832
TEST_F(AuthSrvTest, queryCounterUnexpected) {
833 834 835 836
    // This code isn't exception safe, but we'd rather keep the code
    // simpler and more readable as this is only for tests and if it throws
    // the program would immediately terminate anyway.

837
    // Create UDP query packet.
838 839 840 841
    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::NS());
    createRequestPacket(request_message, IPPROTO_UDP);
842

843
    // Modify the message.
844 845
    delete io_message;
    endpoint = IOEndpoint::create(IPPROTO_UDP,
846
                                  IOAddress(DEFAULT_REMOTE_ADDRESS), 53210);
847 848 849 850 851
    io_message = new IOMessage(request_renderer.getData(),
                               request_renderer.getLength(),
                               getDummyUnknownSocket(), *endpoint);

    EXPECT_THROW(server.processMessage(*io_message, parse_message,
852
                                       response_obuffer, &dnsserv),
853
                 isc::Unexpected);
854
}
855 856 857 858 859

TEST_F(AuthSrvTest, stop) {
    // normal case is covered in command_unittest.cc.  we should primarily
    // test it here, but the current design of the stop test takes time,
    // so we consolidate the cases in the command tests.
860 861
    // If/when the interval timer has finer granularity we'll probably add
    // our own tests here, so we keep this empty test case.
862
}
863 864

TEST_F(AuthSrvTest, listenAddresses) {
865
    isc::testutils::portconfig::listenAddresses(server);
866
}
867

868
}