nc_add.cc 26.2 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 <d2/d2_log.h>
18
#include <d2/d2_cfg_mgr.h>
19 20 21 22 23
#include <d2/nc_add.h>

#include <boost/function.hpp>
#include <boost/bind.hpp>

24 25 26
#include <util/buffer.h>
#include <dns/rdataclass.h>

27 28 29 30 31 32 33 34 35 36 37 38 39
namespace isc {
namespace d2 {

// NameAddTransaction states
const int NameAddTransaction::ADDING_FWD_ADDRS_ST;
const int NameAddTransaction::REPLACING_FWD_ADDRS_ST;
const int NameAddTransaction::REPLACING_REV_PTRS_ST;

// NameAddTransaction events
const int NameAddTransaction::FQDN_IN_USE_EVT;
const int NameAddTransaction::FQDN_NOT_IN_USE_EVT;

NameAddTransaction::
40
NameAddTransaction(asiolink::IOServicePtr& io_service,
41 42
                   dhcp_ddns::NameChangeRequestPtr& ncr,
                   DdnsDomainPtr& forward_domain,
43 44 45 46
                   DdnsDomainPtr& reverse_domain,
                   D2CfgMgrPtr& cfg_mgr)
    : NameChangeTransaction(io_service, ncr, forward_domain, reverse_domain,
                            cfg_mgr) {
47 48 49 50 51 52 53 54 55 56 57 58 59 60
    if (ncr->getChangeType() != isc::dhcp_ddns::CHG_ADD) {
        isc_throw (NameAddTransactionError,
                   "NameAddTransaction, request type must be CHG_ADD");
    }
}

NameAddTransaction::~NameAddTransaction(){
}

void
NameAddTransaction::defineEvents() {
    // Call superclass impl first.
    NameChangeTransaction::defineEvents();

61
    // Define NameAddTransaction events.
62 63 64 65 66 67
    defineEvent(FQDN_IN_USE_EVT, "FQDN_IN_USE_EVT");
    defineEvent(FQDN_NOT_IN_USE_EVT, "FQDN_NOT_IN_USE_EVT");
}

void
NameAddTransaction::verifyEvents() {
68 69 70 71 72 73 74 75 76
    // Call superclass implementation first to verify its events. These are
    // events common to all transactions, and they must be defined.
    // SELECT_SERVER_EVT
    // SERVER_SELECTED_EVT
    // SERVER_IO_ERROR_EVT
    // NO_MORE_SERVERS_EVT
    // IO_COMPLETED_EVT
    // UPDATE_OK_EVT
    // UPDATE_FAILED_EVT
77 78
    NameChangeTransaction::verifyEvents();

79
    // Verify NameAddTransaction events by attempting to fetch them.
80 81 82 83 84 85 86 87 88
    getEvent(FQDN_IN_USE_EVT);
    getEvent(FQDN_NOT_IN_USE_EVT);
}

void
NameAddTransaction::defineStates() {
    // Call superclass impl first.
    NameChangeTransaction::defineStates();

89
    // Define NameAddTransaction states.
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
    defineState(READY_ST, "READY_ST",
             boost::bind(&NameAddTransaction::readyHandler, this));

    defineState(SELECTING_FWD_SERVER_ST, "SELECTING_FWD_SERVER_ST",
             boost::bind(&NameAddTransaction::selectingFwdServerHandler, this));

    defineState(SELECTING_REV_SERVER_ST, "SELECTING_REV_SERVER_ST",
             boost::bind(&NameAddTransaction::selectingRevServerHandler, this));

    defineState(ADDING_FWD_ADDRS_ST, "ADDING_FWD_ADDRS_ST",
             boost::bind(&NameAddTransaction::addingFwdAddrsHandler, this));

    defineState(REPLACING_FWD_ADDRS_ST, "REPLACING_FWD_ADDRS_ST",
             boost::bind(&NameAddTransaction::replacingFwdAddrsHandler, this));

    defineState(REPLACING_REV_PTRS_ST, "REPLACING_REV_PTRS_ST",
             boost::bind(&NameAddTransaction::replacingRevPtrsHandler, this));

    defineState(PROCESS_TRANS_OK_ST, "PROCESS_TRANS_OK_ST",
             boost::bind(&NameAddTransaction::processAddOkHandler, this));

    defineState(PROCESS_TRANS_FAILED_ST, "PROCESS_TRANS_FAILED_ST",
             boost::bind(&NameAddTransaction::processAddFailedHandler, this));

}
void
NameAddTransaction::verifyStates() {
117 118 119 120 121 122 123
    // Call superclass implementation first to verify its states. These are
    // states common to all transactions, and they must be defined.
    // READY_ST
    // SELECTING_FWD_SERVER_ST
    // SELECTING_REV_SERVER_ST
    // PROCESS_TRANS_OK_ST
    // PROCESS_TRANS_FAILED_ST
124 125
    NameChangeTransaction::verifyStates();

126
    // Verify NameAddTransaction states by attempting to fetch them.
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
    getState(ADDING_FWD_ADDRS_ST);
    getState(REPLACING_FWD_ADDRS_ST);
    getState(REPLACING_REV_PTRS_ST);
}

void
NameAddTransaction::readyHandler() {
    switch(getNextEvent()) {
    case START_EVT:
        if (getForwardDomain()) {
            // Request includes a forward change, do that first.
            transition(SELECTING_FWD_SERVER_ST, SELECT_SERVER_EVT);
        } else {
            // Reverse change only, transition accordingly.
            transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT);
        }

        break;
    default:
        // Event is invalid.
        isc_throw(NameAddTransactionError,
                  "Wrong event for context: " << getContextStr());
    }
}

void
NameAddTransaction::selectingFwdServerHandler() {
    switch(getNextEvent()) {
    case SELECT_SERVER_EVT:
        // First time through for this transaction, so initialize server
        // selection.
        initServerSelection(getForwardDomain());
        break;
    case SERVER_IO_ERROR_EVT:
        // We failed to communicate with current server. Attempt to select
        // another one below.
        break;
    default:
        // Event is invalid.
        isc_throw(NameAddTransactionError,
                  "Wrong event for context: " << getContextStr());
    }

    // Select the next server from the list of forward servers.
    if (selectNextServer()) {
        // We have a server to try.
        transition(ADDING_FWD_ADDRS_ST, SERVER_SELECTED_EVT);
    }
    else {
        // Server list is exhausted, so fail the transaction.
        transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT);
    }
}

