nc_trans.cc 16 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 18
#include <d2/d2_log.h>
#include <d2/nc_trans.h>
19
#include <dns/rdata.h>
20

21 22
#include <sstream>

23 24 25 26 27 28 29
namespace isc {
namespace d2 {

// Common transaction states
const int NameChangeTransaction::READY_ST;
const int NameChangeTransaction::SELECTING_FWD_SERVER_ST;
const int NameChangeTransaction::SELECTING_REV_SERVER_ST;
30 31
const int NameChangeTransaction::PROCESS_TRANS_OK_ST;
const int NameChangeTransaction::PROCESS_TRANS_FAILED_ST;
32

33
const int NameChangeTransaction::NCT_DERIVED_STATE_MIN;
34 35 36 37 38 39 40 41 42 43

// Common transaction events
const int NameChangeTransaction::SELECT_SERVER_EVT;
const int NameChangeTransaction::SERVER_SELECTED_EVT;
const int NameChangeTransaction::SERVER_IO_ERROR_EVT;
const int NameChangeTransaction::NO_MORE_SERVERS_EVT;
const int NameChangeTransaction::IO_COMPLETED_EVT;
const int NameChangeTransaction::UPDATE_OK_EVT;
const int NameChangeTransaction::UPDATE_FAILED_EVT;

44
const int NameChangeTransaction::NCT_DERIVED_EVENT_MIN;
45

46 47
const unsigned int NameChangeTransaction::MAX_UPDATE_TRIES_PER_SERVER;

48
NameChangeTransaction::
49
NameChangeTransaction(asiolink::IOServicePtr& io_service,
50
                      dhcp_ddns::NameChangeRequestPtr& ncr,
51
                      DdnsDomainPtr& forward_domain,
52 53
                      DdnsDomainPtr& reverse_domain,
                      D2CfgMgrPtr& cfg_mgr)
54
    : io_service_(io_service), ncr_(ncr), forward_domain_(forward_domain),
55
     reverse_domain_(reverse_domain), dns_client_(), dns_update_request_(),
56
     dns_update_status_(DNSClient::OTHER), dns_update_response_(),
57
     forward_change_completed_(false), reverse_change_completed_(false),
58
     current_server_list_(), current_server_(), next_server_pos_(0),
59
     update_attempts_(0), cfg_mgr_(cfg_mgr), tsig_key_() {
60 61
    /// @todo if io_service is NULL we are multi-threading and should
    /// instantiate our own
62 63 64 65
    if (!io_service_) {
        isc_throw(NameChangeTransactionError, "IOServicePtr cannot be null");
    }

66
    if (!ncr_) {
67 68
        isc_throw(NameChangeTransactionError,
                  "NameChangeRequest cannot be null");
69 70 71 72 73 74 75 76 77 78 79
    }

    if (ncr_->isForwardChange() && !(forward_domain_)) {
        isc_throw(NameChangeTransactionError,
                 "Forward change must have a forward domain");
    }

    if (ncr_->isReverseChange() && !(reverse_domain_)) {
        isc_throw(NameChangeTransactionError,
                 "Reverse change must have a reverse domain");
    }
80 81 82 83 84

    if (!cfg_mgr_) {
        isc_throw(NameChangeTransactionError,
                  "Configuration manager cannot be null");
    }
85 86 87 88 89 90 91
}

NameChangeTransaction::~NameChangeTransaction(){
}

void
NameChangeTransaction::startTransaction() {
92 93 94 95
    LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL,
              DHCP_DDNS_STARTING_TRANSACTION)
              .arg(getTransactionKey().toStr());

96
    setNcrStatus(dhcp_ddns::ST_PENDING);
97
    startModel(READY_ST);
98 99 100 101 102 103
}

void
NameChangeTransaction::operator()(DNSClient::Status status) {
    // Stow the completion status and re-enter the run loop with the event
    // set to indicate IO completed.
104
    // runModel is exception safe so we are good to call it here.
105
    // It won't exit until we hit the next IO wait or the state model ends.
106
    setDnsUpdateStatus(status);
107 108 109 110
    LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL,
              DHCP_DDNS_UPDATE_RESPONSE_RECEIVED)
              .arg(getTransactionKey().toStr())
              .arg(current_server_->toText())
111
              .arg(responseString());
112

113
    runModel(IO_COMPLETED_EVT);
114 115
}

