]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#35,!517] Modify D2ClientMgr functions to accept DdnsParams parameter
authorThomas Markwalder <tmark@isc.org>
Mon, 23 Sep 2019 17:42:21 +0000 (13:42 -0400)
committerThomas Markwalder <tmark@isc.org>
Thu, 10 Oct 2019 12:32:44 +0000 (08:32 -0400)
D2ClientMgr functions now get behavior parameters from a passed in
structure instead of D2ClientMgr::d2_client_config_.

src/lib/dhcpsrv/d2_client_cfg.h
    struct DdnsParams  - new structure to convey request specific
    (i.e.scopable) Ddns params

src/lib/dhcpsrv/alloc_engine.cc
src/lib/dhcpsrv/alloc_engine.h
    Added ddns_params_ to ClientContext4/6

src/lib/dhcpsrv/d2_client_mgr.*
    D2ClientMgr::adjustFqdnFlags()
    D2ClientMgr::adjustDomainName()
    D2ClientMgr::analyzeFqdn()
    D2ClientMgr::generateFqdn()
    D2ClientMgr::qualifyName() - now accept DdnsParams instance as
    parameter

src/lib/dhcpsrv/tests/d2_client_unittest.cc
    Reworked tests to use DdnsParams

src/lib/dhcpsrv/alloc_engine.cc
src/lib/dhcpsrv/alloc_engine.h
src/lib/dhcpsrv/d2_client_cfg.h
src/lib/dhcpsrv/d2_client_mgr.cc
src/lib/dhcpsrv/d2_client_mgr.h
src/lib/dhcpsrv/parsers/dhcp_parsers.cc
src/lib/dhcpsrv/tests/d2_client_unittest.cc

