auth_srv_unittest.cc 27.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// Copyright (C) 2010  Internet Systems Consortium, Inc. ("ISC")
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.

// $Id$

17
#include <config.h>
18 19 20 21 22 23 24 25 26 27 28 29 30

#include <vector>

#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>

JINMEI Tatuya's avatar
JINMEI Tatuya committed
31
#include <datasrc/memory_datasrc.h>
32
#include <auth/auth_srv.h>
33
#include <auth/common.h>
34
#include <auth/statistics.h>
35

36 37
#include <dns/tests/unittest_util.h>
#include <testutils/srv_test.h>
38

JINMEI Tatuya's avatar
JINMEI Tatuya committed
39
using namespace std;
40
using namespace isc::cc;
41
using namespace isc::dns;
42
using namespace isc::dns::rdata;
43
using namespace isc::data;
44
using namespace isc::xfr;
45
using namespace asiolink;
46
using namespace isc::testutils;
Evan Hunt's avatar
Evan Hunt committed
47
using isc::UnitTestUtil;
48 49

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

57
class AuthSrvTest : public SrvTestBase {
58
protected:
JINMEI Tatuya's avatar
JINMEI Tatuya committed
59
    AuthSrvTest() : server(true, xfrout), rrclass(RRClass::IN()) {
60
        server.setXfrinSession(&notify_session);
61
        server.setStatisticsSession(&statistics_session);
62
    }
63 64 65 66
    virtual void processMessage() {
        server.processMessage(*io_message, parse_message, response_obuffer,
                              &dnsserv);
    }
67
    MockSession statistics_session;
68
    MockXfroutClient xfrout;
69
    AuthSrv server;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
70
    const RRClass rrclass;
71
    vector<uint8_t> response_data;
72 73
};

74 75 76 77 78 79 80 81 82 83 84 85 86 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 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
// 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,
                                     response_obuffer, &dnsserv);
    (*server.getDNSAnswerProvider())(*io_message, parse_message,
144
                                     response_message, response_obuffer);
145 146 147 148 149 150 151 152 153 154 155 156 157 158

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

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

169 170
// Unsupported requests.  Should result in NOTIMP.
TEST_F(AuthSrvTest, unsupportedRequest) {
171
    unsupportedRequest();
172
}
173

174 175
// Simple API check
TEST_F(AuthSrvTest, verbose) {
176 177 178 179 180
    EXPECT_FALSE(server.getVerbose());
    server.setVerbose(true);
    EXPECT_TRUE(server.getVerbose());
    server.setVerbose(false);
    EXPECT_FALSE(server.getVerbose());
181 182
}

183 184
// Multiple questions.  Should result in FORMERR.
TEST_F(AuthSrvTest, multiQuestion) {
185
    multiQuestion();
186 187
}

188 189 190
// Incoming data doesn't even contain the complete header.  Must be silently
// dropped.
TEST_F(AuthSrvTest, shortMessage) {
191
    shortMessage();
192 193 194 195 196
}

// 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) {
197
    response();
198 199 200 201
}

// Query with a broken question
TEST_F(AuthSrvTest, shortQuestion) {
202
    shortQuestion();
203
}
204

205 206
// Query with a broken answer section
TEST_F(AuthSrvTest, shortAnswer) {
207
    shortAnswer();
208 209
}

210 211
// Query with unsupported version of EDNS.
TEST_F(AuthSrvTest, ednsBadVers) {
212
    ednsBadVers();
213 214
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
215
TEST_F(AuthSrvTest, AXFROverUDP) {
216
    axfrOverUDP();
JINMEI Tatuya's avatar
JINMEI Tatuya committed
217 218
}

219 220
TEST_F(AuthSrvTest, AXFRSuccess) {
    EXPECT_FALSE(xfrout.isConnected());
Evan Hunt's avatar
Evan Hunt committed
221 222 223
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                         Name("example.com"), RRClass::IN(), RRType::AXFR());
    createRequestPacket(request_message, IPPROTO_TCP);
224 225
    // 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
226 227
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
228
    EXPECT_TRUE(xfrout.isConnected());
229 230 231 232 233
}

TEST_F(AuthSrvTest, AXFRConnectFail) {
    EXPECT_FALSE(xfrout.isConnected()); // check prerequisite
    xfrout.disableConnect();
Evan Hunt's avatar
Evan Hunt committed
234 235 236 237 238
    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());
239
    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
240 241 242 243 244 245 246
                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
247 248 249 250
    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);
251
    EXPECT_TRUE(xfrout.isConnected());
252 253

    xfrout.disableSend();
254 255
    parse_message->clear(Message::PARSE);
    response_obuffer->clear();