void
NameAddTransaction::addingFwdAddrsHandler() {
    if (doOnEntry()) {
        // Clear the request on initial transition. This allows us to reuse
        // the request on retries if necessary.
        clearDnsUpdateRequest();
    }

    switch(getNextEvent()) {
    case SERVER_SELECTED_EVT:
        if (!getDnsUpdateRequest()) {
            // Request hasn't been constructed yet, so build it.
193 194 195 196 197 198 199 200 201 202
            try {
                buildAddFwdAddressRequest();
            } catch (const std::exception& ex) {
                // While unlikely, the build might fail if we have invalid
                // data.  Should that be the case, we need to fail the
                // transaction.
                LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_ADD_BUILD_FAILURE)
                          .arg(getNcr()->toText())
                          .arg(ex.what());
                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
203
                break;
204
            }
205 206 207 208
        }

        // Call sendUpdate() to initiate the async send. Note it also sets
        // next event to NOP_EVT.
209
        sendUpdate("Foward Add");
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
        break;

    case IO_COMPLETED_EVT: {
        switch (getDnsUpdateStatus()) {
        case DNSClient::SUCCESS: {
            // We successfully received a response packet from the server.
            const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode();
            if (rcode == dns::Rcode::NOERROR()) {
                // We were able to add it. Mark it as done.
                setForwardChangeCompleted(true);

                // If request calls for reverse update then do that next,
                // otherwise we can process ok.
                if (getReverseDomain()) {
                    transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT);
                } else {
                    transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT);
                }
            } else if (rcode == dns::Rcode::YXDOMAIN()) {
                // FQDN is in use so we need to attempt to replace
                // forward address.
                transition(REPLACING_FWD_ADDRS_ST, FQDN_IN_USE_EVT);
            } else {
                // Per RFC4703 any other value means cease.
                // If we get not authorized should we try the next server in
                // the list? @todo  This needs some discussion perhaps.
                LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_ADD_REJECTED)
237
                          .arg(getCurrentServer()->toText())
238
                          .arg(getNcr()->getFqdn())
239 240 241 242 243 244 245 246 247 248 249 250 251 252
                          .arg(rcode.getCode());
                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
            }

            break;
        }

        case DNSClient::TIMEOUT:
        case DNSClient::OTHER:
            // We couldn't send to the current server, log it and set up
            // to select the next server for a retry.
            // @note For now we treat OTHER as an IO error like TIMEOUT. It
            // is not entirely clear if this is accurate.
            LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_ADD_IO_ERROR)
