nc_trans_unittests.cc 51.5 KB
Newer Older
1
// Copyright (C) 2013-2015  Internet Systems Consortium, Inc. ("ISC")
2 3 4 5 6 7 8 9 10 11 12 13 14
//
// 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 16
#include <config.h>

17
#include <asiolink/io_service.h>
18
#include <asiolink/interval_timer.h>
19
#include <d2/nc_trans.h>
20 21 22 23 24
#include <dns/opcode.h>
#include <dns/messagerenderer.h>
#include <log/logger_support.h>
#include <log/macros.h>
#include <util/buffer.h>
25
#include <nc_test_utils.h>
26 27 28 29 30

#include <asio/ip/udp.hpp>
#include <asio/socket_base.hpp>
#include <asio.hpp>
#include <asio/error_code.hpp>
31 32 33

#include <boost/function.hpp>
#include <boost/bind.hpp>
34
#include <boost/date_time/posix_time/posix_time.hpp>
35 36 37 38 39
#include <gtest/gtest.h>

using namespace std;
using namespace isc;
using namespace isc::d2;
40
using namespace boost::posix_time;
41 42 43 44 45 46

namespace {

/// @brief Test derivation of NameChangeTransaction for exercising state
/// model mechanics.
///
47
/// This class facilitates testing by making non-public methods accessible so
48 49 50 51 52 53 54
/// they can be invoked directly in test routines.  It implements a very
/// rudimentary state model, sufficient to test the state model mechanics
/// supplied by the base class.
class NameChangeStub : public NameChangeTransaction {
public:

    // NameChangeStub states
55
    static const int DOING_UPDATE_ST = NCT_DERIVED_STATE_MIN + 1;
56 57

    // NameChangeStub events
58
    static const int SEND_UPDATE_EVT = NCT_DERIVED_EVENT_MIN + 2;
59

60 61
    bool use_stub_callback_;

62 63 64
    /// @brief Constructor
    ///
    /// Parameters match those needed by NameChangeTransaction.
65
    NameChangeStub(asiolink::IOServicePtr& io_service,
66
                   dhcp_ddns::NameChangeRequestPtr& ncr,
67 68 69
                   DdnsDomainPtr& forward_domain,
                   DdnsDomainPtr& reverse_domain,
                   D2CfgMgrPtr& cfg_mgr)
70
        : NameChangeTransaction(io_service, ncr, forward_domain,
71
                                reverse_domain, cfg_mgr),
72
                                use_stub_callback_(false) {
73 74 75 76 77 78
    }

    /// @brief Destructor
    virtual ~NameChangeStub() {
    }

79 80 81 82 83
    /// @brief DNSClient callback
    /// Overrides the callback in NameChangeTranscation to allow testing
    /// sendUpdate without incorporating exectution of the state model
    /// into the test.
    /// It sets the DNS status update and posts IO_COMPLETED_EVT as does
84
    /// the normal callback.
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
    virtual void operator()(DNSClient::Status status) {
        if (use_stub_callback_) {
            setDnsUpdateStatus(status);
            postNextEvent(IO_COMPLETED_EVT);
        } else {
            // For tests which need to use the real callback.
            NameChangeTransaction::operator()(status);
        }
    }

    /// @brief Some tests require a server to be selected.  This method
    /// selects the first server in the forward domain without involving
    /// state model execution to do so.
    bool selectFwdServer() {
        if (getForwardDomain()) {
            initServerSelection(getForwardDomain());
            selectNextServer();
            return (getCurrentServer());
        }

        return (false);
    }

108
    /// @brief Empty handler used to satisfy map verification.
109
    void dummyHandler() {
110 111
        isc_throw(NameChangeTransactionError,
                  "dummyHandler - invalid event: " << getContextStr());
112 113
    }

114
    /// @brief State handler for the READY_ST.
115
    ///
116
    /// Serves as the starting state handler, it consumes the
117 118
    /// START_EVT "transitioning" to the state, DOING_UPDATE_ST and
    /// sets the next event to SEND_UPDATE_EVT.
119 120
    void readyHandler() {
        switch(getNextEvent()) {
121 122
        case START_EVT:
            transition(DOING_UPDATE_ST, SEND_UPDATE_EVT);
123 124 125
            break;
        default:
            // its bogus
126 127
            isc_throw(NameChangeTransactionError,
                      "readyHandler - invalid event: " << getContextStr());
128 129 130
        }
    }

131
    /// @brief State handler for the DOING_UPDATE_ST.
132
    ///
133
    /// Simulates a state that starts some form of asynchronous work.
134
    /// When next event is SEND_UPDATE_EVT it sets the status to pending
135
    /// and signals the state model must "wait" for an event by setting
136
    /// next event to NOP_EVT.
137 138
    ///
    /// When next event is IO_COMPLETED_EVT, it transitions to the state,
139 140
    /// PROCESS_TRANS_OK_ST, and sets the next event to UPDATE_OK_EVT.
    void doingUpdateHandler() {
141
        switch(getNextEvent()) {
142
        case SEND_UPDATE_EVT:
143
            setNcrStatus(dhcp_ddns::ST_PENDING);
144
            postNextEvent(NOP_EVT);
145 146
            break;
        case IO_COMPLETED_EVT:
147 148 149 150 151 152
            if (getDnsUpdateStatus() == DNSClient::SUCCESS) {
                setForwardChangeCompleted(true);
                transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT);
            } else {
                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
            }
153 154 155
            break;
        default:
            // its bogus
156 157 158
            isc_throw(NameChangeTransactionError,
                      "doingUpdateHandler - invalid event: "
                      << getContextStr());
159 160 161
        }
    }

162
    /// @brief State handler for the PROCESS_TRANS_OK_ST.
163 164
    ///
    /// This is the last state in the model.  Note that it sets the
165
    /// status to completed and next event to NOP_EVT.
166
    void processTransDoneHandler() {
167
        switch(getNextEvent()) {
168
        case UPDATE_OK_EVT:
169
            setNcrStatus(dhcp_ddns::ST_COMPLETED);
170 171 172 173 174
            endModel();
            break;
        case UPDATE_FAILED_EVT:
            setNcrStatus(dhcp_ddns::ST_FAILED);
            endModel();
175 176 177
            break;
        default:
            // its bogus
178 179 180
            isc_throw(NameChangeTransactionError,
                      "processTransDoneHandler - invalid event: "
                      << getContextStr());
181 182 183
        }
    }

184
    /// @brief Construct the event dictionary.
185
    virtual void defineEvents() {
186
        // Invoke the base call implementation first.
187
        NameChangeTransaction::defineEvents();
188 189

        // Define our events.
190 191 192
        defineEvent(SEND_UPDATE_EVT, "SEND_UPDATE_EVT");
    }

193
    /// @brief Verify the event dictionary.
194
    virtual void verifyEvents() {
195
        // Invoke the base call implementation first.
196
        NameChangeTransaction::verifyEvents();
197 198

        // Define our events.
199 200 201
        getEvent(SEND_UPDATE_EVT);
    }

202 203 204 205 206 207 208
    /// @brief Construct the state dictionary.
    virtual void defineStates() {
        // Invoke the base call implementation first.
        NameChangeTransaction::defineStates();

        // Define our states.
        defineState(READY_ST, "READY_ST",
209
                             boost::bind(&NameChangeStub::readyHandler, this));
210

211
        defineState(SELECTING_FWD_SERVER_ST, "SELECTING_FWD_SERVER_ST",
212
                             boost::bind(&NameChangeStub::dummyHandler, this));
213

214
        defineState(SELECTING_REV_SERVER_ST, "SELECTING_REV_SERVER_ST",
215
                             boost::bind(&NameChangeStub::dummyHandler, this));
216

217
        defineState(DOING_UPDATE_ST, "DOING_UPDATE_ST",
218 219
                             boost::bind(&NameChangeStub::doingUpdateHandler,
                                         this));
220

221
        defineState(PROCESS_TRANS_OK_ST, "PROCESS_TRANS_OK_ST",
222 223
                             boost::bind(&NameChangeStub::
                                         processTransDoneHandler, this));
224

225
        defineState(PROCESS_TRANS_FAILED_ST, "PROCESS_TRANS_FAILED_ST",
226 227
                             boost::bind(&NameChangeStub::
                                         processTransDoneHandler, this));
228 229
    }

230 231 232 233
    /// @brief Verify the event dictionary.
    virtual void verifyStates() {
        // Invoke the base call implementation first.
        NameChangeTransaction::verifyStates();
234

235
        // Check our states.
236
        getState(DOING_UPDATE_ST);
237 238
    }

239
    // Expose the protected methods to be tested.
240
    using StateModel::runModel;
241 242 243
    using StateModel::postNextEvent;
    using StateModel::setState;
    using StateModel::initDictionaries;
244 245 246 247
    using NameChangeTransaction::initServerSelection;
    using NameChangeTransaction::selectNextServer;
    using NameChangeTransaction::getCurrentServer;
    using NameChangeTransaction::getDNSClient;
248
    using NameChangeTransaction::setNcrStatus;
249 250
    using NameChangeTransaction::setDnsUpdateRequest;
    using NameChangeTransaction::clearDnsUpdateRequest;
251 252
    using NameChangeTransaction::setDnsUpdateStatus;
    using NameChangeTransaction::getDnsUpdateResponse;
253 254
    using NameChangeTransaction::setDnsUpdateResponse;
    using NameChangeTransaction::clearDnsUpdateResponse;
255 256 257 258
    using NameChangeTransaction::getForwardChangeCompleted;
    using NameChangeTransaction::getReverseChangeCompleted;
    using NameChangeTransaction::setForwardChangeCompleted;
    using NameChangeTransaction::setReverseChangeCompleted;
259 260 261 262
    using NameChangeTransaction::setUpdateAttempts;
    using NameChangeTransaction::transition;
    using NameChangeTransaction::retryTransition;
    using NameChangeTransaction::sendUpdate;
263 264 265 266
    using NameChangeTransaction::prepNewRequest;
    using NameChangeTransaction::addLeaseAddressRdata;
    using NameChangeTransaction::addDhcidRdata;
    using NameChangeTransaction::addPtrRdata;
267 268
    using NameChangeTransaction::responseString;
    using NameChangeTransaction::transactionOutcomeString;
269 270
};

