auth_srv_unittest.cc 33.8 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
TEST_F(AuthSrvTest, notify) {
Evan Hunt's avatar
Evan Hunt committed
429 430
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
431
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
432 433 434
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
435 436 437

    // An internal command message should have been created and sent to an
    // external module.  Check them.
Evan Hunt's avatar
Evan Hunt committed
438
    EXPECT_EQ("Zonemgr", notify_session.getMessageDest());
439
    EXPECT_EQ("notify",
Evan Hunt's avatar
Evan Hunt committed
440
              notify_session.getSentMessage()->get("command")->get(0)->stringValue());
441
    ConstElementPtr notify_args =
Evan Hunt's avatar
Evan Hunt committed
442
        notify_session.getSentMessage()->get("command")->get(1);
443 444 445
    EXPECT_EQ("example.com.", notify_args->get("zone_name")->stringValue());
    EXPECT_EQ(DEFAULT_REMOTE_ADDRESS,
              notify_args->get("master")->stringValue());
446
    EXPECT_EQ("IN", notify_args->get("zone_class")->stringValue());
447 448

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

452
    // The question must be identical to that of the received notify
453
    ConstQuestionPtr question = *parse_message->beginQuestion();
454 455 456 457 458
    EXPECT_EQ(Name("example.com"), question->getName());
    EXPECT_EQ(RRClass::IN(), question->getClass());
    EXPECT_EQ(RRType::SOA(), question->getType());
}

459 460
TEST_F(AuthSrvTest, notifyForCHClass) {
    // Same as the previous test, but for the CH RRClass.
Evan Hunt's avatar
Evan Hunt committed
461 462
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::CH(), RRType::SOA());
463
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
464 465 466
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
467 468 469

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

475 476 477
TEST_F(AuthSrvTest, notifyEmptyQuestion) {
    request_message.clear(Message::RENDER);
    request_message.setOpcode(Opcode::NOTIFY());
478
    request_message.setRcode(Rcode::NOERROR());
479
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
480 481
    request_message.setQid(default_qid);
    request_message.toWire(request_renderer);
Evan Hunt's avatar
Evan Hunt committed
482 483 484
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
485
    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
486 487 488 489
                Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyMultiQuestions) {
Evan Hunt's avatar
Evan Hunt committed
490 491
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
492 493 494
    // add one more SOA question
    request_message.addQuestion(Question(Name("example.com"), RRClass::IN(),
                                         RRType::SOA()));
495
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
496 497 498
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
499
    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
500 501 502 503
                Opcode::NOTIFY().getCode(), QR_FLAG, 2, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyNonSOAQuestion) {
Evan Hunt's avatar
Evan Hunt committed
504 505
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::NS());
506
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
507 508 509
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
510
    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
511 512 513 514 515
                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
516 517 518 519 520
    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());
521
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
522 523 524 525
                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyWithErrorRcode) {
Evan Hunt's avatar
Evan Hunt committed
526 527
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
528
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
529
    request_message.setRcode(Rcode::SERVFAIL());
Evan Hunt's avatar
Evan Hunt committed
530 531 532
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
533
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
534
                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
Han Feng's avatar
Han Feng committed
535 536
}

537
TEST_F(AuthSrvTest, notifyWithoutSession) {
538
    server.setXfrinSession(NULL);
539

Evan Hunt's avatar
Evan Hunt committed
540 541
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
542
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
543
    createRequestPacket(request_message, IPPROTO_UDP);
544 545 546

    // we simply ignore the notify and let it be resent if an internal error
    // happens.
Evan Hunt's avatar
Evan Hunt committed
547 548
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
549 550 551 552 553
}

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

Evan Hunt's avatar
Evan Hunt committed
554 555
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
556
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
557
    createRequestPacket(request_message, IPPROTO_UDP);
558

Evan Hunt's avatar
Evan Hunt committed
559 560
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
561 562 563 564 565
}

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

Evan Hunt's avatar
Evan Hunt committed
566 567
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
568
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
569 570 571
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
572 573 574
}

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

Evan Hunt's avatar
Evan Hunt committed
577 578
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
579
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
580 581 582
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
583 584 585 586
}

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

Evan Hunt's avatar
Evan Hunt committed
589 590
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(), default_qid,
                         Name("example.com"), RRClass::IN(), RRType::SOA());
