d2_config.cc 33.7 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/d2_cfg_mgr.h>
19
#include <dhcpsrv/parsers/dhcp_parsers.h>
20 21 22 23 24
#include <exceptions/exceptions.h>
#include <asiolink/io_error.h>

#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
25
#include <boost/scoped_ptr.hpp>
26
#include <boost/algorithm/string/predicate.hpp>
27

28
#include <sstream>
29 30
#include <string>

31 32 33
namespace isc {
namespace d2 {

34 35 36 37 38 39 40 41 42 43 44
// *********************** D2Params  *************************

const char *D2Params::DFT_IP_ADDRESS = "127.0.0.1";
const size_t D2Params::DFT_PORT = 53001;
const size_t D2Params::DFT_DNS_SERVER_TIMEOUT = 100;
const char *D2Params::DFT_NCR_PROTOCOL = "UDP";
const char *D2Params::DFT_NCR_FORMAT = "JSON";

D2Params::D2Params(const isc::asiolink::IOAddress& ip_address,
                   const size_t port,
                   const size_t dns_server_timeout,
45 46
                   const dhcp_ddns::NameChangeProtocol& ncr_protocol,
                   const dhcp_ddns::NameChangeFormat& ncr_format)
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
    : ip_address_(ip_address),
    port_(port),
    dns_server_timeout_(dns_server_timeout),
    ncr_protocol_(ncr_protocol),
    ncr_format_(ncr_format) {
    validateContents();
}

D2Params::D2Params()
    : ip_address_(isc::asiolink::IOAddress(DFT_IP_ADDRESS)),
     port_(DFT_PORT),
     dns_server_timeout_(DFT_DNS_SERVER_TIMEOUT),
     ncr_protocol_(dhcp_ddns::NCR_UDP),
     ncr_format_(dhcp_ddns::FMT_JSON) {
    validateContents();
}

D2Params::~D2Params(){};

void
D2Params::validateContents() {
68 69 70
    if ((ip_address_.toText() == "0.0.0.0") || (ip_address_.toText() == "::")) {
        isc_throw(D2CfgError,
                  "D2Params: IP address cannot be \"" << ip_address_ << "\"");
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
    }

    if (port_ == 0) {
        isc_throw(D2CfgError, "D2Params: port cannot be 0");
    }

    if (dns_server_timeout_ < 1) {
        isc_throw(D2CfgError,
                  "D2Params: DNS server timeout must be larger than 0");
    }

    if (ncr_format_ != dhcp_ddns::FMT_JSON) {
        isc_throw(D2CfgError, "D2Params: NCR Format:"
                  << dhcp_ddns::ncrFormatToString(ncr_format_)
                  << " is not yet supported");
    }

    if (ncr_protocol_ != dhcp_ddns::NCR_UDP) {
        isc_throw(D2CfgError, "D2Params: NCR Protocol:"
                  << dhcp_ddns::ncrProtocolToString(ncr_protocol_)
                  << " is not yet supported");
    }
}

95 96 97
std::string
D2Params::getConfigSummary() const {
    std::ostringstream s;
98 99
    s << "listening on " << getIpAddress() << ", port " << getPort()
      << ", using " << ncrProtocolToString(ncr_protocol_);
100 101 102
    return (s.str());
}

103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
bool
D2Params::operator == (const D2Params& other) const {
    return ((ip_address_ == other.ip_address_) &&
            (port_ == other.port_) &&
            (dns_server_timeout_ == other.dns_server_timeout_) &&
            (ncr_protocol_ == other.ncr_protocol_) &&
            (ncr_format_ == other.ncr_format_));
}

bool
D2Params::operator != (const D2Params& other) const {
    return (!(*this == other));
}

std::string
D2Params::toText() const {
    std::ostringstream stream;

    stream << ", ip_address: " << ip_address_.toText()
           << ", port: " << port_
           << ", dns_server_timeout_: " << dns_server_timeout_
           << ", ncr_protocol: "
           << dhcp_ddns::ncrProtocolToString(ncr_protocol_)
           << ", ncr_format: " << ncr_format_
           << dhcp_ddns::ncrFormatToString(ncr_format_);

    return (stream.str());
}

std::ostream&
operator<<(std::ostream& os, const D2Params& config) {
    os << config.toText();
    return (os);
}

138
// *********************** TSIGKeyInfo  *************************
139 140 141 142 143 144 145 146
// Note these values match correpsonding values for Bind9's
// dnssec-keygen
const char* TSIGKeyInfo::HMAC_MD5_STR = "HMAC-MD5";
const char* TSIGKeyInfo::HMAC_SHA1_STR = "HMAC-SHA1";
const char* TSIGKeyInfo::HMAC_SHA224_STR = "HMAC-SHA224";
const char* TSIGKeyInfo::HMAC_SHA256_STR = "HMAC-SHA256";
const char* TSIGKeyInfo::HMAC_SHA384_STR = "HMAC-SHA384";
const char* TSIGKeyInfo::HMAC_SHA512_STR = "HMAC-SHA512";
147 148

TSIGKeyInfo::TSIGKeyInfo(const std::string& name, const std::string& algorithm,
149 150 151
                         const std::string& secret, uint32_t digestbits)
    :name_(name), algorithm_(algorithm), secret_(secret),
     digestbits_(digestbits), tsig_key_() {
152
    remakeKey();
153 154 155 156
}

TSIGKeyInfo::~TSIGKeyInfo() {
}
157

158 159
const dns::Name&
TSIGKeyInfo::stringToAlgorithmName(const std::string& algorithm_id) {
160
    if (boost::iequals(algorithm_id, HMAC_MD5_STR)) {
161
        return (dns::TSIGKey::HMACMD5_NAME());
162
    } else if (boost::iequals(algorithm_id, HMAC_SHA1_STR)) {
163
        return (dns::TSIGKey::HMACSHA1_NAME());
164
    } else if (boost::iequals(algorithm_id, HMAC_SHA224_STR)) {
165
        return (dns::TSIGKey::HMACSHA224_NAME());
166
    } else if (boost::iequals(algorithm_id, HMAC_SHA256_STR)) {
167
        return (dns::TSIGKey::HMACSHA256_NAME());
168
    } else if (boost::iequals(algorithm_id, HMAC_SHA384_STR)) {
169
        return (dns::TSIGKey::HMACSHA384_NAME());
170
    } else if (boost::iequals(algorithm_id, HMAC_SHA512_STR)) {
171 172 173
        return (dns::TSIGKey::HMACSHA512_NAME());
    }

174
    isc_throw(BadValue, "Unknown TSIG Key algorithm: " << algorithm_id);
175 176 177 178 179
}

void
TSIGKeyInfo::remakeKey() {
    try {
180 181 182 183 184 185 186
        // Since our secret value is base64 encoded already, we need to
        // build the input string for the appropriate TSIGKey constructor.
        // If secret isn't a valid base64 value, the constructor will throw.
        std::ostringstream stream;
        stream << dns::Name(name_).toText() << ":"
               << secret_ << ":"
               << stringToAlgorithmName(algorithm_);
187 188 189
        if (digestbits_ > 0) {
            stream << ":" << digestbits_;
        }
190 191

        tsig_key_.reset(new dns::TSIGKey(stream.str()));
192 193 194 195
    } catch (const std::exception& ex) {
        isc_throw(D2CfgError, "Cannot make TSIGKey: " << ex.what());
    }
}
196

197
// *********************** DnsServerInfo  *************************
198

199 200 201
const char* DnsServerInfo::EMPTY_IP_STR = "0.0.0.0";

DnsServerInfo::DnsServerInfo(const std::string& hostname,
202 203
                             isc::asiolink::IOAddress ip_address, uint32_t port,
                             bool enabled)
204
    :hostname_(hostname), ip_address_(ip_address), port_(port),
205 206 207 208 209 210
    enabled_(enabled) {
}

DnsServerInfo::~DnsServerInfo() {
}

211 212 213 214 215 216 217 218 219 220 221 222 223 224
std::string
DnsServerInfo::toText() const {
    std::ostringstream stream;
    stream << (getIpAddress().toText()) << " port:" << getPort();
    return (stream.str());
}


std::ostream&
operator<<(std::ostream& os, const DnsServerInfo& server) {
    os << server.toText();
    return (os);
}

225 226
// *********************** DdnsDomain  *************************

227 228 229 230 231
DdnsDomain::DdnsDomain(const std::string& name,
                       DnsServerInfoStoragePtr servers,
                       const TSIGKeyInfoPtr& tsig_key_info)
    : name_(name), servers_(servers),
      tsig_key_info_(tsig_key_info) {
232 233 234 235 236
}

DdnsDomain::~DdnsDomain() {
}

237 238 239 240 241 242 243 244 245
const std::string
DdnsDomain::getKeyName() const {
    if (tsig_key_info_) {
        return (tsig_key_info_->getName());
    }

    return ("");
}

246 247
// *********************** DdnsDomainLstMgr  *************************

248 249
const char* DdnsDomainListMgr::wildcard_domain_name_ = "*";

250
DdnsDomainListMgr::DdnsDomainListMgr(const std::string& name) : name_(name),
251
    domains_(new DdnsDomainMap()) {
252 253 254 255 256 257
}


DdnsDomainListMgr::~DdnsDomainListMgr () {
}

258 259
void
DdnsDomainListMgr::setDomains(DdnsDomainMapPtr domains) {
260
    if (!domains) {
261
        isc_throw(D2CfgError,
262 263 264 265 266
                  "DdnsDomainListMgr::setDomains: Domain list may not be null");
    }

    domains_ = domains;

267 268 269 270 271 272
    // Look for the wild card domain. If present, set the member variable
    // to remember it.  This saves us from having to look for it every time
    // we attempt a match.
    DdnsDomainMap::iterator gotit = domains_->find(wildcard_domain_name_);
    if (gotit != domains_->end()) {
            wildcard_domain_ = gotit->second;
273 274 275 276 277 278 279 280 281 282 283
    }
}

bool
DdnsDomainListMgr::matchDomain(const std::string& fqdn, DdnsDomainPtr& domain) {
    // First check the case of one domain to rule them all.
    if ((size() == 1) && (wildcard_domain_)) {
        domain = wildcard_domain_;
        return (true);
    }

284 285 286 287 288 289
    // Iterate over the domain map looking for the domain which matches
    // the longest portion of the given fqdn.

    size_t req_len = fqdn.size();
    size_t match_len = 0;
    DdnsDomainMapPair map_pair;
290
    DdnsDomainPtr best_match;
291 292 293 294 295 296 297
    BOOST_FOREACH (map_pair, *domains_) {
        std::string domain_name = map_pair.first;
        size_t dom_len = domain_name.size();

        // If the domain name is longer than the fqdn, then it cant be match.
        if (req_len < dom_len) {
            continue;
298 299
        }

300 301
        // If the lengths are identical and the names match we're done.
        if (req_len == dom_len) {
302
            if (boost::iequals(fqdn, domain_name)) {
303 304 305 306 307 308 309 310 311 312
                // exact match, done
                domain = map_pair.second;
                return (true);
            }
        } else {
            // The fqdn is longer than the domain name.  Adjust the start
            // point of comparison by the excess in length.  Only do the
            // comparison if the adjustment lands on a boundary. This
            // prevents "onetwo.net" from matching "two.net".
            size_t offset = req_len - dom_len;
313
            if ((fqdn[offset - 1] == '.')  &&
314
               (boost::iequals(fqdn.substr(offset), domain_name))) {
315 316 317 318
                // Fqdn contains domain name, keep it if its better than
                // any we have matched so far.
                if (dom_len > match_len) {
                    match_len = dom_len;
319
                    best_match = map_pair.second;
320 321
                }
            }
322 323 324
        }
    }

325
    if (!best_match) {
326 327 328 329 330 331 332 333 334
        // There's no match. If they specified a wild card domain use it
        // otherwise there's no domain for this entry.
        if (wildcard_domain_) {
            domain = wildcard_domain_;
            return (true);
        }

        LOG_WARN(dctl_logger, DHCP_DDNS_NO_MATCH).arg(fqdn);
        return (false);
335
    }
336

337
    domain = best_match;
338
    return (true);
339 340 341 342
}

// *************************** PARSERS ***********************************

343 344 345
// *********************** TSIGKeyInfoParser  *************************

TSIGKeyInfoParser::TSIGKeyInfoParser(const std::string& entry_name,
346
                                     TSIGKeyInfoMapPtr keys)
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
    : entry_name_(entry_name), keys_(keys), local_scalars_() {
    if (!keys_) {
        isc_throw(D2CfgError, "TSIGKeyInfoParser ctor:"
                  " key storage cannot be null");
    }
}

TSIGKeyInfoParser::~TSIGKeyInfoParser() {
}

void
TSIGKeyInfoParser::build(isc::data::ConstElementPtr key_config) {
    isc::dhcp::ConfigPair config_pair;
    // For each element in the key configuration:
    // 1. Create a parser for the element.
    // 2. Invoke the parser's build method passing in the element's
    // configuration.
    // 3. Invoke the parser's commit method to store the element's parsed
    // data to the parser's local storage.
    BOOST_FOREACH (config_pair, key_config->mapValue()) {
367
        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
368 369
                                                       config_pair.second->
                                                       getPosition()));
370 371 372 373 374 375
        parser->build(config_pair.second);
        parser->commit();
    }

