]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Add yaml configuration
authorRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 24 Dec 2024 15:53:19 +0000 (16:53 +0100)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Thu, 16 Jan 2025 08:50:29 +0000 (09:50 +0100)
17 files changed:
pdns/dnsdistdist/Makefile.am
pdns/dnsdistdist/dnsdist-configuration-yaml-internal.hh [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-configuration-yaml.cc [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-configuration-yaml.hh [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-rust-bridge-actions-generated.cc [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-rust-bridge-actions-generated.hh [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-rust-bridge-selectors-generated.cc [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-rust-bridge-selectors-generated.hh [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-rust-bridge.hh [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-rust-lib/Makefile.am
pdns/dnsdistdist/dnsdist-rust-lib/dnsdist-configuration-yaml-items-generated-pre-in.cc [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-rust-lib/dnsdist-configuration-yaml-items-generated.cc [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-rust-lib/dnsdist-settings-generator.py [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-rust-lib/rust-middle-in.rs [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-rust-lib/rust-post-in.rs [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-rust-lib/rust-pre-in.rs [new file with mode: 0644]
pdns/dnsdistdist/dnsdist-rust-lib/rust/src/lib.rs

index 3051932f089100e556d798aa04fb280da5451da2..c1507b80b757db6c90fafdb04a22d72fef6ab031 100644 (file)
@@ -117,6 +117,7 @@ EXTRA_DIST=COPYING \
           dnsdist-rust-bridge-selectors-generated.cc \
           dnsdist-rust-bridge-selectors-generated.hh \
           dnsdist-selectors-definitions.yml \
+          dnsdist-settings-definitions.yml \
           dnslabeltext.rl \
           dnsdist.conf-dist \
           dnsmessage.proto \
@@ -180,6 +181,8 @@ dnsdist_SOURCES = \
        dnsdist-cache.cc dnsdist-cache.hh \
        dnsdist-carbon.cc dnsdist-carbon.hh \
        dnsdist-concurrent-connections.hh \
+       dnsdist-configuration-yaml-internal.hh \
+       dnsdist-configuration-yaml.cc dnsdist-configuration-yaml.hh \
        dnsdist-configuration.cc dnsdist-configuration.hh \
        dnsdist-console.cc dnsdist-console.hh \
        dnsdist-crypto.cc dnsdist-crypto.hh \
@@ -526,6 +529,7 @@ testrunner_LDADD += $(QUICHE_LDFLAGS) $(QUICHE_LIBS)
 endif
 
 if HAVE_YAML_CONFIGURATION
+dnsdist_SOURCES += dnsdist-rust-lib/dnsdist-configuration-yaml-items-generated.cc
 dnsdist_LDADD += $(DNSDIST_RUST_LIBS)
 endif
 
diff --git a/pdns/dnsdistdist/dnsdist-configuration-yaml-internal.hh b/pdns/dnsdistdist/dnsdist-configuration-yaml-internal.hh
new file mode 100644 (file)
index 0000000..bbae2cd
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+
+#include "dnsdist-configuration.hh"
+
+namespace dnsdist::configuration::yaml
+{
+#if defined(HAVE_YAML_CONFIGURATION)
+void convertImmutableFlatSettingsFromRust(const dnsdist::rust::settings::GlobalConfiguration& yamlConfig, dnsdist::configuration::ImmutableConfiguration& config);
+void convertRuntimeFlatSettingsFromRust(const dnsdist::rust::settings::GlobalConfiguration& yamlConfig, dnsdist::configuration::RuntimeConfiguration& config);
+#endif /* HAVE_YAML_CONFIGURATION */
+}
diff --git a/pdns/dnsdistdist/dnsdist-configuration-yaml.cc b/pdns/dnsdistdist/dnsdist-configuration-yaml.cc
new file mode 100644 (file)
index 0000000..4f91015
--- /dev/null
@@ -0,0 +1,1381 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <stdexcept>
+
+#include "dnsdist-configuration-yaml.hh"
+
+#if defined(HAVE_YAML_CONFIGURATION)
+#include "base64.hh"
+#include "dolog.hh"
+#include "dnsdist-actions-factory.hh"
+#include "dnsdist-backend.hh"
+#include "dnsdist-cache.hh"
+#include "dnsdist-discovery.hh"
+#include "dnsdist-dnsparser.hh"
+#include "dnsdist-dynblocks.hh"
+#include "dnsdist-rules.hh"
+#include "dnsdist-rules-factory.hh"
+#include "dnsdist-kvs.hh"
+#include "dnsdist-web.hh"
+#include "doh.hh"
+#include "fstrm_logger.hh"
+#include "iputils.hh"
+#include "remote_logger.hh"
+
+#include "rust/cxx.h"
+#include "rust/lib.rs.h"
+#include "dnsdist-configuration-yaml-internal.hh"
+
+#include <boost/uuid/string_generator.hpp>
+#endif /* HAVE_YAML_CONFIGURATION */
+
+namespace dnsdist::configuration::yaml
+{
+#if defined(HAVE_YAML_CONFIGURATION)
+
+using RegisteredTypes = std::variant<std::shared_ptr<DNSDistPacketCache>, std::shared_ptr<dnsdist::rust::settings::DNSSelector>, std::shared_ptr<dnsdist::rust::settings::DNSActionWrapper>, std::shared_ptr<dnsdist::rust::settings::DNSResponseActionWrapper>, std::shared_ptr<NetmaskGroup>, std::shared_ptr<KeyValueStore>, std::shared_ptr<KeyValueLookupKey>, std::shared_ptr<RemoteLoggerInterface>, std::shared_ptr<ServerPolicy>>;
+static LockGuarded<std::unordered_map<std::string, RegisteredTypes>> s_registeredTypesMap;
+static std::atomic<bool> s_inConfigCheckMode;
+static std::atomic<bool> s_inClientMode;
+
+template <class T>
+static void registerType(const std::shared_ptr<T>& entry, const ::rust::string& rustName)
+{
+  std::string name(rustName);
+  if (name.empty()) {
+    auto uuid = getUniqueID();
+    name = boost::uuids::to_string(uuid);
+  }
+
+  auto [it, inserted] = s_registeredTypesMap.lock()->try_emplace(name, entry);
+  if (!inserted) {
+    throw std::runtime_error("Trying to register a type named '" + name + "' while one already exists");
+  }
+}
+
+template <class T>
+static std::shared_ptr<T> getRegisteredTypeByName(const std::string& name)
+{
+  auto map = s_registeredTypesMap.lock();
+  auto item = map->find(name);
+  if (item == map->end()) {
+    return nullptr;
+  }
+  if (auto* ptr = std::get_if<std::shared_ptr<T>>(&item->second)) {
+    return *ptr;
+  }
+  return nullptr;
+}
+
+template <class T>
+static std::shared_ptr<T> getRegisteredTypeByName(const ::rust::String& name)
+{
+  auto nameStr = std::string(name);
+  return getRegisteredTypeByName<T>(nameStr);
+}
+
+template <class T>
+static T checkedConversionFromStr(const std::string& context, const std::string& parameterName, const std::string& str)
+{
+  try {
+    return pdns::checked_stoi<T>(std::string(str));
+  }
+  catch (const std::exception& exp) {
+    throw std::runtime_error("Error converting value '" + str + "' for parameter '" + parameterName + "' in YAML directive '" + context + "': " + exp.what());
+  }
+}
+
+template <class T>
+static T checkedConversionFromStr(const std::string& context, const std::string& parameterName, const ::rust::string& str)
+{
+  return checkedConversionFromStr<T>(context, parameterName, std::string(str));
+}
+
+static std::set<int> getCPUPiningFromStr(const std::string& context, const std::string& cpuStr)
+{
+  std::set<int> cpus;
+  std::vector<std::string> tokens;
+  stringtok(tokens, cpuStr);
+  for (const auto& token : tokens) {
+    cpus.insert(checkedConversionFromStr<int>(context, "cpus", token));
+  }
+  return cpus;
+}
+
+static TLSConfig getTLSConfigFromRustIncomingTLS(const dnsdist::rust::settings::IncomingTlsConfiguration& incomingTLSConfig)
+{
+  TLSConfig out;
+  for (const auto& certConfig : incomingTLSConfig.certificates) {
+    TLSCertKeyPair pair(std::string(certConfig.certificate));
+    if (!certConfig.key.empty()) {
+      pair.d_key = std::string(certConfig.key);
+    }
+    if (!certConfig.password.empty()) {
+      pair.d_password = std::string(certConfig.password);
+    }
+    out.d_certKeyPairs.push_back(std::move(pair));
+  }
+  for (const auto& ocspFile : incomingTLSConfig.ocsp_response_files) {
+    out.d_ocspFiles.emplace_back(ocspFile);
+  }
+  out.d_ciphers = std::string(incomingTLSConfig.ciphers);
+  out.d_ciphers13 = std::string(incomingTLSConfig.ciphers_tls_13);
+  out.d_minTLSVersion = libssl_tls_version_from_string(std::string(incomingTLSConfig.minimum_version));
+  out.d_ticketKeyFile = std::string(incomingTLSConfig.ticket_key_file);
+  out.d_keyLogFile = std::string(incomingTLSConfig.key_log_file);
+  out.d_maxStoredSessions = incomingTLSConfig.number_of_stored_sessions;
+  out.d_sessionTimeout = incomingTLSConfig.session_timeout;
+  out.d_ticketsKeyRotationDelay = incomingTLSConfig.tickets_keys_rotation_delay;
+  out.d_numberOfTicketsKeys = incomingTLSConfig.number_of_tickets_keys;
+  out.d_preferServerCiphers = incomingTLSConfig.prefer_server_ciphers;
+  out.d_enableTickets = incomingTLSConfig.session_tickets;
+  out.d_releaseBuffers = incomingTLSConfig.release_buffers;
+  out.d_enableRenegotiation = incomingTLSConfig.enable_renegotiation;
+  out.d_asyncMode = incomingTLSConfig.async_mode;
+  out.d_ktls = incomingTLSConfig.ktls;
+  out.d_readAhead = incomingTLSConfig.read_ahead;
+  return out;
+}
+
+static bool validateTLSConfiguration(const dnsdist::rust::settings::BindConfiguration& bind, const TLSConfig& tlsConfig)
+{
+  if (!bind.tls.ignore_configuration_errors) {
+    return true;
+  }
+
+  // we are asked to try to load the certificates so we can return a potential error
+  // and properly ignore the frontend before actually launching it
+  try {
+    std::map<int, std::string> ocspResponses = {};
+    auto ctx = libssl_init_server_context(tlsConfig, ocspResponses);
+  }
+  catch (const std::runtime_error& e) {
+    errlog("Ignoring %s frontend: '%s'", bind.protocol, e.what());
+    return false;
+  }
+
+  return true;
+}
+
+static bool handleTLSConfiguration(const dnsdist::rust::settings::BindConfiguration& bind, ClientState& state)
+{
+  auto tlsConfig = getTLSConfigFromRustIncomingTLS(bind.tls);
+  if (!validateTLSConfiguration(bind, tlsConfig)) {
+    return false;
+  }
+
+  auto protocol = boost::to_lower_copy(std::string(bind.protocol));
+  if (protocol == "dot") {
+    auto frontend = std::make_shared<TLSFrontend>(TLSFrontend::ALPN::DoT);
+    frontend->d_provider = std::string(bind.tls.provider);
+    boost::algorithm::to_lower(frontend->d_provider);
+    frontend->d_proxyProtocolOutsideTLS = bind.tls.proxy_protocol_outside_tls;
+    frontend->d_tlsConfig = std::move(tlsConfig);
+    state.tlsFrontend = std::move(frontend);
+  }
+  else if (protocol == "doq") {
+    auto frontend = std::make_shared<DOQFrontend>();
+    frontend->d_local = ComboAddress(std::string(bind.listen_address), 853);
+    frontend->d_quicheParams.d_tlsConfig = std::move(tlsConfig);
+    frontend->d_quicheParams.d_maxInFlight = bind.doq.max_concurrent_queries_per_connection;
+    frontend->d_quicheParams.d_idleTimeout = bind.quic.idle_timeout;
+    frontend->d_quicheParams.d_keyLogFile = std::string(bind.tls.key_log_file);
+    if (dnsdist::doq::s_available_cc_algorithms.count(std::string(bind.quic.congestion_control_algorithm)) > 0) {
+      frontend->d_quicheParams.d_ccAlgo = std::string(bind.quic.congestion_control_algorithm);
+    }
+    frontend->d_internalPipeBufferSize = bind.quic.internal_pipe_buffer_size;
+    state.doqFrontend = std::move(frontend);
+  }
+  else if (protocol == "doh3") {
+    auto frontend = std::make_shared<DOH3Frontend>();
+    frontend->d_local = ComboAddress(std::string(bind.listen_address), 443);
+    frontend->d_quicheParams.d_tlsConfig = std::move(tlsConfig);
+    frontend->d_quicheParams.d_idleTimeout = bind.quic.idle_timeout;
+    frontend->d_quicheParams.d_keyLogFile = std::string(bind.tls.key_log_file);
+    if (dnsdist::doq::s_available_cc_algorithms.count(std::string(bind.quic.congestion_control_algorithm)) > 0) {
+      frontend->d_quicheParams.d_ccAlgo = std::string(bind.quic.congestion_control_algorithm);
+    }
+    frontend->d_internalPipeBufferSize = bind.quic.internal_pipe_buffer_size;
+    state.doh3Frontend = std::move(frontend);
+  }
+  else if (protocol == "doh") {
+    auto frontend = std::make_shared<DOHFrontend>();
+    frontend->d_tlsContext.d_provider = std::string(bind.tls.provider);
+    boost::algorithm::to_lower(frontend->d_tlsContext.d_provider);
+    frontend->d_library = std::string(bind.doh.provider);
+    if (frontend->d_library == "h2o") {
+#ifdef HAVE_LIBH2OEVLOOP
+      frontend = std::make_shared<H2ODOHFrontend>();
+      // we _really_ need to set it again, as we just replaced the generic frontend by a new one
+      frontend->d_library = "h2o";
+#else /* HAVE_LIBH2OEVLOOP */
+      errlog("DOH bind %s is configured to use libh2o but the library is not available", bind.listen_address);
+      return false;
+#endif /* HAVE_LIBH2OEVLOOP */
+    }
+    else if (frontend->d_library == "nghttp2") {
+#ifndef HAVE_NGHTTP2
+      errlog("DOH bind %s is configured to use nghttp2 but the library is not available", bind.listen_address);
+      return false;
+#endif /* HAVE_NGHTTP2 */
+    }
+    else {
+      errlog("DOH bind %s is configured to use an unknown library ('%s')", bind.listen_address, frontend->d_library);
+      return false;
+    }
+
+    for (const auto& path : bind.doh.paths) {
+      frontend->d_urls.emplace(path);
+    }
+    frontend->d_idleTimeout = bind.doh.idle_timeout;
+    frontend->d_serverTokens = std::string(bind.doh.server_tokens);
+    frontend->d_sendCacheControlHeaders = bind.doh.send_cache_control_headers;
+    frontend->d_keepIncomingHeaders = bind.doh.keep_incoming_headers;
+    frontend->d_trustForwardedForHeader = bind.doh.trust_forwarded_for_header;
+    frontend->d_earlyACLDrop = bind.doh.early_acl_drop;
+    frontend->d_internalPipeBufferSize = bind.doh.internal_pipe_buffer_size;
+    frontend->d_exactPathMatching = bind.doh.exact_path_matching;
+    for (const auto& customHeader : bind.doh.custom_response_headers) {
+      auto headerResponse = std::pair(boost::to_lower_copy(std::string(customHeader.key)), std::string(customHeader.value));
+      frontend->d_customResponseHeaders.insert(std::move(headerResponse));
+    }
+
+    if (!bind.doh.responses_map.empty()) {
+      auto newMap = std::make_shared<std::vector<std::shared_ptr<DOHResponseMapEntry>>>();
+      for (const auto& responsesMap : bind.doh.responses_map) {
+        boost::optional<std::unordered_map<std::string, std::string>> headers;
+        if (!responsesMap.headers.empty()) {
+          headers = std::unordered_map<std::string, std::string>();
+          for (const auto& header : responsesMap.headers) {
+            headers->emplace(boost::to_lower_copy(std::string(header.key)), std::string(header.value));
+          }
+        }
+        auto entry = std::make_shared<DOHResponseMapEntry>(std::string(responsesMap.expression), responsesMap.status, PacketBuffer(responsesMap.content.begin(), responsesMap.content.end()), headers);
+        newMap->emplace_back(std::move(entry));
+      }
+      frontend->d_responsesMap = std::move(newMap);
+    }
+
+    if (!tlsConfig.d_certKeyPairs.empty()) {
+      frontend->d_tlsContext.d_addr = ComboAddress(std::string(bind.listen_address), 443);
+      infolog("DNS over HTTPS configured");
+    }
+    else {
+      frontend->d_tlsContext.d_addr = ComboAddress(std::string(bind.listen_address), 80);
+      infolog("No certificate provided for DoH endpoint %s, running in DNS over HTTP mode instead of DNS over HTTPS", frontend->d_tlsContext.d_addr.toStringWithPort());
+    }
+
+    frontend->d_tlsContext.d_proxyProtocolOutsideTLS = bind.tls.proxy_protocol_outside_tls;
+    frontend->d_tlsContext.d_tlsConfig = std::move(tlsConfig);
+    state.dohFrontend = std::move(frontend);
+  }
+  else if (protocol != "do53") {
+    errlog("Bind %s is configured to use an unknown protocol ('%s')", bind.listen_address, protocol);
+    return false;
+  }
+
+  return true;
+}
+
+template <class T>
+static bool getOptionalLuaFunction(T& destination, const ::rust::string& functionName)
+{
+  auto lua = g_lua.lock();
+  auto function = lua->readVariable<boost::optional<T>>(std::string(functionName));
+  if (!function) {
+    return false;
+  }
+  destination = *function;
+  return true;
+}
+
+static std::shared_ptr<DownstreamState> createBackendFromConfiguration(const dnsdist::rust::settings::BackendConfiguration& config, bool configCheck)
+{
+  DownstreamState::Config backendConfig;
+  std::shared_ptr<TLSCtx> tlsCtx;
+
+  backendConfig.d_numberOfSockets = config.sockets;
+  backendConfig.d_qpsLimit = config.queries_per_second;
+  backendConfig.order = config.order;
+  backendConfig.d_weight = config.weight;
+  backendConfig.d_retries = config.retries;
+  backendConfig.d_maxInFlightQueriesPerConn = config.max_in_flight;
+  backendConfig.d_tcpConcurrentConnectionsLimit = config.max_concurrent_tcp_connections;
+  backendConfig.name = std::string(config.name);
+  if (!config.id.empty()) {
+    backendConfig.id = boost::uuids::string_generator()(std::string(config.id));
+  }
+  backendConfig.useECS = config.use_client_subnet;
+  backendConfig.useProxyProtocol = config.use_proxy_protocol;
+  backendConfig.d_proxyProtocolAdvertiseTLS = config.proxy_protocol_advertise_tls;
+  backendConfig.disableZeroScope = config.disable_zero_scope;
+  backendConfig.ipBindAddrNoPort = config.ip_bind_addr_no_port;
+  backendConfig.reconnectOnUp = config.reconnect_on_up;
+  backendConfig.d_cpus = getCPUPiningFromStr("backend", std::string(config.cpus));
+  backendConfig.d_tcpOnly = config.tcp_only;
+
+  backendConfig.tcpConnectTimeout = config.tcp.connect_timeout;
+  backendConfig.tcpSendTimeout = config.tcp.send_timeout;
+  backendConfig.tcpRecvTimeout = config.tcp.receive_timeout;
+  backendConfig.tcpFastOpen = config.tcp.fast_open;
+
+  const auto& hcConf = config.health_checks;
+  backendConfig.checkInterval = hcConf.interval;
+  if (!hcConf.qname.empty()) {
+    backendConfig.checkName = DNSName(std::string(hcConf.qname));
+  }
+  backendConfig.checkType = std::string(hcConf.qtype);
+  if (!hcConf.qclass.empty()) {
+    backendConfig.checkClass = QClass(std::string(hcConf.qclass));
+  }
+  backendConfig.checkTimeout = hcConf.timeout;
+  backendConfig.d_tcpCheck = hcConf.use_tcp;
+  backendConfig.setCD = hcConf.set_cd;
+  backendConfig.mustResolve = hcConf.must_resolve;
+  backendConfig.maxCheckFailures = hcConf.max_failures;
+  backendConfig.minRiseSuccesses = hcConf.rise;
+
+  getOptionalLuaFunction<DownstreamState::checkfunc_t>(backendConfig.checkFunction, hcConf.function);
+
+  auto availability = DownstreamState::getAvailabilityFromStr(std::string(hcConf.mode));
+  if (availability) {
+    backendConfig.availability = *availability;
+  }
+
+  backendConfig.d_lazyHealthCheckSampleSize = hcConf.lazy.sample_size;
+  backendConfig.d_lazyHealthCheckMinSampleCount = hcConf.lazy.min_sample_count;
+  backendConfig.d_lazyHealthCheckThreshold = hcConf.lazy.threshold;
+  backendConfig.d_lazyHealthCheckFailedInterval = hcConf.lazy.interval;
+  backendConfig.d_lazyHealthCheckUseExponentialBackOff = hcConf.lazy.use_exponential_back_off;
+  backendConfig.d_lazyHealthCheckMaxBackOff = hcConf.lazy.max_back_off;
+  if (hcConf.lazy.mode == "TimeoutOnly") {
+    backendConfig.d_lazyHealthCheckMode = DownstreamState::LazyHealthCheckMode::TimeoutOnly;
+  }
+  else if (hcConf.lazy.mode == "TimeoutOrServFail") {
+    backendConfig.d_lazyHealthCheckMode = DownstreamState::LazyHealthCheckMode::TimeoutOrServFail;
+  }
+  else if (!hcConf.lazy.mode.empty()) {
+    warnlog("Ignoring unknown value '%s' for 'lazy.mode' on backend %s", hcConf.lazy.mode, std::string(config.address));
+  }
+
+  backendConfig.d_upgradeToLazyHealthChecks = config.auto_upgrade.use_lazy_health_check;
+
+  uint16_t serverPort = 53;
+  const auto& tlsConf = config.tls;
+  auto protocol = boost::to_lower_copy(std::string(config.protocol));
+  if (protocol == "dot" || protocol == "doh") {
+    backendConfig.d_tlsParams.d_provider = std::string(tlsConf.provider);
+    backendConfig.d_tlsParams.d_ciphers = std::string(tlsConf.ciphers);
+    backendConfig.d_tlsParams.d_ciphers13 = std::string(tlsConf.ciphers_tls_13);
+    backendConfig.d_tlsParams.d_caStore = std::string(tlsConf.ca_store);
+    backendConfig.d_tlsParams.d_keyLogFile = std::string(tlsConf.key_log_file);
+    backendConfig.d_tlsParams.d_validateCertificates = tlsConf.validate_certificate;
+    backendConfig.d_tlsParams.d_releaseBuffers = tlsConf.release_buffers;
+    backendConfig.d_tlsParams.d_enableRenegotiation = tlsConf.enable_renegotiation;
+    backendConfig.d_tlsParams.d_ktls = tlsConf.ktls;
+    backendConfig.d_tlsSubjectName = std::string(tlsConf.subject_name);
+    if (!tlsConf.subject_address.empty()) {
+      try {
+        ComboAddress addr{std::string(tlsConf.subject_address)};
+        backendConfig.d_tlsSubjectName = addr.toString();
+        backendConfig.d_tlsSubjectIsAddr = true;
+      }
+      catch (const std::exception&) {
+        errlog("Error creating new server: downstream subject_address value must be a valid IP address");
+      }
+    }
+  }
+
+  if (protocol == "dot") {
+    serverPort = 853;
+    backendConfig.d_tlsParams.d_alpn = TLSFrontend::ALPN::DoT;
+  }
+  else if (protocol == "doh") {
+    serverPort = 443;
+    backendConfig.d_tlsParams.d_alpn = TLSFrontend::ALPN::DoH;
+    backendConfig.d_dohPath = std::string(config.doh.path);
+    backendConfig.d_addXForwardedHeaders = config.doh.add_x_forwarded_headers;
+  }
+
+  for (const auto& pool : config.pools) {
+    backendConfig.pools.emplace(pool);
+  }
+
+  backendConfig.remote = ComboAddress(std::string(config.address), serverPort);
+
+#warning handle XSK
+
+  if (protocol == "dot" || protocol == "doh") {
+    tlsCtx = getTLSContext(backendConfig.d_tlsParams);
+  }
+
+  auto downstream = std::make_shared<DownstreamState>(std::move(backendConfig), std::move(tlsCtx), !configCheck);
+
+  const auto& autoUpgradeConf = config.auto_upgrade;
+  if (autoUpgradeConf.enabled && downstream->getProtocol() != dnsdist::Protocol::DoT && downstream->getProtocol() != dnsdist::Protocol::DoH) {
+    dnsdist::ServiceDiscovery::addUpgradeableServer(downstream, autoUpgradeConf.interval, std::string(autoUpgradeConf.pool), autoUpgradeConf.doh_key, autoUpgradeConf.keep);
+  }
+
+  return downstream;
+}
+#endif /* defined(HAVE_YAML_CONFIGURATION) */
+
+bool loadConfigurationFromFile(const std::string& fileName, bool isClient, bool configCheck)
+{
+#if defined(HAVE_YAML_CONFIGURATION)
+  // this is not very elegant but passing a context to the functions called by the
+  // Rust code would be quite cumbersome so for now let's settle for this
+  s_inConfigCheckMode.store(configCheck);
+  s_inClientMode.store(isClient);
+
+  auto file = std::ifstream(fileName);
+  if (!file.is_open()) {
+    errlog("Unable to open YAML file %s: %s", fileName, stringerror(errno));
+    return false;
+  }
+
+  /* register built-in policies */
+  for (const auto& policy : dnsdist::lbpolicies::getBuiltInPolicies()) {
+    registerType<ServerPolicy>(policy, ::rust::string(policy->d_name));
+  }
+
+  try {
+    auto data = std::string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
+
+    auto globalConfig = dnsdist::rust::settings::from_yaml_string(data);
+
+    if (!globalConfig.console.listen_address.empty()) {
+      const auto& consoleConf = globalConfig.console;
+      dnsdist::configuration::updateRuntimeConfiguration([consoleConf](dnsdist::configuration::RuntimeConfiguration& config) {
+        config.d_consoleServerAddress = ComboAddress(std::string(consoleConf.listen_address), 5199);
+        config.d_consoleEnabled = true;
+        config.d_consoleACL.clear();
+        for (const auto& aclEntry : consoleConf.acl) {
+          config.d_consoleACL.addMask(std::string(aclEntry));
+        }
+        B64Decode(std::string(consoleConf.key), config.d_consoleKey);
+      });
+    }
+
+    if (isClient) {
+      return true;
+    }
+
+    if (!globalConfig.acl.empty()) {
+      dnsdist::configuration::updateRuntimeConfiguration([&acl = globalConfig.acl](dnsdist::configuration::RuntimeConfiguration& config) {
+        config.d_ACL.clear();
+        for (const auto& aclEntry : acl) {
+          config.d_ACL.addMask(std::string(aclEntry));
+        }
+      });
+    }
+
+    for (const auto& bind : globalConfig.binds) {
+      ComboAddress listeningAddress(std::string(bind.listen_address), 53);
+      updateImmutableConfiguration([&bind, listeningAddress](ImmutableConfiguration& config) {
+        auto protocol = boost::to_lower_copy(std::string(bind.protocol));
+        auto cpus = getCPUPiningFromStr("binds", std::string(bind.cpus));
+        for (size_t idx = 0; idx < bind.threads; idx++) {
+          auto state = std::make_shared<ClientState>(listeningAddress, protocol != "doq" && protocol != "doh3", bind.reuseport, bind.tcp.fast_open_queue_size, std::string(bind.interface), cpus, false);
+          if (bind.tcp.listen_queue_size > 0) {
+            state->tcpListenQueueSize = bind.tcp.listen_queue_size;
+          }
+          if (bind.tcp.max_in_flight_queries > 0) {
+            state->d_maxInFlightQueriesPerConn = bind.tcp.max_in_flight_queries;
+          }
+          if (bind.tcp.max_concurrent_connections > 0) {
+            state->d_tcpConcurrentConnectionsLimit = bind.tcp.max_concurrent_connections;
+          }
+
+          for (const auto& addr : bind.additional_addresses) {
+            try {
+              ComboAddress address{std::string(addr)};
+              state->d_additionalAddresses.emplace_back(address, -1);
+            }
+            catch (const PDNSException& e) {
+              errlog("Unable to parse additional address %s for %s bind: %s", std::string(addr), protocol, e.reason);
+            }
+          }
+
+          if (protocol != "do53") {
+            if (!handleTLSConfiguration(bind, *state)) {
+              continue;
+            }
+          }
+
+          config.d_frontends.emplace_back(std::move(state));
+          if (protocol == "do53") {
+            /* also create the UDP listener */
+            state = std::make_shared<ClientState>(ComboAddress(std::string(bind.listen_address), 53), false, bind.reuseport, bind.tcp.fast_open_queue_size, std::string(bind.interface), cpus, false);
+            config.d_frontends.emplace_back(std::move(state));
+          }
+        }
+      });
+    }
+
+    for (const auto& backend : globalConfig.backends) {
+      auto downstream = createBackendFromConfiguration(backend, configCheck);
+
+      if (!downstream->d_config.pools.empty()) {
+        for (const auto& poolName : downstream->d_config.pools) {
+          addServerToPool(poolName, downstream);
+        }
+      }
+      else {
+        addServerToPool("", downstream);
+      }
+
+      dnsdist::backend::registerNewBackend(downstream);
+    }
+
+    if (!globalConfig.proxy_protocol.acl.empty()) {
+      dnsdist::configuration::updateRuntimeConfiguration([&globalConfig](dnsdist::configuration::RuntimeConfiguration& config) {
+        config.d_proxyProtocolACL.clear();
+        for (const auto& aclEntry : globalConfig.proxy_protocol.acl) {
+          config.d_proxyProtocolACL.addMask(std::string(aclEntry));
+        }
+      });
+    }
+
+#ifndef DISABLE_CARBON
+    if (!globalConfig.metrics.carbon.empty()) {
+      dnsdist::configuration::updateRuntimeConfiguration([&globalConfig](dnsdist::configuration::RuntimeConfiguration& config) {
+        for (const auto& carbonConfig : globalConfig.metrics.carbon) {
+          auto newEndpoint = dnsdist::Carbon::newEndpoint(std::string(carbonConfig.address),
+                                                          std::string(carbonConfig.name),
+                                                          carbonConfig.interval,
+                                                          carbonConfig.name_space.empty() ? "dnsdist" : std::string(carbonConfig.name_space),
+                                                          carbonConfig.instance.empty() ? "main" : std::string(carbonConfig.instance));
+          config.d_carbonEndpoints.push_back(std::move(newEndpoint));
+        }
+      });
+    }
+#endif /* DISABLE_CARBON */
+
+    if (!globalConfig.webserver.listen_address.empty()) {
+      const auto& webConfig = globalConfig.webserver;
+      ComboAddress local;
+      try {
+        local = ComboAddress{std::string(webConfig.listen_address)};
+      }
+      catch (const PDNSException& e) {
+        throw std::runtime_error(std::string("Error parsing the bind address for the webserver: ") + e.reason);
+      }
+      dnsdist::configuration::updateRuntimeConfiguration([local, webConfig](dnsdist::configuration::RuntimeConfiguration& config) {
+        config.d_webServerAddress = local;
+        if (!webConfig.password.empty()) {
+          auto holder = std::make_shared<CredentialsHolder>(std::string(webConfig.password), webConfig.hash_plaintext_credentials);
+          if (!holder->wasHashed() && holder->isHashingAvailable()) {
+            infolog("Passing a plain-text password via the 'webserver.password' parameter to is not advised, please consider generating a hashed one using 'hashPassword()' instead.");
+          }
+          config.d_webPassword = std::move(holder);
+        }
+        if (!webConfig.api_key.empty()) {
+          auto holder = std::make_shared<CredentialsHolder>(std::string(webConfig.api_key), webConfig.hash_plaintext_credentials);
+          if (!holder->wasHashed() && holder->isHashingAvailable()) {
+            infolog("Passing a plain-text API key via the 'webserver.api_key' parameter to is not advised, please consider generating a hashed one using 'hashPassword()' instead.");
+          }
+          config.d_webAPIKey = std::move(holder);
+        }
+        if (!webConfig.acl.empty()) {
+          config.d_webServerACL.clear();
+          for (const auto& acl : webConfig.acl) {
+            config.d_webServerACL.toMasks(std::string(acl));
+          }
+        }
+        if (!webConfig.custom_headers.empty()) {
+          if (!config.d_webCustomHeaders) {
+            config.d_webCustomHeaders = std::unordered_map<std::string, std::string>();
+            for (const auto& customHeader : webConfig.custom_headers) {
+              auto headerResponse = std::pair(boost::to_lower_copy(std::string(customHeader.key)), std::string(customHeader.value));
+              config.d_webCustomHeaders->insert(std::move(headerResponse));
+            }
+          }
+        }
+
+        config.d_apiRequiresAuthentication = webConfig.api_requires_authentication;
+        config.d_dashboardRequiresAuthentication = webConfig.dashboard_requires_authentication;
+        config.d_statsRequireAuthentication = webConfig.stats_require_authentication;
+        dnsdist::webserver::setMaxConcurrentConnections(webConfig.max_concurrent_connections);
+        config.d_apiConfigDirectory = std::string(webConfig.api_configuration_directory);
+        config.d_apiReadWrite = webConfig.api_read_write;
+      });
+    }
+
+    if (globalConfig.query_count.enabled) {
+      dnsdist::configuration::updateRuntimeConfiguration([&globalConfig](dnsdist::configuration::RuntimeConfiguration& config) {
+        config.d_queryCountConfig.d_enabled = true;
+        if (!globalConfig.query_count.filter.empty()) {
+          getOptionalLuaFunction<dnsdist::QueryCount::Configuration::Filter>(config.d_queryCountConfig.d_filter, globalConfig.query_count.filter);
+        }
+      });
+    }
+
+    if (!globalConfig.dynamic_rules_settings.default_action.empty()) {
+      dnsdist::configuration::updateRuntimeConfiguration([default_action = globalConfig.dynamic_rules_settings.default_action](dnsdist::configuration::RuntimeConfiguration& config) {
+        config.d_dynBlockAction = DNSAction::typeFromString(std::string(default_action));
+      });
+    }
+
+    for (const auto& dbrg : globalConfig.dynamic_rules) {
+      auto dbrgObj = std::make_shared<DynBlockRulesGroup>();
+      dbrgObj->setMasks(dbrg.mask_ipv4, dbrg.mask_ipv6, dbrg.mask_port);
+      for (const auto& range : dbrg.exclude_ranges) {
+        dbrgObj->excludeRange(Netmask(std::string(range)));
+      }
+      for (const auto& range : dbrg.include_ranges) {
+        dbrgObj->includeRange(Netmask(std::string(range)));
+      }
+      for (const auto& domain : dbrg.exclude_domains) {
+        dbrgObj->excludeDomain(DNSName(std::string(domain)));
+      }
+      for (const auto& rule : dbrg.rules) {
+        if (rule.rule_type == "query-rate") {
+          DynBlockRulesGroup::DynBlockRule ruleParams(std::string(rule.comment), rule.action_duration, rule.rate, rule.warning_rate, rule.seconds, rule.action.empty() ? DNSAction::Action::None : DNSAction::typeFromString(std::string(rule.action)));
+          if (ruleParams.d_action == DNSAction::Action::SetTag && !rule.tag_name.empty()) {
+            ruleParams.d_tagSettings = std::make_shared<DynBlock::TagSettings>();
+            ruleParams.d_tagSettings->d_name = std::string(rule.tag_name);
+            ruleParams.d_tagSettings->d_value = std::string(rule.tag_value);
+          }
+          dbrgObj->setQueryRate(std::move(ruleParams));
+        }
+        else if (rule.rule_type == "rcode-rate") {
+          DynBlockRulesGroup::DynBlockRule ruleParams(std::string(rule.comment), rule.action_duration, rule.rate, rule.warning_rate, rule.seconds, rule.action.empty() ? DNSAction::Action::None : DNSAction::typeFromString(std::string(rule.action)));
+          if (ruleParams.d_action == DNSAction::Action::SetTag && !rule.tag_name.empty()) {
+            ruleParams.d_tagSettings = std::make_shared<DynBlock::TagSettings>();
+            ruleParams.d_tagSettings->d_name = std::string(rule.tag_name);
+            ruleParams.d_tagSettings->d_value = std::string(rule.tag_value);
+          }
+          dbrgObj->setRCodeRate(checkedConversionFromStr<int>("dynamic-rules.rules.rcode_rate", "rcode", rule.rcode), std::move(ruleParams));
+        }
+        else if (rule.rule_type == "rcode-ratio") {
+          DynBlockRulesGroup::DynBlockRatioRule ruleParams(std::string(rule.comment), rule.action_duration, rule.ratio, rule.warning_ratio, rule.seconds, rule.action.empty() ? DNSAction::Action::None : DNSAction::typeFromString(std::string(rule.action)), rule.minimum_number_of_responses);
+          if (ruleParams.d_action == DNSAction::Action::SetTag && !rule.tag_name.empty()) {
+            ruleParams.d_tagSettings = std::make_shared<DynBlock::TagSettings>();
+            ruleParams.d_tagSettings->d_name = std::string(rule.tag_name);
+            ruleParams.d_tagSettings->d_value = std::string(rule.tag_value);
+          }
+          dbrgObj->setRCodeRatio(checkedConversionFromStr<int>("dynamic-rules.rules.rcode_ratio", "rcode", rule.rcode), std::move(ruleParams));
+        }
+        else if (rule.rule_type == "qtype-rate") {
+          DynBlockRulesGroup::DynBlockRule ruleParams(std::string(rule.comment), rule.action_duration, rule.rate, rule.warning_rate, rule.seconds, rule.action.empty() ? DNSAction::Action::None : DNSAction::typeFromString(std::string(rule.action)));
+          if (ruleParams.d_action == DNSAction::Action::SetTag && !rule.tag_name.empty()) {
+            ruleParams.d_tagSettings = std::make_shared<DynBlock::TagSettings>();
+            ruleParams.d_tagSettings->d_name = std::string(rule.tag_name);
+            ruleParams.d_tagSettings->d_value = std::string(rule.tag_value);
+          }
+          dbrgObj->setRCodeRate(checkedConversionFromStr<int>("dynamic-rules.rules.qtype_rate", "qtype", rule.qtype), std::move(ruleParams));
+        }
+        else if (rule.rule_type == "qtype-ratio") {
+          DynBlockRulesGroup::DynBlockRatioRule ruleParams(std::string(rule.comment), rule.action_duration, rule.ratio, rule.warning_ratio, rule.seconds, rule.action.empty() ? DNSAction::Action::None : DNSAction::typeFromString(std::string(rule.action)), rule.minimum_number_of_responses);
+          if (ruleParams.d_action == DNSAction::Action::SetTag && !rule.tag_name.empty()) {
+            ruleParams.d_tagSettings = std::make_shared<DynBlock::TagSettings>();
+            ruleParams.d_tagSettings->d_name = std::string(rule.tag_name);
+            ruleParams.d_tagSettings->d_value = std::string(rule.tag_value);
+          }
+          dbrgObj->setRCodeRatio(checkedConversionFromStr<int>("dynamic-rules.rules.qtype_ratio", "qtype", rule.qtype), std::move(ruleParams));
+        }
+        else if (rule.rule_type == "cache-miss-ratio") {
+          DynBlockRulesGroup::DynBlockCacheMissRatioRule ruleParams(std::string(rule.comment), rule.action_duration, rule.ratio, rule.warning_ratio, rule.seconds, rule.action.empty() ? DNSAction::Action::None : DNSAction::typeFromString(std::string(rule.action)), rule.minimum_number_of_responses, rule.minimum_global_cache_hit_ratio);
+          if (ruleParams.d_action == DNSAction::Action::SetTag && !rule.tag_name.empty()) {
+            ruleParams.d_tagSettings = std::make_shared<DynBlock::TagSettings>();
+            ruleParams.d_tagSettings->d_name = std::string(rule.tag_name);
+            ruleParams.d_tagSettings->d_value = std::string(rule.tag_value);
+          }
+          dbrgObj->setCacheMissRatio(std::move(ruleParams));
+        }
+        else if (rule.rule_type == "response-byte-rate") {
+          DynBlockRulesGroup::DynBlockRule ruleParams(std::string(rule.comment), rule.action_duration, rule.rate, rule.warning_rate, rule.seconds, rule.action.empty() ? DNSAction::Action::None : DNSAction::typeFromString(std::string(rule.action)));
+          if (ruleParams.d_action == DNSAction::Action::SetTag && !rule.tag_name.empty()) {
+            ruleParams.d_tagSettings = std::make_shared<DynBlock::TagSettings>();
+            ruleParams.d_tagSettings->d_name = std::string(rule.tag_name);
+            ruleParams.d_tagSettings->d_value = std::string(rule.tag_value);
+          }
+          dbrgObj->setResponseByteRate(std::move(ruleParams));
+        }
+        else if (rule.rule_type == "suffix-match") {
+          DynBlockRulesGroup::DynBlockRule ruleParams(std::string(rule.comment), rule.action_duration, 0, 0, rule.seconds, rule.action.empty() ? DNSAction::Action::None : DNSAction::typeFromString(std::string(rule.action)));
+          if (ruleParams.d_action == DNSAction::Action::SetTag && !rule.tag_name.empty()) {
+            ruleParams.d_tagSettings = std::make_shared<DynBlock::TagSettings>();
+            ruleParams.d_tagSettings->d_name = std::string(rule.tag_name);
+            ruleParams.d_tagSettings->d_value = std::string(rule.tag_value);
+          }
+          DynBlockRulesGroup::smtVisitor_t visitor;
+          getOptionalLuaFunction<DynBlockRulesGroup::smtVisitor_t>(visitor, rule.visitor_function);
+          dbrgObj->setSuffixMatchRule(std::move(ruleParams), std::move(visitor));
+        }
+        else if (rule.rule_type == "suffix-match-ffi") {
+          DynBlockRulesGroup::DynBlockRule ruleParams(std::string(rule.comment), rule.action_duration, 0, 0, rule.seconds, rule.action.empty() ? DNSAction::Action::None : DNSAction::typeFromString(std::string(rule.action)));
+          if (ruleParams.d_action == DNSAction::Action::SetTag && !rule.tag_name.empty()) {
+            ruleParams.d_tagSettings = std::make_shared<DynBlock::TagSettings>();
+            ruleParams.d_tagSettings->d_name = std::string(rule.tag_name);
+            ruleParams.d_tagSettings->d_value = std::string(rule.tag_value);
+          }
+          dnsdist_ffi_stat_node_visitor_t visitor;
+          getOptionalLuaFunction<dnsdist_ffi_stat_node_visitor_t>(visitor, rule.visitor_function);
+          dbrgObj->setSuffixMatchRuleFFI(std::move(ruleParams), std::move(visitor));
+        }
+      }
+      dnsdist::DynamicBlocks::registerGroup(dbrgObj);
+    }
+
+    if (!globalConfig.tuning.tcp.fast_open_key.empty()) {
+      std::vector<uint32_t> key(4);
+      auto ret = sscanf(globalConfig.tuning.tcp.fast_open_key.c_str(), "%" SCNx32 "-%" SCNx32 "-%" SCNx32 "-%" SCNx32, &key.at(0), &key.at(1), &key.at(2), &key.at(3));
+      if (ret < 0 || static_cast<size_t>(ret) != key.size()) {
+        throw std::runtime_error("Invalid value passed to tuning.tcp.fast_open_key!\n");
+      }
+      dnsdist::configuration::updateImmutableConfiguration([&key](dnsdist::configuration::ImmutableConfiguration& config) {
+        config.d_tcpFastOpenKey = std::move(key);
+      });
+    }
+
+    if (!globalConfig.general.capabilities_to_retain.empty()) {
+      dnsdist::configuration::updateImmutableConfiguration([capabilities = globalConfig.general.capabilities_to_retain](dnsdist::configuration::ImmutableConfiguration& config) {
+        for (const auto& capability : capabilities) {
+          config.d_capabilitiesToRetain.emplace(std::string(capability));
+        }
+      });
+    }
+
+    for (const auto& cache : globalConfig.packet_caches) {
+      auto packetCacheObj = std::make_shared<DNSDistPacketCache>(cache.size, cache.max_ttl, cache.min_ttl, cache.temporary_failure_ttl, cache.max_negative_ttl, cache.stale_ttl, cache.dont_age, cache.shards, cache.deferrable_insert_lock, cache.parse_ecs);
+
+      packetCacheObj->setKeepStaleData(cache.keep_stale_data);
+      std::unordered_set<uint16_t> optionsToSkip{EDNSOptionCode::COOKIE};
+
+      for (const auto& option : cache.options_to_skip) {
+        optionsToSkip.insert(pdns::checked_stoi<uint16_t>(std::string(option)));
+      }
+
+      if (cache.cookie_hashing) {
+        optionsToSkip.erase(EDNSOptionCode::COOKIE);
+      }
+
+      packetCacheObj->setSkippedOptions(optionsToSkip);
+      if (cache.maximum_entry_size >= sizeof(dnsheader)) {
+        packetCacheObj->setMaximumEntrySize(cache.maximum_entry_size);
+      }
+
+      registerType<DNSDistPacketCache>(packetCacheObj, cache.name);
+    }
+
+    for (const auto& policy : globalConfig.load_balancing_policies.custom_policies) {
+      if (policy.ffi) {
+        if (policy.per_thread) {
+          auto policyObj = std::make_shared<ServerPolicy>(std::string(policy.name), std::string(policy.function));
+          registerType<ServerPolicy>(policyObj, policy.name);
+        }
+        else {
+          ServerPolicy::ffipolicyfunc_t function;
+
+          if (!getOptionalLuaFunction<ServerPolicy::ffipolicyfunc_t>(function, policy.function)) {
+            throw std::runtime_error("Custom FFI load-balancing policy '" + std::string(policy.name) + "' is referring to a non-existent Lua function '" + std::string(policy.function) + "'");
+          }
+          auto policyObj = std::make_shared<ServerPolicy>(std::string(policy.name), std::move(function));
+          registerType<ServerPolicy>(policyObj, policy.name);
+        }
+      }
+      else {
+        ServerPolicy::policyfunc_t function;
+        if (!getOptionalLuaFunction<ServerPolicy::policyfunc_t>(function, policy.function)) {
+          throw std::runtime_error("Custom load-balancing policy '" + std::string(policy.name) + "' is referring to a non-existent Lua function '" + std::string(policy.function) + "'");
+        }
+        auto policyObj = std::make_shared<ServerPolicy>(std::string(policy.name), std::move(function), true);
+        registerType<ServerPolicy>(policyObj, policy.name);
+      }
+    }
+
+    for (const auto& pool : globalConfig.pools) {
+      std::shared_ptr<ServerPool> poolObj = createPoolIfNotExists(std::string(pool.name));
+      if (!pool.packet_cache.empty()) {
+        poolObj->packetCache = getRegisteredTypeByName<DNSDistPacketCache>(pool.packet_cache);
+      }
+      if (!pool.policy.empty()) {
+        poolObj->policy = getRegisteredTypeByName<ServerPolicy>(pool.policy);
+      }
+    }
+
+    dnsdist::configuration::updateImmutableConfiguration([&globalConfig](dnsdist::configuration::ImmutableConfiguration& config) {
+      convertImmutableFlatSettingsFromRust(globalConfig, config);
+    });
+
+    dnsdist::configuration::updateRuntimeConfiguration([&globalConfig](dnsdist::configuration::RuntimeConfiguration& config) {
+      convertRuntimeFlatSettingsFromRust(globalConfig, config);
+    });
+
+    dnsdist::configuration::updateRuntimeConfiguration([&globalConfig](dnsdist::configuration::RuntimeConfiguration& config) {
+      for (const auto& rule : globalConfig.query_rules) {
+        boost::uuids::uuid ruleUniqueID = rule.uuid.empty() ? getUniqueID() : getUniqueID(std::string(rule.uuid));
+        dnsdist::rules::add(config.d_ruleChains, dnsdist::rules::RuleChain::Rules, std::move(rule.selector.selector->d_rule), rule.action.action->d_action, std::string(rule.name), ruleUniqueID, 0);
+      }
+
+      for (const auto& rule : globalConfig.cache_miss_rules) {
+        boost::uuids::uuid ruleUniqueID = rule.uuid.empty() ? getUniqueID() : getUniqueID(std::string(rule.uuid));
+        dnsdist::rules::add(config.d_ruleChains, dnsdist::rules::RuleChain::CacheMissRules, std::move(rule.selector.selector->d_rule), rule.action.action->d_action, std::string(rule.name), ruleUniqueID, 0);
+      }
+
+      for (const auto& rule : globalConfig.response_rules) {
+        boost::uuids::uuid ruleUniqueID = rule.uuid.empty() ? getUniqueID() : getUniqueID(std::string(rule.uuid));
+        dnsdist::rules::add(config.d_ruleChains, dnsdist::rules::ResponseRuleChain::ResponseRules, std::move(rule.selector.selector->d_rule), rule.action.action->d_action, std::string(rule.name), ruleUniqueID, 0);
+      }
+
+      for (const auto& rule : globalConfig.cache_hit_response_rules) {
+        boost::uuids::uuid ruleUniqueID = rule.uuid.empty() ? getUniqueID() : getUniqueID(std::string(rule.uuid));
+        dnsdist::rules::add(config.d_ruleChains, dnsdist::rules::ResponseRuleChain::CacheHitResponseRules, std::move(rule.selector.selector->d_rule), rule.action.action->d_action, std::string(rule.name), ruleUniqueID, 0);
+      }
+
+      for (const auto& rule : globalConfig.cache_inserted_response_rules) {
+        boost::uuids::uuid ruleUniqueID = rule.uuid.empty() ? getUniqueID() : getUniqueID(std::string(rule.uuid));
+        dnsdist::rules::add(config.d_ruleChains, dnsdist::rules::ResponseRuleChain::CacheInsertedResponseRules, std::move(rule.selector.selector->d_rule), rule.action.action->d_action, std::string(rule.name), ruleUniqueID, 0);
+      }
+
+      for (const auto& rule : globalConfig.self_answered_response_rules) {
+        boost::uuids::uuid ruleUniqueID = rule.uuid.empty() ? getUniqueID() : getUniqueID(std::string(rule.uuid));
+        dnsdist::rules::add(config.d_ruleChains, dnsdist::rules::ResponseRuleChain::SelfAnsweredResponseRules, std::move(rule.selector.selector->d_rule), rule.action.action->d_action, std::string(rule.name), ruleUniqueID, 0);
+      }
+
+      for (const auto& rule : globalConfig.xfr_response_rules) {
+        boost::uuids::uuid ruleUniqueID = rule.uuid.empty() ? getUniqueID() : getUniqueID(std::string(rule.uuid));
+        dnsdist::rules::add(config.d_ruleChains, dnsdist::rules::ResponseRuleChain::XFRResponseRules, std::move(rule.selector.selector->d_rule), rule.action.action->d_action, std::string(rule.name), ruleUniqueID, 0);
+      }
+    });
+
+    s_registeredTypesMap.lock()->clear();
+    return true;
+  }
+  catch (const ::rust::Error& exp) {
+    errlog("Rust error while opening YAML file %s: %s", fileName, exp.what());
+  }
+  catch (const std::exception& exp) {
+    errlog("C++ error while opening YAML file %s: %s", fileName, exp.what());
+  }
+  s_registeredTypesMap.lock()->clear();
+  return false;
+#else
+  (void)fileName;
+  throw std::runtime_error("Unsupported YAML configuration");
+#endif /* HAVE_YAML_CONFIGURATION */
+}
+}
+
+#if defined(HAVE_YAML_CONFIGURATION)
+namespace dnsdist::rust::settings
+{
+
+static std::shared_ptr<DNSSelector> newDNSSelector(std::shared_ptr<DNSRule>&& rule, const ::rust::String& name)
+{
+  auto selector = std::make_shared<DNSSelector>();
+  selector->d_name = std::string(name);
+  selector->d_rule = std::move(rule);
+  dnsdist::configuration::yaml::registerType(selector, name);
+  return selector;
+}
+
+static std::shared_ptr<DNSActionWrapper> newDNSActionWrapper(std::shared_ptr<DNSAction>&& action, const ::rust::String& name)
+{
+  auto wrapper = std::make_shared<DNSActionWrapper>();
+  wrapper->d_name = std::string(name);
+  wrapper->d_action = std::move(action);
+  dnsdist::configuration::yaml::registerType(wrapper, name);
+  return wrapper;
+}
+
+static std::shared_ptr<DNSResponseActionWrapper> newDNSResponseActionWrapper(std::shared_ptr<DNSResponseAction>&& action, const ::rust::String& name)
+{
+  auto wrapper = std::make_shared<DNSResponseActionWrapper>();
+  wrapper->d_name = std::string(name);
+  wrapper->d_action = std::move(action);
+  dnsdist::configuration::yaml::registerType(wrapper, name);
+  return wrapper;
+}
+
+static dnsdist::ResponseConfig convertResponseConfig(const dnsdist::rust::settings::ResponseConfig& rustConfig)
+{
+  dnsdist::ResponseConfig cppConfig{};
+  cppConfig.setAA = rustConfig.set_aa;
+  cppConfig.setAD = rustConfig.set_ad;
+  cppConfig.setRA = rustConfig.set_ra;
+  cppConfig.ttl = rustConfig.ttl;
+  return cppConfig;
+}
+
+static dnsdist::actions::SOAParams convertSOAParams(const dnsdist::rust::settings::SOAParams& soa)
+{
+  dnsdist::actions::SOAParams cppSOA{};
+  cppSOA.serial = soa.serial;
+  cppSOA.refresh = soa.refresh;
+  cppSOA.retry = soa.retry;
+  cppSOA.expire = soa.expire;
+  cppSOA.minimum = soa.minimum;
+  return cppSOA;
+}
+
+static std::vector<::SVCRecordParameters> convertSVCRecordParameters(const ::rust::Vec<dnsdist::rust::settings::SVCRecordParameters>& rustParameters)
+{
+  std::vector<::SVCRecordParameters> cppParameters;
+  for (const auto& rustConfig : rustParameters) {
+    ::SVCRecordParameters cppConfig{};
+    for (auto param : rustConfig.mandatory_params) {
+      cppConfig.mandatoryParams.insert(param);
+    }
+    for (const auto& alpn : rustConfig.alpns) {
+      cppConfig.alpns.emplace_back(alpn);
+    }
+    for (const auto& hint : rustConfig.ipv4_hints) {
+      cppConfig.ipv4hints.emplace_back(std::string(hint));
+    }
+    for (const auto& hint : rustConfig.ipv6_hints) {
+      cppConfig.ipv6hints.emplace_back(std::string(hint));
+    }
+    for (const auto& param : rustConfig.additional_params) {
+      cppConfig.additionalParams.emplace_back(param.key, std::string(param.value));
+    }
+    cppConfig.target = DNSName(std::string(rustConfig.target));
+    if (rustConfig.port != 0) {
+      cppConfig.port = rustConfig.port;
+    }
+    cppConfig.priority = rustConfig.priority;
+    cppConfig.noDefaultAlpn = rustConfig.no_default_alpn;
+
+    cppParameters.emplace_back(std::move(cppConfig));
+  }
+  return cppParameters;
+}
+
+template <class T>
+T convertLuaFunction(const ::rust::String& context, const ::rust::String& name)
+{
+  T function;
+  if (!dnsdist::configuration::yaml::getOptionalLuaFunction<T>(function, name)) {
+    throw std::runtime_error("Context '" + std::string(context) + "' is referring to a non-existent Lua function '" + std::string(name) + "'");
+  }
+  return function;
+}
+
+std::shared_ptr<DNSActionWrapper> getContinueAction(const ContinueActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getContinueAction(config.action.action->d_action);
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+
+std::shared_ptr<DNSActionWrapper> getSetProxyProtocolValuesAction(const SetProxyProtocolValuesActionConfiguration& config)
+{
+  std::vector<std::pair<uint8_t, std::string>> values;
+  for (const auto& value : config.values) {
+    values.emplace_back(value.key, std::string(value.value));
+  }
+  auto action = dnsdist::actions::getSetProxyProtocolValuesAction(values);
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+
+std::shared_ptr<DNSActionWrapper> getSpoofPacketAction(const SpoofPacketActionConfiguration& config)
+{
+  if (config.response.size() < sizeof(dnsheader)) {
+    throw std::runtime_error(std::string("SpoofPacketAction: given packet len is too small"));
+  }
+  auto action = dnsdist::actions::getSpoofAction(PacketBuffer(config.response.data(), config.response.data() + config.response.size()));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+
+std::shared_ptr<DNSActionWrapper> getSpoofAction(const SpoofActionConfiguration& config)
+{
+  std::vector<ComboAddress> addresses;
+  for (const auto& addr : config.ips) {
+    addresses.emplace_back(std::string(addr));
+  }
+  auto action = dnsdist::actions::getSpoofAction(addresses, convertResponseConfig(config.vars));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+
+std::shared_ptr<DNSActionWrapper> getSpoofCNAMEAction(const SpoofCNAMEActionConfiguration& config)
+{
+  auto cname = DNSName(std::string(config.cname));
+  auto action = dnsdist::actions::getSpoofAction(cname, convertResponseConfig(config.vars));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+
+std::shared_ptr<DNSActionWrapper> getSpoofRawAction(const SpoofRawActionConfiguration& config)
+{
+  std::vector<std::string> raws;
+  for (const auto& answer : config.answers) {
+    raws.emplace_back(answer);
+  }
+  std::optional<uint16_t> qtypeForAny;
+  if (!config.qtype_for_any.empty()) {
+    QType qtype;
+    qtype = std::string(config.qtype_for_any);
+    qtypeForAny = qtype.getCode();
+  }
+  auto action = dnsdist::actions::getSpoofAction(raws, qtypeForAny, convertResponseConfig(config.vars));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+
+std::shared_ptr<DNSResponseActionWrapper> getClearRecordTypesResponseAction(const ClearRecordTypesResponseActionConfiguration& config)
+{
+  std::unordered_set<QType> qtypes{};
+  for (const auto& type : config.types) {
+    qtypes.insert(type);
+  }
+  auto action = dnsdist::actions::getClearRecordTypesResponseAction(std::move(qtypes));
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
+
+std::shared_ptr<DNSResponseActionWrapper> getLimitTTLResponseAction(const LimitTTLResponseActionConfiguration& config)
+{
+  std::unordered_set<QType> capTypes;
+  for (const auto& type : config.types) {
+    capTypes.insert(QType(type));
+  }
+
+  auto action = dnsdist::actions::getLimitTTLResponseAction(config.min, config.max, capTypes);
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
+
+std::shared_ptr<DNSResponseActionWrapper> getSetMinTTLResponseAction(const SetMinTTLResponseActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getLimitTTLResponseAction(config.min);
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
+
+std::shared_ptr<DNSResponseActionWrapper> getSetMaxTTLResponseAction(const SetMaxTTLResponseActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getLimitTTLResponseAction(0, config.max);
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
+
+std::shared_ptr<DNSSelector> getQNameSuffixSelector(const QNameSuffixSelectorConfiguration& config)
+{
+  SuffixMatchNode suffixes;
+  for (const auto& suffix : config.suffixes) {
+    suffixes.add(std::string(suffix));
+  }
+  return newDNSSelector(dnsdist::selectors::getQNameSuffixSelector(suffixes, config.quiet), config.name);
+}
+
+std::shared_ptr<DNSSelector> getQNameSetSelector(const QNameSetSelectorConfiguration& config)
+{
+  DNSNameSet qnames;
+  for (const auto& name : config.qnames) {
+    qnames.emplace(std::string(name));
+  }
+  return newDNSSelector(dnsdist::selectors::getQNameSetSelector(qnames), config.name);
+}
+
+std::shared_ptr<DNSSelector> getQNameSelector(const QNameSelectorConfiguration& config)
+{
+  return newDNSSelector(dnsdist::selectors::getQNameSelector(DNSName(std::string(config.qname))), config.name);
+}
+
+std::shared_ptr<DNSSelector> getNetmaskGroupSelector(const NetmaskGroupSelectorConfiguration& config)
+{
+  std::shared_ptr<NetmaskGroup> nmg;
+  if (!config.netmask_group_name.empty()) {
+    nmg = dnsdist::configuration::yaml::getRegisteredTypeByName<NetmaskGroup>(std::string(config.netmask_group_name));
+  }
+  if (!nmg) {
+    nmg = std::make_shared<NetmaskGroup>();
+  }
+  for (const auto& netmask : config.netmasks) {
+    nmg->addMask(std::string(netmask));
+  }
+  auto selector = dnsdist::selectors::getNetmaskGroupSelector(*nmg, config.source, config.quiet);
+  return newDNSSelector(std::move(selector), config.name);
+}
+
+std::shared_ptr<DNSActionWrapper> getKeyValueStoreLookupAction(const KeyValueStoreLookupActionConfiguration& config)
+{
+  auto kvs = dnsdist::configuration::yaml::getRegisteredTypeByName<KeyValueStore>(std::string(config.kvs_name));
+  if (!kvs && !(dnsdist::configuration::yaml::s_inClientMode || dnsdist::configuration::yaml::s_inConfigCheckMode)) {
+    throw std::runtime_error("Unable to find the key-value store named '" + std::string(config.kvs_name) + "'");
+  }
+  auto lookupKey = dnsdist::configuration::yaml::getRegisteredTypeByName<KeyValueLookupKey>(std::string(config.lookup_key_name));
+  if (!lookupKey && !(dnsdist::configuration::yaml::s_inClientMode || dnsdist::configuration::yaml::s_inConfigCheckMode)) {
+    throw std::runtime_error("Unable to find the key-value lookup key named '" + std::string(config.lookup_key_name) + "'");
+  }
+  auto action = dnsdist::actions::getKeyValueStoreLookupAction(kvs, lookupKey, std::string(config.destination_tag));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+
+std::shared_ptr<DNSActionWrapper> getKeyValueStoreRangeLookupAction(const KeyValueStoreRangeLookupActionConfiguration& config)
+{
+  auto kvs = dnsdist::configuration::yaml::getRegisteredTypeByName<KeyValueStore>(std::string(config.kvs_name));
+  if (!kvs && !(dnsdist::configuration::yaml::s_inClientMode || dnsdist::configuration::yaml::s_inConfigCheckMode)) {
+    throw std::runtime_error("Unable to find the key-value store named '" + std::string(config.kvs_name) + "'");
+  }
+  auto lookupKey = dnsdist::configuration::yaml::getRegisteredTypeByName<KeyValueLookupKey>(std::string(config.lookup_key_name));
+  if (!lookupKey && !(dnsdist::configuration::yaml::s_inClientMode || dnsdist::configuration::yaml::s_inConfigCheckMode)) {
+    throw std::runtime_error("Unable to find the key-value lookup key named '" + std::string(config.lookup_key_name) + "'");
+  }
+  auto action = dnsdist::actions::getKeyValueStoreRangeLookupAction(kvs, lookupKey, std::string(config.destination_tag));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+
+std::shared_ptr<DNSSelector> getKeyValueStoreLookupSelector(const KeyValueStoreLookupSelectorConfiguration& config)
+{
+  auto kvs = dnsdist::configuration::yaml::getRegisteredTypeByName<KeyValueStore>(std::string(config.kvs_name));
+  if (!kvs && !(dnsdist::configuration::yaml::s_inClientMode || dnsdist::configuration::yaml::s_inConfigCheckMode)) {
+    throw std::runtime_error("Unable to find the key-value store named '" + std::string(config.kvs_name) + "'");
+  }
+  auto lookupKey = dnsdist::configuration::yaml::getRegisteredTypeByName<KeyValueLookupKey>(std::string(config.lookup_key_name));
+  if (!lookupKey && !(dnsdist::configuration::yaml::s_inClientMode || dnsdist::configuration::yaml::s_inConfigCheckMode)) {
+    throw std::runtime_error("Unable to find the key-value lookup key named '" + std::string(config.lookup_key_name) + "'");
+  }
+  auto selector = dnsdist::selectors::getKeyValueStoreLookupSelector(kvs, lookupKey);
+  return newDNSSelector(std::move(selector), config.name);
+}
+
+std::shared_ptr<DNSSelector> getKeyValueStoreRangeLookupSelector(const KeyValueStoreRangeLookupSelectorConfiguration& config)
+{
+  auto kvs = dnsdist::configuration::yaml::getRegisteredTypeByName<KeyValueStore>(std::string(config.kvs_name));
+  if (!kvs && !(dnsdist::configuration::yaml::s_inClientMode || dnsdist::configuration::yaml::s_inConfigCheckMode)) {
+    throw std::runtime_error("Unable to find the key-value store named '" + std::string(config.kvs_name) + "'");
+  }
+  auto lookupKey = dnsdist::configuration::yaml::getRegisteredTypeByName<KeyValueLookupKey>(std::string(config.lookup_key_name));
+  if (!lookupKey && !(dnsdist::configuration::yaml::s_inClientMode || dnsdist::configuration::yaml::s_inConfigCheckMode)) {
+    throw std::runtime_error("Unable to find the key-value lookup key named '" + std::string(config.lookup_key_name) + "'");
+  }
+  auto selector = dnsdist::selectors::getKeyValueStoreRangeLookupSelector(kvs, lookupKey);
+  return newDNSSelector(std::move(selector), config.name);
+}
+
+std::shared_ptr<DNSActionWrapper> getDnstapLogAction(const DnstapLogActionConfiguration& config)
+{
+#if defined(DISABLE_PROTOBUF) || !defined(HAVE_FSTRM)
+  throw std::runtime_error("Unable to create dnstap log action: dnstap support is not enabled");
+#else
+  auto logger = dnsdist::configuration::yaml::getRegisteredTypeByName<RemoteLoggerInterface>(std::string(config.logger_name));
+  if (!logger && !(dnsdist::configuration::yaml::s_inClientMode || dnsdist::configuration::yaml::s_inConfigCheckMode)) {
+    throw std::runtime_error("Unable to find the dnstap logger named '" + std::string(config.logger_name) + "'");
+  }
+  dnsdist::actions::DnstapAlterFunction alterFunc;
+  dnsdist::configuration::yaml::getOptionalLuaFunction<dnsdist::actions::DnstapAlterFunction>(alterFunc, config.alter_function);
+  auto action = dnsdist::actions::getDnstapLogAction(std::string(config.identity), logger, alterFunc);
+  return newDNSActionWrapper(std::move(action), config.name);
+#endif
+}
+
+std::shared_ptr<DNSResponseActionWrapper> getDnstapLogResponseAction(const DnstapLogResponseActionConfiguration& config)
+{
+#if defined(DISABLE_PROTOBUF) || !defined(HAVE_FSTRM)
+  throw std::runtime_error("Unable to create dnstap log action: dnstap support is not enabled");
+#else
+  auto logger = dnsdist::configuration::yaml::getRegisteredTypeByName<RemoteLoggerInterface>(std::string(config.logger_name));
+  if (!logger && !(dnsdist::configuration::yaml::s_inClientMode || dnsdist::configuration::yaml::s_inConfigCheckMode)) {
+    throw std::runtime_error("Unable to find the dnstap logger named '" + std::string(config.logger_name) + "'");
+  }
+  dnsdist::actions::DnstapAlterResponseFunction alterFunc;
+  dnsdist::configuration::yaml::getOptionalLuaFunction<dnsdist::actions::DnstapAlterResponseFunction>(alterFunc, config.alter_function);
+  auto action = dnsdist::actions::getDnstapLogResponseAction(std::string(config.identity), logger, alterFunc);
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+#endif
+}
+
+std::shared_ptr<DNSActionWrapper> getRemoteLogAction(const RemoteLogActionConfiguration& config)
+{
+#if defined(DISABLE_PROTOBUF)
+  throw std::runtime_error("Unable to create remote log action: protobuf support is disabled");
+#else
+  auto logger = dnsdist::configuration::yaml::getRegisteredTypeByName<RemoteLoggerInterface>(std::string(config.logger_name));
+  if (!logger && !(dnsdist::configuration::yaml::s_inClientMode || dnsdist::configuration::yaml::s_inConfigCheckMode)) {
+    throw std::runtime_error("Unable to find the protobuf logger named '" + std::string(config.logger_name) + "'");
+  }
+  dnsdist::actions::RemoteLogActionConfiguration actionConfig{};
+  actionConfig.logger = std::move(logger);
+  actionConfig.serverID = std::string(config.server_id);
+  actionConfig.ipEncryptKey = std::string(config.ip_encrypt_key);
+  for (const auto& meta : config.metas) {
+    actionConfig.metas.emplace_back(std::string(meta.key), ProtoBufMetaKey(std::string(meta.value)));
+  }
+  if (!config.export_tags.empty()) {
+    actionConfig.tagsToExport = std::unordered_set<std::string>();
+    for (const auto& tag : config.export_tags) {
+      actionConfig.tagsToExport->emplace(std::string(tag));
+    }
+  }
+  dnsdist::actions::ProtobufAlterFunction alterFunc;
+  if (dnsdist::configuration::yaml::getOptionalLuaFunction<dnsdist::actions::ProtobufAlterFunction>(alterFunc, config.alter_function)) {
+    actionConfig.alterQueryFunc = std::move(alterFunc);
+  }
+  auto action = dnsdist::actions::getRemoteLogAction(actionConfig);
+  return newDNSActionWrapper(std::move(action), config.name);
+#endif
+}
+
+std::shared_ptr<DNSResponseActionWrapper> getRemoteLogResponseAction(const RemoteLogResponseActionConfiguration& config)
+{
+#if defined(DISABLE_PROTOBUF)
+  throw std::runtime_error("Unable to create remote log action: protobuf support is disabled");
+#else
+  auto logger = dnsdist::configuration::yaml::getRegisteredTypeByName<RemoteLoggerInterface>(std::string(config.logger_name));
+  if (!logger && !(dnsdist::configuration::yaml::s_inClientMode || dnsdist::configuration::yaml::s_inConfigCheckMode)) {
+    throw std::runtime_error("Unable to find the protobuf logger named '" + std::string(config.logger_name) + "'");
+  }
+  dnsdist::actions::RemoteLogActionConfiguration actionConfig{};
+  actionConfig.logger = std::move(logger);
+  actionConfig.serverID = std::string(config.server_id);
+  actionConfig.ipEncryptKey = std::string(config.ip_encrypt_key);
+  actionConfig.includeCNAME = config.include_cname;
+  for (const auto& meta : config.metas) {
+    actionConfig.metas.emplace_back(std::string(meta.key), ProtoBufMetaKey(std::string(meta.value)));
+  }
+  if (!config.export_tags.empty()) {
+    actionConfig.tagsToExport = std::unordered_set<std::string>();
+    for (const auto& tag : config.export_tags) {
+      actionConfig.tagsToExport->emplace(std::string(tag));
+    }
+  }
+  if (!config.export_extended_errors_to_meta.empty()) {
+    actionConfig.exportExtendedErrorsToMeta = std::string(config.export_extended_errors_to_meta);
+  }
+  dnsdist::actions::ProtobufAlterResponseFunction alterFunc;
+  if (dnsdist::configuration::yaml::getOptionalLuaFunction<dnsdist::actions::ProtobufAlterResponseFunction>(alterFunc, config.alter_function)) {
+    actionConfig.alterResponseFunc = std::move(alterFunc);
+  }
+  auto action = dnsdist::actions::getRemoteLogResponseAction(actionConfig);
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+#endif
+}
+
+void registerProtobufLogger(const ProtobufLoggerConfiguration& config)
+{
+#if defined(DISABLE_PROTOBUF)
+  throw std::runtime_error("Unable to create protobuf logger: protobuf support is disabled");
+#else
+  if (dnsdist::configuration::yaml::s_inClientMode || dnsdist::configuration::yaml::s_inConfigCheckMode) {
+    auto object = std::shared_ptr<RemoteLoggerInterface>(nullptr);
+    dnsdist::configuration::yaml::registerType<RemoteLoggerInterface>(object, config.name);
+    return;
+  }
+  auto object = std::shared_ptr<RemoteLoggerInterface>(std::make_shared<RemoteLogger>(ComboAddress(std::string(config.address)), config.timeout, config.max_queued_entries * 100, config.reconnect_wait_time, false));
+  dnsdist::configuration::yaml::registerType<RemoteLoggerInterface>(object, config.name);
+#endif
+}
+
+void registerDnstapLogger(const DnstapLoggerConfiguration& config)
+{
+#if defined(DISABLE_PROTOBUF) || !defined(HAVE_FSTRM)
+  throw std::runtime_error("Unable to create dnstap logger: dnstap support is disabled");
+#else
+  auto transport = boost::to_lower_copy(std::string(config.transport));
+  int family{0};
+  if (transport == "unix") {
+    family = AF_UNIX;
+  }
+  else if (transport == "tcp") {
+    family = AF_INET;
+  }
+  else {
+    throw std::runtime_error("Unsupport dnstap transport type '" + transport + "'");
+  }
+
+  if (dnsdist::configuration::yaml::s_inClientMode || dnsdist::configuration::yaml::s_inConfigCheckMode) {
+    auto object = std::shared_ptr<RemoteLoggerInterface>(nullptr);
+    dnsdist::configuration::yaml::registerType<RemoteLoggerInterface>(object, config.name);
+    return;
+  }
+
+  std::unordered_map<string, unsigned int> options;
+  options["bufferHint"] = config.buffer_hint;
+  options["flushTimeout"] = config.flush_timeout;
+  options["inputQueueSize"] = config.input_queue_size;
+  options["outputQueueSize"] = config.output_queue_size;
+  options["queueNotifyThreshold"] = config.queue_notify_threshold;
+  options["reopenInterval"] = config.reopen_interval;
+
+  auto object = std::shared_ptr<RemoteLoggerInterface>(std::make_shared<FrameStreamLogger>(family, std::string(config.address), false, options));
+  dnsdist::configuration::yaml::registerType<RemoteLoggerInterface>(object, config.name);
+#endif
+}
+
+void registerKVSObjects(const KeyValueStoresConfiguration& config)
+{
+  bool createObjects = !dnsdist::configuration::yaml::s_inClientMode && !dnsdist::configuration::yaml::s_inConfigCheckMode;
+#if defined(HAVE_LMDB)
+  for (const auto& lmdb : config.lmdb) {
+    auto store = createObjects ? std::shared_ptr<KeyValueStore>(std::make_shared<LMDBKVStore>(std::string(lmdb.file_name), std::string(lmdb.database_name), lmdb.no_lock)) : std::shared_ptr<KeyValueStore>();
+    dnsdist::configuration::yaml::registerType<KeyValueStore>(store, lmdb.name);
+  }
+#endif /* defined(HAVE_LMDB) */
+#if defined(HAVE_CDB)
+  for (const auto& cdb : config.cdb) {
+    auto store = createObjects ? std::shared_ptr<KeyValueStore>(std::make_shared<CDBKVStore>(std::string(cdb.file_name), cdb.refresh_delay)) : std::shared_ptr<KeyValueStore>();
+    dnsdist::configuration::yaml::registerType<KeyValueStore>(store, cdb.name);
+  }
+#endif /* defined(HAVE_CDB) */
+#if defined(HAVE_LMDB) || defined(HAVE_CDB)
+  for (const auto& key : config.lookup_keys.source_ip_keys) {
+    auto lookup = createObjects ? std::shared_ptr<KeyValueLookupKey>(std::make_shared<KeyValueLookupKeySourceIP>(key.v4_mask, key.v6_mask, key.include_port)) : std::shared_ptr<KeyValueLookupKey>();
+    dnsdist::configuration::yaml::registerType<KeyValueLookupKey>(lookup, key.name);
+  }
+  for (const auto& key : config.lookup_keys.qname_keys) {
+    auto lookup = createObjects ? std::shared_ptr<KeyValueLookupKey>(std::make_shared<KeyValueLookupKeyQName>(key.wire_format)) : std::shared_ptr<KeyValueLookupKey>();
+    dnsdist::configuration::yaml::registerType<KeyValueLookupKey>(lookup, key.name);
+  }
+  for (const auto& key : config.lookup_keys.suffix_keys) {
+    auto lookup = createObjects ? std::shared_ptr<KeyValueLookupKey>(std::make_shared<KeyValueLookupKeySuffix>(key.minimum_labels, key.wire_format)) : std::shared_ptr<KeyValueLookupKey>();
+    dnsdist::configuration::yaml::registerType<KeyValueLookupKey>(lookup, key.name);
+  }
+  for (const auto& key : config.lookup_keys.tag_keys) {
+    auto lookup = createObjects ? std::shared_ptr<KeyValueLookupKey>(std::make_shared<KeyValueLookupKeyTag>(std::string(key.tag))) : std::shared_ptr<KeyValueLookupKey>();
+    dnsdist::configuration::yaml::registerType<KeyValueLookupKey>(lookup, key.name);
+  }
+#endif /* defined(HAVE_LMDB) || defined(HAVE_CDB) */
+}
+
+std::shared_ptr<DNSSelector> getAndSelector(const AndSelectorConfiguration& config)
+{
+  std::vector<std::shared_ptr<DNSRule>> selectors;
+  selectors.reserve(config.selectors.size());
+  for (const auto& subSelector : config.selectors) {
+    selectors.emplace_back(subSelector.selector->d_rule);
+  }
+  auto selector = dnsdist::selectors::getAndSelector(selectors);
+  return newDNSSelector(std::move(selector), config.name);
+}
+
+std::shared_ptr<DNSSelector> getOrSelector(const OrSelectorConfiguration& config)
+{
+  std::vector<std::shared_ptr<DNSRule>> selectors;
+  selectors.reserve(config.selectors.size());
+  for (const auto& subSelector : config.selectors) {
+    selectors.emplace_back(subSelector.selector->d_rule);
+  }
+  auto selector = dnsdist::selectors::getOrSelector(selectors);
+  return newDNSSelector(std::move(selector), config.name);
+}
+
+std::shared_ptr<DNSSelector> getNotSelector(const NotSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getNotSelector(config.selector.selector->d_rule);
+  return newDNSSelector(std::move(selector), config.name);
+}
+
+std::shared_ptr<DNSSelector> getByNameSelector(const ByNameSelectorConfiguration& config)
+{
+  return dnsdist::configuration::yaml::getRegisteredTypeByName<DNSSelector>(config.selector_name);
+}
+
+#include "dnsdist-rust-bridge-actions-generated.cc"
+#include "dnsdist-rust-bridge-selectors-generated.cc"
+}
+#endif /* defined(HAVE_YAML_CONFIGURATION) */
diff --git a/pdns/dnsdistdist/dnsdist-configuration-yaml.hh b/pdns/dnsdistdist/dnsdist-configuration-yaml.hh
new file mode 100644 (file)
index 0000000..467b9df
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#pragma once
+
+#include "config.h"
+
+#include <string>
+
+namespace dnsdist::configuration::yaml
+{
+bool loadConfigurationFromFile(const std::string& fileName, bool isClient, bool configCheck);
+}
diff --git a/pdns/dnsdistdist/dnsdist-rust-bridge-actions-generated.cc b/pdns/dnsdistdist/dnsdist-rust-bridge-actions-generated.cc
new file mode 100644 (file)
index 0000000..3204c13
--- /dev/null
@@ -0,0 +1,236 @@
+// !! This file has been generated by dnsdist-settings-generator.py, do not edit by hand!!
+std::shared_ptr<DNSActionWrapper> getAllowAction(const AllowActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getAllowAction();
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getDelayAction(const DelayActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getDelayAction(config.msec);
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getDropAction(const DropActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getDropAction();
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getSetEDNSOptionAction(const SetEDNSOptionActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetEDNSOptionAction(config.code, std::string(config.data));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getERCodeAction(const ERCodeActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getERCodeAction(config.rcode, convertResponseConfig(config.vars));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getHTTPStatusAction(const HTTPStatusActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getHTTPStatusAction(config.status, PacketBuffer(config.body.data(), config.body.data() + config.body.size()), std::string(config.content_type), convertResponseConfig(config.vars));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getLogAction(const LogActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getLogAction(std::string(config.file_name), config.binary, config.append, config.buffered, config.verbose_only, config.include_timestamp);
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getLuaAction(const LuaActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getLuaAction(convertLuaFunction<dnsdist::actions::LuaActionFunction>("LuaActionConfiguration", config.function));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getLuaFFIAction(const LuaFFIActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getLuaFFIAction(convertLuaFunction<dnsdist::actions::LuaActionFFIFunction>("LuaFFIActionConfiguration", config.function));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getLuaFFIPerThreadAction(const LuaFFIPerThreadActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getLuaFFIPerThreadAction(std::string(config.code));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getNegativeAndSOAAction(const NegativeAndSOAActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getNegativeAndSOAAction(config.nxd, DNSName(std::string(config.zone)), config.ttl, DNSName(std::string(config.mname)), DNSName(std::string(config.rname)), convertSOAParams(config.soa_parameters), config.soa_in_authority, convertResponseConfig(config.vars));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getNoneAction(const NoneActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getNoneAction();
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getPoolAction(const PoolActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getPoolAction(std::string(config.pool_name), config.stop_processing);
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getQPSAction(const QPSActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getQPSAction(config.limit);
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getQPSPoolAction(const QPSPoolActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getQPSPoolAction(config.limit, std::string(config.pool_name), config.stop_processing);
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getRCodeAction(const RCodeActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getRCodeAction(config.rcode, convertResponseConfig(config.vars));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getSetAdditionalProxyProtocolValueAction(const SetAdditionalProxyProtocolValueActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetAdditionalProxyProtocolValueAction(config.proxy_type, std::string(config.value));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getSetDisableECSAction(const SetDisableECSActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetDisableECSAction();
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getSetDisableValidationAction(const SetDisableValidationActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetDisableValidationAction();
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getSetECSAction(const SetECSActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetECSAction(std::string(config.ipv4), std::string(config.ipv6));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getSetECSOverrideAction(const SetECSOverrideActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetECSOverrideAction(config.override_existing);
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getSetECSPrefixLengthAction(const SetECSPrefixLengthActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetECSPrefixLengthAction(config.ipv4, config.ipv6);
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getSetExtendedDNSErrorAction(const SetExtendedDNSErrorActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetExtendedDNSErrorAction(config.info_code, std::string(config.extra_text));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getSetMacAddrAction(const SetMacAddrActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetMacAddrAction(config.code);
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getSetMaxReturnedTTLAction(const SetMaxReturnedTTLActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetMaxReturnedTTLAction(config.max);
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getSetNoRecurseAction(const SetNoRecurseActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetNoRecurseAction();
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getSetSkipCacheAction(const SetSkipCacheActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetSkipCacheAction();
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getSetTagAction(const SetTagActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetTagAction(std::string(config.tag), std::string(config.value));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getSetTempFailureCacheTTLAction(const SetTempFailureCacheTTLActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetTempFailureCacheTTLAction(config.maxttl);
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getSNMPTrapAction(const SNMPTrapActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSNMPTrapAction(std::string(config.reason));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getSpoofSVCAction(const SpoofSVCActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSpoofSVCAction(convertSVCRecordParameters(config.parameters), convertResponseConfig(config.vars));
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getTCAction(const TCActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getTCAction();
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSActionWrapper> getTeeAction(const TeeActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getTeeAction(ComboAddress(std::string(config.rca)), ComboAddress(std::string(config.lca)), config.add_ecs, config.add_proxy_protocol);
+  return newDNSActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSResponseActionWrapper> getAllowResponseAction(const AllowResponseActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getAllowResponseAction();
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSResponseActionWrapper> getDelayResponseAction(const DelayResponseActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getDelayResponseAction(config.msec);
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSResponseActionWrapper> getDropResponseAction(const DropResponseActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getDropResponseAction();
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSResponseActionWrapper> getLogResponseAction(const LogResponseActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getLogResponseAction(std::string(config.file_name), config.append, config.buffered, config.verbose_only, config.include_timestamp);
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSResponseActionWrapper> getLuaResponseAction(const LuaResponseActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getLuaResponseAction(convertLuaFunction<dnsdist::actions::LuaResponseActionFunction>("LuaResponseActionConfiguration", config.function));
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSResponseActionWrapper> getLuaFFIResponseAction(const LuaFFIResponseActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getLuaFFIResponseAction(convertLuaFunction<dnsdist::actions::LuaResponseActionFFIFunction>("LuaFFIResponseActionConfiguration", config.function));
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSResponseActionWrapper> getLuaFFIPerThreadResponseAction(const LuaFFIPerThreadResponseActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getLuaFFIPerThreadResponseAction(std::string(config.code));
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSResponseActionWrapper> getSetExtendedDNSErrorResponseAction(const SetExtendedDNSErrorResponseActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetExtendedDNSErrorResponseAction(config.info_code, std::string(config.extra_text));
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSResponseActionWrapper> getSetMaxReturnedTTLResponseAction(const SetMaxReturnedTTLResponseActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetMaxReturnedTTLResponseAction(config.max);
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSResponseActionWrapper> getSetReducedTTLResponseAction(const SetReducedTTLResponseActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetReducedTTLResponseAction(config.percentage);
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSResponseActionWrapper> getSetSkipCacheResponseAction(const SetSkipCacheResponseActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetSkipCacheResponseAction();
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSResponseActionWrapper> getSetTagResponseAction(const SetTagResponseActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSetTagResponseAction(std::string(config.tag), std::string(config.value));
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSResponseActionWrapper> getSNMPTrapResponseAction(const SNMPTrapResponseActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getSNMPTrapResponseAction(std::string(config.reason));
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
+std::shared_ptr<DNSResponseActionWrapper> getTCResponseAction(const TCResponseActionConfiguration& config)
+{
+  auto action = dnsdist::actions::getTCResponseAction();
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}
diff --git a/pdns/dnsdistdist/dnsdist-rust-bridge-actions-generated.hh b/pdns/dnsdistdist/dnsdist-rust-bridge-actions-generated.hh
new file mode 100644 (file)
index 0000000..e628aad
--- /dev/null
@@ -0,0 +1,127 @@
+// !! This file has been generated by dnsdist-settings-generator.py, do not edit by hand!!
+struct AllowActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getAllowAction(const AllowActionConfiguration& config);
+struct ContinueActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getContinueAction(const ContinueActionConfiguration& config);
+struct DelayActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getDelayAction(const DelayActionConfiguration& config);
+struct DnstapLogActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getDnstapLogAction(const DnstapLogActionConfiguration& config);
+struct DropActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getDropAction(const DropActionConfiguration& config);
+struct SetEDNSOptionActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSetEDNSOptionAction(const SetEDNSOptionActionConfiguration& config);
+struct ERCodeActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getERCodeAction(const ERCodeActionConfiguration& config);
+struct HTTPStatusActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getHTTPStatusAction(const HTTPStatusActionConfiguration& config);
+struct KeyValueStoreLookupActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getKeyValueStoreLookupAction(const KeyValueStoreLookupActionConfiguration& config);
+struct KeyValueStoreRangeLookupActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getKeyValueStoreRangeLookupAction(const KeyValueStoreRangeLookupActionConfiguration& config);
+struct LogActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getLogAction(const LogActionConfiguration& config);
+struct LuaActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getLuaAction(const LuaActionConfiguration& config);
+struct LuaFFIActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getLuaFFIAction(const LuaFFIActionConfiguration& config);
+struct LuaFFIPerThreadActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getLuaFFIPerThreadAction(const LuaFFIPerThreadActionConfiguration& config);
+struct NegativeAndSOAActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getNegativeAndSOAAction(const NegativeAndSOAActionConfiguration& config);
+struct NoneActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getNoneAction(const NoneActionConfiguration& config);
+struct PoolActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getPoolAction(const PoolActionConfiguration& config);
+struct QPSActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getQPSAction(const QPSActionConfiguration& config);
+struct QPSPoolActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getQPSPoolAction(const QPSPoolActionConfiguration& config);
+struct RCodeActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getRCodeAction(const RCodeActionConfiguration& config);
+struct RemoteLogActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getRemoteLogAction(const RemoteLogActionConfiguration& config);
+struct SetAdditionalProxyProtocolValueActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSetAdditionalProxyProtocolValueAction(const SetAdditionalProxyProtocolValueActionConfiguration& config);
+struct SetDisableECSActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSetDisableECSAction(const SetDisableECSActionConfiguration& config);
+struct SetDisableValidationActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSetDisableValidationAction(const SetDisableValidationActionConfiguration& config);
+struct SetECSActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSetECSAction(const SetECSActionConfiguration& config);
+struct SetECSOverrideActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSetECSOverrideAction(const SetECSOverrideActionConfiguration& config);
+struct SetECSPrefixLengthActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSetECSPrefixLengthAction(const SetECSPrefixLengthActionConfiguration& config);
+struct SetExtendedDNSErrorActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSetExtendedDNSErrorAction(const SetExtendedDNSErrorActionConfiguration& config);
+struct SetMacAddrActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSetMacAddrAction(const SetMacAddrActionConfiguration& config);
+struct SetMaxReturnedTTLActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSetMaxReturnedTTLAction(const SetMaxReturnedTTLActionConfiguration& config);
+struct SetNoRecurseActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSetNoRecurseAction(const SetNoRecurseActionConfiguration& config);
+struct SetProxyProtocolValuesActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSetProxyProtocolValuesAction(const SetProxyProtocolValuesActionConfiguration& config);
+struct SetSkipCacheActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSetSkipCacheAction(const SetSkipCacheActionConfiguration& config);
+struct SetTagActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSetTagAction(const SetTagActionConfiguration& config);
+struct SetTempFailureCacheTTLActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSetTempFailureCacheTTLAction(const SetTempFailureCacheTTLActionConfiguration& config);
+struct SNMPTrapActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSNMPTrapAction(const SNMPTrapActionConfiguration& config);
+struct SpoofActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSpoofAction(const SpoofActionConfiguration& config);
+struct SpoofCNAMEActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSpoofCNAMEAction(const SpoofCNAMEActionConfiguration& config);
+struct SpoofPacketActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSpoofPacketAction(const SpoofPacketActionConfiguration& config);
+struct SpoofRawActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSpoofRawAction(const SpoofRawActionConfiguration& config);
+struct SpoofSVCActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getSpoofSVCAction(const SpoofSVCActionConfiguration& config);
+struct TCActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getTCAction(const TCActionConfiguration& config);
+struct TeeActionConfiguration;
+std::shared_ptr<DNSActionWrapper> getTeeAction(const TeeActionConfiguration& config);
+struct AllowResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getAllowResponseAction(const AllowResponseActionConfiguration& config);
+struct ClearRecordTypesResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getClearRecordTypesResponseAction(const ClearRecordTypesResponseActionConfiguration& config);
+struct DelayResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getDelayResponseAction(const DelayResponseActionConfiguration& config);
+struct DnstapLogResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getDnstapLogResponseAction(const DnstapLogResponseActionConfiguration& config);
+struct DropResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getDropResponseAction(const DropResponseActionConfiguration& config);
+struct LimitTTLResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getLimitTTLResponseAction(const LimitTTLResponseActionConfiguration& config);
+struct LogResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getLogResponseAction(const LogResponseActionConfiguration& config);
+struct LuaResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getLuaResponseAction(const LuaResponseActionConfiguration& config);
+struct LuaFFIResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getLuaFFIResponseAction(const LuaFFIResponseActionConfiguration& config);
+struct LuaFFIPerThreadResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getLuaFFIPerThreadResponseAction(const LuaFFIPerThreadResponseActionConfiguration& config);
+struct RemoteLogResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getRemoteLogResponseAction(const RemoteLogResponseActionConfiguration& config);
+struct SetExtendedDNSErrorResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getSetExtendedDNSErrorResponseAction(const SetExtendedDNSErrorResponseActionConfiguration& config);
+struct SetMaxReturnedTTLResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getSetMaxReturnedTTLResponseAction(const SetMaxReturnedTTLResponseActionConfiguration& config);
+struct SetMaxTTLResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getSetMaxTTLResponseAction(const SetMaxTTLResponseActionConfiguration& config);
+struct SetMinTTLResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getSetMinTTLResponseAction(const SetMinTTLResponseActionConfiguration& config);
+struct SetReducedTTLResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getSetReducedTTLResponseAction(const SetReducedTTLResponseActionConfiguration& config);
+struct SetSkipCacheResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getSetSkipCacheResponseAction(const SetSkipCacheResponseActionConfiguration& config);
+struct SetTagResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getSetTagResponseAction(const SetTagResponseActionConfiguration& config);
+struct SNMPTrapResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getSNMPTrapResponseAction(const SNMPTrapResponseActionConfiguration& config);
+struct TCResponseActionConfiguration;
+std::shared_ptr<DNSResponseActionWrapper> getTCResponseAction(const TCResponseActionConfiguration& config);
diff --git a/pdns/dnsdistdist/dnsdist-rust-bridge-selectors-generated.cc b/pdns/dnsdistdist/dnsdist-rust-bridge-selectors-generated.cc
new file mode 100644 (file)
index 0000000..8ae735d
--- /dev/null
@@ -0,0 +1,171 @@
+// !! This file has been generated by dnsdist-settings-generator.py, do not edit by hand!!
+std::shared_ptr<DNSSelector> getAllSelector(const AllSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getAllSelector();
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getDNSSECSelector(const DNSSECSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getDNSSECSelector();
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getDSTPortSelector(const DSTPortSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getDSTPortSelector(config.port);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getEDNSOptionSelector(const EDNSOptionSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getEDNSOptionSelector(config.option_code);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getEDNSVersionSelector(const EDNSVersionSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getEDNSVersionSelector(config.version);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getERCodeSelector(const ERCodeSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getERCodeSelector(config.rcode);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getHTTPHeaderSelector(const HTTPHeaderSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getHTTPHeaderSelector(std::string(config.header), std::string(config.expression));
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getHTTPPathSelector(const HTTPPathSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getHTTPPathSelector(std::string(config.path));
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getHTTPPathRegexSelector(const HTTPPathRegexSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getHTTPPathRegexSelector(std::string(config.expression));
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getLuaSelector(const LuaSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getLuaSelector(convertLuaFunction<dnsdist::selectors::LuaSelectorFunction>("LuaSelectorConfiguration", config.function));
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getLuaFFISelector(const LuaFFISelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getLuaFFISelector(convertLuaFunction<dnsdist::selectors::LuaSelectorFFIFunction>("LuaFFISelectorConfiguration", config.function));
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getLuaFFIPerThreadSelector(const LuaFFIPerThreadSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getLuaFFIPerThreadSelector(std::string(config.code));
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getMaxQPSSelector(const MaxQPSSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getMaxQPSSelector(config.qps, config.burst);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getMaxQPSIPSelector(const MaxQPSIPSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getMaxQPSIPSelector(config.qps, config.ipv4_mask, config.ipv6_mask, config.burst, config.expiration, config.cleanup_delay, config.scan_fraction, config.shards);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getOpcodeSelector(const OpcodeSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getOpcodeSelector(config.code);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getPayloadSizeSelector(const PayloadSizeSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getPayloadSizeSelector(std::string(config.comparison), config.size);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getPoolAvailableSelector(const PoolAvailableSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getPoolAvailableSelector(std::string(config.pool));
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getPoolOutstandingSelector(const PoolOutstandingSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getPoolOutstandingSelector(std::string(config.pool), config.max_outstanding);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getProbaSelector(const ProbaSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getProbaSelector(config.probability);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getProxyProtocolValueSelector(const ProxyProtocolValueSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getProxyProtocolValueSelector(config.option_type, std::string(config.option_value));
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getQClassSelector(const QClassSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getQClassSelector(std::string(config.qclass), config.numeric_value);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getQNameLabelsCountSelector(const QNameLabelsCountSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getQNameLabelsCountSelector(config.min_labels_count, config.max_labels_count);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getQNameWireLengthSelector(const QNameWireLengthSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getQNameWireLengthSelector(config.min, config.max);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getQTypeSelector(const QTypeSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getQTypeSelector(std::string(config.qtype), config.numeric_value);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getRCodeSelector(const RCodeSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getRCodeSelector(config.rcode);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getRDSelector(const RDSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getRDSelector();
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getRE2Selector(const RE2SelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getRE2Selector(std::string(config.expression));
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getRecordsCountSelector(const RecordsCountSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getRecordsCountSelector(config.section, config.minimum, config.maximum);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getRecordsTypeCountSelector(const RecordsTypeCountSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getRecordsTypeCountSelector(config.section, config.record_type, config.minimum, config.maximum);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getRegexSelector(const RegexSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getRegexSelector(std::string(config.expression));
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getSNISelector(const SNISelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getSNISelector(std::string(config.server_name));
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getTagSelector(const TagSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getTagSelector(std::string(config.tag), std::string(config.value));
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getTCPSelector(const TCPSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getTCPSelector(config.tcp);
+  return newDNSSelector(std::move(selector), config.name);
+}
+std::shared_ptr<DNSSelector> getTrailingDataSelector(const TrailingDataSelectorConfiguration& config)
+{
+  auto selector = dnsdist::selectors::getTrailingDataSelector();
+  return newDNSSelector(std::move(selector), config.name);
+}
diff --git a/pdns/dnsdistdist/dnsdist-rust-bridge-selectors-generated.hh b/pdns/dnsdistdist/dnsdist-rust-bridge-selectors-generated.hh
new file mode 100644 (file)
index 0000000..796c348
--- /dev/null
@@ -0,0 +1,89 @@
+// !! This file has been generated by dnsdist-settings-generator.py, do not edit by hand!!
+struct AllSelectorConfiguration;
+std::shared_ptr<DNSSelector> getAllSelector(const AllSelectorConfiguration& config);
+struct AndSelectorConfiguration;
+std::shared_ptr<DNSSelector> getAndSelector(const AndSelectorConfiguration& config);
+struct ByNameSelectorConfiguration;
+std::shared_ptr<DNSSelector> getByNameSelector(const ByNameSelectorConfiguration& config);
+struct DNSSECSelectorConfiguration;
+std::shared_ptr<DNSSelector> getDNSSECSelector(const DNSSECSelectorConfiguration& config);
+struct DSTPortSelectorConfiguration;
+std::shared_ptr<DNSSelector> getDSTPortSelector(const DSTPortSelectorConfiguration& config);
+struct EDNSOptionSelectorConfiguration;
+std::shared_ptr<DNSSelector> getEDNSOptionSelector(const EDNSOptionSelectorConfiguration& config);
+struct EDNSVersionSelectorConfiguration;
+std::shared_ptr<DNSSelector> getEDNSVersionSelector(const EDNSVersionSelectorConfiguration& config);
+struct ERCodeSelectorConfiguration;
+std::shared_ptr<DNSSelector> getERCodeSelector(const ERCodeSelectorConfiguration& config);
+struct HTTPHeaderSelectorConfiguration;
+std::shared_ptr<DNSSelector> getHTTPHeaderSelector(const HTTPHeaderSelectorConfiguration& config);
+struct HTTPPathSelectorConfiguration;
+std::shared_ptr<DNSSelector> getHTTPPathSelector(const HTTPPathSelectorConfiguration& config);
+struct HTTPPathRegexSelectorConfiguration;
+std::shared_ptr<DNSSelector> getHTTPPathRegexSelector(const HTTPPathRegexSelectorConfiguration& config);
+struct KeyValueStoreLookupSelectorConfiguration;
+std::shared_ptr<DNSSelector> getKeyValueStoreLookupSelector(const KeyValueStoreLookupSelectorConfiguration& config);
+struct KeyValueStoreRangeLookupSelectorConfiguration;
+std::shared_ptr<DNSSelector> getKeyValueStoreRangeLookupSelector(const KeyValueStoreRangeLookupSelectorConfiguration& config);
+struct LuaSelectorConfiguration;
+std::shared_ptr<DNSSelector> getLuaSelector(const LuaSelectorConfiguration& config);
+struct LuaFFISelectorConfiguration;
+std::shared_ptr<DNSSelector> getLuaFFISelector(const LuaFFISelectorConfiguration& config);
+struct LuaFFIPerThreadSelectorConfiguration;
+std::shared_ptr<DNSSelector> getLuaFFIPerThreadSelector(const LuaFFIPerThreadSelectorConfiguration& config);
+struct MaxQPSSelectorConfiguration;
+std::shared_ptr<DNSSelector> getMaxQPSSelector(const MaxQPSSelectorConfiguration& config);
+struct MaxQPSIPSelectorConfiguration;
+std::shared_ptr<DNSSelector> getMaxQPSIPSelector(const MaxQPSIPSelectorConfiguration& config);
+struct NetmaskGroupSelectorConfiguration;
+std::shared_ptr<DNSSelector> getNetmaskGroupSelector(const NetmaskGroupSelectorConfiguration& config);
+struct NotSelectorConfiguration;
+std::shared_ptr<DNSSelector> getNotSelector(const NotSelectorConfiguration& config);
+struct OpcodeSelectorConfiguration;
+std::shared_ptr<DNSSelector> getOpcodeSelector(const OpcodeSelectorConfiguration& config);
+struct OrSelectorConfiguration;
+std::shared_ptr<DNSSelector> getOrSelector(const OrSelectorConfiguration& config);
+struct PayloadSizeSelectorConfiguration;
+std::shared_ptr<DNSSelector> getPayloadSizeSelector(const PayloadSizeSelectorConfiguration& config);
+struct PoolAvailableSelectorConfiguration;
+std::shared_ptr<DNSSelector> getPoolAvailableSelector(const PoolAvailableSelectorConfiguration& config);
+struct PoolOutstandingSelectorConfiguration;
+std::shared_ptr<DNSSelector> getPoolOutstandingSelector(const PoolOutstandingSelectorConfiguration& config);
+struct ProbaSelectorConfiguration;
+std::shared_ptr<DNSSelector> getProbaSelector(const ProbaSelectorConfiguration& config);
+struct ProxyProtocolValueSelectorConfiguration;
+std::shared_ptr<DNSSelector> getProxyProtocolValueSelector(const ProxyProtocolValueSelectorConfiguration& config);
+struct QClassSelectorConfiguration;
+std::shared_ptr<DNSSelector> getQClassSelector(const QClassSelectorConfiguration& config);
+struct QNameSelectorConfiguration;
+std::shared_ptr<DNSSelector> getQNameSelector(const QNameSelectorConfiguration& config);
+struct QNameLabelsCountSelectorConfiguration;
+std::shared_ptr<DNSSelector> getQNameLabelsCountSelector(const QNameLabelsCountSelectorConfiguration& config);
+struct QNameSetSelectorConfiguration;
+std::shared_ptr<DNSSelector> getQNameSetSelector(const QNameSetSelectorConfiguration& config);
+struct QNameSuffixSelectorConfiguration;
+std::shared_ptr<DNSSelector> getQNameSuffixSelector(const QNameSuffixSelectorConfiguration& config);
+struct QNameWireLengthSelectorConfiguration;
+std::shared_ptr<DNSSelector> getQNameWireLengthSelector(const QNameWireLengthSelectorConfiguration& config);
+struct QTypeSelectorConfiguration;
+std::shared_ptr<DNSSelector> getQTypeSelector(const QTypeSelectorConfiguration& config);
+struct RCodeSelectorConfiguration;
+std::shared_ptr<DNSSelector> getRCodeSelector(const RCodeSelectorConfiguration& config);
+struct RDSelectorConfiguration;
+std::shared_ptr<DNSSelector> getRDSelector(const RDSelectorConfiguration& config);
+struct RE2SelectorConfiguration;
+std::shared_ptr<DNSSelector> getRE2Selector(const RE2SelectorConfiguration& config);
+struct RecordsCountSelectorConfiguration;
+std::shared_ptr<DNSSelector> getRecordsCountSelector(const RecordsCountSelectorConfiguration& config);
+struct RecordsTypeCountSelectorConfiguration;
+std::shared_ptr<DNSSelector> getRecordsTypeCountSelector(const RecordsTypeCountSelectorConfiguration& config);
+struct RegexSelectorConfiguration;
+std::shared_ptr<DNSSelector> getRegexSelector(const RegexSelectorConfiguration& config);
+struct SNISelectorConfiguration;
+std::shared_ptr<DNSSelector> getSNISelector(const SNISelectorConfiguration& config);
+struct TagSelectorConfiguration;
+std::shared_ptr<DNSSelector> getTagSelector(const TagSelectorConfiguration& config);
+struct TCPSelectorConfiguration;
+std::shared_ptr<DNSSelector> getTCPSelector(const TCPSelectorConfiguration& config);
+struct TrailingDataSelectorConfiguration;
+std::shared_ptr<DNSSelector> getTrailingDataSelector(const TrailingDataSelectorConfiguration& config);
diff --git a/pdns/dnsdistdist/dnsdist-rust-bridge.hh b/pdns/dnsdistdist/dnsdist-rust-bridge.hh
new file mode 100644 (file)
index 0000000..8ac4178
--- /dev/null
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <memory>
+#include <string>
+
+class DNSAction;
+class DNSResponseAction;
+class DNSRule;
+
+#include "rust/cxx.h"
+
+namespace dnsdist::rust::settings
+{
+
+struct DNSSelector
+{
+  std::shared_ptr<DNSRule> d_rule;
+  std::string d_name;
+};
+
+struct DNSActionWrapper
+{
+  std::shared_ptr<DNSAction> d_action;
+  std::string d_name;
+};
+
+struct DNSResponseActionWrapper
+{
+  std::shared_ptr<DNSResponseAction> d_action;
+  std::string d_name;
+};
+
+struct ProtobufLoggerConfiguration;
+struct DnstapLoggerConfiguration;
+struct KeyValueStoresConfiguration;
+
+void registerProtobufLogger(const ProtobufLoggerConfiguration& config);
+void registerDnstapLogger(const DnstapLoggerConfiguration& config);
+void registerKVSObjects(const KeyValueStoresConfiguration& config);
+
+#include "dnsdist-rust-bridge-actions-generated.hh"
+#include "dnsdist-rust-bridge-selectors-generated.hh"
+}
index 65595f3547abacc4ad67fd1e0493e3415e6d7cdf..ece3a114280fbf8b896c7c891da7b9f11ce005a5 100644 (file)
@@ -1,4 +1,23 @@
 EXTRA_DIST = \
+       dnsdist-configuration-yaml-items-generated-pre-in.cc \
+       dnsdist-configuration-yaml-items-generated.cc \
+       dnsdist-settings-generator.py \
+       rust-pre-in.rs \
+       rust-middle-in.rs \
+       rust-post-in.rs \
        rust/src/lib.rs
 
-all:
+BUILT_SOURCES=rust/src/lib.rs \
+       dnsdist-configuration-yaml-items-generated.cc
+
+all: rust/src/lib.rs dnsdist-configuration-yaml-items-generated.cc
+
+rust/src/lib.rs dnsdist-configuration-yaml-items-generated.cc: dnsdist-settings-generator.py ../dnsdist-settings-definitions.yml rust-pre-in.rs rust-middle-in.rs rust-post-in.rs dnsdist-configuration-yaml-items-generated-pre-in.cc ../dnsdist-actions-definitions.yml ../dnsdist-response-actions-definitions.yml ../dnsdist-selectors-definitions.yml
+       @if test "$(PYTHON)" = ":"; then echo "Settings definitions have changed, python is needed to regenerate the related settings files but python was not found. Please install python and re-run configure"; exit 1; fi
+       @if ! $(PYTHON) --version | grep -q "Python 3"; then echo $(PYTHON) should be at least version 3. Please install python 3 and re-run configure; exit 1; fi
+       $(MAKE) -C rust clean
+       (cd ${srcdir} && $(PYTHON) dnsdist-settings-generator.py ../dnsdist-settings-definitions.yml)
+
+clean-local:
+       rm -f dnsdist-configuration-yaml-items-generated.cc
+       rm -f rust/src/lib.rs
diff --git a/pdns/dnsdistdist/dnsdist-rust-lib/dnsdist-configuration-yaml-items-generated-pre-in.cc b/pdns/dnsdistdist/dnsdist-rust-lib/dnsdist-configuration-yaml-items-generated-pre-in.cc
new file mode 100644 (file)
index 0000000..bae85b6
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dnsdist-configuration.hh"
+#include "dnsdist-configuration-yaml.hh"
diff --git a/pdns/dnsdistdist/dnsdist-rust-lib/dnsdist-configuration-yaml-items-generated.cc b/pdns/dnsdistdist/dnsdist-rust-lib/dnsdist-configuration-yaml-items-generated.cc
new file mode 100644 (file)
index 0000000..697ee5b
--- /dev/null
@@ -0,0 +1,223 @@
+// !! This file has been generated by dnsdist-settings-generator.py, do not edit by hand!!
+// START INCLUDE ./dnsdist-configuration-yaml-items-generated-pre-in.cc
+/*
+ * This file is part of PowerDNS or dnsdist.
+ * Copyright -- PowerDNS.COM B.V. and its contributors
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * In addition, for the avoidance of any doubt, permission is granted to
+ * link this program with OpenSSL and to (re)distribute the binaries
+ * produced as the result of such linking.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "dnsdist-configuration.hh"
+#include "dnsdist-configuration-yaml.hh"
+// END INCLUDE ./dnsdist-configuration-yaml-items-generated-pre-in.cc
+#if defined(HAVE_YAML_CONFIGURATION)
+#include "rust/cxx.h"
+#include "rust/lib.rs.h"
+#include "dnsdist-configuration-yaml-internal.hh"
+
+namespace dnsdist::configuration::yaml
+{
+void convertRuntimeFlatSettingsFromRust(const dnsdist::rust::settings::GlobalConfiguration& yamlConfig, dnsdist::configuration::RuntimeConfiguration& config)
+{
+  if (yamlConfig.console.maximum_output_size != 10000000 && config.d_consoleOutputMsgMaxSize == 10000000) {
+    config.d_consoleOutputMsgMaxSize = yamlConfig.console.maximum_output_size;
+  }
+  if (yamlConfig.console.log_connections != true && config.d_logConsoleConnections == true) {
+    config.d_logConsoleConnections = yamlConfig.console.log_connections;
+  }
+  if (yamlConfig.edns_client_subnet.override_existing != false && config.d_ecsOverride == false) {
+    config.d_ecsOverride = yamlConfig.edns_client_subnet.override_existing;
+  }
+  if (yamlConfig.edns_client_subnet.source_prefix_v4 != 32 && config.d_ECSSourcePrefixV4 == 32) {
+    config.d_ECSSourcePrefixV4 = yamlConfig.edns_client_subnet.source_prefix_v4;
+  }
+  if (yamlConfig.edns_client_subnet.source_prefix_v6 != 56 && config.d_ECSSourcePrefixV6 == 56) {
+    config.d_ECSSourcePrefixV6 = yamlConfig.edns_client_subnet.source_prefix_v6;
+  }
+  if (yamlConfig.dynamic_rules_settings.purge_interval != 60 && config.d_dynBlocksPurgeInterval == 60) {
+    config.d_dynBlocksPurgeInterval = yamlConfig.dynamic_rules_settings.purge_interval;
+  }
+  if (yamlConfig.tuning.tcp.receive_timeout != 2 && config.d_tcpRecvTimeout == 2) {
+    config.d_tcpRecvTimeout = yamlConfig.tuning.tcp.receive_timeout;
+  }
+  if (yamlConfig.tuning.tcp.send_timeout != 2 && config.d_tcpSendTimeout == 2) {
+    config.d_tcpSendTimeout = yamlConfig.tuning.tcp.send_timeout;
+  }
+  if (yamlConfig.tuning.tcp.max_queries_per_connection != 0 && config.d_maxTCPQueriesPerConn == 0) {
+    config.d_maxTCPQueriesPerConn = yamlConfig.tuning.tcp.max_queries_per_connection;
+  }
+  if (yamlConfig.tuning.tcp.max_connection_duration != 0 && config.d_maxTCPConnectionDuration == 0) {
+    config.d_maxTCPConnectionDuration = yamlConfig.tuning.tcp.max_connection_duration;
+  }
+  if (yamlConfig.tuning.tls.outgoing_tickets_cache_cleanup_delay != 60 && config.d_tlsSessionCacheCleanupDelay == 60) {
+    config.d_tlsSessionCacheCleanupDelay = yamlConfig.tuning.tls.outgoing_tickets_cache_cleanup_delay;
+  }
+  if (yamlConfig.tuning.tls.outgoing_tickets_cache_validity != 600 && config.d_tlsSessionCacheSessionValidity == 600) {
+    config.d_tlsSessionCacheSessionValidity = yamlConfig.tuning.tls.outgoing_tickets_cache_validity;
+  }
+  if (yamlConfig.tuning.tls.max_outgoing_tickets_per_backend != 20 && config.d_tlsSessionCacheMaxSessionsPerBackend == 20) {
+    config.d_tlsSessionCacheMaxSessionsPerBackend = yamlConfig.tuning.tls.max_outgoing_tickets_per_backend;
+  }
+  if (yamlConfig.cache_settings.stale_entries_ttl != 0 && config.d_staleCacheEntriesTTL == 0) {
+    config.d_staleCacheEntriesTTL = yamlConfig.cache_settings.stale_entries_ttl;
+  }
+  if (yamlConfig.cache_settings.cleaning_delay != 60 && config.d_cacheCleaningDelay == 60) {
+    config.d_cacheCleaningDelay = yamlConfig.cache_settings.cleaning_delay;
+  }
+  if (yamlConfig.cache_settings.cleaning_percentage != 100 && config.d_cacheCleaningPercentage == 100) {
+    config.d_cacheCleaningPercentage = yamlConfig.cache_settings.cleaning_percentage;
+  }
+  if (yamlConfig.security_polling.polling_interval != 3600 && config.d_secPollInterval == 3600) {
+    config.d_secPollInterval = yamlConfig.security_polling.polling_interval;
+  }
+  if (yamlConfig.security_polling.suffix != "secpoll.powerdns.com." && config.d_secPollSuffix == "secpoll.powerdns.com.") {
+    config.d_secPollSuffix = std::string(yamlConfig.security_polling.suffix);
+  }
+  if (yamlConfig.general.edns_udp_payload_size_self_generated_answers != 1232 && config.d_payloadSizeSelfGenAnswers == 1232) {
+    config.d_payloadSizeSelfGenAnswers = yamlConfig.general.edns_udp_payload_size_self_generated_answers;
+  }
+  if (yamlConfig.general.add_edns_to_self_generated_answers != true && config.d_addEDNSToSelfGeneratedResponses == true) {
+    config.d_addEDNSToSelfGeneratedResponses = yamlConfig.general.add_edns_to_self_generated_answers;
+  }
+  if (yamlConfig.general.truncate_tc_answers != false && config.d_truncateTC == false) {
+    config.d_truncateTC = yamlConfig.general.truncate_tc_answers;
+  }
+  if (yamlConfig.general.fixup_case != false && config.d_fixupCase == false) {
+    config.d_fixupCase = yamlConfig.general.fixup_case;
+  }
+  if (yamlConfig.general.verbose != false && config.d_verbose == false) {
+    config.d_verbose = yamlConfig.general.verbose;
+  }
+  if (yamlConfig.general.verbose_health_checks != false && config.d_verboseHealthChecks == false) {
+    config.d_verboseHealthChecks = yamlConfig.general.verbose_health_checks;
+  }
+  if (yamlConfig.general.allow_empty_responses != false && config.d_allowEmptyResponse == false) {
+    config.d_allowEmptyResponse = yamlConfig.general.allow_empty_responses;
+  }
+  if (yamlConfig.general.drop_empty_queries != false && config.d_dropEmptyQueries == false) {
+    config.d_dropEmptyQueries = yamlConfig.general.drop_empty_queries;
+  }
+  if (yamlConfig.proxy_protocol.maximum_payload_size != 512 && config.d_proxyProtocolMaximumSize == 512) {
+    config.d_proxyProtocolMaximumSize = yamlConfig.proxy_protocol.maximum_payload_size;
+  }
+  if (yamlConfig.proxy_protocol.apply_acl_to_proxied_clients != false && config.d_applyACLToProxiedClients == false) {
+    config.d_applyACLToProxiedClients = yamlConfig.proxy_protocol.apply_acl_to_proxied_clients;
+  }
+  if (yamlConfig.load_balancing_policies.servfail_on_no_server != false && config.d_servFailOnNoPolicy == false) {
+    config.d_servFailOnNoPolicy = yamlConfig.load_balancing_policies.servfail_on_no_server;
+  }
+  if (yamlConfig.load_balancing_policies.round_robin_servfail_on_no_server != false && config.d_roundrobinFailOnNoServer == false) {
+    config.d_roundrobinFailOnNoServer = yamlConfig.load_balancing_policies.round_robin_servfail_on_no_server;
+  }
+}
+void convertImmutableFlatSettingsFromRust(const dnsdist::rust::settings::GlobalConfiguration& yamlConfig, dnsdist::configuration::ImmutableConfiguration& config)
+{
+  if (yamlConfig.console.max_concurrent_connections != 0 && config.d_consoleMaxConcurrentConnections == 0) {
+    config.d_consoleMaxConcurrentConnections = yamlConfig.console.max_concurrent_connections;
+  }
+  if (yamlConfig.ring_buffers.size != 10000 && config.d_ringsCapacity == 10000) {
+    config.d_ringsCapacity = yamlConfig.ring_buffers.size;
+  }
+  if (yamlConfig.ring_buffers.shards != 10 && config.d_ringsNumberOfShards == 10) {
+    config.d_ringsNumberOfShards = yamlConfig.ring_buffers.shards;
+  }
+  if (yamlConfig.ring_buffers.lock_retries != 5 && config.d_ringsNbLockTries == 5) {
+    config.d_ringsNbLockTries = yamlConfig.ring_buffers.lock_retries;
+  }
+  if (yamlConfig.ring_buffers.record_queries != true && config.d_ringsRecordQueries == true) {
+    config.d_ringsRecordQueries = yamlConfig.ring_buffers.record_queries;
+  }
+  if (yamlConfig.ring_buffers.record_responses != true && config.d_ringsRecordResponses == true) {
+    config.d_ringsRecordResponses = yamlConfig.ring_buffers.record_responses;
+  }
+  if (yamlConfig.tuning.tcp.worker_threads != 10 && config.d_maxTCPClientThreads == 10) {
+    config.d_maxTCPClientThreads = yamlConfig.tuning.tcp.worker_threads;
+  }
+  if (yamlConfig.tuning.tcp.max_queued_connections != 10000 && config.d_maxTCPQueuedConnections == 10000) {
+    config.d_maxTCPQueuedConnections = yamlConfig.tuning.tcp.max_queued_connections;
+  }
+  if (yamlConfig.tuning.tcp.internal_pipe_buffer_size != 1048576 && config.d_tcpInternalPipeBufferSize == 1048576) {
+    config.d_tcpInternalPipeBufferSize = yamlConfig.tuning.tcp.internal_pipe_buffer_size;
+  }
+  if (yamlConfig.tuning.tcp.outgoing_max_idle_time != 300 && config.d_outgoingTCPMaxIdleTime == 300) {
+    config.d_outgoingTCPMaxIdleTime = yamlConfig.tuning.tcp.outgoing_max_idle_time;
+  }
+  if (yamlConfig.tuning.tcp.outgoing_cleanup_interval != 60 && config.d_outgoingTCPCleanupInterval == 60) {
+    config.d_outgoingTCPCleanupInterval = yamlConfig.tuning.tcp.outgoing_cleanup_interval;
+  }
+  if (yamlConfig.tuning.tcp.outgoing_max_idle_connection_per_backend != 10 && config.d_outgoingTCPMaxIdlePerBackend == 10) {
+    config.d_outgoingTCPMaxIdlePerBackend = yamlConfig.tuning.tcp.outgoing_max_idle_connection_per_backend;
+  }
+  if (yamlConfig.tuning.tcp.max_connections_per_client != 0 && config.d_maxTCPConnectionsPerClient == 0) {
+    config.d_maxTCPConnectionsPerClient = yamlConfig.tuning.tcp.max_connections_per_client;
+  }
+  if (yamlConfig.tuning.udp.messages_per_round != 1 && config.d_udpVectorSize == 1) {
+    config.d_udpVectorSize = yamlConfig.tuning.udp.messages_per_round;
+  }
+  if (yamlConfig.tuning.udp.send_buffer_size != 0 && config.d_socketUDPSendBuffer == 0) {
+    config.d_socketUDPSendBuffer = yamlConfig.tuning.udp.send_buffer_size;
+  }
+  if (yamlConfig.tuning.udp.receive_buffer_size != 0 && config.d_socketUDPRecvBuffer == 0) {
+    config.d_socketUDPRecvBuffer = yamlConfig.tuning.udp.receive_buffer_size;
+  }
+  if (yamlConfig.tuning.udp.max_outstanding_per_backend != 65535 && config.d_maxUDPOutstanding == 65535) {
+    config.d_maxUDPOutstanding = yamlConfig.tuning.udp.max_outstanding_per_backend;
+  }
+  if (yamlConfig.tuning.udp.timeout != 2 && config.d_udpTimeout == 2) {
+    config.d_udpTimeout = yamlConfig.tuning.udp.timeout;
+  }
+  if (yamlConfig.tuning.udp.randomize_outgoing_sockets_to_backend != false && config.d_randomizeUDPSocketsToBackend == false) {
+    config.d_randomizeUDPSocketsToBackend = yamlConfig.tuning.udp.randomize_outgoing_sockets_to_backend;
+  }
+  if (yamlConfig.tuning.udp.randomize_ids_to_backend != false && config.d_randomizeIDsToBackend == false) {
+    config.d_randomizeIDsToBackend = yamlConfig.tuning.udp.randomize_ids_to_backend;
+  }
+  if (yamlConfig.tuning.doh.outgoing_worker_threads != 10 && config.d_outgoingDoHWorkers == 10) {
+    config.d_outgoingDoHWorkers = yamlConfig.tuning.doh.outgoing_worker_threads;
+  }
+  if (yamlConfig.tuning.doh.outgoing_max_idle_time != 300 && config.d_outgoingDoHMaxIdleTime == 300) {
+    config.d_outgoingDoHMaxIdleTime = yamlConfig.tuning.doh.outgoing_max_idle_time;
+  }
+  if (yamlConfig.tuning.doh.outgoing_cleanup_interval != 60 && config.d_outgoingDoHCleanupInterval == 60) {
+    config.d_outgoingDoHCleanupInterval = yamlConfig.tuning.doh.outgoing_cleanup_interval;
+  }
+  if (yamlConfig.tuning.doh.outgoing_max_idle_connection_per_backend != 10 && config.d_outgoingDoHMaxIdlePerBackend == 10) {
+    config.d_outgoingDoHMaxIdlePerBackend = yamlConfig.tuning.doh.outgoing_max_idle_connection_per_backend;
+  }
+  if (yamlConfig.snmp.enabled != false && config.d_snmpEnabled == false) {
+    config.d_snmpEnabled = yamlConfig.snmp.enabled;
+  }
+  if (yamlConfig.snmp.traps_enabled != false && config.d_snmpTrapsEnabled == false) {
+    config.d_snmpTrapsEnabled = yamlConfig.snmp.traps_enabled;
+  }
+  if (yamlConfig.snmp.daemon_socket != "" && config.d_snmpDaemonSocketPath == "") {
+    config.d_snmpDaemonSocketPath = std::string(yamlConfig.snmp.daemon_socket);
+  }
+  if (yamlConfig.load_balancing_policies.weighted_balancing_factor != 0.0 && config.d_weightedBalancingFactor == 0.0) {
+    config.d_weightedBalancingFactor = yamlConfig.load_balancing_policies.weighted_balancing_factor;
+  }
+  if (yamlConfig.load_balancing_policies.consistent_hashing_balancing_factor != 0.0 && config.d_consistentHashBalancingFactor == 0.0) {
+    config.d_consistentHashBalancingFactor = yamlConfig.load_balancing_policies.consistent_hashing_balancing_factor;
+  }
+  if (yamlConfig.load_balancing_policies.hash_perturbation != 0 && config.d_hashPerturbation == 0) {
+    config.d_hashPerturbation = yamlConfig.load_balancing_policies.hash_perturbation;
+  }
+}
+
+}
+#endif /* defined(HAVE_YAML_CONFIGURATION) */
diff --git a/pdns/dnsdistdist/dnsdist-rust-lib/dnsdist-settings-generator.py b/pdns/dnsdistdist/dnsdist-rust-lib/dnsdist-settings-generator.py
new file mode 100644 (file)
index 0000000..f9a7fe8
--- /dev/null
@@ -0,0 +1,812 @@
+#!/usr/bin/python3
+"""Load settings definitions and generates C++ and Rust code to handle them."""
+# 1/ Loads the settings definitions from
+# - dnsdist-settings-definitions.yml
+# and generates Rust structures and functions that are used to parse the
+# YAML settings and populate the Rust structures (via Serde):
+# rust/src/lib.rs
+# Note that some existing structures and functions present in
+# - rust-pre-in.rs
+# - rust-middle-in.rs
+# - rust-post-in.rs
+# are also included into the final rust/src/lib.rs file
+# Note that during the compilation of the Rust code to create the static
+# dnsdist_rust library, the cxx module also creates corresponding C++ structures
+# for interoperability
+# 2/ Creates methods to fill DNSdist's internal configuration structures
+# from the YAML parameters for all trivial values:
+# - dnsdist-configuration-yaml-items-generated.cc
+# 3/ Loads the action definitions from:
+# - dnsdist-actions-definitions.yml
+# - dnsdist-response-actions-definitions.yml
+# and generates C++ headers and code to create the wrappers
+# for these actions from the Rust structures:
+# - dnsdist-rust-bridge-actions-generated.hh
+# - dnsdist-rust-bridge-actions-generated.cc
+# 2/ Loads the selector definitions from:
+# - dnsdist-selectors-definitions.yml
+# - dnsdist-rust-bridge-selectors-generated.hh
+# - dnsdist-rust-bridge-selectors-generated.cc
+# and generates C++ headers and code to create the wrappers
+# for these selectors from the Rust structures:
+# The format of the definitions, in YAML, is a simple list of items.
+# Each item has a name and an optional list of parameters.
+# Parameters have a name, a type, and optionally a default value
+# Types are the Rust ones, converted to the C++ equivalent when needed
+# Default values are written as quoted strings, with the exception of the
+# special unquoted true value which means to use the default value for the
+# object type, which needs to exist.
+# Items can optionally have the following properties:
+# - 'skip-cpp' is not used by this script but is used by the dnsdist-rules-generator.py one, where it means that the corresponding C++ factory and Lua bindinds will not be generated, which is useful for objects taking parameters that cannot be directly mapped
+# - 'skip-rust' means that the C++ code to create the Rust-side version of an action or selector will not generated
+# - 'skip-serde' means that the Rust structure representing that action or selector in the YAML setting will not be directly created by Serde. It is used for selectors that reference another selector themselves, or actions referencing another action.
+# - 'lua-name' name of the Lua directive for this setting
+# - 'internal-field-name' name of the corresponding field in DNSdist's internal configuration structures, which is used to generate 'dnsdist-configuration-yaml-items-generated.cc'
+# - 'runtime-configurable' whether this setting can be set at runtime or can only be set at configuration time
+
+import os
+import re
+import sys
+import tempfile
+import yaml
+
+def quote(arg):
+    """Return a quoted string"""
+    return '"' + arg + '"'
+
+def get_vector_sub_type(rust_type):
+    return rust_type[4:-1]
+
+def is_vector_of(rust_type):
+    return rust_type.startswith('Vec<')
+
+def is_type_native(rust_type):
+    if is_vector_of(rust_type):
+        sub_type = get_vector_sub_type(rust_type)
+        return is_type_native(sub_type)
+    return rust_type in ['bool', 'u8', 'u16', 'u32', 'u64', 'f64', 'String']
+
+def is_value_rust_default(rust_type, value):
+    """Is a value the same as its corresponding Rust default?"""
+    if rust_type == 'bool':
+        return value == 'false'
+    if rust_type  in ('u8', 'u16', 'u32', 'u64'):
+        return value in (0, '0', '')
+    if rust_type == 'f64':
+        return value in ('0.0', 0.0)
+    if rust_type == 'String':
+        return value == ''
+    if rust_type == 'Vec<String>':
+        # FIXME
+        return True
+    return False
+
+def get_rust_field_name(name):
+    return name.replace('-', '_').lower()
+
+def get_rust_object_name(name):
+    object_name = ''
+    capitalize = True
+    for char in name:
+        if char == '-':
+            capitalize = True
+            continue
+        if capitalize:
+            char = char.upper()
+            capitalize = False
+        object_name += char
+
+    return object_name
+
+def gen_rust_vec_default_functions(name, type_name, def_value):
+    """Generate Rust code for the default handling of a vector for type_name"""
+    ret = f'// DEFAULT HANDLING for {name}\n'
+    ret += f'fn default_value_{name}() -> Vec<dnsdistsettings::{type_name}> {{\n'
+    ret += f'    let msg = "default value defined for `{name}\' should be valid YAML";'
+    ret += f'    let deserialized: Vec<dnsdistsettings::{type_name}> = serde_yaml::from_str({quote(def_value)}).expect(&msg);\n'
+    ret += '    deserialized\n'
+    ret += '}\n'
+    ret += f'fn default_value_equal_{name}(value: &Vec<dnsdistsettings::{type_name}>)'
+    ret += '-> bool {\n'
+    ret += f'    let def = default_value_{name}();\n'
+    ret += '    &def == value\n'
+    ret += '}\n\n'
+    return ret
+
+# Example snippet generated
+# fn default_value_general_query_local_address() -> Vec<String> {
+#    vec![String::from("0.0.0.0"), ]
+#}
+#fn default_value_equal_general_query_local_address(value: &Vec<String>) -> bool {
+#    let def = default_value_general_query_local_address();
+#    &def == value
+#}
+def gen_rust_stringvec_default_functions(default, name):
+    """Generate Rust code for the default handling of a vector for Strings"""
+    ret = f'// DEFAULT HANDLING for {name}\n'
+    ret += f'fn default_value_{name}() -> Vec<String> {{\n'
+    parts = re.split('[ \t,]+', default)
+    if len(parts) > 0:
+        ret += '    vec![\n'
+        for part in parts:
+            if part == '':
+                continue
+            ret += f'        String::from({quote(part)}),\n'
+        ret += '    ]\n'
+    else:
+        ret  += '    vec![]\n'
+    ret += '}\n'
+    ret += f'fn default_value_equal_{name}(value: &Vec<String>) -> bool {{\n'
+    ret += f'    let def = default_value_{name}();\n'
+    ret += '    &def == value\n'
+    ret += '}\n\n'
+    return ret
+
+def gen_rust_default_functions(rust_type, default, name):
+    """Generate Rust code for the default handling"""
+    if rust_type in ['Vec<String>']:
+        return gen_rust_stringvec_default_functions(default, name)
+    ret = f'// DEFAULT HANDLING for {name}\n'
+    ret += f'fn default_value_{name}() -> {rust_type} {{\n'
+    rustdef = quote(default)
+    ret += f"    String::from({rustdef})\n"
+    ret += '}\n'
+    if rust_type == 'String':
+        rust_type = 'str'
+    ret += f'fn default_value_equal_{name}(value: &{rust_type})'
+    ret += '-> bool {\n'
+    ret += f'    value == default_value_{name}()\n'
+    ret += '}\n\n'
+    return ret
+
+def write_rust_default_trait_impl(struct, skip_namespace=False):
+    """Generate Rust code for the default Trait for a structure"""
+    namespace = 'dnsdistsettings::' if not skip_namespace else ''
+    result = ''
+    result += f'impl Default for {namespace}{struct} {{\n'
+    result += '    fn default() -> Self {\n'
+    result += f'        let deserialized: {namespace}{struct} = serde_yaml::from_str("").unwrap();\n'
+    result += '        deserialized\n'
+    result += '    }\n'
+    result += '}\n\n'
+    return result
+
+def get_rust_serde_annotations(rust_type, default, rename, obj, field, default_functions):
+    rename_value = f'rename = "{rename}", ' if rename else ''
+    if default is None:
+        if not rename_value:
+            return ''
+        return f'#[serde({rename_value})]'
+    if default is True or is_value_rust_default(rust_type, default):
+        return f'#[serde({rename_value}default, skip_serializing_if = "crate::is_default")]'
+    type_upper = rust_type.capitalize()
+    if rust_type == 'bool':
+        return f'''#[serde({rename_value}default = "crate::{type_upper}::<{default}>::value", skip_serializing_if = "crate::if_true")]'''
+    if rust_type in ['String', 'Vec<String>']:
+        basename = obj + '_' + field
+        default_functions.append(gen_rust_default_functions(rust_type, default, basename))
+        return f'''#[serde({rename_value}default = "crate::default_value_{basename}", skip_serializing_if = "crate::default_value_equal_{basename}")]'''
+    return f'''#[serde({rename_value}default = "crate::{type_upper}::<{default}>::value", skip_serializing_if = "crate::{type_upper}::<{default}>::is_equal")]'''
+
+def get_converted_serde_type(rust_type):
+    if is_type_native(rust_type):
+        return rust_type
+
+    if is_vector_of(rust_type):
+        sub_type = get_vector_sub_type(rust_type)
+        if sub_type in ['Action', 'Selector']:
+            return rust_type
+        if sub_type in ['QueryRuleConfiguration', 'ResponseRuleConfiguration']:
+            return f'Vec<{sub_type}Serde>'
+        return f'Vec<dnsdistsettings::{sub_type}>'
+
+    return f'dnsdistsettings::{rust_type}'
+
+def get_rust_struct_fields_from_definition(name, keys, default_functions, indent_spaces, special_serde_object=False):
+    if not 'parameters' in keys:
+        return ''
+    output = ''
+    indent = ' '*indent_spaces
+    for parameter in keys['parameters']:
+        parameter_name = get_rust_field_name(parameter['name']) if not 'rename' in parameter else parameter['rename']
+        rust_type = parameter['type']
+        if 'rust-type' in parameter:
+            rust_type = parameter['rust-type']
+        if special_serde_object:
+            rust_type = get_converted_serde_type(rust_type)
+        else:
+            # cxx does not support Enums, so we have to convert them to opaque types
+            if rust_type == 'Action':
+                rust_type = 'SharedDNSAction'
+            elif rust_type == 'ResponseAction':
+                rust_type = 'SharedDNSResponseAction'
+            elif rust_type == 'Selector':
+                rust_type = 'SharedDNSSelector'
+            elif rust_type == 'Vec<Selector>':
+                rust_type = 'Vec<SharedDNSSelector>'
+        rename = parameter['name'] if parameter_name != parameter['name'] else None
+        if special_serde_object or not 'skip-serde' in keys or not keys['skip-serde']:
+            default_str = get_rust_serde_annotations(rust_type, parameter['default'] if 'default' in parameter else None, rename, get_rust_field_name(name), parameter_name, default_functions)
+            if default_str:
+                output += indent + default_str + '\n'
+        output += f'{indent}{parameter_name}: {rust_type},\n'
+
+    return output
+
+def get_rust_struct_from_definition(name, keys, default_functions, indent_spaces=4, special_serde_object=False):
+    if not 'parameters' in keys:
+        return ''
+    obj_name = get_rust_object_name(name)
+    indent = ' '*indent_spaces
+    output = ''
+    if special_serde_object or not 'skip-serde' in keys or not keys['skip-serde']:
+        output += f'''{indent}#[derive(Deserialize, Serialize, Debug, PartialEq)]
+{indent}#[serde(deny_unknown_fields)]
+'''
+    elif name == 'global':
+        output += f'{indent}#[derive(Default)]\n'
+
+    name_suffix = 'Serde' if name == 'global' and special_serde_object else ''
+    output += f'''{indent}struct {obj_name}Configuration{name_suffix} {{
+'''
+    indent_spaces += 4
+    indent = ' '*indent_spaces
+    output += get_rust_struct_fields_from_definition(name, keys, default_functions, indent_spaces, special_serde_object=special_serde_object)
+    output += '    }\n'
+    if special_serde_object or not 'skip-serde' in keys or not keys['skip-serde']:
+        default_functions.append(write_rust_default_trait_impl(f'{obj_name}Configuration{name_suffix}', special_serde_object))
+    return output
+
+def should_validate_type(rust_type):
+    if is_vector_of(rust_type):
+        sub_type = get_vector_sub_type(rust_type)
+        return should_validate_type(sub_type)
+    if rust_type in ['bool', 'u8', 'u16', 'u32', 'u64', 'f64', 'String']:
+        return False
+    if rust_type in ['Action', 'Selector', 'dnsdistsettings::SelectorsConfiguration']:
+        return False
+    return True
+
+def get_validation_for_field(field_name, rust_type):
+    if not should_validate_type(rust_type):
+        return ''
+    if not is_vector_of(rust_type):
+        return f'        self.{field_name}.validate()?;\n'
+
+    return f'''        for sub_type in &self.{field_name} {{
+        sub_type.validate()?;
+    }}
+'''
+
+def get_struct_validation_function_from_definition(name, parameters, special_serde_object=False):
+    if len(parameters) == 0:
+        return ''
+    namespace = 'dnsdistsettings::' if not special_serde_object else ''
+    suffix = 'Serde' if special_serde_object else ''
+    struct_name = get_rust_object_name(name)
+    output = f'''impl {namespace}{struct_name}Configuration{suffix} {{
+    fn validate(&self) -> Result<(), ValidationError> {{
+'''
+    for parameter in parameters:
+        field_name = get_rust_field_name(parameter['name']) if parameter['name'] != 'namespace' else 'name_space'
+        rust_type = parameter['type']
+        output += get_validation_for_field(field_name, rust_type)
+    output += '''        Ok(())
+    }
+}'''
+    return output
+
+def get_definitions_from_file(def_file):
+    with open(def_file, 'rt', encoding="utf-8") as fd:
+        definitions = yaml.safe_load(fd.read())
+        return definitions
+
+def include_file(out_fp, include_file_name):
+    with open(include_file_name, mode='r', encoding='utf-8') as in_fp:
+        out_fp.write(f'// START INCLUDE {include_file_name}\n')
+        out_fp.write(in_fp.read())
+        out_fp.write(f'// END INCLUDE {include_file_name}\n')
+
+def generate_flat_settings_for_cxx(definitions, out_file_path):
+    cxx_flat_settings_fp = get_temporary_file_for_generated_code(out_file_path)
+
+    include_file(cxx_flat_settings_fp, out_file_path + 'dnsdist-configuration-yaml-items-generated-pre-in.cc')
+
+    # first we do runtime-settable settings
+    cxx_flat_settings_fp.write('''#if defined(HAVE_YAML_CONFIGURATION)
+#include "rust/cxx.h"
+#include "rust/lib.rs.h"
+#include "dnsdist-configuration-yaml-internal.hh"
+
+namespace dnsdist::configuration::yaml
+{
+void convertRuntimeFlatSettingsFromRust(const dnsdist::rust::settings::GlobalConfiguration& yamlConfig, dnsdist::configuration::RuntimeConfiguration& config)
+{\n''')
+    for category_name, keys in definitions.items():
+        if not 'parameters' in keys:
+            continue
+
+        if 'category' in keys:
+            category_name = keys['category']
+        else:
+            category_name = get_rust_field_name(category_name)
+
+        for parameter in keys['parameters']:
+            if not 'internal-field-name' in parameter or not 'runtime-configurable' in parameter or not parameter['runtime-configurable']:
+                continue
+            internal_field_name = parameter['internal-field-name']
+            rust_field_name = get_rust_field_name(parameter['name']) if not 'rename' in parameter else parameter['rename']
+            default = parameter['default'] if parameter['type'] != 'String' else '"' + parameter['default'] + '"'
+            cxx_flat_settings_fp.write(f'  if (yamlConfig.{category_name}.{rust_field_name} != {default} && config.{internal_field_name} == {default}) {{\n')
+            if parameter['type'] != 'String':
+                cxx_flat_settings_fp.write(f'    config.{internal_field_name} = yamlConfig.{category_name}.{rust_field_name};\n')
+            else:
+                cxx_flat_settings_fp.write(f'    config.{internal_field_name} = std::string(yamlConfig.{category_name}.{rust_field_name});\n')
+            cxx_flat_settings_fp.write('  }\n')
+
+    cxx_flat_settings_fp.write('''}\n''')
+
+    # then immutable ones
+    cxx_flat_settings_fp.write('''void convertImmutableFlatSettingsFromRust(const dnsdist::rust::settings::GlobalConfiguration& yamlConfig, dnsdist::configuration::ImmutableConfiguration& config)
+{\n''')
+    for category_name, keys in definitions.items():
+        if not 'parameters' in keys:
+            continue
+
+        if 'category' in keys:
+            category_name = keys['category']
+        else:
+            category_name = get_rust_field_name(category_name)
+
+        for parameter in keys['parameters']:
+            if not 'internal-field-name' in parameter or not 'runtime-configurable' in parameter or parameter['runtime-configurable']:
+                continue
+            internal_field_name = parameter['internal-field-name']
+            rust_field_name = get_rust_field_name(parameter['name']) if not 'rename' in parameter else parameter['rename']
+            default = parameter['default'] if parameter['type'] != 'String' else '"' + parameter['default'] + '"'
+            cxx_flat_settings_fp.write(f'  if (yamlConfig.{category_name}.{rust_field_name} != {default} && config.{internal_field_name} == {default}) {{\n')
+            if parameter['type'] != 'String':
+                cxx_flat_settings_fp.write(f'    config.{internal_field_name} = yamlConfig.{category_name}.{rust_field_name};\n')
+            else:
+                cxx_flat_settings_fp.write(f'    config.{internal_field_name} = std::string(yamlConfig.{category_name}.{rust_field_name});\n')
+            cxx_flat_settings_fp.write('  }\n')
+
+    cxx_flat_settings_fp.write('''}\n
+}
+#endif /* defined(HAVE_YAML_CONFIGURATION) */
+''')
+
+    os.rename(cxx_flat_settings_fp.name, out_file_path + 'dnsdist-configuration-yaml-items-generated.cc')
+
+def generate_actions_config(output, response, default_functions):
+    suffix = 'ResponseAction' if response else 'Action'
+    actions_definitions = get_actions_definitions(response)
+    action_buffer = ''
+    for action in actions_definitions:
+        name = get_rust_object_name(action['name'])
+        struct_name = f'{name}{suffix}Configuration'
+        indent = ' ' * 4
+        if not 'skip-serde' in action or not action['skip-serde']:
+            action_buffer += f'''{indent}#[derive(Deserialize, Serialize, Debug, PartialEq)]
+{indent}#[serde(deny_unknown_fields)]\n'''
+        else:
+            action_buffer += f'{indent}#[derive(Default)]\n'
+
+        action_buffer += f'{indent}struct {struct_name} {{\n'
+
+        indent = ' ' * 8
+        if not 'skip-serde' in action or not action['skip-serde']:
+            action_buffer += f'{indent}#[serde(default, skip_serializing_if = "crate::is_default")]\n'
+        action_buffer += f'{indent}name: String,\n'
+
+        action_buffer += get_rust_struct_fields_from_definition(struct_name, action, default_functions, 8)
+
+        action_buffer += '    }\n\n'
+
+    output.write(action_buffer)
+
+def generate_selectors_config(output, default_functions):
+    suffix = 'Selector'
+    selectors_definitions = get_selectors_definitions()
+    selector_buffer = ''
+    for selector in selectors_definitions:
+        name = get_rust_object_name(selector['name'])
+        struct_name = f'{name}{suffix}Configuration'
+        indent = ' ' * 4
+        if not 'skip-serde' in selector or not selector['skip-serde']:
+            selector_buffer += f'''{indent}#[derive(Deserialize, Serialize, Debug, PartialEq)]
+{indent}#[serde(deny_unknown_fields)]\n'''
+        else:
+            selector_buffer += f'{indent}#[derive(Default)]\n'
+
+        selector_buffer += f'{indent}struct {struct_name} {{\n'
+
+        indent = ' ' * 8
+        if not 'skip-serde' in selector or not selector['skip-serde']:
+            selector_buffer += f'{indent}#[serde(default, skip_serializing_if = "crate::is_default")]\n'
+        selector_buffer += f'{indent}name: String,\n'
+
+        selector_buffer += get_rust_struct_fields_from_definition(struct_name, selector, default_functions, 8)
+
+        selector_buffer += '    }\n\n'
+
+    output.write(selector_buffer)
+
+def generate_cpp_action_headers():
+    cpp_action_headers_fp = get_temporary_file_for_generated_code('..')
+    header_buffer = ''
+
+    # query actions
+    actions_definitions = get_actions_definitions(False)
+    suffix = 'Action'
+    for action in actions_definitions:
+        name = get_rust_object_name(action['name'])
+        struct_name = f'{name}{suffix}Configuration'
+        header_buffer += f'struct {struct_name};\n'
+        header_buffer += f'std::shared_ptr<DNS{suffix}Wrapper> get{name}{suffix}(const {struct_name}& config);\n'
+
+    # response actions
+    actions_definitions = get_actions_definitions(True)
+    suffix = 'ResponseAction'
+    for action in actions_definitions:
+        name = get_rust_object_name(action['name'])
+        struct_name = f'{name}{suffix}Configuration'
+        header_buffer += f'struct {struct_name};\n'
+        header_buffer += f'std::shared_ptr<DNS{suffix}Wrapper> get{name}{suffix}(const {struct_name}& config);\n'
+
+    cpp_action_headers_fp.write(header_buffer)
+    os.rename(cpp_action_headers_fp.name, '../dnsdist-rust-bridge-actions-generated.hh')
+
+def generate_cpp_selector_headers():
+    cpp_selector_headers_fp = get_temporary_file_for_generated_code('..')
+    header_buffer = ''
+
+    selectors_definitions = get_selectors_definitions()
+    suffix = 'Selector'
+    for selector in selectors_definitions:
+        name = get_rust_object_name(selector['name'])
+        struct_name = f'{name}{suffix}Configuration'
+        header_buffer += f'struct {struct_name};\n'
+        header_buffer += f'std::shared_ptr<DNS{suffix}> get{name}{suffix}(const {struct_name}& config);\n'
+    cpp_selector_headers_fp.write(header_buffer)
+    os.rename(cpp_selector_headers_fp.name, '../dnsdist-rust-bridge-selectors-generated.hh')
+
+def get_cpp_parameters(struct_type, struct_name, parameters, skip_name):
+    output = ''
+    for parameter in parameters:
+        name = parameter['name']
+        ptype = parameter['type']
+        if name == 'name' and skip_name:
+            continue
+        pname = get_rust_field_name(name)
+        if len(output) > 0:
+            output += ', '
+        field = f'{struct_name}.{pname}'
+        if ptype == 'PacketBuffer':
+            field = f'PacketBuffer({field}.data(), {field}.data() + {field}.size())'
+        elif ptype == 'DNSName':
+            field = f'DNSName(std::string({field}))'
+        elif ptype == 'ComboAddress':
+            field = f'ComboAddress(std::string({field}))'
+        elif ptype == 'String':
+            field = f'std::string({field})'
+        elif ptype == 'ResponseConfig':
+            field = f'convertResponseConfig({field})'
+        elif ptype == 'Vec<SVCRecordParameters>':
+            field = f'convertSVCRecordParameters({field})'
+        elif ptype == 'SOAParams':
+            field = f'convertSOAParams({field})'
+        elif ptype in ['dnsdist::actions::LuaActionFunction', 'dnsdist::actions::LuaActionFFIFunction', 'dnsdist::actions::LuaResponseActionFunction', 'dnsdist::actions::LuaResponseActionFFIFunction', 'dnsdist::selectors::LuaSelectorFunction', 'dnsdist::selectors::LuaSelectorFFIFunction']:
+            field = f'convertLuaFunction<{ptype}>("{struct_type}", {field})'
+        output += field
+    return output
+
+def generate_cpp_action_wrappers():
+    cpp_action_wrappers_fp = get_temporary_file_for_generated_code('..')
+    wrappers_buffer = ''
+
+    # query actions
+    actions_definitions = get_actions_definitions(False)
+    suffix = 'Action'
+    for action in actions_definitions:
+        if 'skip-rust' in action and action['skip-rust']:
+            continue
+        name = get_rust_object_name(action['name'])
+        struct_name = f'{name}{suffix}Configuration'
+        parameters = get_cpp_parameters(struct_name, 'config', action['parameters'], True) if 'parameters' in action else ''
+        wrappers_buffer += f'''std::shared_ptr<DNS{suffix}Wrapper> get{name}{suffix}(const {struct_name}& config)
+{{
+  auto action = dnsdist::actions::get{name}{suffix}({parameters});
+  return newDNSActionWrapper(std::move(action), config.name);
+}}
+'''
+
+    # response actions
+    actions_definitions = get_actions_definitions(True)
+    suffix = 'ResponseAction'
+    for action in actions_definitions:
+        if 'skip-rust' in action and action['skip-rust']:
+            continue
+        name = get_rust_object_name(action['name'])
+        struct_name = f'{name}{suffix}Configuration'
+        parameters = get_cpp_parameters(struct_name, 'config', action['parameters'], True) if 'parameters' in action else ''
+        wrappers_buffer += f'''std::shared_ptr<DNS{suffix}Wrapper> get{name}{suffix}(const {struct_name}& config)
+{{
+  auto action = dnsdist::actions::get{name}{suffix}({parameters});
+  return newDNSResponseActionWrapper(std::move(action), config.name);
+}}
+'''
+
+    cpp_action_wrappers_fp.write(wrappers_buffer)
+    os.rename(cpp_action_wrappers_fp.name, '../dnsdist-rust-bridge-actions-generated.cc')
+
+def generate_cpp_selector_wrappers():
+    cpp_selector_wrappers_fp = get_temporary_file_for_generated_code('..')
+    wrappers_buffer = ''
+
+    selectors_definitions = get_selectors_definitions()
+    suffix = 'Selector'
+    for selector in selectors_definitions:
+        if 'skip-rust' in selector and selector['skip-rust']:
+            continue
+        name = get_rust_object_name(selector['name'])
+        struct_name = f'{name}{suffix}Configuration'
+        parameters = get_cpp_parameters(struct_name, 'config', selector['parameters'], True) if 'parameters' in selector else ''
+        wrappers_buffer += f'''std::shared_ptr<DNS{suffix}> get{name}{suffix}(const {struct_name}& config)
+{{
+  auto selector = dnsdist::selectors::get{name}{suffix}({parameters});
+  return newDNSSelector(std::move(selector), config.name);
+}}
+'''
+
+    cpp_selector_wrappers_fp.write(wrappers_buffer)
+    os.rename(cpp_selector_wrappers_fp.name, '../dnsdist-rust-bridge-selectors-generated.cc')
+
+def generate_rust_actions_enum(output, response):
+    suffix = 'ResponseAction' if response else 'Action'
+    actions_definitions = get_actions_definitions(response)
+    enum_buffer = f'''#[derive(Default, Serialize, Deserialize, Debug, PartialEq)]
+#[serde(tag = "type")]
+enum {suffix} {{
+    #[default]
+    Default,
+'''
+
+    for action in actions_definitions:
+        name = get_rust_object_name(action['name'])
+        struct_name = f'{name}{suffix}Configuration'
+        if struct_name in ['ContinueActionConfiguration']:
+            # special version for Serde
+            enum_buffer += f'    {name}({struct_name}Serde),\n'
+        else:
+            enum_buffer += f'    {name}(dnsdistsettings::{struct_name}),\n'
+
+    enum_buffer += '}\n\n'
+
+    output.write(enum_buffer)
+
+def generate_rust_selectors_enum(output):
+    suffix = 'Selector'
+    selectors_definitions = get_selectors_definitions()
+    enum_buffer = f'''#[derive(Default, Serialize, Deserialize, Debug, PartialEq)]
+#[serde(tag = "type")]
+enum {suffix} {{
+    #[default]
+    Default,
+'''
+
+    for selector in selectors_definitions:
+        name = get_rust_object_name(selector['name'])
+        struct_name = f'{name}{suffix}Configuration'
+        if struct_name in ['AndSelectorConfiguration', 'OrSelectorConfiguration', 'NotSelectorConfiguration']:
+            # special version for Serde
+            enum_buffer += f'    {name}({struct_name}Serde),\n'
+        else:
+            enum_buffer += f'    {name}(dnsdistsettings::{struct_name}),\n'
+
+    enum_buffer += '}\n\n'
+
+    output.write(enum_buffer)
+
+def get_actions_definitions(response):
+    def_file = '../dnsdist-response-actions-definitions.yml' if response else '../dnsdist-actions-definitions.yml'
+    return get_definitions_from_file(def_file)
+
+def get_selectors_definitions():
+    def_file = '../dnsdist-selectors-definitions.yml'
+    return get_definitions_from_file(def_file)
+
+def generate_cpp_action_selector_functions_callable_from_rust(output):
+    output_buffer = '''
+    /*
+     * Functions callable from Rust (actions and selectors)
+     */
+    unsafe extern "C++" {
+'''
+    # first query actions
+    actions_definitions = get_actions_definitions(False)
+    suffix = 'Action'
+    for action in actions_definitions:
+        name = get_rust_object_name(action['name'])
+        output_buffer += f'        fn get{name}{suffix}(config: &{name}{suffix}Configuration) -> SharedPtr<DNS{suffix}Wrapper>;\n'
+
+    # then response actions
+    actions_definitions = get_actions_definitions(True)
+    suffix = 'ResponseAction'
+    for action in actions_definitions:
+        name = get_rust_object_name(action['name'])
+        output_buffer += f'        fn get{name}{suffix}(config: &{name}{suffix}Configuration) -> SharedPtr<DNS{suffix}Wrapper>;\n'
+
+    # then selectors
+    selectors_definitions = get_selectors_definitions()
+    suffix = 'Selector'
+    for selector in selectors_definitions:
+        name = get_rust_object_name(selector['name'])
+        output_buffer += f'        fn get{name}{suffix}(config: &{name}{suffix}Configuration) -> SharedPtr<DNS{suffix}>;\n'
+
+    output_buffer += '    }\n'
+    output.write(output_buffer)
+
+def generate_rust_action_to_config(output, response):
+    suffix = 'ResponseAction' if response else 'Action'
+    actions_definitions = get_actions_definitions(response)
+    function_name = 'get_one_action_from_serde' if not response else 'get_one_response_action_from_serde'
+    enum_buffer = f'''fn {function_name}(action: &{suffix}) -> Option<dnsdistsettings::SharedDNS{suffix}> {{
+    match action {{
+        {suffix}::Default => {{}}
+'''
+
+    for action in actions_definitions:
+        name = get_rust_object_name(action['name'])
+        var = name.lower()
+        if name in ['Continue']:
+            enum_buffer += f'''        {suffix}::{name}(cont) => {{
+             let mut config: dnsdistsettings::{name}{suffix}Configuration = Default::default();
+             let new_action = get_one_action_from_serde(&*cont.action);
+             if new_action.is_some() {{
+                 config.action = new_action.unwrap();
+             }}
+             return Some(dnsdistsettings::SharedDNS{suffix} {{
+                 action: dnsdistsettings::get{name}{suffix}(&config),
+             }});
+        }}
+'''
+        else:
+            enum_buffer += f'''        {suffix}::{name}(config) => {{
+                let tmp_action = dnsdistsettings::get{name}{suffix}(&config);
+                return Some(dnsdistsettings::SharedDNS{suffix} {{
+                    action: tmp_action,
+                }});
+            }}
+'''
+
+    enum_buffer += '''    }
+    None
+}
+'''
+
+    output.write(enum_buffer)
+
+def generate_rust_selector_to_config(output):
+    suffix = 'Selector'
+    selectors_definitions = get_selectors_definitions()
+    function_name = 'get_one_selector_from_serde'
+    enum_buffer = f'''fn {function_name}(selector: &{suffix}) -> Option<dnsdistsettings::SharedDNS{suffix}> {{
+    match selector {{
+        {suffix}::Default => {{}}
+'''
+
+    for selector in selectors_definitions:
+        name = get_rust_object_name(selector['name'])
+        var = name.lower()
+        if name in ['And', 'Or']:
+            enum_buffer += f'''        {suffix}::{name}({var}) => {{
+             let mut config: dnsdistsettings::{name}{suffix}Configuration = Default::default();
+             for sub_selector in &{var}.selectors {{
+                 let new_selector = get_one_selector_from_serde(&sub_selector);
+                 if new_selector.is_some() {{
+                     config.selectors.push(new_selector.unwrap());
+                 }}
+             }}
+             return Some(dnsdistsettings::SharedDNS{suffix} {{
+                 selector: dnsdistsettings::get{name}{suffix}(&config),
+             }});
+        }}
+'''
+        elif name in ['Not']:
+            enum_buffer += f'''        {suffix}::{name}({var}) => {{
+             let mut config: dnsdistsettings::{name}{suffix}Configuration = Default::default();
+             let new_selector = get_one_selector_from_serde(&*{var}.selector);
+             if new_selector.is_some() {{
+                 config.selector = new_selector.unwrap();
+             }}
+             return Some(dnsdistsettings::SharedDNS{suffix} {{
+                 selector: dnsdistsettings::get{name}{suffix}(&config),
+             }});
+        }}
+'''
+        else:
+            enum_buffer += f'''        {suffix}::{name}({var}) => {{
+            let tmp_selector = dnsdistsettings::get{name}{suffix}(&{var});
+            return Some(dnsdistsettings::SharedDNS{suffix} {{
+                selector: tmp_selector,
+            }});
+        }}
+'''
+
+    enum_buffer += '''    }
+    None
+}
+'''
+
+    output.write(enum_buffer)
+
+def handle_structures(generated_fp, definitions, default_functions, validation_functions):
+    for definition_name, keys in definitions.items():
+        generated_fp.write(get_rust_struct_from_definition(definition_name, keys, default_functions) + '\n')
+        validation_functions.append(get_struct_validation_function_from_definition(definition_name, keys['parameters'] if 'parameters' in keys else []))
+
+def get_temporary_file_for_generated_code(directory):
+    generated_fp = tempfile.NamedTemporaryFile(mode='w+t', encoding='utf-8', dir=directory, delete=False)
+    generated_fp.write('// !! This file has been generated by dnsdist-settings-generator.py, do not edit by hand!!\n')
+    return generated_fp
+
+def main():
+    if len(sys.argv) != 2:
+        print(f'Usage: {sys.argv[0]} <path/to/definitions/file>')
+        sys.exit(1)
+
+    src_dir = './'
+    definitions = get_definitions_from_file(sys.argv[1])
+    default_functions = []
+    validation_functions = []
+    global_objects = {}
+
+    generate_cpp_action_headers()
+    generate_cpp_action_wrappers()
+    generate_cpp_selector_headers()
+    generate_cpp_selector_wrappers()
+
+    generated_fp = get_temporary_file_for_generated_code(src_dir + '/rust/src/')
+    include_file(generated_fp, src_dir + 'rust-pre-in.rs')
+
+    generate_actions_config(generated_fp, False, default_functions)
+    generate_actions_config(generated_fp, True, default_functions)
+    generate_selectors_config(generated_fp, default_functions)
+
+    generate_flat_settings_for_cxx(definitions, src_dir)
+
+    handle_structures(generated_fp, definitions, default_functions, validation_functions)
+
+    generate_cpp_action_selector_functions_callable_from_rust(generated_fp)
+
+    include_file(generated_fp, src_dir + 'rust-middle-in.rs')
+    # we are now outside of the dnsdistsettings namespace
+
+    # generate the special global configuration Serde structure
+    for definition_name, keys in definitions.items():
+        if definition_name == 'global':
+            generated_fp.write(get_rust_struct_from_definition(definition_name, keys, default_functions, special_serde_object=True) + '\n')
+            validation_functions.append(get_struct_validation_function_from_definition(definition_name, keys['parameters'] if 'parameters' in keys else [], True))
+
+    generate_rust_actions_enum(generated_fp, False)
+    generate_rust_actions_enum(generated_fp, True)
+    generate_rust_selectors_enum(generated_fp)
+
+    # the generated functions for the default values and validation
+    for function_def in default_functions:
+        generated_fp.write(function_def + '\n')
+
+    for function_def in validation_functions:
+        generated_fp.write(function_def + '\n')
+
+    generate_rust_action_to_config(generated_fp, False)
+    generate_rust_action_to_config(generated_fp, True)
+    generate_rust_selector_to_config(generated_fp)
+
+    include_file(generated_fp, src_dir + 'rust-post-in.rs')
+
+    os.rename(generated_fp.name, src_dir + '/rust/src/lib.rs')
+
+if __name__ == '__main__':
+    main()
diff --git a/pdns/dnsdistdist/dnsdist-rust-lib/rust-middle-in.rs b/pdns/dnsdistdist/dnsdist-rust-lib/rust-middle-in.rs
new file mode 100644 (file)
index 0000000..6ed97c3
--- /dev/null
@@ -0,0 +1,127 @@
+    /*
+     * Functions callable from C++
+     */
+    extern "Rust" {
+        fn from_yaml_string(str: &str) -> Result<GlobalConfiguration>;
+    }
+    /*
+     * Functions callable from Rust
+     */
+    unsafe extern "C++" {
+        include!("dnsdist-rust-bridge.hh");
+        type DNSSelector;
+        type DNSActionWrapper;
+        type DNSResponseActionWrapper;
+        fn registerProtobufLogger(config: &ProtobufLoggerConfiguration);
+        fn registerDnstapLogger(config: &DnstapLoggerConfiguration);
+        fn registerKVSObjects(config: &KeyValueStoresConfiguration);
+    }
+}
+
+impl Default for dnsdistsettings::SharedDNSAction {
+    fn default() -> dnsdistsettings::SharedDNSAction {
+        dnsdistsettings::SharedDNSAction {
+            action: cxx::SharedPtr::null(),
+        }
+    }
+}
+
+impl Default for dnsdistsettings::SharedDNSSelector {
+    fn default() -> dnsdistsettings::SharedDNSSelector {
+        dnsdistsettings::SharedDNSSelector {
+            selector: cxx::SharedPtr::null(),
+        }
+    }
+}
+
+#[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+#[serde(deny_unknown_fields)]
+struct AndSelectorConfigurationSerde {
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    selectors: Vec<Selector>,
+}
+
+#[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+#[serde(deny_unknown_fields)]
+struct OrSelectorConfigurationSerde {
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    selectors: Vec<Selector>,
+}
+
+#[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+#[serde(deny_unknown_fields)]
+struct NotSelectorConfigurationSerde {
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    selector: Box<Selector>,
+}
+
+#[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+#[serde(deny_unknown_fields)]
+struct ContinueActionConfigurationSerde {
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    action: Box<Action>,
+}
+
+impl Selector {
+  fn validate(&self) -> Result<(), ValidationError> {
+    Ok(())
+  }
+}
+
+impl Action {
+  fn validate(&self) -> Result<(), ValidationError> {
+    Ok(())
+  }
+}
+
+impl ResponseAction {
+  fn validate(&self) -> Result<(), ValidationError> {
+    Ok(())
+  }
+}
+
+#[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+#[serde(deny_unknown_fields)]
+struct QueryRuleConfigurationSerde {
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    name: String,
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    uuid: String,
+    selector: Selector,
+    action: Action,
+}
+
+impl QueryRuleConfigurationSerde {
+  fn validate(&self) -> Result<(), ValidationError> {
+    Ok(())
+  }
+}
+
+#[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+#[serde(deny_unknown_fields)]
+struct ResponseRuleConfigurationSerde {
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    name: String,
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    uuid: String,
+    selector: Selector,
+    action: ResponseAction,
+}
+
+impl ResponseRuleConfigurationSerde {
+  fn validate(&self) -> Result<(), ValidationError> {
+    Ok(())
+  }
+}
+
+impl dnsdistsettings::SharedDNSAction {
+  fn validate(&self) -> Result<(), ValidationError> {
+    Ok(())
+  }
+}
+
+impl dnsdistsettings::SharedDNSResponseAction {
+  fn validate(&self) -> Result<(), ValidationError> {
+    Ok(())
+  }
+}
diff --git a/pdns/dnsdistdist/dnsdist-rust-lib/rust-post-in.rs b/pdns/dnsdistdist/dnsdist-rust-lib/rust-post-in.rs
new file mode 100644 (file)
index 0000000..1209fe8
--- /dev/null
@@ -0,0 +1,124 @@
+fn get_selectors_from_serde(
+    selectors_from_serde: &Vec<Selector>,
+) -> Vec<dnsdistsettings::SharedDNSSelector> {
+    let mut results: Vec<dnsdistsettings::SharedDNSSelector> = Vec::new();
+
+    for rule in selectors_from_serde {
+        let selector = get_one_selector_from_serde(&rule);
+        if selector.is_some() {
+            results.push(selector.unwrap());
+        }
+    }
+    results
+}
+
+fn get_query_rules_from_serde(
+    rules_from_serde: &Vec<QueryRuleConfigurationSerde>,
+) -> Vec<dnsdistsettings::QueryRuleConfiguration> {
+    let mut results: Vec<dnsdistsettings::QueryRuleConfiguration> = Vec::new();
+
+    for rule in rules_from_serde {
+        let selector = get_one_selector_from_serde(&rule.selector);
+        let action = get_one_action_from_serde(&rule.action);
+        if selector.is_some() && action.is_some() {
+            results.push(dnsdistsettings::QueryRuleConfiguration {
+              name: rule.name.clone(),
+              uuid: rule.uuid.clone(),
+              selector: selector.unwrap(),
+              action: action.unwrap(),
+            });
+        }
+    }
+    results
+}
+
+fn get_response_rules_from_serde(
+    rules_from_serde: &Vec<ResponseRuleConfigurationSerde>,
+) -> Vec<dnsdistsettings::ResponseRuleConfiguration> {
+    let mut results: Vec<dnsdistsettings::ResponseRuleConfiguration> = Vec::new();
+
+    for rule in rules_from_serde {
+        let selector = get_one_selector_from_serde(&rule.selector);
+        let action = get_one_response_action_from_serde(&rule.action);
+        if selector.is_some() && action.is_some() {
+            results.push(dnsdistsettings::ResponseRuleConfiguration {
+              name: rule.name.clone(),
+              uuid: rule.uuid.clone(),
+              selector: selector.unwrap(),
+              action: action.unwrap(),
+            });
+        }
+    }
+    results
+}
+
+fn register_remote_loggers(
+  config: &dnsdistsettings::RemoteLoggingConfiguration,
+) {
+  for logger in &config.protobuf_loggers {
+    dnsdistsettings::registerProtobufLogger(&logger);
+  }
+  for logger in &config.dnstap_loggers {
+    dnsdistsettings::registerDnstapLogger(&logger);
+  }
+}
+
+fn get_global_configuration_from_serde(
+    serde: GlobalConfigurationSerde,
+) -> dnsdistsettings::GlobalConfiguration {
+    let mut config: dnsdistsettings::GlobalConfiguration = Default::default();
+    config.key_value_stores = serde.key_value_stores;
+    config.webserver = serde.webserver;
+    config.console = serde.console;
+    config.edns_client_subnet = serde.edns_client_subnet;
+    config.dynamic_rules_settings = serde.dynamic_rules_settings;
+    config.dynamic_rules = serde.dynamic_rules;
+    config.acl = serde.acl;
+    config.ring_buffers = serde.ring_buffers;
+    config.binds = serde.binds;
+    config.backends = serde.backends;
+    config.cache_settings = serde.cache_settings;
+    config.security_polling = serde.security_polling;
+    config.general = serde.general;
+    config.packet_caches = serde.packet_caches;
+    config.proxy_protocol = serde.proxy_protocol;
+    config.snmp = serde.snmp;
+    config.query_count = serde.query_count;
+    config.load_balancing_policies = serde.load_balancing_policies;
+    config.pools = serde.pools;
+    config.metrics = serde.metrics;
+    config.remote_logging = serde.remote_logging;
+    config.tuning = serde.tuning;
+    // this needs to be done before the rules so that they can refer to the loggers
+    register_remote_loggers(&config.remote_logging);
+    // this needs to be done before the rules so that they can refer to the KVS objects
+    dnsdistsettings::registerKVSObjects(&config.key_value_stores);
+    // this needs to be done BEFORE the rules so that they can refer to the selectors
+    // by name
+    config.selectors = get_selectors_from_serde(&serde.selectors);
+    config.query_rules = get_query_rules_from_serde(&serde.query_rules);
+    config.cache_miss_rules = get_query_rules_from_serde(&serde.cache_miss_rules);
+    config.response_rules = get_response_rules_from_serde(&serde.response_rules);
+    config.cache_hit_response_rules = get_response_rules_from_serde(&serde.cache_hit_response_rules);
+    config.cache_inserted_response_rules = get_response_rules_from_serde(&serde.cache_inserted_response_rules);
+    config.self_answered_response_rules = get_response_rules_from_serde(&serde.self_answered_response_rules);
+    config.xfr_response_rules = get_response_rules_from_serde(&serde.xfr_response_rules);
+    config
+}
+
+pub fn from_yaml_string(
+    str: &str,
+) -> Result<dnsdistsettings::GlobalConfiguration, serde_yaml::Error> {
+    let serde_config: Result<GlobalConfigurationSerde, serde_yaml::Error> =
+        serde_yaml::from_str(str);
+
+    if !serde_config.is_err() {
+      let validation_result = serde_config.as_ref().unwrap().validate();
+      if let Err(e) = validation_result {
+          println!("Error validating the configuration loaded from {}: {}", str, e);
+      }
+    }
+    let config: dnsdistsettings::GlobalConfiguration =
+        get_global_configuration_from_serde(serde_config?);
+    return Ok(config);
+}
diff --git a/pdns/dnsdistdist/dnsdist-rust-lib/rust-pre-in.rs b/pdns/dnsdistdist/dnsdist-rust-lib/rust-pre-in.rs
new file mode 100644 (file)
index 0000000..7860963
--- /dev/null
@@ -0,0 +1,82 @@
+use serde::{Deserialize, Serialize};
+
+mod helpers;
+use helpers::*;
+
+// Suppresses "Deserialize unused" warning
+#[derive(Deserialize, Serialize)]
+struct UnusedStruct {}
+
+#[derive(Debug)]
+pub struct ValidationError {
+    msg: String,
+}
+
+#[cxx::bridge(namespace = dnsdist::rust::settings)]
+mod dnsdistsettings {
+    #[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct ResponseConfig {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        set_aa: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        set_ad: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        set_ra: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        ttl: u32,
+    }
+
+    #[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SOAParams {
+        serial: u32,
+        refresh: u32,
+        retry: u32,
+        expire: u32,
+        minimum: u32,
+    }
+
+    #[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SVCRecordAdditionalParams {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        key: u16,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        value: String,
+    }
+
+    #[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SVCRecordParameters {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        mandatory_params: Vec<u16>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        alpns: Vec<String>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        ipv4_hints: Vec<String>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        ipv6_hints: Vec<String>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        additional_params: Vec<SVCRecordAdditionalParams>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        target: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        port: u16,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        priority: u16,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        no_default_alpn: bool,
+    }
+
+    struct SharedDNSSelector {
+        selector: SharedPtr<DNSSelector>,
+    }
+
+    struct SharedDNSAction {
+        action: SharedPtr<DNSActionWrapper>,
+    }
+
+    struct SharedDNSResponseAction {
+        action: SharedPtr<DNSResponseActionWrapper>,
+    }
index 5d614f0c44102cdbfd60ed838fbf619ededd874d..c48b935ab4244fe40e56a5038854aabc9333736c 100644 (file)
@@ -1,3 +1,5 @@
+// !! This file has been generated by dnsdist-settings-generator.py, do not edit by hand!!
+// START INCLUDE ./rust-pre-in.rs
 use serde::{Deserialize, Serialize};
 
 mod helpers;
@@ -13,16 +15,4288 @@ pub struct ValidationError {
 }
 
 #[cxx::bridge(namespace = dnsdist::rust::settings)]
-mod dnsdistsetttings {
+mod dnsdistsettings {
+    #[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct ResponseConfig {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        set_aa: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        set_ad: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        set_ra: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        ttl: u32,
+    }
+
+    #[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SOAParams {
+        serial: u32,
+        refresh: u32,
+        retry: u32,
+        expire: u32,
+        minimum: u32,
+    }
+
+    #[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SVCRecordAdditionalParams {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        key: u16,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        value: String,
+    }
+
+    #[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SVCRecordParameters {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        mandatory_params: Vec<u16>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        alpns: Vec<String>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        ipv4_hints: Vec<String>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        ipv6_hints: Vec<String>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        additional_params: Vec<SVCRecordAdditionalParams>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        target: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        port: u16,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        priority: u16,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        no_default_alpn: bool,
+    }
+
+    struct SharedDNSSelector {
+        selector: SharedPtr<DNSSelector>,
+    }
 
+    struct SharedDNSAction {
+        action: SharedPtr<DNSActionWrapper>,
+    }
+
+    struct SharedDNSResponseAction {
+        action: SharedPtr<DNSResponseActionWrapper>,
+    }
+// END INCLUDE ./rust-pre-in.rs
     #[derive(Deserialize, Serialize, Debug, PartialEq)]
     #[serde(deny_unknown_fields)]
-    struct CarbonConfiguration {
+    struct AllowActionConfiguration {
         #[serde(default, skip_serializing_if = "crate::is_default")]
-        address: String,
+        name: String,
+    }
+
+    #[derive(Default)]
+    struct ContinueActionConfiguration {
+        name: String,
+        action: SharedDNSAction,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct DelayActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        msec: u32,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct DnstapLogActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        identity: String,
+        #[serde(rename = "logger-name", )]
+        logger_name: String,
+        #[serde(rename = "alter-function", default, skip_serializing_if = "crate::is_default")]
+        alter_function: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct DropActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetEDNSOptionActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        code: u32,
+        data: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct ERCodeActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        rcode: u8,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        vars: ResponseConfig,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct HTTPStatusActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        status: u16,
+        body: String,
+        #[serde(rename = "content-type", default, skip_serializing_if = "crate::is_default")]
+        content_type: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        vars: ResponseConfig,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct KeyValueStoreLookupActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "kvs-name", )]
+        kvs_name: String,
+        #[serde(rename = "lookup-key-name", )]
+        lookup_key_name: String,
+        #[serde(rename = "destination-tag", )]
+        destination_tag: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct KeyValueStoreRangeLookupActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "kvs-name", )]
+        kvs_name: String,
+        #[serde(rename = "lookup-key-name", )]
+        lookup_key_name: String,
+        #[serde(rename = "destination-tag", )]
+        destination_tag: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct LogActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "file-name", default, skip_serializing_if = "crate::is_default")]
+        file_name: String,
+        #[serde(default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        binary: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        append: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        buffered: bool,
+        #[serde(rename = "verbose-only", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        verbose_only: bool,
+        #[serde(rename = "include-timestamp", default, skip_serializing_if = "crate::is_default")]
+        include_timestamp: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct LuaActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        function: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct LuaFFIActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        function: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct LuaFFIPerThreadActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        code: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct NegativeAndSOAActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        nxd: bool,
+        zone: String,
+        ttl: u32,
+        mname: String,
+        rname: String,
+        #[serde(rename = "soa-parameters", )]
+        soa_parameters: SOAParams,
+        #[serde(rename = "soa-in-authority", default, skip_serializing_if = "crate::is_default")]
+        soa_in_authority: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        vars: ResponseConfig,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct NoneActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct PoolActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "pool-name", )]
+        pool_name: String,
+        #[serde(rename = "stop-processing", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        stop_processing: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct QPSActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        limit: u32,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct QPSPoolActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        limit: u32,
+        #[serde(rename = "pool-name", )]
+        pool_name: String,
+        #[serde(rename = "stop-processing", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        stop_processing: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct RCodeActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        rcode: u8,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        vars: ResponseConfig,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct RemoteLogActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "logger-name", )]
+        logger_name: String,
+        #[serde(rename = "alter-function", default, skip_serializing_if = "crate::is_default")]
+        alter_function: String,
+        #[serde(rename = "server-id", default, skip_serializing_if = "crate::is_default")]
+        server_id: String,
+        #[serde(rename = "ip-encrypt-key", default, skip_serializing_if = "crate::is_default")]
+        ip_encrypt_key: String,
+        #[serde(rename = "export-tags", default, skip_serializing_if = "crate::is_default")]
+        export_tags: Vec<String>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        metas: Vec<ProtoBufMetaConfiguration>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetAdditionalProxyProtocolValueActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "proxy-type", )]
+        proxy_type: u8,
+        value: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetDisableECSActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetDisableValidationActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetECSActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        ipv4: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        ipv6: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetECSOverrideActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "override-existing", )]
+        override_existing: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetECSPrefixLengthActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        ipv4: u16,
+        ipv6: u16,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetExtendedDNSErrorActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "info-code", )]
+        info_code: u16,
+        #[serde(rename = "extra-text", default, skip_serializing_if = "crate::is_default")]
+        extra_text: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetMacAddrActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        code: u32,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetMaxReturnedTTLActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        max: u32,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetNoRecurseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetProxyProtocolValuesActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        values: Vec<ProxyProtocolValueConfiguration>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetSkipCacheActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetTagActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        tag: String,
+        value: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetTempFailureCacheTTLActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "maxTTL", )]
+        maxttl: u32,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SNMPTrapActionConfiguration {
         #[serde(default, skip_serializing_if = "crate::is_default")]
         name: String,
         #[serde(default, skip_serializing_if = "crate::is_default")]
-        interval: u16
+        reason: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SpoofActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        ips: Vec<String>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        vars: ResponseConfig,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SpoofCNAMEActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        cname: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        vars: ResponseConfig,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SpoofPacketActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        response: String,
+        len: u32,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SpoofRawActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        answers: Vec<String>,
+        #[serde(rename = "qtype-for-any", default, skip_serializing_if = "crate::is_default")]
+        qtype_for_any: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        vars: ResponseConfig,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SpoofSVCActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        parameters: Vec<SVCRecordParameters>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        vars: ResponseConfig,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct TCActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct TeeActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        rca: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        lca: String,
+        #[serde(rename = "add-ecs", default, skip_serializing_if = "crate::is_default")]
+        add_ecs: bool,
+        #[serde(rename = "add-proxy-protocol", default, skip_serializing_if = "crate::is_default")]
+        add_proxy_protocol: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct AllowResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct ClearRecordTypesResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        types: Vec<u16>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct DelayResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        msec: u32,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct DnstapLogResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        identity: String,
+        #[serde(rename = "logger-name", )]
+        logger_name: String,
+        #[serde(rename = "alter-function", default, skip_serializing_if = "crate::is_default")]
+        alter_function: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct DropResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct LimitTTLResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        min: u32,
+        max: u32,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        types: Vec<u16>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct LogResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "file-name", default, skip_serializing_if = "crate::is_default")]
+        file_name: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        append: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        buffered: bool,
+        #[serde(rename = "verbose-only", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        verbose_only: bool,
+        #[serde(rename = "include-timestamp", default, skip_serializing_if = "crate::is_default")]
+        include_timestamp: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct LuaResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        function: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct LuaFFIResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        function: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct LuaFFIPerThreadResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        code: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct RemoteLogResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "logger-name", )]
+        logger_name: String,
+        #[serde(rename = "alter-function", default, skip_serializing_if = "crate::is_default")]
+        alter_function: String,
+        #[serde(rename = "server-id", default, skip_serializing_if = "crate::is_default")]
+        server_id: String,
+        #[serde(rename = "ip-encrypt-key", default, skip_serializing_if = "crate::is_default")]
+        ip_encrypt_key: String,
+        #[serde(rename = "include-cname", default, skip_serializing_if = "crate::is_default")]
+        include_cname: bool,
+        #[serde(rename = "export-tags", default, skip_serializing_if = "crate::is_default")]
+        export_tags: Vec<String>,
+        #[serde(rename = "export-extended-errors-to-meta", default, skip_serializing_if = "crate::is_default")]
+        export_extended_errors_to_meta: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        metas: Vec<ProtoBufMetaConfiguration>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetExtendedDNSErrorResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "info-code", )]
+        info_code: u16,
+        #[serde(rename = "extra-text", default, skip_serializing_if = "crate::is_default")]
+        extra_text: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetMaxReturnedTTLResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        max: u32,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetMaxTTLResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        max: u32,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetMinTTLResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        min: u32,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetReducedTTLResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        percentage: u8,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetSkipCacheResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SetTagResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        tag: String,
+        value: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SNMPTrapResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        reason: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct TCResponseActionConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct AllSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+    }
+
+    #[derive(Default)]
+    struct AndSelectorConfiguration {
+        name: String,
+        selectors: Vec<SharedDNSSelector>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct ByNameSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "selector-name", )]
+        selector_name: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct DNSSECSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct DSTPortSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        port: u16,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct EDNSOptionSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "option-code", )]
+        option_code: u16,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct EDNSVersionSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        version: u8,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct ERCodeSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        rcode: u64,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct HTTPHeaderSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        header: String,
+        expression: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct HTTPPathSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        path: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct HTTPPathRegexSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        expression: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct KeyValueStoreLookupSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "kvs-name", )]
+        kvs_name: String,
+        #[serde(rename = "lookup-key-name", )]
+        lookup_key_name: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct KeyValueStoreRangeLookupSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "kvs-name", )]
+        kvs_name: String,
+        #[serde(rename = "lookup-key-name", )]
+        lookup_key_name: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct LuaSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        function: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct LuaFFISelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        function: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct LuaFFIPerThreadSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        code: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct MaxQPSSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        qps: u32,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        burst: u32,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct MaxQPSIPSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        qps: u32,
+        #[serde(rename = "ipv4-mask", default = "crate::U8::<32>::value", skip_serializing_if = "crate::U8::<32>::is_equal")]
+        ipv4_mask: u8,
+        #[serde(rename = "ipv6-mask", default = "crate::U8::<64>::value", skip_serializing_if = "crate::U8::<64>::is_equal")]
+        ipv6_mask: u8,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        burst: u32,
+        #[serde(default = "crate::U32::<300>::value", skip_serializing_if = "crate::U32::<300>::is_equal")]
+        expiration: u32,
+        #[serde(rename = "cleanup-delay", default = "crate::U32::<60>::value", skip_serializing_if = "crate::U32::<60>::is_equal")]
+        cleanup_delay: u32,
+        #[serde(rename = "scan-fraction", default = "crate::U32::<10>::value", skip_serializing_if = "crate::U32::<10>::is_equal")]
+        scan_fraction: u32,
+        #[serde(default = "crate::U32::<10>::value", skip_serializing_if = "crate::U32::<10>::is_equal")]
+        shards: u32,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct NetmaskGroupSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "netmask-group-name", default, skip_serializing_if = "crate::is_default")]
+        netmask_group_name: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        netmasks: Vec<String>,
+        #[serde(default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        source: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        quiet: bool,
+    }
+
+    #[derive(Default)]
+    struct NotSelectorConfiguration {
+        name: String,
+        selector: SharedDNSSelector,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct OpcodeSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        code: u8,
+    }
+
+    #[derive(Default)]
+    struct OrSelectorConfiguration {
+        name: String,
+        selectors: Vec<SharedDNSSelector>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct PayloadSizeSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        comparison: String,
+        size: u16,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct PoolAvailableSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        pool: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct PoolOutstandingSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        pool: String,
+        #[serde(rename = "max-outstanding", )]
+        max_outstanding: u64,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct ProbaSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        probability: f64,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct ProxyProtocolValueSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "option-type", )]
+        option_type: u8,
+        #[serde(rename = "option-value", default, skip_serializing_if = "crate::is_default")]
+        option_value: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct QClassSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        qclass: String,
+        #[serde(rename = "numeric-value", default, skip_serializing_if = "crate::is_default")]
+        numeric_value: u16,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct QNameSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        qname: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct QNameLabelsCountSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "min-labels-count", )]
+        min_labels_count: u16,
+        #[serde(rename = "max-labels-count", )]
+        max_labels_count: u16,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct QNameSetSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        qnames: Vec<String>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct QNameSuffixSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        suffixes: Vec<String>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        quiet: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct QNameWireLengthSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        min: u16,
+        max: u16,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct QTypeSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        qtype: String,
+        #[serde(rename = "numeric-value", default, skip_serializing_if = "crate::is_default")]
+        numeric_value: u16,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct RCodeSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        rcode: u64,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct RDSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct RE2SelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        expression: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct RecordsCountSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        section: u8,
+        minimum: u16,
+        maximum: u16,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct RecordsTypeCountSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        section: u8,
+        #[serde(rename = "record-type", )]
+        record_type: u16,
+        minimum: u16,
+        maximum: u16,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct RegexSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        expression: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SNISelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(rename = "server-name", )]
+        server_name: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct TagSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        tag: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        value: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct TCPSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        tcp: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct TrailingDataSelectorConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+    }
+
+    #[derive(Default)]
+    struct GlobalConfiguration {
+        acl: Vec<String>,
+        backends: Vec<BackendConfiguration>,
+        binds: Vec<BindConfiguration>,
+        cache_hit_response_rules: Vec<ResponseRuleConfiguration>,
+        cache_inserted_response_rules: Vec<ResponseRuleConfiguration>,
+        cache_miss_rules: Vec<QueryRuleConfiguration>,
+        cache_settings: CacheSettingsConfiguration,
+        console: ConsoleConfiguration,
+        dynamic_rules: Vec<DynamicRulesConfiguration>,
+        dynamic_rules_settings: DynamicRulesSettingsConfiguration,
+        edns_client_subnet: EdnsClientSubnetConfiguration,
+        general: GeneralConfiguration,
+        key_value_stores: KeyValueStoresConfiguration,
+        load_balancing_policies: LoadBalancingPoliciesConfiguration,
+        metrics: MetricsConfiguration,
+        packet_caches: Vec<PacketCacheConfiguration>,
+        pools: Vec<PoolConfiguration>,
+        proxy_protocol: ProxyProtocolConfiguration,
+        query_count: QueryCountConfiguration,
+        query_rules: Vec<QueryRuleConfiguration>,
+        remote_logging: RemoteLoggingConfiguration,
+        response_rules: Vec<ResponseRuleConfiguration>,
+        ring_buffers: RingBuffersConfiguration,
+        security_polling: SecurityPollingConfiguration,
+        selectors: Vec<SharedDNSSelector>,
+        self_answered_response_rules: Vec<ResponseRuleConfiguration>,
+        snmp: SnmpConfiguration,
+        tuning: TuningConfiguration,
+        webserver: WebserverConfiguration,
+        xfr_response_rules: Vec<ResponseRuleConfiguration>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct MetricsConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        carbon: Vec<CarbonConfiguration>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct CarbonConfiguration {
+        address: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        #[serde(default = "crate::U32::<30>::value", skip_serializing_if = "crate::U32::<30>::is_equal")]
+        interval: u32,
+        #[serde(rename = "namespace", default, skip_serializing_if = "crate::is_default")]
+        name_space: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        instance: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct RemoteLoggingConfiguration {
+        #[serde(rename = "protobuf-loggers", default, skip_serializing_if = "crate::is_default")]
+        protobuf_loggers: Vec<ProtobufLoggerConfiguration>,
+        #[serde(rename = "dnstap-loggers", default, skip_serializing_if = "crate::is_default")]
+        dnstap_loggers: Vec<DnstapLoggerConfiguration>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct ProtobufLoggerConfiguration {
+        name: String,
+        address: String,
+        #[serde(default = "crate::U16::<2>::value", skip_serializing_if = "crate::U16::<2>::is_equal")]
+        timeout: u16,
+        #[serde(rename = "max-queued-entries", default = "crate::U64::<100>::value", skip_serializing_if = "crate::U64::<100>::is_equal")]
+        max_queued_entries: u64,
+        #[serde(rename = "reconnect-wait-time", default = "crate::U8::<1>::value", skip_serializing_if = "crate::U8::<1>::is_equal")]
+        reconnect_wait_time: u8,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct DnstapLoggerConfiguration {
+        name: String,
+        transport: String,
+        address: String,
+        #[serde(rename = "buffer-hint", default, skip_serializing_if = "crate::is_default")]
+        buffer_hint: u64,
+        #[serde(rename = "flush-timeout", default, skip_serializing_if = "crate::is_default")]
+        flush_timeout: u64,
+        #[serde(rename = "input-queue-size", default, skip_serializing_if = "crate::is_default")]
+        input_queue_size: u64,
+        #[serde(rename = "output-queue-size", default, skip_serializing_if = "crate::is_default")]
+        output_queue_size: u64,
+        #[serde(rename = "queue-notify-threshold", default, skip_serializing_if = "crate::is_default")]
+        queue_notify_threshold: u64,
+        #[serde(rename = "reopen-interval", default, skip_serializing_if = "crate::is_default")]
+        reopen_interval: u64,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct ProtoBufMetaConfiguration {
+        key: String,
+        value: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct LMDBKVStoreConfiguration {
+        name: String,
+        #[serde(rename = "file-name", )]
+        file_name: String,
+        #[serde(rename = "database-name", )]
+        database_name: String,
+        #[serde(rename = "no-lock", default, skip_serializing_if = "crate::is_default")]
+        no_lock: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct CDBKVStoreConfiguration {
+        name: String,
+        #[serde(rename = "file-name", )]
+        file_name: String,
+        #[serde(rename = "refresh-delay", )]
+        refresh_delay: u32,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct KVSLookupKeySourceIPConfiguration {
+        name: String,
+        #[serde(rename = "v4-mask", default = "crate::U8::<32>::value", skip_serializing_if = "crate::U8::<32>::is_equal")]
+        v4_mask: u8,
+        #[serde(rename = "v6-mask", default = "crate::U8::<128>::value", skip_serializing_if = "crate::U8::<128>::is_equal")]
+        v6_mask: u8,
+        #[serde(rename = "include-port", default, skip_serializing_if = "crate::is_default")]
+        include_port: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct KVSLookupKeyQNameConfiguration {
+        name: String,
+        #[serde(rename = "wire-format", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        wire_format: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct KVSLookupKeySuffixConfiguration {
+        name: String,
+        #[serde(rename = "minimum-labels", default, skip_serializing_if = "crate::is_default")]
+        minimum_labels: u16,
+        #[serde(rename = "wire-format", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        wire_format: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct KVSLookupKeyTagConfiguration {
+        name: String,
+        tag: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct KVSLookupKeysConfiguration {
+        #[serde(rename = "source-ip-keys", default, skip_serializing_if = "crate::is_default")]
+        source_ip_keys: Vec<KVSLookupKeySourceIPConfiguration>,
+        #[serde(rename = "qname-keys", default, skip_serializing_if = "crate::is_default")]
+        qname_keys: Vec<KVSLookupKeyQNameConfiguration>,
+        #[serde(rename = "suffix-keys", default, skip_serializing_if = "crate::is_default")]
+        suffix_keys: Vec<KVSLookupKeySuffixConfiguration>,
+        #[serde(rename = "tag-keys", default, skip_serializing_if = "crate::is_default")]
+        tag_keys: Vec<KVSLookupKeyTagConfiguration>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct KeyValueStoresConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        lmdb: Vec<LMDBKVStoreConfiguration>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        cdb: Vec<CDBKVStoreConfiguration>,
+        #[serde(rename = "lookup-keys", default, skip_serializing_if = "crate::is_default")]
+        lookup_keys: KVSLookupKeysConfiguration,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct WebserverConfiguration {
+        #[serde(rename = "listen-address", default, skip_serializing_if = "crate::is_default")]
+        listen_address: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        password: String,
+        #[serde(rename = "api-key", default, skip_serializing_if = "crate::is_default")]
+        api_key: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        acl: Vec<String>,
+        #[serde(rename = "api-requires-authentication", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        api_requires_authentication: bool,
+        #[serde(rename = "stats-require-authentication", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        stats_require_authentication: bool,
+        #[serde(rename = "dashboard-requires-authentication", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        dashboard_requires_authentication: bool,
+        #[serde(rename = "max-concurrent-connections", default = "crate::U32::<100>::value", skip_serializing_if = "crate::U32::<100>::is_equal")]
+        max_concurrent_connections: u32,
+        #[serde(rename = "hash-plaintext-credentials", default, skip_serializing_if = "crate::is_default")]
+        hash_plaintext_credentials: bool,
+        #[serde(rename = "custom-headers", default, skip_serializing_if = "crate::is_default")]
+        custom_headers: Vec<HttpCustomResponseHeaderConfiguration>,
+        #[serde(rename = "api-configuration-directory", default, skip_serializing_if = "crate::is_default")]
+        api_configuration_directory: String,
+        #[serde(rename = "api-read-write", default, skip_serializing_if = "crate::is_default")]
+        api_read_write: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct ConsoleConfiguration {
+        #[serde(rename = "listen-address", default, skip_serializing_if = "crate::is_default")]
+        listen_address: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        key: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        acl: Vec<String>,
+        #[serde(rename = "maximum-output-size", default = "crate::U32::<10000000>::value", skip_serializing_if = "crate::U32::<10000000>::is_equal")]
+        maximum_output_size: u32,
+        #[serde(rename = "log-connections", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        log_connections: bool,
+        #[serde(rename = "max-concurrent-connections", default, skip_serializing_if = "crate::is_default")]
+        max_concurrent_connections: u64,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct EdnsClientSubnetConfiguration {
+        #[serde(rename = "override-existing", default, skip_serializing_if = "crate::is_default")]
+        override_existing: bool,
+        #[serde(rename = "source-prefix-v4", default = "crate::U8::<32>::value", skip_serializing_if = "crate::U8::<32>::is_equal")]
+        source_prefix_v4: u8,
+        #[serde(rename = "source-prefix-v6", default = "crate::U8::<56>::value", skip_serializing_if = "crate::U8::<56>::is_equal")]
+        source_prefix_v6: u8,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct DynamicRulesSettingsConfiguration {
+        #[serde(rename = "purge-interval", default = "crate::U64::<60>::value", skip_serializing_if = "crate::U64::<60>::is_equal")]
+        purge_interval: u64,
+        #[serde(rename = "default-action", default = "crate::default_value_dynamic_rules_settings_default_action", skip_serializing_if = "crate::default_value_equal_dynamic_rules_settings_default_action")]
+        default_action: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct DynamicRuleConfiguration {
+        #[serde(rename = "type", )]
+        rule_type: String,
+        seconds: u32,
+        #[serde(rename = "action-duration", )]
+        action_duration: u32,
+        comment: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        rate: u32,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        ratio: f64,
+        #[serde(default = "crate::default_value_dynamic_rule_action", skip_serializing_if = "crate::default_value_equal_dynamic_rule_action")]
+        action: String,
+        #[serde(rename = "warning-rate", default, skip_serializing_if = "crate::is_default")]
+        warning_rate: u32,
+        #[serde(rename = "warning-ratio", default, skip_serializing_if = "crate::is_default")]
+        warning_ratio: f64,
+        #[serde(rename = "tag-name", default, skip_serializing_if = "crate::is_default")]
+        tag_name: String,
+        #[serde(rename = "tag-value", default = "crate::default_value_dynamic_rule_tag_value", skip_serializing_if = "crate::default_value_equal_dynamic_rule_tag_value")]
+        tag_value: String,
+        #[serde(rename = "visitor-function", default, skip_serializing_if = "crate::is_default")]
+        visitor_function: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        rcode: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        qtype: String,
+        #[serde(rename = "minimum-number-of-responses", default, skip_serializing_if = "crate::is_default")]
+        minimum_number_of_responses: u32,
+        #[serde(rename = "minimum-global-cache-hit-ratio", default, skip_serializing_if = "crate::is_default")]
+        minimum_global_cache_hit_ratio: f64,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct DynamicRulesConfiguration {
+        name: String,
+        #[serde(rename = "mask-ipv4", default = "crate::U8::<32>::value", skip_serializing_if = "crate::U8::<32>::is_equal")]
+        mask_ipv4: u8,
+        #[serde(rename = "mask-ipv6", default = "crate::U8::<64>::value", skip_serializing_if = "crate::U8::<64>::is_equal")]
+        mask_ipv6: u8,
+        #[serde(rename = "mask-port", default, skip_serializing_if = "crate::is_default")]
+        mask_port: u8,
+        #[serde(rename = "exclude-ranges", default, skip_serializing_if = "crate::is_default")]
+        exclude_ranges: Vec<String>,
+        #[serde(rename = "include-ranges", default, skip_serializing_if = "crate::is_default")]
+        include_ranges: Vec<String>,
+        #[serde(rename = "exclude-domains", default, skip_serializing_if = "crate::is_default")]
+        exclude_domains: Vec<String>,
+        rules: Vec<DynamicRuleConfiguration>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct RingBuffersConfiguration {
+        #[serde(default = "crate::U64::<10000>::value", skip_serializing_if = "crate::U64::<10000>::is_equal")]
+        size: u64,
+        #[serde(default = "crate::U64::<10>::value", skip_serializing_if = "crate::U64::<10>::is_equal")]
+        shards: u64,
+        #[serde(rename = "lock-retries", default = "crate::U64::<5>::value", skip_serializing_if = "crate::U64::<5>::is_equal")]
+        lock_retries: u64,
+        #[serde(rename = "record-queries", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        record_queries: bool,
+        #[serde(rename = "record-responses", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        record_responses: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct IncomingTlsCertificateKeyPairConfiguration {
+        certificate: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        key: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        password: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct IncomingTlsConfiguration {
+        #[serde(default = "crate::default_value_incoming_tls_provider", skip_serializing_if = "crate::default_value_equal_incoming_tls_provider")]
+        provider: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        certificates: Vec<IncomingTlsCertificateKeyPairConfiguration>,
+        #[serde(rename = "ignore-errors", default, skip_serializing_if = "crate::is_default")]
+        ignore_errors: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        ciphers: String,
+        #[serde(rename = "ciphers-tls-13", default, skip_serializing_if = "crate::is_default")]
+        ciphers_tls_13: String,
+        #[serde(rename = "minimum-version", default = "crate::default_value_incoming_tls_minimum_version", skip_serializing_if = "crate::default_value_equal_incoming_tls_minimum_version")]
+        minimum_version: String,
+        #[serde(rename = "ticket-key-file", default, skip_serializing_if = "crate::is_default")]
+        ticket_key_file: String,
+        #[serde(rename = "tickets-keys-rotation-delay", default = "crate::U32::<43200>::value", skip_serializing_if = "crate::U32::<43200>::is_equal")]
+        tickets_keys_rotation_delay: u32,
+        #[serde(rename = "number-of-tickets-keys", default = "crate::U32::<5>::value", skip_serializing_if = "crate::U32::<5>::is_equal")]
+        number_of_tickets_keys: u32,
+        #[serde(rename = "prefer-server-ciphers", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        prefer_server_ciphers: bool,
+        #[serde(rename = "session-timeout", default, skip_serializing_if = "crate::is_default")]
+        session_timeout: u32,
+        #[serde(rename = "session-tickets", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        session_tickets: bool,
+        #[serde(rename = "number-of-stored-sessions", default = "crate::U32::<20480>::value", skip_serializing_if = "crate::U32::<20480>::is_equal")]
+        number_of_stored_sessions: u32,
+        #[serde(rename = "ocsp-response-files", default, skip_serializing_if = "crate::is_default")]
+        ocsp_response_files: Vec<String>,
+        #[serde(rename = "key-log-file", default, skip_serializing_if = "crate::is_default")]
+        key_log_file: String,
+        #[serde(rename = "release-buffers", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        release_buffers: bool,
+        #[serde(rename = "enable-renegotiation", default, skip_serializing_if = "crate::is_default")]
+        enable_renegotiation: bool,
+        #[serde(rename = "async-mode", default, skip_serializing_if = "crate::is_default")]
+        async_mode: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        ktls: bool,
+        #[serde(rename = "read-ahead", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        read_ahead: bool,
+        #[serde(rename = "proxy-protocol-outside-tls", default, skip_serializing_if = "crate::is_default")]
+        proxy_protocol_outside_tls: bool,
+        #[serde(rename = "ignore-configuration-errors", default, skip_serializing_if = "crate::is_default")]
+        ignore_configuration_errors: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct OutgoingTlsConfiguration {
+        #[serde(default = "crate::default_value_outgoing_tls_provider", skip_serializing_if = "crate::default_value_equal_outgoing_tls_provider")]
+        provider: String,
+        #[serde(rename = "subject-name", default, skip_serializing_if = "crate::is_default")]
+        subject_name: String,
+        #[serde(rename = "subject-address", default, skip_serializing_if = "crate::is_default")]
+        subject_address: String,
+        #[serde(rename = "validate-certificate", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        validate_certificate: bool,
+        #[serde(rename = "ca-store", default, skip_serializing_if = "crate::is_default")]
+        ca_store: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        ciphers: String,
+        #[serde(rename = "ciphers-tls-13", default, skip_serializing_if = "crate::is_default")]
+        ciphers_tls_13: String,
+        #[serde(rename = "key-log-file", default, skip_serializing_if = "crate::is_default")]
+        key_log_file: String,
+        #[serde(rename = "release-buffers", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        release_buffers: bool,
+        #[serde(rename = "enable-renegotiation", default, skip_serializing_if = "crate::is_default")]
+        enable_renegotiation: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        ktls: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct HttpCustomResponseHeaderConfiguration {
+        key: String,
+        value: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct HttpResponsesMapConfiguration {
+        expression: String,
+        status: u16,
+        content: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        headers: Vec<HttpCustomResponseHeaderConfiguration>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct IncomingDohConfiguration {
+        #[serde(default = "crate::default_value_incoming_doh_provider", skip_serializing_if = "crate::default_value_equal_incoming_doh_provider")]
+        provider: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        paths: Vec<String>,
+        #[serde(rename = "idle-timeout", default = "crate::U64::<30>::value", skip_serializing_if = "crate::U64::<30>::is_equal")]
+        idle_timeout: u64,
+        #[serde(rename = "server-tokens", default = "crate::default_value_incoming_doh_server_tokens", skip_serializing_if = "crate::default_value_equal_incoming_doh_server_tokens")]
+        server_tokens: String,
+        #[serde(rename = "send-cache-control-headers", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        send_cache_control_headers: bool,
+        #[serde(rename = "keep-incoming-headers", default, skip_serializing_if = "crate::is_default")]
+        keep_incoming_headers: bool,
+        #[serde(rename = "trust-forwarded-for-header", default, skip_serializing_if = "crate::is_default")]
+        trust_forwarded_for_header: bool,
+        #[serde(rename = "early-acl-drop", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        early_acl_drop: bool,
+        #[serde(rename = "exact-path-matching", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        exact_path_matching: bool,
+        #[serde(rename = "internal-pipe-buffer-size", default = "crate::U32::<1048576>::value", skip_serializing_if = "crate::U32::<1048576>::is_equal")]
+        internal_pipe_buffer_size: u32,
+        #[serde(rename = "custom-response-headers", default, skip_serializing_if = "crate::is_default")]
+        custom_response_headers: Vec<HttpCustomResponseHeaderConfiguration>,
+        #[serde(rename = "responses-map", default, skip_serializing_if = "crate::is_default")]
+        responses_map: Vec<HttpResponsesMapConfiguration>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct IncomingDoqConfiguration {
+        #[serde(default = "crate::U64::<65535>::value", skip_serializing_if = "crate::U64::<65535>::is_equal")]
+        max_concurrent_queries_per_connection: u64,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct IncomingQuicConfiguration {
+        #[serde(rename = "idle-timeout", default = "crate::U64::<5>::value", skip_serializing_if = "crate::U64::<5>::is_equal")]
+        idle_timeout: u64,
+        #[serde(rename = "congestion-control-algorithm", default = "crate::default_value_incoming_quic_congestion_control_algorithm", skip_serializing_if = "crate::default_value_equal_incoming_quic_congestion_control_algorithm")]
+        congestion_control_algorithm: String,
+        #[serde(rename = "internal-pipe-buffer-size", default = "crate::U32::<1048576>::value", skip_serializing_if = "crate::U32::<1048576>::is_equal")]
+        internal_pipe_buffer_size: u32,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct OutgoingDohConfiguration {
+        #[serde(default = "crate::default_value_outgoing_doh_path", skip_serializing_if = "crate::default_value_equal_outgoing_doh_path")]
+        path: String,
+        #[serde(rename = "add-x-forwarded-headers", default, skip_serializing_if = "crate::is_default")]
+        add_x_forwarded_headers: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct IncomingTcpConfiguration {
+        #[serde(rename = "max-in-flight-queries", default, skip_serializing_if = "crate::is_default")]
+        max_in_flight_queries: u32,
+        #[serde(rename = "listen-queue-size", default, skip_serializing_if = "crate::is_default")]
+        listen_queue_size: u32,
+        #[serde(rename = "fast-open-queue-size", default, skip_serializing_if = "crate::is_default")]
+        fast_open_queue_size: u32,
+        #[serde(rename = "max-concurrent-connections", default, skip_serializing_if = "crate::is_default")]
+        max_concurrent_connections: u32,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct BindConfiguration {
+        #[serde(rename = "listen-address", )]
+        listen_address: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        reuseport: bool,
+        #[serde(default = "crate::default_value_bind_protocol", skip_serializing_if = "crate::default_value_equal_bind_protocol")]
+        protocol: String,
+        #[serde(default = "crate::U32::<1>::value", skip_serializing_if = "crate::U32::<1>::is_equal")]
+        threads: u32,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        interface: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        cpus: String,
+        #[serde(rename = "enable-proxy-protocol", default, skip_serializing_if = "crate::is_default")]
+        enable_proxy_protocol: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        tcp: IncomingTcpConfiguration,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        tls: IncomingTlsConfiguration,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        doh: IncomingDohConfiguration,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        doq: IncomingDoqConfiguration,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        quic: IncomingQuicConfiguration,
+        #[serde(rename = "additional-addresses", default, skip_serializing_if = "crate::is_default")]
+        additional_addresses: Vec<String>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct OutgoingTcpConfiguration {
+        #[serde(rename = "connect-timeout", default = "crate::U16::<5>::value", skip_serializing_if = "crate::U16::<5>::is_equal")]
+        connect_timeout: u16,
+        #[serde(rename = "send-timeout", default = "crate::U16::<30>::value", skip_serializing_if = "crate::U16::<30>::is_equal")]
+        send_timeout: u16,
+        #[serde(rename = "receive-timeout", default = "crate::U16::<30>::value", skip_serializing_if = "crate::U16::<30>::is_equal")]
+        receive_timeout: u16,
+        #[serde(rename = "fast-open", default, skip_serializing_if = "crate::is_default")]
+        fast_open: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct ProxyProtocolValueConfiguration {
+        key: u8,
+        value: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct LazyHealthCheckConfiguration {
+        #[serde(default = "crate::U16::<30>::value", skip_serializing_if = "crate::U16::<30>::is_equal")]
+        interval: u16,
+        #[serde(rename = "min-sample-count", default = "crate::U16::<1>::value", skip_serializing_if = "crate::U16::<1>::is_equal")]
+        min_sample_count: u16,
+        #[serde(default = "crate::default_value_lazy_health_check_mode", skip_serializing_if = "crate::default_value_equal_lazy_health_check_mode")]
+        mode: String,
+        #[serde(rename = "sample-size", default = "crate::U16::<100>::value", skip_serializing_if = "crate::U16::<100>::is_equal")]
+        sample_size: u16,
+        #[serde(default = "crate::U16::<20>::value", skip_serializing_if = "crate::U16::<20>::is_equal")]
+        threshold: u16,
+        #[serde(rename = "use-exponential-back-off", default, skip_serializing_if = "crate::is_default")]
+        use_exponential_back_off: bool,
+        #[serde(rename = "max-back-off", default = "crate::U16::<3600>::value", skip_serializing_if = "crate::U16::<3600>::is_equal")]
+        max_back_off: u16,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct HealthCheckConfiguration {
+        #[serde(default = "crate::default_value_health_check_mode", skip_serializing_if = "crate::default_value_equal_health_check_mode")]
+        mode: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        qname: String,
+        #[serde(default = "crate::default_value_health_check_qclass", skip_serializing_if = "crate::default_value_equal_health_check_qclass")]
+        qclass: String,
+        #[serde(default = "crate::default_value_health_check_qtype", skip_serializing_if = "crate::default_value_equal_health_check_qtype")]
+        qtype: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        function: String,
+        #[serde(default = "crate::U16::<1000>::value", skip_serializing_if = "crate::U16::<1000>::is_equal")]
+        timeout: u16,
+        #[serde(rename = "set-cd", default, skip_serializing_if = "crate::is_default")]
+        set_cd: bool,
+        #[serde(rename = "max-failures", default = "crate::U8::<1>::value", skip_serializing_if = "crate::U8::<1>::is_equal")]
+        max_failures: u8,
+        #[serde(default = "crate::U8::<1>::value", skip_serializing_if = "crate::U8::<1>::is_equal")]
+        rise: u8,
+        #[serde(default = "crate::U32::<1>::value", skip_serializing_if = "crate::U32::<1>::is_equal")]
+        interval: u32,
+        #[serde(rename = "must-resolve", default, skip_serializing_if = "crate::is_default")]
+        must_resolve: bool,
+        #[serde(rename = "use-tcp", default, skip_serializing_if = "crate::is_default")]
+        use_tcp: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        lazy: LazyHealthCheckConfiguration,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct OutgoingAutoUpgradeConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        enabled: bool,
+        #[serde(default = "crate::U32::<3600>::value", skip_serializing_if = "crate::U32::<3600>::is_equal")]
+        interval: u32,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        keep: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        pool: String,
+        #[serde(rename = "doh-key", default = "crate::U8::<7>::value", skip_serializing_if = "crate::U8::<7>::is_equal")]
+        doh_key: u8,
+        #[serde(rename = "use-lazy-health-check", default, skip_serializing_if = "crate::is_default")]
+        use_lazy_health_check: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct BackendConfiguration {
+        address: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        id: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        name: String,
+        protocol: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        tls: OutgoingTlsConfiguration,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        doh: OutgoingDohConfiguration,
+        #[serde(rename = "use-client-subnet", default, skip_serializing_if = "crate::is_default")]
+        use_client_subnet: bool,
+        #[serde(rename = "use-proxy-protocol", default, skip_serializing_if = "crate::is_default")]
+        use_proxy_protocol: bool,
+        #[serde(rename = "queries-per-second", default, skip_serializing_if = "crate::is_default")]
+        queries_per_second: u32,
+        #[serde(default = "crate::U32::<1>::value", skip_serializing_if = "crate::U32::<1>::is_equal")]
+        order: u32,
+        #[serde(default = "crate::U32::<1>::value", skip_serializing_if = "crate::U32::<1>::is_equal")]
+        weight: u32,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        pools: Vec<String>,
+        #[serde(default = "crate::U16::<5>::value", skip_serializing_if = "crate::U16::<5>::is_equal")]
+        retries: u16,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        tcp: OutgoingTcpConfiguration,
+        #[serde(rename = "ip-bind-addr-no-port", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        ip_bind_addr_no_port: bool,
+        #[serde(rename = "health-checks", default, skip_serializing_if = "crate::is_default")]
+        health_checks: HealthCheckConfiguration,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        source: String,
+        #[serde(default = "crate::U32::<1>::value", skip_serializing_if = "crate::U32::<1>::is_equal")]
+        sockets: u32,
+        #[serde(rename = "disable-zero-scope", default, skip_serializing_if = "crate::is_default")]
+        disable_zero_scope: bool,
+        #[serde(rename = "reconnect-on-up", default, skip_serializing_if = "crate::is_default")]
+        reconnect_on_up: bool,
+        #[serde(rename = "max-in-flight", default = "crate::U32::<1>::value", skip_serializing_if = "crate::U32::<1>::is_equal")]
+        max_in_flight: u32,
+        #[serde(rename = "tcp-only", default, skip_serializing_if = "crate::is_default")]
+        tcp_only: bool,
+        #[serde(rename = "auto-upgrade", default, skip_serializing_if = "crate::is_default")]
+        auto_upgrade: OutgoingAutoUpgradeConfiguration,
+        #[serde(rename = "max-concurrent-tcp-connections", default, skip_serializing_if = "crate::is_default")]
+        max_concurrent_tcp_connections: u32,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        ktls: bool,
+        #[serde(rename = "proxy-protocol-advertise-tls", default, skip_serializing_if = "crate::is_default")]
+        proxy_protocol_advertise_tls: bool,
+        #[serde(rename = "xsk-sockets", default, skip_serializing_if = "crate::is_default")]
+        xsk_sockets: Vec<String>,
+        #[serde(rename = "mac-address", default, skip_serializing_if = "crate::is_default")]
+        mac_address: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        cpus: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct TuningConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        doh: DohTuningConfiguration,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        tcp: TcpTuningConfiguration,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        tls: TlsTuningConfiguration,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        udp: UdpTuningConfiguration,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct TcpTuningConfiguration {
+        #[serde(rename = "worker-threads", default = "crate::U32::<10>::value", skip_serializing_if = "crate::U32::<10>::is_equal")]
+        worker_threads: u32,
+        #[serde(rename = "receive-timeout", default = "crate::U32::<2>::value", skip_serializing_if = "crate::U32::<2>::is_equal")]
+        receive_timeout: u32,
+        #[serde(rename = "send-timeout", default = "crate::U32::<2>::value", skip_serializing_if = "crate::U32::<2>::is_equal")]
+        send_timeout: u32,
+        #[serde(rename = "max-queries-per-connection", default, skip_serializing_if = "crate::is_default")]
+        max_queries_per_connection: u64,
+        #[serde(rename = "max-connection-duration", default, skip_serializing_if = "crate::is_default")]
+        max_connection_duration: u64,
+        #[serde(rename = "max-queued-connections", default = "crate::U64::<10000>::value", skip_serializing_if = "crate::U64::<10000>::is_equal")]
+        max_queued_connections: u64,
+        #[serde(rename = "internal-pipe-buffer-size", default = "crate::U32::<1048576>::value", skip_serializing_if = "crate::U32::<1048576>::is_equal")]
+        internal_pipe_buffer_size: u32,
+        #[serde(rename = "outgoing-max-idle-time", default = "crate::U64::<300>::value", skip_serializing_if = "crate::U64::<300>::is_equal")]
+        outgoing_max_idle_time: u64,
+        #[serde(rename = "outgoing-cleanup-interval", default = "crate::U64::<60>::value", skip_serializing_if = "crate::U64::<60>::is_equal")]
+        outgoing_cleanup_interval: u64,
+        #[serde(rename = "outgoing-max-idle-connection-per-backend", default = "crate::U64::<10>::value", skip_serializing_if = "crate::U64::<10>::is_equal")]
+        outgoing_max_idle_connection_per_backend: u64,
+        #[serde(rename = "max-connections-per-client", default, skip_serializing_if = "crate::is_default")]
+        max_connections_per_client: u32,
+        #[serde(rename = "fast-open-key", default, skip_serializing_if = "crate::is_default")]
+        fast_open_key: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct UdpTuningConfiguration {
+        #[serde(rename = "messages-per-round", default = "crate::U32::<1>::value", skip_serializing_if = "crate::U32::<1>::is_equal")]
+        messages_per_round: u32,
+        #[serde(rename = "send-buffer-size", default, skip_serializing_if = "crate::is_default")]
+        send_buffer_size: u32,
+        #[serde(rename = "receive-buffer-size", default, skip_serializing_if = "crate::is_default")]
+        receive_buffer_size: u32,
+        #[serde(rename = "max-outstanding-per-backend", default = "crate::U32::<65535>::value", skip_serializing_if = "crate::U32::<65535>::is_equal")]
+        max_outstanding_per_backend: u32,
+        #[serde(default = "crate::U8::<2>::value", skip_serializing_if = "crate::U8::<2>::is_equal")]
+        timeout: u8,
+        #[serde(rename = "randomize-outgoing-sockets-to-backend", default, skip_serializing_if = "crate::is_default")]
+        randomize_outgoing_sockets_to_backend: bool,
+        #[serde(rename = "randomize-ids-to-backend", default, skip_serializing_if = "crate::is_default")]
+        randomize_ids_to_backend: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct TlsTuningConfiguration {
+        #[serde(rename = "outgoing-tickets-cache-cleanup-delay", default = "crate::U16::<60>::value", skip_serializing_if = "crate::U16::<60>::is_equal")]
+        outgoing_tickets_cache_cleanup_delay: u16,
+        #[serde(rename = "outgoing-tickets-cache-validity", default = "crate::U16::<600>::value", skip_serializing_if = "crate::U16::<600>::is_equal")]
+        outgoing_tickets_cache_validity: u16,
+        #[serde(rename = "max-outgoing-tickets-per-backend", default = "crate::U16::<20>::value", skip_serializing_if = "crate::U16::<20>::is_equal")]
+        max_outgoing_tickets_per_backend: u16,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct DohTuningConfiguration {
+        #[serde(rename = "outgoing-worker-threads", default = "crate::U32::<10>::value", skip_serializing_if = "crate::U32::<10>::is_equal")]
+        outgoing_worker_threads: u32,
+        #[serde(rename = "outgoing-max-idle-time", default = "crate::U64::<300>::value", skip_serializing_if = "crate::U64::<300>::is_equal")]
+        outgoing_max_idle_time: u64,
+        #[serde(rename = "outgoing-cleanup-interval", default = "crate::U64::<60>::value", skip_serializing_if = "crate::U64::<60>::is_equal")]
+        outgoing_cleanup_interval: u64,
+        #[serde(rename = "outgoing-max-idle-connection-per-backend", default = "crate::U64::<10>::value", skip_serializing_if = "crate::U64::<10>::is_equal")]
+        outgoing_max_idle_connection_per_backend: u64,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct CacheSettingsConfiguration {
+        #[serde(rename = "stale-entries-ttl", default, skip_serializing_if = "crate::is_default")]
+        stale_entries_ttl: u32,
+        #[serde(rename = "cleaning-delay", default = "crate::U16::<60>::value", skip_serializing_if = "crate::U16::<60>::is_equal")]
+        cleaning_delay: u16,
+        #[serde(rename = "cleaning-percentage", default = "crate::U16::<100>::value", skip_serializing_if = "crate::U16::<100>::is_equal")]
+        cleaning_percentage: u16,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SecurityPollingConfiguration {
+        #[serde(rename = "polling-interval", default = "crate::U32::<3600>::value", skip_serializing_if = "crate::U32::<3600>::is_equal")]
+        polling_interval: u32,
+        #[serde(default = "crate::default_value_security_polling_suffix", skip_serializing_if = "crate::default_value_equal_security_polling_suffix")]
+        suffix: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct GeneralConfiguration {
+        #[serde(rename = "edns-udp-payload-size-self-generated-answers", default = "crate::U16::<1232>::value", skip_serializing_if = "crate::U16::<1232>::is_equal")]
+        edns_udp_payload_size_self_generated_answers: u16,
+        #[serde(rename = "add-edns-to-self-generated-answers", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        add_edns_to_self_generated_answers: bool,
+        #[serde(rename = "truncate-tc-answers", default, skip_serializing_if = "crate::is_default")]
+        truncate_tc_answers: bool,
+        #[serde(rename = "fixup-case", default, skip_serializing_if = "crate::is_default")]
+        fixup_case: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        verbose: bool,
+        #[serde(rename = "verbose-health-checks", default, skip_serializing_if = "crate::is_default")]
+        verbose_health_checks: bool,
+        #[serde(rename = "allow-empty-responses", default, skip_serializing_if = "crate::is_default")]
+        allow_empty_responses: bool,
+        #[serde(rename = "drop-empty-queries", default, skip_serializing_if = "crate::is_default")]
+        drop_empty_queries: bool,
+        #[serde(rename = "capabilities-to-retain", default, skip_serializing_if = "crate::is_default")]
+        capabilities_to_retain: Vec<String>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct PacketCacheConfiguration {
+        name: String,
+        size: u64,
+        #[serde(rename = "deferrable-insert-lock", default = "crate::Bool::<true>::value", skip_serializing_if = "crate::if_true")]
+        deferrable_insert_lock: bool,
+        #[serde(rename = "dont-age", default, skip_serializing_if = "crate::is_default")]
+        dont_age: bool,
+        #[serde(rename = "keep-stale-data", default, skip_serializing_if = "crate::is_default")]
+        keep_stale_data: bool,
+        #[serde(rename = "max-negative-ttl", default = "crate::U32::<3600>::value", skip_serializing_if = "crate::U32::<3600>::is_equal")]
+        max_negative_ttl: u32,
+        #[serde(rename = "max-ttl", default = "crate::U32::<86400>::value", skip_serializing_if = "crate::U32::<86400>::is_equal")]
+        max_ttl: u32,
+        #[serde(rename = "min-ttl", default, skip_serializing_if = "crate::is_default")]
+        min_ttl: u32,
+        #[serde(default = "crate::U32::<20>::value", skip_serializing_if = "crate::U32::<20>::is_equal")]
+        shards: u32,
+        #[serde(rename = "parse-ecs", default, skip_serializing_if = "crate::is_default")]
+        parse_ecs: bool,
+        #[serde(rename = "stale-ttl", default = "crate::U32::<60>::value", skip_serializing_if = "crate::U32::<60>::is_equal")]
+        stale_ttl: u32,
+        #[serde(rename = "temporary-failure-ttl", default = "crate::U32::<60>::value", skip_serializing_if = "crate::U32::<60>::is_equal")]
+        temporary_failure_ttl: u32,
+        #[serde(rename = "cookie-hashing", default, skip_serializing_if = "crate::is_default")]
+        cookie_hashing: bool,
+        #[serde(rename = "maximum-entry-size", default, skip_serializing_if = "crate::is_default")]
+        maximum_entry_size: u32,
+        #[serde(rename = "options-to-skip", default, skip_serializing_if = "crate::is_default")]
+        options_to_skip: Vec<String>,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct ProxyProtocolConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        acl: Vec<String>,
+        #[serde(rename = "maximum-payload-size", default = "crate::U32::<512>::value", skip_serializing_if = "crate::U32::<512>::is_equal")]
+        maximum_payload_size: u32,
+        #[serde(rename = "apply-acl-to-proxied-clients", default, skip_serializing_if = "crate::is_default")]
+        apply_acl_to_proxied_clients: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct SnmpConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        enabled: bool,
+        #[serde(rename = "traps-enabled", default, skip_serializing_if = "crate::is_default")]
+        traps_enabled: bool,
+        #[serde(rename = "daemon-socket", default, skip_serializing_if = "crate::is_default")]
+        daemon_socket: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct QueryCountConfiguration {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        enabled: bool,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        filter: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct PoolConfiguration {
+        name: String,
+        #[serde(rename = "packet-cache", default, skip_serializing_if = "crate::is_default")]
+        packet_cache: String,
+        #[serde(default = "crate::default_value_pool_policy", skip_serializing_if = "crate::default_value_equal_pool_policy")]
+        policy: String,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct CustomLoadBalancingPolicyConfiguration {
+        name: String,
+        function: String,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        ffi: bool,
+        #[serde(rename = "per-thread", default, skip_serializing_if = "crate::is_default")]
+        per_thread: bool,
+    }
+
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct LoadBalancingPoliciesConfiguration {
+        #[serde(rename = "servfail-on-no-server", default, skip_serializing_if = "crate::is_default")]
+        servfail_on_no_server: bool,
+        #[serde(rename = "round-robin-servfail-on-no-server", default, skip_serializing_if = "crate::is_default")]
+        round_robin_servfail_on_no_server: bool,
+        #[serde(rename = "weighted-balancing-factor", default, skip_serializing_if = "crate::is_default")]
+        weighted_balancing_factor: f64,
+        #[serde(rename = "consistent-hashing-balancing-factor", default, skip_serializing_if = "crate::is_default")]
+        consistent_hashing_balancing_factor: f64,
+        #[serde(rename = "custom-policies", default, skip_serializing_if = "crate::is_default")]
+        custom_policies: Vec<CustomLoadBalancingPolicyConfiguration>,
+        #[serde(rename = "hash-perturbation", default, skip_serializing_if = "crate::is_default")]
+        hash_perturbation: u32,
+    }
+
+    struct QueryRuleConfiguration {
+        name: String,
+        uuid: String,
+        selector: SharedDNSSelector,
+        action: SharedDNSAction,
+    }
+
+    struct ResponseRuleConfiguration {
+        name: String,
+        uuid: String,
+        selector: SharedDNSSelector,
+        action: SharedDNSResponseAction,
+    }
+
+
+    /*
+     * Functions callable from Rust (actions and selectors)
+     */
+    unsafe extern "C++" {
+        fn getAllowAction(config: &AllowActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getContinueAction(config: &ContinueActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getDelayAction(config: &DelayActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getDnstapLogAction(config: &DnstapLogActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getDropAction(config: &DropActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSetEDNSOptionAction(config: &SetEDNSOptionActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getERCodeAction(config: &ERCodeActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getHTTPStatusAction(config: &HTTPStatusActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getKeyValueStoreLookupAction(config: &KeyValueStoreLookupActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getKeyValueStoreRangeLookupAction(config: &KeyValueStoreRangeLookupActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getLogAction(config: &LogActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getLuaAction(config: &LuaActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getLuaFFIAction(config: &LuaFFIActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getLuaFFIPerThreadAction(config: &LuaFFIPerThreadActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getNegativeAndSOAAction(config: &NegativeAndSOAActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getNoneAction(config: &NoneActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getPoolAction(config: &PoolActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getQPSAction(config: &QPSActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getQPSPoolAction(config: &QPSPoolActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getRCodeAction(config: &RCodeActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getRemoteLogAction(config: &RemoteLogActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSetAdditionalProxyProtocolValueAction(config: &SetAdditionalProxyProtocolValueActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSetDisableECSAction(config: &SetDisableECSActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSetDisableValidationAction(config: &SetDisableValidationActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSetECSAction(config: &SetECSActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSetECSOverrideAction(config: &SetECSOverrideActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSetECSPrefixLengthAction(config: &SetECSPrefixLengthActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSetExtendedDNSErrorAction(config: &SetExtendedDNSErrorActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSetMacAddrAction(config: &SetMacAddrActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSetMaxReturnedTTLAction(config: &SetMaxReturnedTTLActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSetNoRecurseAction(config: &SetNoRecurseActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSetProxyProtocolValuesAction(config: &SetProxyProtocolValuesActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSetSkipCacheAction(config: &SetSkipCacheActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSetTagAction(config: &SetTagActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSetTempFailureCacheTTLAction(config: &SetTempFailureCacheTTLActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSNMPTrapAction(config: &SNMPTrapActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSpoofAction(config: &SpoofActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSpoofCNAMEAction(config: &SpoofCNAMEActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSpoofPacketAction(config: &SpoofPacketActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSpoofRawAction(config: &SpoofRawActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getSpoofSVCAction(config: &SpoofSVCActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getTCAction(config: &TCActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getTeeAction(config: &TeeActionConfiguration) -> SharedPtr<DNSActionWrapper>;
+        fn getAllowResponseAction(config: &AllowResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getClearRecordTypesResponseAction(config: &ClearRecordTypesResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getDelayResponseAction(config: &DelayResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getDnstapLogResponseAction(config: &DnstapLogResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getDropResponseAction(config: &DropResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getLimitTTLResponseAction(config: &LimitTTLResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getLogResponseAction(config: &LogResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getLuaResponseAction(config: &LuaResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getLuaFFIResponseAction(config: &LuaFFIResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getLuaFFIPerThreadResponseAction(config: &LuaFFIPerThreadResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getRemoteLogResponseAction(config: &RemoteLogResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getSetExtendedDNSErrorResponseAction(config: &SetExtendedDNSErrorResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getSetMaxReturnedTTLResponseAction(config: &SetMaxReturnedTTLResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getSetMaxTTLResponseAction(config: &SetMaxTTLResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getSetMinTTLResponseAction(config: &SetMinTTLResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getSetReducedTTLResponseAction(config: &SetReducedTTLResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getSetSkipCacheResponseAction(config: &SetSkipCacheResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getSetTagResponseAction(config: &SetTagResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getSNMPTrapResponseAction(config: &SNMPTrapResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getTCResponseAction(config: &TCResponseActionConfiguration) -> SharedPtr<DNSResponseActionWrapper>;
+        fn getAllSelector(config: &AllSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getAndSelector(config: &AndSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getByNameSelector(config: &ByNameSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getDNSSECSelector(config: &DNSSECSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getDSTPortSelector(config: &DSTPortSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getEDNSOptionSelector(config: &EDNSOptionSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getEDNSVersionSelector(config: &EDNSVersionSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getERCodeSelector(config: &ERCodeSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getHTTPHeaderSelector(config: &HTTPHeaderSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getHTTPPathSelector(config: &HTTPPathSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getHTTPPathRegexSelector(config: &HTTPPathRegexSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getKeyValueStoreLookupSelector(config: &KeyValueStoreLookupSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getKeyValueStoreRangeLookupSelector(config: &KeyValueStoreRangeLookupSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getLuaSelector(config: &LuaSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getLuaFFISelector(config: &LuaFFISelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getLuaFFIPerThreadSelector(config: &LuaFFIPerThreadSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getMaxQPSSelector(config: &MaxQPSSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getMaxQPSIPSelector(config: &MaxQPSIPSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getNetmaskGroupSelector(config: &NetmaskGroupSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getNotSelector(config: &NotSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getOpcodeSelector(config: &OpcodeSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getOrSelector(config: &OrSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getPayloadSizeSelector(config: &PayloadSizeSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getPoolAvailableSelector(config: &PoolAvailableSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getPoolOutstandingSelector(config: &PoolOutstandingSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getProbaSelector(config: &ProbaSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getProxyProtocolValueSelector(config: &ProxyProtocolValueSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getQClassSelector(config: &QClassSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getQNameSelector(config: &QNameSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getQNameLabelsCountSelector(config: &QNameLabelsCountSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getQNameSetSelector(config: &QNameSetSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getQNameSuffixSelector(config: &QNameSuffixSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getQNameWireLengthSelector(config: &QNameWireLengthSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getQTypeSelector(config: &QTypeSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getRCodeSelector(config: &RCodeSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getRDSelector(config: &RDSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getRE2Selector(config: &RE2SelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getRecordsCountSelector(config: &RecordsCountSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getRecordsTypeCountSelector(config: &RecordsTypeCountSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getRegexSelector(config: &RegexSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getSNISelector(config: &SNISelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getTagSelector(config: &TagSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getTCPSelector(config: &TCPSelectorConfiguration) -> SharedPtr<DNSSelector>;
+        fn getTrailingDataSelector(config: &TrailingDataSelectorConfiguration) -> SharedPtr<DNSSelector>;
+    }
+// START INCLUDE ./rust-middle-in.rs
+    /*
+     * Functions callable from C++
+     */
+    extern "Rust" {
+        fn from_yaml_string(str: &str) -> Result<GlobalConfiguration>;
+    }
+    /*
+     * Functions callable from Rust
+     */
+    unsafe extern "C++" {
+        include!("dnsdist-rust-bridge.hh");
+        type DNSSelector;
+        type DNSActionWrapper;
+        type DNSResponseActionWrapper;
+        fn registerProtobufLogger(config: &ProtobufLoggerConfiguration);
+        fn registerDnstapLogger(config: &DnstapLoggerConfiguration);
+        fn registerKVSObjects(config: &KeyValueStoresConfiguration);
+    }
+}
+
+impl Default for dnsdistsettings::SharedDNSAction {
+    fn default() -> dnsdistsettings::SharedDNSAction {
+        dnsdistsettings::SharedDNSAction {
+            action: cxx::SharedPtr::null(),
+        }
+    }
+}
+
+impl Default for dnsdistsettings::SharedDNSSelector {
+    fn default() -> dnsdistsettings::SharedDNSSelector {
+        dnsdistsettings::SharedDNSSelector {
+            selector: cxx::SharedPtr::null(),
+        }
+    }
+}
+
+#[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+#[serde(deny_unknown_fields)]
+struct AndSelectorConfigurationSerde {
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    selectors: Vec<Selector>,
+}
+
+#[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+#[serde(deny_unknown_fields)]
+struct OrSelectorConfigurationSerde {
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    selectors: Vec<Selector>,
+}
+
+#[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+#[serde(deny_unknown_fields)]
+struct NotSelectorConfigurationSerde {
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    selector: Box<Selector>,
+}
+
+#[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+#[serde(deny_unknown_fields)]
+struct ContinueActionConfigurationSerde {
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    action: Box<Action>,
+}
+
+impl Selector {
+  fn validate(&self) -> Result<(), ValidationError> {
+    Ok(())
+  }
+}
+
+impl Action {
+  fn validate(&self) -> Result<(), ValidationError> {
+    Ok(())
+  }
+}
+
+impl ResponseAction {
+  fn validate(&self) -> Result<(), ValidationError> {
+    Ok(())
+  }
+}
+
+#[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+#[serde(deny_unknown_fields)]
+struct QueryRuleConfigurationSerde {
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    name: String,
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    uuid: String,
+    selector: Selector,
+    action: Action,
+}
+
+impl QueryRuleConfigurationSerde {
+  fn validate(&self) -> Result<(), ValidationError> {
+    Ok(())
+  }
+}
+
+#[derive(Default, Deserialize, Serialize, Debug, PartialEq)]
+#[serde(deny_unknown_fields)]
+struct ResponseRuleConfigurationSerde {
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    name: String,
+    #[serde(default, skip_serializing_if = "crate::is_default")]
+    uuid: String,
+    selector: Selector,
+    action: ResponseAction,
+}
+
+impl ResponseRuleConfigurationSerde {
+  fn validate(&self) -> Result<(), ValidationError> {
+    Ok(())
+  }
+}
+
+impl dnsdistsettings::SharedDNSAction {
+  fn validate(&self) -> Result<(), ValidationError> {
+    Ok(())
+  }
+}
+
+impl dnsdistsettings::SharedDNSResponseAction {
+  fn validate(&self) -> Result<(), ValidationError> {
+    Ok(())
+  }
+}
+// END INCLUDE ./rust-middle-in.rs
+    #[derive(Deserialize, Serialize, Debug, PartialEq)]
+    #[serde(deny_unknown_fields)]
+    struct GlobalConfigurationSerde {
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        acl: Vec<String>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        backends: Vec<dnsdistsettings::BackendConfiguration>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        binds: Vec<dnsdistsettings::BindConfiguration>,
+        #[serde(rename = "cache-hit-response-rules", default, skip_serializing_if = "crate::is_default")]
+        cache_hit_response_rules: Vec<ResponseRuleConfigurationSerde>,
+        #[serde(rename = "cache-inserted-response-rules", default, skip_serializing_if = "crate::is_default")]
+        cache_inserted_response_rules: Vec<ResponseRuleConfigurationSerde>,
+        #[serde(rename = "cache-miss-rules", default, skip_serializing_if = "crate::is_default")]
+        cache_miss_rules: Vec<QueryRuleConfigurationSerde>,
+        #[serde(rename = "cache-settings", default, skip_serializing_if = "crate::is_default")]
+        cache_settings: dnsdistsettings::CacheSettingsConfiguration,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        console: dnsdistsettings::ConsoleConfiguration,
+        #[serde(rename = "dynamic-rules", default, skip_serializing_if = "crate::is_default")]
+        dynamic_rules: Vec<dnsdistsettings::DynamicRulesConfiguration>,
+        #[serde(rename = "dynamic-rules-settings", default, skip_serializing_if = "crate::is_default")]
+        dynamic_rules_settings: dnsdistsettings::DynamicRulesSettingsConfiguration,
+        #[serde(rename = "edns-client-subnet", default, skip_serializing_if = "crate::is_default")]
+        edns_client_subnet: dnsdistsettings::EdnsClientSubnetConfiguration,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        general: dnsdistsettings::GeneralConfiguration,
+        #[serde(rename = "key-value-stores", default, skip_serializing_if = "crate::is_default")]
+        key_value_stores: dnsdistsettings::KeyValueStoresConfiguration,
+        #[serde(rename = "load-balancing-policies", default, skip_serializing_if = "crate::is_default")]
+        load_balancing_policies: dnsdistsettings::LoadBalancingPoliciesConfiguration,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        metrics: dnsdistsettings::MetricsConfiguration,
+        #[serde(rename = "packet-caches", default, skip_serializing_if = "crate::is_default")]
+        packet_caches: Vec<dnsdistsettings::PacketCacheConfiguration>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        pools: Vec<dnsdistsettings::PoolConfiguration>,
+        #[serde(rename = "proxy-protocol", default, skip_serializing_if = "crate::is_default")]
+        proxy_protocol: dnsdistsettings::ProxyProtocolConfiguration,
+        #[serde(rename = "query-count", default, skip_serializing_if = "crate::is_default")]
+        query_count: dnsdistsettings::QueryCountConfiguration,
+        #[serde(rename = "query-rules", default, skip_serializing_if = "crate::is_default")]
+        query_rules: Vec<QueryRuleConfigurationSerde>,
+        #[serde(rename = "remote-logging", default, skip_serializing_if = "crate::is_default")]
+        remote_logging: dnsdistsettings::RemoteLoggingConfiguration,
+        #[serde(rename = "response-rules", default, skip_serializing_if = "crate::is_default")]
+        response_rules: Vec<ResponseRuleConfigurationSerde>,
+        #[serde(rename = "ring-buffers", default, skip_serializing_if = "crate::is_default")]
+        ring_buffers: dnsdistsettings::RingBuffersConfiguration,
+        #[serde(rename = "security-polling", default, skip_serializing_if = "crate::is_default")]
+        security_polling: dnsdistsettings::SecurityPollingConfiguration,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        selectors: Vec<Selector>,
+        #[serde(rename = "self-answered-response-rules", default, skip_serializing_if = "crate::is_default")]
+        self_answered_response_rules: Vec<ResponseRuleConfigurationSerde>,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        snmp: dnsdistsettings::SnmpConfiguration,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        tuning: dnsdistsettings::TuningConfiguration,
+        #[serde(default, skip_serializing_if = "crate::is_default")]
+        webserver: dnsdistsettings::WebserverConfiguration,
+        #[serde(rename = "xfr-response-rules", default, skip_serializing_if = "crate::is_default")]
+        xfr_response_rules: Vec<ResponseRuleConfigurationSerde>,
+    }
+
+#[derive(Default, Serialize, Deserialize, Debug, PartialEq)]
+#[serde(tag = "type")]
+enum Action {
+    #[default]
+    Default,
+    Allow(dnsdistsettings::AllowActionConfiguration),
+    Continue(ContinueActionConfigurationSerde),
+    Delay(dnsdistsettings::DelayActionConfiguration),
+    DnstapLog(dnsdistsettings::DnstapLogActionConfiguration),
+    Drop(dnsdistsettings::DropActionConfiguration),
+    SetEDNSOption(dnsdistsettings::SetEDNSOptionActionConfiguration),
+    ERCode(dnsdistsettings::ERCodeActionConfiguration),
+    HTTPStatus(dnsdistsettings::HTTPStatusActionConfiguration),
+    KeyValueStoreLookup(dnsdistsettings::KeyValueStoreLookupActionConfiguration),
+    KeyValueStoreRangeLookup(dnsdistsettings::KeyValueStoreRangeLookupActionConfiguration),
+    Log(dnsdistsettings::LogActionConfiguration),
+    Lua(dnsdistsettings::LuaActionConfiguration),
+    LuaFFI(dnsdistsettings::LuaFFIActionConfiguration),
+    LuaFFIPerThread(dnsdistsettings::LuaFFIPerThreadActionConfiguration),
+    NegativeAndSOA(dnsdistsettings::NegativeAndSOAActionConfiguration),
+    None(dnsdistsettings::NoneActionConfiguration),
+    Pool(dnsdistsettings::PoolActionConfiguration),
+    QPS(dnsdistsettings::QPSActionConfiguration),
+    QPSPool(dnsdistsettings::QPSPoolActionConfiguration),
+    RCode(dnsdistsettings::RCodeActionConfiguration),
+    RemoteLog(dnsdistsettings::RemoteLogActionConfiguration),
+    SetAdditionalProxyProtocolValue(dnsdistsettings::SetAdditionalProxyProtocolValueActionConfiguration),
+    SetDisableECS(dnsdistsettings::SetDisableECSActionConfiguration),
+    SetDisableValidation(dnsdistsettings::SetDisableValidationActionConfiguration),
+    SetECS(dnsdistsettings::SetECSActionConfiguration),
+    SetECSOverride(dnsdistsettings::SetECSOverrideActionConfiguration),
+    SetECSPrefixLength(dnsdistsettings::SetECSPrefixLengthActionConfiguration),
+    SetExtendedDNSError(dnsdistsettings::SetExtendedDNSErrorActionConfiguration),
+    SetMacAddr(dnsdistsettings::SetMacAddrActionConfiguration),
+    SetMaxReturnedTTL(dnsdistsettings::SetMaxReturnedTTLActionConfiguration),
+    SetNoRecurse(dnsdistsettings::SetNoRecurseActionConfiguration),
+    SetProxyProtocolValues(dnsdistsettings::SetProxyProtocolValuesActionConfiguration),
+    SetSkipCache(dnsdistsettings::SetSkipCacheActionConfiguration),
+    SetTag(dnsdistsettings::SetTagActionConfiguration),
+    SetTempFailureCacheTTL(dnsdistsettings::SetTempFailureCacheTTLActionConfiguration),
+    SNMPTrap(dnsdistsettings::SNMPTrapActionConfiguration),
+    Spoof(dnsdistsettings::SpoofActionConfiguration),
+    SpoofCNAME(dnsdistsettings::SpoofCNAMEActionConfiguration),
+    SpoofPacket(dnsdistsettings::SpoofPacketActionConfiguration),
+    SpoofRaw(dnsdistsettings::SpoofRawActionConfiguration),
+    SpoofSVC(dnsdistsettings::SpoofSVCActionConfiguration),
+    TC(dnsdistsettings::TCActionConfiguration),
+    Tee(dnsdistsettings::TeeActionConfiguration),
+}
+
+#[derive(Default, Serialize, Deserialize, Debug, PartialEq)]
+#[serde(tag = "type")]
+enum ResponseAction {
+    #[default]
+    Default,
+    Allow(dnsdistsettings::AllowResponseActionConfiguration),
+    ClearRecordTypes(dnsdistsettings::ClearRecordTypesResponseActionConfiguration),
+    Delay(dnsdistsettings::DelayResponseActionConfiguration),
+    DnstapLog(dnsdistsettings::DnstapLogResponseActionConfiguration),
+    Drop(dnsdistsettings::DropResponseActionConfiguration),
+    LimitTTL(dnsdistsettings::LimitTTLResponseActionConfiguration),
+    Log(dnsdistsettings::LogResponseActionConfiguration),
+    Lua(dnsdistsettings::LuaResponseActionConfiguration),
+    LuaFFI(dnsdistsettings::LuaFFIResponseActionConfiguration),
+    LuaFFIPerThread(dnsdistsettings::LuaFFIPerThreadResponseActionConfiguration),
+    RemoteLog(dnsdistsettings::RemoteLogResponseActionConfiguration),
+    SetExtendedDNSError(dnsdistsettings::SetExtendedDNSErrorResponseActionConfiguration),
+    SetMaxReturnedTTL(dnsdistsettings::SetMaxReturnedTTLResponseActionConfiguration),
+    SetMaxTTL(dnsdistsettings::SetMaxTTLResponseActionConfiguration),
+    SetMinTTL(dnsdistsettings::SetMinTTLResponseActionConfiguration),
+    SetReducedTTL(dnsdistsettings::SetReducedTTLResponseActionConfiguration),
+    SetSkipCache(dnsdistsettings::SetSkipCacheResponseActionConfiguration),
+    SetTag(dnsdistsettings::SetTagResponseActionConfiguration),
+    SNMPTrap(dnsdistsettings::SNMPTrapResponseActionConfiguration),
+    TC(dnsdistsettings::TCResponseActionConfiguration),
+}
+
+#[derive(Default, Serialize, Deserialize, Debug, PartialEq)]
+#[serde(tag = "type")]
+enum Selector {
+    #[default]
+    Default,
+    All(dnsdistsettings::AllSelectorConfiguration),
+    And(AndSelectorConfigurationSerde),
+    ByName(dnsdistsettings::ByNameSelectorConfiguration),
+    DNSSEC(dnsdistsettings::DNSSECSelectorConfiguration),
+    DSTPort(dnsdistsettings::DSTPortSelectorConfiguration),
+    EDNSOption(dnsdistsettings::EDNSOptionSelectorConfiguration),
+    EDNSVersion(dnsdistsettings::EDNSVersionSelectorConfiguration),
+    ERCode(dnsdistsettings::ERCodeSelectorConfiguration),
+    HTTPHeader(dnsdistsettings::HTTPHeaderSelectorConfiguration),
+    HTTPPath(dnsdistsettings::HTTPPathSelectorConfiguration),
+    HTTPPathRegex(dnsdistsettings::HTTPPathRegexSelectorConfiguration),
+    KeyValueStoreLookup(dnsdistsettings::KeyValueStoreLookupSelectorConfiguration),
+    KeyValueStoreRangeLookup(dnsdistsettings::KeyValueStoreRangeLookupSelectorConfiguration),
+    Lua(dnsdistsettings::LuaSelectorConfiguration),
+    LuaFFI(dnsdistsettings::LuaFFISelectorConfiguration),
+    LuaFFIPerThread(dnsdistsettings::LuaFFIPerThreadSelectorConfiguration),
+    MaxQPS(dnsdistsettings::MaxQPSSelectorConfiguration),
+    MaxQPSIP(dnsdistsettings::MaxQPSIPSelectorConfiguration),
+    NetmaskGroup(dnsdistsettings::NetmaskGroupSelectorConfiguration),
+    Not(NotSelectorConfigurationSerde),
+    Opcode(dnsdistsettings::OpcodeSelectorConfiguration),
+    Or(OrSelectorConfigurationSerde),
+    PayloadSize(dnsdistsettings::PayloadSizeSelectorConfiguration),
+    PoolAvailable(dnsdistsettings::PoolAvailableSelectorConfiguration),
+    PoolOutstanding(dnsdistsettings::PoolOutstandingSelectorConfiguration),
+    Proba(dnsdistsettings::ProbaSelectorConfiguration),
+    ProxyProtocolValue(dnsdistsettings::ProxyProtocolValueSelectorConfiguration),
+    QClass(dnsdistsettings::QClassSelectorConfiguration),
+    QName(dnsdistsettings::QNameSelectorConfiguration),
+    QNameLabelsCount(dnsdistsettings::QNameLabelsCountSelectorConfiguration),
+    QNameSet(dnsdistsettings::QNameSetSelectorConfiguration),
+    QNameSuffix(dnsdistsettings::QNameSuffixSelectorConfiguration),
+    QNameWireLength(dnsdistsettings::QNameWireLengthSelectorConfiguration),
+    QType(dnsdistsettings::QTypeSelectorConfiguration),
+    RCode(dnsdistsettings::RCodeSelectorConfiguration),
+    RD(dnsdistsettings::RDSelectorConfiguration),
+    RE2(dnsdistsettings::RE2SelectorConfiguration),
+    RecordsCount(dnsdistsettings::RecordsCountSelectorConfiguration),
+    RecordsTypeCount(dnsdistsettings::RecordsTypeCountSelectorConfiguration),
+    Regex(dnsdistsettings::RegexSelectorConfiguration),
+    SNI(dnsdistsettings::SNISelectorConfiguration),
+    Tag(dnsdistsettings::TagSelectorConfiguration),
+    TCP(dnsdistsettings::TCPSelectorConfiguration),
+    TrailingData(dnsdistsettings::TrailingDataSelectorConfiguration),
+}
+
+impl Default for dnsdistsettings::MetricsConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::MetricsConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::CarbonConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::CarbonConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::RemoteLoggingConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::RemoteLoggingConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::ProtobufLoggerConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::ProtobufLoggerConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::DnstapLoggerConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::DnstapLoggerConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::ProtoBufMetaConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::ProtoBufMetaConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::LMDBKVStoreConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::LMDBKVStoreConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::CDBKVStoreConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::CDBKVStoreConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::KVSLookupKeySourceIPConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::KVSLookupKeySourceIPConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::KVSLookupKeyQNameConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::KVSLookupKeyQNameConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::KVSLookupKeySuffixConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::KVSLookupKeySuffixConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::KVSLookupKeyTagConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::KVSLookupKeyTagConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::KVSLookupKeysConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::KVSLookupKeysConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::KeyValueStoresConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::KeyValueStoresConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::WebserverConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::WebserverConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::ConsoleConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::ConsoleConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::EdnsClientSubnetConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::EdnsClientSubnetConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+// DEFAULT HANDLING for dynamic_rules_settings_default_action
+fn default_value_dynamic_rules_settings_default_action() -> String {
+    String::from("Drop")
+}
+fn default_value_equal_dynamic_rules_settings_default_action(value: &str)-> bool {
+    value == default_value_dynamic_rules_settings_default_action()
+}
+
+
+impl Default for dnsdistsettings::DynamicRulesSettingsConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::DynamicRulesSettingsConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+// DEFAULT HANDLING for dynamic_rule_action
+fn default_value_dynamic_rule_action() -> String {
+    String::from("drop")
+}
+fn default_value_equal_dynamic_rule_action(value: &str)-> bool {
+    value == default_value_dynamic_rule_action()
+}
+
+
+// DEFAULT HANDLING for dynamic_rule_tag_value
+fn default_value_dynamic_rule_tag_value() -> String {
+    String::from("0")
+}
+fn default_value_equal_dynamic_rule_tag_value(value: &str)-> bool {
+    value == default_value_dynamic_rule_tag_value()
+}
+
+
+impl Default for dnsdistsettings::DynamicRuleConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::DynamicRuleConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::DynamicRulesConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::DynamicRulesConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::RingBuffersConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::RingBuffersConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::IncomingTlsCertificateKeyPairConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::IncomingTlsCertificateKeyPairConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+// DEFAULT HANDLING for incoming_tls_provider
+fn default_value_incoming_tls_provider() -> String {
+    String::from("OpenSSL")
+}
+fn default_value_equal_incoming_tls_provider(value: &str)-> bool {
+    value == default_value_incoming_tls_provider()
+}
+
+
+// DEFAULT HANDLING for incoming_tls_minimum_version
+fn default_value_incoming_tls_minimum_version() -> String {
+    String::from("tls1.0")
+}
+fn default_value_equal_incoming_tls_minimum_version(value: &str)-> bool {
+    value == default_value_incoming_tls_minimum_version()
+}
+
+
+impl Default for dnsdistsettings::IncomingTlsConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::IncomingTlsConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+// DEFAULT HANDLING for outgoing_tls_provider
+fn default_value_outgoing_tls_provider() -> String {
+    String::from("OpenSSL")
+}
+fn default_value_equal_outgoing_tls_provider(value: &str)-> bool {
+    value == default_value_outgoing_tls_provider()
+}
+
+
+impl Default for dnsdistsettings::OutgoingTlsConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::OutgoingTlsConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::HttpCustomResponseHeaderConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::HttpCustomResponseHeaderConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::HttpResponsesMapConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::HttpResponsesMapConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+// DEFAULT HANDLING for incoming_doh_provider
+fn default_value_incoming_doh_provider() -> String {
+    String::from("nghttp2")
+}
+fn default_value_equal_incoming_doh_provider(value: &str)-> bool {
+    value == default_value_incoming_doh_provider()
+}
+
+
+// DEFAULT HANDLING for incoming_doh_server_tokens
+fn default_value_incoming_doh_server_tokens() -> String {
+    String::from("h2o/dnsdist")
+}
+fn default_value_equal_incoming_doh_server_tokens(value: &str)-> bool {
+    value == default_value_incoming_doh_server_tokens()
+}
+
+
+impl Default for dnsdistsettings::IncomingDohConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::IncomingDohConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::IncomingDoqConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::IncomingDoqConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+// DEFAULT HANDLING for incoming_quic_congestion_control_algorithm
+fn default_value_incoming_quic_congestion_control_algorithm() -> String {
+    String::from("reno")
+}
+fn default_value_equal_incoming_quic_congestion_control_algorithm(value: &str)-> bool {
+    value == default_value_incoming_quic_congestion_control_algorithm()
+}
+
+
+impl Default for dnsdistsettings::IncomingQuicConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::IncomingQuicConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+// DEFAULT HANDLING for outgoing_doh_path
+fn default_value_outgoing_doh_path() -> String {
+    String::from("/dns-query")
+}
+fn default_value_equal_outgoing_doh_path(value: &str)-> bool {
+    value == default_value_outgoing_doh_path()
+}
+
+
+impl Default for dnsdistsettings::OutgoingDohConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::OutgoingDohConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::IncomingTcpConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::IncomingTcpConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+// DEFAULT HANDLING for bind_protocol
+fn default_value_bind_protocol() -> String {
+    String::from("Do53")
+}
+fn default_value_equal_bind_protocol(value: &str)-> bool {
+    value == default_value_bind_protocol()
+}
+
+
+impl Default for dnsdistsettings::BindConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::BindConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::OutgoingTcpConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::OutgoingTcpConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::ProxyProtocolValueConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::ProxyProtocolValueConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+// DEFAULT HANDLING for lazy_health_check_mode
+fn default_value_lazy_health_check_mode() -> String {
+    String::from("TimeoutOrServFail")
+}
+fn default_value_equal_lazy_health_check_mode(value: &str)-> bool {
+    value == default_value_lazy_health_check_mode()
+}
+
+
+impl Default for dnsdistsettings::LazyHealthCheckConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::LazyHealthCheckConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+// DEFAULT HANDLING for health_check_mode
+fn default_value_health_check_mode() -> String {
+    String::from("auto")
+}
+fn default_value_equal_health_check_mode(value: &str)-> bool {
+    value == default_value_health_check_mode()
+}
+
+
+// DEFAULT HANDLING for health_check_qclass
+fn default_value_health_check_qclass() -> String {
+    String::from("IN")
+}
+fn default_value_equal_health_check_qclass(value: &str)-> bool {
+    value == default_value_health_check_qclass()
+}
+
+
+// DEFAULT HANDLING for health_check_qtype
+fn default_value_health_check_qtype() -> String {
+    String::from("A")
+}
+fn default_value_equal_health_check_qtype(value: &str)-> bool {
+    value == default_value_health_check_qtype()
+}
+
+
+impl Default for dnsdistsettings::HealthCheckConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::HealthCheckConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::OutgoingAutoUpgradeConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::OutgoingAutoUpgradeConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::BackendConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::BackendConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::TuningConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::TuningConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::TcpTuningConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::TcpTuningConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::UdpTuningConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::UdpTuningConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::TlsTuningConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::TlsTuningConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::DohTuningConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::DohTuningConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::CacheSettingsConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::CacheSettingsConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+// DEFAULT HANDLING for security_polling_suffix
+fn default_value_security_polling_suffix() -> String {
+    String::from("secpoll.powerdns.com.")
+}
+fn default_value_equal_security_polling_suffix(value: &str)-> bool {
+    value == default_value_security_polling_suffix()
+}
+
+
+impl Default for dnsdistsettings::SecurityPollingConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::SecurityPollingConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::GeneralConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::GeneralConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::PacketCacheConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::PacketCacheConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::ProxyProtocolConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::ProxyProtocolConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::SnmpConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::SnmpConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::QueryCountConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::QueryCountConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+// DEFAULT HANDLING for pool_policy
+fn default_value_pool_policy() -> String {
+    String::from("least-outstanding")
+}
+fn default_value_equal_pool_policy(value: &str)-> bool {
+    value == default_value_pool_policy()
+}
+
+
+impl Default for dnsdistsettings::PoolConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::PoolConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::CustomLoadBalancingPolicyConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::CustomLoadBalancingPolicyConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for dnsdistsettings::LoadBalancingPoliciesConfiguration {
+    fn default() -> Self {
+        let deserialized: dnsdistsettings::LoadBalancingPoliciesConfiguration = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl Default for GlobalConfigurationSerde {
+    fn default() -> Self {
+        let deserialized: GlobalConfigurationSerde = serde_yaml::from_str("").unwrap();
+        deserialized
+    }
+}
+
+
+impl dnsdistsettings::GlobalConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        for sub_type in &self.backends {
+        sub_type.validate()?;
+    }
+        for sub_type in &self.binds {
+        sub_type.validate()?;
+    }
+        for sub_type in &self.cache_hit_response_rules {
+        sub_type.validate()?;
+    }
+        for sub_type in &self.cache_inserted_response_rules {
+        sub_type.validate()?;
+    }
+        for sub_type in &self.cache_miss_rules {
+        sub_type.validate()?;
+    }
+        self.cache_settings.validate()?;
+        self.console.validate()?;
+        for sub_type in &self.dynamic_rules {
+        sub_type.validate()?;
+    }
+        self.dynamic_rules_settings.validate()?;
+        self.edns_client_subnet.validate()?;
+        self.general.validate()?;
+        self.key_value_stores.validate()?;
+        self.load_balancing_policies.validate()?;
+        self.metrics.validate()?;
+        for sub_type in &self.packet_caches {
+        sub_type.validate()?;
+    }
+        for sub_type in &self.pools {
+        sub_type.validate()?;
+    }
+        self.proxy_protocol.validate()?;
+        self.query_count.validate()?;
+        for sub_type in &self.query_rules {
+        sub_type.validate()?;
+    }
+        self.remote_logging.validate()?;
+        for sub_type in &self.response_rules {
+        sub_type.validate()?;
+    }
+        self.ring_buffers.validate()?;
+        self.security_polling.validate()?;
+        for sub_type in &self.self_answered_response_rules {
+        sub_type.validate()?;
+    }
+        self.snmp.validate()?;
+        self.tuning.validate()?;
+        self.webserver.validate()?;
+        for sub_type in &self.xfr_response_rules {
+        sub_type.validate()?;
+    }
+        Ok(())
+    }
+}
+impl dnsdistsettings::MetricsConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        for sub_type in &self.carbon {
+        sub_type.validate()?;
+    }
+        Ok(())
+    }
+}
+impl dnsdistsettings::CarbonConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::RemoteLoggingConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        for sub_type in &self.protobuf_loggers {
+        sub_type.validate()?;
+    }
+        for sub_type in &self.dnstap_loggers {
+        sub_type.validate()?;
+    }
+        Ok(())
+    }
+}
+impl dnsdistsettings::ProtobufLoggerConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::DnstapLoggerConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::ProtoBufMetaConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::LMDBKVStoreConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::CDBKVStoreConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::KVSLookupKeySourceIPConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::KVSLookupKeyQNameConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::KVSLookupKeySuffixConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::KVSLookupKeyTagConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::KVSLookupKeysConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        for sub_type in &self.source_ip_keys {
+        sub_type.validate()?;
+    }
+        for sub_type in &self.qname_keys {
+        sub_type.validate()?;
+    }
+        for sub_type in &self.suffix_keys {
+        sub_type.validate()?;
+    }
+        for sub_type in &self.tag_keys {
+        sub_type.validate()?;
+    }
+        Ok(())
+    }
+}
+impl dnsdistsettings::KeyValueStoresConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        for sub_type in &self.lmdb {
+        sub_type.validate()?;
+    }
+        for sub_type in &self.cdb {
+        sub_type.validate()?;
+    }
+        self.lookup_keys.validate()?;
+        Ok(())
+    }
+}
+impl dnsdistsettings::WebserverConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        for sub_type in &self.custom_headers {
+        sub_type.validate()?;
+    }
+        Ok(())
+    }
+}
+impl dnsdistsettings::ConsoleConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::EdnsClientSubnetConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::DynamicRulesSettingsConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::DynamicRuleConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::DynamicRulesConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        for sub_type in &self.rules {
+        sub_type.validate()?;
+    }
+        Ok(())
+    }
+}
+impl dnsdistsettings::RingBuffersConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::IncomingTlsCertificateKeyPairConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::IncomingTlsConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        for sub_type in &self.certificates {
+        sub_type.validate()?;
+    }
+        Ok(())
+    }
+}
+impl dnsdistsettings::OutgoingTlsConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::HttpCustomResponseHeaderConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::HttpResponsesMapConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        for sub_type in &self.headers {
+        sub_type.validate()?;
+    }
+        Ok(())
+    }
+}
+impl dnsdistsettings::IncomingDohConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        for sub_type in &self.custom_response_headers {
+        sub_type.validate()?;
+    }
+        for sub_type in &self.responses_map {
+        sub_type.validate()?;
+    }
+        Ok(())
+    }
+}
+impl dnsdistsettings::IncomingDoqConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::IncomingQuicConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::OutgoingDohConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::IncomingTcpConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::BindConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        self.tcp.validate()?;
+        self.tls.validate()?;
+        self.doh.validate()?;
+        self.doq.validate()?;
+        self.quic.validate()?;
+        Ok(())
+    }
+}
+impl dnsdistsettings::OutgoingTcpConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::ProxyProtocolValueConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::LazyHealthCheckConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::HealthCheckConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        self.lazy.validate()?;
+        Ok(())
+    }
+}
+impl dnsdistsettings::OutgoingAutoUpgradeConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::BackendConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        self.tls.validate()?;
+        self.doh.validate()?;
+        self.tcp.validate()?;
+        self.health_checks.validate()?;
+        self.auto_upgrade.validate()?;
+        Ok(())
+    }
+}
+impl dnsdistsettings::TuningConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        self.doh.validate()?;
+        self.tcp.validate()?;
+        self.tls.validate()?;
+        self.udp.validate()?;
+        Ok(())
+    }
+}
+impl dnsdistsettings::TcpTuningConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::UdpTuningConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::TlsTuningConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::DohTuningConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::CacheSettingsConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::SecurityPollingConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::GeneralConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::PacketCacheConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::ProxyProtocolConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::SnmpConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::QueryCountConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::PoolConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::CustomLoadBalancingPolicyConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::LoadBalancingPoliciesConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        for sub_type in &self.custom_policies {
+        sub_type.validate()?;
+    }
+        Ok(())
+    }
+}
+impl dnsdistsettings::QueryRuleConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        Ok(())
+    }
+}
+impl dnsdistsettings::ResponseRuleConfiguration {
+    fn validate(&self) -> Result<(), ValidationError> {
+        self.action.validate()?;
+        Ok(())
+    }
+}
+impl GlobalConfigurationSerde {
+    fn validate(&self) -> Result<(), ValidationError> {
+        for sub_type in &self.backends {
+        sub_type.validate()?;
+    }
+        for sub_type in &self.binds {
+        sub_type.validate()?;
+    }
+        for sub_type in &self.cache_hit_response_rules {
+        sub_type.validate()?;
+    }
+        for sub_type in &self.cache_inserted_response_rules {
+        sub_type.validate()?;
+    }
+        for sub_type in &self.cache_miss_rules {
+        sub_type.validate()?;
+    }
+        self.cache_settings.validate()?;
+        self.console.validate()?;
+        for sub_type in &self.dynamic_rules {
+        sub_type.validate()?;
+    }
+        self.dynamic_rules_settings.validate()?;
+        self.edns_client_subnet.validate()?;
+        self.general.validate()?;
+        self.key_value_stores.validate()?;
+        self.load_balancing_policies.validate()?;
+        self.metrics.validate()?;
+        for sub_type in &self.packet_caches {
+        sub_type.validate()?;
+    }
+        for sub_type in &self.pools {
+        sub_type.validate()?;
+    }
+        self.proxy_protocol.validate()?;
+        self.query_count.validate()?;
+        for sub_type in &self.query_rules {
+        sub_type.validate()?;
+    }
+        self.remote_logging.validate()?;
+        for sub_type in &self.response_rules {
+        sub_type.validate()?;
+    }
+        self.ring_buffers.validate()?;
+        self.security_polling.validate()?;
+        for sub_type in &self.self_answered_response_rules {
+        sub_type.validate()?;
+    }
+        self.snmp.validate()?;
+        self.tuning.validate()?;
+        self.webserver.validate()?;
+        for sub_type in &self.xfr_response_rules {
+        sub_type.validate()?;
+    }
+        Ok(())
+    }
+}
+fn get_one_action_from_serde(action: &Action) -> Option<dnsdistsettings::SharedDNSAction> {
+    match action {
+        Action::Default => {}
+        Action::Allow(config) => {
+                let tmp_action = dnsdistsettings::getAllowAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::Continue(cont) => {
+             let mut config: dnsdistsettings::ContinueActionConfiguration = Default::default();
+             let new_action = get_one_action_from_serde(&*cont.action);
+             if new_action.is_some() {
+                 config.action = new_action.unwrap();
+             }
+             return Some(dnsdistsettings::SharedDNSAction {
+                 action: dnsdistsettings::getContinueAction(&config),
+             });
+        }
+        Action::Delay(config) => {
+                let tmp_action = dnsdistsettings::getDelayAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::DnstapLog(config) => {
+                let tmp_action = dnsdistsettings::getDnstapLogAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::Drop(config) => {
+                let tmp_action = dnsdistsettings::getDropAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SetEDNSOption(config) => {
+                let tmp_action = dnsdistsettings::getSetEDNSOptionAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::ERCode(config) => {
+                let tmp_action = dnsdistsettings::getERCodeAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::HTTPStatus(config) => {
+                let tmp_action = dnsdistsettings::getHTTPStatusAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::KeyValueStoreLookup(config) => {
+                let tmp_action = dnsdistsettings::getKeyValueStoreLookupAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::KeyValueStoreRangeLookup(config) => {
+                let tmp_action = dnsdistsettings::getKeyValueStoreRangeLookupAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::Log(config) => {
+                let tmp_action = dnsdistsettings::getLogAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::Lua(config) => {
+                let tmp_action = dnsdistsettings::getLuaAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::LuaFFI(config) => {
+                let tmp_action = dnsdistsettings::getLuaFFIAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::LuaFFIPerThread(config) => {
+                let tmp_action = dnsdistsettings::getLuaFFIPerThreadAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::NegativeAndSOA(config) => {
+                let tmp_action = dnsdistsettings::getNegativeAndSOAAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::None(config) => {
+                let tmp_action = dnsdistsettings::getNoneAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::Pool(config) => {
+                let tmp_action = dnsdistsettings::getPoolAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::QPS(config) => {
+                let tmp_action = dnsdistsettings::getQPSAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::QPSPool(config) => {
+                let tmp_action = dnsdistsettings::getQPSPoolAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::RCode(config) => {
+                let tmp_action = dnsdistsettings::getRCodeAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::RemoteLog(config) => {
+                let tmp_action = dnsdistsettings::getRemoteLogAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SetAdditionalProxyProtocolValue(config) => {
+                let tmp_action = dnsdistsettings::getSetAdditionalProxyProtocolValueAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SetDisableECS(config) => {
+                let tmp_action = dnsdistsettings::getSetDisableECSAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SetDisableValidation(config) => {
+                let tmp_action = dnsdistsettings::getSetDisableValidationAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SetECS(config) => {
+                let tmp_action = dnsdistsettings::getSetECSAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SetECSOverride(config) => {
+                let tmp_action = dnsdistsettings::getSetECSOverrideAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SetECSPrefixLength(config) => {
+                let tmp_action = dnsdistsettings::getSetECSPrefixLengthAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SetExtendedDNSError(config) => {
+                let tmp_action = dnsdistsettings::getSetExtendedDNSErrorAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SetMacAddr(config) => {
+                let tmp_action = dnsdistsettings::getSetMacAddrAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SetMaxReturnedTTL(config) => {
+                let tmp_action = dnsdistsettings::getSetMaxReturnedTTLAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SetNoRecurse(config) => {
+                let tmp_action = dnsdistsettings::getSetNoRecurseAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SetProxyProtocolValues(config) => {
+                let tmp_action = dnsdistsettings::getSetProxyProtocolValuesAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SetSkipCache(config) => {
+                let tmp_action = dnsdistsettings::getSetSkipCacheAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SetTag(config) => {
+                let tmp_action = dnsdistsettings::getSetTagAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SetTempFailureCacheTTL(config) => {
+                let tmp_action = dnsdistsettings::getSetTempFailureCacheTTLAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SNMPTrap(config) => {
+                let tmp_action = dnsdistsettings::getSNMPTrapAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::Spoof(config) => {
+                let tmp_action = dnsdistsettings::getSpoofAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SpoofCNAME(config) => {
+                let tmp_action = dnsdistsettings::getSpoofCNAMEAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SpoofPacket(config) => {
+                let tmp_action = dnsdistsettings::getSpoofPacketAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SpoofRaw(config) => {
+                let tmp_action = dnsdistsettings::getSpoofRawAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::SpoofSVC(config) => {
+                let tmp_action = dnsdistsettings::getSpoofSVCAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::TC(config) => {
+                let tmp_action = dnsdistsettings::getTCAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+        Action::Tee(config) => {
+                let tmp_action = dnsdistsettings::getTeeAction(&config);
+                return Some(dnsdistsettings::SharedDNSAction {
+                    action: tmp_action,
+                });
+            }
+    }
+    None
+}
+fn get_one_response_action_from_serde(action: &ResponseAction) -> Option<dnsdistsettings::SharedDNSResponseAction> {
+    match action {
+        ResponseAction::Default => {}
+        ResponseAction::Allow(config) => {
+                let tmp_action = dnsdistsettings::getAllowResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::ClearRecordTypes(config) => {
+                let tmp_action = dnsdistsettings::getClearRecordTypesResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::Delay(config) => {
+                let tmp_action = dnsdistsettings::getDelayResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::DnstapLog(config) => {
+                let tmp_action = dnsdistsettings::getDnstapLogResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::Drop(config) => {
+                let tmp_action = dnsdistsettings::getDropResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::LimitTTL(config) => {
+                let tmp_action = dnsdistsettings::getLimitTTLResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::Log(config) => {
+                let tmp_action = dnsdistsettings::getLogResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::Lua(config) => {
+                let tmp_action = dnsdistsettings::getLuaResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::LuaFFI(config) => {
+                let tmp_action = dnsdistsettings::getLuaFFIResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::LuaFFIPerThread(config) => {
+                let tmp_action = dnsdistsettings::getLuaFFIPerThreadResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::RemoteLog(config) => {
+                let tmp_action = dnsdistsettings::getRemoteLogResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::SetExtendedDNSError(config) => {
+                let tmp_action = dnsdistsettings::getSetExtendedDNSErrorResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::SetMaxReturnedTTL(config) => {
+                let tmp_action = dnsdistsettings::getSetMaxReturnedTTLResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::SetMaxTTL(config) => {
+                let tmp_action = dnsdistsettings::getSetMaxTTLResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::SetMinTTL(config) => {
+                let tmp_action = dnsdistsettings::getSetMinTTLResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::SetReducedTTL(config) => {
+                let tmp_action = dnsdistsettings::getSetReducedTTLResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::SetSkipCache(config) => {
+                let tmp_action = dnsdistsettings::getSetSkipCacheResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::SetTag(config) => {
+                let tmp_action = dnsdistsettings::getSetTagResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::SNMPTrap(config) => {
+                let tmp_action = dnsdistsettings::getSNMPTrapResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+        ResponseAction::TC(config) => {
+                let tmp_action = dnsdistsettings::getTCResponseAction(&config);
+                return Some(dnsdistsettings::SharedDNSResponseAction {
+                    action: tmp_action,
+                });
+            }
+    }
+    None
+}
+fn get_one_selector_from_serde(selector: &Selector) -> Option<dnsdistsettings::SharedDNSSelector> {
+    match selector {
+        Selector::Default => {}
+        Selector::All(all) => {
+            let tmp_selector = dnsdistsettings::getAllSelector(&all);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::And(and) => {
+             let mut config: dnsdistsettings::AndSelectorConfiguration = Default::default();
+             for sub_selector in &and.selectors {
+                 let new_selector = get_one_selector_from_serde(&sub_selector);
+                 if new_selector.is_some() {
+                     config.selectors.push(new_selector.unwrap());
+                 }
+             }
+             return Some(dnsdistsettings::SharedDNSSelector {
+                 selector: dnsdistsettings::getAndSelector(&config),
+             });
+        }
+        Selector::ByName(byname) => {
+            let tmp_selector = dnsdistsettings::getByNameSelector(&byname);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::DNSSEC(dnssec) => {
+            let tmp_selector = dnsdistsettings::getDNSSECSelector(&dnssec);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::DSTPort(dstport) => {
+            let tmp_selector = dnsdistsettings::getDSTPortSelector(&dstport);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::EDNSOption(ednsoption) => {
+            let tmp_selector = dnsdistsettings::getEDNSOptionSelector(&ednsoption);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::EDNSVersion(ednsversion) => {
+            let tmp_selector = dnsdistsettings::getEDNSVersionSelector(&ednsversion);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::ERCode(ercode) => {
+            let tmp_selector = dnsdistsettings::getERCodeSelector(&ercode);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::HTTPHeader(httpheader) => {
+            let tmp_selector = dnsdistsettings::getHTTPHeaderSelector(&httpheader);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::HTTPPath(httppath) => {
+            let tmp_selector = dnsdistsettings::getHTTPPathSelector(&httppath);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::HTTPPathRegex(httppathregex) => {
+            let tmp_selector = dnsdistsettings::getHTTPPathRegexSelector(&httppathregex);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::KeyValueStoreLookup(keyvaluestorelookup) => {
+            let tmp_selector = dnsdistsettings::getKeyValueStoreLookupSelector(&keyvaluestorelookup);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::KeyValueStoreRangeLookup(keyvaluestorerangelookup) => {
+            let tmp_selector = dnsdistsettings::getKeyValueStoreRangeLookupSelector(&keyvaluestorerangelookup);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::Lua(lua) => {
+            let tmp_selector = dnsdistsettings::getLuaSelector(&lua);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::LuaFFI(luaffi) => {
+            let tmp_selector = dnsdistsettings::getLuaFFISelector(&luaffi);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::LuaFFIPerThread(luaffiperthread) => {
+            let tmp_selector = dnsdistsettings::getLuaFFIPerThreadSelector(&luaffiperthread);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::MaxQPS(maxqps) => {
+            let tmp_selector = dnsdistsettings::getMaxQPSSelector(&maxqps);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::MaxQPSIP(maxqpsip) => {
+            let tmp_selector = dnsdistsettings::getMaxQPSIPSelector(&maxqpsip);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::NetmaskGroup(netmaskgroup) => {
+            let tmp_selector = dnsdistsettings::getNetmaskGroupSelector(&netmaskgroup);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::Not(not) => {
+             let mut config: dnsdistsettings::NotSelectorConfiguration = Default::default();
+             let new_selector = get_one_selector_from_serde(&*not.selector);
+             if new_selector.is_some() {
+                 config.selector = new_selector.unwrap();
+             }
+             return Some(dnsdistsettings::SharedDNSSelector {
+                 selector: dnsdistsettings::getNotSelector(&config),
+             });
+        }
+        Selector::Opcode(opcode) => {
+            let tmp_selector = dnsdistsettings::getOpcodeSelector(&opcode);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::Or(or) => {
+             let mut config: dnsdistsettings::OrSelectorConfiguration = Default::default();
+             for sub_selector in &or.selectors {
+                 let new_selector = get_one_selector_from_serde(&sub_selector);
+                 if new_selector.is_some() {
+                     config.selectors.push(new_selector.unwrap());
+                 }
+             }
+             return Some(dnsdistsettings::SharedDNSSelector {
+                 selector: dnsdistsettings::getOrSelector(&config),
+             });
+        }
+        Selector::PayloadSize(payloadsize) => {
+            let tmp_selector = dnsdistsettings::getPayloadSizeSelector(&payloadsize);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::PoolAvailable(poolavailable) => {
+            let tmp_selector = dnsdistsettings::getPoolAvailableSelector(&poolavailable);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::PoolOutstanding(pooloutstanding) => {
+            let tmp_selector = dnsdistsettings::getPoolOutstandingSelector(&pooloutstanding);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::Proba(proba) => {
+            let tmp_selector = dnsdistsettings::getProbaSelector(&proba);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::ProxyProtocolValue(proxyprotocolvalue) => {
+            let tmp_selector = dnsdistsettings::getProxyProtocolValueSelector(&proxyprotocolvalue);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::QClass(qclass) => {
+            let tmp_selector = dnsdistsettings::getQClassSelector(&qclass);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::QName(qname) => {
+            let tmp_selector = dnsdistsettings::getQNameSelector(&qname);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::QNameLabelsCount(qnamelabelscount) => {
+            let tmp_selector = dnsdistsettings::getQNameLabelsCountSelector(&qnamelabelscount);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::QNameSet(qnameset) => {
+            let tmp_selector = dnsdistsettings::getQNameSetSelector(&qnameset);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::QNameSuffix(qnamesuffix) => {
+            let tmp_selector = dnsdistsettings::getQNameSuffixSelector(&qnamesuffix);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::QNameWireLength(qnamewirelength) => {
+            let tmp_selector = dnsdistsettings::getQNameWireLengthSelector(&qnamewirelength);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::QType(qtype) => {
+            let tmp_selector = dnsdistsettings::getQTypeSelector(&qtype);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::RCode(rcode) => {
+            let tmp_selector = dnsdistsettings::getRCodeSelector(&rcode);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::RD(rd) => {
+            let tmp_selector = dnsdistsettings::getRDSelector(&rd);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::RE2(re2) => {
+            let tmp_selector = dnsdistsettings::getRE2Selector(&re2);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::RecordsCount(recordscount) => {
+            let tmp_selector = dnsdistsettings::getRecordsCountSelector(&recordscount);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::RecordsTypeCount(recordstypecount) => {
+            let tmp_selector = dnsdistsettings::getRecordsTypeCountSelector(&recordstypecount);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::Regex(regex) => {
+            let tmp_selector = dnsdistsettings::getRegexSelector(&regex);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::SNI(sni) => {
+            let tmp_selector = dnsdistsettings::getSNISelector(&sni);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::Tag(tag) => {
+            let tmp_selector = dnsdistsettings::getTagSelector(&tag);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::TCP(tcp) => {
+            let tmp_selector = dnsdistsettings::getTCPSelector(&tcp);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+        Selector::TrailingData(trailingdata) => {
+            let tmp_selector = dnsdistsettings::getTrailingDataSelector(&trailingdata);
+            return Some(dnsdistsettings::SharedDNSSelector {
+                selector: tmp_selector,
+            });
+        }
+    }
+    None
+}
+// START INCLUDE ./rust-post-in.rs
+fn get_selectors_from_serde(
+    selectors_from_serde: &Vec<Selector>,
+) -> Vec<dnsdistsettings::SharedDNSSelector> {
+    let mut results: Vec<dnsdistsettings::SharedDNSSelector> = Vec::new();
+
+    for rule in selectors_from_serde {
+        let selector = get_one_selector_from_serde(&rule);
+        if selector.is_some() {
+            results.push(selector.unwrap());
+        }
+    }
+    results
+}
+
+fn get_query_rules_from_serde(
+    rules_from_serde: &Vec<QueryRuleConfigurationSerde>,
+) -> Vec<dnsdistsettings::QueryRuleConfiguration> {
+    let mut results: Vec<dnsdistsettings::QueryRuleConfiguration> = Vec::new();
+
+    for rule in rules_from_serde {
+        let selector = get_one_selector_from_serde(&rule.selector);
+        let action = get_one_action_from_serde(&rule.action);
+        if selector.is_some() && action.is_some() {
+            results.push(dnsdistsettings::QueryRuleConfiguration {
+              name: rule.name.clone(),
+              uuid: rule.uuid.clone(),
+              selector: selector.unwrap(),
+              action: action.unwrap(),
+            });
+        }
+    }
+    results
+}
+
+fn get_response_rules_from_serde(
+    rules_from_serde: &Vec<ResponseRuleConfigurationSerde>,
+) -> Vec<dnsdistsettings::ResponseRuleConfiguration> {
+    let mut results: Vec<dnsdistsettings::ResponseRuleConfiguration> = Vec::new();
+
+    for rule in rules_from_serde {
+        let selector = get_one_selector_from_serde(&rule.selector);
+        let action = get_one_response_action_from_serde(&rule.action);
+        if selector.is_some() && action.is_some() {
+            results.push(dnsdistsettings::ResponseRuleConfiguration {
+              name: rule.name.clone(),
+              uuid: rule.uuid.clone(),
+              selector: selector.unwrap(),
+              action: action.unwrap(),
+            });
+        }
+    }
+    results
+}
+
+fn register_remote_loggers(
+  config: &dnsdistsettings::RemoteLoggingConfiguration,
+) {
+  for logger in &config.protobuf_loggers {
+    dnsdistsettings::registerProtobufLogger(&logger);
+  }
+  for logger in &config.dnstap_loggers {
+    dnsdistsettings::registerDnstapLogger(&logger);
+  }
+}
+
+fn get_global_configuration_from_serde(
+    serde: GlobalConfigurationSerde,
+) -> dnsdistsettings::GlobalConfiguration {
+    let mut config: dnsdistsettings::GlobalConfiguration = Default::default();
+    config.key_value_stores = serde.key_value_stores;
+    config.webserver = serde.webserver;
+    config.console = serde.console;
+    config.edns_client_subnet = serde.edns_client_subnet;
+    config.dynamic_rules_settings = serde.dynamic_rules_settings;
+    config.dynamic_rules = serde.dynamic_rules;
+    config.acl = serde.acl;
+    config.ring_buffers = serde.ring_buffers;
+    config.binds = serde.binds;
+    config.backends = serde.backends;
+    config.cache_settings = serde.cache_settings;
+    config.security_polling = serde.security_polling;
+    config.general = serde.general;
+    config.packet_caches = serde.packet_caches;
+    config.proxy_protocol = serde.proxy_protocol;
+    config.snmp = serde.snmp;
+    config.query_count = serde.query_count;
+    config.load_balancing_policies = serde.load_balancing_policies;
+    config.pools = serde.pools;
+    config.metrics = serde.metrics;
+    config.remote_logging = serde.remote_logging;
+    config.tuning = serde.tuning;
+    // this needs to be done before the rules so that they can refer to the loggers
+    register_remote_loggers(&config.remote_logging);
+    // this needs to be done before the rules so that they can refer to the KVS objects
+    dnsdistsettings::registerKVSObjects(&config.key_value_stores);
+    // this needs to be done BEFORE the rules so that they can refer to the selectors
+    // by name
+    config.selectors = get_selectors_from_serde(&serde.selectors);
+    config.query_rules = get_query_rules_from_serde(&serde.query_rules);
+    config.cache_miss_rules = get_query_rules_from_serde(&serde.cache_miss_rules);
+    config.response_rules = get_response_rules_from_serde(&serde.response_rules);
+    config.cache_hit_response_rules = get_response_rules_from_serde(&serde.cache_hit_response_rules);
+    config.cache_inserted_response_rules = get_response_rules_from_serde(&serde.cache_inserted_response_rules);
+    config.self_answered_response_rules = get_response_rules_from_serde(&serde.self_answered_response_rules);
+    config.xfr_response_rules = get_response_rules_from_serde(&serde.xfr_response_rules);
+    config
+}
+
+pub fn from_yaml_string(
+    str: &str,
+) -> Result<dnsdistsettings::GlobalConfiguration, serde_yaml::Error> {
+    let serde_config: Result<GlobalConfigurationSerde, serde_yaml::Error> =
+        serde_yaml::from_str(str);
+
+    if !serde_config.is_err() {
+      let validation_result = serde_config.as_ref().unwrap().validate();
+      if let Err(e) = validation_result {
+          println!("Error validating the configuration loaded from {}: {}", str, e);
+      }
     }
+    let config: dnsdistsettings::GlobalConfiguration =
+        get_global_configuration_from_serde(serde_config?);
+    return Ok(config);
 }
+// END INCLUDE ./rust-post-in.rs