auth_srv_unittest.cc 38.3 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 58

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

66
class AuthSrvTest : public SrvTestBase {
67
protected:
68 69 70 71 72 73
    AuthSrvTest() :
        dnss_(ios_, NULL, NULL, NULL),
        server(true, xfrout),
        rrclass(RRClass::IN())
    {
        server.setDNSService(dnss_);
74
        server.setXfrinSession(&notify_session);
75
        server.setStatisticsSession(&statistics_session);
76
    }
77 78 79 80
    virtual void processMessage() {
        server.processMessage(*io_message, parse_message, response_obuffer,
                              &dnsserv);
    }
81 82
    IOService ios_;
    DNSService dnss_;
83
    MockSession statistics_session;
84
    MockXfroutClient xfrout;
85
    AuthSrv server;
JINMEI Tatuya's avatar
JINMEI Tatuya committed
86
    const RRClass rrclass;
87
    vector<uint8_t> response_data;
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 144 145 146 147 148 149 150 151 152 153 154 155 156 157
// 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,
158
                                     response_message,
159 160
                                     response_obuffer, &dnsserv);
    (*server.getDNSAnswerProvider())(*io_message, parse_message,
161
                                     response_message, response_obuffer);
162 163 164 165 166 167 168 169 170 171 172 173

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

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

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

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

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

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

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

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

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

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

228 229
TEST_F(AuthSrvTest, AXFRSuccess) {
    EXPECT_FALSE(xfrout.isConnected());
Evan Hunt's avatar
Evan Hunt committed
230
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
231 232
                                       Name("example.com"), RRClass::IN(),
                                       RRType::AXFR());
Evan Hunt's avatar
Evan Hunt committed
233
    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
    TSIGContext context(key);
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
248 249
                                       Name("version.bind"), RRClass::CH(),
                                       RRType::TXT());
250 251
    createRequestPacket(request_message, IPPROTO_UDP, &context);

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

    const TSIGRecord* tsig = m.getTSIGRecord();
269
    ASSERT_TRUE(tsig != NULL) << "Missing TSIG signature";
270 271 272 273 274 275 276 277
    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
278
TEST_F(AuthSrvTest, TSIGSignedBadKey) {
279 280 281
    TSIGKey key("key:c2VjcmV0Cg==:hmac-sha1");
    TSIGContext context(key);
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
282 283
                                       Name("version.bind"), RRClass::CH(),
                                       RRType::TXT());
284 285 286
    createRequestPacket(request_message, IPPROTO_UDP, &context);

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

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

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

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

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

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

341 342 343 344 345 346 347 348 349 350 351 352 353
// 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);

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

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

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

435 436 437 438
TEST_F(AuthSrvTest, IXFRConnectFail) {
    EXPECT_FALSE(xfrout.isConnected()); // check prerequisite
    xfrout.disableConnect();
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
439 440
                                       Name("example.com"), RRClass::IN(),
                                       RRType::IXFR());
441 442 443 444 445 446 447 448 449 450 451 452
    createRequestPacket(request_message, IPPROTO_TCP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
    EXPECT_FALSE(xfrout.isConnected());
}

TEST_F(AuthSrvTest, IXFRSendFail) {
    // first send a valid query, making the connection with the xfr process
    // open.
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
453 454
                                       Name("example.com"), RRClass::IN(),
                                       RRType::IXFR());
455 456 457 458 459 460 461 462
    createRequestPacket(request_message, IPPROTO_TCP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(xfrout.isConnected());

    xfrout.disableSend();
    parse_message->clear(Message::PARSE);
    response_obuffer->clear();
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
463 464
                                       Name("example.com"), RRClass::IN(),
                                       RRType::IXFR());
465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
    createRequestPacket(request_message, IPPROTO_TCP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);

    // The connection should have been closed due to the send failure.
    EXPECT_FALSE(xfrout.isConnected());
}

TEST_F(AuthSrvTest, IXFRDisconnectFail) {
    // In our usage disconnect() shouldn't fail.  So we'll see the exception
    // should it be thrown.
    xfrout.disableSend();
    xfrout.disableDisconnect();
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
Jelte Jansen's avatar
Jelte Jansen committed
481 482
                                       Name("example.com"), RRClass::IN(),
                                       RRType::IXFR());