    std::string name;
    std::string algorithm;
376
    uint32_t digestbits = 0;
377
    std::string secret;
378
    std::map<std::string, isc::data::Element::Position> pos;
379

380
    // Fetch the key's parsed scalar values from parser's local storage.
381
    // Only digestbits is optional and doesn't throw when missing
382
    try {
383 384
        pos["name"] = local_scalars_.getParam("name", name);
        pos["algorithm"] = local_scalars_.getParam("algorithm", algorithm);
385 386
        pos["digest_bits"] = local_scalars_.getParam("digest_bits", digestbits,
                                                     DCfgContextBase::OPTIONAL);
387
        pos["secret"] = local_scalars_.getParam("secret", secret);
388 389
    } catch (const std::exception& ex) {
        isc_throw(D2CfgError, "TSIG Key incomplete : " << ex.what()
390
                  << " (" << key_config->getPosition() << ")");
391
    }
392 393 394

    // Name cannot be blank.
    if (name.empty()) {
395
        isc_throw(D2CfgError, "TSIG key must specify name (" << pos["name"] << ")");
396 397
    }

398 399 400 401 402
    // Currently, the premise is that key storage is always empty prior to
    // parsing so we are always adding keys never replacing them. Duplicates
    // are not allowed and should be flagged as a configuration error.
    if (keys_->find(name) != keys_->end()) {
        isc_throw(D2CfgError, "Duplicate TSIG key name specified : " << name
403
                              << " (" << pos["name"] << ")");
404 405 406 407 408 409
    }