index 9032974972654e756a5cd9cbd22451722b5ae5e3..35849386528af7bd1af7c9e8dcd7d9f7c230040d 100644 (file)
@@ -430,7 +430,8 @@ namespace dhcp {
 AllocEngine::ClientContext6::ClientContext6()
     : query_(), fake_allocation_(false), subnet_(), host_subnet_(), duid_(),
       hwaddr_(), host_identifiers_(), hosts_(), fwd_dns_update_(false),
-      rev_dns_update_(false), hostname_(), callout_handle_(), ias_() {
+      rev_dns_update_(false), hostname_(), callout_handle_(), ias_(),
+      ddns_params_(new DdnsParams()) {
 }
 
 AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet,
@@ -445,7 +446,7 @@ AllocEngine::ClientContext6::ClientContext6(const Subnet6Ptr& subnet,
       duid_(duid), hwaddr_(), host_identifiers_(), hosts_(),
       fwd_dns_update_(fwd_dns), rev_dns_update_(rev_dns), hostname_(hostname),
       callout_handle_(callout_handle), allocated_resources_(), new_leases_(),
-      ias_() {
+      ias_(), ddns_params_(new DdnsParams()) {
 
     // Initialize host identifiers.
     if (duid) {
@@ -1156,7 +1157,8 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
                             // the hostname as it is specified for the reservation.
                             OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
                             ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
-                                qualifyName(host->getHostname(), static_cast<bool>(fqdn));
+                                qualifyName(host->getHostname(), *ctx.ddns_params_,
+                                            static_cast<bool>(fqdn));
                         }
                     }
                 }
@@ -1228,7 +1230,8 @@ AllocEngine::allocateReservedLeases6(ClientContext6& ctx,
                         // the hostname as it is specified for the reservation.
                         OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
                         ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
-                            qualifyName(host->getHostname(), static_cast<bool>(fqdn));
+                            qualifyName(host->getHostname(), *ctx.ddns_params_,
+                                        static_cast<bool>(fqdn));
                     }
                 }
 
@@ -1303,7 +1306,8 @@ AllocEngine::allocateGlobalReservedLeases6(ClientContext6& ctx,
                     // the hostname as it is specified for the reservation.
                     OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
                     ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
-                                    qualifyName(ghost->getHostname(), static_cast<bool>(fqdn));
+                                    qualifyName(ghost->getHostname(), *ctx.ddns_params_,
+                                                static_cast<bool>(fqdn));
             }
 
             // If this is a real allocation, we may need to extend the lease
@@ -1353,7 +1357,8 @@ AllocEngine::allocateGlobalReservedLeases6(ClientContext6& ctx,
                 // the hostname as it is specified for the reservation.
                 OptionPtr fqdn = ctx.query_->getOption(D6O_CLIENT_FQDN);
                 ctx.hostname_ = CfgMgr::instance().getD2ClientMgr().
-                                qualifyName(ghost->getHostname(), static_cast<bool>(fqdn));
+                                qualifyName(ghost->getHostname(), *ctx.ddns_params_,
+                                            static_cast<bool>(fqdn));
             }
 
             // Ok, let's create a new lease...
@@ -3002,7 +3007,8 @@ AllocEngine::ClientContext4::ClientContext4()
       fwd_dns_update_(false), rev_dns_update_(false),
       hostname_(""), callout_handle_(), fake_allocation_(false),
       old_lease_(), new_lease_(), hosts_(), conflicting_lease_(),
-      query_(), host_identifiers_() {
+      query_(), host_identifiers_(),
+      ddns_params_(new DdnsParams()) {
 }
 
 AllocEngine::ClientContext4::ClientContext4(const Subnet4Ptr& subnet,
@@ -3018,7 +3024,8 @@ AllocEngine::ClientContext4::ClientContext4(const Subnet4Ptr& subnet,
       fwd_dns_update_(fwd_dns_update), rev_dns_update_(rev_dns_update),
       hostname_(hostname), callout_handle_(),
       fake_allocation_(fake_allocation), old_lease_(), new_lease_(),
-      hosts_(), host_identifiers_() {
+      hosts_(), host_identifiers_(),
+      ddns_params_(new DdnsParams()) {
 
     // Initialize host identifiers.
     if (hwaddr) {
index fee5e23e274b6b5511b9ccb87c439ff24f155877..89e5765ab0b79e016aba9ebd9329659a9904c4a3 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <asiolink/io_address.h>
 #include <dhcp/classify.h>
+#include <dhcpsrv/d2_client_cfg.h>
 #include <dhcp/duid.h>
 #include <dhcp/hwaddr.h>
 #include <dhcp/pkt4.h>
@@ -552,6 +553,9 @@ public:
         /// @brief Container holding IA specific contexts.
         std::vector<IAContext> ias_;
 
+        /// @brief Holds scoped DDNS behavioral parameters
+        DdnsParamsPtr ddns_params_;
+
         /// @brief Convenience method adding allocated prefix or address.
         ///
         /// @param prefix Prefix or address.
@@ -1319,6 +1323,9 @@ public:
         /// received by the server.
         IdentifierList host_identifiers_;
 
+        /// @brief Holds scoped DDNS behavioral parameters
+        DdnsParamsPtr ddns_params_;
+
         /// @brief Convenience function adding host identifier into
         /// @ref host_identifiers_ list.
         ///
index 4fdec9c81c7d4b7b9335c603d76a02779ec9b8ef..d6f1972dd885286886d83d18160f72b0fcec2fce 100644 (file)
@@ -369,6 +369,41 @@ operator<<(std::ostream& os, const D2ClientConfig& config);
 /// @brief Defines a pointer for D2ClientConfig instances.
 typedef boost::shared_ptr<D2ClientConfig> D2ClientConfigPtr;
 
+/// @Brief Convenience container for conveying DDNS behaviorial parameters
+/// It is intended to be populated per Packet exchange and passed into
+/// functions that require them
+struct DdnsParams {
+    DdnsParams() :
+        enable_updates_(false), override_no_update_(false), override_client_update_(false),
+        replace_client_name_mode_(D2ClientConfig::RCM_NEVER),
+        generated_prefix_(""), qualifying_suffix_("") {};
+
+    /// @brief Indicates whether or not DHCP DDNS updating is enabled.
+    bool enable_updates_;
+
+    /// @brief Should Kea perform updates, even if client requested no updates.
+    /// Overrides the client request for no updates via the N flag.
+    bool override_no_update_;
+
+    /// @brief Should Kea perform updates, even if client requested delegation.
+    bool override_client_update_;
+
+    /// @brief How Kea should handle the domain-name supplied by the client.
+    D2ClientConfig::ReplaceClientNameMode replace_client_name_mode_;
+
+    /// @brief Prefix Kea should use when generating domain-names.
+    std::string generated_prefix_;
+
+    /// @brief Suffix Kea should use when to qualify partial domain-names.
+    std::string qualifying_suffix_;
+
+    /// @brief Pointer to compiled regular expression string sanitizer
+    util::str::StringSanitizerPtr hostname_sanitizer_;
+};
+
+/// @brief Defines a pointer for DdnsParams instances.
+typedef boost::shared_ptr<DdnsParams> DdnsParamsPtr;
+
 } // namespace isc
 } // namespace dhcp
 
index 162eaf5443f2f861f5d67fd133cdaba0c53273d7..9712403acfbfbe038f9358adb88af47c985b62c1 100644 (file)
@@ -114,7 +114,8 @@ D2ClientMgr::getD2ClientConfig() const {
 
 void
 D2ClientMgr::analyzeFqdn(const bool client_s, const bool client_n,
-                         bool& server_s, bool& server_n) const {
+                         bool& server_s, bool& server_n, 
+                         const DdnsParams& ddns_params) const {
     // Per RFC 4702 & 4704, the client N and S flags allow the client to
     // request one of three options:
     //
@@ -132,27 +133,27 @@ D2ClientMgr::analyzeFqdn(const bool client_s, const bool client_n,
 
     switch (mask) {
     case 0:
-        if (!d2_client_config_->getEnableUpdates()) {
+        if (!ddns_params.enable_updates_) {
             server_s = false;
             server_n = true;
         } else {
             // If updates are enabled and we are overriding client delegation
             // then S flag should be true.  N-flag should be false.
-            server_s = d2_client_config_->getOverrideClientUpdate();
+            server_s = ddns_params.override_client_update_;
             server_n = false;
         }
         break;
 
     case 1:
-        server_s = d2_client_config_->getEnableUpdates();
+        server_s = ddns_params.enable_updates_;
         server_n = !server_s;
         break;
 
     case 2:
         // If updates are enabled and we are overriding "no updates" then
         // S flag should be true.
-        server_s = (d2_client_config_->getEnableUpdates() &&
-                    d2_client_config_->getOverrideNoUpdate());
+        server_s = (ddns_params.enable_updates_ &&
+                    ddns_params.override_no_update_);
         server_n = !server_s;
         break;
 
@@ -166,31 +167,33 @@ D2ClientMgr::analyzeFqdn(const bool client_s, const bool client_n,
 
 std::string
 D2ClientMgr::generateFqdn(const asiolink::IOAddress& address,
+                          const DdnsParams& ddns_params,
                           const bool trailing_dot) const {
     std::string hostname = address.toText();
     std::replace(hostname.begin(), hostname.end(),
                  (address.isV4() ? '.' : ':'), '-');
 
     std::ostringstream gen_name;
-    gen_name << d2_client_config_->getGeneratedPrefix() << "-" << hostname;
-    return (qualifyName(gen_name.str(), trailing_dot));
+    gen_name << ddns_params.generated_prefix_ << "-" << hostname;
+    return (qualifyName(gen_name.str(), ddns_params, trailing_dot));
 }
 
 
 std::string
 D2ClientMgr::qualifyName(const std::string& partial_name,
+                         const DdnsParams& ddns_params,
                          const bool trailing_dot) const {
     std::ostringstream gen_name;
 
     gen_name << partial_name;
-    if (!d2_client_config_->getQualifyingSuffix().empty()) {
+    if (!ddns_params.qualifying_suffix_.empty()) {
         std::string str = gen_name.str();
         size_t len = str.length();
         if ((len > 0) && (str[len - 1] != '.')) {
             gen_name << ".";
         }
 
-        gen_name << d2_client_config_->getQualifyingSuffix();
+        gen_name << ddns_params.qualifying_suffix_;
     }
 
     std::string str = gen_name.str();
index ca2d1db339127f66c7d77d920bc9e375e890bd3b..cc655b4c121cda5592c399fcbbee1e1441fdf17c 100644 (file)
@@ -147,11 +147,12 @@ public:
     /// @param client_n  N Flag from the client's FQDN
     /// @param server_s [out] S Flag for the server's FQDN
     /// @param server_n [out] N Flag for the server's FQDN
+    /// @param ddns_params DDNS behaviorial configuration parameters
     ///
     /// @throw isc::BadValue if client_s and client_n are both 1 as this is
     /// an invalid combination per RFCs.
     void analyzeFqdn(const bool client_s, const bool client_n, bool& server_s,
-                     bool& server_n) const;
+                     bool& server_n, const DdnsParams& ddns_params) const;
 
     /// @brief Builds a FQDN based on the configuration and given IP address.
     ///
@@ -166,11 +167,13 @@ public:
     /// ('.' for IPv4 or ':' for IPv6) replaced with a hyphen, '-'.
     ///
     /// @param address IP address from which to derive the name (IPv4 or IPv6)
+    /// @param ddns_params DDNS behaviorial configuration parameters
     /// @param trailing_dot A boolean value which indicates whether trailing
     /// dot should be appended (if true) or not (false).
     ///
     /// @return std::string containing the generated name.
     std::string generateFqdn(const asiolink::IOAddress& address,
+                             const DdnsParams& ddns_params,
                              const bool trailing_dot = true) const;
 
     /// @brief Adds a qualifying suffix to a given domain name
@@ -181,6 +184,7 @@ public:
     ///     <partial_name>.<qualifying-suffix>.
     ///
     /// @param partial_name domain name to qualify
+    /// @param ddns_params DDNS behaviorial configuration parameters
     /// @param trailing_dot A boolean value which when true guarantees the
     /// result will end with a "." and when false that the result will not
     /// end with a "."   Note that this rule is applied even if the qualifying
@@ -188,6 +192,7 @@ public:
     ///
     /// @return std::string containing the qualified name.
     std::string qualifyName(const std::string& partial_name,
+                            const DdnsParams& ddns_params,
                             const bool trailing_dot) const;
 
     /// @brief Set server FQDN flags based on configuration and a given FQDN
@@ -199,10 +204,12 @@ public:
     ///
     /// @param fqdn FQDN option from which to read client (inbound) flags
     /// @param fqdn_resp FQDN option to update with the server (outbound) flags
+    /// @param ddns_params DDNS behaviorial configuration parameters
     /// @tparam T FQDN Option class containing the FQDN data such as
     /// dhcp::Option4ClientFqdn or dhcp::Option6ClientFqdn
     template <class T>
-    void adjustFqdnFlags(const T& fqdn, T& fqdn_resp);
+    void adjustFqdnFlags(const T& fqdn, T& fqdn_resp, 
+                         const DdnsParams& ddns_params);
 
     /// @brief Get directional update flags based on server FQDN flags
     ///
@@ -248,10 +255,12 @@ public:
     ///
     /// @param fqdn FQDN option from which to get client (inbound) name
     /// @param fqdn_resp FQDN option to update with the adjusted name
+    /// @param ddns_params DDNS behaviorial configuration parameters
     /// @tparam T  FQDN Option class containing the FQDN data such as
     /// dhcp::Option4ClientFqdn or dhcp::Option6ClientFqdn
     template <class T>
-    void adjustDomainName(const T& fqdn, T& fqdn_resp);
+    void adjustDomainName(const T& fqdn, T& fqdn_resp,
+                          const DdnsParams& ddns_params);
 
     /// @brief Enables sending NameChangeRequests to kea-dhcp-ddns
     ///
@@ -435,11 +444,11 @@ private:
 
 template <class T>
 void
-D2ClientMgr::adjustFqdnFlags(const T& fqdn, T& fqdn_resp) {
+D2ClientMgr::adjustFqdnFlags(const T& fqdn, T& fqdn_resp, const DdnsParams& ddns_params) {
     bool server_s = false;
     bool server_n = false;
     analyzeFqdn(fqdn.getFlag(T::FLAG_S), fqdn.getFlag(T::FLAG_N),
-                server_s, server_n);
+                server_s, server_n, ddns_params);
 
     // Reset the flags to zero to avoid triggering N and S both 1 check.
     fqdn_resp.resetFlags();
@@ -462,18 +471,18 @@ D2ClientMgr::getUpdateDirections(const T& fqdn_resp,
 
 template <class T>
 void
-D2ClientMgr::adjustDomainName(const T& fqdn, T& fqdn_resp) {
+D2ClientMgr::adjustDomainName(const T& fqdn, T& fqdn_resp, const DdnsParams& ddns_params) {
     // If we're configured to replace it or the supplied name is blank
     // set the response name to blank.
-    if ((d2_client_config_->getReplaceClientNameMode() == D2ClientConfig::RCM_ALWAYS ||
-         d2_client_config_->getReplaceClientNameMode() == D2ClientConfig::RCM_WHEN_PRESENT) ||
+    if ((ddns_params.replace_client_name_mode_ == D2ClientConfig::RCM_ALWAYS ||
+         ddns_params.replace_client_name_mode_ == D2ClientConfig::RCM_WHEN_PRESENT) ||
         fqdn.getDomainName().empty()) {
         fqdn_resp.setDomainName("", T::PARTIAL);
     } else {
         // Sanitize the name the client sent us, if we're configured to do so.
         std::string client_name = fqdn.getDomainName();
 
-        if (d2_client_config_->getHostnameSanitizer()) {
+        if (ddns_params.hostname_sanitizer_) {
             // We need the raw text form, so we can replace escaped chars
             dns::Name tmp(client_name);
             std::string raw_name = tmp.toRawText();
@@ -490,7 +499,7 @@ D2ClientMgr::adjustDomainName(const T& fqdn, T& fqdn_resp) {
                     ss << ".";
                 }
 
-                ss << d2_client_config_->getHostnameSanitizer()->scrub(*label);
+                ss << ddns_params.hostname_sanitizer_->scrub(*label);
             }
 
             client_name = ss.str();
@@ -498,7 +507,7 @@ D2ClientMgr::adjustDomainName(const T& fqdn, T& fqdn_resp) {
 
         // If the supplied name is partial, qualify it by adding the suffix.
         if (fqdn.getDomainNameType() == T::PARTIAL) {
-            fqdn_resp.setDomainName(qualifyName(client_name,true), T::FULL);
+            fqdn_resp.setDomainName(qualifyName(client_name, ddns_params, true), T::FULL);
         }
         else  {
             fqdn_resp.setDomainName(client_name, T::FULL);
@@ -506,6 +515,7 @@ D2ClientMgr::adjustDomainName(const T& fqdn, T& fqdn_resp) {
     }
 }
 
+
 /// @brief Defines a pointer for D2ClientMgr instances.
 typedef boost::shared_ptr<D2ClientMgr> D2ClientMgrPtr;
 
index a9c20aa85e378ef7e048b89e8568fa51478f542b..a484208c5c9c42efa817b26977515784b608f7aa 100644 (file)
@@ -1440,11 +1440,13 @@ D2ClientConfigParser::parse(isc::data::ConstElementPtr client_config) {
 
     // qualifying-suffix is the only parameter which has no default
     std::string qualifying_suffix = "";
+#if 0
     bool found_qualifying_suffix = false;
     if (client_config->contains("qualifying-suffix")) {
             qualifying_suffix = getString(client_config, "qualifying-suffix");
             found_qualifying_suffix = true;
     }
+#endif
 
     IOAddress sender_ip(0);
     if (sender_ip_str.empty()) {
@@ -1461,6 +1463,7 @@ D2ClientConfigParser::parse(isc::data::ConstElementPtr client_config) {
         }
     }
 
+#if 0
     // Qualifying-suffix is required when updates are enabled
     if (enable_updates && !found_qualifying_suffix) {
         isc_throw(DhcpConfigError,
@@ -1468,6 +1471,7 @@ D2ClientConfigParser::parse(isc::data::ConstElementPtr client_config) {
                   "updates are enabled ("
                   << client_config->getPosition() << ")");
     }
+#endif
 
     // Now we check for logical errors. This repeats what is done in
     // D2ClientConfig::validate(), but doing it here permits us to
index e1b59a442906964bf6bff817dcc4ac57f67b2981..b05db835a2e48b8f88e4d9a53951530e44df1277 100644 (file)
@@ -492,29 +492,10 @@ TEST(D2ClientMgr, analyzeFqdnInvalidCombination) {
     bool server_s = false;
     bool server_n = false;
 
-    // Create disabled configuration.
-    D2ClientConfigPtr cfg;
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig()));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
-    ASSERT_FALSE(mgr.ddnsEnabled());
+    DdnsParams ddns_params;
 
     // client S=1 N=1 is invalid.  analyzeFqdn should throw.
-    ASSERT_THROW(mgr.analyzeFqdn(true, true, server_s, server_n),
-                 isc::BadValue);
-
-    // Create enabled configuration with all controls off (no overrides).
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  false, false, D2ClientConfig::RCM_NEVER,
-                                  "pre-fix", "suf-fix", "", "")));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
-    ASSERT_TRUE(mgr.ddnsEnabled());
-
-    // client S=1 N=1 is invalid.  analyzeFqdn should throw.
-    ASSERT_THROW(mgr.analyzeFqdn(true, true, server_s, server_n),
+    ASSERT_THROW(mgr.analyzeFqdn(true, true, server_s, server_n, ddns_params),
                  isc::BadValue);
 }
 
@@ -526,30 +507,25 @@ TEST(D2ClientMgr, analyzeFqdnEnabledNoOverrides) {
     bool server_n = false;
 
     // Create enabled configuration with all controls off (no overrides).
-    D2ClientConfigPtr cfg;
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  false, false, D2ClientConfig::RCM_NEVER,
-                                  "pre-fix", "suf-fix", "", "")));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
-    ASSERT_TRUE(mgr.ddnsEnabled());
-    ASSERT_FALSE(cfg->getOverrideClientUpdate());
-    ASSERT_FALSE(cfg->getOverrideNoUpdate());
+    DdnsParams ddns_params;
+    ddns_params.enable_updates_ = true;
+    ddns_params.override_no_update_ = false;
+    ddns_params.override_client_update_ = false;
+    ddns_params.replace_client_name_mode_ = D2ClientConfig::RCM_NEVER;
+    ddns_params.generated_prefix_ = "";
+    ddns_params.qualifying_suffix_ = "";
 
     // client S=0 N=0 means client wants to do forward update.
     // server S should be 0 (server is not doing forward updates)
     // and server N should be 0 (server doing reverse updates)
-    mgr.analyzeFqdn(false, false, server_s, server_n);
+    mgr.analyzeFqdn(false, false, server_s, server_n, ddns_params);
     EXPECT_FALSE(server_s);
     EXPECT_FALSE(server_n);
 
     // client S=1 N=0 means client wants server to do forward update.
     // server S should be 1 (server is doing forward updates)
     // and server N should be 0 (server doing updates)
-    mgr.analyzeFqdn(true, false, server_s, server_n);
+    mgr.analyzeFqdn(true, false, server_s, server_n, ddns_params);
     EXPECT_TRUE(server_s);
     EXPECT_FALSE(server_n);
 
@@ -557,7 +533,7 @@ TEST(D2ClientMgr, analyzeFqdnEnabledNoOverrides) {
     // client S=0 N=1 means client wants no one to do forward updates.
     // server S should be 0 (server is  not forward updates)
     // and server N should be 1 (server is not doing any updates)
-    mgr.analyzeFqdn(false, true, server_s, server_n);
+    mgr.analyzeFqdn(false, true, server_s, server_n, ddns_params);
     EXPECT_FALSE(server_s);
     EXPECT_TRUE(server_n);
 }
@@ -569,38 +545,33 @@ TEST(D2ClientMgr, analyzeFqdnEnabledOverrideNoUpdate) {
     bool server_s = false;
     bool server_n = false;
 
-    // Create enabled configuration with OVERRIDE_NO_UPDATE on.
-    D2ClientConfigPtr cfg;
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  true, false, D2ClientConfig::RCM_NEVER,
-                                  "pre-fix", "suf-fix", "", "")));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
-    ASSERT_TRUE(mgr.ddnsEnabled());
-    ASSERT_TRUE(cfg->getOverrideNoUpdate());
-    ASSERT_FALSE(cfg->getOverrideClientUpdate());
+    // Create enabled configuration with override-no-update true.
+    DdnsParams ddns_params;
+    ddns_params.enable_updates_ = true;
+    ddns_params.override_no_update_ = true;
+    ddns_params.override_client_update_ = false;
+    ddns_params.replace_client_name_mode_ = D2ClientConfig::RCM_NEVER;
+    ddns_params.generated_prefix_ = "";
+    ddns_params.qualifying_suffix_ = "";
 
     // client S=0 N=0 means client wants to do forward update.
     // server S should be 0 (server is not doing forward updates)
     // and server N should be 0 (server is doing reverse updates)
-    mgr.analyzeFqdn(false, false, server_s, server_n);
+    mgr.analyzeFqdn(false, false, server_s, server_n, ddns_params);
     EXPECT_FALSE(server_s);
     EXPECT_FALSE(server_n);
 
     // client S=1 N=0 means client wants server to do forward update.
     // server S should be 1 (server is doing forward updates)
     // and server N should be 0 (server doing updates)
-    mgr.analyzeFqdn(true, false, server_s, server_n);
+    mgr.analyzeFqdn(true, false, server_s, server_n, ddns_params);
     EXPECT_TRUE(server_s);
     EXPECT_FALSE(server_n);
 
     // client S=0 N=1 means client wants no one to do forward updates.
     // server S should be 1 (server is doing forward updates)
     // and server N should be 0 (server is doing updates)
-    mgr.analyzeFqdn(false, true, server_s, server_n);
+    mgr.analyzeFqdn(false, true, server_s, server_n, ddns_params);
     EXPECT_TRUE(server_s);
     EXPECT_FALSE(server_n);
 }
@@ -612,38 +583,34 @@ TEST(D2ClientMgr, analyzeFqdnEnabledOverrideClientUpdate) {
     bool server_s = false;
     bool server_n = false;
 
-    // Create enabled configuration with OVERRIDE_CLIENT_UPDATE on.
-    D2ClientConfigPtr cfg;
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  false, true, D2ClientConfig::RCM_NEVER,
-                                  "pre-fix", "suf-fix", "", "")));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
-    ASSERT_TRUE(mgr.ddnsEnabled());
-    ASSERT_FALSE(cfg->getOverrideNoUpdate());
-    ASSERT_TRUE(cfg->getOverrideClientUpdate());
+    // Create enabled configuration with override-client-update true.
+    DdnsParams ddns_params;
+    ddns_params.enable_updates_ = true;
+    ddns_params.override_no_update_ = false;
+    ddns_params.override_client_update_ = true;
+    ddns_params.replace_client_name_mode_ = D2ClientConfig::RCM_NEVER;
+    ddns_params.generated_prefix_ = "";
+    ddns_params.qualifying_suffix_ = "";
+
 
     // client S=0 N=0 means client wants to do forward update.
     // server S should be 1 (server is doing forward updates)
     // and server N should be 0 (server doing updates)
-    mgr.analyzeFqdn(false, false, server_s, server_n);
+    mgr.analyzeFqdn(false, false, server_s, server_n, ddns_params);
     EXPECT_TRUE(server_s);
     EXPECT_FALSE(server_n);
 
     // client S=1 N=0 means client wants server to do forward update.
     // server S should be 1 (server is doing forward updates)
     // and server N should be 0 (server doing updates)
-    mgr.analyzeFqdn(true, false, server_s, server_n);
+    mgr.analyzeFqdn(true, false, server_s, server_n, ddns_params);
     EXPECT_TRUE(server_s);
     EXPECT_FALSE(server_n);
 
     // client S=0 N=1 means client wants no one to do forward updates.
     // server S should be 0 (server is  not forward updates)
     // and server N should be 1 (server is not doing any updates)
-    mgr.analyzeFqdn(false, true, server_s, server_n);
+    mgr.analyzeFqdn(false, true, server_s, server_n, ddns_params);
     EXPECT_FALSE(server_s);
     EXPECT_TRUE(server_n);
 }
@@ -656,19 +623,14 @@ TEST(D2ClientMgr, adjustFqdnFlagsV4) {
     Option4ClientFqdnPtr request;
     Option4ClientFqdnPtr response;
 
-    // Create enabled configuration and override-no-update on.
-    D2ClientConfigPtr cfg;
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  true, false, D2ClientConfig::RCM_NEVER,
-                                  "pre-fix", "suf-fix", "", "")));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
-    ASSERT_TRUE(mgr.ddnsEnabled());
-    ASSERT_TRUE(cfg->getOverrideNoUpdate());
-    ASSERT_FALSE(cfg->getOverrideClientUpdate());
+    // Create enabled configuration with override-no-update true.
+    DdnsParams ddns_params;
+    ddns_params.enable_updates_ = true;
+    ddns_params.override_no_update_ = true;
+    ddns_params.override_client_update_ = false;
+    ddns_params.replace_client_name_mode_ = D2ClientConfig::RCM_NEVER;
+    ddns_params.generated_prefix_ = "";
+    ddns_params.qualifying_suffix_ = "";
 
     // client S=0 N=0 means client wants to do forward update.
     // server S should be 0 (server is not doing forward updates)
@@ -679,7 +641,7 @@ TEST(D2ClientMgr, adjustFqdnFlagsV4) {
     response.reset(new Option4ClientFqdn(*request));
     response->resetFlags();
 
-    mgr.adjustFqdnFlags<Option4ClientFqdn>(*request, *response);
+    mgr.adjustFqdnFlags<Option4ClientFqdn>(*request, *response, ddns_params);
     EXPECT_FALSE(response->getFlag(Option4ClientFqdn::FLAG_S));
     EXPECT_FALSE(response->getFlag(Option4ClientFqdn::FLAG_N));
     EXPECT_FALSE(response->getFlag(Option4ClientFqdn::FLAG_O));
@@ -694,7 +656,7 @@ TEST(D2ClientMgr, adjustFqdnFlagsV4) {
     response.reset(new Option4ClientFqdn(*request));
     response->resetFlags();
 
-    mgr.adjustFqdnFlags<Option4ClientFqdn>(*request, *response);
+    mgr.adjustFqdnFlags<Option4ClientFqdn>(*request, *response, ddns_params);
     EXPECT_TRUE(response->getFlag(Option4ClientFqdn::FLAG_S));
     EXPECT_FALSE(response->getFlag(Option4ClientFqdn::FLAG_N));
     EXPECT_FALSE(response->getFlag(Option4ClientFqdn::FLAG_O));
@@ -709,7 +671,7 @@ TEST(D2ClientMgr, adjustFqdnFlagsV4) {
     response.reset(new Option4ClientFqdn(*request));
     response->resetFlags();
 
-    mgr.adjustFqdnFlags<Option4ClientFqdn>(*request, *response);
+    mgr.adjustFqdnFlags<Option4ClientFqdn>(*request, *response, ddns_params);
     EXPECT_TRUE(response->getFlag(Option4ClientFqdn::FLAG_S));
     EXPECT_FALSE(response->getFlag(Option4ClientFqdn::FLAG_N));
     EXPECT_TRUE(response->getFlag(Option4ClientFqdn::FLAG_O));
@@ -754,100 +716,64 @@ TEST(D2ClientMgr, updateDirectionsV4) {
 /// @brief Tests the qualifyName method's ability to construct FQDNs
 TEST(D2ClientMgr, qualifyName) {
     D2ClientMgr mgr;
-
-    // Create enabled configuration.
-    D2ClientConfigPtr cfg;
-
-    //append suffix and dot
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  false, true, D2ClientConfig::RCM_NEVER,
-                                  "prefix", "suffix.com", "", "")));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
-
-    // Verify that the qualifying suffix gets appended with trailing dot added.
+    bool do_not_dot = false;
+    bool do_dot = true;
+
+    // Create enabled configuration
+    DdnsParams ddns_params;
+    ddns_params.enable_updates_ = true;
+    ddns_params.override_no_update_ = false;
+    ddns_params.override_client_update_ = false;
+    ddns_params.replace_client_name_mode_ = D2ClientConfig::RCM_NEVER;
+    ddns_params.generated_prefix_ = "prefix";
+    ddns_params.qualifying_suffix_ = "suffix.com";
+
+    // Verify that the qualifying suffix gets appended with a trailing dot added.
     std::string partial_name = "somehost";
-    std::string qualified_name = mgr.qualifyName(partial_name, true);
+    std::string qualified_name = mgr.qualifyName(partial_name, ddns_params, do_dot);
     EXPECT_EQ("somehost.suffix.com.", qualified_name);
 
-
-        //append suffix but dot
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  false, true, D2ClientConfig::RCM_NEVER,
-                                  "prefix", "suffix.com", "", "")));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+    // Verify that the qualifying suffix gets appended without a trailing dot.
     partial_name = "somehost";
-    qualified_name = mgr.qualifyName(partial_name, false); //false means no dot
+    qualified_name = mgr.qualifyName(partial_name, ddns_params, do_not_dot);
     EXPECT_EQ("somehost.suffix.com", qualified_name);
 
-
-        //append no suffix and not dot
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  false, true, D2ClientConfig::RCM_NEVER,
-                                  "prefix", "", "", ""))); //empty suffix
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+    // Verify that an empty suffix and false flag, does not change the name
+    ddns_params.qualifying_suffix_ = "";
     partial_name = "somehost";
-    qualified_name = mgr.qualifyName(partial_name, false); //false means no dot
+    qualified_name = mgr.qualifyName(partial_name, ddns_params, do_not_dot);
     EXPECT_EQ("somehost", qualified_name);
 
-    // Verify that the qualifying suffix gets appended with trailing dot added.
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  false, true, D2ClientConfig::RCM_NEVER,
-                                  "prefix", "hasdot.com.", "", "")));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
-
-    // Verify that the qualifying suffix gets appended without dot added.
-    qualified_name = mgr.qualifyName(partial_name, true);
+    // Verify that a qualifying suffix that already has a trailing
+    // dot gets appended without doubling the dot.
+    ddns_params.qualifying_suffix_ = "hasdot.com.";
+    qualified_name = mgr.qualifyName(partial_name, ddns_params, do_dot);
     EXPECT_EQ("somehost.hasdot.com.", qualified_name);
 
     // Verify that the qualifying suffix gets appended without an
     // extraneous dot when partial_name ends with a "."
-    qualified_name = mgr.qualifyName("somehost.", true);
+    qualified_name = mgr.qualifyName("somehost.", ddns_params, do_dot);
     EXPECT_EQ("somehost.hasdot.com.", qualified_name);
 
-    // Reconfigure to a "" suffix
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  false, true, D2ClientConfig::RCM_NEVER,
-                                  "prefix", "", "", "")));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
-
     // Verify that a name with a trailing dot does not get an extraneous
     // dot when the suffix is blank