253
                      .arg(getNcr()->getFqdn())
254
                      .arg(getCurrentServer()->toText());
255 256 257 258 259 260 261 262

            retryTransition(SELECTING_FWD_SERVER_ST);
            break;

        case DNSClient::INVALID_RESPONSE:
            // A response was received but was corrupt. Retry it like an IO
            // error.
            LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_ADD_RESP_CORRUPT)
263
                      .arg(getCurrentServer()->toText())
264
                      .arg(getNcr()->getFqdn());
265 266 267 268 269 270 271

            retryTransition(SELECTING_FWD_SERVER_ST);
            break;

        default:
            // Any other value and we will fail this transaction, something
            // bigger is wrong.
272 273
            LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_ADD_BAD_DNSCLIENT_STATUS)
                      .arg(getDnsUpdateStatus())
274
                      .arg(getNcr()->getFqdn())
275
                      .arg(getCurrentServer()->toText());
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303

            transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
            break;
        } // end switch on dns_status

        break;
    } // end case IO_COMPLETE_EVT

    default:
        // Event is invalid.
        isc_throw(NameAddTransactionError,
                  "Wrong event for context: " << getContextStr());
    }
}

void
NameAddTransaction::replacingFwdAddrsHandler() {
    if (doOnEntry()) {
        // Clear the request on initial transition. This allows us to reuse
        // the request on retries if necessary.
        clearDnsUpdateRequest();
    }

    switch(getNextEvent()) {
    case FQDN_IN_USE_EVT:
    case SERVER_SELECTED_EVT:
        if (!getDnsUpdateRequest()) {
            // Request hasn't been constructed yet, so build it.
304 305 306 307 308 309 310 311 312 313
            try {
                buildReplaceFwdAddressRequest();
            } catch (const std::exception& ex) {
                // While unlikely, the build might fail if we have invalid
                // data.  Should that be the case, we need to fail the
                // transaction.
                LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REPLACE_BUILD_FAILURE)
                          .arg(getNcr()->toText())
                          .arg(ex.what());
                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
314
                break;
315
            }
316 317 318 319
        }

        // Call sendUpdate() to initiate the async send. Note it also sets
        // next event to NOP_EVT.
320
        sendUpdate("Forward Replace");
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 346 347 348
        break;

    case IO_COMPLETED_EVT: {
        switch (getDnsUpdateStatus()) {
        case DNSClient::SUCCESS: {
            // We successfully received a response packet from the server.
            const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode();
            if (rcode == dns::Rcode::NOERROR()) {
                // We were able to replace the forward mapping. Mark it as done.
                setForwardChangeCompleted(true);

                // If request calls for reverse update then do that next,
                // otherwise we can process ok.
                if (getReverseDomain()) {
                    transition(SELECTING_REV_SERVER_ST, SELECT_SERVER_EVT);
                } else {
                    transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT);
                }
            } else if (rcode == dns::Rcode::NXDOMAIN()) {
                // FQDN is NOT in use so go back and do the forward add address.
                // Covers the case that it was there when we tried to add it,
                // but has since been removed per RFC 4703.
                transition(ADDING_FWD_ADDRS_ST, SERVER_SELECTED_EVT);
            } else {
                // Per RFC4703 any other value means cease.
                // If we get not authorized should try the next server in
                // the list? @todo  This needs some discussion perhaps.
                LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REPLACE_REJECTED)
349
                          .arg(getCurrentServer()->toText())
350
                          .arg(getNcr()->getFqdn())
351 352 353 354 355 356 357 358 359 360 361 362 363 364
                          .arg(rcode.getCode());
                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
            }

            break;
        }

        case DNSClient::TIMEOUT:
        case DNSClient::OTHER:
            // We couldn't send to the current server, log it and set up
            // to select the next server for a retry.
            // @note For now we treat OTHER as an IO error like TIMEOUT. It
            // is not entirely clear if this is accurate.
            LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REPLACE_IO_ERROR)
365
                      .arg(getNcr()->getFqdn())
366
                      .arg(getCurrentServer()->toText());