271 272 273 274
// Declare them so Gtest can see them.
const int NameChangeStub::DOING_UPDATE_ST;
const int NameChangeStub::SEND_UPDATE_EVT;

275
/// @brief Defines a pointer to a NameChangeStubPtr instance.
276 277 278 279 280 281
typedef boost::shared_ptr<NameChangeStub> NameChangeStubPtr;

/// @brief Test fixture for testing NameChangeTransaction
///
/// Note this class uses NameChangeStub class to exercise non-public
/// aspects of NameChangeTransaction.
282
class NameChangeTransactionTest : public TransactionTest {
283
public:
284
    NameChangeTransactionTest() {
285
    }
286

287 288 289
    virtual ~NameChangeTransactionTest() {
    }

290

291 292 293 294
    /// @brief  Instantiates a NameChangeStub test transaction
    /// The transaction is constructed around a predefined (i.e "canned")
    /// NameChangeRequest. The request has both forward and reverse DNS
    /// changes requested, and both forward and reverse domains are populated.
295 296
    /// @param tsig_key_info pointer to the TSIGKeyInfo to use, defaults to
    /// an empty pointer, in which case TSIG will not be used.
297 298 299 300 301 302 303 304 305
    NameChangeStubPtr makeCannedTransaction(const TSIGKeyInfoPtr&
                                            tsig_key_info = TSIGKeyInfoPtr()) {
        // Creates IPv4 remove request, forward, and reverse domains.
        setupForIPv4Transaction(dhcp_ddns::CHG_ADD, FWD_AND_REV_CHG,
                                tsig_key_info);

        // Now create the test transaction as would occur in update manager.
        // Instantiate the transaction as would be done by update manager.
        return (NameChangeStubPtr(new NameChangeStub(io_service_, ncr_,
306
                                  forward_domain_, reverse_domain_, cfg_mgr_)));
307 308
    }

309 310 311 312 313 314
    /// @brief  Instantiates a NameChangeStub test transaction
    /// The transaction is constructed around a predefined (i.e "canned")
    /// NameChangeRequest. The request has both forward and reverse DNS
    /// changes requested, and both forward and reverse domains are populated.
    /// @param key_name value to use to create TSIG key, if blank TSIG will not
    /// be used.
315
    NameChangeStubPtr makeCannedTransaction(const std::string& key_name) {
316
        // Creates IPv4 remove request, forward, and reverse domains.
317
        setupForIPv4Transaction(dhcp_ddns::CHG_ADD, FWD_AND_REV_CHG, key_name);
318

319
        // Now create the test transaction as would occur in update manager.
320
        // Instantiate the transaction as would be done by update manager.
321
        return (NameChangeStubPtr(new NameChangeStub(io_service_, ncr_,
322
                                  forward_domain_, reverse_domain_, cfg_mgr_)));
323 324
    }

325 326 327 328 329
    /// @brief Builds and then sends an update request
    ///
    /// This method is used to build and send and update request. It is used
    /// in conjuction with FauxServer to test various message response
    /// scenarios.
330 331 332
    /// @param name_change Transaction under test
    /// @param run_time Maximum time to permit IO processing to run before
    /// timing out (in milliseconds)
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354
    void doOneExchange(NameChangeStubPtr name_change,
                       unsigned int run_time = 500) {
        // Create a valid request for the transaction.
        D2UpdateMessagePtr req;
        ASSERT_NO_THROW(req.reset(new D2UpdateMessage(D2UpdateMessage::
                                                      OUTBOUND)));
        ASSERT_NO_THROW(name_change->setDnsUpdateRequest(req));
        req->setZone(dns::Name("request.example.com"), dns::RRClass::ANY());
        req->setRcode(dns::Rcode(dns::Rcode::NOERROR_CODE));

        // Set the flag to use the NameChangeStub's DNSClient callback.
        name_change->use_stub_callback_ = true;

        // Invoke sendUpdate.
        ASSERT_NO_THROW(name_change->sendUpdate());

        // Update attempt count should be 1, next event should be NOP_EVT.
        ASSERT_EQ(1, name_change->getUpdateAttempts());
        ASSERT_EQ(NameChangeTransaction::NOP_EVT,
                  name_change->getNextEvent());

        while (name_change->getNextEvent() == NameChangeTransaction::NOP_EVT) {
355
            int cnt = 0;
356 357 358 359 360 361
            ASSERT_NO_THROW(cnt = runTimedIO(run_time));
            if (cnt == 0) {
                FAIL() << "IO Service stopped unexpectedly";
            }
        }
    }
362 363 364 365 366 367 368 369 370 371 372
};