483 484 485 486 487 488 489 490 491 492
    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();
}

493
TEST_F(AuthSrvTest, notify) {
Jelte Jansen's avatar
Jelte Jansen committed
494 495 496
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
497
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
498 499 500
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
501 502 503

    // An internal command message should have been created and sent to an
    // external module.  Check them.
Evan Hunt's avatar
Evan Hunt committed
504
    EXPECT_EQ("Zonemgr", notify_session.getMessageDest());
505
    EXPECT_EQ("notify",
Evan Hunt's avatar
Evan Hunt committed
506
              notify_session.getSentMessage()->get("command")->get(0)->stringValue());
507
    ConstElementPtr notify_args =
Evan Hunt's avatar
Evan Hunt committed
508
        notify_session.getSentMessage()->get("command")->get(1);
509 510 511
    EXPECT_EQ("example.com.", notify_args->get("zone_name")->stringValue());
    EXPECT_EQ(DEFAULT_REMOTE_ADDRESS,
              notify_args->get("master")->stringValue());
512
    EXPECT_EQ("IN", notify_args->get("zone_class")->stringValue());
513 514

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

518
    // The question must be identical to that of the received notify
519
    ConstQuestionPtr question = *parse_message->beginQuestion();
520 521 522 523 524
    EXPECT_EQ(Name("example.com"), question->getName());
    EXPECT_EQ(RRClass::IN(), question->getClass());
    EXPECT_EQ(RRType::SOA(), question->getType());
}

525 526
TEST_F(AuthSrvTest, notifyForCHClass) {
    // Same as the previous test, but for the CH RRClass.
Jelte Jansen's avatar
Jelte Jansen committed
527 528 529
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::CH(), RRType::SOA());
530
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
531 532 533
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
534 535 536

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

542 543 544
TEST_F(AuthSrvTest, notifyEmptyQuestion) {
    request_message.clear(Message::RENDER);
    request_message.setOpcode(Opcode::NOTIFY());
545
    request_message.setRcode(Rcode::NOERROR());
546
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
547 548
    request_message.setQid(default_qid);
    request_message.toWire(request_renderer);
Evan Hunt's avatar
Evan Hunt committed
549 550 551
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
552
    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
553 554 555 556
                Opcode::NOTIFY().getCode(), QR_FLAG, 0, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyMultiQuestions) {
Jelte Jansen's avatar
Jelte Jansen committed
557 558 559
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
560 561 562
    // add one more SOA question
    request_message.addQuestion(Question(Name("example.com"), RRClass::IN(),
                                         RRType::SOA()));
563
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
564 565 566
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
567
    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
568 569 570 571
                Opcode::NOTIFY().getCode(), QR_FLAG, 2, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyNonSOAQuestion) {
Jelte Jansen's avatar
Jelte Jansen committed
572 573 574
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::NS());
575
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
576 577 578
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
579
    headerCheck(*parse_message, default_qid, Rcode::FORMERR(),
580 581 582 583 584
                Opcode::NOTIFY().getCode(), QR_FLAG, 1, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyWithoutAA) {
    // implicitly leave the AA bit off.  our implementation will accept it.
Jelte Jansen's avatar
Jelte Jansen committed
585 586 587
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
Evan Hunt's avatar
Evan Hunt committed
588 589 590
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
591
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
592 593 594 595
                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
}

TEST_F(AuthSrvTest, notifyWithErrorRcode) {
Jelte Jansen's avatar
Jelte Jansen committed
596 597 598
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
599
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
600
    request_message.setRcode(Rcode::SERVFAIL());
Evan Hunt's avatar
Evan Hunt committed
601 602 603
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
604
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
605
                Opcode::NOTIFY().getCode(), QR_FLAG | AA_FLAG, 1, 0, 0, 0);
Han Feng's avatar
Han Feng committed
606 607
}

608
TEST_F(AuthSrvTest, notifyWithoutSession) {
609
    server.setXfrinSession(NULL);
610

Jelte Jansen's avatar
Jelte Jansen committed
611 612 613
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
614
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
615
    createRequestPacket(request_message, IPPROTO_UDP);
616 617 618

    // we simply ignore the notify and let it be resent if an internal error
    // happens.
Evan Hunt's avatar
Evan Hunt committed
619 620
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
621 622 623 624 625
}

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