367 368 369 370 371 372 373 374 375 376

            // If we are out of retries on this server, we go back and start
            // all over on a new server.
            retryTransition(SELECTING_FWD_SERVER_ST);
            break;

        case DNSClient::INVALID_RESPONSE:
            // A response was received but was corrupt. Retry it like an IO
            // error.
            LOG_ERROR(dctl_logger, DHCP_DDNS_FORWARD_REPLACE_RESP_CORRUPT)
377
                      .arg(getCurrentServer()->toText())
378
                      .arg(getNcr()->getFqdn());
379 380 381 382 383 384 385 386 387

            // If we are out of retries on this server, we go back and start
            // all over on a new server.
            retryTransition(SELECTING_FWD_SERVER_ST);
            break;

        default:
            // Any other value and we will fail this transaction, something
            // bigger is wrong.
388 389 390
            LOG_ERROR(dctl_logger,
                      DHCP_DDNS_FORWARD_REPLACE_BAD_DNSCLIENT_STATUS)
                      .arg(getDnsUpdateStatus())
391
                      .arg(getNcr()->getFqdn())
392
                      .arg(getCurrentServer()->toText());
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449

            transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
            break;
        } // end switch on dns_status

        break;
    } // end case IO_COMPLETE_EVT

    default:
        // Event is invalid.
        isc_throw(NameAddTransactionError,
                  "Wrong event for context: " << getContextStr());
    }
}

void
NameAddTransaction::selectingRevServerHandler() {
    switch(getNextEvent()) {
    case SELECT_SERVER_EVT:
        // First time through for this transaction, so initialize server
        // selection.
        initServerSelection(getReverseDomain());
        break;
    case SERVER_IO_ERROR_EVT:
        // We failed to communicate with current server. Attempt to select
        // another one below.
        break;
    default:
        // Event is invalid.
        isc_throw(NameAddTransactionError,
                  "Wrong event for context: " << getContextStr());
    }

    // Select the next server from the list of forward servers.
    if (selectNextServer()) {
        // We have a server to try.
        transition(REPLACING_REV_PTRS_ST, SERVER_SELECTED_EVT);
    }
    else {
        // Server list is exhausted, so fail the transaction.
        transition(PROCESS_TRANS_FAILED_ST, NO_MORE_SERVERS_EVT);
    }
}


void
NameAddTransaction::replacingRevPtrsHandler() {
    if (doOnEntry()) {
        // Clear the request on initial transition. This allows us to reuse
        // the request on retries if necessary.
        clearDnsUpdateRequest();
    }

    switch(getNextEvent()) {
    case SERVER_SELECTED_EVT:
        if (!getDnsUpdateRequest()) {
            // Request hasn't been constructed yet, so build it.
450 451 452 453 454 455 456 457 458 459
            try {
                buildReplaceRevPtrsRequest();
            } catch (const std::exception& ex) {
                // While unlikely, the build might fail if we have invalid
                // data.  Should that be the case, we need to fail the
                // transaction.
                LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REPLACE_BUILD_FAILURE)
                          .arg(getNcr()->toText())
                          .arg(ex.what());
                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
460
                break;
461
            }
462 463 464 465
        }

        // Call sendUpdate() to initiate the async send. Note it also sets
        // next event to NOP_EVT.
466
        sendUpdate("Reverse Replace");
467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
        break;

    case IO_COMPLETED_EVT: {
        switch (getDnsUpdateStatus()) {
        case DNSClient::SUCCESS: {
            // We successfully received a response packet from the server.
            const dns::Rcode& rcode = getDnsUpdateResponse()->getRcode();
            if (rcode == dns::Rcode::NOERROR()) {
                // We were able to update the reverse mapping. Mark it as done.
                setReverseChangeCompleted(true);
                transition(PROCESS_TRANS_OK_ST, UPDATE_OK_EVT);
            } else {
                // Per RFC4703 any other value means cease.
                // If we get not authorized should try the next server in
                // the list? @todo  This needs some discussion perhaps.
                LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REPLACE_REJECTED)
483
                          .arg(getCurrentServer()->toText())
484
                          .arg(getNcr()->getFqdn())
485 486 487 488 489 490 491 492 493 494 495 496 497 498
                          .arg(rcode.getCode());
                transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
            }

            break;
        }

        case DNSClient::TIMEOUT:
        case DNSClient::OTHER:
            // We couldn't send to the current server, log it and set up
            // to select the next server for a retry.
            // @note For now we treat OTHER as an IO error like TIMEOUT. It
            // is not entirely clear if this is accurate.
            LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REPLACE_IO_ERROR)
499
                      .arg(getNcr()->getFqdn())
500
                      .arg(getCurrentServer()->toText());
501 502 503 504 505 506 507 508 509 510

            // If we are out of retries on this server, we go back and start
            // all over on a new server.
            retryTransition(SELECTING_REV_SERVER_ST);
            break;

        case DNSClient::INVALID_RESPONSE:
            // A response was received but was corrupt. Retry it like an IO
            // error.
            LOG_ERROR(dctl_logger, DHCP_DDNS_REVERSE_REPLACE_RESP_CORRUPT)
511
                      .arg(getCurrentServer()->toText())
512
                      .arg(getNcr()->getFqdn());
513 514 515 516 517 518 519 520 521

            // If we are out of retries on this server, we go back and start
            // all over on a new server.
            retryTransition(SELECTING_REV_SERVER_ST);
            break;

        default:
            // Any other value and we will fail this transaction, something
            // bigger is wrong.
522 523 524
            LOG_ERROR(dctl_logger,
                      DHCP_DDNS_REVERSE_REPLACE_BAD_DNSCLIENT_STATUS)
                      .arg(getDnsUpdateStatus())
525
                      .arg(getNcr()->getFqdn())
526
                      .arg(getCurrentServer()->toText());
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545

            transition(PROCESS_TRANS_FAILED_ST, UPDATE_FAILED_EVT);
            break;
        } // end switch on dns_status

        break;
    } // end case IO_COMPLETE_EVT

    default:
        // Event is invalid.
        isc_throw(NameAddTransactionError,
                  "Wrong event for context: " << getContextStr());
    }
}

