d2_cfg_mgr_unittests.cc 49 KB
Newer Older
1
// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC")
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
//
// 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.

#include <config/module_spec.h>
#include <d2/d2_config.h>
#include <d2/d2_cfg_mgr.h>
#include <d_test_stubs.h>
19
#include <test_data_files_config.h>
20
#include <util/encode/base64.h>
21 22 23 24 25 26 27 28 29 30

#include <boost/foreach.hpp>
#include <gtest/gtest.h>

using namespace std;
using namespace isc;
using namespace isc::d2;

namespace {

31 32 33 34
std::string specfile(const std::string& name) {
    return (std::string(D2_SRC_DIR) + "/" + name);
}

35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
/// @brief Test fixture class for testing D2CfgMgr class.
/// It maintains an member instance of D2CfgMgr and provides methods for
/// converting JSON strings to configuration element sets, checking parse
/// results, and accessing the configuration context.
class D2CfgMgrTest : public ConfigParseTest {
public:

    /// @brief Constructor
    D2CfgMgrTest():cfg_mgr_(new D2CfgMgr) {
    }

    /// @brief Destructor
    ~D2CfgMgrTest() {
    }

    /// @brief Configuration manager instance.
    D2CfgMgrPtr cfg_mgr_;
};

/// @brief Tests that the spec file is valid.
/// Verifies that the BIND10 DHCP-DDNS configuration specification file
//  is valid.
57
TEST(D2SpecTest, basicSpec) {
58 59
    ASSERT_NO_THROW(isc::config::
                    moduleSpecFromFile(specfile("dhcp-ddns.spec")));
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
}

/// @brief Convenience function which compares the contents of the given
/// DnsServerInfo against the given set of values.
///
/// It is structured in such a way that each value is checked, and output
/// is generate for all that do not match.
///
/// @param server is a pointer to the server to check against.
/// @param hostname is the value to compare against server's hostname_.
/// @param ip_address is the string value to compare against server's
/// ip_address_.
/// @param port is the value to compare against server's port.
///
/// @return returns true if there is a match across the board, otherwise it
/// returns false.
bool checkServer(DnsServerInfoPtr server, const char* hostname,
                 const char *ip_address, uint32_t port)
{
    // Return value, assume its a match.
    bool result = true;

82
    if (!server) {
83 84 85 86 87 88
        EXPECT_TRUE(server);
        return false;
    }

    // Check hostname.
    if (server->getHostname() != hostname) {
89
        EXPECT_EQ(hostname, server->getHostname());
90 91 92 93 94
        result = false;
    }

    // Check IP address.
    if (server->getIpAddress().toText() != ip_address) {
95
        EXPECT_EQ(ip_address, server->getIpAddress().toText());
96 97 98 99 100
        result = false;
    }

    // Check port.
    if (server->getPort() !=  port) {
101
        EXPECT_EQ (port, server->getPort());
102 103 104 105 106 107
        result = false;
    }

    return (result);
}

108 109 110 111 112 113 114 115 116 117
/// @brief Convenience function which compares the contents of the given
/// TSIGKeyInfo against the given set of values.
///
/// @param key is a pointer to the key to check against.
/// @param name is the value to compare against key's name_.
/// @param algorithm is the string value to compare against key's algorithm.
/// @param secret is the value to compare against key's secret.
///
/// @return returns true if there is a match across the board, otherwise it
/// returns false.
118
bool checkKey(TSIGKeyInfoPtr key, const std::string& name,
119
                 const std::string& algorithm, const std::string& secret) {
120
    // Return value, assume its a match.
121 122 123 124 125
    return (((key) &&
        (key->getName() == name) &&
        (key->getAlgorithm() == algorithm)  &&
        (key->getSecret() ==  secret)  &&
        (key->getTSIGKey())));
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
}

/// @brief Test fixture class for testing DnsServerInfo parsing.
class TSIGKeyInfoTest : public ConfigParseTest {
public:

    /// @brief Constructor
    TSIGKeyInfoTest() {
        reset();
    }

    /// @brief Destructor
    ~TSIGKeyInfoTest() {
    }

    /// @brief Wipe out the current storage and parser and replace
    /// them with new ones.
    void reset() {
        keys_.reset(new TSIGKeyInfoMap());
        parser_.reset(new TSIGKeyInfoParser("test", keys_));
    }

    /// @brief Storage for "committing" keys.
    TSIGKeyInfoMapPtr keys_;

    /// @brief Pointer to the current parser instance.
    isc::dhcp::ParserPtr parser_;
};

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
/// @brief Test fixture class for testing DnsServerInfo parsing.
class DnsServerInfoTest : public ConfigParseTest {
public:

    /// @brief Constructor
    DnsServerInfoTest() {
        reset();
    }

    /// @brief Destructor
    ~DnsServerInfoTest() {
    }

    /// @brief Wipe out the current storage and parser and replace
    /// them with new ones.
    void reset() {
        servers_.reset(new DnsServerInfoStorage());
        parser_.reset(new DnsServerInfoParser("test", servers_));
    }

    /// @brief Storage for "committing" servers.
    DnsServerInfoStoragePtr servers_;

    /// @brief Pointer to the current parser instance.
    isc::dhcp::ParserPtr parser_;
};

182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211

/// @brief Test fixture class for testing DDnsDomain parsing.
class DdnsDomainTest : public ConfigParseTest {
public:

    /// @brief Constructor
    DdnsDomainTest() {
        reset();
    }

    /// @brief Destructor
    ~DdnsDomainTest() {
    }