    // Algorithm must be valid.
    try {
        TSIGKeyInfo::stringToAlgorithmName(algorithm);
    } catch (const std::exception& ex) {
410
        isc_throw(D2CfgError, "TSIG key : " << ex.what() << " (" << pos["algorithm"] << ")");
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 450
    // Not zero Digestbits must be an integral number of octets, greater
    // than 80 and the half of the full length
    if (digestbits > 0) {
      if ((digestbits % 8) != 0) {
          isc_throw(D2CfgError, "Invalid TSIG key digest_bits specified : " <<
                                digestbits << " (" << pos["digest_bits"] << ")");
      }
      if (digestbits < 80) {
          isc_throw(D2CfgError, "TSIG key digest_bits too small : " <<
                                digestbits << " (" << pos["digest_bits"] << ")");
      }
      if (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA224_STR)) {
          if (digestbits < 112) {
              isc_throw(D2CfgError, "TSIG key digest_bits too small : " <<
                                    digestbits << " (" << pos["digest_bits"]
                                    << ")");
          }
      } else if (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA256_STR)) {
          if (digestbits < 128) {
              isc_throw(D2CfgError, "TSIG key digest_bits too small : " <<
                                    digestbits << " (" << pos["digest_bits"]
                                    << ")");
          }
      } else if (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA384_STR)) {
          if (digestbits < 192) {
              isc_throw(D2CfgError, "TSIG key digest_bits too small : " <<
                                    digestbits << " (" << pos["digest_bits"]
                                    << ")");
          }
      } else if (boost::iequals(algorithm, TSIGKeyInfo::HMAC_SHA512_STR)) {
          if (digestbits < 256) {
              isc_throw(D2CfgError, "TSIG key digest_bits too small : " <<
                                    digestbits << " (" << pos["digest_bits"]
                                    << ")");
          }
      }
    }