/// @brief Tests NameChangeTransaction construction.
/// This test verifies that:
/// 1. Construction with null NameChangeRequest
/// 2. Construction with null forward domain is not allowed when the request
/// requires forward change.
/// 3. Construction with null reverse domain is not allowed when the request
/// requires reverse change.
/// 4. Valid construction functions properly
TEST(NameChangeTransaction, construction) {
373
    asiolink::IOServicePtr io_service(new isc::asiolink::IOService());
374
    D2CfgMgrPtr cfg_mgr(new D2CfgMgr());
375 376 377 378 379 380

    const char* msg_str =
        "{"
        " \"change_type\" : 0 , "
        " \"forward_change\" : true , "
        " \"reverse_change\" : true , "
381
        " \"fqdn\" : \"example.com.\" , "
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396
        " \"ip_address\" : \"192.168.2.1\" , "
        " \"dhcid\" : \"0102030405060708\" , "
        " \"lease_expires_on\" : \"20130121132405\" , "
        " \"lease_length\" : 1300 "
        "}";

    dhcp_ddns::NameChangeRequestPtr ncr;

    dhcp_ddns::NameChangeRequestPtr empty_ncr;
    DnsServerInfoStoragePtr servers;
    DdnsDomainPtr forward_domain;
    DdnsDomainPtr reverse_domain;
    DdnsDomainPtr empty_domain;

    ASSERT_NO_THROW(ncr = dhcp_ddns::NameChangeRequest::fromJSON(msg_str));
397 398
    ASSERT_NO_THROW(forward_domain.reset(new DdnsDomain("*", servers)));
    ASSERT_NO_THROW(reverse_domain.reset(new DdnsDomain("*", servers)));
399

400
    // Verify that construction with a null IOServicePtr fails.
401
    // @todo Subject to change if multi-threading is implemented.
402
    asiolink::IOServicePtr empty;
403
    EXPECT_THROW(NameChangeTransaction(empty, ncr,
404
                                       forward_domain, reverse_domain, cfg_mgr),
405 406
                                       NameChangeTransactionError);

407
    // Verify that construction with an empty NameChangeRequest throws.
408
    EXPECT_THROW(NameChangeTransaction(io_service, empty_ncr,
409
                                       forward_domain, reverse_domain, cfg_mgr),
410 411
                                        NameChangeTransactionError);

412 413 414 415 416 417 418 419
    // Verify that construction with an empty D2CfgMgr throws.
    D2CfgMgrPtr empty_cfg;
    EXPECT_THROW(NameChangeTransaction(io_service, empty_ncr,
                                       forward_domain, reverse_domain,
                                       empty_cfg),
                                       NameChangeTransactionError);


420
    // Verify that construction with an empty forward domain when the
421
    // NameChangeRequest calls for a forward change throws.
422
    EXPECT_THROW(NameChangeTransaction(io_service, ncr,
423
                                       empty_domain, reverse_domain, cfg_mgr),
424 425 426
                                       NameChangeTransactionError);

    // Verify that construction with an empty reverse domain when the
427
    // NameChangeRequest calls for a reverse change throws.
428
    EXPECT_THROW(NameChangeTransaction(io_service, ncr,
429
                                       forward_domain, empty_domain, cfg_mgr),
430 431 432 433
                                       NameChangeTransactionError);

    // Verify that a valid construction attempt works.
    EXPECT_NO_THROW(NameChangeTransaction(io_service, ncr,
434 435
                                          forward_domain, reverse_domain,
                                          cfg_mgr));
436 437

    // Verify that an empty forward domain is allowed when the requests does
438
    // not include a forward change.
439
    ncr->setForwardChange(false);
440
    ncr->setReverseChange(true);
441
    EXPECT_NO_THROW(NameChangeTransaction(io_service, ncr,
442 443
                                          empty_domain, reverse_domain,
                                          cfg_mgr));
444 445

    // Verify that an empty reverse domain is allowed when the requests does
446 447
    // not include a reverse change.
    ncr->setForwardChange(true);
448 449
    ncr->setReverseChange(false);
    EXPECT_NO_THROW(NameChangeTransaction(io_service, ncr,
450 451
                                          forward_domain, empty_domain,
                                          cfg_mgr));
452 453
}

454 455 456
/// @brief General testing of member accessors.
/// Most if not all of these are also tested as a byproduct off larger tests.
TEST_F(NameChangeTransactionTest, accessors) {
457 458 459
    NameChangeStubPtr name_change;
    ASSERT_NO_THROW(name_change = makeCannedTransaction());

460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496
    // Verify that fetching the NameChangeRequest works.
    dhcp_ddns::NameChangeRequestPtr ncr = name_change->getNcr();
    ASSERT_TRUE(ncr);

    // Verify that getTransactionKey works.
    EXPECT_EQ(ncr->getDhcid(), name_change->getTransactionKey());

    // Verify that NcrStatus can be set and retrieved.
    EXPECT_NO_THROW(name_change->setNcrStatus(dhcp_ddns::ST_FAILED));
    EXPECT_EQ(dhcp_ddns::ST_FAILED, ncr->getStatus());

    // Verify that the forward domain can be retrieved.
    ASSERT_TRUE(name_change->getForwardDomain());
    EXPECT_EQ(forward_domain_, name_change->getForwardDomain());

    // Verify that the reverse domain can be retrieved.
    ASSERT_TRUE(name_change->getReverseDomain());
    EXPECT_EQ(reverse_domain_, name_change->getReverseDomain());

    // Neither of these have direct setters, but are tested under server
    // selection.
    EXPECT_FALSE(name_change->getDNSClient());
    EXPECT_FALSE(name_change->getCurrentServer());

    // Verify that DNS update status can be set and retrieved.
    EXPECT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::TIMEOUT));
    EXPECT_EQ(DNSClient::TIMEOUT, name_change->getDnsUpdateStatus());

    // Verify that the forward change complete flag can be set and fetched.
    EXPECT_NO_THROW(name_change->setForwardChangeCompleted(true));
    EXPECT_TRUE(name_change->getForwardChangeCompleted());

    // Verify that the reverse change complete flag can be set and fetched.
    EXPECT_NO_THROW(name_change->setReverseChangeCompleted(true));
    EXPECT_TRUE(name_change->getReverseChangeCompleted());
}