    /// @brief Wipe out the current storage and parser and replace
    /// them with new ones.
    void reset() {
        keys_.reset(new TSIGKeyInfoMap());
        domains_.reset(new DdnsDomainMap());
        parser_.reset(new DdnsDomainParser("test", domains_, keys_));
    }

    /// @brief Add TSIGKeyInfos to the key map
    ///
    /// @param name the name of the key
    /// @param algorithm the algorithm of the key
    /// @param secret the secret value of the key
    void addKey(const std::string& name, const std::string& algorithm,
                const std::string& secret) {
        TSIGKeyInfoPtr key_info(new TSIGKeyInfo(name, algorithm, secret));
212
        (*keys_)[name]=key_info;
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
    }

    /// @brief Storage for "committing" domains.
    DdnsDomainMapPtr domains_;

    /// @brief Storage for TSIGKeys
    TSIGKeyInfoMapPtr keys_;

    /// @brief Pointer to the current parser instance.
    isc::dhcp::ParserPtr parser_;
};

/// @brief Tests the enforcement of data validation when parsing TSIGKeyInfos.
/// It verifies that:
/// 1. Name cannot be blank.
/// 2. Algorithm cannot be blank.
229
/// 3. Secret cannot be blank.
230
TEST_F(TSIGKeyInfoTest, invalidEntry) {
231 232 233
    // Config with a blank name entry.
    std::string config = "{"
                         " \"name\": \"\" , "
234
                         " \"algorithm\": \"HMAC-MD5\" , "
235
                         "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
236
                         "}";
237
    ASSERT_TRUE(fromJSON(config));
238

239 240
    // Verify that build fails on blank name.
    EXPECT_THROW(parser_->build(config_set_), D2CfgError);
241 242 243 244 245

    // Config with a blank algorithm entry.
    config = "{"
                         " \"name\": \"d2_key_one\" , "
                         " \"algorithm\": \"\" , "
246
                         "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
247 248
                         "}";

249
    ASSERT_TRUE(fromJSON(config));
250

251 252
    // Verify that build fails on blank algorithm.
    EXPECT_THROW(parser_->build(config_set_), D2CfgError);
253

254 255 256 257
    // Config with an invalid algorithm entry.
    config = "{"
                         " \"name\": \"d2_key_one\" , "
                         " \"algorithm\": \"bogus\" , "
258
                         "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
259 260 261 262 263 264 265
                         "}";

    ASSERT_TRUE(fromJSON(config));

    // Verify that build fails on blank algorithm.
    EXPECT_THROW(parser_->build(config_set_), D2CfgError);

266 267 268
    // Config with a blank secret entry.
    config = "{"
                         " \"name\": \"d2_key_one\" , "
269
                         " \"algorithm\": \"HMAC-MD5\" , "
270 271 272
                         " \"secret\": \"\" "
                         "}";

273
    ASSERT_TRUE(fromJSON(config));
274

275 276
    // Verify that build fails blank secret
    EXPECT_THROW(parser_->build(config_set_), D2CfgError);
277 278 279 280

    // Config with an invalid secret entry.
    config = "{"
                         " \"name\": \"d2_key_one\" , "
281
                         " \"algorithm\": \"HMAC-MD5\" , "
282 283 284 285 286 287 288
                         " \"secret\": \"bogus\" "
                         "}";

    ASSERT_TRUE(fromJSON(config));

    // Verify that build fails an invalid secret
    EXPECT_THROW(parser_->build(config_set_), D2CfgError);
289 290 291 292
}

/// @brief Verifies that TSIGKeyInfo parsing creates a proper TSIGKeyInfo
/// when given a valid combination of entries.
293
TEST_F(TSIGKeyInfoTest, validEntry) {
294 295 296
    // Valid entries for TSIG key, all items are required.
    std::string config = "{"
                         " \"name\": \"d2_key_one\" , "
297
                         " \"algorithm\": \"HMAC-MD5\" , "
298
                         " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
299
                         "}";
300
    ASSERT_TRUE(fromJSON(config));
301 302

    // Verify that it builds and commits without throwing.
303 304
    //ASSERT_NO_THROW(parser_->build(config_set_));
    (parser_->build(config_set_));
305 306 307 308
    ASSERT_NO_THROW(parser_->commit());

    // Verify the correct number of keys are present
    int count =  keys_->size();
309
    EXPECT_EQ(1, count);
310 311 312 313 314 315 316

    // Find the key and retrieve it.
    TSIGKeyInfoMap::iterator gotit = keys_->find("d2_key_one");
    ASSERT_TRUE(gotit != keys_->end());
    TSIGKeyInfoPtr& key = gotit->second;

    // Verify the key contents.
317
    EXPECT_TRUE(checkKey(key, "d2_key_one", "HMAC-MD5",
318
                         "dGhpcyBrZXkgd2lsbCBtYXRjaA=="));
319 320 321 322 323 324 325 326 327
}

/// @brief Verifies that attempting to parse an invalid list of TSIGKeyInfo
/// entries is detected.
TEST_F(TSIGKeyInfoTest, invalidTSIGKeyList) {
    // Construct a list of keys with an invalid key entry.
    std::string config = "["

                         " { \"name\": \"key1\" , "
328
                         "   \"algorithm\": \"HMAC-MD5\" ,"
329
                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
330
                         " },"
331
                         // this entry has an invalid algorithm
332 333
                         " { \"name\": \"key2\" , "
                         "   \"algorithm\": \"\" ,"
334
                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
335 336
                         " },"
                         " { \"name\": \"key3\" , "
337
                         "   \"algorithm\": \"HMAC-MD5\" ,"
338
                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
339 340 341
                         " }"
                         " ]";

342
    ASSERT_TRUE(fromJSON(config));
343 344 345 346 347 348

    // Create the list parser.
    isc::dhcp::ParserPtr parser;
    ASSERT_NO_THROW(parser.reset(new TSIGKeyInfoListParser("test", keys_)));

    // Verify that the list builds without errors.
349
    EXPECT_THROW(parser->build(config_set_), D2CfgError);
350 351 352 353 354 355 356 357 358
}

/// @brief Verifies that attempting to parse an invalid list of TSIGKeyInfo
/// entries is detected.
TEST_F(TSIGKeyInfoTest, duplicateTSIGKey) {
    // Construct a list of keys with an invalid key entry.
    std::string config = "["

                         " { \"name\": \"key1\" , "
359
                         "   \"algorithm\": \"HMAC-MD5\" ,"
360
                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
361 362
                         " },"
                         " { \"name\": \"key2\" , "
363
                         "   \"algorithm\": \"HMAC-MD5\" ,"
364
                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
365 366
                         " },"
                         " { \"name\": \"key1\" , "
367
                         "   \"algorithm\": \"HMAC-MD5\" ,"
368
                         "   \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" "
369 370 371
                         " }"
                         " ]";

372
    ASSERT_TRUE(fromJSON(config));
373 374 375 376 377 378

    // Create the list parser.
    isc::dhcp::ParserPtr parser;
    ASSERT_NO_THROW(parser.reset(new TSIGKeyInfoListParser("test", keys_)));

    // Verify that the list builds without errors.
379
    EXPECT_THROW(parser->build(config_set_), D2CfgError);
380 381 382
}

/// @brief Verifies a valid list of TSIG Keys parses correctly.
383
/// Also verifies that all of the supported algorithm names work.
384
TEST_F(TSIGKeyInfoTest, validTSIGKeyList) {
385
    // Construct a valid list of keys.
386 387 388
    std::string config = "["

                         " { \"name\": \"key1\" , "
389
                         "   \"algorithm\": \"HMAC-MD5\" ,"
390
                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
391 392
                         " },"
                         " { \"name\": \"key2\" , "
393
                         "   \"algorithm\": \"HMAC-SHA1\" ,"
394
                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
395 396
                         " },"
                         " { \"name\": \"key3\" , "
397
                         "   \"algorithm\": \"HMAC-SHA256\" ,"
398
                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
399 400
                         " },"
                         " { \"name\": \"key4\" , "
401
                         "   \"algorithm\": \"HMAC-SHA224\" ,"
402
                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
403 404
                         " },"
                         " { \"name\": \"key5\" , "
405
                         "   \"algorithm\": \"HMAC-SHA384\" ,"
406
                         "  \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
407 408
                         " },"
                         " { \"name\": \"key6\" , "
409
                         "   \"algorithm\": \"HMAC-SHA512\" ,"
410
                         "   \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" "
411 412 413
                         " }"
                         " ]";

414
    ASSERT_TRUE(fromJSON(config));
415 416 417 418 419 420 421 422

    // Verify that the list builds and commits without errors.
    // Create the list parser.
    isc::dhcp::ParserPtr parser;
    ASSERT_NO_THROW(parser.reset(new TSIGKeyInfoListParser("test", keys_)));
    ASSERT_NO_THROW(parser->build(config_set_));
    ASSERT_NO_THROW(parser->commit());

423
    std::string ref_secret = "dGhpcyBrZXkgd2lsbCBtYXRjaA==";
424 425
    // Verify the correct number of keys are present
    int count =  keys_->size();
426
    ASSERT_EQ(6, count);
427 428 429 430 431 432 433

    // Find the 1st key and retrieve it.
    TSIGKeyInfoMap::iterator gotit = keys_->find("key1");
    ASSERT_TRUE(gotit != keys_->end());
    TSIGKeyInfoPtr& key = gotit->second;

    // Verify the key contents.
434
    EXPECT_TRUE(checkKey(key, "key1", TSIGKeyInfo::HMAC_MD5_STR, ref_secret));
435 436 437 438 439 440 441

    // Find the 2nd key and retrieve it.
    gotit = keys_->find("key2");
    ASSERT_TRUE(gotit != keys_->end());
    key = gotit->second;

    // Verify the key contents.
442
    EXPECT_TRUE(checkKey(key, "key2", TSIGKeyInfo::HMAC_SHA1_STR, ref_secret));
443 444 445 446 447 448 449

    // Find the 3rd key and retrieve it.
    gotit = keys_->find("key3");
    ASSERT_TRUE(gotit != keys_->end());
    key = gotit->second;

    // Verify the key contents.
450 451
    EXPECT_TRUE(checkKey(key, "key3", TSIGKeyInfo::HMAC_SHA256_STR,
                         ref_secret));
452 453 454 455 456 457 458

    // Find the 4th key and retrieve it.
    gotit = keys_->find("key4");
    ASSERT_TRUE(gotit != keys_->end());
    key = gotit->second;

    // Verify the key contents.
459 460
    EXPECT_TRUE(checkKey(key, "key4", TSIGKeyInfo::HMAC_SHA224_STR,
                         ref_secret));
461 462 463 464 465 466 467

    // Find the 5th key and retrieve it.
    gotit = keys_->find("key5");
    ASSERT_TRUE(gotit != keys_->end());
    key = gotit->second;

    // Verify the key contents.
468 469
    EXPECT_TRUE(checkKey(key, "key5", TSIGKeyInfo::HMAC_SHA384_STR,
                         ref_secret));
470 471 472 473 474 475 476

    // Find the 6th key and retrieve it.
    gotit = keys_->find("key6");
    ASSERT_TRUE(gotit != keys_->end());
    key = gotit->second;

    // Verify the key contents.
477 478
    EXPECT_TRUE(checkKey(key, "key6", TSIGKeyInfo::HMAC_SHA512_STR,
                         ref_secret));
479 480
}

481 482 483 484 485
/// @brief Tests the enforcement of data validation when parsing DnsServerInfos.
/// It verifies that:
/// 1. Specifying both a hostname and an ip address is not allowed.
/// 2. Specifying both blank a hostname and blank ip address is not allowed.
/// 3. Specifying a negative port number is not allowed.
486
TEST_F(DnsServerInfoTest, invalidEntry) {
487
    // Create a config in which both host and ip address are supplied.
488
    // Verify that build fails.
489 490
    std::string config = "{ \"hostname\": \"pegasus.tmark\", "
                         "  \"ip_address\": \"127.0.0.1\" } ";
491
    ASSERT_TRUE(fromJSON(config));
492
    EXPECT_THROW(parser_->build(config_set_), D2CfgError);
493 494

    // Neither host nor ip address supplied
495
    // Verify that builds fails.
496 497
    config = "{ \"hostname\": \"\", "
             "  \"ip_address\": \"\" } ";
498
    ASSERT_TRUE(fromJSON(config));
499
    EXPECT_THROW(parser_->build(config_set_), D2CfgError);
500 501 502 503 504

    // Create a config with a negative port number.
    // Verify that build fails.
    config = "{ \"ip_address\": \"192.168.5.6\" ,"
             "  \"port\": -100 }";
505
    ASSERT_TRUE(fromJSON(config));
506 507 508
    EXPECT_THROW (parser_->build(config_set_), isc::BadValue);
}

509

510 511 512 513 514 515
/// @brief Verifies that DnsServerInfo parsing creates a proper DnsServerInfo
/// when given a valid combination of entries.
/// It verifies that:
/// 1. A DnsServerInfo entry is correctly made, when given only a hostname.
/// 2. A DnsServerInfo entry is correctly made, when given ip address and port.
/// 3. A DnsServerInfo entry is correctly made, when given only an ip address.
516
TEST_F(DnsServerInfoTest, validEntry) {
517 518
    // Valid entries for dynamic host
    std::string config = "{ \"hostname\": \"pegasus.tmark\" }";
519
    ASSERT_TRUE(fromJSON(config));
520 521 522 523 524 525 526

    // Verify that it builds and commits without throwing.
    ASSERT_NO_THROW(parser_->build(config_set_));
    ASSERT_NO_THROW(parser_->commit());

    // Verify the correct number of servers are present
    int count =  servers_->size();
527
    EXPECT_EQ(1, count);
528 529 530 531

    // Verify the server exists and has the correct values.
    DnsServerInfoPtr server = (*servers_)[0];
    EXPECT_TRUE(checkServer(server, "pegasus.tmark",
532 533
                            DnsServerInfo::EMPTY_IP_STR,
                            DnsServerInfo::STANDARD_DNS_PORT));
534 535 536 537 538 539 540

    // Start over for a new test.
    reset();

    // Valid entries for static ip
    config = " { \"ip_address\": \"127.0.0.1\" , "
             "  \"port\": 100 }";
541
    ASSERT_TRUE(fromJSON(config));
542 543 544 545 546 547 548

    // Verify that it builds and commits without throwing.
    ASSERT_NO_THROW(parser_->build(config_set_));
    ASSERT_NO_THROW(parser_->commit());

    // Verify the correct number of servers are present
    count =  servers_->size();
549
    EXPECT_EQ(1, count);
550 551 552 553 554 555 556 557 558 559

    // Verify the server exists and has the correct values.
    server = (*servers_)[0];
    EXPECT_TRUE(checkServer(server, "", "127.0.0.1", 100));

    // Start over for a new test.
    reset();

    // Valid entries for static ip, no port
    config = " { \"ip_address\": \"192.168.2.5\" }";
560
    ASSERT_TRUE(fromJSON(config));
561 562 563 564 565 566 567

    // Verify that it builds and commits without throwing.
    ASSERT_NO_THROW(parser_->build(config_set_));
    ASSERT_NO_THROW(parser_->commit());

    // Verify the correct number of servers are present
    count =  servers_->size();
568
    EXPECT_EQ(1, count);
569 570 571 572

    // Verify the server exists and has the correct values.
    server = (*servers_)[0];
    EXPECT_TRUE(checkServer(server, "", "192.168.2.5",
573
                            DnsServerInfo::STANDARD_DNS_PORT));
574 575 576 577 578 579 580 581 582
}

/// @brief Verifies that attempting to parse an invalid list of DnsServerInfo
/// entries is detected.
TEST_F(ConfigParseTest, invalidServerList) {
    // Construct a list of servers with an invalid server entry.
    std::string config = "[ { \"hostname\": \"one.tmark\" }, "
                        "{ \"hostname\": \"\" }, "
                        "{ \"hostname\": \"three.tmark\" } ]";
583
    ASSERT_TRUE(fromJSON(config));
584 585 586 587 588 589

    // Create the server storage and list parser.
    DnsServerInfoStoragePtr servers(new DnsServerInfoStorage());
    isc::dhcp::ParserPtr parser;
    ASSERT_NO_THROW(parser.reset(new DnsServerInfoListParser("test", servers)));

590 591
    // Verify that build fails.
    EXPECT_THROW(parser->build(config_set_), D2CfgError);
592 593 594 595 596 597 598 599 600
}

/// @brief Verifies that a list of DnsServerInfo entries parses correctly given
/// a valid configuration.
TEST_F(ConfigParseTest, validServerList) {
    // Create a valid list of servers.
    std::string config = "[ { \"hostname\": \"one.tmark\" }, "
                        "{ \"hostname\": \"two.tmark\" }, "
                        "{ \"hostname\": \"three.tmark\" } ]";
601
    ASSERT_TRUE(fromJSON(config));
602 603 604 605 606 607 608 609 610 611 612 613

    // Create the server storage and list parser.
    DnsServerInfoStoragePtr servers(new DnsServerInfoStorage());
    isc::dhcp::ParserPtr parser;
    ASSERT_NO_THROW(parser.reset(new DnsServerInfoListParser("test", servers)));

    // Verfiy that the list builds and commits without error.
    ASSERT_NO_THROW(parser->build(config_set_));
    ASSERT_NO_THROW(parser->commit());

    // Verify that the server storage contains the correct number of servers.
    int count =  servers->size();
614
    EXPECT_EQ(3, count);
615 616 617

    // Verify the first server exists and has the correct values.
    DnsServerInfoPtr server = (*servers)[0];
618 619
    EXPECT_TRUE(checkServer(server, "one.tmark", DnsServerInfo::EMPTY_IP_STR,
                            DnsServerInfo::STANDARD_DNS_PORT));
620 621 622

    // Verify the second server exists and has the correct values.
    server = (*servers)[1];
623 624
    EXPECT_TRUE(checkServer(server, "two.tmark", DnsServerInfo::EMPTY_IP_STR,
                            DnsServerInfo::STANDARD_DNS_PORT));
625 626 627

    // Verify the third server exists and has the correct values.
    server = (*servers)[2];
628 629
    EXPECT_TRUE(checkServer(server, "three.tmark", DnsServerInfo::EMPTY_IP_STR,
                            DnsServerInfo::STANDARD_DNS_PORT));
630 631 632 633 634 635 636 637
}

/// @brief Tests the enforcement of data validation when parsing DdnsDomains.
/// It verifies that:
/// 1. Domain storage cannot be null when constructing a DdnsDomainParser.
/// 2. The name entry is not optional.
/// 3. The server list man not be empty.
/// 4. That a mal-formed server entry is detected.
638 639
/// 5. That an undefined key name is detected.
TEST_F(DdnsDomainTest, invalidDdnsDomainEntry) {
640
    // Verify that attempting to construct the parser with null storage fails.
641 642 643
    DdnsDomainMapPtr domains;
    ASSERT_THROW(isc::dhcp::ParserPtr(
                 new DdnsDomainParser("test", domains, keys_)), D2CfgError);
644 645 646 647 648 649 650 651 652 653

    // Create a domain configuration without a name
    std::string config = "{  \"key_name\": \"d2_key.tmark.org\" , "
                         "  \"dns_servers\" : [ "
                         "  {  \"ip_address\": \"127.0.0.1\" , "
                         "    \"port\": 100 },"
                         "  { \"ip_address\": \"127.0.0.2\" , "
                         "    \"port\": 200 },"
                         "  {  \"ip_address\": \"127.0.0.3\" , "
                         "    \"port\": 300 } ] } ";
654
    ASSERT_TRUE(fromJSON(config));
655

656 657
    // Verify that the domain configuration builds fails.
    EXPECT_THROW(parser_->build(config_set_), isc::dhcp::DhcpConfigError);
658 659 660 661 662 663

    // Create a domain configuration with an empty server list.
    config = "{ \"name\": \"tmark.org\" , "
             "  \"key_name\": \"d2_key.tmark.org\" , "
             "  \"dns_servers\" : [ "
             "   ] } ";
664
    ASSERT_TRUE(fromJSON(config));
665 666

    // Verify that the domain configuration build fails.
667
    EXPECT_THROW(parser_->build(config_set_), D2CfgError);
668 669 670 671 672 673 674

    // Create a domain configuration with a mal-formed server entry.
    config = "{ \"name\": \"tmark.org\" , "
             "  \"key_name\": \"d2_key.tmark.org\" , "
             "  \"dns_servers\" : [ "
             "  {  \"ip_address\": \"127.0.0.3\" , "
             "    \"port\": -1 } ] } ";
675
    ASSERT_TRUE(fromJSON(config));
676 677

    // Verify that the domain configuration build fails.
678
    EXPECT_THROW(parser_->build(config_set_), isc::BadValue);
679

680 681 682 683 684 685
    // Create a domain configuration without an defined key name
    config = "{ \"name\": \"tmark.org\" , "
             "  \"key_name\": \"d2_key.tmark.org\" , "
             "  \"dns_servers\" : [ "
             "  {  \"ip_address\": \"127.0.0.3\" , "
             "    \"port\": 300 } ] } ";
686
    ASSERT_TRUE(fromJSON(config));
687

688 689
    // Verify that the domain configuration build fails.
    EXPECT_THROW(parser_->build(config_set_), D2CfgError);
690
}
691 692 693 694 695 696

/// @brief Verifies the basics of parsing DdnsDomains.
/// It verifies that:
/// 1. Valid construction of DdnsDomainParser functions.
/// 2. Given a valid, configuration entry, DdnsDomainParser parses
/// correctly.
697 698
/// (It indirectly verifies the operation of DdnsDomainMap).
TEST_F(DdnsDomainTest, ddnsDomainParsing) {
699 700 701 702 703 704 705 706 707 708 709 710
    // Create a valid domain configuration entry containing three valid
    // servers.
    std::string config =
                        "{ \"name\": \"tmark.org\" , "
                        "  \"key_name\": \"d2_key.tmark.org\" , "
                        "  \"dns_servers\" : [ "
                        "  {  \"ip_address\": \"127.0.0.1\" , "
                        "    \"port\": 100 },"
                        "  { \"ip_address\": \"127.0.0.2\" , "
                        "    \"port\": 200 },"
                        "  {  \"ip_address\": \"127.0.0.3\" , "
                        "    \"port\": 300 } ] } ";
711
    ASSERT_TRUE(fromJSON(config));
712

713
    // Add a TSIG key to the test key map, so key validation will pass.
714
    addKey("d2_key.tmark.org", "HMAC-MD5", "GWG/Xfbju4O2iXGqkSu4PQ==");
715 716

    // Verify that the domain configuration builds and commits without error.
717 718
    ASSERT_NO_THROW(parser_->build(config_set_));
    ASSERT_NO_THROW(parser_->commit());
719 720

    // Verify that the domain storage contains the correct number of domains.
721
    int count =  domains_->size();
722
    EXPECT_EQ(1, count);
723 724 725

    // Verify that the expected domain exists and can be retrieved from
    // the storage.
726 727
    DdnsDomainMap::iterator gotit = domains_->find("tmark.org");
    ASSERT_TRUE(gotit != domains_->end());
728 729 730
    DdnsDomainPtr& domain = gotit->second;

    // Verify the name and key_name values.
731 732
    EXPECT_EQ("tmark.org", domain->getName());
    EXPECT_EQ("d2_key.tmark.org", domain->getKeyName());
733 734
    ASSERT_TRUE(domain->getTSIGKeyInfo());
    ASSERT_TRUE(domain->getTSIGKeyInfo()->getTSIGKey());
735 736 737 738 739 740

    // Verify that the server list exists and contains the correct number of
    // servers.
    const DnsServerInfoStoragePtr& servers = domain->getServers();
    EXPECT_TRUE(servers);
    count =  servers->size();
741
    EXPECT_EQ(3, count);
742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762

    // Fetch each server and verify its contents.
    DnsServerInfoPtr server = (*servers)[0];
    EXPECT_TRUE(server);

    EXPECT_TRUE(checkServer(server, "", "127.0.0.1", 100));

    server = (*servers)[1];
    EXPECT_TRUE(server);

    EXPECT_TRUE(checkServer(server, "", "127.0.0.2", 200));

    server = (*servers)[2];
    EXPECT_TRUE(server);

    EXPECT_TRUE(checkServer(server, "", "127.0.0.3", 300));
}

/// @brief Tests the fundamentals of parsing DdnsDomain lists.
/// This test verifies that given a valid domain list configuration
/// it will accurately parse and populate each domain in the list.
763
TEST_F(DdnsDomainTest, DdnsDomainListParsing) {
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
    // Create a valid domain list configuration, with two domains
    // that have three servers each.
    std::string config =
                        "[ "
                        "{ \"name\": \"tmark.org\" , "
                        "  \"key_name\": \"d2_key.tmark.org\" , "
                        "  \"dns_servers\" : [ "
                        "  { \"ip_address\": \"127.0.0.1\" , "
                        "    \"port\": 100 },"
                        "  { \"ip_address\": \"127.0.0.2\" , "
                        "    \"port\": 200 },"
                        "  { \"ip_address\": \"127.0.0.3\" , "
                        "    \"port\": 300 } ] } "
                        ", "
                        "{ \"name\": \"billcat.net\" , "
                        "  \"key_name\": \"d2_key.billcat.net\" , "
                        "  \"dns_servers\" : [ "
                        "  { \"ip_address\": \"127.0.0.4\" , "
                        "    \"port\": 400 },"
                        "  { \"ip_address\": \"127.0.0.5\" , "
                        "    \"port\": 500 },"
                        "  { \"ip_address\": \"127.0.0.6\" , "
                        "    \"port\": 600 } ] } "
                        "] ";

789
    ASSERT_TRUE(fromJSON(config));
790

791
    // Add keys to key map so key validation passes.
792 793
    addKey("d2_key.tmark.org", "HMAC-MD5", "GWG/Xfbju4O2iXGqkSu4PQ==");
    addKey("d2_key.billcat.net", "HMAC-MD5", "GWG/Xfbju4O2iXGqkSu4PQ==");
794 795

    // Create the list parser
796
    isc::dhcp::ParserPtr list_parser;
797 798
    ASSERT_NO_THROW(list_parser.reset(
                    new DdnsDomainListParser("test", domains_, keys_)));
799 800

    // Verify that the domain configuration builds and commits without error.
801 802
    ASSERT_NO_THROW(list_parser->build(config_set_));
    ASSERT_NO_THROW(list_parser->commit());
803 804

    // Verify that the domain storage contains the correct number of domains.
805
    int count =  domains_->size();
806
    EXPECT_EQ(2, count);
807 808

    // Verify that the first domain exists and can be retrieved.
809 810
    DdnsDomainMap::iterator gotit = domains_->find("tmark.org");
    ASSERT_TRUE(gotit != domains_->end());
811 812 813
    DdnsDomainPtr& domain = gotit->second;

    // Verify the name and key_name values of the first domain.
814 815
    EXPECT_EQ("tmark.org", domain->getName());
    EXPECT_EQ("d2_key.tmark.org", domain->getKeyName());
816 817
    ASSERT_TRUE(domain->getTSIGKeyInfo());
    ASSERT_TRUE(domain->getTSIGKeyInfo()->getTSIGKey());
818 819 820 821 822

    // Verify the each of the first domain's servers
    DnsServerInfoStoragePtr servers = domain->getServers();
    EXPECT_TRUE(servers);
    count =  servers->size();
823
    EXPECT_EQ(3, count);
824 825 826 827 828 829 830 831 832 833 834 835 836 837

    DnsServerInfoPtr server = (*servers)[0];
    EXPECT_TRUE(server);
    EXPECT_TRUE(checkServer(server, "", "127.0.0.1", 100));

    server = (*servers)[1];
    EXPECT_TRUE(server);
    EXPECT_TRUE(checkServer(server, "", "127.0.0.2", 200));

    server = (*servers)[2];
    EXPECT_TRUE(server);
    EXPECT_TRUE(checkServer(server, "", "127.0.0.3", 300));

    // Verify second domain
838 839
    gotit = domains_->find("billcat.net");
    ASSERT_TRUE(gotit != domains_->end());
840 841 842
    domain = gotit->second;

    // Verify the name and key_name values of the second domain.
843 844
    EXPECT_EQ("billcat.net", domain->getName());
    EXPECT_EQ("d2_key.billcat.net", domain->getKeyName());
845 846
    ASSERT_TRUE(domain->getTSIGKeyInfo());
    ASSERT_TRUE(domain->getTSIGKeyInfo()->getTSIGKey());
847 848 849 850 851

    // Verify the each of second domain's servers
    servers = domain->getServers();
    EXPECT_TRUE(servers);
    count =  servers->size();
852
    EXPECT_EQ(3, count);
853 854 855 856 857 858 859 860 861 862 863 864 865 866 867

    server = (*servers)[0];
    EXPECT_TRUE(server);
    EXPECT_TRUE(checkServer(server, "", "127.0.0.4", 400));

    server = (*servers)[1];
    EXPECT_TRUE(server);
    EXPECT_TRUE(checkServer(server, "", "127.0.0.5", 500));

    server = (*servers)[2];
    EXPECT_TRUE(server);
    EXPECT_TRUE(checkServer(server, "", "127.0.0.6", 600));
}

/// @brief Tests that a domain list configuration cannot contain duplicates.
868
TEST_F(DdnsDomainTest, duplicateDomain) {
869 870 871 872 873 874 875 876 877 878 879 880 881 882
    // Create a domain list configuration that contains two domains with
    // the same name.
    std::string config =
                        "[ "
                        "{ \"name\": \"tmark.org\" , "
                        "  \"dns_servers\" : [ "
                        "  { \"ip_address\": \"127.0.0.3\" , "
                        "    \"port\": 300 } ] } "
                        ", "
                        "{ \"name\": \"tmark.org\" , "
                        "  \"dns_servers\" : [ "
                        "  { \"ip_address\": \"127.0.0.3\" , "
                        "    \"port\": 300 } ] } "
                        "] ";
883
    ASSERT_TRUE(fromJSON(config));
884

885
    // Create the list parser
886
    isc::dhcp::ParserPtr list_parser;
887 888
    ASSERT_NO_THROW(list_parser.reset(
                    new DdnsDomainListParser("test", domains_, keys_)));
889

890 891
    // Verify that the parse build fails.
    EXPECT_THROW(list_parser->build(config_set_), D2CfgError);
892 893 894 895 896 897 898 899
}

/// @brief Tests construction of D2CfgMgr
/// This test verifies that a D2CfgMgr constructs properly.
TEST(D2CfgMgr, construction) {
    D2CfgMgr *cfg_mgr = NULL;

    // Verify that configuration manager constructions without error.
900
    ASSERT_NO_THROW(cfg_mgr = new D2CfgMgr());
901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920

    // Verify that the context can be retrieved and is not null.
    D2CfgContextPtr context;
    ASSERT_NO_THROW(context = cfg_mgr->getD2CfgContext());
    EXPECT_TRUE(context);

    // Verify that the forward manager can be retrieved and is not null.
    EXPECT_TRUE(context->getForwardMgr());

    // Verify that the reverse manager can be retrieved and is not null.
    EXPECT_TRUE(context->getReverseMgr());

    // Verify that the manager can be destructed without error.
    EXPECT_NO_THROW(delete cfg_mgr);
}

/// @brief Tests the parsing of a complete, valid DHCP-DDNS configuration.
/// This tests passes the configuration into an instance of D2CfgMgr just
/// as it would be done by d2_process in response to a configuration update
/// event.
921
TEST_F(D2CfgMgrTest, fullConfig) {
922 923 924 925 926 927 928
    // Create a configuration with all of application level parameters, plus
    // both the forward and reverse ddns managers.  Both managers have two
    // domains with three servers per domain.
    std::string config = "{ "
                        "\"interface\" : \"eth1\" , "
                        "\"ip_address\" : \"192.168.1.33\" , "
                        "\"port\" : 88 , "
929 930 931
                        "\"tsig_keys\": ["
                        "{"
                        "  \"name\": \"d2_key.tmark.org\" , "
932
                        "  \"algorithm\": \"hmac-md5\" , "
933
                        "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
934 935 936
                        "},"
                        "{"
                        "  \"name\": \"d2_key.billcat.net\" , "
937
                        "  \"algorithm\": \"hmac-md5\" , "
938
                        "   \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" "
939 940
                        "}"
                        "],"
941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976
                        "\"forward_ddns\" : {"
                        "\"ddns_domains\": [ "
                        "{ \"name\": \"tmark.org\" , "
                        "  \"key_name\": \"d2_key.tmark.org\" , "
                        "  \"dns_servers\" : [ "
                        "  { \"hostname\": \"one.tmark\" } , "
                        "  { \"hostname\": \"two.tmark\" } , "
                        "  { \"hostname\": \"three.tmark\"} "
                        "  ] } "
                        ", "
                        "{ \"name\": \"billcat.net\" , "
                        "  \"key_name\": \"d2_key.billcat.net\" , "
                        "  \"dns_servers\" : [ "
                        "  { \"hostname\": \"four.billcat\" } , "
                        "  { \"hostname\": \"five.billcat\" } , "
                        "  { \"hostname\": \"six.billcat\" } "
                        "  ] } "
                        "] },"
                        "\"reverse_ddns\" : {"
                        "\"ddns_domains\": [ "
                        "{ \"name\": \" 0.168.192.in.addr.arpa.\" , "
                        "  \"key_name\": \"d2_key.tmark.org\" , "
                        "  \"dns_servers\" : [ "
                        "  { \"hostname\": \"one.rev\" } , "
                        "  { \"hostname\": \"two.rev\" } , "
                        "  { \"hostname\": \"three.rev\" } "
                        "  ] } "
                        ", "
                        "{ \"name\": \" 0.247.106.in.addr.arpa.\" , "
                        "  \"key_name\": \"d2_key.billcat.net\" , "
                        "  \"dns_servers\" : [ "
                        "  { \"hostname\": \"four.rev\" }, "
                        "  { \"hostname\": \"five.rev\" } , "
                        "  { \"hostname\": \"six.rev\" } "
                        "  ] } "
                        "] } }";
977
    ASSERT_TRUE(fromJSON(config));
978 979 980

    // Verify that we can parse the configuration.
    answer_ = cfg_mgr_->parseConfig(config_set_);
981
    ASSERT_TRUE(checkAnswer(0));
982 983 984 985 986 987 988 989

    // Verify that the D2 context can be retrieved and is not null.
    D2CfgContextPtr context;
    ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext());