Jelte Jansen's avatar
Jelte Jansen committed
626 627 628
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
629
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
630
    createRequestPacket(request_message, IPPROTO_UDP);
631

Evan Hunt's avatar
Evan Hunt committed
632 633
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
634 635 636 637 638
}

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

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

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

Jelte Jansen's avatar
Jelte Jansen committed
651 652 653
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
654
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
655 656 657
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
658 659 660 661
}

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

Jelte Jansen's avatar
Jelte Jansen committed
664 665 666
    UnitTestUtil::createRequestMessage(request_message, Opcode::NOTIFY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::SOA());
667
    request_message.setHeaderFlag(Message::HEADERFLAG_AA);
Evan Hunt's avatar
Evan Hunt committed
668 669 670
    createRequestPacket(request_message, IPPROTO_UDP);
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_FALSE(dnsserv.hasAnswer());
671 672
}

673
void
JINMEI Tatuya's avatar
JINMEI Tatuya committed
674
updateConfig(AuthSrv* server, const char* const config_data,
675 676
             const bool expect_success)
{
677
    ConstElementPtr config_answer =
JINMEI Tatuya's avatar
JINMEI Tatuya committed
678
        server->updateConfig(Element::fromJSON(config_data));
679 680 681
    EXPECT_EQ(Element::map, config_answer->getType());
    EXPECT_TRUE(config_answer->contains("result"));

682
    ConstElementPtr result = config_answer->get("result");
683
    EXPECT_EQ(Element::list, result->getType());
684
    EXPECT_EQ(expect_success ? 0 : 1, result->get(0)->intValue());
685 686 687 688
}

// Install a Sqlite3 data source with testing data.
TEST_F(AuthSrvTest, updateConfig) {
689
    updateConfig(&server, CONFIG_TESTDB, true);
690 691 692 693

    // 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.
694
    createDataFromFile("examplequery_fromWire.wire");
Evan Hunt's avatar
Evan Hunt committed
695 696
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
697
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
698 699 700 701
                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}

TEST_F(AuthSrvTest, datasourceFail) {
702
    updateConfig(&server, CONFIG_TESTDB, true);
703 704 705 706 707

    // 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.
708
    createDataFromFile("badExampleQuery_fromWire.wire");
Evan Hunt's avatar
Evan Hunt committed
709 710
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
711
    headerCheck(*parse_message, default_qid, Rcode::SERVFAIL(),
712
                opcode.getCode(), QR_FLAG, 1, 0, 0, 0);
713
}
714 715 716 717 718 719 720 721 722

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.
723
    createDataFromFile("examplequery_fromWire.wire");
Evan Hunt's avatar
Evan Hunt committed
724 725
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
    EXPECT_TRUE(dnsserv.hasAnswer());
726
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(), opcode.getCode(),
727 728
                QR_FLAG | AA_FLAG, 1, 1, 1, 0);
}
JINMEI Tatuya's avatar
JINMEI Tatuya committed
729

730
TEST_F(AuthSrvTest, updateWithInMemoryClient) {
JINMEI Tatuya's avatar
JINMEI Tatuya committed
731 732 733 734
    // 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
735
    EXPECT_EQ(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
JINMEI Tatuya's avatar
JINMEI Tatuya committed
736 737 738
    updateConfig(&server,
                 "{\"datasources\": [{\"type\": \"memory\"}]}", true);
    // after successful configuration, we should have one (with empty zoneset).
739 740
    ASSERT_NE(AuthSrv::InMemoryClientPtr(), server.getInMemoryClient(rrclass));
    EXPECT_EQ(0, server.getInMemoryClient(rrclass)->getZoneCount());
Jerry's avatar
Jerry committed
741

742
    // The memory data source is empty, should return REFUSED rcode.
Jerry's avatar
Jerry committed
743
    createDataFromFile("examplequery_fromWire.wire");
744 745 746 747 748 749 750
    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);
}