void
NameAddTransaction::processAddOkHandler() {
    switch(getNextEvent()) {
    case UPDATE_OK_EVT:
546
        LOG_INFO(dctl_logger, DHCP_DDNS_ADD_SUCCEEDED)
547
                  .arg(getNcr()->toText());
548 549 550 551 552 553 554 555 556 557 558 559 560 561
        setNcrStatus(dhcp_ddns::ST_COMPLETED);
        endModel();
        break;
    default:
        // Event is invalid.
        isc_throw(NameAddTransactionError,
                  "Wrong event for context: " << getContextStr());
    }
}

void
NameAddTransaction::processAddFailedHandler() {
    switch(getNextEvent()) {
    case UPDATE_FAILED_EVT:
562
    case NO_MORE_SERVERS_EVT:
563
        setNcrStatus(dhcp_ddns::ST_FAILED);
564 565
        LOG_ERROR(dctl_logger, DHCP_DDNS_ADD_FAILED)
                  .arg(transactionOutcomeString());
566 567 568 569 570 571 572 573 574 575 576
        endModel();
        break;
    default:
        // Event is invalid.
        isc_throw(NameAddTransactionError,
                  "Wrong event for context: " << getContextStr());
    }
}

void
NameAddTransaction::buildAddFwdAddressRequest() {
577 578 579 580 581 582
    // Construct an empty request.
    D2UpdateMessagePtr request = prepNewRequest(getForwardDomain());

    // Construct dns::Name from NCR fqdn.
    dns::Name fqdn(dns::Name(getNcr()->getFqdn()));

583
    // Content on this request is based on RFC 4703, section 5.3.1
584 585
    // First build the Prerequisite Section.

586 587 588
    // Create 'FQDN Is Not In Use' prerequisite and add it to the
    // prerequisite section.
    // Based on RFC 2136, section 2.4.5
589 590 591 592 593 594
    dns::RRsetPtr prereq(new dns::RRset(fqdn, dns::RRClass::NONE(),
                             dns::RRType::ANY(), dns::RRTTL(0)));
    request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);

    // Next build the Update Section.

595 596 597
    // Create the TTL based on lease length.
    dns::RRTTL lease_ttl(getNcr()->getLeaseLength());

598 599
    // Create the FQDN/IP 'add' RR and add it to the to update section.
    // Based on RFC 2136, section 2.5.1
600
    dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::IN(),
601
                         getAddressRRType(), lease_ttl));
602 603 604

    addLeaseAddressRdata(update);
    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);
605 606 607

    // Now create the FQDN/DHCID 'add' RR and add it to update section.
    // Based on RFC 2136, section 2.5.1
608
    update.reset(new dns::RRset(fqdn, dns::RRClass::IN(),
609
                                dns::RRType::DHCID(), lease_ttl));