    // Verify that the application level scalars have the proper values.
    std::string interface;
    EXPECT_NO_THROW (context->getParam("interface", interface));
990
    EXPECT_EQ("eth1", interface);
991 992 993

    std::string ip_address;
    EXPECT_NO_THROW (context->getParam("ip_address", ip_address));
994
    EXPECT_EQ("192.168.1.33", ip_address);
995 996 997

    uint32_t port = 0;
    EXPECT_NO_THROW (context->getParam("port", port));
998
    EXPECT_EQ(88, port);
999 1000 1001 1002 1003 1004

    // Verify that the forward manager can be retrieved.
    DdnsDomainListMgrPtr mgr = context->getForwardMgr();
    ASSERT_TRUE(mgr);

    // Verify that the forward manager has the correct number of domains.
1005
    DdnsDomainMapPtr domains = mgr->getDomains();
1006 1007
    ASSERT_TRUE(domains);
    int count =  domains->size();
1008
    EXPECT_EQ(2, count);
1009 1010 1011 1012 1013

    // Verify that the server count in each of the forward manager domains.
    // NOTE that since prior tests have validated server parsing, we are are
    // assuming that the servers did in fact parse correctly if the correct
    // number of them are there.
1014
    DdnsDomainMapPair domain_pair;
1015 1016 1017 1018 1019
    BOOST_FOREACH(domain_pair, (*domains)) {
        DdnsDomainPtr domain = domain_pair.second;
        DnsServerInfoStoragePtr servers = domain->getServers();
        count = servers->size();
        EXPECT_TRUE(servers);
1020
        EXPECT_EQ(3, count);
1021 1022 1023 1024 1025 1026 1027 1028 1029
    }