116
std::string
117
NameChangeTransaction::responseString() const {
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
    std::ostringstream stream;
    switch (getDnsUpdateStatus()) {
        case DNSClient::SUCCESS:
            stream << "SUCCESS, rcode: ";
            if (getDnsUpdateResponse()) {
                 stream << getDnsUpdateResponse()->getRcode().toText();
            } else {
                stream << " update response is NULL";
            }
            break;
        case DNSClient::TIMEOUT:
            stream << "TIMEOUT";
            break;
        case DNSClient::IO_STOPPED:
            stream << "IO_STOPPED";
            break;
        case DNSClient::INVALID_RESPONSE:
            stream << "INVALID_RESPONSE";
            break;
        case DNSClient::OTHER:
            stream << "OTHER";
            break;
        default:
            stream << "UKNOWNN("
                   << static_cast<int>(getDnsUpdateStatus()) << ")";
            break;

    }

    return (stream.str());
}

150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
std::string
NameChangeTransaction::transactionOutcomeString() const {
    std::ostringstream stream;
    stream << "Status: " << (getNcrStatus() == dhcp_ddns::ST_COMPLETED
                             ? "Completed, " : "Failed, ")
           << "Event: " << getEventLabel(getNextEvent()) << ", ";

    if (ncr_->isForwardChange()) {
        stream << " Forward change:" << (getForwardChangeCompleted()
                                         ? " completed, " : " failed, ");
    }

    if (ncr_->isReverseChange()) {
        stream << " Reverse change:" << (getReverseChangeCompleted()
                                          ? " completed, " : " failed, ");
    }

    stream << " request: " << ncr_->toText();
    return (stream.str());
}


172
void
173
NameChangeTransaction::sendUpdate(const std::string& comment) {
174 175 176 177 178 179
    try {
        ++update_attempts_;
        // @todo add logic to add/replace TSIG key info in request if
        // use_tsig_ is true. We should be able to navigate to the TSIG key
        // for the current server.  If not we would need to add that.

180
        D2ParamsPtr d2_params = cfg_mgr_->getD2Params();
181
        dns_client_->doUpdate(*io_service_, current_server_->getIpAddress(),
182
                              current_server_->getPort(), *dns_update_request_,
183
                              d2_params->getDnsServerTimeout(), tsig_key_);
184 185
        // Message is on its way, so the next event should be NOP_EVT.
        postNextEvent(NOP_EVT);
186 187
        LOG_DEBUG(dctl_logger, DBGLVL_TRACE_DETAIL,
                  DHCP_DDNS_UPDATE_REQUEST_SENT)
188
                  .arg(comment)
189 190
                  .arg(getTransactionKey().toStr())
                  .arg(current_server_->toText());
191 192 193 194 195
    } catch (const std::exception& ex) {
        // We were unable to initiate the send.
        // It is presumed that any throw from doUpdate is due to a programmatic
        // error, such as an unforeseen permutation of data, rather than an IO
        // failure. IO errors should be caught by the underlying asiolink
196
        // mechanisms and manifested as an unsuccessful IO status in the
197 198
        // DNSClient callback.  Any problem here most likely means the request
        // is corrupt in some way and cannot be completed, therefore we will
199 200 201
        // log it and transition it to failure.
        LOG_ERROR(dctl_logger, DHCP_DDNS_TRANS_SEND_ERROR).arg(ex.what());
        transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
202 203 204
    }
}

205 206
void
NameChangeTransaction::defineEvents() {
207
    // Call superclass impl first.
208
    StateModel::defineEvents();
209 210

    // Define NCT events.
211 212 213 214 215 216 217 218 219 220 221
    defineEvent(SELECT_SERVER_EVT, "SELECT_SERVER_EVT");
    defineEvent(SERVER_SELECTED_EVT, "SERVER_SELECTED_EVT");
    defineEvent(SERVER_IO_ERROR_EVT, "SERVER_IO_ERROR_EVT");
    defineEvent(NO_MORE_SERVERS_EVT, "NO_MORE_SERVERS_EVT");
    defineEvent(IO_COMPLETED_EVT, "IO_COMPLETED_EVT");
    defineEvent(UPDATE_OK_EVT, "UPDATE_OK_EVT");
    defineEvent(UPDATE_FAILED_EVT, "UPDATE_FAILED_EVT");
}

void
NameChangeTransaction::verifyEvents() {
222
    // Call superclass impl first.
223
    StateModel::verifyEvents();
224 225

    // Verify NCT events.
226 227 228 229 230 231 232 233
    getEvent(SELECT_SERVER_EVT);
    getEvent(SERVER_SELECTED_EVT);
    getEvent(SERVER_IO_ERROR_EVT);
    getEvent(NO_MORE_SERVERS_EVT);
    getEvent(IO_COMPLETED_EVT);
    getEvent(UPDATE_OK_EVT);
    getEvent(UPDATE_FAILED_EVT);
}
234 235

void
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
NameChangeTransaction::defineStates() {
    // Call superclass impl first.
    StateModel::defineStates();
    // This class is "abstract" in that it does not supply handlers for its
    // states, derivations must do that therefore they must define them.
}