610 611 612 613 614
    addDhcidRdata(update);
    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);

    // Set the transaction's update request to the new request.
    setDnsUpdateRequest(request);
615 616 617 618
}

void
NameAddTransaction::buildReplaceFwdAddressRequest() {
619 620 621 622 623 624
    // Construct an empty request.
    D2UpdateMessagePtr request = prepNewRequest(getForwardDomain());

    // Construct dns::Name from NCR fqdn.
    dns::Name fqdn(dns::Name(getNcr()->getFqdn()));

625
    // Content on this request is based on RFC 4703, section 5.3.2
626 627
    // First build the Prerequisite Section.

628 629 630
    // Create an 'FQDN Is In Use' prerequisite and add it to the
    // pre-requisite section.
    // Based on RFC 2136, section 2.4.4
631 632 633 634
    dns::RRsetPtr prereq(new dns::RRset(fqdn, dns::RRClass::ANY(),
                               dns::RRType::ANY(), dns::RRTTL(0)));
    request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);

635 636 637
    // Create an DHCID matches prerequisite RR and add it to the
    // pre-requisite section.
    // Based on RFC 2136, section 2.4.2.
638 639 640 641 642 643 644
    prereq.reset(new dns::RRset(fqdn, dns::RRClass::IN(),
                 dns::RRType::DHCID(), dns::RRTTL(0)));
    addDhcidRdata(prereq);
    request->addRRset(D2UpdateMessage::SECTION_PREREQUISITE, prereq);

    // Next build the Update Section.

645 646 647
    // Create the TTL based on lease length.
    dns::RRTTL lease_ttl(getNcr()->getLeaseLength());

648 649
    // Create the FQDN/IP 'delete' RR and add it to the update section.
    // Based on RFC 2136, section 2.5.2
650 651 652 653
    dns::RRsetPtr update(new dns::RRset(fqdn, dns::RRClass::ANY(),
                         getAddressRRType(), dns::RRTTL(0)));
    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);

654 655
    // Create the FQDN/IP 'add' RR and add it to the update section.
    // Based on RFC 2136, section 2.5.1
656
    update.reset(new dns::RRset(fqdn, dns::RRClass::IN(),
657
                                getAddressRRType(), lease_ttl));
658 659 660 661 662
    addLeaseAddressRdata(update);
    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);

    // Set the transaction's update request to the new request.
    setDnsUpdateRequest(request);
663 664 665 666
}

void
NameAddTransaction::buildReplaceRevPtrsRequest() {
667 668 669 670 671 672 673
    // Construct an empty request.
    D2UpdateMessagePtr request = prepNewRequest(getReverseDomain());

    // Create the reverse IP address "FQDN".
    std::string rev_addr = D2CfgMgr::reverseIpAddress(getNcr()->getIpAddress());
    dns::Name rev_ip(rev_addr);

674 675 676
    // Create the TTL based on lease length.
    dns::RRTTL lease_ttl(getNcr()->getLeaseLength());

677
    // Content on this request is based on RFC 4703, section 5.4
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
    // Reverse replacement has no prerequisites so straight on to
    // building the Update section.

    // Create the PTR 'delete' RR and add it to update section.
    dns::RRsetPtr update(new dns::RRset(rev_ip, dns::RRClass::ANY(),
                         dns::RRType::PTR(), dns::RRTTL(0)));
    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);

    // Create the DHCID 'delete' RR and add it to the update section.
    update.reset(new dns::RRset(rev_ip, dns::RRClass::ANY(),
                                dns::RRType::DHCID(), dns::RRTTL(0)));
    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);

    // Create the FQDN/IP PTR 'add' RR, add the FQDN as the PTR Rdata
    // then add it to update section.
    update.reset(new dns::RRset(rev_ip, dns::RRClass::IN(),
694
                                dns::RRType::PTR(), lease_ttl));
695 696 697 698 699 700
    addPtrRdata(update);
    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);

    // Create the FQDN/IP PTR 'add' RR, add the DHCID Rdata
    // then add it to update section.
    update.reset(new dns::RRset(rev_ip, dns::RRClass::IN(),
701
                                dns::RRType::DHCID(), lease_ttl));
702 703 704 705 706
    addDhcidRdata(update);
    request->addRRset(D2UpdateMessage::SECTION_UPDATE, update);

    // Set the transaction's update request to the new request.
    setDnsUpdateRequest(request);
707 708 709 710
}

} // namespace isc::d2
} // namespace isc