-    qualified_name = mgr.qualifyName("somehost.", true);
+    ddns_params.qualifying_suffix_ = "";
+    qualified_name = mgr.qualifyName("somehost.", ddns_params, do_dot);
     EXPECT_EQ("somehost.", qualified_name);
 
     // Verify that a name with no trailing dot gets just a dot when the
     // suffix is blank
-    qualified_name = mgr.qualifyName("somehost", true);
+    qualified_name = mgr.qualifyName("somehost", ddns_params, do_dot);
     EXPECT_EQ("somehost.", qualified_name);
 
     // Verify that a name with no trailing dot does not get dotted when the
     // suffix is blank and trailing dot is false
-    qualified_name = mgr.qualifyName("somehost", false);
+    qualified_name = mgr.qualifyName("somehost", ddns_params, do_not_dot);
     EXPECT_EQ("somehost", qualified_name);
 
     // Verify that a name with trailing dot gets "undotted" when the
     // suffix is blank and trailing dot is false
-    qualified_name = mgr.qualifyName("somehost.", false);
+    qualified_name = mgr.qualifyName("somehost.", ddns_params, do_not_dot);
     EXPECT_EQ("somehost", qualified_name);
 
 }
@@ -856,214 +782,260 @@ TEST(D2ClientMgr, qualifyName) {
 /// @brief Tests the generateFdqn method's ability to construct FQDNs
 TEST(D2ClientMgr, generateFqdn) {
     D2ClientMgr mgr;
+    bool do_dot = true;
 
-    // Create enabled configuration.
-    D2ClientConfigPtr cfg;
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  false, true, D2ClientConfig::RCM_NEVER,
-                                  "prefix", "suffix.com", "", "")));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+    // Create enabled configuration
+    DdnsParams ddns_params;
+    ddns_params.enable_updates_ = true;
+    ddns_params.override_no_update_ = false;
+    ddns_params.override_client_update_ = false;
+    ddns_params.replace_client_name_mode_ = D2ClientConfig::RCM_NEVER;
+    ddns_params.generated_prefix_ = "prefix";
+    ddns_params.qualifying_suffix_ = "suffix.com";
 
     // Verify that it works with an IPv4 address.
     asiolink::IOAddress v4address("192.0.2.75");
