// Copyright (C) 2013-2014 Internet Systems Consortium, Inc. ("ISC") // // 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 #include #include #include #include #include #include #include using namespace std; using namespace isc; using namespace isc::d2; namespace { std::string specfile(const std::string& name) { return (std::string(D2_SRC_DIR) + "/" + name); } /// @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()), d2_params_() { } /// @brief Destructor ~D2CfgMgrTest() { } /// @brief Configuration manager instance. D2CfgMgrPtr cfg_mgr_; /// @brief Build JSON configuration string for a D2Params element /// /// Constructs a JSON string for "params" element using replacable /// parameters. /// /// @param ip_address string to insert as ip_address value /// @param port integer to insert as port value /// @param dns_server_timeout integer to insert as dns_server_timeout value /// @param ncr_protocol string to insert as ncr_protocol value /// @param ncr_format string to insert as ncr_format value /// /// @return std::string containing the JSON configuration text std::string makeParamsConfigString(const std::string& ip_address, const int port, const int dns_server_timeout, const std::string& ncr_protocol, const std::string& ncr_format) { std::ostringstream config; config << "{" " \"ip_address\": \"" << ip_address << "\" , " " \"port\": " << port << " , " " \"dns_server_timeout\": " << dns_server_timeout << " , " " \"ncr_protocol\": \"" << ncr_protocol << "\" , " " \"ncr_format\": \"" << ncr_format << "\", " "\"tsig_keys\": [], " "\"forward_ddns\" : {}, " "\"reverse_ddns\" : {} " "}"; return (config.str()); } /// @brief Enumeration to select between expected configuration outcomes enum RunConfigMode { SHOULD_PASS, SHOULD_FAIL }; /// @brief Parses a configuration string and tests against a given outcome /// /// Convenience method which accepts JSON text and an expected pass or fail /// outcome. It converts the text into an ElementPtr and passes that to /// configuration manager's parseConfig method. It then tests the /// parse result against the expected outcome If they do not match it /// the method asserts a failure. If they do match, it refreshes the /// the D2Params pointer with the newly parsed instance. /// /// @param config_str the JSON configuration text to parse /// @param mode indicator if the parsing should fail or not. It defaults /// defaults to SHOULD_PASS. /// void runConfig(std::string config_str, RunConfigMode mode=SHOULD_PASS) { // We assume the config string is valid JSON. ASSERT_TRUE(fromJSON(config_str)); // Parse the configuration and verify we got the expected outcome. answer_ = cfg_mgr_->parseConfig(config_set_); ASSERT_TRUE(checkAnswer(mode == SHOULD_FAIL)); // Verify that the D2 context can be retrieved and is not null. D2CfgContextPtr context; ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext()); // Verify that the global scalars have the proper values. d2_params_ = context->getD2Params(); ASSERT_TRUE(d2_params_); } /// @brief Pointer the D2Params most recently parsed. D2ParamsPtr d2_params_; }; /// @brief Tests that the spec file is valid. /// Verifies that the BIND10 DHCP-DDNS configuration specification file // is valid. TEST(D2SpecTest, basicSpec) { ASSERT_NO_THROW(isc::config:: moduleSpecFromFile(specfile("dhcp-ddns.spec"))); } /// @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; if (!server) { EXPECT_TRUE(server); return false; } // Check hostname. if (server->getHostname() != hostname) { EXPECT_EQ(hostname, server->getHostname()); result = false; } // Check IP address. if (server->getIpAddress().toText() != ip_address) { EXPECT_EQ(ip_address, server->getIpAddress().toText()); result = false; } // Check port. if (server->getPort() != port) { EXPECT_EQ (port, server->getPort()); result = false; } return (result); } /// @brief Convenience function which compares the contents of the given /// TSIGKeyInfo against the given set of values, and that the TSIGKey /// member points to a key. /// /// @param key is a pointer to the TSIGKeyInfo instance to verify /// @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. bool checkKey(TSIGKeyInfoPtr key, const std::string& name, const std::string& algorithm, const std::string& secret) { // Return value, assume its a match. return (((key) && (key->getName() == name) && (key->getAlgorithm() == algorithm) && (key->getSecret() == secret) && (key->getTSIGKey()))); } /// @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_; }; /// @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_; }; /// @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)); (*keys_)[name]=key_info; } /// @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 a basic valid configuration for D2Param. TEST_F(D2CfgMgrTest, validParamsEntry) { // Verify that ip_address can be valid v4 address. std::string config = makeParamsConfigString ("192.0.0.1", 777, 333, "UDP", "JSON"); runConfig(config); EXPECT_EQ(isc::asiolink::IOAddress("192.0.0.1"), d2_params_->getIpAddress()); EXPECT_EQ(777, d2_params_->getPort()); EXPECT_EQ(333, d2_params_->getDnsServerTimeout()); EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_params_->getNcrProtocol()); EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_params_->getNcrFormat()); // Verify that ip_address can be valid v6 address. config = makeParamsConfigString ("3001::5", 777, 333, "UDP", "JSON"); runConfig(config); // Verify that the global scalars have the proper values. EXPECT_EQ(isc::asiolink::IOAddress("3001::5"), d2_params_->getIpAddress()); } /// @brief Tests default values for D2Params. /// It verifies that D2Params is populated with default value for optional /// parameter if not supplied in the configuration. /// Currently they are all optional. TEST_F(D2CfgMgrTest, defaultValues) { // Check that omitting ip_address gets you its default std::string config = "{" " \"port\": 777 , " " \"dns_server_timeout\": 333 , " " \"ncr_protocol\": \"UDP\" , " " \"ncr_format\": \"JSON\", " "\"tsig_keys\": [], " "\"forward_ddns\" : {}, " "\"reverse_ddns\" : {} " "}"; runConfig(config); EXPECT_EQ(isc::asiolink::IOAddress(D2Params::DFT_IP_ADDRESS), d2_params_->getIpAddress()); // Check that omitting port gets you its default config = "{" " \"ip_address\": \"192.0.0.1\" , " " \"dns_server_timeout\": 333 , " " \"ncr_protocol\": \"UDP\" , " " \"ncr_format\": \"JSON\", " "\"tsig_keys\": [], " "\"forward_ddns\" : {}, " "\"reverse_ddns\" : {} " "}"; runConfig(config); EXPECT_EQ(D2Params::DFT_PORT, d2_params_->getPort()); // Check that omitting timeout gets you its default config = "{" " \"ip_address\": \"192.0.0.1\" , " " \"port\": 777 , " " \"ncr_protocol\": \"UDP\" , " " \"ncr_format\": \"JSON\", " "\"tsig_keys\": [], " "\"forward_ddns\" : {}, " "\"reverse_ddns\" : {} " "}"; runConfig(config); EXPECT_EQ(D2Params::DFT_DNS_SERVER_TIMEOUT, d2_params_->getDnsServerTimeout()); // Check that protocol timeout gets you its default config = "{" " \"ip_address\": \"192.0.0.1\" , " " \"port\": 777 , " " \"dns_server_timeout\": 333 , " " \"ncr_format\": \"JSON\", " "\"tsig_keys\": [], " "\"forward_ddns\" : {}, " "\"reverse_ddns\" : {} " "}"; runConfig(config); EXPECT_EQ(dhcp_ddns::stringToNcrProtocol(D2Params::DFT_NCR_PROTOCOL), d2_params_->getNcrProtocol()); // Check that format timeout gets you its default config = "{" " \"ip_address\": \"192.0.0.1\" , " " \"port\": 777 , " " \"dns_server_timeout\": 333 , " " \"ncr_protocol\": \"UDP\", " "\"tsig_keys\": [], " "\"forward_ddns\" : {}, " "\"reverse_ddns\" : {} " "}"; runConfig(config); EXPECT_EQ(dhcp_ddns::stringToNcrFormat(D2Params::DFT_NCR_FORMAT), d2_params_->getNcrFormat()); } /// @brief Tests the unsupported scalar parameters and objects are detected. TEST_F(D2CfgMgrTest, unsupportedTopLevelItems) { // Check that an unsupported top level parameter fails. std::string config = "{" " \"ip_address\": \"127.0.0.1\", " " \"port\": 777 , " " \"dns_server_timeout\": 333 , " " \"ncr_protocol\": \"UDP\" , " " \"ncr_format\": \"JSON\", " "\"tsig_keys\": [], " "\"forward_ddns\" : {}, " "\"reverse_ddns\" : {}, " "\"bogus_param\" : true " "}"; runConfig(config, SHOULD_FAIL); // Check that unsupported top level objects fails. For // D2 these fail as they are not in the parse order. config = "{" " \"ip_address\": \"127.0.0.1\", " " \"port\": 777 , " " \"dns_server_timeout\": 333 , " " \"ncr_protocol\": \"UDP\" , " " \"ncr_format\": \"JSON\", " "\"tsig_keys\": [], " "\"bogus_object_one\" : {}, " "\"forward_ddns\" : {}, " "\"reverse_ddns\" : {}, " "\"bogus_object_two\" : {} " "}"; runConfig(config, SHOULD_FAIL); } /// @brief Tests the enforcement of data validation when parsing D2Params. /// It verifies that: /// -# ip_address cannot be "0.0.0.0" /// -# ip_address cannot be "::" /// -# port cannot be 0 /// -# dns_server_timeout cannat be 0 /// -# ncr_protocol must be valid /// -# ncr_format must be valid TEST_F(D2CfgMgrTest, invalidEntry) { // Cannot use IPv4 ANY address std::string config = makeParamsConfigString ("0.0.0.0", 777, 333, "UDP", "JSON"); runConfig(config, SHOULD_FAIL); // Cannot use IPv6 ANY address config = makeParamsConfigString ("::", 777, 333, "UDP", "JSON"); runConfig(config, SHOULD_FAIL); // Cannot use port 0 config = makeParamsConfigString ("127.0.0.1", 0, 333, "UDP", "JSON"); runConfig(config, SHOULD_FAIL); // Cannot use dns server timeout of 0 config = makeParamsConfigString ("127.0.0.1", 777, 0, "UDP", "JSON"); runConfig(config, SHOULD_FAIL); // Invalid protocol config = makeParamsConfigString ("127.0.0.1", 777, 333, "BOGUS", "JSON"); runConfig(config, SHOULD_FAIL); // Invalid format config = makeParamsConfigString ("127.0.0.1", 777, 333, "UDP", "BOGUS"); runConfig(config, SHOULD_FAIL); } /// @brief Tests the enforcement of data validation when parsing TSIGKeyInfos. /// It verifies that: /// 1. Name cannot be blank. /// 2. Algorithm cannot be blank. /// 3. Secret cannot be blank. TEST_F(TSIGKeyInfoTest, invalidEntry) { // Config with a blank name entry. std::string config = "{" " \"name\": \"\" , " " \"algorithm\": \"HMAC-MD5\" , " " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" " "}"; ASSERT_TRUE(fromJSON(config)); // Verify that build fails on blank name. EXPECT_THROW(parser_->build(config_set_), D2CfgError); // Config with a blank algorithm entry. config = "{" " \"name\": \"d2_key_one\" , " " \"algorithm\": \"\" , " " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" " "}"; ASSERT_TRUE(fromJSON(config)); // Verify that build fails on blank algorithm. EXPECT_THROW(parser_->build(config_set_), D2CfgError); // Config with an invalid algorithm entry. config = "{" " \"name\": \"d2_key_one\" , " " \"algorithm\": \"bogus\" , " " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" " "}"; ASSERT_TRUE(fromJSON(config)); // Verify that build fails on blank algorithm. EXPECT_THROW(parser_->build(config_set_), D2CfgError); // Config with a blank secret entry. config = "{" " \"name\": \"d2_key_one\" , " " \"algorithm\": \"HMAC-MD5\" , " " \"secret\": \"\" " "}"; ASSERT_TRUE(fromJSON(config)); // Verify that build fails blank secret EXPECT_THROW(parser_->build(config_set_), D2CfgError); // Config with an invalid secret entry. config = "{" " \"name\": \"d2_key_one\" , " " \"algorithm\": \"HMAC-MD5\" , " " \"secret\": \"bogus\" " "}"; ASSERT_TRUE(fromJSON(config)); // Verify that build fails an invalid secret EXPECT_THROW(parser_->build(config_set_), D2CfgError); } /// @brief Verifies that TSIGKeyInfo parsing creates a proper TSIGKeyInfo /// when given a valid combination of entries. TEST_F(TSIGKeyInfoTest, validEntry) { // Valid entries for TSIG key, all items are required. std::string config = "{" " \"name\": \"d2_key_one\" , " " \"algorithm\": \"HMAC-MD5\" , " " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" " "}"; ASSERT_TRUE(fromJSON(config)); // Verify that it builds and commits without throwing. //ASSERT_NO_THROW(parser_->build(config_set_)); (parser_->build(config_set_)); ASSERT_NO_THROW(parser_->commit()); // Verify the correct number of keys are present int count = keys_->size(); EXPECT_EQ(1, count); // 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. EXPECT_TRUE(checkKey(key, "d2_key_one", "HMAC-MD5", "dGhpcyBrZXkgd2lsbCBtYXRjaA==")); } /// @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\" , " " \"algorithm\": \"HMAC-MD5\" ," " \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" " " }," // this entry has an invalid algorithm " { \"name\": \"key2\" , " " \"algorithm\": \"\" ," " \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" " " }," " { \"name\": \"key3\" , " " \"algorithm\": \"HMAC-MD5\" ," " \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" " " }" " ]"; ASSERT_TRUE(fromJSON(config)); // Create the list parser. isc::dhcp::ParserPtr parser; ASSERT_NO_THROW(parser.reset(new TSIGKeyInfoListParser("test", keys_))); // Verify that the list builds without errors. EXPECT_THROW(parser->build(config_set_), D2CfgError); } /// @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\" , " " \"algorithm\": \"HMAC-MD5\" ," " \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" " " }," " { \"name\": \"key2\" , " " \"algorithm\": \"HMAC-MD5\" ," " \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" " " }," " { \"name\": \"key1\" , " " \"algorithm\": \"HMAC-MD5\" ," " \"secret\": \"GWG/Xfbju4O2iXGqkSu4PQ==\" " " }" " ]"; ASSERT_TRUE(fromJSON(config)); // Create the list parser. isc::dhcp::ParserPtr parser; ASSERT_NO_THROW(parser.reset(new TSIGKeyInfoListParser("test", keys_))); // Verify that the list builds without errors. EXPECT_THROW(parser->build(config_set_), D2CfgError); } /// @brief Verifies a valid list of TSIG Keys parses correctly. /// Also verifies that all of the supported algorithm names work. TEST_F(TSIGKeyInfoTest, validTSIGKeyList) { // Construct a valid list of keys. std::string config = "[" " { \"name\": \"key1\" , " " \"algorithm\": \"HMAC-MD5\" ," " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" " " }," " { \"name\": \"key2\" , " " \"algorithm\": \"HMAC-SHA1\" ," " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" " " }," " { \"name\": \"key3\" , " " \"algorithm\": \"HMAC-SHA256\" ," " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" " " }," " { \"name\": \"key4\" , " " \"algorithm\": \"HMAC-SHA224\" ," " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" " " }," " { \"name\": \"key5\" , " " \"algorithm\": \"HMAC-SHA384\" ," " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" " " }," " { \"name\": \"key6\" , " " \"algorithm\": \"HMAC-SHA512\" ," " \"secret\": \"dGhpcyBrZXkgd2lsbCBtYXRjaA==\" " " }" " ]"; ASSERT_TRUE(fromJSON(config)); // 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()); std::string ref_secret = "dGhpcyBrZXkgd2lsbCBtYXRjaA=="; // Verify the correct number of keys are present int count = keys_->size(); ASSERT_EQ(6, count); // 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. EXPECT_TRUE(checkKey(key, "key1", TSIGKeyInfo::HMAC_MD5_STR, ref_secret)); // Find the 2nd key and retrieve it. gotit = keys_->find("key2"); ASSERT_TRUE(gotit != keys_->end()); key = gotit->second; // Verify the key contents. EXPECT_TRUE(checkKey(key, "key2", TSIGKeyInfo::HMAC_SHA1_STR, ref_secret)); // Find the 3rd key and retrieve it. gotit = keys_->find("key3"); ASSERT_TRUE(gotit != keys_->end()); key = gotit->second; // Verify the key contents. EXPECT_TRUE(checkKey(key, "key3", TSIGKeyInfo::HMAC_SHA256_STR, ref_secret)); // Find the 4th key and retrieve it. gotit = keys_->find("key4"); ASSERT_TRUE(gotit != keys_->end()); key = gotit->second; // Verify the key contents. EXPECT_TRUE(checkKey(key, "key4", TSIGKeyInfo::HMAC_SHA224_STR, ref_secret)); // Find the 5th key and retrieve it. gotit = keys_->find("key5"); ASSERT_TRUE(gotit != keys_->end()); key = gotit->second; // Verify the key contents. EXPECT_TRUE(checkKey(key, "key5", TSIGKeyInfo::HMAC_SHA384_STR, ref_secret)); // Find the 6th key and retrieve it. gotit = keys_->find("key6"); ASSERT_TRUE(gotit != keys_->end()); key = gotit->second; // Verify the key contents. EXPECT_TRUE(checkKey(key, "key6", TSIGKeyInfo::HMAC_SHA512_STR, ref_secret)); } /// @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. TEST_F(DnsServerInfoTest, invalidEntry) { // Create a config in which both host and ip address are supplied. // Verify that build fails. std::string config = "{ \"hostname\": \"pegasus.tmark\", " " \"ip_address\": \"127.0.0.1\" } "; ASSERT_TRUE(fromJSON(config)); EXPECT_THROW(parser_->build(config_set_), D2CfgError); // Neither host nor ip address supplied // Verify that builds fails. config = "{ \"hostname\": \"\", " " \"ip_address\": \"\" } "; ASSERT_TRUE(fromJSON(config)); EXPECT_THROW(parser_->build(config_set_), D2CfgError); // Create a config with a negative port number. // Verify that build fails. config = "{ \"ip_address\": \"192.168.5.6\" ," " \"port\": -100 }"; ASSERT_TRUE(fromJSON(config)); EXPECT_THROW (parser_->build(config_set_), isc::BadValue); } /// @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. TEST_F(DnsServerInfoTest, validEntry) { // Valid entries for dynamic host std::string config = "{ \"hostname\": \"pegasus.tmark\" }"; ASSERT_TRUE(fromJSON(config)); // 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(); EXPECT_EQ(1, count); // Verify the server exists and has the correct values. DnsServerInfoPtr server = (*servers_)[0]; EXPECT_TRUE(checkServer(server, "pegasus.tmark", DnsServerInfo::EMPTY_IP_STR, DnsServerInfo::STANDARD_DNS_PORT)); // Start over for a new test. reset(); // Valid entries for static ip config = " { \"ip_address\": \"127.0.0.1\" , " " \"port\": 100 }"; ASSERT_TRUE(fromJSON(config)); // 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(); EXPECT_EQ(1, count); // 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\" }"; ASSERT_TRUE(fromJSON(config)); // 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(); EXPECT_EQ(1, count); // Verify the server exists and has the correct values. server = (*servers_)[0]; EXPECT_TRUE(checkServer(server, "", "192.168.2.5", DnsServerInfo::STANDARD_DNS_PORT)); } /// @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\" } ]"; ASSERT_TRUE(fromJSON(config)); // Create the server storage and list parser. DnsServerInfoStoragePtr servers(new DnsServerInfoStorage()); isc::dhcp::ParserPtr parser; ASSERT_NO_THROW(parser.reset(new DnsServerInfoListParser("test", servers))); // Verify that build fails. EXPECT_THROW(parser->build(config_set_), D2CfgError); } /// @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\" } ]"; ASSERT_TRUE(fromJSON(config)); // 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(); EXPECT_EQ(3, count); // Verify the first server exists and has the correct values. DnsServerInfoPtr server = (*servers)[0]; EXPECT_TRUE(checkServer(server, "one.tmark", DnsServerInfo::EMPTY_IP_STR, DnsServerInfo::STANDARD_DNS_PORT)); // Verify the second server exists and has the correct values. server = (*servers)[1]; EXPECT_TRUE(checkServer(server, "two.tmark", DnsServerInfo::EMPTY_IP_STR, DnsServerInfo::STANDARD_DNS_PORT)); // Verify the third server exists and has the correct values. server = (*servers)[2]; EXPECT_TRUE(checkServer(server, "three.tmark", DnsServerInfo::EMPTY_IP_STR, DnsServerInfo::STANDARD_DNS_PORT)); } /// @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. /// 5. That an undefined key name is detected. TEST_F(DdnsDomainTest, invalidDdnsDomainEntry) { // Verify that attempting to construct the parser with null storage fails. DdnsDomainMapPtr domains; ASSERT_THROW(isc::dhcp::ParserPtr( new DdnsDomainParser("test", domains, keys_)), D2CfgError); // 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 } ] } "; ASSERT_TRUE(fromJSON(config)); // Verify that the domain configuration builds fails. EXPECT_THROW(parser_->build(config_set_), isc::dhcp::DhcpConfigError); // Create a domain configuration with an empty server list. config = "{ \"name\": \"tmark.org\" , " " \"key_name\": \"d2_key.tmark.org\" , " " \"dns_servers\" : [ " " ] } "; ASSERT_TRUE(fromJSON(config)); // Verify that the domain configuration build fails. EXPECT_THROW(parser_->build(config_set_), D2CfgError); // 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 } ] } "; ASSERT_TRUE(fromJSON(config)); // Verify that the domain configuration build fails. EXPECT_THROW(parser_->build(config_set_), isc::BadValue); // 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 } ] } "; ASSERT_TRUE(fromJSON(config)); // Verify that the domain configuration build fails. EXPECT_THROW(parser_->build(config_set_), D2CfgError); } /// @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. /// (It indirectly verifies the operation of DdnsDomainMap). TEST_F(DdnsDomainTest, ddnsDomainParsing) { // 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 } ] } "; ASSERT_TRUE(fromJSON(config)); // Add a TSIG key to the test key map, so key validation will pass. addKey("d2_key.tmark.org", "HMAC-MD5", "GWG/Xfbju4O2iXGqkSu4PQ=="); // Verify that the domain configuration builds and commits without error. ASSERT_NO_THROW(parser_->build(config_set_)); ASSERT_NO_THROW(parser_->commit()); // Verify that the domain storage contains the correct number of domains. int count = domains_->size(); EXPECT_EQ(1, count); // Verify that the expected domain exists and can be retrieved from // the storage. DdnsDomainMap::iterator gotit = domains_->find("tmark.org"); ASSERT_TRUE(gotit != domains_->end()); DdnsDomainPtr& domain = gotit->second; // Verify the name and key_name values. EXPECT_EQ("tmark.org", domain->getName()); EXPECT_EQ("d2_key.tmark.org", domain->getKeyName()); ASSERT_TRUE(domain->getTSIGKeyInfo()); ASSERT_TRUE(domain->getTSIGKeyInfo()->getTSIGKey()); // Verify that the server list exists and contains the correct number of // servers. const DnsServerInfoStoragePtr& servers = domain->getServers(); EXPECT_TRUE(servers); count = servers->size(); EXPECT_EQ(3, count); // 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. TEST_F(DdnsDomainTest, DdnsDomainListParsing) { // 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 } ] } " "] "; ASSERT_TRUE(fromJSON(config)); // Add keys to key map so key validation passes. addKey("d2_key.tmark.org", "HMAC-MD5", "GWG/Xfbju4O2iXGqkSu4PQ=="); addKey("d2_key.billcat.net", "HMAC-MD5", "GWG/Xfbju4O2iXGqkSu4PQ=="); // Create the list parser isc::dhcp::ParserPtr list_parser; ASSERT_NO_THROW(list_parser.reset( new DdnsDomainListParser("test", domains_, keys_))); // Verify that the domain configuration builds and commits without error. ASSERT_NO_THROW(list_parser->build(config_set_)); ASSERT_NO_THROW(list_parser->commit()); // Verify that the domain storage contains the correct number of domains. int count = domains_->size(); EXPECT_EQ(2, count); // Verify that the first domain exists and can be retrieved. DdnsDomainMap::iterator gotit = domains_->find("tmark.org"); ASSERT_TRUE(gotit != domains_->end()); DdnsDomainPtr& domain = gotit->second; // Verify the name and key_name values of the first domain. EXPECT_EQ("tmark.org", domain->getName()); EXPECT_EQ("d2_key.tmark.org", domain->getKeyName()); ASSERT_TRUE(domain->getTSIGKeyInfo()); ASSERT_TRUE(domain->getTSIGKeyInfo()->getTSIGKey()); // Verify the each of the first domain's servers DnsServerInfoStoragePtr servers = domain->getServers(); EXPECT_TRUE(servers); count = servers->size(); EXPECT_EQ(3, count); 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 gotit = domains_->find("billcat.net"); ASSERT_TRUE(gotit != domains_->end()); domain = gotit->second; // Verify the name and key_name values of the second domain. EXPECT_EQ("billcat.net", domain->getName()); EXPECT_EQ("d2_key.billcat.net", domain->getKeyName()); ASSERT_TRUE(domain->getTSIGKeyInfo()); ASSERT_TRUE(domain->getTSIGKeyInfo()->getTSIGKey()); // Verify the each of second domain's servers servers = domain->getServers(); EXPECT_TRUE(servers); count = servers->size(); EXPECT_EQ(3, count); 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. TEST_F(DdnsDomainTest, duplicateDomain) { // 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 } ] } " "] "; ASSERT_TRUE(fromJSON(config)); // Create the list parser isc::dhcp::ParserPtr list_parser; ASSERT_NO_THROW(list_parser.reset( new DdnsDomainListParser("test", domains_, keys_))); // Verify that the parse build fails. EXPECT_THROW(list_parser->build(config_set_), D2CfgError); } /// @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. ASSERT_NO_THROW(cfg_mgr = new D2CfgMgr()); // 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. TEST_F(D2CfgMgrTest, fullConfig) { // 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 = "{ " "\"ip_address\" : \"192.168.1.33\" , " "\"port\" : 88 , " " \"dns_server_timeout\": 333 , " " \"ncr_protocol\": \"UDP\" , " " \"ncr_format\": \"JSON\", " "\"tsig_keys\": [" "{" " \"name\": \"d2_key.tmark.org\" , " " \"algorithm\": \"hmac-md5\" , " " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" " "}," "{" " \"name\": \"d2_key.billcat.net\" , " " \"algorithm\": \"hmac-md5\" , " " \"secret\": \"LSWXnfkKZjdPJI5QxlpnfQ==\" " "}" "]," "\"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\" } " " ] } " "] } }"; ASSERT_TRUE(fromJSON(config)); // Verify that we can parse the configuration. answer_ = cfg_mgr_->parseConfig(config_set_); ASSERT_TRUE(checkAnswer(0)); // Verify that the D2 context can be retrieved and is not null. D2CfgContextPtr context; ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext()); // Verify that the global scalars have the proper values. D2ParamsPtr& d2_params = context->getD2Params(); ASSERT_TRUE(d2_params); EXPECT_EQ(isc::asiolink::IOAddress("192.168.1.33"), d2_params->getIpAddress()); EXPECT_EQ(88, d2_params->getPort()); EXPECT_EQ(333, d2_params->getDnsServerTimeout()); EXPECT_EQ(dhcp_ddns::NCR_UDP, d2_params->getNcrProtocol()); EXPECT_EQ(dhcp_ddns::FMT_JSON, d2_params->getNcrFormat()); // 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. DdnsDomainMapPtr domains = mgr->getDomains(); ASSERT_TRUE(domains); int count = domains->size(); EXPECT_EQ(2, count); // 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. DdnsDomainMapPair domain_pair; BOOST_FOREACH(domain_pair, (*domains)) { DdnsDomainPtr domain = domain_pair.second; DnsServerInfoStoragePtr servers = domain->getServers(); count = servers->size(); EXPECT_TRUE(servers); EXPECT_EQ(3, count); } // 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(); EXPECT_EQ(2, count); // 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); EXPECT_EQ(3, count); } // Test directional update flags. EXPECT_TRUE(cfg_mgr_->forwardUpdatesEnabled()); EXPECT_TRUE(cfg_mgr_->reverseUpdatesEnabled()); // Verify that parsing the exact same configuration a second time // does not cause a duplicate value errors. answer_ = cfg_mgr_->parseConfig(config_set_); ASSERT_TRUE(checkAnswer(0)); } /// @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. TEST_F(D2CfgMgrTest, forwardMatch) { // Create configuration with one domain, one sub domain, and the wild // card. std::string config = "{ " "\"ip_address\" : \"192.168.1.33\" , " "\"port\" : 88 , " "\"tsig_keys\": [] ," "\"forward_ddns\" : {" "\"ddns_domains\": [ " "{ \"name\": \"tmark.org\" , " " \"dns_servers\" : [ " " { \"ip_address\": \"127.0.0.1\" } " " ] } " ", " "{ \"name\": \"one.tmark.org\" , " " \"dns_servers\" : [ " " { \"ip_address\": \"127.0.0.2\" } " " ] } " ", " "{ \"name\": \"*\" , " " \"dns_servers\" : [ " " { \"hostname\": \"global.net\" } " " ] } " "] }, " "\"reverse_ddns\" : {} " "}"; ASSERT_TRUE(fromJSON(config)); // Verify that we can parse the configuration. answer_ = cfg_mgr_->parseConfig(config_set_); ASSERT_TRUE(checkAnswer(0)); // Verify that the D2 context can be retrieved and is not null. D2CfgContextPtr context; ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext()); // Test directional update flags. EXPECT_TRUE(cfg_mgr_->forwardUpdatesEnabled()); EXPECT_FALSE(cfg_mgr_->reverseUpdatesEnabled()); DdnsDomainPtr match; // Verify that an exact match works. EXPECT_TRUE(cfg_mgr_->matchForward("tmark.org", match)); EXPECT_EQ("tmark.org", match->getName()); // Verify that search is case insensisitive. EXPECT_TRUE(cfg_mgr_->matchForward("TMARK.ORG", match)); EXPECT_EQ("tmark.org", match->getName()); // Verify that an exact match works. EXPECT_TRUE(cfg_mgr_->matchForward("one.tmark.org", match)); EXPECT_EQ("one.tmark.org", match->getName()); // Verify that a FQDN for sub-domain matches. EXPECT_TRUE(cfg_mgr_->matchForward("blue.tmark.org", match)); EXPECT_EQ("tmark.org", match->getName()); // Verify that a FQDN for sub-domain matches. EXPECT_TRUE(cfg_mgr_->matchForward("red.one.tmark.org", match)); EXPECT_EQ("one.tmark.org", match->getName()); // Verify that an FQDN with no match, returns the wild card domain. EXPECT_TRUE(cfg_mgr_->matchForward("shouldbe.wildcard", match)); EXPECT_EQ("*", match->getName()); // Verify that an attempt to match an empty FQDN throws. ASSERT_THROW(cfg_mgr_->matchForward("", match), D2CfgError); } /// @brief Tests domain matching when there is no wild card domain. /// This test verifies that matches are found only for FQDNs that match /// some or all of a domain name. FQDNs without matches should not return /// a match. TEST_F(D2CfgMgrTest, matchNoWildcard) { // Create a configuration with one domain, one sub-domain, and NO wild card. std::string config = "{ " "\"ip_address\" : \"192.168.1.33\" , " "\"port\" : 88 , " "\"tsig_keys\": [] ," "\"forward_ddns\" : {" "\"ddns_domains\": [ " "{ \"name\": \"tmark.org\" , " " \"dns_servers\" : [ " " { \"ip_address\": \"127.0.0.1\" } " " ] } " ", " "{ \"name\": \"one.tmark.org\" , " " \"dns_servers\" : [ " " { \"ip_address\": \"127.0.0.2\" } " " ] } " "] }, " "\"reverse_ddns\" : {} " " }"; ASSERT_TRUE(fromJSON(config)); // Verify that we can parse the configuration. answer_ = cfg_mgr_->parseConfig(config_set_); ASSERT_TRUE(checkAnswer(0)); // Verify that the D2 context can be retrieved and is not null. D2CfgContextPtr context; ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext()); DdnsDomainPtr match; // Verify that full or partial matches, still match. EXPECT_TRUE(cfg_mgr_->matchForward("tmark.org", match)); EXPECT_EQ("tmark.org", match->getName()); EXPECT_TRUE(cfg_mgr_->matchForward("blue.tmark.org", match)); EXPECT_EQ("tmark.org", match->getName()); EXPECT_TRUE(cfg_mgr_->matchForward("red.one.tmark.org", match)); EXPECT_EQ("one.tmark.org", match->getName()); // Verify that a FQDN with no match, fails to match. EXPECT_FALSE(cfg_mgr_->matchForward("shouldbe.wildcard", match)); } /// @brief Tests domain matching when there is ONLY a wild card domain. /// This test verifies that any FQDN matches the wild card. TEST_F(D2CfgMgrTest, matchAll) { std::string config = "{ " "\"ip_address\" : \"192.168.1.33\" , " "\"port\" : 88 , " "\"tsig_keys\": [] ," "\"forward_ddns\" : {" "\"ddns_domains\": [ " "{ \"name\": \"*\" , " " \"dns_servers\" : [ " " { \"ip_address\": \"127.0.0.1\" } " " ] } " "] }, " "\"reverse_ddns\" : {} " "}"; ASSERT_TRUE(fromJSON(config)); // Verify that we can parse the configuration. answer_ = cfg_mgr_->parseConfig(config_set_); ASSERT_TRUE(checkAnswer(0)); // Verify that the D2 context can be retrieved and is not null. D2CfgContextPtr context; ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext()); // Verify that wild card domain is returned for any FQDN. DdnsDomainPtr match; EXPECT_TRUE(cfg_mgr_->matchForward("tmark.org", match)); EXPECT_EQ("*", match->getName()); EXPECT_TRUE(cfg_mgr_->matchForward("shouldbe.wildcard", match)); EXPECT_EQ("*", match->getName()); // Verify that an attempt to match an empty FQDN still throws. ASSERT_THROW(cfg_mgr_->matchReverse("", match), D2CfgError); } /// @brief Tests the basics of the D2CfgMgr reverse FQDN-domain matching /// This test uses a valid configuration to exercise the D2CfgMgr's /// reverse 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. TEST_F(D2CfgMgrTest, matchReverse) { std::string config = "{ " "\"ip_address\" : \"192.168.1.33\" , " "\"port\" : 88 , " "\"tsig_keys\": [] ," "\"forward_ddns\" : {}, " "\"reverse_ddns\" : {" "\"ddns_domains\": [ " "{ \"name\": \"5.100.168.192.in-addr.arpa.\" , " " \"dns_servers\" : [ " " { \"ip_address\": \"127.0.0.1\" } " " ] }, " "{ \"name\": \"100.200.192.in-addr.arpa.\" , " " \"dns_servers\" : [ " " { \"ip_address\": \"127.0.0.1\" } " " ] }, " "{ \"name\": \"170.192.in-addr.arpa.\" , " " \"dns_servers\" : [ " " { \"ip_address\": \"127.0.0.1\" } " " ] }, " // Note mixed case to test case insensitivity. "{ \"name\": \"2.0.3.0.8.b.d.0.1.0.0.2.IP6.ARPA.\" , " " \"dns_servers\" : [ " " { \"ip_address\": \"127.0.0.1\" } " " ] }," "{ \"name\": \"*\" , " " \"dns_servers\" : [ " " { \"ip_address\": \"127.0.0.1\" } " " ] } " "] } }"; ASSERT_TRUE(fromJSON(config)); // Verify that we can parse the configuration. answer_ = cfg_mgr_->parseConfig(config_set_); ASSERT_TRUE(checkAnswer(0)); // Verify that the D2 context can be retrieved and is not null. D2CfgContextPtr context; ASSERT_NO_THROW(context = cfg_mgr_->getD2CfgContext()); // Test directional update flags. EXPECT_FALSE(cfg_mgr_->forwardUpdatesEnabled()); EXPECT_TRUE(cfg_mgr_->reverseUpdatesEnabled()); DdnsDomainPtr match; // Verify an exact match. EXPECT_TRUE(cfg_mgr_->matchReverse("192.168.100.5", match)); EXPECT_EQ("5.100.168.192.in-addr.arpa.", match->getName()); // Verify a sub-domain match. EXPECT_TRUE(cfg_mgr_->matchReverse("192.200.100.27", match)); EXPECT_EQ("100.200.192.in-addr.arpa.", match->getName()); // Verify a sub-domain match. EXPECT_TRUE(cfg_mgr_->matchReverse("192.170.50.30", match)); EXPECT_EQ("170.192.in-addr.arpa.", match->getName()); // Verify a wild card match. EXPECT_TRUE(cfg_mgr_->matchReverse("1.1.1.1", match)); EXPECT_EQ("*", match->getName()); // Verify a IPv6 match. EXPECT_TRUE(cfg_mgr_->matchReverse("2001:db8:302:99::",match)); EXPECT_EQ("2.0.3.0.8.b.d.0.1.0.0.2.IP6.ARPA.", match->getName()); // Verify a IPv6 wild card match. EXPECT_TRUE(cfg_mgr_->matchReverse("2001:db8:99:302::",match)); EXPECT_EQ("*", match->getName()); // Verify that an attempt to match an invalid IP address throws. ASSERT_THROW(cfg_mgr_->matchReverse("", match), D2CfgError); } } // end of anonymous namespace