451
    // Secret cannot be blank.
452
    // Cryptolink lib doesn't offer any way to validate these. As long as it
453 454 455
    // isn't blank we'll accept it.  If the content is bad, the call to in
    // TSIGKeyInfo::remakeKey() made in the TSIGKeyInfo ctor will throw.
    // We'll deal with that below.
456
    if (secret.empty()) {
457
        isc_throw(D2CfgError, "TSIG key must specify secret (" << pos["secret"] << ")");
458 459
    }

460 461 462 463 464
    // Everything should be valid, so create the key instance.
    // It is possible for the asiodns::dns::TSIGKey create to fail such as
    // with an invalid secret content.
    TSIGKeyInfoPtr key_info;
    try {
465
        key_info.reset(new TSIGKeyInfo(name, algorithm, secret, digestbits));
466
    } catch (const std::exception& ex) {
467
        isc_throw(D2CfgError, ex.what() << " (" << key_config->getPosition() << ")");
468

469
    }
470 471 472 473 474

    // Add the new TSIGKeyInfo to the key storage.
    (*keys_)[name]=key_info;
}

475
isc::dhcp::ParserPtr
476 477
TSIGKeyInfoParser::createConfigParser(const std::string& config_id,
                                      const isc::data::Element::Position& pos) {
478 479 480 481 482 483 484 485
    DhcpConfigParser* parser = NULL;
    // Based on the configuration id of the element, create the appropriate
    // parser. Scalars are set to use the parser's local scalar storage.
    if ((config_id == "name")  ||
        (config_id == "algorithm") ||
        (config_id == "secret")) {
        parser = new isc::dhcp::StringParser(config_id,
                                             local_scalars_.getStringStorage());
486 487 488
    } else if (config_id == "digest_bits") {
        parser = new isc::dhcp::Uint32Parser(config_id,
                                             local_scalars_.getUint32Storage());
489 490 491
    } else {
        isc_throw(NotImplemented,
                  "parser error: TSIGKeyInfo parameter not supported: "
492
                  << config_id << " (" << pos << ")");
493 494 495 496 497 498 499 500 501 502
    }

    // Return the new parser instance.
    return (isc::dhcp::ParserPtr(parser));
}