Evan Hunt's avatar
Evan Hunt committed
256 257 258 259 260
    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());
261
    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
262 263 264 265 266 267 268 269 270 271 272
                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
273 274 275
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                         Name("example.com"), RRClass::IN(), RRType::AXFR());
    createRequestPacket(request_message, IPPROTO_TCP);
276
    EXPECT_THROW(server.processMessage(*io_message, parse_message,
Evan Hunt's avatar
Evan Hunt committed
277
                                       response_obuffer, &dnsserv),
278 279 280 281 282 283 284
                 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();
}

285
TEST_F(AuthSrvTest, notify) {
Evan Hunt's avatar
Evan Hunt committed
286 287
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
288
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
289 290 291
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
292 293 294

    // An internal command message should have been created and sent to an
    // external module.  Check them.
Evan Hunt's avatar
Evan Hunt committed
295
    EXPECT_EQ("Zonemgr", notify_session.getMessageDest());
296
    EXPECT_EQ("notify",
Evan Hunt's avatar
Evan Hunt committed
297
              notify_session.getSentMessage()->get("command")->get(0)->stringValue());
298
    ConstElementPtr notify_args =
Evan Hunt's avatar
Evan Hunt committed
299
        notify_session.getSentMessage()->get("command")->get(1);
300 301 302
    EXPECT_EQ("example.com.", notify_args->get("zone_name")->stringValue());
    EXPECT_EQ(DEFAULT_REMOTE_ADDRESS,
              notify_args->get("master")->stringValue());
303
    EXPECT_EQ("IN", notify_args->get("zone_class")->stringValue());
304 305

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

309
    // The question must be identical to that of the received notify
310
    ConstQuestionPtr question = *parse_message->beginQuestion();
311 312 313 314 315
    EXPECT_EQ(Name("example.com"), question->getName());
    EXPECT_EQ(RRClass::IN(), question->getClass());
    EXPECT_EQ(RRType::SOA(), question->getType());
}

316 317
TEST_F(AuthSrvTest, notifyForCHClass) {
    // Same as the previous test, but for the CH RRClass.
Evan Hunt's avatar
Evan Hunt committed
318 319
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::CH(), RRType::SOA());
320
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
321 322 323
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
324 325 326

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

332 333 334
TEST_F(AuthSrvTest, notifyEmptyQuestion) {
    request_message.clear(Message::RENDER);
    request_message.setOpcode(Opcode::NOTIFY());
335
    request_message.setRcode(Rcode::NOERROR());
336
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
337 338
    request_message.setQid(default_qid);
    request_message.toWire(request_renderer);
Evan Hunt's avatar
Evan Hunt committed
339 340 341
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
342
    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
343 344 345 346
                Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyMultiQuestions) {
Evan Hunt's avatar
Evan Hunt committed
347 348
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
349 350 351
    // add one more SOA question
    request_message.addQuestion(Question(Name("example.com"), RRClass::IN(),
                                         RRType::SOA()));
352
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
353 354 355
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
356
    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
357 358 359 360
                Opcode::NOTIFY().getCode(), QR_FLAG, 2, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyNonSOAQuestion) {
Evan Hunt's avatar
Evan Hunt committed
361 362
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::NS());
363
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
364 365 366
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
367
    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
368 369 370 371 372
                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
373 374 375 376 377
    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());
378
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
379 380 381 382
                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyWithErrorRcode) {
Evan Hunt's avatar
Evan Hunt committed
383 384
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
385
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
386
    request_message.setRcode(Rcode::SERVFAIL());
Evan Hunt's avatar
Evan Hunt committed
387 388 389
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
390
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
391
                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
Han Feng's avatar
Han Feng committed
392 393
}

394
TEST_F(AuthSrvTest, notifyWithoutSession) {
395
    server.setXfrinSession(NULL);
396

Evan Hunt's avatar
Evan Hunt committed
397 398
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
399
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
400
    createRequestPacket(request_message, IPPROTO_UDP);
401 402 403

    // we simply ignore the notify and let it be resent if an internal error
    // happens.
Evan Hunt's avatar
Evan Hunt committed
404 405
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
406 407 408 409 410
}

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

Evan Hunt's avatar
Evan Hunt committed
411 412
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
413
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
414
    createRequestPacket(request_message, IPPROTO_UDP);
415

Evan Hunt's avatar
Evan Hunt committed
416 417
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
418 419 420 421 422
}

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

Evan Hunt's avatar
Evan Hunt committed
423 424
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
425
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
426 427 428
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
429 430 431
}

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

Evan Hunt's avatar
Evan Hunt committed
434 435
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
436
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
437 438 439
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
440 441 442 443
}

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