497
/// @brief Tests DNS update request accessor methods.
498
TEST_F(NameChangeTransactionTest, dnsUpdateRequestAccessors) {
499
    // Create a transaction.
500 501 502
    NameChangeStubPtr name_change;
    ASSERT_NO_THROW(name_change = makeCannedTransaction());

503
    // Post transaction construction, there should not be an update request.
504 505
    EXPECT_FALSE(name_change->getDnsUpdateRequest());

506 507 508 509 510
    // Create a request.
    D2UpdateMessagePtr req;
    ASSERT_NO_THROW(req.reset(new D2UpdateMessage(D2UpdateMessage::OUTBOUND)));

    // Use the setter and then verify we can fetch the request.
511 512 513
    ASSERT_NO_THROW(name_change->setDnsUpdateRequest(req));

    // Post set, we should be able to fetch it.
514
    ASSERT_TRUE(name_change->getDnsUpdateRequest());
515 516 517 518 519 520 521 522

    // Should be able to clear it.
    ASSERT_NO_THROW(name_change->clearDnsUpdateRequest());

    // Should be empty again.
    EXPECT_FALSE(name_change->getDnsUpdateRequest());
}

523
/// @brief Tests DNS update request accessor methods.
524
TEST_F(NameChangeTransactionTest, dnsUpdateResponseAccessors) {
525
    // Create a transaction.
526 527 528
    NameChangeStubPtr name_change;
    ASSERT_NO_THROW(name_change = makeCannedTransaction());

529 530 531 532
    // Post transaction construction, there should not be an update response.
    EXPECT_FALSE(name_change->getDnsUpdateResponse());

    // Create a response.
533 534 535
    D2UpdateMessagePtr resp;
    ASSERT_NO_THROW(resp.reset(new D2UpdateMessage(D2UpdateMessage::INBOUND)));

536
    // Use the setter and then verify we can fetch the response.
537 538 539 540 541 542 543 544 545 546
    ASSERT_NO_THROW(name_change->setDnsUpdateResponse(resp));

    // Post set, we should be able to fetch it.
    EXPECT_TRUE(name_change->getDnsUpdateResponse());

    // Should be able to clear it.
    ASSERT_NO_THROW(name_change->clearDnsUpdateResponse());

    // Should be empty again.
    EXPECT_FALSE(name_change->getDnsUpdateResponse());
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583

}

/// @brief Tests responseString method.
TEST_F(NameChangeTransactionTest, responseString) {
    // Create a transaction.
    NameChangeStubPtr name_change;
    ASSERT_NO_THROW(name_change = makeCannedTransaction());

    // Make sure it is safe to call when status says success but there
    // is no update response.
    ASSERT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::SUCCESS));
    EXPECT_EQ("SUCCESS, rcode:  update response is NULL",
              name_change->responseString());

    // Create a response. (We use an OUTBOUND message so we can set RCODE)
    D2UpdateMessagePtr resp;
    ASSERT_NO_THROW(resp.reset(new D2UpdateMessage(D2UpdateMessage::OUTBOUND)));
    ASSERT_NO_THROW(name_change->setDnsUpdateResponse(resp));

    // Make sure we decode Rcode when status is successful.
    ASSERT_NO_THROW(resp->setRcode(dns::Rcode::NXDOMAIN()));
    EXPECT_EQ("SUCCESS, rcode: NXDOMAIN", name_change->responseString());

    // Test all of the non-success values for status.
    ASSERT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::TIMEOUT));
    EXPECT_EQ("TIMEOUT", name_change->responseString());

    ASSERT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::IO_STOPPED));
    EXPECT_EQ("IO_STOPPED", name_change->responseString());

    ASSERT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::
                                                    INVALID_RESPONSE));
    EXPECT_EQ("INVALID_RESPONSE", name_change->responseString());

    ASSERT_NO_THROW(name_change->setDnsUpdateStatus(DNSClient::OTHER));
    EXPECT_EQ("OTHER", name_change->responseString());
584 585
}

586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
/// @brief Tests transactionOutcomeString method.
TEST_F(NameChangeTransactionTest, transactionOutcomeString) {
    // Create a transaction.
    NameChangeStubPtr name_change;
    dhcp_ddns::NameChangeRequestPtr ncr;
    ASSERT_NO_THROW(name_change = makeCannedTransaction());
    ncr = name_change->getNcr();

    // Check case of failed transaction in both directions
    std::string exp_str("Status: Failed, Event: UNDEFINED,  Forward change:"
                        " failed,  Reverse change: failed,  request: ");
    exp_str += ncr->toText();

    std::string tstring = name_change->transactionOutcomeString();
    std::cout << "tstring is: [" << tstring << "]" << std::endl;

    EXPECT_EQ(exp_str, name_change->transactionOutcomeString());

    // Check case of success all around
    name_change->setNcrStatus(dhcp_ddns::ST_COMPLETED);
    name_change->setForwardChangeCompleted(true);
    name_change->setReverseChangeCompleted(true);

    exp_str = "Status: Completed, Event: UNDEFINED,  Forward change: completed,"
              "  Reverse change: completed,  request: " + ncr->toText();
    EXPECT_EQ(exp_str, name_change->transactionOutcomeString());

    // Check case of success, with no forward change
    name_change->setNcrStatus(dhcp_ddns::ST_COMPLETED);
    ncr->setForwardChange(false);
    exp_str = "Status: Completed, Event: UNDEFINED, "
              " Reverse change: completed,  request: " + ncr->toText();
    EXPECT_EQ(exp_str, name_change->transactionOutcomeString());

    // Check case of success, with no reverse change
    name_change->setNcrStatus(dhcp_ddns::ST_COMPLETED);
    ncr->setForwardChange(true);
    ncr->setReverseChange(false);
    exp_str = "Status: Completed, Event: UNDEFINED, "
              " Forward change: completed,  request: " + ncr->toText();
    EXPECT_EQ(exp_str, name_change->transactionOutcomeString());
}
628