void
NameChangeTransaction::verifyStates() {
    // Call superclass impl first.
    StateModel::verifyStates();

    // Verify NCT states. This ensures that derivations provide the handlers.
    getState(READY_ST);
    getState(SELECTING_FWD_SERVER_ST);
    getState(SELECTING_REV_SERVER_ST);
    getState(PROCESS_TRANS_OK_ST);
    getState(PROCESS_TRANS_FAILED_ST);
254 255 256
}

void
257
NameChangeTransaction::onModelFailure(const std::string& explanation) {
258
    setNcrStatus(dhcp_ddns::ST_FAILED);
259 260
    LOG_ERROR(dctl_logger, DHCP_DDNS_STATE_MODEL_UNEXPECTED_ERROR)
                  .arg(explanation);
261 262
}

263
void
264
NameChangeTransaction::retryTransition(const int fail_to_state) {
265 266 267 268
    if (update_attempts_ < MAX_UPDATE_TRIES_PER_SERVER) {
        // Re-enter the current state with same server selected.
        transition(getCurrState(), SERVER_SELECTED_EVT);
    } else  {
269
        // Transition to given fail_to_state state if we are out
270
        // of retries.
271
        transition(fail_to_state, SERVER_IO_ERROR_EVT);
272 273 274 275 276 277 278 279 280 281
    }
}

void
NameChangeTransaction::setDnsUpdateRequest(D2UpdateMessagePtr& request) {
    dns_update_request_ = request;
}

void
NameChangeTransaction::clearDnsUpdateRequest() {
282
    update_attempts_ = 0;
283 284 285
    dns_update_request_.reset();
}

286 287 288 289 290
void
NameChangeTransaction::setDnsUpdateStatus(const DNSClient::Status& status) {
    dns_update_status_ = status;
}

291 292 293 294 295 296 297 298 299 300
void
NameChangeTransaction::setDnsUpdateResponse(D2UpdateMessagePtr& response) {
    dns_update_response_ = response;
}

void
NameChangeTransaction::clearDnsUpdateResponse() {
    dns_update_response_.reset();
}

301 302 303 304 305 306 307 308 309 310
void
NameChangeTransaction::setForwardChangeCompleted(const bool value) {
    forward_change_completed_ = value;
}

void
NameChangeTransaction::setReverseChangeCompleted(const bool value) {
    reverse_change_completed_ = value;
}

311
void
312
NameChangeTransaction::setUpdateAttempts(const size_t value) {
313 314 315
    update_attempts_ = value;
}

316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
D2UpdateMessagePtr
NameChangeTransaction::prepNewRequest(DdnsDomainPtr domain) {
    if (!domain) {
        isc_throw(NameChangeTransactionError,
                  "prepNewRequest - domain cannot be null");
    }

    try {
        // Create a "blank" update request.
        D2UpdateMessagePtr request(new D2UpdateMessage(D2UpdateMessage::
                                                       OUTBOUND));
        // Construct the Zone Section.
        dns::Name zone_name(domain->getName());
        request->setZone(zone_name, dns::RRClass::IN());
        return (request);
    } catch (const std::exception& ex) {
        isc_throw(NameChangeTransactionError, "Cannot create new request :"
                  << ex.what());
    }
}

void
NameChangeTransaction::addLeaseAddressRdata(dns::RRsetPtr& rrset) {
    if (!rrset) {
        isc_throw(NameChangeTransactionError,
                  "addLeaseAddressRdata - RRset cannot cannot be null");
    }

    try {
        // Manufacture an RData from the lease address then add it to the RR.
Thomas Markwalder's avatar
Thomas Markwalder committed
346
        dns::rdata::ConstRdataPtr rdata;
347
        if (ncr_->isV4()) {
Thomas Markwalder's avatar
Thomas Markwalder committed
348
            rdata.reset(new dns::rdata::in::A(ncr_->getIpAddress()));
349
        } else {
Thomas Markwalder's avatar
Thomas Markwalder committed
350
            rdata.reset(new dns::rdata::in::AAAA(ncr_->getIpAddress()));
351
        }
Thomas Markwalder's avatar
Thomas Markwalder committed
352
        rrset->addRdata(rdata);
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368
    } catch (const std::exception& ex) {
        isc_throw(NameChangeTransactionError, "Cannot add address rdata: "
                  << ex.what());
    }
}