void
TSIGKeyInfoParser::commit() {
}

503 504 505 506
// *********************** TSIGKeyInfoListParser  *************************

TSIGKeyInfoListParser::TSIGKeyInfoListParser(const std::string& list_name,
                                       TSIGKeyInfoMapPtr keys)
507
    :list_name_(list_name), keys_(keys), local_keys_(new TSIGKeyInfoMap()),
508
     parsers_() {
509 510 511 512 513 514
    if (!keys_) {
        isc_throw(D2CfgError, "TSIGKeyInfoListParser ctor:"
                  " key storage cannot be null");
    }
}

515
TSIGKeyInfoListParser::~TSIGKeyInfoListParser() {
516 517 518 519
}

void
TSIGKeyInfoListParser::
520
build(isc::data::ConstElementPtr key_list) {
521 522 523 524 525 526 527 528 529 530 531
    int i = 0;
    isc::data::ConstElementPtr key_config;
    // For each key element in the key list:
    // 1. Create a parser for the key element.
    // 2. Invoke the parser's build method passing in the key's
    // configuration.
    // 3. Add the parser to a local collection of parsers.
    BOOST_FOREACH(key_config, key_list->listValue()) {
        // Create a name for the parser based on its position in the list.
        std::string entry_name = boost::lexical_cast<std::string>(i++);
        isc::dhcp::ParserPtr parser(new TSIGKeyInfoParser(entry_name,
532
                                                            local_keys_));
533 534 535
        parser->build(key_config);
        parsers_.push_back(parser);
    }
536 537 538 539

    // Now that we know we have a valid list, commit that list to the
    // area given to us during construction (i.e. to the d2 context).
    *keys_ = *local_keys_;
540 541 542 543 544 545 546 547 548 549 550
}

void
TSIGKeyInfoListParser::commit() {
    // Invoke commit on each server parser. This will cause each one to
    // create it's server instance and commit it to storage.
    BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
        parser->commit();
    }
}

551 552 553
// *********************** DnsServerInfoParser  *************************

DnsServerInfoParser::DnsServerInfoParser(const std::string& entry_name,
554
    DnsServerInfoStoragePtr servers)
555 556
    : entry_name_(entry_name), servers_(servers), local_scalars_() {
    if (!servers_) {
557
        isc_throw(D2CfgError, "DnsServerInfoParser ctor:"
558 559 560 561 562 563 564
                  " server storage cannot be null");
    }
}

DnsServerInfoParser::~DnsServerInfoParser() {
}

565
void
566 567 568 569
DnsServerInfoParser::build(isc::data::ConstElementPtr server_config) {
    isc::dhcp::ConfigPair config_pair;
    // For each element in the server configuration:
    // 1. Create a parser for the element.
570
    // 2. Invoke the parser's build method passing in the element's
571 572 573 574
    // configuration.
    // 3. Invoke the parser's commit method to store the element's parsed
    // data to the parser's local storage.
    BOOST_FOREACH (config_pair, server_config->mapValue()) {
575
        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
576 577
                                                       config_pair.second->
                                                       getPosition()));
578 579 580
        parser->build(config_pair.second);
        parser->commit();
    }
581

582 583
    std::string hostname;
    std::string ip_address;
584
    uint32_t port = DnsServerInfo::STANDARD_DNS_PORT;
585
    std::map<std::string, isc::data::Element::Position> pos;
586

587
    // Fetch the server configuration's parsed scalar values from parser's
588
    // local storage.  They're all optional, so no try-catch here.
589 590 591 592 593 594
    pos["hostname"] = local_scalars_.getParam("hostname", hostname,
                                              DCfgContextBase::OPTIONAL);
    pos["ip_address"] = local_scalars_.getParam("ip_address", ip_address,
                                                DCfgContextBase::OPTIONAL);
    pos["port"] =  local_scalars_.getParam("port", port,
                                           DCfgContextBase::OPTIONAL);