629 630
/// @brief Tests event and state dictionary construction and verification.
TEST_F(NameChangeTransactionTest, dictionaryCheck) {
631
    NameChangeStubPtr name_change;
632 633
    ASSERT_NO_THROW(name_change = makeCannedTransaction());

634 635 636 637
    // Verify that the event and state dictionary validation fails prior
    // dictionary construction.
    ASSERT_THROW(name_change->verifyEvents(), StateModelError);
    ASSERT_THROW(name_change->verifyStates(), StateModelError);
638

639 640 641
    // Construct both dictionaries.
    ASSERT_NO_THROW(name_change->defineEvents());
    ASSERT_NO_THROW(name_change->defineStates());
642

643 644 645
    // Verify both event and state dictionaries now pass validation.
    ASSERT_NO_THROW(name_change->verifyEvents());
    ASSERT_NO_THROW(name_change->verifyStates());
646
}
647

648 649 650 651 652 653
/// @brief Tests server selection methods.
/// Each transaction has a list of one or more servers for each DNS direction
/// it is required to update.  The transaction must be able to start at the
/// beginning of a server list and cycle through them one at time, as needed,
/// when a DNS exchange fails due to an IO error.  This test verifies the
/// ability to iteratively select a server from the list as the current server.
654 655 656 657
TEST_F(NameChangeTransactionTest, serverSelectionTest) {
    NameChangeStubPtr name_change;
    ASSERT_NO_THROW(name_change = makeCannedTransaction());

658
    // Verify that the forward domain and its list of servers can be retrieved.
659 660 661 662 663 664 665 666 667
    DdnsDomainPtr& domain = name_change->getForwardDomain();
    ASSERT_TRUE(domain);
    DnsServerInfoStoragePtr servers = domain->getServers();
    ASSERT_TRUE(servers);

    // Get the number of entries in the server list.
    int num_servers = servers->size();
    ASSERT_TRUE(num_servers > 0);

668 669 670
    // Verify that we can initialize server selection.  This "resets" the
    // selection process to start over using the list of servers in the
    // given domain.
671 672
    ASSERT_NO_THROW(name_change->initServerSelection(domain));

673 674 675 676 677
    // The server selection process determines the current server,
    // instantiates a new DNSClient, and a DNS response message buffer.
    //  We need to save the values before each selection, so we can verify
    // they are correct after each selection.
    DnsServerInfoPtr prev_server = name_change->getCurrentServer();
678
    DNSClientPtr prev_client = name_change->getDNSClient();
679 680 681 682 683 684 685 686 687 688

    // Verify response pointer is empty.
    EXPECT_FALSE(name_change->getDnsUpdateResponse());

    // Create dummy response so we can verify it is cleared at each
    // new server select.
    D2UpdateMessagePtr dummyResp;
    dummyResp.reset(new D2UpdateMessage(D2UpdateMessage::INBOUND));
    ASSERT_NO_THROW(name_change->setDnsUpdateResponse(dummyResp));
    ASSERT_TRUE(name_change->getDnsUpdateResponse());
689 690 691 692

    // Iteratively select through the list of servers.
    int passes = 0;
    while (name_change->selectNextServer()) {
693
        // Get the new values after the selection has been made.
694 695 696
        DnsServerInfoPtr server = name_change->getCurrentServer();
        DNSClientPtr client = name_change->getDNSClient();
        D2UpdateMessagePtr response = name_change->getDnsUpdateResponse();
697 698

        // Verify that the new values are not empty.
699 700
        EXPECT_TRUE(server);
        EXPECT_TRUE(client);
701 702 703

        // Verify response pointer is now empty.
        EXPECT_FALSE(name_change->getDnsUpdateResponse());
704

705
        // Verify that the new values are indeed new.
706 707 708
        EXPECT_NE(server, prev_server);
        EXPECT_NE(client, prev_client);

709
        // Remember the selected values for the next pass.
710 711
        prev_server = server;
        prev_client = client;
712 713 714 715 716

        // Create new dummy response.
        dummyResp.reset(new D2UpdateMessage(D2UpdateMessage::INBOUND));
        ASSERT_NO_THROW(name_change->setDnsUpdateResponse(dummyResp));
        ASSERT_TRUE(name_change->getDnsUpdateResponse());
717

718
        ++passes;
719 720
    }

721
    // Verify that the number of passes made equal the number of servers.
722 723 724
    EXPECT_EQ (passes, num_servers);

    // Repeat the same test using the reverse domain.
725
    // Verify that the reverse domain and its list of servers can be retrieved.
726 727 728 729 730
    domain = name_change->getReverseDomain();
    ASSERT_TRUE(domain);
    servers = domain->getServers();
    ASSERT_TRUE(servers);

731
    // Get the number of entries in the server list.
732 733 734
    num_servers = servers->size();
    ASSERT_TRUE(num_servers > 0);

735 736 737
    // Verify that we can initialize server selection.  This "resets" the
    // selection process to start over using the list of servers in the
    // given domain.
738 739
    ASSERT_NO_THROW(name_change->initServerSelection(domain));

740
    // The server selection process determines the current server,
741
    // instantiates a new DNSClient, and resets the DNS response message buffer.
742 743 744
    // We need to save the values before each selection, so we can verify
    // they are correct after each selection.
    prev_server = name_change->getCurrentServer();
745 746
    prev_client = name_change->getDNSClient();

747
    // Iteratively select through the list of servers.
748 749
    passes = 0;
    while (name_change->selectNextServer()) {
750
        // Get the new values after the selection has been made.
751 752 753
        DnsServerInfoPtr server = name_change->getCurrentServer();
        DNSClientPtr client = name_change->getDNSClient();
        D2UpdateMessagePtr response = name_change->getDnsUpdateResponse();
754 755

        // Verify that the new values are not empty.
756 757
        EXPECT_TRUE(server);
        EXPECT_TRUE(client);
758 759 760

        // Verify response pointer is now empty.
        EXPECT_FALSE(name_change->getDnsUpdateResponse());
761

762
        // Verify that the new values are indeed new.
763 764 765
        EXPECT_NE(server, prev_server);
        EXPECT_NE(client, prev_client);

766
        // Remember the selected values for the next pass.
767 768
        prev_server = server;
        prev_client = client;
769 770 771 772 773

        // Create new dummy response.
        dummyResp.reset(new D2UpdateMessage(D2UpdateMessage::INBOUND));
        ASSERT_NO_THROW(name_change->setDnsUpdateResponse(dummyResp));
        ASSERT_TRUE(name_change->getDnsUpdateResponse());
774

775
        ++passes;
776 777
    }

778
    // Verify that the number of passes made equal the number of servers.
779 780 781
    EXPECT_EQ (passes, num_servers);
}