-    EXPECT_EQ("prefix-192-0-2-75.suffix.com.", mgr.generateFqdn(v4address,true));
+    EXPECT_EQ("prefix-192-0-2-75.suffix.com.",
+              mgr.generateFqdn(v4address, ddns_params, do_dot));
 
     // Verify that it works with an IPv6 address.
     asiolink::IOAddress v6address("2001:db8::2");
-    EXPECT_EQ("prefix-2001-db8--2.suffix.com.", mgr.generateFqdn(v6address,true));
+    EXPECT_EQ("prefix-2001-db8--2.suffix.com.",
+              mgr.generateFqdn(v6address, ddns_params, do_dot));
 
     // Create a disabled config.
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig()));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
+    ddns_params.enable_updates_ = false;
 
     // Verify names generate properly with a disabled configuration.
-    EXPECT_EQ("myhost-192-0-2-75.", mgr.generateFqdn(v4address,true));
-    EXPECT_EQ("myhost-2001-db8--2.", mgr.generateFqdn(v6address,true));
+    EXPECT_EQ("prefix-192-0-2-75.suffix.com.",
+               mgr.generateFqdn(v4address, ddns_params, do_dot));
+    EXPECT_EQ("prefix-2001-db8--2.suffix.com.",
+               mgr.generateFqdn(v6address, ddns_params, do_dot));
 }
 
 /// @brief Tests adjustDomainName template method with Option4ClientFqdn
 TEST(D2ClientMgr, adjustDomainNameV4) {
     D2ClientMgr mgr;
-    Option4ClientFqdnPtr request;
-    Option4ClientFqdnPtr response;
 
-    // Create enabled configuration.
-    D2ClientConfigPtr cfg;
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  false, false, D2ClientConfig::RCM_NEVER,
-                                  "prefix", "suffix.com", "", "")));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
-    ASSERT_EQ(D2ClientConfig::RCM_NEVER, cfg->getReplaceClientNameMode());
+    // Create enabled configuration
+    DdnsParams ddns_params;
+    ddns_params.enable_updates_ = true;
+    ddns_params.override_no_update_ = false;
+    ddns_params.override_client_update_ = false;
+    ddns_params.replace_client_name_mode_ = D2ClientConfig::RCM_NEVER;
+    ddns_params.generated_prefix_ = "prefix";
+    ddns_params.qualifying_suffix_ = "suffix.com";
 