751
TEST_F(AuthSrvTest, chQueryWithInMemoryClient) {
752 753 754 755 756 757 758 759 760 761 762
    // 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);
763
    EXPECT_TRUE(dnsserv.hasAnswer());
764 765
    headerCheck(*parse_message, default_qid, Rcode::NOERROR(),
                opcode.getCode(), QR_FLAG | AA_FLAG, 1, 1, 1, 0);
JINMEI Tatuya's avatar
JINMEI Tatuya committed
766 767
}

JINMEI Tatuya's avatar
JINMEI Tatuya committed
768 769 770 771 772 773 774 775 776
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());
}
777

778 779 780
// Submit UDP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterUDPNormal) {
    // The counter should be initialized to 0.
781
    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
782 783 784 785 786 787 788
    // 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);
789
    // After processing UDP query, the counter should be 1.
790
    EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_UDP_QUERY));
791 792
}

793 794 795
// Submit TCP normal query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPNormal) {
    // The counter should be initialized to 0.
796
    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
797 798 799 800 801 802 803
    // 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);
804
    // After processing TCP query, the counter should be 1.
805
    EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
806 807 808 809 810
}

// Submit TCP AXFR query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPAXFR) {
    // The counter should be initialized to 0.
811
    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
812 813 814 815
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                         Name("example.com"), RRClass::IN(), RRType::AXFR());
    createRequestPacket(request_message, IPPROTO_TCP);
    // On success, the AXFR query has been passed to a separate process,
Jelte Jansen's avatar
Jelte Jansen committed
816
    // so auth itself shouldn't respond.
817
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
Jelte Jansen's avatar
Jelte Jansen committed
818
    EXPECT_FALSE(dnsserv.hasAnswer());
819
    // After processing TCP AXFR query, the counter should be 1.
820
    EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
821 822
}

823 824 825
// Submit TCP IXFR query and check query counter
TEST_F(AuthSrvTest, queryCounterTCPIXFR) {
    // The counter should be initialized to 0.
826
    EXPECT_EQ(0, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
827 828 829
    UnitTestUtil::createRequestMessage(request_message, opcode, default_qid,
                         Name("example.com"), RRClass::IN(), RRType::IXFR());
    createRequestPacket(request_message, IPPROTO_TCP);
Jelte Jansen's avatar
Jelte Jansen committed
830 831
    // On success, the IXFR query has been passed to a separate process,
    // so auth itself shouldn't respond.
832
    server.processMessage(*io_message, parse_message, response_obuffer, &dnsserv);
Jelte Jansen's avatar
Jelte Jansen committed
833 834
    EXPECT_FALSE(dnsserv.hasAnswer());
    // After processing TCP IXFR query, the counter should be 1.
835
    EXPECT_EQ(1, server.getCounter(AuthCounters::SERVER_TCP_QUERY));
836 837
}

838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
// 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);
}

855
// Submit unexpected type of query and check it throws isc::Unexpected
856
TEST_F(AuthSrvTest, queryCounterUnexpected) {
857 858 859 860
    // 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.

861
    // Create UDP query packet.
862 863 864 865
    UnitTestUtil::createRequestMessage(request_message, Opcode::QUERY(),
                                       default_qid, Name("example.com"),
                                       RRClass::IN(), RRType::NS());
    createRequestPacket(request_message, IPPROTO_UDP);
866

867
    // Modify the message.
868 869
    delete io_message;
    endpoint = IOEndpoint::create(IPPROTO_UDP,
870
                                  IOAddress(DEFAULT_REMOTE_ADDRESS), 53210);
871 872 873 874 875
    io_message = new IOMessage(request_renderer.getData(),
                               request_renderer.getLength(),
                               getDummyUnknownSocket(), *endpoint);

    EXPECT_THROW(server.processMessage(*io_message, parse_message,
876
                                       response_obuffer, &dnsserv),
877
                 isc::Unexpected);
878
}
879 880 881 882 883

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.
884 885
    // If/when the interval timer has finer granularity we'll probably add
    // our own tests here, so we keep this empty test case.
886
}
887 888

TEST_F(AuthSrvTest, listenAddresses) {
889
    isc::testutils::portconfig::listenAddresses(server);
890
}
891

892
}