    // Verify that the reverse manager can be retrieved.
    mgr = context->getReverseMgr();
    ASSERT_TRUE(mgr);

    // Verify that the reverse manager has the correct number of domains.
    domains = mgr->getDomains();
    count =  domains->size();
1030
    EXPECT_EQ(2, count);
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040

    // Verify that the server count in each of the reverse manager domains.
    // NOTE that since prior tests have validated server parsing, we are are
    // assuming that the servers did in fact parse correctly if the correct
    // number of them are there.
    BOOST_FOREACH(domain_pair, (*domains)) {
        DdnsDomainPtr domain = domain_pair.second;
        DnsServerInfoStoragePtr servers = domain->getServers();
        count = servers->size();
        EXPECT_TRUE(servers);
1041
        EXPECT_EQ(3, count);
1042
    }
1043

1044 1045 1046 1047
    // Test directional update flags.
    EXPECT_TRUE(cfg_mgr_->forwardUpdatesEnabled());
    EXPECT_TRUE(cfg_mgr_->reverseUpdatesEnabled());

1048
    // Verify that parsing the exact same configuration a second time
1049
    // does not cause a duplicate value errors.
1050 1051
    answer_ = cfg_mgr_->parseConfig(config_set_);
    ASSERT_TRUE(checkAnswer(0));
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062
}

/// @brief Tests the basics of the D2CfgMgr FQDN-domain matching
/// This test uses a valid configuration to exercise the D2CfgMgr
/// forward FQDN-to-domain matching.
/// It verifies that:
/// 1. Given an FQDN which exactly matches a domain's name, that domain is
/// returned as match.
/// 2. Given a FQDN for sub-domain in the list, returns the proper match.
/// 3. Given a FQDN that matches no domain name, returns the wild card domain
/// as a match.
1063
TEST_F(D2CfgMgrTest, forwardMatch) {
1064 1065 1066 1067 1068 1069
    // Create  configuration with one domain, one sub domain, and the wild
    // card.
    std::string config = "{ "
                        "\"interface\" : \"eth1\" , "
                        "\"ip_address\" : \"192.168.1.33\" , "
                        "\"port\" : 88 , "
1070
                        "\"tsig_keys\": [] ,"
1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083