-    // replace-client-name is false, client passes in empty fqdn
-    // response domain should be empty/partial.
-    request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
-                                        "", Option4ClientFqdn::PARTIAL));
-    response.reset(new Option4ClientFqdn(*request));
-
-    mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
-    EXPECT_EQ("", response->getDomainName());
-    EXPECT_EQ(Option4ClientFqdn::PARTIAL, response->getDomainNameType());
-
-
-    // replace-client-name is false, client passes in a partial fqdn
-    // response should contain client's name plus the qualifying suffix.
-    request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
-                                        "myhost", Option4ClientFqdn::PARTIAL));
-    response.reset(new Option4ClientFqdn(*request));
-
-    mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
-    EXPECT_EQ("myhost.suffix.com.", response->getDomainName());
-    EXPECT_EQ(Option4ClientFqdn::FULL, response->getDomainNameType());
-
-
-    // replace-client-name is false, client passes in a full fqdn
-    // response domain should not be altered.
-    request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
-                                        "myhost.example.com.",
-                                         Option4ClientFqdn::FULL));
-    response.reset(new Option4ClientFqdn(*request));
-    mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
-    EXPECT_EQ("myhost.example.com.", response->getDomainName());
-    EXPECT_EQ(Option4ClientFqdn::FULL, response->getDomainNameType());
-
-    // Create enabled configuration.
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  false, false, D2ClientConfig::RCM_WHEN_PRESENT,
-                                  "prefix", "suffix.com", "", "")));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
-    ASSERT_EQ(D2ClientConfig::RCM_WHEN_PRESENT, cfg->getReplaceClientNameMode());
-
-    // replace-client-name is true, client passes in empty fqdn
-    // response domain should be empty/partial.
-    request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
-                                        "", Option4ClientFqdn::PARTIAL));
-    response.reset(new Option4ClientFqdn(*request));
-
-    mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
-    EXPECT_EQ("", response->getDomainName());
-    EXPECT_EQ(Option4ClientFqdn::PARTIAL, response->getDomainNameType());
-
-    // replace-client-name is true, client passes in a partial fqdn
-    // response domain should be empty/partial.
-    request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
-                                        "myhost", Option4ClientFqdn::PARTIAL));
-    response.reset(new Option4ClientFqdn(*request));
-
-    mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
-    EXPECT_EQ("", response->getDomainName());
-    EXPECT_EQ(Option4ClientFqdn::PARTIAL, response->getDomainNameType());
+    struct Scenario {
+        std::string description_;
+        D2ClientConfig::ReplaceClientNameMode mode_;
+        std::string client_name_;
+        Option4ClientFqdn::DomainNameType client_name_type_;
+        std::string expected_name_;
+        Option4ClientFqdn::DomainNameType expected_name_type_;
+    };
 