782 783 784 785 786
/// @brief Tests that the transaction will be "failed" upon model errors.
TEST_F(NameChangeTransactionTest, modelFailure) {
    NameChangeStubPtr name_change;
    ASSERT_NO_THROW(name_change = makeCannedTransaction());

787 788 789
    // Now call runModel() with an undefined event which should not throw,
    // but should result in a failed model and failed transaction.
    EXPECT_NO_THROW(name_change->runModel(9999));
790 791 792 793 794 795 796 797 798

    // Verify that the model reports are done but failed.
    EXPECT_TRUE(name_change->isModelDone());
    EXPECT_TRUE(name_change->didModelFail());

    // Verify that the transaction has failed.
    EXPECT_EQ(dhcp_ddns::ST_FAILED, name_change->getNcrStatus());
}

799
/// @brief Tests the ability to use startTransaction to initiate the state
800
/// model execution, and DNSClient callback, operator(), to resume the
801
/// model with a update successful outcome.
802 803 804
TEST_F(NameChangeTransactionTest, successfulUpdateTest) {
    NameChangeStubPtr name_change;
    ASSERT_NO_THROW(name_change = makeCannedTransaction());
805
    ASSERT_TRUE(name_change->selectFwdServer());
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837

    EXPECT_TRUE(name_change->isModelNew());
    EXPECT_FALSE(name_change->getForwardChangeCompleted());

    // Launch the transaction by calling startTransaction.  The state model
    // should run up until the "IO" operation is initiated in DOING_UPDATE_ST.
    ASSERT_NO_THROW(name_change->startTransaction());

    // Verify that the model is running but waiting, and that forward change
    // completion is still false.
    EXPECT_TRUE(name_change->isModelRunning());
    EXPECT_TRUE(name_change->isModelWaiting());
    EXPECT_FALSE(name_change->getForwardChangeCompleted());

    // Simulate completion of DNSClient exchange by invoking the callback, as
    // DNSClient would.  This should cause the state model to progress through
    // completion.
    EXPECT_NO_THROW((*name_change)(DNSClient::SUCCESS));

    // The model should have worked through to completion.
    // Verify that the model is done and not failed.
    EXPECT_TRUE(name_change->isModelDone());
    EXPECT_FALSE(name_change->didModelFail());

    // Verify that NCR status is completed, and that the forward change
    // was completed.
    EXPECT_EQ(dhcp_ddns::ST_COMPLETED, name_change->getNcrStatus());
    EXPECT_TRUE(name_change->getForwardChangeCompleted());
}

/// @brief Tests the ability to use startTransaction to initate the state
/// model execution, and DNSClient callback, operator(), to resume the
838
/// model with a update failure outcome.
839 840 841
TEST_F(NameChangeTransactionTest, failedUpdateTest) {
    NameChangeStubPtr name_change;
    ASSERT_NO_THROW(name_change = makeCannedTransaction());
842
    ASSERT_TRUE(name_change->selectFwdServer());
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869

    // Launch the transaction by calling startTransaction.  The state model
    // should run up until the "IO" operation is initiated in DOING_UPDATE_ST.
    ASSERT_NO_THROW(name_change->startTransaction());

    // Vefity that the model is running but waiting, and that the forward
    // change has not been completed.
    EXPECT_TRUE(name_change->isModelRunning());
    EXPECT_TRUE(name_change->isModelWaiting());
    EXPECT_FALSE(name_change->getForwardChangeCompleted());

    // Simulate completion of DNSClient exchange by invoking the callback, as
    // DNSClient would.  This should cause the state model to progress through
    // to completion.
    EXPECT_NO_THROW((*name_change)(DNSClient::TIMEOUT));

    // The model should have worked through to completion.
    // Verify that the model is done and not failed.
    EXPECT_TRUE(name_change->isModelDone());
    EXPECT_FALSE(name_change->didModelFail());

    // Verify that the NCR status is failed and that the forward change
    // was not completed.
    EXPECT_EQ(dhcp_ddns::ST_FAILED, name_change->getNcrStatus());
    EXPECT_FALSE(name_change->getForwardChangeCompleted());
}

870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886
/// @brief Tests update attempt accessors.
TEST_F(NameChangeTransactionTest, updateAttempts) {
    NameChangeStubPtr name_change;
    ASSERT_NO_THROW(name_change = makeCannedTransaction());

    // Post transaction construction, update attempts should be 0.
    EXPECT_EQ(0, name_change->getUpdateAttempts());

    // Set it to a known value.
    name_change->setUpdateAttempts(5);

    // Verify that the value is as expected.
    EXPECT_EQ(5, name_change->getUpdateAttempts());
}

/// @brief Tests retryTransition method
///
887
/// Verifies that while the maximum number of update attempts has not
888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910
/// been exceeded, the method will leave the state unchanged but post a
/// SERVER_SELECTED_EVT.  Once the maximum is exceeded, the method should
/// transition to the state given with a next event of SERVER_IO_ERROR_EVT.
TEST_F(NameChangeTransactionTest, retryTransition) {
    // Create the transaction.
    NameChangeStubPtr name_change;
    ASSERT_NO_THROW(name_change = makeCannedTransaction());

    // Define dictionaries.
    ASSERT_NO_THROW(name_change->initDictionaries());

    // Transition to a known spot.
    ASSERT_NO_THROW(name_change->transition(
                    NameChangeStub::DOING_UPDATE_ST,
                    NameChangeStub::SEND_UPDATE_EVT));

    // Verify we are at the known spot.
    ASSERT_EQ(NameChangeStub::DOING_UPDATE_ST,
              name_change->getCurrState());
    ASSERT_EQ(NameChangeStub::SEND_UPDATE_EVT,
              name_change->getNextEvent());

    // Verify that we have not exceeded maximum number of attempts.
911 912
    ASSERT_LT(name_change->getUpdateAttempts(),
              NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER);
913 914 915 916 917

    // Call retryTransition.
    ASSERT_NO_THROW(name_change->retryTransition(
                    NameChangeTransaction::PROCESS_TRANS_FAILED_ST));

918
    // Since the number of update attempts is less than the maximum allowed
919 920 921 922 923 924 925 926 927 928 929 930 931 932
    // we should remain in our current state but with next event of
    // SERVER_SELECTED_EVT posted.
    ASSERT_EQ(NameChangeStub::DOING_UPDATE_ST,
              name_change->getCurrState());
    ASSERT_EQ(NameChangeTransaction::SERVER_SELECTED_EVT,
              name_change->getNextEvent());

    // Now set the number of attempts to the maximum.
    name_change->setUpdateAttempts(NameChangeTransaction::
                                   MAX_UPDATE_TRIES_PER_SERVER);
    // Call retryTransition.
    ASSERT_NO_THROW(name_change->retryTransition(
                    NameChangeTransaction::PROCESS_TRANS_FAILED_ST));

933 934
    // Since we have exceeded maximum attempts, we should transition to
    // PROCESS_UPDATE_FAILED_ST with a next event of SERVER_IO_ERROR_EVT.
935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957
    ASSERT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
              name_change->getCurrState());
    ASSERT_EQ(NameChangeTransaction::SERVER_IO_ERROR_EVT,
              name_change->getNextEvent());
}