595 596

    // The configuration must specify one or the other.
597 598
    if (hostname.empty() == ip_address.empty()) {
        isc_throw(D2CfgError, "Dns Server must specify one or the other"
599
                  " of hostname or IP address"
600
                  << " (" << server_config->getPosition() << ")");
601 602 603 604 605
    }

    // Port cannot be zero.
    if (port == 0) {
        isc_throw(D2CfgError, "Dns Server : port cannot be 0"
606
                  << " (" << pos["port"] << ")");
607 608
    }

609 610
    DnsServerInfoPtr serverInfo;
    if (!hostname.empty()) {
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
        /// @todo when resolvable hostname is supported we create the entry
        /// as follows:
        ///
        /// @code
        /// // When  hostname is specified, create a valid, blank IOAddress
        /// // and then create the DnsServerInfo.
        /// isc::asiolink::IOAddress io_addr(DnsServerInfo::EMPTY_IP_STR);
        /// serverInfo.reset(new DnsServerInfo(hostname, io_addr, port));
        ///
        /// @endcode
        ///
        /// Resolution will be done prior to connection during transaction
        /// processing.
        /// Until then we'll throw unsupported.
        isc_throw(D2CfgError, "Dns Server : hostname is not yet supported"
626
                  << " (" << pos["hostname"] << ")");
627 628 629 630 631
    } else {
        try {
            // Create an IOAddress from the IP address string given and then
            // create the DnsServerInfo.
            isc::asiolink::IOAddress io_addr(ip_address);
632
            serverInfo.reset(new DnsServerInfo(hostname, io_addr, port));
633
        } catch (const isc::asiolink::IOError& ex) {
634
            isc_throw(D2CfgError, "Dns Server : invalid IP address : "
635
                      << ip_address << " (" << pos["ip_address"] << ")");
636 637 638 639
        }
    }

    // Add the new DnsServerInfo to the server storage.
640
    servers_->push_back(serverInfo);
641 642
}

643
isc::dhcp::ParserPtr
644 645 646
DnsServerInfoParser::createConfigParser(const std::string& config_id,
                                        const isc::data::Element::
                                        Position& pos) {
647 648 649 650 651 652 653 654 655 656 657 658 659
    DhcpConfigParser* parser = NULL;
    // Based on the configuration id of the element, create the appropriate
    // parser. Scalars are set to use the parser's local scalar storage.
    if ((config_id == "hostname")  ||
        (config_id == "ip_address")) {
        parser = new isc::dhcp::StringParser(config_id,
                                             local_scalars_.getStringStorage());
    } else if (config_id == "port") {
        parser = new isc::dhcp::Uint32Parser(config_id,
                                             local_scalars_.getUint32Storage());
    } else {
        isc_throw(NotImplemented,
                  "parser error: DnsServerInfo parameter not supported: "
660
                  << config_id << " (" << pos << ")");
661 662 663 664 665 666 667 668 669 670
    }

    // Return the new parser instance.
    return (isc::dhcp::ParserPtr(parser));
}

void
DnsServerInfoParser::commit() {
}

671 672 673 674 675 676 677 678 679 680 681 682 683 684
// *********************** DnsServerInfoListParser  *************************

DnsServerInfoListParser::DnsServerInfoListParser(const std::string& list_name,
                                       DnsServerInfoStoragePtr servers)
    :list_name_(list_name), servers_(servers), parsers_() {
    if (!servers_) {
        isc_throw(D2CfgError, "DdnsServerInfoListParser ctor:"
                  " server storage cannot be null");
    }
}

DnsServerInfoListParser::~DnsServerInfoListParser(){
}

685
void
686 687 688 689
DnsServerInfoListParser::
build(isc::data::ConstElementPtr server_list){
    int i = 0;
    isc::data::ConstElementPtr server_config;
690
    // For each server element in the server list:
691
    // 1. Create a parser for the server element.
692
    // 2. Invoke the parser's build method passing in the server's
693 694 695 696 697
    // configuration.
    // 3. Add the parser to a local collection of parsers.
    BOOST_FOREACH(server_config, server_list->listValue()) {
        // Create a name for the parser based on its position in the list.
        std::string entry_name = boost::lexical_cast<std::string>(i++);
698
        isc::dhcp::ParserPtr parser(new DnsServerInfoParser(entry_name,
699 700 701 702 703 704 705
                                                            servers_));
        parser->build(server_config);
        parsers_.push_back(parser);
    }

    // Domains must have at least one server.
    if (parsers_.size() == 0) {
706
        isc_throw (D2CfgError, "Server List must contain at least one server"
707
                   << " (" << server_list->getPosition() << ")");
708
    }
709
}
710