+    std::vector<Scenario> scenarios = {
+        {
+            "RCM_NEVER #1, empty client name",
+            D2ClientConfig::RCM_NEVER,
+            "", Option4ClientFqdn::PARTIAL,
+            "", Option4ClientFqdn::PARTIAL
+        },
+        {
+            "RCM_NEVER #2, partial client name",
+            D2ClientConfig::RCM_NEVER,
+            "myhost", Option4ClientFqdn::PARTIAL,
+            "myhost.suffix.com.", Option4ClientFqdn::FULL
+        },
+        {
+            "RCM_NEVER #3, full client name",
+            D2ClientConfig::RCM_NEVER,
+            "myhost.example.com.", Option4ClientFqdn::FULL,
+            "myhost.example.com.", Option4ClientFqdn::FULL
+        },
+        {
+            "RCM_ALWAYS #1, empty client name",
+            D2ClientConfig::RCM_ALWAYS,
+            "", Option4ClientFqdn::PARTIAL,
+            "", Option4ClientFqdn::PARTIAL
+        },
+        {
+            "RCM_ALWAYS #2, partial client name",
+            D2ClientConfig::RCM_ALWAYS,
+            "myhost", Option4ClientFqdn::PARTIAL,
+            "", Option4ClientFqdn::PARTIAL
+        },
+        {
+            "RCM_ALWAYS #3, full client name",
+            D2ClientConfig::RCM_ALWAYS,
+            "myhost.example.com.", Option4ClientFqdn::FULL,
+            "", Option4ClientFqdn::PARTIAL
+        },
+        {
+            "RCM_WHEN_PRESENT #1, empty client name",
+            D2ClientConfig::RCM_WHEN_PRESENT,
+            "", Option4ClientFqdn::PARTIAL,
+            "", Option4ClientFqdn::PARTIAL
+        },
+        {
+            "RCM_WHEN_PRESENT #2, partial client name",
+            D2ClientConfig::RCM_WHEN_PRESENT,
+            "myhost", Option4ClientFqdn::PARTIAL,
+            "", Option4ClientFqdn::PARTIAL
+        },
+        {
+            "RCM_WHEN_PRESENT #3, full client name",
+            D2ClientConfig::RCM_WHEN_PRESENT,
+            "myhost.example.com.", Option4ClientFqdn::FULL,
+            "", Option4ClientFqdn::PARTIAL
+        },
+        {
+            "RCM_WHEN_NOT_PRESENT #1, empty client name",
+            D2ClientConfig::RCM_WHEN_NOT_PRESENT,
+            "", Option4ClientFqdn::PARTIAL,
+            "", Option4ClientFqdn::PARTIAL
+        },
+        {
+            "RCM_WHEN_NOT_PRESENT #2, partial client name",
+            D2ClientConfig::RCM_WHEN_NOT_PRESENT,
+            "myhost", Option4ClientFqdn::PARTIAL,
+            "myhost.suffix.com.", Option4ClientFqdn::FULL
+        },
+        {
+            "RCM_WHEN_NOT_PRESENT #3, full client name",
+            D2ClientConfig::RCM_WHEN_NOT_PRESENT,
+            "myhost.example.com.", Option4ClientFqdn::FULL,
+            "myhost.example.com.", Option4ClientFqdn::FULL,
+        }
+    };
 
-    // replace-client-name is true, client passes in a full fqdn
-    // response domain should be empty/partial.
-    request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
-                                        "myhost.example.com.",
-                                         Option4ClientFqdn::FULL));
-    response.reset(new Option4ClientFqdn(*request));
-    mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
-    EXPECT_EQ("", response->getDomainName());
-    EXPECT_EQ(Option4ClientFqdn::PARTIAL, response->getDomainNameType());
+    for (auto scenario : scenarios) {
+        SCOPED_TRACE(scenario.description_);
+        {
+            ddns_params.replace_client_name_mode_ = scenario.mode_;
+            Option4ClientFqdn request (0, Option4ClientFqdn::RCODE_CLIENT(),
+                                       scenario.client_name_,
+                                       scenario.client_name_type_);
+
+            Option4ClientFqdn response(request);
+            mgr.adjustDomainName<Option4ClientFqdn>(request, response, ddns_params);
+            EXPECT_EQ(scenario.expected_name_, response.getDomainName());
+            EXPECT_EQ(scenario.expected_name_type_, response.getDomainNameType());
+        }
+    }
 }
 
 /// @brief Tests adjustDomainName template method with Option6ClientFqdn
 TEST(D2ClientMgr, adjustDomainNameV6) {
     D2ClientMgr mgr;
-    Option6ClientFqdnPtr request;
-    Option6ClientFqdnPtr response;
 
-    // Create enabled configuration.
-    D2ClientConfigPtr cfg;
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  false, false, D2ClientConfig::RCM_NEVER,
-                                  "prefix", "suffix.com", "", "")));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
-    ASSERT_EQ(D2ClientConfig::RCM_NEVER, cfg->getReplaceClientNameMode());
-
-    // replace-client-name is false, client passes in empty fqdn
-    // response domain should be empty/partial.
-    request.reset(new Option6ClientFqdn(0, "", Option6ClientFqdn::PARTIAL));
-    response.reset(new Option6ClientFqdn(*request));
-
-    mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
-    EXPECT_EQ("", response->getDomainName());
-    EXPECT_EQ(Option6ClientFqdn::PARTIAL, response->getDomainNameType());
-
-    // replace-client-name is false, client passes in a partial fqdn
-    // response should contain client's name plus the qualifying suffix.
-    request.reset(new Option6ClientFqdn(0, "myhost",
-                                        Option6ClientFqdn::PARTIAL));
-    response.reset(new Option6ClientFqdn(*request));
-
-    mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
-    EXPECT_EQ("myhost.suffix.com.", response->getDomainName());
-    EXPECT_EQ(Option6ClientFqdn::FULL, response->getDomainNameType());
-
-
-    // replace-client-name is false, client passes in a full fqdn
-    // response domain should not be altered.
-    request.reset(new Option6ClientFqdn(0, "myhost.example.com.",
-                                        Option6ClientFqdn::FULL));
-    response.reset(new Option6ClientFqdn(*request));
-    mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
-    EXPECT_EQ("myhost.example.com.", response->getDomainName());
-    EXPECT_EQ(Option6ClientFqdn::FULL, response->getDomainNameType());
+    // Create enabled configuration
+    DdnsParams ddns_params;
+    ddns_params.enable_updates_ = true;
+    ddns_params.override_no_update_ = false;
+    ddns_params.override_client_update_ = false;
+    ddns_params.replace_client_name_mode_ = D2ClientConfig::RCM_NEVER;
+    ddns_params.generated_prefix_ = "prefix";
+    ddns_params.qualifying_suffix_ = "suffix.com";
 