Evan Hunt's avatar
Evan Hunt committed
446 447
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
448
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
449 450 451
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
452 453
}

454
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
455
updateConfig(AuthSrv* server, const char* const config_data,
456 457
             const bool expect_success)
{
458
    ConstElementPtr config_answer =
JINMEI Tatuya's avatar
JINMEI Tatuya committed
459
        server->updateConfig(Element::fromJSON(config_data));
460 461 462
    EXPECT_EQ(Element::map, config_answer->getType());
    EXPECT_TRUE(config_answer->contains("result"));

463
    ConstElementPtr result = config_answer->get("result");
464
    EXPECT_EQ(Element::list, result->getType());
465
    EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue());
466 467 468 469
}

// Install a Sqlite3 data source with testing data.
TEST_F(AuthSrvTest, updateConfig) {
470
    updateConfig(&server, CONFIG_TESTDB, true);
471 472 473 474

    // 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.
475
    createDataFromFile("examplequery_fromWire.wire");
Evan Hunt's avatar
Evan Hunt committed
476 477
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
478
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
479 480 481 482
                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}

TEST_F(AuthSrvTest, datasourceFail) {
483
    updateConfig(&server, CONFIG_TESTDB, true);
484 485 486 487 488

    // 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.
489
    createDataFromFile("badExampleQuery_fromWire.wire");
Evan Hunt's avatar
Evan Hunt committed
490 491
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
492
    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
493
                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
494
}
495 496 497 498 499 500 501 502 503

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.
504
    createDataFromFile("examplequery_fromWire.wire");
Evan Hunt's avatar
Evan Hunt committed
505 506
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
507
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
508 509
                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
510

JINMEI Tatuya's avatar
JINMEI Tatuya committed
511 512 513 514 515 516 517 518 519 520 521
TEST_F(AuthSrvTest, updateWithMemoryDataSrc) {
    // 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
    EXPECT_EQ(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass));
    updateConfig(&server,
                 "{\"datasources\": [{\"type\": \"memory\"}]}", true);
    // after successful configuration, we should have one (with empty zoneset).
    ASSERT_NE(AuthSrv::MemoryDataSrcPtr(), server.getMemoryDataSrc(rrclass));
    EXPECT_EQ(0, server.getMemoryDataSrc(rrclass)->getZoneCount());
Jerry's avatar
Jerry committed
522

523
    // The memory data source is empty, should return REFUSED rcode.
Jerry's avatar
Jerry committed
524
    createDataFromFile("examplequery_fromWire.wire");
525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543
    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);
}

TEST_F(AuthSrvTest, chQueryWithMemoryDataSrc) {
    // 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);
544
    EXPECT_TRUE(dnsserv.hasAnswer());
545 546
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
                opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
547 548
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
549 550 551 552 553 554 555 556 557
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());
}
558

559 560 561
// Submit UDP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterUDPNormal) {
    // The counter should be initialized to 0.
562
    EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_UDP_QUERY));
563 564 565 566 567 568 569
    // 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);
570
    // After processing UDP query, the counter should be 1.
571
    EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_UDP_QUERY));
572 573
}

574 575 576
// Submit TCP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPNormal) {
    // The counter should be initialized to 0.
577
    EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
578 579 580 581 582 583 584
    // 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);
585
    // After processing TCP query, the counter should be 1.
586
    EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
587 588 589 590 591
}

// Submit TCP AXFR query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
    // The counter should be initialized to 0.
592
    EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
593 594 595 596 597 598
    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);
599
    // After processing TCP AXFR query, the counter should be 1.
600
    EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619
}

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

620
// Submit unexpected type of query and check it throws isc::Unexpected
621
TEST_F(AuthSrvTest, queryCounterUnexpected) {
622 623 624 625
    // 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.

626
    // Create UDP query packet.
627 628 629 630
    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::NS());
    createRequestPacket(request_message, IPPROTO_UDP);
631

632
    // Modify the message.
633 634 635 636 637 638 639 640
    delete io_message;
    endpoint = IOEndpoint::create(IPPROTO_UDP,
                                  IOAddress(DEFAULT_REMOTE_ADDRESS), 5300);
    io_message = new IOMessage(request_renderer.getData(),
                               request_renderer.getLength(),
                               getDummyUnknownSocket(), *endpoint);

    EXPECT_THROW(server.processMessage(*io_message, parse_message,
641
                                       response_obuffer, &dnsserv),
642
                 isc::Unexpected);
643
}
644 645 646 647 648 649 650 651 652

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.

    // stop before start is prohibited.
    EXPECT_THROW(server.stop(), FatalError);
}
653
}