711 712 713
void
DnsServerInfoListParser::commit() {
    // Invoke commit on each server parser.
714 715 716 717 718 719 720 721
    BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
        parser->commit();
    }
}

// *********************** DdnsDomainParser  *************************

DdnsDomainParser::DdnsDomainParser(const std::string& entry_name,
722 723 724
                                   DdnsDomainMapPtr domains,
                                   TSIGKeyInfoMapPtr keys)
    : entry_name_(entry_name), domains_(domains), keys_(keys),
725 726
    local_servers_(new DnsServerInfoStorage()), local_scalars_() {
    if (!domains_) {
727
        isc_throw(D2CfgError,
728 729 730 731 732 733 734 735
                  "DdnsDomainParser ctor, domain storage cannot be null");
    }
}


DdnsDomainParser::~DdnsDomainParser() {
}

736
void
737 738 739
DdnsDomainParser::build(isc::data::ConstElementPtr domain_config) {
    // For each element in the domain configuration:
    // 1. Create a parser for the element.
740
    // 2. Invoke the parser's build method passing in the element's
741 742 743 744 745
    // configuration.
    // 3. Invoke the parser's commit method to store the element's parsed
    // data to the parser's local storage.
    isc::dhcp::ConfigPair config_pair;
    BOOST_FOREACH(config_pair, domain_config->mapValue()) {
746
        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
747 748
                                                       config_pair.second->
                                                       getPosition()));
749 750 751 752
        parser->build(config_pair.second);
        parser->commit();
    }

753
    // Now construct the domain.
754 755
    std::string name;
    std::string key_name;
756 757
    std::map<std::string, isc::data::Element::Position> pos;

758

759 760 761
    // Fetch the parsed scalar values from parser's local storage.
    // Any required that are missing will throw.
    try {
762 763 764
        pos["name"] = local_scalars_.getParam("name", name);
        pos["key_name"] = local_scalars_.getParam("key_name", key_name,
                                                  DCfgContextBase::OPTIONAL);
765 766
    } catch (const std::exception& ex) {
        isc_throw(D2CfgError, "DdnsDomain incomplete : " << ex.what()
767
                  << " (" << domain_config->getPosition() << ")");
768
    }
769 770

    // Blank domain names are not allowed.
771
    if (name.empty()) {
772 773
        isc_throw(D2CfgError, "DndsDomain : name cannot be blank ("
                   << pos["name"] << ")");
774 775 776 777 778 779 780
    }

    // Currently, the premise is that domain storage is always empty
    // prior to parsing so always adding domains never replacing them.
    // Duplicates are not allowed and should be flagged as a configuration
    // error.
    if (domains_->find(name) != domains_->end()) {
781
        isc_throw(D2CfgError, "Duplicate domain specified:" << name
782
                  << " (" << pos["name"] << ")");
783 784
    }

785 786 787
    // Key name is optional. If it is not blank, then find the key in the
    /// list of defined keys.
    TSIGKeyInfoPtr tsig_key_info;
788
    if (!key_name.empty()) {
789 790 791 792 793 794 795 796
        if (keys_) {
            TSIGKeyInfoMap::iterator kit = keys_->find(key_name);
            if (kit != keys_->end()) {
                tsig_key_info = kit->second;
            }
        }

        if (!tsig_key_info) {
797 798
            isc_throw(D2CfgError, "DdnsDomain : " << name
                      << " specifies an undefined key: " << key_name
799
                      << " (" << pos["key_name"] << ")");
800 801
        }
    }
802 803

    // Instantiate the new domain and add it to domain storage.
804
    DdnsDomainPtr domain(new DdnsDomain(name, local_servers_, tsig_key_info));
805 806

    // Add the new domain to the domain storage.
807 808 809 810
    (*domains_)[name] = domain;
}

isc::dhcp::ParserPtr
811 812
DdnsDomainParser::createConfigParser(const std::string& config_id,
                                     const isc::data::Element::Position& pos) {
813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
    DhcpConfigParser* parser = NULL;
    // Based on the configuration id of the element, create the appropriate
    // parser. Scalars are set to use the parser's local scalar storage.
    if ((config_id == "name")  ||
        (config_id == "key_name")) {
        parser = new isc::dhcp::StringParser(config_id,
                                             local_scalars_.getStringStorage());
    } else if (config_id == "dns_servers") {
       // Server list parser is given in our local server storage. It will pass
       // this down to its server parsers and is where they will write their
       // server instances upon commit.
       parser = new DnsServerInfoListParser(config_id, local_servers_);
    } else {
       isc_throw(NotImplemented,
                "parser error: DdnsDomain parameter not supported: "
828
                << config_id << " (" << pos << ")");
829 830 831 832 833 834 835 836
    }

    // Return the new domain parser instance.
    return (isc::dhcp::ParserPtr(parser));
}