-    // Create enabled configuration.
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  false, false, D2ClientConfig::RCM_WHEN_PRESENT,
-                                  "prefix", "suffix.com", "", "")));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
-    ASSERT_EQ(D2ClientConfig::RCM_WHEN_PRESENT, cfg->getReplaceClientNameMode());
-
-    // replace-client-name is true, client passes in empty fqdn
-    // response domain should be empty/partial.
-    request.reset(new Option6ClientFqdn(0, "", Option6ClientFqdn::PARTIAL));
-    response.reset(new Option6ClientFqdn(*request));
-
-    mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
-    EXPECT_EQ("", response->getDomainName());
-    EXPECT_EQ(Option6ClientFqdn::PARTIAL, response->getDomainNameType());
-
-    // replace-client-name is true, client passes in a partial fqdn
-    // response domain should be empty/partial.
-    request.reset(new Option6ClientFqdn(0, "myhost",
-                                        Option6ClientFqdn::PARTIAL));
-    response.reset(new Option6ClientFqdn(*request));
-
-    mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
-    EXPECT_EQ("", response->getDomainName());
-    EXPECT_EQ(Option6ClientFqdn::PARTIAL, response->getDomainNameType());
+    struct Scenario {
+        std::string description_;
+        D2ClientConfig::ReplaceClientNameMode mode_;
+        std::string client_name_;
+        Option6ClientFqdn::DomainNameType client_name_type_;
+        std::string expected_name_;
+        Option6ClientFqdn::DomainNameType expected_name_type_;
+    };
 
+    std::vector<Scenario> scenarios = {
+        {
+            "RCM_NEVER #1, empty client name",
+            D2ClientConfig::RCM_NEVER,
+            "", Option6ClientFqdn::PARTIAL,
+            "", Option6ClientFqdn::PARTIAL
+        },
+        {
+            "RCM_NEVER #2, partial client name",
+            D2ClientConfig::RCM_NEVER,
+            "myhost", Option6ClientFqdn::PARTIAL,
+            "myhost.suffix.com.", Option6ClientFqdn::FULL
+        },
+        {
+            "RCM_NEVER #3, full client name",
+            D2ClientConfig::RCM_NEVER,
+            "myhost.example.com.", Option6ClientFqdn::FULL,
+            "myhost.example.com.", Option6ClientFqdn::FULL
+        },
+        {
+            "RCM_ALWAYS #1, empty client name",
+            D2ClientConfig::RCM_ALWAYS,
+            "", Option6ClientFqdn::PARTIAL,
+            "", Option6ClientFqdn::PARTIAL
+        },
+        {
+            "RCM_ALWAYS #2, partial client name",
+            D2ClientConfig::RCM_ALWAYS,
+            "myhost", Option6ClientFqdn::PARTIAL,
+            "", Option6ClientFqdn::PARTIAL
+        },
+        {
+            "RCM_ALWAYS #3, full client name",
+            D2ClientConfig::RCM_ALWAYS,
+            "myhost.example.com.", Option6ClientFqdn::FULL,
+            "", Option6ClientFqdn::PARTIAL
+        },
+        {
+            "RCM_WHEN_PRESENT #1, empty client name",
+            D2ClientConfig::RCM_WHEN_PRESENT,
+            "", Option6ClientFqdn::PARTIAL,
+            "", Option6ClientFqdn::PARTIAL
+        },
+        {
+            "RCM_WHEN_PRESENT #2, partial client name",
+            D2ClientConfig::RCM_WHEN_PRESENT,
+            "myhost", Option6ClientFqdn::PARTIAL,
+            "", Option6ClientFqdn::PARTIAL
+        },
+        {
+            "RCM_WHEN_PRESENT #3, full client name",
+            D2ClientConfig::RCM_WHEN_PRESENT,
+            "myhost.example.com.", Option6ClientFqdn::FULL,
+            "", Option6ClientFqdn::PARTIAL
+        },
+        {
+            "RCM_WHEN_NOT_PRESENT #1, empty client name",
+            D2ClientConfig::RCM_WHEN_NOT_PRESENT,
+            "", Option6ClientFqdn::PARTIAL,
+            "", Option6ClientFqdn::PARTIAL
+        },
+        {
+            "RCM_WHEN_NOT_PRESENT #2, partial client name",
+            D2ClientConfig::RCM_WHEN_NOT_PRESENT,
+            "myhost", Option6ClientFqdn::PARTIAL,
+            "myhost.suffix.com.", Option6ClientFqdn::FULL
+        },
+        {
+            "RCM_WHEN_NOT_PRESENT #3, full client name",
+            D2ClientConfig::RCM_WHEN_NOT_PRESENT,
+            "myhost.example.com.", Option6ClientFqdn::FULL,
+            "myhost.example.com.", Option6ClientFqdn::FULL,
+        }
+    };
 
-    // replace-client-name is true, client passes in a full fqdn
-    // response domain should be empty/partial.
-    request.reset(new Option6ClientFqdn(0, "myhost.example.com.",
-                                        Option6ClientFqdn::FULL));
-    response.reset(new Option6ClientFqdn(*request));
-    mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
-    EXPECT_EQ("", response->getDomainName());
-    EXPECT_EQ(Option6ClientFqdn::PARTIAL, response->getDomainNameType());
+    for (auto scenario : scenarios) {
+        SCOPED_TRACE(scenario.description_);
+        {
+            ddns_params.replace_client_name_mode_ = scenario.mode_;
+            Option6ClientFqdn request(0, scenario.client_name_,
+                                      scenario.client_name_type_);
+
+            Option6ClientFqdn response(request);
+            mgr.adjustDomainName<Option6ClientFqdn>(request, response, ddns_params);
+            EXPECT_EQ(scenario.expected_name_, response.getDomainName());
+            EXPECT_EQ(scenario.expected_name_type_, response.getDomainNameType());
+        }
+    }
 }
 
 /// @brief Verifies the adustFqdnFlags template with Option6ClientFqdn objects.
@@ -1074,19 +1046,14 @@ TEST(D2ClientMgr, adjustFqdnFlagsV6) {
     Option6ClientFqdnPtr request;
     Option6ClientFqdnPtr response;
 
-    // Create enabled configuration and override-no-update on.
-    D2ClientConfigPtr cfg;
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  true, false, D2ClientConfig::RCM_NEVER,
-                                  "pre-fix", "suf-fix", "", "")));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
-    ASSERT_TRUE(mgr.ddnsEnabled());
-    ASSERT_TRUE(cfg->getOverrideNoUpdate());
-    ASSERT_FALSE(cfg->getOverrideClientUpdate());
+    // Create enabled configuration with override-no-update true.
+    DdnsParams ddns_params;
+    ddns_params.enable_updates_ = true;
+    ddns_params.override_no_update_ = true;
+    ddns_params.override_client_update_ = false;
+    ddns_params.replace_client_name_mode_ = D2ClientConfig::RCM_NEVER;
+    ddns_params.generated_prefix_ = "";
+    ddns_params.qualifying_suffix_ = "";
 
     // client S=0 N=0 means client wants to do forward update.
     // server S should be 0 (server is not doing forward updates)