/// @brief Tests sendUpdate method when underlying doUpdate throws.
///
/// DNSClient::doUpdate can throw for a variety of reasons. This tests
/// sendUpdate handling of such a throw by passing doUpdate a request
/// that will not render.
TEST_F(NameChangeTransactionTest, sendUpdateDoUpdateFailure) {
    NameChangeStubPtr name_change;
    ASSERT_NO_THROW(name_change = makeCannedTransaction());
    ASSERT_NO_THROW(name_change->initDictionaries());
    ASSERT_TRUE(name_change->selectFwdServer());

    // Set the transaction's request to an empty DNS update.
    D2UpdateMessagePtr req;
    ASSERT_NO_THROW(req.reset(new D2UpdateMessage(D2UpdateMessage::OUTBOUND)));
    ASSERT_NO_THROW(name_change->setDnsUpdateRequest(req));

    // Verify that sendUpdate does not throw, but it should fail because
958
    // the request won't render.
959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974
    ASSERT_NO_THROW(name_change->sendUpdate());

    // Verify that we transition to failed state and event.
    ASSERT_EQ(NameChangeTransaction::PROCESS_TRANS_FAILED_ST,
              name_change->getCurrState());
    ASSERT_EQ(NameChangeTransaction::UPDATE_FAILED_EVT,
              name_change->getNextEvent());
}

/// @brief Tests sendUpdate method when underlying doUpdate times out.
TEST_F(NameChangeTransactionTest, sendUpdateTimeout) {
    NameChangeStubPtr name_change;
    ASSERT_NO_THROW(name_change = makeCannedTransaction());
    ASSERT_NO_THROW(name_change->initDictionaries());
    ASSERT_TRUE(name_change->selectFwdServer());

975 976 977
    // Build a valid request, call sendUpdate and process the response.
    // Note we have to wait for DNSClient timeout plus a bit more to allow
    // DNSClient to timeout.
978 979 980 981
    // The method, doOneExchange, can suffer fatal assertions which invalidate
    // not only it but the invoking test as well. In other words, if the
    // doOneExchange blows up the rest of test is pointless. I use
    // ASSERT_NO_FATAL_FAILURE to abort the test immediately.
982 983 984 985 986

    D2ParamsPtr d2_params = cfg_mgr_->getD2Params();
    size_t timeout = d2_params->getDnsServerTimeout() + 100;

    ASSERT_NO_FATAL_FAILURE(doOneExchange(name_change, timeout));
987 988 989 990 991 992 993

    // Verify that next event is IO_COMPLETED_EVT and DNS status is TIMEOUT.
    ASSERT_EQ(NameChangeTransaction::IO_COMPLETED_EVT,
              name_change->getNextEvent());
    ASSERT_EQ(DNSClient::TIMEOUT, name_change->getDnsUpdateStatus());
}

994
/// @brief Tests sendUpdate method when it receives a corrupt response from
995 996 997 998 999 1000 1001 1002
/// the server.
TEST_F(NameChangeTransactionTest, sendUpdateCorruptResponse) {
    NameChangeStubPtr name_change;
    ASSERT_NO_THROW(name_change = makeCannedTransaction());
    ASSERT_NO_THROW(name_change->initDictionaries());
    ASSERT_TRUE(name_change->selectFwdServer());

    // Create a server and start it listening.
1003
    FauxServer server(*io_service_, *(name_change->getCurrentServer()));
1004 1005
    server.receive(FauxServer::CORRUPT_RESP);

1006 1007
    // Build a valid request, call sendUpdate and process the response.
    ASSERT_NO_FATAL_FAILURE(doOneExchange(name_change));
1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022

    // Verify that next event is IO_COMPLETED_EVT and DNS status is INVALID.
    ASSERT_EQ(NameChangeTransaction::IO_COMPLETED_EVT,
              name_change->getNextEvent());
    ASSERT_EQ(DNSClient::INVALID_RESPONSE, name_change->getDnsUpdateStatus());
}

/// @brief Tests sendUpdate method when the exchange succeeds.
TEST_F(NameChangeTransactionTest, sendUpdate) {
    NameChangeStubPtr name_change;
    ASSERT_NO_THROW(name_change = makeCannedTransaction());
    ASSERT_NO_THROW(name_change->initDictionaries());
    ASSERT_TRUE(name_change->selectFwdServer());

    // Create a server and start it listening.
1023
    FauxServer server(*io_service_, *(name_change->getCurrentServer()));
1024 1025
    server.receive (FauxServer::USE_RCODE, dns::Rcode::NOERROR());

1026 1027
    // Build a valid request, call sendUpdate and process the response.
    ASSERT_NO_FATAL_FAILURE(doOneExchange(name_change));
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043

    // Verify that next event is IO_COMPLETED_EVT and DNS status is SUCCESS.
    ASSERT_EQ(NameChangeTransaction::IO_COMPLETED_EVT,
              name_change->getNextEvent());
    ASSERT_EQ(DNSClient::SUCCESS, name_change->getDnsUpdateStatus());

    // Verify that we have a response and it's Rcode is NOERROR,
    // and the zone is as expected.
    D2UpdateMessagePtr response = name_change->getDnsUpdateResponse();
    ASSERT_TRUE(response);
    ASSERT_EQ(dns::Rcode::NOERROR().getCode(), response->getRcode().getCode());
    D2ZonePtr zone = response->getZone();
    EXPECT_TRUE(zone);
    EXPECT_EQ("response.example.com.", zone->getName().toText());
}

1044 1045 1046
/// @brief Tests that an unsigned response to a signed request is an error
TEST_F(NameChangeTransactionTest, tsigUnsignedResponse) {
    NameChangeStubPtr name_change;
1047
    ASSERT_NO_THROW(name_change = makeCannedTransaction("key_one"));
1048 1049 1050 1051 1052 1053 1054
    ASSERT_NO_THROW(name_change->initDictionaries());
    ASSERT_TRUE(name_change->selectFwdServer());

    // Create a server and start it listening.
    FauxServer server(*io_service_, *(name_change->getCurrentServer()));
    server.receive (FauxServer::USE_RCODE, dns::Rcode::NOERROR());

1055
    // Do the udpate.
1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076
    ASSERT_NO_FATAL_FAILURE(doOneExchange(name_change));

    // Verify that next event is IO_COMPLETED_EVT and DNS status is
    // INVALID_RESPONSE.
    ASSERT_EQ(NameChangeTransaction::IO_COMPLETED_EVT,
              name_change->getNextEvent());

    ASSERT_EQ(DNSClient::INVALID_RESPONSE, name_change->getDnsUpdateStatus());

    // When TSIG errors occur, only the message header (including Rcode) is
    // unpacked.  In this case, it should be NOERROR but have no other
    // information.
    D2UpdateMessagePtr response = name_change->getDnsUpdateResponse();
    ASSERT_TRUE(response);
    ASSERT_EQ(dns::Rcode::NOERROR().getCode(), response->getRcode().getCode());
    EXPECT_FALSE(response->getZone());
}