void
DdnsDomainParser::commit() {
837 838 839 840 841
}

// *********************** DdnsDomainListParser  *************************

DdnsDomainListParser::DdnsDomainListParser(const std::string& list_name,
842 843 844
                                           DdnsDomainMapPtr domains,
                                           TSIGKeyInfoMapPtr keys)
    :list_name_(list_name), domains_(domains), keys_(keys), parsers_() {
845 846 847 848 849 850 851 852 853
    if (!domains_) {
        isc_throw(D2CfgError, "DdnsDomainListParser ctor:"
                  " domain storage cannot be null");
    }
}

DdnsDomainListParser::~DdnsDomainListParser(){
}

854
void
855 856
DdnsDomainListParser::
build(isc::data::ConstElementPtr domain_list){
857
    // For each domain element in the domain list:
858
    // 1. Create a parser for the domain element.
859
    // 2. Invoke the parser's build method passing in the domain's
860 861 862 863 864 865
    // configuration.
    // 3. Add the parser to the local collection of parsers.
    int i = 0;
    isc::data::ConstElementPtr domain_config;
    BOOST_FOREACH(domain_config, domain_list->listValue()) {
        std::string entry_name = boost::lexical_cast<std::string>(i++);
866 867
        isc::dhcp::ParserPtr parser(new DdnsDomainParser(entry_name,
                                                         domains_, keys_));
868 869 870 871 872
        parser->build(domain_config);
        parsers_.push_back(parser);
    }
}

873
void
874 875
DdnsDomainListParser::commit() {
    // Invoke commit on each server parser. This will cause each one to
876
    // create it's server instance and commit it to storage.
877 878 879 880 881 882 883 884 885
    BOOST_FOREACH(isc::dhcp::ParserPtr parser, parsers_) {
        parser->commit();
    }
}


// *********************** DdnsDomainListMgrParser  *************************

DdnsDomainListMgrParser::DdnsDomainListMgrParser(const std::string& entry_name,
886 887 888
                              DdnsDomainListMgrPtr mgr, TSIGKeyInfoMapPtr keys)
    : entry_name_(entry_name), mgr_(mgr), keys_(keys),
    local_domains_(new DdnsDomainMap()), local_scalars_() {
889 890 891 892 893 894
}


DdnsDomainListMgrParser::~DdnsDomainListMgrParser() {
}

895
void
896 897 898
DdnsDomainListMgrParser::build(isc::data::ConstElementPtr domain_config) {
    // For each element in the domain manager configuration:
    // 1. Create a parser for the element.
899
    // 2. Invoke the parser's build method passing in the element's
900 901 902 903 904
    // configuration.
    // 3. Invoke the parser's commit method to store the element's parsed
    // data to the parser's local storage.
    isc::dhcp::ConfigPair config_pair;
    BOOST_FOREACH(config_pair, domain_config->mapValue()) {
905 906 907
        isc::dhcp::ParserPtr parser(createConfigParser(config_pair.first,
                                                       config_pair.second->
                                                       getPosition()));
908 909 910
        parser->build(config_pair.second);
        parser->commit();
    }
911 912 913

    // Add the new domain to the domain storage.
    mgr_->setDomains(local_domains_);
914 915
}

916
isc::dhcp::ParserPtr
917 918 919
DdnsDomainListMgrParser::createConfigParser(const std::string& config_id,
                                            const isc::data::Element::
                                            Position& pos) {
920 921 922 923 924
    DhcpConfigParser* parser = NULL;
    if (config_id == "ddns_domains") {
       // Domain list parser is given our local domain storage. It will pass
       // this down to its domain parsers and is where they will write their
       // domain instances upon commit.
925
       parser = new DdnsDomainListParser(config_id, local_domains_, keys_);
926 927
    } else {
       isc_throw(NotImplemented, "parser error: "
928
                 "DdnsDomainListMgr parameter not supported: " << config_id
929
                 << " (" << pos << ")");
930 931 932 933 934 935
    }

    // Return the new domain parser instance.
    return (isc::dhcp::ParserPtr(parser));
}

936
void
937 938 939 940 941 942
DdnsDomainListMgrParser::commit() {
}


}; // end of isc::dhcp namespace
}; // end of isc namespace