void
NameChangeTransaction::addDhcidRdata(dns::RRsetPtr& rrset) {
    if (!rrset) {
        isc_throw(NameChangeTransactionError,
                  "addDhcidRdata - RRset cannot cannot be null");
    }

    try {
        const std::vector<uint8_t>& ncr_dhcid = ncr_->getDhcid().getBytes();
        util::InputBuffer buffer(ncr_dhcid.data(), ncr_dhcid.size());
369 370
        dns::rdata::ConstRdataPtr rdata (new dns::rdata::in::
                                         DHCID(buffer, ncr_dhcid.size()));
371 372 373 374 375
        rrset->addRdata(rdata);
    } catch (const std::exception& ex) {
        isc_throw(NameChangeTransactionError, "Cannot add DCHID rdata: "
                  << ex.what());
    }
376

377 378 379 380 381 382 383 384 385 386
}

void
NameChangeTransaction::addPtrRdata(dns::RRsetPtr& rrset) {
    if (!rrset) {
        isc_throw(NameChangeTransactionError,
                  "addPtrRdata - RRset cannot cannot be null");
    }

    try {
387 388
        dns::rdata::ConstRdataPtr rdata(new dns::rdata::generic::
                                        PTR(getNcr()->getFqdn()));
389 390 391 392 393 394 395
        rrset->addRdata(rdata);
    } catch (const std::exception& ex) {
        isc_throw(NameChangeTransactionError, "Cannot add PTR rdata: "
                  << ex.what());
    }
}

396
const dhcp_ddns::NameChangeRequestPtr&
397 398 399 400
NameChangeTransaction::getNcr() const {
    return (ncr_);
}

401
const TransactionKey&
402 403 404 405 406 407 408 409 410
NameChangeTransaction::getTransactionKey() const {
    return (ncr_->getDhcid());
}

dhcp_ddns::NameChangeStatus
NameChangeTransaction::getNcrStatus() const {
    return (ncr_->getStatus());
}

411 412 413 414 415 416 417 418 419 420 421
DdnsDomainPtr&
NameChangeTransaction::getForwardDomain() {
    return (forward_domain_);
}

DdnsDomainPtr&
NameChangeTransaction::getReverseDomain() {
    return (reverse_domain_);
}

void
422 423 424 425 426
NameChangeTransaction::initServerSelection(const DdnsDomainPtr& domain) {
    if (!domain) {
        isc_throw(NameChangeTransactionError,
                  "initServerSelection called with an empty domain");
    }
427

428 429 430 431 432 433 434 435
    // Set the tsig_key to that of the DdnsDomain.
    TSIGKeyInfoPtr tsig_key_info = domain->getTSIGKeyInfo();
    if (tsig_key_info) {
        tsig_key_ = tsig_key_info->getTSIGKey();
    } else {
        tsig_key_.reset();
    }

436 437 438 439 440 441 442 443 444 445
    current_server_list_ = domain->getServers();
    next_server_pos_ = 0;
    current_server_.reset();
}

bool
NameChangeTransaction::selectNextServer() {
    if ((current_server_list_) &&
        (next_server_pos_ < current_server_list_->size())) {
        current_server_  = (*current_server_list_)[next_server_pos_];
446 447 448
        // Toss out any previous response.
        dns_update_response_.reset();

449 450
        // @todo  Protocol is set on DNSClient constructor.  We need
        // to propagate a configuration value downward, probably starting
451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
        // at global, then domain, then server
        // Once that is supported we need to add it here.
        dns_client_.reset(new DNSClient(dns_update_response_ , this,
                                        DNSClient::UDP));
        ++next_server_pos_;
        return (true);
    }

    return (false);
}

const DNSClientPtr&
NameChangeTransaction::getDNSClient() const {
    return (dns_client_);
}

const DnsServerInfoPtr&
NameChangeTransaction::getCurrentServer() const {
    return (current_server_);
}

472 473 474 475 476
void
NameChangeTransaction::setNcrStatus(const dhcp_ddns::NameChangeStatus& status) {
    return (ncr_->setStatus(status));
}

477 478 479 480 481
const D2UpdateMessagePtr&
NameChangeTransaction::getDnsUpdateRequest() const {
    return (dns_update_request_);
}

482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
DNSClient::Status
NameChangeTransaction::getDnsUpdateStatus() const {
    return (dns_update_status_);
}

const D2UpdateMessagePtr&
NameChangeTransaction::getDnsUpdateResponse() const {
    return (dns_update_response_);
}

bool
NameChangeTransaction::getForwardChangeCompleted() const {
    return (forward_change_completed_);
}

bool
NameChangeTransaction::getReverseChangeCompleted() const {
    return (reverse_change_completed_);
}

502 503 504 505 506
size_t
NameChangeTransaction::getUpdateAttempts() const {
    return (update_attempts_);
}

507 508
const dns::RRType&
NameChangeTransaction::getAddressRRType() const {
509
    return (ncr_->isV4() ?  dns::RRType::A() : dns::RRType::AAAA());
510 511
}

512 513
} // namespace isc::d2
} // namespace isc