@@ -1096,7 +1063,7 @@ TEST(D2ClientMgr, adjustFqdnFlagsV6) {
     response.reset(new Option6ClientFqdn(*request));
     response->resetFlags();
 
-    mgr.adjustFqdnFlags<Option6ClientFqdn>(*request, *response);
+    mgr.adjustFqdnFlags<Option6ClientFqdn>(*request, *response, ddns_params);
     EXPECT_FALSE(response->getFlag(Option6ClientFqdn::FLAG_S));
     EXPECT_FALSE(response->getFlag(Option6ClientFqdn::FLAG_N));
     EXPECT_FALSE(response->getFlag(Option6ClientFqdn::FLAG_O));
@@ -1110,7 +1077,7 @@ TEST(D2ClientMgr, adjustFqdnFlagsV6) {
     response.reset(new Option6ClientFqdn(*request));
     response->resetFlags();
 
-    mgr.adjustFqdnFlags<Option6ClientFqdn>(*request, *response);
+    mgr.adjustFqdnFlags<Option6ClientFqdn>(*request, *response, ddns_params);
     EXPECT_TRUE(response->getFlag(Option6ClientFqdn::FLAG_S));
     EXPECT_FALSE(response->getFlag(Option6ClientFqdn::FLAG_N));
     EXPECT_FALSE(response->getFlag(Option6ClientFqdn::FLAG_O));
@@ -1124,12 +1091,13 @@ TEST(D2ClientMgr, adjustFqdnFlagsV6) {
     response.reset(new Option6ClientFqdn(*request));
     response->resetFlags();
 
-    mgr.adjustFqdnFlags<Option6ClientFqdn>(*request, *response);
+    mgr.adjustFqdnFlags<Option6ClientFqdn>(*request, *response, ddns_params);
     EXPECT_TRUE(response->getFlag(Option6ClientFqdn::FLAG_S));
     EXPECT_FALSE(response->getFlag(Option6ClientFqdn::FLAG_N));
     EXPECT_TRUE(response->getFlag(Option6ClientFqdn::FLAG_O));
 }
 
+
 /// @brief Verified the getUpdateDirections template method with
 /// Option6ClientFqdn objects.
 TEST(D2ClientMgr, updateDirectionsV6) {
@@ -1167,18 +1135,18 @@ TEST(D2ClientMgr, updateDirectionsV6) {
 TEST(D2ClientMgr, sanitizeFqdnV4) {
     D2ClientMgr mgr;
 
-    // Create enabled configuration.
-    // replace-client-name is false, client passes in empty fqdn
-    D2ClientConfigPtr cfg;
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  false, false, D2ClientConfig::RCM_NEVER,
-                                  "prefix", "suffix.com", "[^A-Za-z0-9-]", "x")));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
-    ASSERT_EQ(D2ClientConfig::RCM_NEVER, cfg->getReplaceClientNameMode());
+    // Create enabled configuration with override-no-update true.
+    DdnsParams ddns_params;
+    ddns_params.enable_updates_ = true;
+    ddns_params.override_no_update_ = false;
+    ddns_params.override_client_update_ = false;
+    ddns_params.replace_client_name_mode_ = D2ClientConfig::RCM_NEVER;
+    ddns_params.generated_prefix_ = "prefix";
+    ddns_params.qualifying_suffix_ = "suffix.com";
+
+    // Create and assign the sanitizer.
+    ASSERT_NO_THROW(ddns_params.hostname_sanitizer_.reset(new
+                    isc::util::str::StringSanitizer("[^A-Za-z0-9-]", "x")));
 
     struct Scenario {
         std::string description_;
@@ -1226,19 +1194,16 @@ TEST(D2ClientMgr, sanitizeFqdnV4) {
         }
     };
 
-    Option4ClientFqdnPtr request;
-    Option4ClientFqdnPtr response;
-    for (auto scenario = scenarios.begin(); scenario != scenarios.end(); ++scenario) {
-        SCOPED_TRACE((*scenario).description_);
+    for (auto scenario : scenarios) {
+        SCOPED_TRACE(scenario.description_);
         {
-            request.reset(new Option4ClientFqdn(0, Option4ClientFqdn::RCODE_CLIENT(),
-                                                (*scenario).client_name_,
-                                                (*scenario).name_type_));
-
-            response.reset(new Option4ClientFqdn(*request));
-            mgr.adjustDomainName<Option4ClientFqdn>(*request, *response);
-            EXPECT_EQ((*scenario).expected_name_, response->getDomainName());
-            EXPECT_EQ(Option4ClientFqdn::FULL, response->getDomainNameType());
+            Option4ClientFqdn request(0, Option4ClientFqdn::RCODE_CLIENT(),
+                                      scenario.client_name_, scenario.name_type_);
+            Option4ClientFqdn response(request);
+
+            mgr.adjustDomainName<Option4ClientFqdn>(request, response, ddns_params);
+            EXPECT_EQ(scenario.expected_name_, response.getDomainName());
+            EXPECT_EQ(Option4ClientFqdn::FULL, response.getDomainNameType());
         }
     }
 }
@@ -1251,18 +1216,18 @@ TEST(D2ClientMgr, sanitizeFqdnV4) {
 TEST(D2ClientMgr, sanitizeFqdnV6) {
     D2ClientMgr mgr;
 
-    // Create enabled configuration.
-    // replace-client-name is false, client passes in empty fqdn
-    D2ClientConfigPtr cfg;
-    ASSERT_NO_THROW(cfg.reset(new D2ClientConfig(true,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 477,
-                                  isc::asiolink::IOAddress("127.0.0.1"), 478,
-                                  1024,
-                                  dhcp_ddns::NCR_UDP, dhcp_ddns::FMT_JSON,
-                                  false, false, D2ClientConfig::RCM_NEVER,
-                                  "prefix", "suffix.com", "[^A-Za-z0-9-]", "x")));
-    ASSERT_NO_THROW(mgr.setD2ClientConfig(cfg));
-    ASSERT_EQ(D2ClientConfig::RCM_NEVER, cfg->getReplaceClientNameMode());
+    // Create enabled configuration with override-no-update true.
+    DdnsParams ddns_params;
+    ddns_params.enable_updates_ = true;
+    ddns_params.override_no_update_ = false;
+    ddns_params.override_client_update_ = false;
+    ddns_params.replace_client_name_mode_ = D2ClientConfig::RCM_NEVER;
+    ddns_params.generated_prefix_ = "prefix";
+    ddns_params.qualifying_suffix_ = "suffix.com";
+
+    // Create and assign the sanitizer.
+    ASSERT_NO_THROW(ddns_params.hostname_sanitizer_.reset(new
+                    isc::util::str::StringSanitizer("[^A-Za-z0-9-]", "x")));
 
     struct Scenario {
         std::string description_;
@@ -1310,18 +1275,16 @@ TEST(D2ClientMgr, sanitizeFqdnV6) {
         }
     };
 
-    Option6ClientFqdnPtr request;
     Option6ClientFqdnPtr response;
-    for (auto scenario = scenarios.begin(); scenario != scenarios.end(); ++scenario) {
-        SCOPED_TRACE((*scenario).description_);
+    for (auto scenario : scenarios) {
+        SCOPED_TRACE(scenario.description_);
         {
-            request.reset(new Option6ClientFqdn(0, (*scenario).client_name_,
-                                                (*scenario).name_type_));
+            Option6ClientFqdn request(0, scenario.client_name_, scenario.name_type_);
+            Option6ClientFqdn response(request);
 
-            response.reset(new Option6ClientFqdn(*request));
-            mgr.adjustDomainName<Option6ClientFqdn>(*request, *response);
-            EXPECT_EQ((*scenario).expected_name_, response->getDomainName());
-            EXPECT_EQ(Option6ClientFqdn::FULL, response->getDomainNameType());
+            mgr.adjustDomainName<Option6ClientFqdn>(request, response, ddns_params);
+            EXPECT_EQ(scenario.expected_name_, response.getDomainName());
+            EXPECT_EQ(Option6ClientFqdn::FULL, response.getDomainNameType());
         }
     }
 }