/// @brief Tests that a response signed with the wrong key is an error
TEST_F(NameChangeTransactionTest, tsigInvalidResponse) {
    NameChangeStubPtr name_change;
1077
    ASSERT_NO_THROW(name_change = makeCannedTransaction("key_one"));
1078 1079 1080 1081 1082 1083 1084 1085
    ASSERT_NO_THROW(name_change->initDictionaries());
    ASSERT_TRUE(name_change->selectFwdServer());

    // Create a server, tell it to sign responses with a "random" key,
    // then start it listening.
    FauxServer server(*io_service_, *(name_change->getCurrentServer()));
    server.receive (FauxServer::INVALID_TSIG, dns::Rcode::NOERROR());

1086
    // Do the udpate.
1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136
    ASSERT_NO_FATAL_FAILURE(doOneExchange(name_change));

    // Verify that next event is IO_COMPLETED_EVT and DNS status is
    // INVALID_RESPONSE.
    ASSERT_EQ(NameChangeTransaction::IO_COMPLETED_EVT,
              name_change->getNextEvent());

    ASSERT_EQ(DNSClient::INVALID_RESPONSE, name_change->getDnsUpdateStatus());

    // When TSIG errors occur, only the message header (including Rcode) is
    // unpacked.  In this case, it should be NOERROR but have no other
    // information.
    D2UpdateMessagePtr response = name_change->getDnsUpdateResponse();
    ASSERT_TRUE(response);
    ASSERT_EQ(dns::Rcode::NOERROR().getCode(), response->getRcode().getCode());
    EXPECT_FALSE(response->getZone());
}

/// @brief Tests that a signed response to an unsigned request is ok.
/// Currently our policy is to accept a signed response to an unsigned request
/// even though the spec says a server MUST not do that.
TEST_F(NameChangeTransactionTest, tsigUnexpectedSignedResponse) {
    NameChangeStubPtr name_change;
    ASSERT_NO_THROW(name_change = makeCannedTransaction());
    ASSERT_NO_THROW(name_change->initDictionaries());
    ASSERT_TRUE(name_change->selectFwdServer());

    // Create a server, tell it to sign responses with a "random" key,
    // then start it listening.
    FauxServer server(*io_service_, *(name_change->getCurrentServer()));
    server.receive (FauxServer::INVALID_TSIG, dns::Rcode::NOERROR());

    // Perform an update without TSIG.
    ASSERT_NO_FATAL_FAILURE(doOneExchange(name_change));

    // Verify that next event is IO_COMPLETED_EVT and DNS status is SUCCESS.
    ASSERT_EQ(NameChangeTransaction::IO_COMPLETED_EVT,
              name_change->getNextEvent());

    ASSERT_EQ(DNSClient::SUCCESS, name_change->getDnsUpdateStatus());

    D2UpdateMessagePtr response = name_change->getDnsUpdateResponse();
    ASSERT_TRUE(response);
    ASSERT_EQ(dns::Rcode::NOERROR().getCode(), response->getRcode().getCode());
    D2ZonePtr zone = response->getZone();
    EXPECT_TRUE(zone);
    EXPECT_EQ("response.example.com.", zone->getName().toText());
}

/// @brief Tests that a TSIG udpate succeeds when client and server both use
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147
/// the right key.  Runs the test for all supported algorithms.
TEST_F(NameChangeTransactionTest, tsigAllValid) {
    std::vector<std::string>algorithms;
    algorithms.push_back(TSIGKeyInfo::HMAC_MD5_STR);
    algorithms.push_back(TSIGKeyInfo::HMAC_SHA1_STR);
    algorithms.push_back(TSIGKeyInfo::HMAC_SHA224_STR);
    algorithms.push_back(TSIGKeyInfo::HMAC_SHA256_STR);
    algorithms.push_back(TSIGKeyInfo::HMAC_SHA384_STR);
    algorithms.push_back(TSIGKeyInfo::HMAC_SHA512_STR);

    for (int i = 0; i < algorithms.size(); ++i) {
1148
        SCOPED_TRACE (algorithms[i]);
1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171
        TSIGKeyInfoPtr key;
        ASSERT_NO_THROW(key.reset(new TSIGKeyInfo("test_key",
                                                  algorithms[i],
                                                  "GWG/Xfbju4O2iXGqkSu4PQ==")));
        NameChangeStubPtr name_change;
        ASSERT_NO_THROW(name_change = makeCannedTransaction(key));
        ASSERT_NO_THROW(name_change->initDictionaries());
        ASSERT_TRUE(name_change->selectFwdServer());

        // Create a server, set its TSIG key, and then start it listening.
        FauxServer server(*io_service_, *(name_change->getCurrentServer()));
        // Since we create a new server instance each time we need to tell
        // it not reschedule receives automatically.
        server.perpetual_receive_ = false;
        server.setTSIGKey(key->getTSIGKey());
        server.receive (FauxServer::USE_RCODE, dns::Rcode::NOERROR());

        // Do the update.
        ASSERT_NO_FATAL_FAILURE(doOneExchange(name_change));

        // Verify that next event is IO_COMPLETED_EVT and DNS status is SUCCESS.
        ASSERT_EQ(NameChangeTransaction::IO_COMPLETED_EVT,
                  name_change->getNextEvent());
1172

1173
        ASSERT_EQ(DNSClient::SUCCESS, name_change->getDnsUpdateStatus());
1174

1175 1176 1177 1178 1179 1180 1181 1182
        D2UpdateMessagePtr response = name_change->getDnsUpdateResponse();
        ASSERT_TRUE(response);
        ASSERT_EQ(dns::Rcode::NOERROR().getCode(),
                  response->getRcode().getCode());
        D2ZonePtr zone = response->getZone();
        EXPECT_TRUE(zone);
        EXPECT_EQ("response.example.com.", zone->getName().toText());
    }
1183 1184
}

1185

1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196
/// @brief Tests the prepNewRequest method
TEST_F(NameChangeTransactionTest, prepNewRequest) {
    NameChangeStubPtr name_change;
    ASSERT_NO_THROW(name_change = makeCannedTransaction());
    D2UpdateMessagePtr request;

    // prepNewRequest should fail on empty domain.
    ASSERT_THROW(request = name_change->prepNewRequest(DdnsDomainPtr()),
        NameChangeTransactionError);

    // Verify that prepNewRequest fails on invalid zone name.
1197
    // @todo This test becomes obsolete if/when DdnsDomain enforces valid
1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208