591
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
592 593 594
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
595 596
}

597
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
598
updateConfig(AuthSrv* server, const char* const config_data,
599 600
             const bool expect_success)
{
601
    ConstElementPtr config_answer =
JINMEI Tatuya's avatar
JINMEI Tatuya committed
602
        server->updateConfig(Element::fromJSON(config_data));
603 604 605
    EXPECT_EQ(Element::map, config_answer->getType());
    EXPECT_TRUE(config_answer->contains("result"));

606
    ConstElementPtr result = config_answer->get("result");
607
    EXPECT_EQ(Element::list, result->getType());
608
    EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue());
609 610 611 612
}

// Install a Sqlite3 data source with testing data.
TEST_F(AuthSrvTest, updateConfig) {
613
    updateConfig(&server, CONFIG_TESTDB, true);
614 615 616 617

    // 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.
618
    createDataFromFile("examplequery_fromWire.wire");
Evan Hunt's avatar
Evan Hunt committed
619 620
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
621
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
622 623 624 625
                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}

TEST_F(AuthSrvTest, datasourceFail) {
626
    updateConfig(&server, CONFIG_TESTDB, true);
627 628 629 630 631

    // 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.
632
    createDataFromFile("badExampleQuery_fromWire.wire");
Evan Hunt's avatar
Evan Hunt committed
633 634
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
635
    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
636
                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
637
}
638 639 640 641 642 643 644 645 646

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.
647
    createDataFromFile("examplequery_fromWire.wire");
Evan Hunt's avatar
Evan Hunt committed
648 649
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
650
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
651 652
                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
653

JINMEI Tatuya's avatar
JINMEI Tatuya committed
654 655 656 657 658 659 660 661 662 663 664
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
665

666
    // The memory data source is empty, should return REFUSED rcode.
Jerry's avatar
Jerry committed
667
    createDataFromFile("examplequery_fromWire.wire");
668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
    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);
687
    EXPECT_TRUE(dnsserv.hasAnswer());
688 689
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
                opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
690 691
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
692 693 694 695 696 697 698 699 700
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());
}
701

702 703 704
// Submit UDP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterUDPNormal) {
    // The counter should be initialized to 0.
705
    EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_UDP_QUERY));
706 707 708 709 710 711 712
    // 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);
713
    // After processing UDP query, the counter should be 1.
714
    EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_UDP_QUERY));
715 716
}

717 718 719
// Submit TCP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPNormal) {
    // The counter should be initialized to 0.
720
    EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
721 722 723 724 725 726 727
    // 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);
728
    // After processing TCP query, the counter should be 1.
729
    EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
730 731 732 733 734
}

// Submit TCP AXFR query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
    // The counter should be initialized to 0.
735
    EXPECT_EQ(0, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
736 737 738 739 740 741
    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);
742
    // After processing TCP AXFR query, the counter should be 1.
743
    EXPECT_EQ(1, server.getCounter(AuthCounters::COUNTER_TCP_QUERY));
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
}

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

763
// Submit unexpected type of query and check it throws isc::Unexpected
764
TEST_F(AuthSrvTest, queryCounterUnexpected) {
765 766 767 768
    // 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.

769
    // Create UDP query packet.
770 771 772 773
    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::NS());
    createRequestPacket(request_message, IPPROTO_UDP);
774

775
    // Modify the message.
776 777
    delete io_message;
    endpoint = IOEndpoint::create(IPPROTO_UDP,
778
                                  IOAddress(DEFAULT_REMOTE_ADDRESS), 53210);
779 780 781 782 783
    io_message = new IOMessage(request_renderer.getData(),
                               request_renderer.getLength(),
                               getDummyUnknownSocket(), *endpoint);

    EXPECT_THROW(server.processMessage(*io_message, parse_message,
784
                                       response_obuffer, &dnsserv),
785
                 isc::Unexpected);
786
}
787 788 789 790 791

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.
792 793
    // If/when the interval timer has finer granularity we'll probably add
    // our own tests here, so we keep this empty test case.
794
}
795 796

TEST_F(AuthSrvTest, listenAddresses) {
797
    isc::testutils::portconfig::listenAddresses(server);
798
}
799

800
}